using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.CodeAnalysis;
using ProtoBuf.Compiler;
using ProtoBuf.Meta;
using ProtoBuf.Serializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("2.4.8.1")]
[assembly: AssemblyInformationalVersion("2.4.8.1+cce1c58590")]
[assembly: InternalsVisibleTo("protobuf-net.Test, PublicKey=002400000480000094000000060200000024000052534131000400000100010009ed9caa457bfc205716c3d4e8b255a63ddf71c9e53b1b5f574ab6ffdba11e80ab4b50be9c46d43b75206280070ddba67bd4c830f93f0317504a76ba6a48243c36d2590695991164592767a7bbc4453b34694e31e20815a096e4483605139a32a76ec2fef196507487329c12047bf6a68bca8ee9354155f4d01daf6eec5ff6bc")]
[assembly: TargetFramework(".NETFramework,Version=v4.5.2", FrameworkDisplayName = ".NET Framework 4.5.2")]
[assembly: AssemblyCompany("Marc Gravell")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("See https://github.com/mgravell/protobuf-net")]
[assembly: AssemblyDescription("Provides simple access to fast and efficient \"Protocol Buffers\" serialization from .NET applications")]
[assembly: AssemblyProduct("protobuf-net (net452)")]
[assembly: AssemblyTitle("protobuf-net")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/mgravell/protobuf-net")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.4.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
[GeneratedCode("Nerdbank.GitVersioning.Tasks", "3.5.119.9565")]
[ExcludeFromCodeCoverage]
internal static class ThisAssembly
{
	internal const string AssemblyConfiguration = "Release";

	internal const string AssemblyFileVersion = "2.4.8.1";

	internal const string AssemblyInformationalVersion = "2.4.8.1+cce1c58590";

	internal const string AssemblyName = "protobuf-net";

	internal const string AssemblyTitle = "protobuf-net";

	internal const string AssemblyVersion = "2.4.0.0";

	internal static readonly DateTime GitCommitDate = new DateTime(638088657180000000L, DateTimeKind.Utc);

	internal const string GitCommitId = "cce1c5859052675325a242a1b0d1139411d9964e";

	internal const bool IsPrerelease = false;

	internal const bool IsPublicRelease = true;

	internal const string PublicKey = "002400000480000094000000060200000024000052534131000400000100010009ed9caa457bfc205716c3d4e8b255a63ddf71c9e53b1b5f574ab6ffdba11e80ab4b50be9c46d43b75206280070ddba67bd4c830f93f0317504a76ba6a48243c36d2590695991164592767a7bbc4453b34694e31e20815a096e4483605139a32a76ec2fef196507487329c12047bf6a68bca8ee9354155f4d01daf6eec5ff6bc";

	internal const string PublicKeyToken = "257b51d87d2e4d67";

	internal const string RootNamespace = "ProtoBuf";
}
namespace ProtoBuf
{
	internal enum TimeSpanScale
	{
		Days = 0,
		Hours = 1,
		Minutes = 2,
		Seconds = 3,
		Milliseconds = 4,
		Ticks = 5,
		MinMax = 15
	}
	public static class BclHelpers
	{
		[Flags]
		public enum NetObjectOptions : byte
		{
			None = 0,
			AsReference = 1,
			DynamicType = 2,
			UseConstructor = 4,
			LateSet = 8
		}

		private const int FieldTimeSpanValue = 1;

		private const int FieldTimeSpanScale = 2;

		private const int FieldTimeSpanKind = 3;

		internal static readonly DateTime[] EpochOrigin = new DateTime[3]
		{
			new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
			new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc),
			new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Local)
		};

		private static readonly DateTime TimestampEpoch = EpochOrigin[1];

		private const int FieldDecimalLow = 1;

		private const int FieldDecimalHigh = 2;

		private const int FieldDecimalSignScale = 3;

		private const int FieldGuidLow = 1;

		private const int FieldGuidHigh = 2;

		private const int FieldExistingObjectKey = 1;

		private const int FieldNewObjectKey = 2;

		private const int FieldExistingTypeKey = 3;

		private const int FieldNewTypeKey = 4;

		private const int FieldTypeName = 8;

		private const int FieldObject = 10;

		public static object GetUninitializedObject(Type type)
		{
			return FormatterServices.GetUninitializedObject(type);
		}

		public static void WriteTimeSpan(TimeSpan timeSpan, ProtoWriter dest)
		{
			WriteTimeSpanImpl(timeSpan, dest, DateTimeKind.Unspecified);
		}

		private static void WriteTimeSpanImpl(TimeSpan timeSpan, ProtoWriter dest, DateTimeKind kind)
		{
			if (dest == null)
			{
				throw new ArgumentNullException("dest");
			}
			switch (dest.WireType)
			{
			case WireType.String:
			case WireType.StartGroup:
			{
				long num = timeSpan.Ticks;
				TimeSpanScale timeSpanScale;
				if (timeSpan == TimeSpan.MaxValue)
				{
					num = 1L;
					timeSpanScale = TimeSpanScale.MinMax;
				}
				else if (timeSpan == TimeSpan.MinValue)
				{
					num = -1L;
					timeSpanScale = TimeSpanScale.MinMax;
				}
				else if (num % 864000000000L == 0L)
				{
					timeSpanScale = TimeSpanScale.Days;
					num /= 864000000000L;
				}
				else if (num % 36000000000L == 0L)
				{
					timeSpanScale = TimeSpanScale.Hours;
					num /= 36000000000L;
				}
				else if (num % 600000000 == 0L)
				{
					timeSpanScale = TimeSpanScale.Minutes;
					num /= 600000000;
				}
				else if (num % 10000000 == 0L)
				{
					timeSpanScale = TimeSpanScale.Seconds;
					num /= 10000000;
				}
				else if (num % 10000 == 0L)
				{
					timeSpanScale = TimeSpanScale.Milliseconds;
					num /= 10000;
				}
				else
				{
					timeSpanScale = TimeSpanScale.Ticks;
				}
				SubItemToken token = ProtoWriter.StartSubItem(null, dest);
				if (num != 0L)
				{
					ProtoWriter.WriteFieldHeader(1, WireType.SignedVariant, dest);
					ProtoWriter.WriteInt64(num, dest);
				}
				if (timeSpanScale != TimeSpanScale.Days)
				{
					ProtoWriter.WriteFieldHeader(2, WireType.Variant, dest);
					ProtoWriter.WriteInt32((int)timeSpanScale, dest);
				}
				if (kind != DateTimeKind.Unspecified)
				{
					ProtoWriter.WriteFieldHeader(3, WireType.Variant, dest);
					ProtoWriter.WriteInt32((int)kind, dest);
				}
				ProtoWriter.EndSubItem(token, dest);
				break;
			}
			case WireType.Fixed64:
				ProtoWriter.WriteInt64(timeSpan.Ticks, dest);
				break;
			default:
				throw new ProtoException("Unexpected wire-type: " + dest.WireType);
			}
		}

		public static TimeSpan ReadTimeSpan(ProtoReader source)
		{
			DateTimeKind kind;
			long num = ReadTimeSpanTicks(source, out kind);
			return num switch
			{
				long.MinValue => TimeSpan.MinValue, 
				long.MaxValue => TimeSpan.MaxValue, 
				_ => TimeSpan.FromTicks(num), 
			};
		}

		public static TimeSpan ReadDuration(ProtoReader source)
		{
			long seconds = 0L;
			int nanos = 0;
			SubItemToken token = ProtoReader.StartSubItem(source);
			int num;
			while ((num = source.ReadFieldHeader()) > 0)
			{
				switch (num)
				{
				case 1:
					seconds = source.ReadInt64();
					break;
				case 2:
					nanos = source.ReadInt32();
					break;
				default:
					source.SkipField();
					break;
				}
			}
			ProtoReader.EndSubItem(token, source);
			return FromDurationSeconds(seconds, nanos);
		}

		public static void WriteDuration(TimeSpan value, ProtoWriter dest)
		{
			int nanos;
			long seconds = ToDurationSeconds(value, out nanos);
			WriteSecondsNanos(seconds, nanos, dest);
		}

		private static void WriteSecondsNanos(long seconds, int nanos, ProtoWriter dest)
		{
			SubItemToken token = ProtoWriter.StartSubItem(null, dest);
			if (seconds != 0L)
			{
				ProtoWriter.WriteFieldHeader(1, WireType.Variant, dest);
				ProtoWriter.WriteInt64(seconds, dest);
			}
			if (nanos != 0)
			{
				ProtoWriter.WriteFieldHeader(2, WireType.Variant, dest);
				ProtoWriter.WriteInt32(nanos, dest);
			}
			ProtoWriter.EndSubItem(token, dest);
		}

		public static DateTime ReadTimestamp(ProtoReader source)
		{
			return TimestampEpoch + ReadDuration(source);
		}

		public static void WriteTimestamp(DateTime value, ProtoWriter dest)
		{
			int nanos;
			long num = ToDurationSeconds(value - TimestampEpoch, out nanos);
			if (nanos < 0)
			{
				num--;
				nanos += 1000000000;
			}
			WriteSecondsNanos(num, nanos, dest);
		}

		private static TimeSpan FromDurationSeconds(long seconds, int nanos)
		{
			checked
			{
				long value = seconds * 10000000 + unchecked(checked(unchecked((long)nanos) * 10000L) / 1000000);
				return TimeSpan.FromTicks(value);
			}
		}

		private static long ToDurationSeconds(TimeSpan value, out int nanos)
		{
			nanos = (int)(value.Ticks % 10000000 * 1000000 / 10000);
			return value.Ticks / 10000000;
		}

		public static DateTime ReadDateTime(ProtoReader source)
		{
			DateTimeKind kind;
			long num = ReadTimeSpanTicks(source, out kind);
			return num switch
			{
				long.MinValue => DateTime.MinValue, 
				long.MaxValue => DateTime.MaxValue, 
				_ => EpochOrigin[(int)kind].AddTicks(num), 
			};
		}

		public static void WriteDateTime(DateTime value, ProtoWriter dest)
		{
			WriteDateTimeImpl(value, dest, includeKind: false);
		}

		public static void WriteDateTimeWithKind(DateTime value, ProtoWriter dest)
		{
			WriteDateTimeImpl(value, dest, includeKind: true);
		}

		private static void WriteDateTimeImpl(DateTime value, ProtoWriter dest, bool includeKind)
		{
			if (dest == null)
			{
				throw new ArgumentNullException("dest");
			}
			WireType wireType = dest.WireType;
			TimeSpan timeSpan;
			if ((uint)(wireType - 2) <= 1u)
			{
				if (value == DateTime.MaxValue)
				{
					timeSpan = TimeSpan.MaxValue;
					includeKind = false;
				}
				else if (value == DateTime.MinValue)
				{
					timeSpan = TimeSpan.MinValue;
					includeKind = false;
				}
				else
				{
					timeSpan = value - EpochOrigin[0];
				}
			}
			else
			{
				timeSpan = value - EpochOrigin[0];
			}
			WriteTimeSpanImpl(timeSpan, dest, includeKind ? value.Kind : DateTimeKind.Unspecified);
		}

		private static long ReadTimeSpanTicks(ProtoReader source, out DateTimeKind kind)
		{
			kind = DateTimeKind.Unspecified;
			switch (source.WireType)
			{
			case WireType.String:
			case WireType.StartGroup:
			{
				SubItemToken token = ProtoReader.StartSubItem(source);
				TimeSpanScale timeSpanScale = TimeSpanScale.Days;
				long num = 0L;
				int num2;
				while ((num2 = source.ReadFieldHeader()) > 0)
				{
					switch (num2)
					{
					case 2:
						timeSpanScale = (TimeSpanScale)source.ReadInt32();
						break;
					case 1:
						source.Assert(WireType.SignedVariant);
						num = source.ReadInt64();
						break;
					case 3:
					{
						kind = (DateTimeKind)source.ReadInt32();
						DateTimeKind dateTimeKind = kind;
						if ((uint)dateTimeKind > 2u)
						{
							throw new ProtoException("Invalid date/time kind: " + kind);
						}
						break;
					}
					default:
						source.SkipField();
						break;
					}
				}
				ProtoReader.EndSubItem(token, source);
				return timeSpanScale switch
				{
					TimeSpanScale.Days => num * 864000000000L, 
					TimeSpanScale.Hours => num * 36000000000L, 
					TimeSpanScale.Minutes => num * 600000000, 
					TimeSpanScale.Seconds => num * 10000000, 
					TimeSpanScale.Milliseconds => num * 10000, 
					TimeSpanScale.Ticks => num, 
					TimeSpanScale.MinMax => num switch
					{
						1L => long.MaxValue, 
						-1L => long.MinValue, 
						_ => throw new ProtoException("Unknown min/max value: " + num), 
					}, 
					_ => throw new ProtoException("Unknown timescale: " + timeSpanScale), 
				};
			}
			case WireType.Fixed64:
				return source.ReadInt64();
			default:
				throw new ProtoException("Unexpected wire-type: " + source.WireType);
			}
		}

		public static decimal ReadDecimal(ProtoReader reader)
		{
			ulong num = 0uL;
			uint num2 = 0u;
			uint num3 = 0u;
			SubItemToken token = ProtoReader.StartSubItem(reader);
			int num4;
			while ((num4 = reader.ReadFieldHeader()) > 0)
			{
				switch (num4)
				{
				case 1:
					num = reader.ReadUInt64();
					break;
				case 2:
					num2 = reader.ReadUInt32();
					break;
				case 3:
					num3 = reader.ReadUInt32();
					break;
				default:
					reader.SkipField();
					break;
				}
			}
			ProtoReader.EndSubItem(token, reader);
			int lo = (int)(num & 0xFFFFFFFFu);
			int mid = (int)((num >> 32) & 0xFFFFFFFFu);
			int hi = (int)num2;
			bool isNegative = (num3 & 1) == 1;
			byte scale = (byte)((num3 & 0x1FE) >> 1);
			return new decimal(lo, mid, hi, isNegative, scale);
		}

		public static void WriteDecimal(decimal value, ProtoWriter writer)
		{
			int[] bits = decimal.GetBits(value);
			ulong num = (ulong)((long)bits[1] << 32);
			ulong num2 = (ulong)(bits[0] & 0xFFFFFFFFu);
			ulong num3 = num | num2;
			uint num4 = (uint)bits[2];
			uint num5 = (uint)(((bits[3] >> 15) & 0x1FE) | ((bits[3] >> 31) & 1));
			SubItemToken token = ProtoWriter.StartSubItem(null, writer);
			if (num3 != 0L)
			{
				ProtoWriter.WriteFieldHeader(1, WireType.Variant, writer);
				ProtoWriter.WriteUInt64(num3, writer);
			}
			if (num4 != 0)
			{
				ProtoWriter.WriteFieldHeader(2, WireType.Variant, writer);
				ProtoWriter.WriteUInt32(num4, writer);
			}
			if (num5 != 0)
			{
				ProtoWriter.WriteFieldHeader(3, WireType.Variant, writer);
				ProtoWriter.WriteUInt32(num5, writer);
			}
			ProtoWriter.EndSubItem(token, writer);
		}

		public static void WriteGuid(Guid value, ProtoWriter dest)
		{
			byte[] data = value.ToByteArray();
			SubItemToken token = ProtoWriter.StartSubItem(null, dest);
			if (value != Guid.Empty)
			{
				ProtoWriter.WriteFieldHeader(1, WireType.Fixed64, dest);
				ProtoWriter.WriteBytes(data, 0, 8, dest);
				ProtoWriter.WriteFieldHeader(2, WireType.Fixed64, dest);
				ProtoWriter.WriteBytes(data, 8, 8, dest);
			}
			ProtoWriter.EndSubItem(token, dest);
		}

		public static Guid ReadGuid(ProtoReader source)
		{
			ulong num = 0uL;
			ulong num2 = 0uL;
			SubItemToken token = ProtoReader.StartSubItem(source);
			int num3;
			while ((num3 = source.ReadFieldHeader()) > 0)
			{
				switch (num3)
				{
				case 1:
					num = source.ReadUInt64();
					break;
				case 2:
					num2 = source.ReadUInt64();
					break;
				default:
					source.SkipField();
					break;
				}
			}
			ProtoReader.EndSubItem(token, source);
			if (num == 0L && num2 == 0L)
			{
				return Guid.Empty;
			}
			uint num4 = (uint)(num >> 32);
			uint a = (uint)num;
			uint num5 = (uint)(num2 >> 32);
			uint num6 = (uint)num2;
			return new Guid((int)a, (short)num4, (short)(num4 >> 16), (byte)num6, (byte)(num6 >> 8), (byte)(num6 >> 16), (byte)(num6 >> 24), (byte)num5, (byte)(num5 >> 8), (byte)(num5 >> 16), (byte)(num5 >> 24));
		}

		public static object ReadNetObject(object value, ProtoReader source, int key, Type type, NetObjectOptions options)
		{
			SubItemToken token = ProtoReader.StartSubItem(source);
			int num = -1;
			int num2 = -1;
			int num3;
			while ((num3 = source.ReadFieldHeader()) > 0)
			{
				switch (num3)
				{
				case 1:
				{
					int key2 = source.ReadInt32();
					value = source.NetCache.GetKeyedObject(key2);
					break;
				}
				case 2:
					num = source.ReadInt32();
					break;
				case 3:
				{
					int key2 = source.ReadInt32();
					type = (Type)source.NetCache.GetKeyedObject(key2);
					key = source.GetTypeKey(ref type);
					break;
				}
				case 4:
					num2 = source.ReadInt32();
					break;
				case 8:
				{
					string text = source.ReadString();
					type = source.DeserializeType(text);
					if (type == null)
					{
						throw new ProtoException("Unable to resolve type: " + text + " (you can use the TypeModel.DynamicTypeFormatting event to provide a custom mapping)");
					}
					if (type == typeof(string))
					{
						key = -1;
						break;
					}
					key = source.GetTypeKey(ref type);
					if (key >= 0)
					{
						break;
					}
					throw new InvalidOperationException("Dynamic type is not a contract-type: " + type.Name);
				}
				case 10:
				{
					bool flag = type == typeof(string);
					bool flag2 = value == null;
					bool flag3 = flag2 && (flag || (options & NetObjectOptions.LateSet) != 0);
					if (num >= 0 && !flag3)
					{
						if (value == null)
						{
							source.TrapNextObject(num);
						}
						else
						{
							source.NetCache.SetKeyedObject(num, value);
						}
						if (num2 >= 0)
						{
							source.NetCache.SetKeyedObject(num2, type);
						}
					}
					object obj = value;
					value = ((!flag) ? ProtoReader.ReadTypedObject(obj, key, source, type) : source.ReadString());
					if (num >= 0)
					{
						if (flag2 && !flag3)
						{
							obj = source.NetCache.GetKeyedObject(num);
						}
						if (flag3)
						{
							source.NetCache.SetKeyedObject(num, value);
							if (num2 >= 0)
							{
								source.NetCache.SetKeyedObject(num2, type);
							}
						}
					}
					if (num >= 0 && !flag3 && obj != value)
					{
						throw new ProtoException("A reference-tracked object changed reference during deserialization");
					}
					if (num < 0 && num2 >= 0)
					{
						source.NetCache.SetKeyedObject(num2, type);
					}
					break;
				}
				default:
					source.SkipField();
					break;
				}
			}
			if (num >= 0 && (options & NetObjectOptions.AsReference) == 0)
			{
				throw new ProtoException("Object key in input stream, but reference-tracking was not expected");
			}
			ProtoReader.EndSubItem(token, source);
			return value;
		}

		public static void WriteNetObject(object value, ProtoWriter dest, int key, NetObjectOptions options)
		{
			if (dest == null)
			{
				throw new ArgumentNullException("dest");
			}
			bool flag = (options & NetObjectOptions.DynamicType) != 0;
			bool flag2 = (options & NetObjectOptions.AsReference) != 0;
			WireType wireType = dest.WireType;
			SubItemToken token = ProtoWriter.StartSubItem(null, dest);
			bool flag3 = true;
			if (flag2)
			{
				bool existing;
				int value2 = dest.NetCache.AddObjectKey(value, out existing);
				ProtoWriter.WriteFieldHeader(existing ? 1 : 2, WireType.Variant, dest);
				ProtoWriter.WriteInt32(value2, dest);
				if (existing)
				{
					flag3 = false;
				}
			}
			if (flag3)
			{
				if (flag)
				{
					Type type = value.GetType();
					if (!(value is string))
					{
						key = dest.GetTypeKey(ref type);
						if (key < 0)
						{
							throw new InvalidOperationException("Dynamic type is not a contract-type: " + type.Name);
						}
					}
					bool existing2;
					int value3 = dest.NetCache.AddObjectKey(type, out existing2);
					ProtoWriter.WriteFieldHeader(existing2 ? 3 : 4, WireType.Variant, dest);
					ProtoWriter.WriteInt32(value3, dest);
					if (!existing2)
					{
						ProtoWriter.WriteFieldHeader(8, WireType.String, dest);
						ProtoWriter.WriteString(dest.SerializeType(type), dest);
					}
				}
				ProtoWriter.WriteFieldHeader(10, wireType, dest);
				if (value is string)
				{
					ProtoWriter.WriteString((string)value, dest);
				}
				else
				{
					ProtoWriter.WriteObject(value, key, dest);
				}
			}
			ProtoWriter.EndSubItem(token, dest);
		}
	}
	public sealed class BufferExtension : IExtension, IExtensionResettable
	{
		private byte[] buffer;

		void IExtensionResettable.Reset()
		{
			buffer = null;
		}

		int IExtension.GetLength()
		{
			if (buffer != null)
			{
				return buffer.Length;
			}
			return 0;
		}

		Stream IExtension.BeginAppend()
		{
			return new MemoryStream();
		}

		void IExtension.EndAppend(Stream stream, bool commit)
		{
			using (stream)
			{
				int num;
				if (commit && (num = (int)stream.Length) > 0)
				{
					MemoryStream memoryStream = (MemoryStream)stream;
					if (buffer == null)
					{
						buffer = memoryStream.ToArray();
						return;
					}
					int num2 = buffer.Length;
					byte[] dst = new byte[num2 + num];
					Buffer.BlockCopy(buffer, 0, dst, 0, num2);
					Buffer.BlockCopy(Helpers.GetBuffer(memoryStream), 0, dst, num2, num);
					buffer = dst;
				}
			}
		}

		Stream IExtension.BeginQuery()
		{
			if (buffer != null)
			{
				return new MemoryStream(buffer);
			}
			return Stream.Null;
		}

		void IExtension.EndQuery(Stream stream)
		{
			using (stream)
			{
			}
		}
	}
	internal sealed class BufferPool
	{
		private class CachedBuffer
		{
			private readonly WeakReference _reference;

			public int Size { get; }

			public bool IsAlive => _reference.IsAlive;

			public byte[] Buffer => (byte[])_reference.Target;

			public CachedBuffer(byte[] buffer)
			{
				Size = buffer.Length;
				_reference = new WeakReference(buffer);
			}
		}

		private const int POOL_SIZE = 20;

		internal const int BUFFER_LENGTH = 1024;

		private static readonly CachedBuffer[] Pool = new CachedBuffer[20];

		private const int MaxByteArraySize = 2147483591;

		internal static void Flush()
		{
			lock (Pool)
			{
				for (int i = 0; i < Pool.Length; i++)
				{
					Pool[i] = null;
				}
			}
		}

		private BufferPool()
		{
		}

		internal static byte[] GetBuffer()
		{
			return GetBuffer(1024);
		}

		internal static byte[] GetBuffer(int minSize)
		{
			byte[] cachedBuffer = GetCachedBuffer(minSize);
			return cachedBuffer ?? new byte[minSize];
		}

		internal static byte[] GetCachedBuffer(int minSize)
		{
			lock (Pool)
			{
				int num = -1;
				byte[] array = null;
				for (int i = 0; i < Pool.Length; i++)
				{
					CachedBuffer cachedBuffer = Pool[i];
					if (cachedBuffer != null && cachedBuffer.Size >= minSize && (array == null || array.Length >= cachedBuffer.Size))
					{
						byte[] buffer = cachedBuffer.Buffer;
						if (buffer == null)
						{
							Pool[i] = null;
							continue;
						}
						array = buffer;
						num = i;
					}
				}
				if (num >= 0)
				{
					Pool[num] = null;
				}
				return array;
			}
		}

		internal static void ResizeAndFlushLeft(ref byte[] buffer, int toFitAtLeastBytes, int copyFromIndex, int copyBytes)
		{
			int num = buffer.Length * 2;
			if (num < 0)
			{
				num = 2147483591;
			}
			if (num < toFitAtLeastBytes)
			{
				num = toFitAtLeastBytes;
			}
			if (copyBytes == 0)
			{
				ReleaseBufferToPool(ref buffer);
			}
			byte[] array = GetCachedBuffer(toFitAtLeastBytes) ?? new byte[num];
			if (copyBytes > 0)
			{
				Buffer.BlockCopy(buffer, copyFromIndex, array, 0, copyBytes);
				ReleaseBufferToPool(ref buffer);
			}
			buffer = array;
		}

		internal static void ReleaseBufferToPool(ref byte[] buffer)
		{
			if (buffer == null)
			{
				return;
			}
			lock (Pool)
			{
				int num = 0;
				int num2 = int.MaxValue;
				for (int i = 0; i < Pool.Length; i++)
				{
					CachedBuffer cachedBuffer = Pool[i];
					if (cachedBuffer == null || !cachedBuffer.IsAlive)
					{
						num = 0;
						break;
					}
					if (cachedBuffer.Size < num2)
					{
						num = i;
						num2 = cachedBuffer.Size;
					}
				}
				Pool[num] = new CachedBuffer(buffer);
			}
			buffer = null;
		}
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
	[ImmutableObject(true)]
	public sealed class ProtoBeforeSerializationAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
	[ImmutableObject(true)]
	public sealed class ProtoAfterSerializationAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
	[ImmutableObject(true)]
	public sealed class ProtoBeforeDeserializationAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
	[ImmutableObject(true)]
	public sealed class ProtoAfterDeserializationAttribute : Attribute
	{
	}
	public enum DataFormat
	{
		Default,
		ZigZag,
		TwosComplement,
		FixedSize,
		Group,
		WellKnown
	}
	[Serializable]
	public readonly struct DiscriminatedUnionObject : ISerializable
	{
		public readonly object Object;

		public int Discriminator { get; }

		public bool Is(int discriminator)
		{
			return Discriminator == discriminator;
		}

		public DiscriminatedUnionObject(int discriminator, object value)
		{
			Discriminator = discriminator;
			Object = value;
		}

		public static void Reset(ref DiscriminatedUnionObject value, int discriminator)
		{
			if (value.Discriminator == discriminator)
			{
				value = default(DiscriminatedUnionObject);
			}
		}

		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
		{
			if (Discriminator != 0)
			{
				info.AddValue("d", Discriminator);
			}
			if (Object != null)
			{
				info.AddValue("o", Object);
			}
		}

		private DiscriminatedUnionObject(SerializationInfo info, StreamingContext context)
		{
			this = default(DiscriminatedUnionObject);
			SerializationInfoEnumerator enumerator = info.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SerializationEntry current = enumerator.Current;
				string name = current.Name;
				if (!(name == "d"))
				{
					if (name == "o")
					{
						Object = current.Value;
					}
				}
				else
				{
					Discriminator = (int)current.Value;
				}
			}
		}
	}
	[Serializable]
	[StructLayout(LayoutKind.Explicit)]
	public readonly struct DiscriminatedUnion64 : ISerializable
	{
		[FieldOffset(0)]
		private readonly int _discriminator;

		[FieldOffset(8)]
		public readonly long Int64;

		[FieldOffset(8)]
		public readonly ulong UInt64;

		[FieldOffset(8)]
		public readonly int Int32;

		[FieldOffset(8)]
		public readonly uint UInt32;

		[FieldOffset(8)]
		public readonly bool Boolean;

		[FieldOffset(8)]
		public readonly float Single;

		[FieldOffset(8)]
		public readonly double Double;

		[FieldOffset(8)]
		public readonly DateTime DateTime;

		[FieldOffset(8)]
		public readonly TimeSpan TimeSpan;

		public int Discriminator => _discriminator;

		unsafe static DiscriminatedUnion64()
		{
			if (sizeof(DateTime) > 8)
			{
				throw new InvalidOperationException("DateTime was unexpectedly too big for DiscriminatedUnion64");
			}
			if (sizeof(TimeSpan) > 8)
			{
				throw new InvalidOperationException("TimeSpan was unexpectedly too big for DiscriminatedUnion64");
			}
		}

		private DiscriminatedUnion64(int discriminator)
		{
			this = default(DiscriminatedUnion64);
			_discriminator = discriminator;
		}

		public bool Is(int discriminator)
		{
			return _discriminator == discriminator;
		}

		public DiscriminatedUnion64(int discriminator, long value)
			: this(discriminator)
		{
			Int64 = value;
		}

		public DiscriminatedUnion64(int discriminator, int value)
			: this(discriminator)
		{
			Int32 = value;
		}

		public DiscriminatedUnion64(int discriminator, ulong value)
			: this(discriminator)
		{
			UInt64 = value;
		}

		public DiscriminatedUnion64(int discriminator, uint value)
			: this(discriminator)
		{
			UInt32 = value;
		}

		public DiscriminatedUnion64(int discriminator, float value)
			: this(discriminator)
		{
			Single = value;
		}

		public DiscriminatedUnion64(int discriminator, double value)
			: this(discriminator)
		{
			Double = value;
		}

		public DiscriminatedUnion64(int discriminator, bool value)
			: this(discriminator)
		{
			Boolean = value;
		}

		public DiscriminatedUnion64(int discriminator, DateTime? value)
			: this(value.HasValue ? discriminator : 0)
		{
			DateTime = value.GetValueOrDefault();
		}

		public DiscriminatedUnion64(int discriminator, TimeSpan? value)
			: this(value.HasValue ? discriminator : 0)
		{
			TimeSpan = value.GetValueOrDefault();
		}

		public static void Reset(ref DiscriminatedUnion64 value, int discriminator)
		{
			if (value.Discriminator == discriminator)
			{
				value = default(DiscriminatedUnion64);
			}
		}

		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
		{
			if (_discriminator != 0)
			{
				info.AddValue("d", _discriminator);
			}
			if (Int64 != 0L)
			{
				info.AddValue("i", Int64);
			}
		}

		private DiscriminatedUnion64(SerializationInfo info, StreamingContext context)
		{
			this = default(DiscriminatedUnion64);
			SerializationInfoEnumerator enumerator = info.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SerializationEntry current = enumerator.Current;
				string name = current.Name;
				if (!(name == "d"))
				{
					if (name == "i")
					{
						Int64 = (long)current.Value;
					}
				}
				else
				{
					_discriminator = (int)current.Value;
				}
			}
		}
	}
	[Serializable]
	[StructLayout(LayoutKind.Explicit)]
	public readonly struct DiscriminatedUnion128Object : ISerializable
	{
		[FieldOffset(0)]
		private readonly int _discriminator;

		[FieldOffset(8)]
		public readonly long Int64;

		[FieldOffset(8)]
		public readonly ulong UInt64;

		[FieldOffset(8)]
		public readonly int Int32;

		[FieldOffset(8)]
		public readonly uint UInt32;

		[FieldOffset(8)]
		public readonly bool Boolean;

		[FieldOffset(8)]
		public readonly float Single;

		[FieldOffset(8)]
		public readonly double Double;

		[FieldOffset(8)]
		public readonly DateTime DateTime;

		[FieldOffset(8)]
		public readonly TimeSpan TimeSpan;

		[FieldOffset(8)]
		public readonly Guid Guid;

		[FieldOffset(24)]
		public readonly object Object;

		[FieldOffset(8)]
		private readonly long _lo;

		[FieldOffset(16)]
		private readonly long _hi;

		public int Discriminator => _discriminator;

		unsafe static DiscriminatedUnion128Object()
		{
			if (sizeof(DateTime) > 16)
			{
				throw new InvalidOperationException("DateTime was unexpectedly too big for DiscriminatedUnion128Object");
			}
			if (sizeof(TimeSpan) > 16)
			{
				throw new InvalidOperationException("TimeSpan was unexpectedly too big for DiscriminatedUnion128Object");
			}
			if (sizeof(Guid) > 16)
			{
				throw new InvalidOperationException("Guid was unexpectedly too big for DiscriminatedUnion128Object");
			}
		}

		private DiscriminatedUnion128Object(int discriminator)
		{
			this = default(DiscriminatedUnion128Object);
			_discriminator = discriminator;
		}

		public bool Is(int discriminator)
		{
			return _discriminator == discriminator;
		}

		public DiscriminatedUnion128Object(int discriminator, long value)
			: this(discriminator)
		{
			Int64 = value;
		}

		public DiscriminatedUnion128Object(int discriminator, int value)
			: this(discriminator)
		{
			Int32 = value;
		}

		public DiscriminatedUnion128Object(int discriminator, ulong value)
			: this(discriminator)
		{
			UInt64 = value;
		}

		public DiscriminatedUnion128Object(int discriminator, uint value)
			: this(discriminator)
		{
			UInt32 = value;
		}

		public DiscriminatedUnion128Object(int discriminator, float value)
			: this(discriminator)
		{
			Single = value;
		}

		public DiscriminatedUnion128Object(int discriminator, double value)
			: this(discriminator)
		{
			Double = value;
		}

		public DiscriminatedUnion128Object(int discriminator, bool value)
			: this(discriminator)
		{
			Boolean = value;
		}

		public DiscriminatedUnion128Object(int discriminator, object value)
			: this((value != null) ? discriminator : 0)
		{
			Object = value;
		}

		public DiscriminatedUnion128Object(int discriminator, DateTime? value)
			: this(value.HasValue ? discriminator : 0)
		{
			DateTime = value.GetValueOrDefault();
		}

		public DiscriminatedUnion128Object(int discriminator, TimeSpan? value)
			: this(value.HasValue ? discriminator : 0)
		{
			TimeSpan = value.GetValueOrDefault();
		}

		public DiscriminatedUnion128Object(int discriminator, Guid? value)
			: this(value.HasValue ? discriminator : 0)
		{
			Guid = value.GetValueOrDefault();
		}

		public static void Reset(ref DiscriminatedUnion128Object value, int discriminator)
		{
			if (value.Discriminator == discriminator)
			{
				value = default(DiscriminatedUnion128Object);
			}
		}

		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
		{
			if (_discriminator != 0)
			{
				info.AddValue("d", _discriminator);
			}
			if (_lo != 0L)
			{
				info.AddValue("l", _lo);
			}
			if (_hi != 0L)
			{
				info.AddValue("h", _hi);
			}
			if (Object != null)
			{
				info.AddValue("o", Object);
			}
		}

		private DiscriminatedUnion128Object(SerializationInfo info, StreamingContext context)
		{
			this = default(DiscriminatedUnion128Object);
			SerializationInfoEnumerator enumerator = info.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SerializationEntry current = enumerator.Current;
				switch (current.Name)
				{
				case "d":
					_discriminator = (int)current.Value;
					break;
				case "l":
					_lo = (long)current.Value;
					break;
				case "h":
					_hi = (long)current.Value;
					break;
				case "o":
					Object = current.Value;
					break;
				}
			}
		}
	}
	[Serializable]
	[StructLayout(LayoutKind.Explicit)]
	public readonly struct DiscriminatedUnion128 : ISerializable
	{
		[FieldOffset(0)]
		private readonly int _discriminator;

		[FieldOffset(8)]
		public readonly long Int64;

		[FieldOffset(8)]
		public readonly ulong UInt64;

		[FieldOffset(8)]
		public readonly int Int32;

		[FieldOffset(8)]
		public readonly uint UInt32;

		[FieldOffset(8)]
		public readonly bool Boolean;

		[FieldOffset(8)]
		public readonly float Single;

		[FieldOffset(8)]
		public readonly double Double;

		[FieldOffset(8)]
		public readonly DateTime DateTime;

		[FieldOffset(8)]
		public readonly TimeSpan TimeSpan;

		[FieldOffset(8)]
		public readonly Guid Guid;

		[FieldOffset(8)]
		private readonly long _lo;

		[FieldOffset(16)]
		private readonly long _hi;

		public int Discriminator => _discriminator;

		unsafe static DiscriminatedUnion128()
		{
			if (sizeof(DateTime) > 16)
			{
				throw new InvalidOperationException("DateTime was unexpectedly too big for DiscriminatedUnion128");
			}
			if (sizeof(TimeSpan) > 16)
			{
				throw new InvalidOperationException("TimeSpan was unexpectedly too big for DiscriminatedUnion128");
			}
			if (sizeof(Guid) > 16)
			{
				throw new InvalidOperationException("Guid was unexpectedly too big for DiscriminatedUnion128");
			}
		}

		private DiscriminatedUnion128(int discriminator)
		{
			this = default(DiscriminatedUnion128);
			_discriminator = discriminator;
		}

		public bool Is(int discriminator)
		{
			return _discriminator == discriminator;
		}

		public DiscriminatedUnion128(int discriminator, long value)
			: this(discriminator)
		{
			Int64 = value;
		}

		public DiscriminatedUnion128(int discriminator, int value)
			: this(discriminator)
		{
			Int32 = value;
		}

		public DiscriminatedUnion128(int discriminator, ulong value)
			: this(discriminator)
		{
			UInt64 = value;
		}

		public DiscriminatedUnion128(int discriminator, uint value)
			: this(discriminator)
		{
			UInt32 = value;
		}

		public DiscriminatedUnion128(int discriminator, float value)
			: this(discriminator)
		{
			Single = value;
		}

		public DiscriminatedUnion128(int discriminator, double value)
			: this(discriminator)
		{
			Double = value;
		}

		public DiscriminatedUnion128(int discriminator, bool value)
			: this(discriminator)
		{
			Boolean = value;
		}

		public DiscriminatedUnion128(int discriminator, DateTime? value)
			: this(value.HasValue ? discriminator : 0)
		{
			DateTime = value.GetValueOrDefault();
		}

		public DiscriminatedUnion128(int discriminator, TimeSpan? value)
			: this(value.HasValue ? discriminator : 0)
		{
			TimeSpan = value.GetValueOrDefault();
		}

		public DiscriminatedUnion128(int discriminator, Guid? value)
			: this(value.HasValue ? discriminator : 0)
		{
			Guid = value.GetValueOrDefault();
		}

		public static void Reset(ref DiscriminatedUnion128 value, int discriminator)
		{
			if (value.Discriminator == discriminator)
			{
				value = default(DiscriminatedUnion128);
			}
		}

		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
		{
			if (_discriminator != 0)
			{
				info.AddValue("d", _discriminator);
			}
			if (_lo != 0L)
			{
				info.AddValue("l", _lo);
			}
			if (_hi != 0L)
			{
				info.AddValue("h", _hi);
			}
		}

		private DiscriminatedUnion128(SerializationInfo info, StreamingContext context)
		{
			this = default(DiscriminatedUnion128);
			SerializationInfoEnumerator enumerator = info.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SerializationEntry current = enumerator.Current;
				switch (current.Name)
				{
				case "d":
					_discriminator = (int)current.Value;
					break;
				case "l":
					_lo = (long)current.Value;
					break;
				case "h":
					_hi = (long)current.Value;
					break;
				}
			}
		}
	}
	[Serializable]
	[StructLayout(LayoutKind.Explicit)]
	public readonly struct DiscriminatedUnion64Object : ISerializable
	{
		[FieldOffset(0)]
		private readonly int _discriminator;

		[FieldOffset(8)]
		public readonly long Int64;

		[FieldOffset(8)]
		public readonly ulong UInt64;

		[FieldOffset(8)]
		public readonly int Int32;

		[FieldOffset(8)]
		public readonly uint UInt32;

		[FieldOffset(8)]
		public readonly bool Boolean;

		[FieldOffset(8)]
		public readonly float Single;

		[FieldOffset(8)]
		public readonly double Double;

		[FieldOffset(8)]
		public readonly DateTime DateTime;

		[FieldOffset(8)]
		public readonly TimeSpan TimeSpan;

		[FieldOffset(16)]
		public readonly object Object;

		public int Discriminator => _discriminator;

		unsafe static DiscriminatedUnion64Object()
		{
			if (sizeof(DateTime) > 8)
			{
				throw new InvalidOperationException("DateTime was unexpectedly too big for DiscriminatedUnion64Object");
			}
			if (sizeof(TimeSpan) > 8)
			{
				throw new InvalidOperationException("TimeSpan was unexpectedly too big for DiscriminatedUnion64Object");
			}
		}

		private DiscriminatedUnion64Object(int discriminator)
		{
			this = default(DiscriminatedUnion64Object);
			_discriminator = discriminator;
		}

		public bool Is(int discriminator)
		{
			return _discriminator == discriminator;
		}

		public DiscriminatedUnion64Object(int discriminator, long value)
			: this(discriminator)
		{
			Int64 = value;
		}

		public DiscriminatedUnion64Object(int discriminator, int value)
			: this(discriminator)
		{
			Int32 = value;
		}

		public DiscriminatedUnion64Object(int discriminator, ulong value)
			: this(discriminator)
		{
			UInt64 = value;
		}

		public DiscriminatedUnion64Object(int discriminator, uint value)
			: this(discriminator)
		{
			UInt32 = value;
		}

		public DiscriminatedUnion64Object(int discriminator, float value)
			: this(discriminator)
		{
			Single = value;
		}

		public DiscriminatedUnion64Object(int discriminator, double value)
			: this(discriminator)
		{
			Double = value;
		}

		public DiscriminatedUnion64Object(int discriminator, bool value)
			: this(discriminator)
		{
			Boolean = value;
		}

		public DiscriminatedUnion64Object(int discriminator, object value)
			: this((value != null) ? discriminator : 0)
		{
			Object = value;
		}

		public DiscriminatedUnion64Object(int discriminator, DateTime? value)
			: this(value.HasValue ? discriminator : 0)
		{
			DateTime = value.GetValueOrDefault();
		}

		public DiscriminatedUnion64Object(int discriminator, TimeSpan? value)
			: this(value.HasValue ? discriminator : 0)
		{
			TimeSpan = value.GetValueOrDefault();
		}

		public static void Reset(ref DiscriminatedUnion64Object value, int discriminator)
		{
			if (value.Discriminator == discriminator)
			{
				value = default(DiscriminatedUnion64Object);
			}
		}

		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
		{
			if (_discriminator != 0)
			{
				info.AddValue("d", _discriminator);
			}
			if (Int64 != 0L)
			{
				info.AddValue("i", Int64);
			}
			if (Object != null)
			{
				info.AddValue("o", Object);
			}
		}

		private DiscriminatedUnion64Object(SerializationInfo info, StreamingContext context)
		{
			this = default(DiscriminatedUnion64Object);
			SerializationInfoEnumerator enumerator = info.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SerializationEntry current = enumerator.Current;
				switch (current.Name)
				{
				case "d":
					_discriminator = (int)current.Value;
					break;
				case "i":
					Int64 = (long)current.Value;
					break;
				case "o":
					Object = current.Value;
					break;
				}
			}
		}
	}
	[Serializable]
	[StructLayout(LayoutKind.Explicit)]
	public readonly struct DiscriminatedUnion32 : ISerializable
	{
		[FieldOffset(0)]
		private readonly int _discriminator;

		[FieldOffset(4)]
		public readonly int Int32;

		[FieldOffset(4)]
		public readonly uint UInt32;

		[FieldOffset(4)]
		public readonly bool Boolean;

		[FieldOffset(4)]
		public readonly float Single;

		public int Discriminator => _discriminator;

		private DiscriminatedUnion32(int discriminator)
		{
			this = default(DiscriminatedUnion32);
			_discriminator = discriminator;
		}

		public bool Is(int discriminator)
		{
			return _discriminator == discriminator;
		}

		public DiscriminatedUnion32(int discriminator, int value)
			: this(discriminator)
		{
			Int32 = value;
		}

		public DiscriminatedUnion32(int discriminator, uint value)
			: this(discriminator)
		{
			UInt32 = value;
		}

		public DiscriminatedUnion32(int discriminator, float value)
			: this(discriminator)
		{
			Single = value;
		}

		public DiscriminatedUnion32(int discriminator, bool value)
			: this(discriminator)
		{
			Boolean = value;
		}

		public static void Reset(ref DiscriminatedUnion32 value, int discriminator)
		{
			if (value.Discriminator == discriminator)
			{
				value = default(DiscriminatedUnion32);
			}
		}

		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
		{
			if (_discriminator != 0)
			{
				info.AddValue("d", _discriminator);
			}
			if (Int32 != 0)
			{
				info.AddValue("i", Int32);
			}
		}

		private DiscriminatedUnion32(SerializationInfo info, StreamingContext context)
		{
			this = default(DiscriminatedUnion32);
			SerializationInfoEnumerator enumerator = info.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SerializationEntry current = enumerator.Current;
				string name = current.Name;
				if (!(name == "d"))
				{
					if (name == "i")
					{
						Int32 = (int)current.Value;
					}
				}
				else
				{
					_discriminator = (int)current.Value;
				}
			}
		}
	}
	[Serializable]
	[StructLayout(LayoutKind.Explicit)]
	public readonly struct DiscriminatedUnion32Object : ISerializable
	{
		[FieldOffset(0)]
		private readonly int _discriminator;

		[FieldOffset(4)]
		public readonly int Int32;

		[FieldOffset(4)]
		public readonly uint UInt32;

		[FieldOffset(4)]
		public readonly bool Boolean;

		[FieldOffset(4)]
		public readonly float Single;

		[FieldOffset(8)]
		public readonly object Object;

		public int Discriminator => _discriminator;

		private DiscriminatedUnion32Object(int discriminator)
		{
			this = default(DiscriminatedUnion32Object);
			_discriminator = discriminator;
		}

		public bool Is(int discriminator)
		{
			return _discriminator == discriminator;
		}

		public DiscriminatedUnion32Object(int discriminator, int value)
			: this(discriminator)
		{
			Int32 = value;
		}

		public DiscriminatedUnion32Object(int discriminator, uint value)
			: this(discriminator)
		{
			UInt32 = value;
		}

		public DiscriminatedUnion32Object(int discriminator, float value)
			: this(discriminator)
		{
			Single = value;
		}

		public DiscriminatedUnion32Object(int discriminator, bool value)
			: this(discriminator)
		{
			Boolean = value;
		}

		public DiscriminatedUnion32Object(int discriminator, object value)
			: this((value != null) ? discriminator : 0)
		{
			Object = value;
		}

		public static void Reset(ref DiscriminatedUnion32Object value, int discriminator)
		{
			if (value.Discriminator == discriminator)
			{
				value = default(DiscriminatedUnion32Object);
			}
		}

		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
		{
			if (_discriminator != 0)
			{
				info.AddValue("d", _discriminator);
			}
			if (Int32 != 0)
			{
				info.AddValue("i", Int32);
			}
			if (Object != null)
			{
				info.AddValue("o", Object);
			}
		}

		private DiscriminatedUnion32Object(SerializationInfo info, StreamingContext context)
		{
			this = default(DiscriminatedUnion32Object);
			SerializationInfoEnumerator enumerator = info.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SerializationEntry current = enumerator.Current;
				switch (current.Name)
				{
				case "d":
					_discriminator = (int)current.Value;
					break;
				case "i":
					Int32 = (int)current.Value;
					break;
				case "o":
					Object = current.Value;
					break;
				}
			}
		}
	}
	public abstract class Extensible : IExtensible
	{
		private IExtension extensionObject;

		IExtension IExtensible.GetExtensionObject(bool createIfMissing)
		{
			return GetExtensionObject(createIfMissing);
		}

		protected virtual IExtension GetExtensionObject(bool createIfMissing)
		{
			return GetExtensionObject(ref extensionObject, createIfMissing);
		}

		public static IExtension GetExtensionObject(ref IExtension extensionObject, bool createIfMissing)
		{
			if (createIfMissing && extensionObject == null)
			{
				extensionObject = new BufferExtension();
			}
			return extensionObject;
		}

		public static void AppendValue<TValue>(IExtensible instance, int tag, TValue value)
		{
			AppendValue(instance, tag, DataFormat.Default, value);
		}

		public static void AppendValue<TValue>(IExtensible instance, int tag, DataFormat format, TValue value)
		{
			ExtensibleUtil.AppendExtendValue(RuntimeTypeModel.Default, instance, tag, format, value);
		}

		public static TValue GetValue<TValue>(IExtensible instance, int tag)
		{
			return GetValue<TValue>(instance, tag, DataFormat.Default);
		}

		public static TValue GetValue<TValue>(IExtensible instance, int tag, DataFormat format)
		{
			TryGetValue<TValue>(instance, tag, format, out var value);
			return value;
		}

		public static bool TryGetValue<TValue>(IExtensible instance, int tag, out TValue value)
		{
			return TryGetValue<TValue>(instance, tag, DataFormat.Default, out value);
		}

		public static bool TryGetValue<TValue>(IExtensible instance, int tag, DataFormat format, out TValue value)
		{
			return TryGetValue<TValue>(instance, tag, format, allowDefinedTag: false, out value);
		}

		public static bool TryGetValue<TValue>(IExtensible instance, int tag, DataFormat format, bool allowDefinedTag, out TValue value)
		{
			value = default(TValue);
			bool result = false;
			foreach (TValue extendedValue in ExtensibleUtil.GetExtendedValues<TValue>(instance, tag, format, singleton: true, allowDefinedTag))
			{
				value = extendedValue;
				result = true;
			}
			return result;
		}

		public static IEnumerable<TValue> GetValues<TValue>(IExtensible instance, int tag)
		{
			return ExtensibleUtil.GetExtendedValues<TValue>(instance, tag, DataFormat.Default, singleton: false, allowDefinedTag: false);
		}

		public static IEnumerable<TValue> GetValues<TValue>(IExtensible instance, int tag, DataFormat format)
		{
			return ExtensibleUtil.GetExtendedValues<TValue>(instance, tag, format, singleton: false, allowDefinedTag: false);
		}

		public static bool TryGetValue(TypeModel model, Type type, IExtensible instance, int tag, DataFormat format, bool allowDefinedTag, out object value)
		{
			value = null;
			bool result = false;
			foreach (object extendedValue in ExtensibleUtil.GetExtendedValues(model, type, instance, tag, format, singleton: true, allowDefinedTag))
			{
				value = extendedValue;
				result = true;
			}
			return result;
		}

		public static IEnumerable GetValues(TypeModel model, Type type, IExtensible instance, int tag, DataFormat format)
		{
			return ExtensibleUtil.GetExtendedValues(model, type, instance, tag, format, singleton: false, allowDefinedTag: false);
		}

		public static void AppendValue(TypeModel model, IExtensible instance, int tag, DataFormat format, object value)
		{
			ExtensibleUtil.AppendExtendValue(model, instance, tag, format, value);
		}
	}
	internal static class ExtensibleUtil
	{
		internal static IEnumerable<TValue> GetExtendedValues<TValue>(IExtensible instance, int tag, DataFormat format, bool singleton, bool allowDefinedTag)
		{
			foreach (TValue extendedValue in GetExtendedValues(RuntimeTypeModel.Default, typeof(TValue), instance, tag, format, singleton, allowDefinedTag))
			{
				yield return extendedValue;
			}
		}

		internal static IEnumerable GetExtendedValues(TypeModel model, Type type, IExtensible instance, int tag, DataFormat format, bool singleton, bool allowDefinedTag)
		{
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			if (tag <= 0)
			{
				throw new ArgumentOutOfRangeException("tag");
			}
			IExtension extn = instance.GetExtensionObject(createIfMissing: false);
			if (extn == null)
			{
				yield break;
			}
			Stream stream = extn.BeginQuery();
			object value = null;
			ProtoReader reader = null;
			try
			{
				SerializationContext context = new SerializationContext();
				reader = ProtoReader.Create(stream, model, context, -1L);
				while (model.TryDeserializeAuxiliaryType(reader, format, tag, type, ref value, skipOtherFields: true, asListItem: true, autoCreate: false, insideList: false, null) && value != null)
				{
					if (!singleton)
					{
						yield return value;
						value = null;
					}
				}
				if (singleton && value != null)
				{
					yield return value;
				}
			}
			finally
			{
				ProtoReader.Recycle(reader);
				extn.EndQuery(stream);
			}
		}

		internal static void AppendExtendValue(TypeModel model, IExtensible instance, int tag, DataFormat format, object value)
		{
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			if (value == null)
			{
				throw new ArgumentNullException("value");
			}
			IExtension extensionObject = instance.GetExtensionObject(createIfMissing: true);
			if (extensionObject == null)
			{
				throw new InvalidOperationException("No extension object available; appended data would be lost.");
			}
			bool commit = false;
			Stream stream = extensionObject.BeginAppend();
			try
			{
				using (ProtoWriter protoWriter = ProtoWriter.Create(stream, model))
				{
					model.TrySerializeAuxiliaryType(protoWriter, null, format, tag, value, isInsideList: false, null);
					protoWriter.Close();
				}
				commit = true;
			}
			finally
			{
				extensionObject.EndAppend(stream, commit);
			}
		}
	}
	internal sealed class Helpers
	{
		public static readonly Type[] EmptyTypes = Type.EmptyTypes;

		private Helpers()
		{
		}

		public static StringBuilder AppendLine(StringBuilder builder)
		{
			return builder.AppendLine();
		}

		[Conditional("DEBUG")]
		public static void DebugWriteLine(string message, object obj)
		{
		}

		[Conditional("DEBUG")]
		public static void DebugWriteLine(string message)
		{
		}

		[Conditional("TRACE")]
		public static void TraceWriteLine(string message)
		{
		}

		[Conditional("DEBUG")]
		public static void DebugAssert(bool condition, string message)
		{
		}

		[Conditional("DEBUG")]
		public static void DebugAssert(bool condition, string message, params object[] args)
		{
		}

		[Conditional("DEBUG")]
		public static void DebugAssert(bool condition)
		{
		}

		public static void Sort(int[] keys, object[] values)
		{
			bool flag;
			do
			{
				flag = false;
				for (int i = 1; i < keys.Length; i++)
				{
					if (keys[i - 1] > keys[i])
					{
						int num = keys[i];
						keys[i] = keys[i - 1];
						keys[i - 1] = num;
						object obj = values[i];
						values[i] = values[i - 1];
						values[i - 1] = obj;
						flag = true;
					}
				}
			}
			while (flag);
		}

		internal static MethodInfo GetInstanceMethod(Type declaringType, string name)
		{
			return declaringType.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		internal static MethodInfo GetStaticMethod(Type declaringType, string name)
		{
			return declaringType.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}

		internal static MethodInfo GetStaticMethod(Type declaringType, string name, Type[] parameterTypes)
		{
			return declaringType.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
		}

		internal static MethodInfo GetInstanceMethod(Type declaringType, string name, Type[] types)
		{
			if (types == null)
			{
				types = EmptyTypes;
			}
			return declaringType.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null);
		}

		internal static bool IsSubclassOf(Type type, Type baseClass)
		{
			return type.IsSubclassOf(baseClass);
		}

		public static ProtoTypeCode GetTypeCode(Type type)
		{
			TypeCode typeCode = Type.GetTypeCode(type);
			switch (typeCode)
			{
			case TypeCode.Empty:
			case TypeCode.Boolean:
			case TypeCode.Char:
			case TypeCode.SByte:
			case TypeCode.Byte:
			case TypeCode.Int16:
			case TypeCode.UInt16:
			case TypeCode.Int32:
			case TypeCode.UInt32:
			case TypeCode.Int64:
			case TypeCode.UInt64:
			case TypeCode.Single:
			case TypeCode.Double:
			case TypeCode.Decimal:
			case TypeCode.DateTime:
			case TypeCode.String:
				return (ProtoTypeCode)typeCode;
			default:
				if (type == typeof(TimeSpan))
				{
					return ProtoTypeCode.TimeSpan;
				}
				if (type == typeof(Guid))
				{
					return ProtoTypeCode.Guid;
				}
				if (type == typeof(Uri))
				{
					return ProtoTypeCode.Uri;
				}
				if (type == typeof(byte[]))
				{
					return ProtoTypeCode.ByteArray;
				}
				if (type == typeof(Type))
				{
					return ProtoTypeCode.Type;
				}
				return ProtoTypeCode.Unknown;
			}
		}

		internal static Type GetUnderlyingType(Type type)
		{
			return Nullable.GetUnderlyingType(type);
		}

		internal static bool IsValueType(Type type)
		{
			return type.IsValueType;
		}

		internal static bool IsSealed(Type type)
		{
			return type.IsSealed;
		}

		internal static bool IsClass(Type type)
		{
			return type.IsClass;
		}

		internal static bool IsEnum(Type type)
		{
			return type.IsEnum;
		}

		internal static MethodInfo GetGetMethod(PropertyInfo property, bool nonPublic, bool allowInternal)
		{
			if (property == null)
			{
				return null;
			}
			MethodInfo methodInfo = property.GetGetMethod(nonPublic);
			if (methodInfo == null && !nonPublic && allowInternal)
			{
				methodInfo = property.GetGetMethod(nonPublic: true);
				if (methodInfo == null && !methodInfo.IsAssembly && !methodInfo.IsFamilyOrAssembly)
				{
					methodInfo = null;
				}
			}
			return methodInfo;
		}

		internal static MethodInfo GetSetMethod(PropertyInfo property, bool nonPublic, bool allowInternal)
		{
			if (property == null)
			{
				return null;
			}
			MethodInfo methodInfo = property.GetSetMethod(nonPublic);
			if (methodInfo == null && !nonPublic && allowInternal)
			{
				methodInfo = property.GetGetMethod(nonPublic: true);
				if (methodInfo == null && !methodInfo.IsAssembly && !methodInfo.IsFamilyOrAssembly)
				{
					methodInfo = null;
				}
			}
			return methodInfo;
		}

		internal static ConstructorInfo GetConstructor(Type type, Type[] parameterTypes, bool nonPublic)
		{
			return type.GetConstructor(nonPublic ? (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : (BindingFlags.Instance | BindingFlags.Public), null, parameterTypes, null);
		}

		internal static ConstructorInfo[] GetConstructors(Type type, bool nonPublic)
		{
			return type.GetConstructors(nonPublic ? (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : (BindingFlags.Instance | BindingFlags.Public));
		}

		internal static PropertyInfo GetProperty(Type type, string name, bool nonPublic)
		{
			return type.GetProperty(name, nonPublic ? (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : (BindingFlags.Instance | BindingFlags.Public));
		}

		internal static object ParseEnum(Type type, string value)
		{
			return Enum.Parse(type, value, ignoreCase: true);
		}

		internal static MemberInfo[] GetInstanceFieldsAndProperties(Type type, bool publicOnly)
		{
			BindingFlags bindingAttr = (publicOnly ? (BindingFlags.Instance | BindingFlags.Public) : (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
			PropertyInfo[] properties = type.GetProperties(bindingAttr);
			FieldInfo[] fields = type.GetFields(bindingAttr);
			MemberInfo[] array = new MemberInfo[fields.Length + properties.Length];
			properties.CopyTo(array, 0);
			fields.CopyTo(array, properties.Length);
			return array;
		}

		internal static Type GetMemberType(MemberInfo member)
		{
			return member.MemberType switch
			{
				MemberTypes.Field => ((FieldInfo)member).FieldType, 
				MemberTypes.Property => ((PropertyInfo)member).PropertyType, 
				_ => null, 
			};
		}

		internal static bool IsAssignableFrom(Type target, Type type)
		{
			return target.IsAssignableFrom(type);
		}

		internal static Assembly GetAssembly(Type type)
		{
			return type.Assembly;
		}

		internal static byte[] GetBuffer(MemoryStream ms)
		{
			return ms.GetBuffer();
		}
	}
	internal enum ProtoTypeCode
	{
		Empty = 0,
		Unknown = 1,
		Boolean = 3,
		Char = 4,
		SByte = 5,
		Byte = 6,
		Int16 = 7,
		UInt16 = 8,
		Int32 = 9,
		UInt32 = 10,
		Int64 = 11,
		UInt64 = 12,
		Single = 13,
		Double = 14,
		Decimal = 15,
		DateTime = 16,
		String = 18,
		TimeSpan = 100,
		ByteArray = 101,
		Guid = 102,
		Uri = 103,
		Type = 104
	}
	public interface IExtensible
	{
		IExtension GetExtensionObject(bool createIfMissing);
	}
	public interface IExtension
	{
		Stream BeginAppend();

		void EndAppend(Stream stream, bool commit);

		Stream BeginQuery();

		void EndQuery(Stream stream);

		int GetLength();
	}
	public interface IExtensionResettable : IExtension
	{
		void Reset();
	}
	public enum ImplicitFields
	{
		None,
		AllPublic,
		AllFields
	}
	public interface IProtoInput<TInput>
	{
		T Deserialize<T>(TInput source, T value = default(T), object userState = null);
	}
	public interface IProtoOutput<TOutput>
	{
		void Serialize<T>(TOutput destination, T value, object userState = null);
	}
	public interface IMeasuredProtoOutput<TOutput> : IProtoOutput<TOutput>
	{
		MeasureState<T> Measure<T>(T value, object userState = null);

		void Serialize<T>(MeasureState<T> measured, TOutput destination);
	}
	[StructLayout(LayoutKind.Sequential, Size = 1)]
	public struct MeasureState<T> : IDisposable
	{
		public long Length
		{
			get
			{
				throw new NotImplementedException();
			}
		}

		public void Dispose()
		{
			throw new NotImplementedException();
		}
	}
	internal sealed class NetObjectCache
	{
		private sealed class ReferenceComparer : IEqualityComparer<object>
		{
			public static readonly ReferenceComparer Default = new ReferenceComparer();

			private ReferenceComparer()
			{
			}

			bool IEqualityComparer<object>.Equals(object x, object y)
			{
				return x == y;
			}

			int IEqualityComparer<object>.GetHashCode(object obj)
			{
				return RuntimeHelpers.GetHashCode(obj);
			}
		}

		internal const int Root = 0;

		private MutableList underlyingList;

		private object rootObject;

		private int trapStartIndex;

		private Dictionary<string, int> stringKeys;

		private Dictionary<object, int> objectKeys;

		private MutableList List => underlyingList ?? (underlyingList = new MutableList());

		internal object GetKeyedObject(int key)
		{
			if (key-- == 0)
			{
				if (rootObject == null)
				{
					throw new ProtoException("No root object assigned");
				}
				return rootObject;
			}
			BasicList list = List;
			if (key < 0 || key >= list.Count)
			{
				throw new ProtoException("Internal error; a missing key occurred");
			}
			object obj = list[key];
			if (obj == null)
			{
				throw new ProtoException("A deferred key does not have a value yet");
			}
			return obj;
		}

		internal void SetKeyedObject(int key, object value)
		{
			if (key-- == 0)
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}
				if (rootObject != null && rootObject != value)
				{
					throw new ProtoException("The root object cannot be reassigned");
				}
				rootObject = value;
				return;
			}
			MutableList list = List;
			if (key < list.Count)
			{
				object obj = list[key];
				if (obj == null)
				{
					list[key] = value;
				}
				else if (obj != value)
				{
					throw new ProtoException("Reference-tracked objects cannot change reference");
				}
			}
			else if (key != list.Add(value))
			{
				throw new ProtoException("Internal error; a key mismatch occurred");
			}
		}

		internal int AddObjectKey(object value, out bool existing)
		{
			if (value == null)
			{
				throw new ArgumentNullException("value");
			}
			if (value == rootObject)
			{
				existing = true;
				return 0;
			}
			string text = value as string;
			BasicList list = List;
			int value2;
			if (text == null)
			{
				if (objectKeys == null)
				{
					objectKeys = new Dictionary<object, int>(ReferenceComparer.Default);
					value2 = -1;
				}
				else if (!objectKeys.TryGetValue(value, out value2))
				{
					value2 = -1;
				}
			}
			else if (stringKeys == null)
			{
				stringKeys = new Dictionary<string, int>();
				value2 = -1;
			}
			else if (!stringKeys.TryGetValue(text, out value2))
			{
				value2 = -1;
			}
			if (!(existing = value2 >= 0))
			{
				value2 = list.Add(value);
				if (text == null)
				{
					objectKeys.Add(value, value2);
				}
				else
				{
					stringKeys.Add(text, value2);
				}
			}
			return value2 + 1;
		}

		internal void RegisterTrappedObject(object value)
		{
			if (rootObject == null)
			{
				rootObject = value;
			}
			else
			{
				if (underlyingList == null)
				{
					return;
				}
				for (int i = trapStartIndex; i < underlyingList.Count; i++)
				{
					trapStartIndex = i + 1;
					if (underlyingList[i] == null)
					{
						underlyingList[i] = value;
						break;
					}
				}
			}
		}

		internal void Clear()
		{
			trapStartIndex = 0;
			rootObject = null;
			if (underlyingList != null)
			{
				underlyingList.Clear();
			}
			if (stringKeys != null)
			{
				stringKeys.Clear();
			}
			if (objectKeys != null)
			{
				objectKeys.Clear();
			}
		}
	}
	public enum PrefixStyle
	{
		None,
		Base128,
		Fixed32,
		Fixed32BigEndian
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
	public sealed class ProtoContractAttribute : Attribute
	{
		private int implicitFirstTag;

		private ushort flags;

		private const ushort OPTIONS_InferTagFromName = 1;

		private const ushort OPTIONS_InferTagFromNameHasValue = 2;

		private const ushort OPTIONS_UseProtoMembersOnly = 4;

		private const ushort OPTIONS_SkipConstructor = 8;

		private const ushort OPTIONS_IgnoreListHandling = 16;

		private const ushort OPTIONS_AsReferenceDefault = 32;

		private const ushort OPTIONS_EnumPassthru = 64;

		private const ushort OPTIONS_EnumPassthruHasValue = 128;

		private const ushort OPTIONS_IsGroup = 256;

		public string Name { get; set; }

		public int ImplicitFirstTag
		{
			get
			{
				return implicitFirstTag;
			}
			set
			{
				if (value < 1)
				{
					throw new ArgumentOutOfRangeException("ImplicitFirstTag");
				}
				implicitFirstTag = value;
			}
		}

		public bool UseProtoMembersOnly
		{
			get
			{
				return HasFlag(4);
			}
			set
			{
				SetFlag(4, value);
			}
		}

		public bool IgnoreListHandling
		{
			get
			{
				return HasFlag(16);
			}
			set
			{
				SetFlag(16, value);
			}
		}

		public ImplicitFields ImplicitFields { get; set; }

		public bool InferTagFromName
		{
			get
			{
				return HasFlag(1);
			}
			set
			{
				SetFlag(1, value);
				SetFlag(2, value: true);
			}
		}

		internal bool InferTagFromNameHasValue => HasFlag(2);

		public int DataMemberOffset { get; set; }

		public bool SkipConstructor
		{
			get
			{
				return HasFlag(8);
			}
			set
			{
				SetFlag(8, value);
			}
		}

		public bool AsReferenceDefault
		{
			get
			{
				return HasFlag(32);
			}
			set
			{
				SetFlag(32, value);
			}
		}

		public bool IsGroup
		{
			get
			{
				return HasFlag(256);
			}
			set
			{
				SetFlag(256, value);
			}
		}

		public bool EnumPassthru
		{
			get
			{
				return HasFlag(64);
			}
			set
			{
				SetFlag(64, value);
				SetFlag(128, value: true);
			}
		}

		public Type Surrogate { get; set; }

		internal bool EnumPassthruHasValue => HasFlag(128);

		private bool HasFlag(ushort flag)
		{
			return (flags & flag) == flag;
		}

		private void SetFlag(ushort flag, bool value)
		{
			if (value)
			{
				flags |= flag;
			}
			else
			{
				flags = (ushort)(flags & ~flag);
			}
		}
	}
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
	public class ProtoConverterAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
	public sealed class ProtoEnumAttribute : Attribute
	{
		private bool hasValue;

		private int enumValue;

		public int Value
		{
			get
			{
				return enumValue;
			}
			set
			{
				enumValue = value;
				hasValue = true;
			}
		}

		public string Name { get; set; }

		public bool HasValue()
		{
			return hasValue;
		}
	}
	[Serializable]
	public class ProtoException : Exception
	{
		public ProtoException()
		{
		}

		public ProtoException(string message)
			: base(message)
		{
		}

		public ProtoException(string message, Exception innerException)
			: base(message, innerException)
		{
		}

		protected ProtoException(SerializationInfo info, StreamingContext context)
			: base(info, context)
		{
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class ProtoIgnoreAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
	public sealed class ProtoPartialIgnoreAttribute : ProtoIgnoreAttribute
	{
		public string MemberName { get; }

		public ProtoPartialIgnoreAttribute(string memberName)
		{
			if (string.IsNullOrEmpty(memberName))
			{
				throw new ArgumentNullException("memberName");
			}
			MemberName = memberName;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)]
	public sealed class ProtoIncludeAttribute : Attribute
	{
		public int Tag { get; }

		public string KnownTypeName { get; }

		public Type KnownType => TypeModel.ResolveKnownType(KnownTypeName, null, null);

		[DefaultValue(DataFormat.Default)]
		public DataFormat DataFormat { get; set; }

		public ProtoIncludeAttribute(int tag, Type knownType)
			: this(tag, (knownType == null) ? "" : knownType.AssemblyQualifiedName)
		{
		}

		public ProtoIncludeAttribute(int tag, string knownTypeName)
		{
			if (tag <= 0)
			{
				throw new ArgumentOutOfRangeException("tag", "Tags must be positive integers");
			}
			if (string.IsNullOrEmpty(knownTypeName))
			{
				throw new ArgumentNullException("knownTypeName", "Known type cannot be blank");
			}
			Tag = tag;
			KnownTypeName = knownTypeName;
		}
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
	public class ProtoMapAttribute : Attribute
	{
		public DataFormat KeyFormat { get; set; }

		public DataFormat ValueFormat { get; set; }

		public bool DisableMap { get; set; }
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class ProtoMemberAttribute : Attribute, IComparable, IComparable<ProtoMemberAttribute>
	{
		internal MemberInfo Member;

		internal MemberInfo BackingMember;

		internal bool TagIsPinned;

		private string name;

		private DataFormat dataFormat;

		private int tag;

		private MemberSerializationOptions options;

		public string Name
		{
			get
			{
				return name;
			}
			set
			{
				name = value;
			}
		}

		public DataFormat DataFormat
		{
			get
			{
				return dataFormat;
			}
			set
			{
				dataFormat = value;
			}
		}

		public int Tag => tag;

		public bool IsRequired
		{
			get
			{
				return (options & MemberSerializationOptions.Required) == MemberSerializationOptions.Required;
			}
			set
			{
				if (value)
				{
					options |= MemberSerializationOptions.Required;
				}
				else
				{
					options &= ~MemberSerializationOptions.Required;
				}
			}
		}

		public bool IsPacked
		{
			get
			{
				return (options & MemberSerializationOptions.Packed) == MemberSerializationOptions.Packed;
			}
			set
			{
				if (value)
				{
					options |= MemberSerializationOptions.Packed;
				}
				else
				{
					options &= ~MemberSerializationOptions.Packed;
				}
			}
		}

		public bool OverwriteList
		{
			get
			{
				return (options & MemberSerializationOptions.OverwriteList) == MemberSerializationOptions.OverwriteList;
			}
			set
			{
				if (value)
				{
					options |= MemberSerializationOptions.OverwriteList;
				}
				else
				{
					options &= ~MemberSerializationOptions.OverwriteList;
				}
			}
		}

		public bool AsReference
		{
			get
			{
				return (options & MemberSerializationOptions.AsReference) == MemberSerializationOptions.AsReference;
			}
			set
			{
				if (value)
				{
					options |= MemberSerializationOptions.AsReference;
				}
				else
				{
					options &= ~MemberSerializationOptions.AsReference;
				}
				options |= MemberSerializationOptions.AsReferenceHasValue;
			}
		}

		internal bool AsReferenceHasValue
		{
			get
			{
				return (options & MemberSerializationOptions.AsReferenceHasValue) == MemberSerializationOptions.AsReferenceHasValue;
			}
			set
			{
				if (value)
				{
					options |= MemberSerializationOptions.AsReferenceHasValue;
				}
				else
				{
					options &= ~MemberSerializationOptions.AsReferenceHasValue;
				}
			}
		}

		public bool DynamicType
		{
			get
			{
				return (options & MemberSerializationOptions.DynamicType) == MemberSerializationOptions.DynamicType;
			}
			set
			{
				if (value)
				{
					options |= MemberSerializationOptions.DynamicType;
				}
				else
				{
					options &= ~MemberSerializationOptions.DynamicType;
				}
			}
		}

		public MemberSerializationOptions Options
		{
			get
			{
				return options;
			}
			set
			{
				options = value;
			}
		}

		public int CompareTo(object other)
		{
			return CompareTo(other as ProtoMemberAttribute);
		}

		public int CompareTo(ProtoMemberAttribute other)
		{
			if (other == null)
			{
				return -1;
			}
			if (this == other)
			{
				return 0;
			}
			int num = tag.CompareTo(other.tag);
			if (num == 0)
			{
				num = string.CompareOrdinal(name, other.name);
			}
			return num;
		}

		public ProtoMemberAttribute(int tag)
			: this(tag, forced: false)
		{
		}

		internal ProtoMemberAttribute(int tag, bool forced)
		{
			if (tag <= 0 && !forced)
			{
				throw new ArgumentOutOfRangeException("tag");
			}
			this.tag = tag;
		}

		internal void Rebase(int tag)
		{
			this.tag = tag;
		}
	}
	[Flags]
	public enum MemberSerializationOptions
	{
		None = 0,
		Packed = 1,
		Required = 2,
		AsReference = 4,
		DynamicType = 8,
		OverwriteList = 0x10,
		AsReferenceHasValue = 0x20
	}
	[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
	public sealed class ProtoPartialMemberAttribute : ProtoMemberAttribute
	{
		public string MemberName { get; private set; }

		public ProtoPartialMemberAttribute(int tag, string memberName)
			: base(tag)
		{
			if (string.IsNullOrEmpty(memberName))
			{
				throw new ArgumentNullException("memberName");
			}
			MemberName = memberName;
		}
	}
	public sealed class ProtoReader : IDisposable
	{
		private Stream source;

		private byte[] ioBuffer;

		private TypeModel model;

		private int fieldNumber;

		private int depth;

		private int ioIndex;

		private int available;

		private long position64;

		private long blockEnd64;

		private long dataRemaining64;

		private WireType wireType;

		private bool isFixedLength;

		private bool internStrings;

		private NetObjectCache netCache;

		private uint trapCount;

		internal const long TO_EOF = -1L;

		private SerializationContext context;

		private const long Int64Msb = long.MinValue;

		private const int Int32Msb = int.MinValue;

		private Dictionary<string, string> stringInterner;

		private static readonly UTF8Encoding encoding = new UTF8Encoding();

		private static readonly byte[] EmptyBlob = new byte[0];

		[ThreadStatic]
		private static ProtoReader lastReader;

		public int FieldNumber => fieldNumber;

		public WireType WireType => wireType;

		public bool InternStrings
		{
			get
			{
				return internStrings;
			}
			set
			{
				internStrings = value;
			}
		}

		public SerializationContext Context => context;

		public int Position => checked((int)position64);

		public long LongPosition => position64;

		public TypeModel Model => model;

		internal NetObjectCache NetCache => netCache;

		[Obsolete("Please use ProtoReader.Create; this API may be removed in a future version", false)]
		public ProtoReader(Stream source, TypeModel model, SerializationContext context)
		{
			Init(this, source, model, context, -1L);
		}

		[Obsolete("Please use ProtoReader.Create; this API may be removed in a future version", false)]
		public ProtoReader(Stream source, TypeModel model, SerializationContext context, int length)
		{
			Init(this, source, model, context, length);
		}

		[Obsolete("Please use ProtoReader.Create; this API may be removed in a future version", false)]
		public ProtoReader(Stream source, TypeModel model, SerializationContext context, long length)
		{
			Init(this, source, model, context, length);
		}

		private static void Init(ProtoReader reader, Stream source, TypeModel model, SerializationContext context, long length)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (!source.CanRead)
			{
				throw new ArgumentException("Cannot read from stream", "source");
			}
			reader.source = source;
			reader.ioBuffer = BufferPool.GetBuffer();
			reader.model = model;
			reader.dataRemaining64 = ((reader.isFixedLength = length >= 0) ? length : 0);
			if (context == null)
			{
				context = SerializationContext.Default;
			}
			else
			{
				context.Freeze();
			}
			reader.context = context;
			reader.position64 = 0L;
			reader.available = (reader.depth = (reader.fieldNumber = (reader.ioIndex = 0)));
			reader.blockEnd64 = long.MaxValue;
			reader.internStrings = RuntimeTypeModel.Default.InternStrings;
			reader.wireType = WireType.None;
			reader.trapCount = 1u;
			if (reader.netCache == null)
			{
				reader.netCache = new NetObjectCache();
			}
		}

		public void Dispose()
		{
			source = null;
			model = null;
			BufferPool.ReleaseBufferToPool(ref ioBuffer);
			if (stringInterner != null)
			{
				stringInterner.Clear();
				stringInterner = null;
			}
			if (netCache != null)
			{
				netCache.Clear();
			}
		}

		internal int TryReadUInt32VariantWithoutMoving(bool trimNegative, out uint value)
		{
			if (available < 10)
			{
				Ensure(10, strict: false);
			}
			if (available == 0)
			{
				value = 0u;
				return 0;
			}
			int num = ioIndex;
			value = ioBuffer[num++];
			if ((value & 0x80) == 0)
			{
				return 1;
			}
			value &= 127u;
			if (available == 1)
			{
				throw EoF(this);
			}
			uint num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 7;
			if ((num2 & 0x80) == 0)
			{
				return 2;
			}
			if (available == 2)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 14;
			if ((num2 & 0x80) == 0)
			{
				return 3;
			}
			if (available == 3)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 21;
			if ((num2 & 0x80) == 0)
			{
				return 4;
			}
			if (available == 4)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num];
			value |= num2 << 28;
			if ((num2 & 0xF0) == 0)
			{
				return 5;
			}
			if (trimNegative && (num2 & 0xF0) == 240 && available >= 10 && ioBuffer[++num] == byte.MaxValue && ioBuffer[++num] == byte.MaxValue && ioBuffer[++num] == byte.MaxValue && ioBuffer[++num] == byte.MaxValue && ioBuffer[++num] == 1)
			{
				return 10;
			}
			throw AddErrorData(new OverflowException(), this);
		}

		private uint ReadUInt32Variant(bool trimNegative)
		{
			uint value;
			int num = TryReadUInt32VariantWithoutMoving(trimNegative, out value);
			if (num > 0)
			{
				ioIndex += num;
				available -= num;
				position64 += num;
				return value;
			}
			throw EoF(this);
		}

		private bool TryReadUInt32Variant(out uint value)
		{
			int num = TryReadUInt32VariantWithoutMoving(trimNegative: false, out value);
			if (num > 0)
			{
				ioIndex += num;
				available -= num;
				position64 += num;
				return true;
			}
			return false;
		}

		public uint ReadUInt32()
		{
			switch (wireType)
			{
			case WireType.Variant:
				return ReadUInt32Variant(trimNegative: false);
			case WireType.Fixed32:
				if (available < 4)
				{
					Ensure(4, strict: true);
				}
				position64 += 4L;
				available -= 4;
				return (uint)(ioBuffer[ioIndex++] | (ioBuffer[ioIndex++] << 8) | (ioBuffer[ioIndex++] << 16) | (ioBuffer[ioIndex++] << 24));
			case WireType.Fixed64:
			{
				ulong num = ReadUInt64();
				return checked((uint)num);
			}
			default:
				throw CreateWireTypeException();
			}
		}

		internal void Ensure(int count, bool strict)
		{
			if (count > ioBuffer.Length)
			{
				BufferPool.ResizeAndFlushLeft(ref ioBuffer, count, ioIndex, available);
				ioIndex = 0;
			}
			else if (ioIndex + count >= ioBuffer.Length)
			{
				Buffer.BlockCopy(ioBuffer, ioIndex, ioBuffer, 0, available);
				ioIndex = 0;
			}
			count -= available;
			int num = ioIndex + available;
			int num2 = ioBuffer.Length - num;
			if (isFixedLength && dataRemaining64 < num2)
			{
				num2 = (int)dataRemaining64;
			}
			int num3;
			while (count > 0 && num2 > 0 && (num3 = source.Read(ioBuffer, num, num2)) > 0)
			{
				available += num3;
				count -= num3;
				num2 -= num3;
				num += num3;
				if (isFixedLength)
				{
					dataRemaining64 -= num3;
				}
			}
			if (strict && count > 0)
			{
				throw EoF(this);
			}
		}

		public short ReadInt16()
		{
			return checked((short)ReadInt32());
		}

		public ushort ReadUInt16()
		{
			return checked((ushort)ReadUInt32());
		}

		public byte ReadByte()
		{
			return checked((byte)ReadUInt32());
		}

		public sbyte ReadSByte()
		{
			return checked((sbyte)ReadInt32());
		}

		public int ReadInt32()
		{
			switch (wireType)
			{
			case WireType.Variant:
				return (int)ReadUInt32Variant(trimNegative: true);
			case WireType.Fixed32:
				if (available < 4)
				{
					Ensure(4, strict: true);
				}
				position64 += 4L;
				available -= 4;
				return ioBuffer[ioIndex++] | (ioBuffer[ioIndex++] << 8) | (ioBuffer[ioIndex++] << 16) | (ioBuffer[ioIndex++] << 24);
			case WireType.Fixed64:
			{
				long num = ReadInt64();
				return checked((int)num);
			}
			case WireType.SignedVariant:
				return Zag(ReadUInt32Variant(trimNegative: true));
			default:
				throw CreateWireTypeException();
			}
		}

		private static int Zag(uint ziggedValue)
		{
			return (int)(0 - (ziggedValue & 1)) ^ (((int)ziggedValue >> 1) & 0x7FFFFFFF);
		}

		private static long Zag(ulong ziggedValue)
		{
			return (long)(0L - (ziggedValue & 1)) ^ (((long)ziggedValue >> 1) & 0x7FFFFFFFFFFFFFFFL);
		}

		public long ReadInt64()
		{
			switch (wireType)
			{
			case WireType.Variant:
				return (long)ReadUInt64Variant();
			case WireType.Fixed32:
				return ReadInt32();
			case WireType.Fixed64:
				if (available < 8)
				{
					Ensure(8, strict: true);
				}
				position64 += 8L;
				available -= 8;
				return (long)(ioBuffer[ioIndex++] | ((ulong)ioBuffer[ioIndex++] << 8) | ((ulong)ioBuffer[ioIndex++] << 16) | ((ulong)ioBuffer[ioIndex++] << 24) | ((ulong)ioBuffer[ioIndex++] << 32) | ((ulong)ioBuffer[ioIndex++] << 40) | ((ulong)ioBuffer[ioIndex++] << 48) | ((ulong)ioBuffer[ioIndex++] << 56));
			case WireType.SignedVariant:
				return Zag(ReadUInt64Variant());
			default:
				throw CreateWireTypeException();
			}
		}

		private int TryReadUInt64VariantWithoutMoving(out ulong value)
		{
			if (available < 10)
			{
				Ensure(10, strict: false);
			}
			if (available == 0)
			{
				value = 0uL;
				return 0;
			}
			int num = ioIndex;
			value = ioBuffer[num++];
			if ((value & 0x80) == 0L)
			{
				return 1;
			}
			value &= 127uL;
			if (available == 1)
			{
				throw EoF(this);
			}
			ulong num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 7;
			if ((num2 & 0x80) == 0L)
			{
				return 2;
			}
			if (available == 2)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 14;
			if ((num2 & 0x80) == 0L)
			{
				return 3;
			}
			if (available == 3)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 21;
			if ((num2 & 0x80) == 0L)
			{
				return 4;
			}
			if (available == 4)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 28;
			if ((num2 & 0x80) == 0L)
			{
				return 5;
			}
			if (available == 5)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 35;
			if ((num2 & 0x80) == 0L)
			{
				return 6;
			}
			if (available == 6)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 42;
			if ((num2 & 0x80) == 0L)
			{
				return 7;
			}
			if (available == 7)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 49;
			if ((num2 & 0x80) == 0L)
			{
				return 8;
			}
			if (available == 8)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num++];
			value |= (num2 & 0x7F) << 56;
			if ((num2 & 0x80) == 0L)
			{
				return 9;
			}
			if (available == 9)
			{
				throw EoF(this);
			}
			num2 = ioBuffer[num];
			value |= num2 << 63;
			if ((num2 & 0xFFFFFFFFFFFFFFFEuL) != 0L)
			{
				throw AddErrorData(new OverflowException(), this);
			}
			return 10;
		}

		private ulong ReadUInt64Variant()
		{
			ulong value;
			int num = TryReadUInt64VariantWithoutMoving(out value);
			if (num > 0)
			{
				ioIndex += num;
				available -= num;
				position64 += num;
				return value;
			}
			throw EoF(this);
		}

		private string Intern(string value)
		{
			if (value == null)
			{
				return null;
			}
			if (value.Length == 0)
			{
				return "";
			}
			string value2;
			if (stringInterner == null)
			{
				stringInterner = new Dictionary<string, string> { { value, value } };
			}
			else if (stringInterner.TryGetValue(value, out value2))
			{
				value = value2;
			}
			else
			{
				stringInterner.Add(value, value);
			}
			return value;
		}

		public string ReadString()
		{
			if (wireType == WireType.String)
			{
				int num = (int)ReadUInt32Variant(trimNegative: false);
				if (num == 0)
				{
					return "";
				}
				if (num < 0)
				{
					ThrowInvalidLength(num);
				}
				if (available < num)
				{
					Ensure(num, strict: true);
				}
				string text = encoding.GetString(ioBuffer, ioIndex, num);
				if (internStrings)
				{
					text = Intern(text);
				}
				available -= num;
				position64 += num;
				ioIndex += num;
				return text;
			}
			throw CreateWireTypeException();
		}

		public void ThrowEnumException(Type type, int value)
		{
			string text = ((type == null) ? "<null>" : type.FullName);
			throw AddErrorData(new ProtoException("No " + text + " enum is mapped to the wire-value " + value), this);
		}

		private void ThrowInvalidLength(long length)
		{
			throw AddErrorData(new InvalidOperationException("Invalid length: " + length), this);
		}

		private Exception CreateWireTypeException()
		{
			return CreateException("Invalid wire-type; this usually means you have over-written a file without truncating or setting the length; see https://stackoverflow.com/q/2152978/23354");
		}

		private Exception CreateException(string message)
		{
			return AddErrorData(new ProtoException(message), this);
		}

		public unsafe double ReadDouble()
		{
			switch (wireType)
			{
			case WireType.Fixed32:
				return ReadSingle();
			case WireType.Fixed64:
			{
				long num = ReadInt64();
				return *(double*)(&num);
			}
			default:
				throw CreateWireTypeException();
			}
		}

		public static object ReadObject(object value, int key, ProtoReader reader)
		{
			return ReadTypedObject(value, key, reader, null);
		}

		internal static object ReadTypedObject(object value, int key, ProtoReader reader, Type type)
		{
			if (reader.model == null)
			{
				throw AddErrorData(new InvalidOperationException("Cannot deserialize sub-objects unless a model is provided"), reader);
			}
			SubItemToken token = StartSubItem(reader);
			if (key >= 0)
			{
				value = reader.model.Deserialize(key, value, reader);
			}
			else if (!(type != null) || !reader.model.TryDeserializeAuxiliaryType(reader, DataFormat.Default, 1, type, ref value, skipOtherFields: true, asListItem: false, autoCreate: true, insideList: false, null))
			{
				TypeModel.ThrowUnexpectedType(type);
			}
			EndSubItem(token, reader);
			return value;
		}

		public static void EndSubItem(SubItemToken token, ProtoReader reader)
		{
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			long value = token.value64;
			WireType wireType = reader.wireType;
			if (wireType == WireType.EndGroup)
			{
				if (value >= 0)
				{
					throw AddErrorData(new ArgumentException("token"), reader);
				}
				if (-(int)value != reader.fieldNumber)
				{
					throw reader.CreateException("Wrong group was ended");
				}
				reader.wireType = WireType.None;
				reader.depth--;
			}
			else
			{
				if (value < reader.position64)
				{
					throw reader.CreateException($"Sub-message not read entirely; expected {value}, was {reader.position64}");
				}
				if (reader.blockEnd64 != reader.position64 && reader.blockEnd64 != long.MaxValue)
				{
					throw reader.CreateException("Sub-message not read correctly");
				}
				reader.blockEnd64 = value;
				reader.depth--;
			}
		}

		public static SubItemToken StartSubItem(ProtoReader reader)
		{
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			switch (reader.wireType)
			{
			case WireType.StartGroup:
				reader.wireType = WireType.None;
				reader.depth++;
				return new SubItemToken((long)(-reader.fieldNumber));
			case WireType.String:
			{
				long num = (long)reader.ReadUInt64Variant();
				if (num < 0)
				{
					reader.ThrowInvalidLength(num);
				}
				long value = reader.blockEnd64;
				reader.blockEnd64 = reader.position64 + num;
				reader.depth++;
				return new SubItemToken(value);
			}
			default:
				throw reader.CreateWireTypeException();
			}
		}

		public int ReadFieldHeader()
		{
			if (blockEnd64 <= position64 || wireType == WireType.EndGroup)
			{
				return 0;
			}
			if (TryReadUInt32Variant(out var value) && value != 0)
			{
				wireType = (WireType)(value & 7);
				fieldNumber = (int)(value >> 3);
				if (fieldNumber < 1)
				{
					throw new ProtoException("Invalid field in source data: " + fieldNumber);
				}
			}
			else
			{
				wireType = WireType.None;
				fieldNumber = 0;
			}
			if (wireType == WireType.EndGroup)
			{
				if (depth > 0)
				{
					return 0;
				}
				throw new ProtoException("Unexpected end-group in source data; this usually means the source data is corrupt");
			}
			return fieldNumber;
		}

		public bool TryReadFieldHeader(int field)
		{
			if (blockEnd64 <= position64 || this.wireType == WireType.EndGroup)
			{
				return false;
			}
			uint value;
			int num = TryReadUInt32VariantWithoutMoving(trimNegative: false, out value);
			WireType wireType;
			if (num > 0 && (int)value >> 3 == field && (wireType = (WireType)(value & 7)) != WireType.EndGroup)
			{
				this.wireType = wireType;
				fieldNumber = field;
				position64 += num;
				ioIndex += num;
				available -= num;
				return true;
			}
			return false;
		}

		public void Hint(WireType wireType)
		{
			if (this.wireType != wireType && (wireType & (WireType)7) == this.wireType)
			{
				this.wireType = wireType;
			}
		}

		public void Assert(WireType wireType)
		{
			if (this.wireType != wireType)
			{
				if ((wireType & (WireType)7) != this.wireType)
				{
					throw CreateWireTypeException();
				}
				this.wireType = wireType;
			}
		}

		public void SkipField()
		{
			switch (wireType)
			{
			case WireType.Fixed32:
				if (available < 4)
				{
					Ensure(4, strict: true);
				}
				available -= 4;
				ioIndex += 4;
				position64 += 4L;
				break;
			case WireType.Fixed64:
				if (available < 8)
				{
					Ensure(8, strict: true);
				}
				available -= 8;
				ioIndex += 8;
				position64 += 8L;
				break;
			case WireType.String:
			{
				long num2 = (long)ReadUInt64Variant();
				if (num2 < 0)
				{
					ThrowInvalidLength(num2);
				}
				if (num2 <= available)
				{
					available -= (int)num2;
					ioIndex += (int)num2;
					position64 += num2;
					break;
				}
				position64 += num2;
				num2 -= available;
				ioIndex = (available = 0);
				if (isFixedLength)
				{
					if (num2 > dataRemaining64)
					{
						throw EoF(this);
					}
					dataRemaining64 -= num2;
				}
				Seek(source, num2, ioBuffer);
				break;
			}
			case WireType.Variant:
			case WireType.SignedVariant:
				ReadUInt64Variant();
				break;
			case WireType.StartGroup:
			{
				int num = fieldNumber;
				depth++;
				while (ReadFieldHeader() > 0)
				{
					SkipField();
				}
				depth--;
				if (wireType == WireType.EndGroup && fieldNumber == num)
				{
					wireType = WireType.None;
					break;
				}
				throw CreateWireTypeException();
			}
			default:
				throw CreateWireTypeException();
			}
		}

		public ulong ReadUInt64()
		{
			switch (wireType)
			{
			case WireType.Variant:
				return ReadUInt64Variant();
			case WireType.Fixed32:
				return ReadUInt32();
			case WireType.Fixed64:
				if (available < 8)
				{
					Ensure(8, strict: true);
				}
				position64 += 8L;
				available -= 8;
				return ioBuffer[ioIndex++] | ((ulong)ioBuffer[ioIndex++] << 8) | ((ulong)ioBuffer[ioIndex++] << 16) | ((ulong)ioBuffer[ioIndex++] << 24) | ((ulong)ioBuffer[ioIndex++] << 32) | ((ulong)ioBuffer[ioIndex++] << 40) | ((ulong)ioBuffer[ioIndex++] << 48) | ((ulong)ioBuffer[ioIndex++] << 56);
			default:
				throw CreateWireTypeException();
			}
		}

		public unsafe float ReadSingle()
		{
			switch (wireType)
			{
			case WireType.Fixed32:
			{
				int num3 = ReadInt32();
				return *(float*)(&num3);
			}
			case WireType.Fixed64:
			{
				double num = ReadDouble();
				float num2 = (float)num;
				if (float.IsInfinity(num2) && !double.IsInfinity(num))
				{
					throw AddErrorData(new OverflowException(), this);
				}
				return num2;
			}
			default:
				throw CreateWireTypeException();
			}
		}

		public bool ReadBoolean()
		{
			return ReadUInt32() switch
			{
				0u => false, 
				1u => true, 
				_ => throw CreateException("Unexpected boolean value"), 
			};
		}

		public static byte[] AppendBytes(byte[] value, ProtoReader reader)
		{
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			switch (reader.wireType)
			{
			case WireType.String:
			{
				int num = (int)reader.ReadUInt32Variant(trimNegative: false);
				reader.wireType = WireType.None;
				if (num == 0)
				{
					return value ?? EmptyBlob;
				}
				if (num < 0)
				{
					reader.ThrowInvalidLength(num);
				}
				int num2;
				if (value == null || value.Length == 0)
				{
					num2 = 0;
					value = new byte[num];
				}
				else
				{
					num2 = value.Length;
					byte[] array = new byte[value.Length + num];
					Buffer.BlockCopy(value, 0, array, 0, value.Length);
					value = array;
				}
				reader.position64 += num;
				while (num > reader.available)
				{
					if (reader.available > 0)
					{
						Buffer.BlockCopy(reader.ioBuffer, reader.ioIndex, value, num2, reader.available);
						num -= reader.available;
						num2 += reader.available;
						reader.ioIndex = (reader.available = 0);
					}
					int num3 = ((num > reader.ioBuffer.Length) ? reader.ioBuffer.Length : num);
					if (num3 > 0)
					{
						reader.Ensure(num3, strict: true);
					}
				}
				if (num > 0)
				{
					Buffer.BlockCopy(reader.ioBuffer, reader.ioIndex, value, num2, num);
					reader.ioIndex += num;
					reader.available -= num;
				}
				return value;
			}
			case WireType.Variant:
				return new byte[0];
			default:
				throw reader.CreateWireTypeException();
			}
		}

		private static int ReadByteOrThrow(Stream source)
		{
			int num = source.ReadByte();
			if (num < 0)
			{
				throw EoF(null);
			}
			return num;
		}

		public static int ReadLengthPrefix(Stream source, bool expectHeader, PrefixStyle style, out int fieldNumber)
		{
			int bytesRead;
			return ReadLengthPrefix(source, expectHeader, style, out fieldNumber, out bytesRead);
		}

		public static int DirectReadLittleEndianInt32(Stream source)
		{
			return ReadByteOrThrow(source) | (ReadByteOrThrow(source) << 8) | (ReadByteOrThrow(source) << 16) | (ReadByteOrThrow(source) << 24);
		}

		public static int DirectReadBigEndianInt32(Stream source)
		{
			return (ReadByteOrThrow(source) << 24) | (ReadByteOrThrow(source) << 16) | (ReadByteOrThrow(source) << 8) | ReadByteOrThrow(source);
		}

		public static int DirectReadVarintInt32(Stream source)
		{
			ulong value;
			int num = TryReadUInt64Variant(source, out value);
			if (num <= 0)
			{
				throw EoF(null);
			}
			return checked((int)value);
		}

		public static void DirectReadBytes(Stream source, byte[] buffer, int offset, int count)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			int num;
			while (count > 0 && (num = source.Read(buffer, offset, count)) > 0)
			{
				count -= num;
				offset += num;
			}
			if (count > 0)
			{
				throw EoF(null);
			}
		}

		public static byte[] DirectReadBytes(Stream source, int count)
		{
			byte[] array = new byte[count];
			DirectReadBytes(source, array, 0, count);
			return array;
		}

		public static string DirectReadString(Stream source, int length)
		{
			byte[] array = new byte[length];
			DirectReadBytes(source, array, 0, length);
			return Encoding.UTF8.GetString(array, 0, length);
		}

		public static int ReadLengthPrefix(Stream source, bool expectHeader, PrefixStyle style, out int fieldNumber, out int bytesRead)
		{
			if (style == PrefixStyle.None)
			{
				bytesRead = (fieldNumber = 0);
				return int.MaxValue;
			}
			long num = ReadLongLengthPrefix(source, expectHeader, style, out fieldNumber, out bytesRead);
			return checked((int)num);
		}

		public static long ReadLongLengthPrefix(Stream source, bool expectHeader, PrefixStyle style, out int fieldNumber, out int bytesRead)
		{
			fieldNumber = 0;
			switch (style)
			{
			case PrefixStyle.None:
				bytesRead = 0;
				return long.MaxValue;
			case PrefixStyle.Base128:
			{
				bytesRead = 0;
				ulong value;
				int num2;
				if (expectHeader)
				{
					num2 = TryReadUInt64Variant(source, out value);
					bytesRead += num2;
					if (num2 > 0)
					{
						if ((value & 7) != 2)
						{
							throw new InvalidOperationException();
						}
						fieldNumber = (int)(value >> 3);
						num2 = TryReadUInt64Variant(source, out value);
						bytesRead += num2;
						if (bytesRead == 0)
						{
							throw EoF(null);
						}
						return (long)value;
					}
					bytesRead = 0;
					return -1L;
				}
				num2 = TryReadUInt64Variant(source, out value);
				bytesRead += num2;
				if (bytesRead >= 0)
				{
					return (long)value;
				}
				return -1L;
			}
			case PrefixStyle.Fixed32:
			{
				int num3 = source.ReadByte();
				if (num3 < 0)
				{
					bytesRead = 0;
					return -1L;
				}
				bytesRead = 4;
				return num3 | (ReadByteOrThrow(source) << 8) | (ReadByteOrThrow(source) << 16) | (ReadByteOrThrow(source) << 24);
			}
			case PrefixStyle.Fixed32BigEndian:
			{
				int num = source.ReadByte();
				if (num < 0)
				{
					bytesRead = 0;
					return -1L;
				}
				bytesRead = 4;
				return (num << 24) | (ReadByteOrThrow(source) << 16) | (ReadByteOrThrow(source) << 8) | ReadByteOrThrow(source);
			}
			default:
				throw new ArgumentOutOfRangeException("style");
			}
		}

		private static int TryReadUInt64Variant(Stream source, out ulong value)
		{
			value = 0uL;
			int num = source.ReadByte();
			if (num < 0)
			{
				return 0;
			}
			value = (uint)num;
			if ((value & 0x80) == 0L)
			{
				return 1;
			}
			value &= 127uL;
			int num2 = 1;
			int num3 = 7;
			while (num2 < 9)
			{
				num = source.ReadByte();
				if (num < 0)
				{
					throw EoF(null);
				}
				value |= ((ulong)num & 0x7FuL) << num3;
				num3 += 7;
				num2++;
				if ((num & 0x80) == 0)
				{
					return num2;
				}
			}
			num = source.ReadByte();
			if (num < 0)
			{
				throw EoF(null);
			}
			if ((num & 1) == 0)
			{
				value |= ((ulong)num & 0x7FuL) << num3;
				return ++num2;
			}
			throw new OverflowException();
		}

		internal static void Seek(Stream source, long count, byte[] buffer)
		{
			if (source.CanSeek)
			{
				source.Seek(count, SeekOrigin.Current);
				count = 0L;
			}
			else if (buffer != null)
			{
				int num;
				while (count > buffer.Length && (num = source.Read(buffer, 0, buffer.Length)) > 0)
				{
					count -= num;
				}
				while (count > 0 && (num = source.Read(buffer, 0, (int)count)) > 0)
				{
					count -= num;
				}
			}
			else
			{
				buffer = BufferPool.GetBuffer();
				try
				{
					int num2;
					while (count > buffer.Length && (num2 = source.Read(buffer, 0, buffer.Length)) > 0)
					{
						count -= num2;
					}
					while (count > 0 && (num2 = source.Read(buffer, 0, (int)count)) > 0)
					{
						count -= num2;
					}
				}
				finally
				{
					BufferPool.ReleaseBufferToPool(ref buffer);
				}
			}
			if (count > 0)
			{
				throw EoF(null);
			}
		}

		internal static Exception AddErrorData(Exception exception, ProtoReader source)
		{
			if (exception != null && source != null && !exception.Data.Contains("protoSource"))
			{
				exception.Data.Add("protoSource", string.Format("tag={0}; wire-type={1}; offset={2}; depth={3}", new object[4] { source.fieldNumber, source.wireType, source.position64, source.depth }));
			}
			return exception;
		}

		private static Exception EoF(ProtoReader source)
		{
			return AddErrorData(new EndOfStreamException(), source);
		}

		public void AppendExtensionData(IExtensible instance)
		{
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			IExtension extensionObject = instance.GetExtensionObject(createIfMissing: true);
			bool commit = false;
			Stream stream = extensionObject.BeginAppend();
			try
			{
				using (ProtoWriter protoWriter = ProtoWriter.Create(stream, model))
				{
					AppendExtensionField(protoWriter);
					protoWriter.Close();
				}
				commit = true;
			}
			finally
			{
				extensionObject.EndAppend(stream, commit);
			}
		}

		private void AppendExtensionField(ProtoWriter writer)
		{
			ProtoWriter.WriteFieldHeader(fieldNumber, wireType, writer);
			switch (wireType)
			{
			case WireType.Fixed32:
				ProtoWriter.WriteInt32(ReadInt32(), writer);
				break;
			case WireType.Variant:
			case WireType.Fixed64:
			case WireType.SignedVariant:
				ProtoWriter.WriteInt64(ReadInt64(), writer);
				break;
			case WireType.String:
				ProtoWriter.WriteBytes(AppendBytes(null, this), writer);
				break;
			case WireType.StartGroup:
			{
				SubItemToken token = StartSubItem(this);
				SubItemToken token2 = ProtoWriter.StartSubItem(null, writer);
				while (ReadFieldHeader() > 0)
				{
					AppendExtensionField(writer);
				}
				EndSubItem(token, this);
				ProtoWriter.EndSubItem(token2, writer);
				break;
			}
			default:
				throw CreateWireTypeException();
			}
		}

		public static bool HasSubValue(WireType wireType, ProtoReader source)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			if (source.blockEnd64 <= source.position64 || wireType == WireType.EndGroup)
			{
				return false;
			}
			source.wireType = wireType;
			return true;
		}

		internal int GetTypeKey(ref Type type)
		{
			return model.GetKey(ref type);
		}

		internal Type DeserializeType(string value)
		{
			return TypeModel.DeserializeType(model, value);
		}

		internal void SetRootObject(object value)
		{
			netCache.SetKeyedObject(0, value);
			trapCount--;
		}

		public static void NoteObject(object value, ProtoReader reader)
		{
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			if (reader.trapCount != 0)
			{
				reader.netCache.RegisterTrappedObject(value);
				reader.trapCount--;
			}
		}

		public Type ReadType()
		{
			return TypeModel.DeserializeType(model, ReadString());
		}

		internal void TrapNextObject(int newObjectKey)
		{
			trapCount++;
			netCache.SetKeyedObject(newObjectKey, null);
		}

		internal void CheckFullyConsumed()
		{
			if (isFixedLength)
			{
				if (dataRemaining64 != 0L)
				{
					throw new ProtoException("Incorrect number of bytes consumed");
				}
			}
			else if (available != 0)
			{
				throw new ProtoException("Unconsumed data left in the buffer; this suggests corrupt input");
			}
		}

		public static object Merge(ProtoReader parent, object from, object to)
		{
			if (parent == null)
			{
				throw new ArgumentNullException("parent");
			}
			TypeModel typeModel = parent.Model;
			SerializationContext serializationContext = parent.Context;
			if (typeModel == null)
			{
				throw new InvalidOperationException("Types cannot be merged unless a type-model has been specified");
			}
			using MemoryStream memoryStream = new MemoryStream();
			typeModel.Serialize(memoryStream, from, serializationContext);
			memoryStream.Position = 0L;
			return typeModel.Deserialize(memoryStream, to, null);
		}

		internal static ProtoReader Create(Stream source, TypeModel model, SerializationContext context, int len)
		{
			return Create(source, model, context, (long)len);
		}

		public static ProtoReader Create(Stream source, TypeModel model, SerializationContext context = null, long length = -1L)
		{
			ProtoReader recycled = GetRecycled();
			if (recycled == null)
			{
				return new ProtoReader(source, model, context, length);
			}
			Init(recycled, source, model, context, length);
			return recycled;
		}

		private static ProtoReader GetRecycled()
		{
			ProtoReader result = lastReader;
			lastReader = null;
			return result;
		}

		internal static void Recycle(ProtoReader reader)
		{
			if (reader != null)
			{
				reader.Dispose();
				lastReader = reader;
			}
		}
	}
	public sealed class ProtoWriter : IDisposable
	{
		private Stream dest;

		private TypeModel model;

		private readonly NetObjectCache netCache = new NetObjectCache();

		private int fieldNumber;

		private int flushLock;

		private WireType wireType;

		private int depth;

		private const int RecursionCheckDepth = 25;

		private MutableList recursionStack;

		private readonly SerializationContext context;

		private byte[] ioBuffer;

		private int ioIndex;

		private long position64;

		private static readonly UTF8Encoding encoding = new UTF8Encoding();

		private int packedFieldNumber;

		internal NetObjectCache NetCache => netCache;

		internal WireType WireType => wireType;

		public SerializationContext Context => context;

		public TypeModel Model => model;

		public static void WriteObject(object value, int key, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			if (writer.model == null)
			{
				throw new InvalidOperationException("Cannot serialize sub-objects unless a model is provided");
			}
			SubItemToken token = StartSubItem(value, writer);
			if (key >= 0)
			{
				writer.model.Serialize(key, value, writer);
			}
			else if (writer.model == null || !writer.model.TrySerializeAuxiliaryType(writer, value.GetType(), DataFormat.Default, 1, value, isInsideList: false, null))
			{
				TypeModel.ThrowUnexpectedType(value.GetType());
			}
			EndSubItem(token, writer);
		}

		public static void WriteRecursionSafeObject(object value, int key, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			if (writer.model == null)
			{
				throw new InvalidOperationException("Cannot serialize sub-objects unless a model is provided");
			}
			SubItemToken token = StartSubItem(null, writer);
			writer.model.Serialize(key, value, writer);
			EndSubItem(token, writer);
		}

		internal static void WriteObject(object value, int key, ProtoWriter writer, PrefixStyle style, int fieldNumber)
		{
			if (writer.model == null)
			{
				throw new InvalidOperationException("Cannot serialize sub-objects unless a model is provided");
			}
			if (writer.wireType != WireType.None)
			{
				throw CreateException(writer);
			}
			switch (style)
			{
			case PrefixStyle.Base128:
				writer.wireType = WireType.String;
				writer.fieldNumber = fieldNumber;
				if (fieldNumber > 0)
				{
					WriteHeaderCore(fieldNumber, WireType.String, writer);
				}
				break;
			case PrefixStyle.Fixed32:
			case PrefixStyle.Fixed32BigEndian:
				writer.fieldNumber = 0;
				writer.wireType = WireType.Fixed32;
				break;
			default:
				throw new ArgumentOutOfRangeException("style");
			}
			SubItemToken token = StartSubItem(value, writer, allowFixed: true);
			if (key < 0)
			{
				if (!writer.model.TrySerializeAuxiliaryType(writer, value.GetType(), DataFormat.Default, 1, value, isInsideList: false, null))
				{
					TypeModel.ThrowUnexpectedType(value.GetType());
				}
			}
			else
			{
				writer.model.Serialize(key, value, writer);
			}
			EndSubItem(token, writer, style);
		}

		internal int GetTypeKey(ref Type type)
		{
			return model.GetKey(ref type);
		}

		public static void WriteFieldHeader(int fieldNumber, WireType wireType, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			if (writer.wireType != WireType.None)
			{
				throw new InvalidOperationException("Cannot write a " + wireType.ToString() + " header until the " + writer.wireType.ToString() + " data has been written");
			}
			if (fieldNumber < 0)
			{
				throw new ArgumentOutOfRangeException("fieldNumber");
			}
			if (writer.packedFieldNumber == 0)
			{
				writer.fieldNumber = fieldNumber;
				writer.wireType = wireType;
				WriteHeaderCore(fieldNumber, wireType, writer);
				return;
			}
			if (writer.packedFieldNumber == fieldNumber)
			{
				if ((uint)wireType > 1u && wireType != WireType.Fixed32 && wireType != WireType.SignedVariant)
				{
					throw new InvalidOperationException("Wire-type cannot be encoded as packed: " + wireType);
				}
				writer.fieldNumber = fieldNumber;
				writer.wireType = wireType;
				return;
			}
			throw new InvalidOperationException("Field mismatch during packed encoding; expected " + writer.packedFieldNumber + " but received " + fieldNumber);
		}

		internal static void WriteHeaderCore(int fieldNumber, WireType wireType, ProtoWriter writer)
		{
			uint value = (uint)(fieldNumber << 3) | (uint)(wireType & (WireType)7);
			WriteUInt32Variant(value, writer);
		}

		public static void WriteBytes(byte[] data, ProtoWriter writer)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			WriteBytes(data, 0, data.Length, writer);
		}

		public static void WriteBytes(byte[] data, int offset, int length, ProtoWriter writer)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			switch (writer.wireType)
			{
			case WireType.Fixed32:
				if (length != 4)
				{
					throw new ArgumentException("length");
				}
				break;
			case WireType.Fixed64:
				if (length != 8)
				{
					throw new ArgumentException("length");
				}
				break;
			case WireType.String:
				WriteUInt32Variant((uint)length, writer);
				writer.wireType = WireType.None;
				if (length == 0)
				{
					return;
				}
				if (writer.flushLock == 0 && length > writer.ioBuffer.Length)
				{
					Flush(writer);
					writer.dest.Write(data, offset, length);
					writer.position64 += length;
					return;
				}
				break;
			default:
				throw CreateException(writer);
			}
			DemandSpace(length, writer);
			Buffer.BlockCopy(data, offset, writer.ioBuffer, writer.ioIndex, length);
			IncrementedAndReset(length, writer);
		}

		private static void CopyRawFromStream(Stream source, ProtoWriter writer)
		{
			byte[] array = writer.ioBuffer;
			int num = array.Length - writer.ioIndex;
			int num2 = 1;
			while (num > 0 && (num2 = source.Read(array, writer.ioIndex, num)) > 0)
			{
				writer.ioIndex += num2;
				writer.position64 += num2;
				num -= num2;
			}
			if (num2 <= 0)
			{
				return;
			}
			if (writer.flushLock == 0)
			{
				Flush(writer);
				while ((num2 = source.Read(array, 0, array.Length)) > 0)
				{
					writer.dest.Write(array, 0, num2);
					writer.position64 += num2;
				}
				return;
			}
			while (true)
			{
				DemandSpace(128, writer);
				if ((num2 = source.Read(writer.ioBuffer, writer.ioIndex, writer.ioBuffer.Length - writer.ioIndex)) > 0)
				{
					writer.position64 += num2;
					writer.ioIndex += num2;
					continue;
				}
				break;
			}
		}

		private static void IncrementedAndReset(int length, ProtoWriter writer)
		{
			writer.ioIndex += length;
			writer.position64 += length;
			writer.wireType = WireType.None;
		}

		public static SubItemToken StartSubItem(object instance, ProtoWriter writer)
		{
			return StartSubItem(instance, writer, allowFixed: false);
		}

		private void CheckRecursionStackAndPush(object instance)
		{
			int num;
			if (recursionStack == null)
			{
				recursionStack = new MutableList();
			}
			else if (instance != null && (num = recursionStack.IndexOfReference(instance)) >= 0)
			{
				throw new ProtoException("Possible recursion detected (offset: " + (recursionStack.Count - num) + " level(s)): " + instance.ToString());
			}
			recursionStack.Add(instance);
		}

		private void PopRecursionStack()
		{
			recursionStack.RemoveLast();
		}

		private static SubItemToken StartSubItem(object instance, ProtoWriter writer, bool allowFixed)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			if (++writer.depth > 25)
			{
				writer.CheckRecursionStackAndPush(instance);
			}
			if (writer.packedFieldNumber != 0)
			{
				throw new InvalidOperationException("Cannot begin a sub-item while performing packed encoding");
			}
			switch (writer.wireType)
			{
			case WireType.StartGroup:
				writer.wireType = WireType.None;
				return new SubItemToken((long)(-writer.fieldNumber));
			case WireType.String:
				writer.wireType = WireType.None;
				DemandSpace(32, writer);
				writer.flushLock++;
				writer.position64++;
				return new SubItemToken((long)writer.ioIndex++);
			case WireType.Fixed32:
			{
				if (!allowFixed)
				{
					throw CreateException(writer);
				}
				DemandSpace(32, writer);
				writer.flushLock++;
				SubItemToken result = new SubItemToken((long)writer.ioIndex);
				IncrementedAndReset(4, writer);
				return result;
			}
			default:
				throw CreateException(writer);
			}
		}

		public static void EndSubItem(SubItemToken token, ProtoWriter writer)
		{
			EndSubItem(token, writer, PrefixStyle.Base128);
		}

		private static void EndSubItem(SubItemToken token, ProtoWriter writer, PrefixStyle style)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			if (writer.wireType != WireType.None)
			{
				throw CreateException(writer);
			}
			int num = (int)token.value64;
			if (writer.depth <= 0)
			{
				throw CreateException(writer);
			}
			if (writer.depth-- > 25)
			{
				writer.PopRecursionStack();
			}
			writer.packedFieldNumber = 0;
			if (num < 0)
			{
				WriteHeaderCore(-num, WireType.EndGroup, writer);
				writer.wireType = WireType.None;
				return;
			}
			switch (style)
			{
			case PrefixStyle.Fixed32:
			{
				int num2 = writer.ioIndex - num - 4;
				WriteInt32ToBuffer(num2, writer.ioBuffer, num);
				break;
			}
			case PrefixStyle.Fixed32BigEndian:
			{
				int num2 = writer.ioIndex - num - 4;
				byte[] array2 = writer.ioBuffer;
				WriteInt32ToBuffer(num2, array2, num);
				byte b = array2[num];
				array2[num] = array2[num + 3];
				array2[num + 3] = b;
				b = array2[num + 1];
				array2[num + 1] = array2[num + 2];
				array2[num + 2] = b;
				break;
			}
			case PrefixStyle.Base128:
			{
				int num2 = writer.ioIndex - num - 1;
				int num3 = 0;
				uint num4 = (uint)num2;
				while ((num4 >>= 7) != 0)
				{
					num3++;
				}
				if (num3 == 0)
				{
					writer.ioBuffer[num] = (byte)(num2 & 0x7F);
					break;
				}
				DemandSpace(num3, writer);
				byte[] array = writer.ioBuffer;
				Buffer.BlockCopy(array, num + 1, array, num + 1 + num3, num2);
				num4 = (uint)num2;
				do
				{
					array[num++] = (byte)((num4 & 0x7F) | 0x80);
				}
				while ((num4 >>= 7) != 0);
				array[num - 1] = (byte)(array[num - 1] & -129);
				writer.position64 += num3;
				writer.ioIndex += num3;
				break;
			}
			default:
				throw new ArgumentOutOfRangeException("style");
			}
			if (--writer.flushLock == 0 && writer.ioIndex >= 1024)
			{
				Flush(writer);
			}
		}

		public static ProtoWriter Create(Stream dest, TypeModel model, SerializationContext context = null)
		{
			return new ProtoWriter(dest, model, context);
		}

		[Obsolete("Please use ProtoWriter.Create; this API may be removed in a future version", false)]
		public ProtoWriter(Stream dest, TypeModel model, SerializationContext context)
		{
			if (dest == null)
			{
				throw new ArgumentNullException("dest");
			}
			if (!dest.CanWrite)
			{
				throw new ArgumentException("Cannot write to stream", "dest");
			}
			this.dest = dest;
			ioBuffer = BufferPool.GetBuffer();
			this.model = model;
			wireType = WireType.None;
			if (context == null)
			{
				context = SerializationContext.Default;
			}
			else
			{
				context.Freeze();
			}
			this.context = context;
		}

		void IDisposable.Dispose()
		{
			Dispose();
		}

		private void Dispose()
		{
			if (dest != null)
			{
				Flush(this);
				dest = null;
			}
			model = null;
			BufferPool.ReleaseBufferToPool(ref ioBuffer);
		}

		internal static long GetLongPosition(ProtoWriter writer)
		{
			return writer.position64;
		}

		internal static int GetPosition(ProtoWriter writer)
		{
			return checked((int)writer.position64);
		}

		private static void DemandSpace(int required, ProtoWriter writer)
		{
			if (writer.ioBuffer.Length - writer.ioIndex < required)
			{
				TryFlushOrResize(required, writer);
			}
		}

		private static void TryFlushOrResize(int required, ProtoWriter writer)
		{
			if (writer.flushLock == 0)
			{
				Flush(writer);
				if (writer.ioBuffer.Length - writer.ioIndex >= required)
				{
					return;
				}
			}
			BufferPool.ResizeAndFlushLeft(ref writer.ioBuffer, required + writer.ioIndex, 0, writer.ioIndex);
		}

		public void Close()
		{
			if (depth != 0 || flushLock != 0)
			{
				throw new InvalidOperationException("Unable to close stream in an incomplete state");
			}
			Dispose();
		}

		internal void CheckDepthFlushlock()
		{
			if (depth != 0 || flushLock != 0)
			{
				throw new InvalidOperationException("The writer is in an incomplete state");
			}
		}

		internal static void Flush(ProtoWriter writer)
		{
			if (writer.flushLock == 0 && writer.ioIndex != 0)
			{
				writer.dest.Write(writer.ioBuffer, 0, writer.ioIndex);
				writer.ioIndex = 0;
			}
		}

		private static void WriteUInt32Variant(uint value, ProtoWriter writer)
		{
			DemandSpace(5, writer);
			int num = 0;
			do
			{
				writer.ioBuffer[writer.ioIndex++] = (byte)((value & 0x7F) | 0x80);
				num++;
			}
			while ((value >>= 7) != 0);
			writer.ioBuffer[writer.ioIndex - 1] &= 127;
			writer.position64 += num;
		}

		internal static uint Zig(int value)
		{
			return (uint)((value << 1) ^ (value >> 31));
		}

		internal static ulong Zig(long value)
		{
			return (ulong)((value << 1) ^ (value >> 63));
		}

		private static void WriteUInt64Variant(ulong value, ProtoWriter writer)
		{
			DemandSpace(10, writer);
			int num = 0;
			do
			{
				writer.ioBuffer[writer.ioIndex++] = (byte)((value & 0x7F) | 0x80);
				num++;
			}
			while ((value >>= 7) != 0L);
			writer.ioBuffer[writer.ioIndex - 1] &= 127;
			writer.position64 += num;
		}

		public static void WriteString(string value, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			if (writer.wireType != WireType.String)
			{
				throw CreateException(writer);
			}
			if (value == null)
			{
				throw new ArgumentNullException("value");
			}
			if (value.Length == 0)
			{
				WriteUInt32Variant(0u, writer);
				writer.wireType = WireType.None;
				return;
			}
			int byteCount = encoding.GetByteCount(value);
			WriteUInt32Variant((uint)byteCount, writer);
			DemandSpace(byteCount, writer);
			int bytes = encoding.GetBytes(value, 0, value.Length, writer.ioBuffer, writer.ioIndex);
			IncrementedAndReset(bytes, writer);
		}

		public static void WriteUInt64(ulong value, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			switch (writer.wireType)
			{
			case WireType.Fixed64:
				WriteInt64((long)value, writer);
				break;
			case WireType.Variant:
				WriteUInt64Variant(value, writer);
				writer.wireType = WireType.None;
				break;
			case WireType.Fixed32:
				WriteUInt32(checked((uint)value), writer);
				break;
			default:
				throw CreateException(writer);
			}
		}

		public static void WriteInt64(long value, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			switch (writer.wireType)
			{
			case WireType.Fixed64:
			{
				DemandSpace(8, writer);
				byte[] array = writer.ioBuffer;
				int num = writer.ioIndex;
				array[num] = (byte)value;
				array[num + 1] = (byte)(value >> 8);
				array[num + 2] = (byte)(value >> 16);
				array[num + 3] = (byte)(value >> 24);
				array[num + 4] = (byte)(value >> 32);
				array[num + 5] = (byte)(value >> 40);
				array[num + 6] = (byte)(value >> 48);
				array[num + 7] = (byte)(value >> 56);
				IncrementedAndReset(8, writer);
				break;
			}
			case WireType.SignedVariant:
				WriteUInt64Variant(Zig(value), writer);
				writer.wireType = WireType.None;
				break;
			case WireType.Variant:
			{
				if (value >= 0)
				{
					WriteUInt64Variant((ulong)value, writer);
					writer.wireType = WireType.None;
					break;
				}
				DemandSpace(10, writer);
				byte[] array = writer.ioBuffer;
				int num = writer.ioIndex;
				array[num] = (byte)(value | 0x80);
				array[num + 1] = (byte)((int)(value >> 7) | 0x80);
				array[num + 2] = (byte)((int)(value >> 14) | 0x80);
				array[num + 3] = (byte)((int)(value >> 21) | 0x80);
				array[num + 4] = (byte)((int)(value >> 28) | 0x80);
				array[num + 5] = (byte)((int)(value >> 35) | 0x80);
				array[num + 6] = (byte)((int)(value >> 42) | 0x80);
				array[num + 7] = (byte)((int)(value >> 49) | 0x80);
				array[num + 8] = (byte)((int)(value >> 56) | 0x80);
				array[num + 9] = 1;
				IncrementedAndReset(10, writer);
				break;
			}
			case WireType.Fixed32:
				WriteInt32(checked((int)value), writer);
				break;
			default:
				throw CreateException(writer);
			}
		}

		public static void WriteUInt32(uint value, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			switch (writer.wireType)
			{
			case WireType.Fixed32:
				WriteInt32((int)value, writer);
				break;
			case WireType.Fixed64:
				WriteInt64((int)value, writer);
				break;
			case WireType.Variant:
				WriteUInt32Variant(value, writer);
				writer.wireType = WireType.None;
				break;
			default:
				throw CreateException(writer);
			}
		}

		public static void WriteInt16(short value, ProtoWriter writer)
		{
			WriteInt32(value, writer);
		}

		public static void WriteUInt16(ushort value, ProtoWriter writer)
		{
			WriteUInt32(value, writer);
		}

		public static void WriteByte(byte value, ProtoWriter writer)
		{
			WriteUInt32(value, writer);
		}

		public static void WriteSByte(sbyte value, ProtoWriter writer)
		{
			WriteInt32(value, writer);
		}

		private static void WriteInt32ToBuffer(int value, byte[] buffer, int index)
		{
			buffer[index] = (byte)value;
			buffer[index + 1] = (byte)(value >> 8);
			buffer[index + 2] = (byte)(value >> 16);
			buffer[index + 3] = (byte)(value >> 24);
		}

		public static void WriteInt32(int value, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			switch (writer.wireType)
			{
			case WireType.Fixed32:
				DemandSpace(4, writer);
				WriteInt32ToBuffer(value, writer.ioBuffer, writer.ioIndex);
				IncrementedAndReset(4, writer);
				break;
			case WireType.Fixed64:
			{
				DemandSpace(8, writer);
				byte[] array = writer.ioBuffer;
				int num = writer.ioIndex;
				array[num] = (byte)value;
				array[num + 1] = (byte)(value >> 8);
				array[num + 2] = (byte)(value >> 16);
				array[num + 3] = (byte)(value >> 24);
				array[num + 4] = (array[num + 5] = (array[num + 6] = (array[num + 7] = 0)));
				IncrementedAndReset(8, writer);
				break;
			}
			case WireType.SignedVariant:
				WriteUInt32Variant(Zig(value), writer);
				writer.wireType = WireType.None;
				break;
			case WireType.Variant:
			{
				if (value >= 0)
				{
					WriteUInt32Variant((uint)value, writer);
					writer.wireType = WireType.None;
					break;
				}
				DemandSpace(10, writer);
				byte[] array = writer.ioBuffer;
				int num = writer.ioIndex;
				array[num] = (byte)(value | 0x80);
				array[num + 1] = (byte)((value >> 7) | 0x80);
				array[num + 2] = (byte)((value >> 14) | 0x80);
				array[num + 3] = (byte)((value >> 21) | 0x80);
				array[num + 4] = (byte)((value >> 28) | 0x80);
				byte[] array2 = array;
				int num2 = num + 5;
				byte[] array3 = array;
				int num3 = num + 6;
				byte[] array4 = array;
				int num4 = num + 7;
				byte b;
				array[num + 8] = (b = byte.MaxValue);
				array2[num2] = (array3[num3] = (array4[num4] = b));
				array[num + 9] = 1;
				IncrementedAndReset(10, writer);
				break;
			}
			default:
				throw CreateException(writer);
			}
		}

		public unsafe static void WriteDouble(double value, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			switch (writer.wireType)
			{
			case WireType.Fixed32:
			{
				float num = (float)value;
				if (float.IsInfinity(num) && !double.IsInfinity(value))
				{
					throw new OverflowException();
				}
				WriteSingle(num, writer);
				break;
			}
			case WireType.Fixed64:
				WriteInt64(*(long*)(&value), writer);
				break;
			default:
				throw CreateException(writer);
			}
		}

		public unsafe static void WriteSingle(float value, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			switch (writer.wireType)
			{
			case WireType.Fixed32:
				WriteInt32(*(int*)(&value), writer);
				break;
			case WireType.Fixed64:
				WriteDouble(value, writer);
				break;
			default:
				throw CreateException(writer);
			}
		}

		public static void ThrowEnumException(ProtoWriter writer, object enumValue)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			string text = ((enumValue == null) ? "<null>" : (enumValue.GetType().FullName + "." + enumValue.ToString()));
			throw new ProtoException("No wire-value is mapped to the enum " + text + " at position " + writer.position64);
		}

		internal static Exception CreateException(ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			return new ProtoException("Invalid serialization operation with wire-type " + writer.wireType.ToString() + " at position " + writer.position64);
		}

		public static void WriteBoolean(bool value, ProtoWriter writer)
		{
			WriteUInt32(value ? 1u : 0u, writer);
		}

		public static void AppendExtensionData(IExtensible instance, ProtoWriter writer)
		{
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			if (writer.wireType != WireType.None)
			{
				throw CreateException(writer);
			}
			IExtension extensionObject = instance.GetExtensionObject(createIfMissing: false);
			if (extensionObject != null)
			{
				Stream stream = extensionObject.BeginQuery();
				try
				{
					CopyRawFromStream(stream, writer);
				}
				finally
				{
					extensionObject.EndQuery(stream);
				}
			}
		}

		public static void SetPackedField(int fieldNumber, ProtoWriter writer)
		{
			if (fieldNumber <= 0)
			{
				throw new ArgumentOutOfRangeException("fieldNumber");
			}
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			writer.packedFieldNumber = fieldNumber;
		}

		public static void ClearPackedField(int fieldNumber, ProtoWriter writer)
		{
			if (fieldNumber != writer.packedFieldNumber)
			{
				throw new InvalidOperationException("Field mismatch during packed encoding; expected " + writer.packedFieldNumber + " but received " + fieldNumber);
			}
			writer.packedFieldNumber = 0;
		}

		public static void WritePackedPrefix(int elementCount, WireType wireType, ProtoWriter writer)
		{
			if (writer.WireType != WireType.String)
			{
				throw new InvalidOperationException("Invalid wire-type: " + writer.WireType);
			}
			if (elementCount < 0)
			{
				throw new ArgumentOutOfRangeException("elementCount");
			}
			WriteUInt64Variant(wireType switch
			{
				WireType.Fixed32 => (ulong)((long)elementCount << 2), 
				WireType.Fixed64 => (ulong)((long)elementCount << 3), 
				_ => throw new ArgumentOutOfRangeException("wireType", "Invalid wire-type: " + wireType), 
			}, writer);
			writer.wireType = WireType.None;
		}

		internal string SerializeType(Type type)
		{
			return TypeModel.SerializeType(model, type);
		}

		public void SetRootObject(object value)
		{
			NetCache.SetKeyedObject(0, value);
		}

		public static void WriteType(Type value, ProtoWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			WriteString(writer.SerializeType(value), writer);
		}
	}
	public sealed class SerializationContext
	{
		private bool frozen;

		private object context;

		private static readonly SerializationContext @default;

		private StreamingContextStates state = StreamingContextStates.Persistence;

		public object Context
		{
			get
			{
				return context;
			}
			set
			{
				if (context != value)
				{
					ThrowIfFrozen();
					context = value;
				}
			}
		}

		internal static SerializationContext Default => @default;

		public StreamingContextStates State
		{
			get
			{
				return state;
			}
			set
			{
				if (state != value)
				{
					ThrowIfFrozen();
					state = value;
				}
			}
		}

		internal void Freeze()
		{
			frozen = true;
		}

		private void ThrowIfFrozen()
		{
			if (frozen)
			{
				throw new InvalidOperationException("The serialization-context cannot be changed once it is in use");
			}
		}

		static SerializationContext()
		{
			@default = new SerializationContext();
			@default.Freeze();
		}

		public static implicit operator StreamingContext(SerializationContext ctx)
		{
			if (ctx == null)
			{
				return new StreamingContext(StreamingContextStates.Persistence);
			}
			return new StreamingContext(ctx.state, ctx.context);
		}

		public static implicit operator SerializationContext(StreamingContext ctx)
		{
			SerializationContext serializationContext = new SerializationContext();
			serializationContext.Context = ctx.Context;
			serializationContext.State = ctx.State;
			return serializationContext;
		}
	}
	public static class Serializer
	{
		public static class NonGeneric
		{
			public static object DeepClone(object instance)
			{
				if (instance != null)
				{
					return RuntimeTypeModel.Default.DeepClone(instance);
				}
				return null;
			}

			public static void Serialize(Stream dest, object instance)
			{
				if (instance != null)
				{
					RuntimeTypeModel.Default.Serialize(dest, instance);
				}
			}

			public static object Deserialize(Type type, Stream source)
			{
				return RuntimeTypeModel.Default.Deserialize(source, null, type);
			}

			public static object Merge(Stream source, object instance)
			{
				if (instance == null)
				{
					throw new ArgumentNullException("instance");
				}
				return RuntimeTypeModel.Default.Deserialize(source, instance, instance.GetType(), null);
			}

			public static void SerializeWithLengthPrefix(Stream destination, object instance, PrefixStyle style, int fieldNumber)
			{
				if (instance == null)
				{
					throw new ArgumentNullException("instance");
				}
				RuntimeTypeModel runtimeTypeModel = RuntimeTypeModel.Default;
				runtimeTypeModel.SerializeWithLengthPrefix(destination, instance, runtimeTypeModel.MapType(instance.GetType()), style, fieldNumber);
			}

			public static bool TryDeserializeWithLengthPrefix(Stream source, PrefixStyle style, TypeResolver resolver, out object value)
			{
				value = RuntimeTypeModel.Default.DeserializeWithLengthPrefix(source, null, null, style, 0, resolver);
				return value != null;
			}

			public static bool CanSerialize(Type type)
			{
				return RuntimeTypeModel.Default.IsDefined(type);
			}

			public static void PrepareSerializer(Type t)
			{
				RuntimeTypeModel runtimeTypeModel = RuntimeTypeModel.Default;
				runtimeTypeModel[runtimeTypeModel.MapType(t)].CompileInPlace();
			}
		}

		public static class GlobalOptions
		{
			[Obsolete("Please use RuntimeTypeModel.Default.InferTagFromNameDefault instead (or on a per-model basis)", false)]
			public static bool InferTagFromName
			{
				get
				{
					return RuntimeTypeModel.Default.InferTagFromNameDefault;
				}
				set
				{
					RuntimeTypeModel.Default.InferTagFromNameDefault = value;
				}
			}
		}

		public delegate Type TypeResolver(int fieldNumber);

		private const string ProtoBinaryField = "proto";

		public const int ListItemTag = 1;

		public static string GetProto<T>()
		{
			return GetProto<T>(ProtoSyntax.Proto2);
		}

		public static string GetProto<T>(ProtoSyntax syntax)
		{
			return RuntimeTypeModel.Default.GetSchema(RuntimeTypeModel.Default.MapType(typeof(T)), syntax);
		}

		public static T DeepClone<T>(T instance)
		{
			if (instance != null)
			{
				return (T)RuntimeTypeModel.Default.DeepClone(instance);
			}
			return instance;
		}

		public static T Merge<T>(Stream source, T instance)
		{
			return (T)RuntimeTypeModel.Default.Deserialize(source, instance, typeof(T));
		}

		public static T Deserialize<T>(Stream source)
		{
			return (T)RuntimeTypeModel.Default.Deserialize(source, null, typeof(T));
		}

		public static object Deserialize(Type type, Stream source)
		{
			return RuntimeTypeModel.Default.Deserialize(source, null, type);
		}

		public static void Serialize<T>(Stream destination, T instance)
		{
			if (instance != null)
			{
				RuntimeTypeModel.Default.Serialize(destination, instance);
			}
		}

		public static TTo ChangeType<TFrom, TTo>(TFrom instance)
		{
			using MemoryStream memoryStream = new MemoryStream();
			Serialize((Stream)memoryStream, instance);
			memoryStream.Position = 0L;
			return Deserialize<TTo>(memoryStream);
		}

		public static void Serialize<T>(SerializationInfo info, T instance) where T : class, ISerializable
		{
			Serialize(info, new StreamingContext(StreamingContextStates.Persistence), instance);
		}

		public static void Serialize<T>(SerializationInfo info, StreamingContext context, T instance) where T : class, ISerializable
		{
			if (info == null)
			{
				throw new ArgumentNullException("info");
			}
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			if (instance.GetType() != typeof(T))
			{
				throw new ArgumentException("Incorrect type", "instance");
			}
			using MemoryStream memoryStream = new MemoryStream();
			RuntimeTypeModel.Default.Serialize(memoryStream, instance, context);
			info.AddValue("proto", memoryStream.ToArray());
		}

		public static void Serialize<T>(XmlWriter writer, T instance) where T : IXmlSerializable
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			using MemoryStream memoryStream = new MemoryStream();
			Serialize((Stream)memoryStream, instance);
			writer.WriteBase64(Helpers.GetBuffer(memoryStream), 0, (int)memoryStream.Length);
		}

		public static void Merge<T>(XmlReader reader, T instance) where T : IXmlSerializable
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			byte[] array = new byte[4096];
			using MemoryStream memoryStream = new MemoryStream();
			int depth = reader.Depth;
			while (reader.Read() && reader.Depth > depth)
			{
				if ((int)reader.NodeType == 3)
				{
					int count;
					while ((count = reader.ReadContentAsBase64(array, 0, 4096)) > 0)
					{
						memoryStream.Write(array, 0, count);
					}
					if (reader.Depth <= depth)
					{
						break;
					}
				}
			}
			memoryStream.Position = 0L;
			Merge((Stream)memoryStream, instance);
		}

		public static void Merge<T>(SerializationInfo info, T instance) where T : class, ISerializable
		{
			Merge(info, new StreamingContext(StreamingContextStates.Persistence), instance);
		}

		public static void Merge<T>(SerializationInfo info, StreamingContext context, T instance) where T : class, ISerializable
		{
			if (info == null)
			{
				throw new ArgumentNullException("info");
			}
			if (instance == null)
			{
				throw new ArgumentNullException("instance");
			}
			if (instance.GetType() != typeof(T))
			{
				throw new ArgumentException("Incorrect type", "instance");
			}
			byte[] buffer = (byte[])info.GetValue("proto", typeof(byte[]));
			using MemoryStream source = new MemoryStream(buffer);
			T val = (T)RuntimeTypeModel.Default.Deserialize(source, instance, typeof(T), context);
			if (val != instance)
			{
				throw new ProtoException("Deserialization changed the instance; cannot succeed.");
			}
		}

		public static void PrepareSerializer<T>()
		{
			NonGeneric.PrepareSerializer(typeof(T));
		}

		public static IFormatter CreateFormatter<T>()
		{
			return RuntimeTypeModel.Default.CreateFormatter(typeof(T));
		}

		public static IEnumerable<T> DeserializeItems<T>(Stream source, PrefixStyle style, int fieldNumber)
		{
			return RuntimeTypeModel.Default.DeserializeItems<T>(source, style, fieldNumber);
		}

		public static T DeserializeWithLengthPrefix<T>(Stream source, PrefixStyle style)
		{
			return DeserializeWithLengthPrefix<T>(source, style, 0);
		}

		public static T DeserializeWithLengthPrefix<T>(Stream source, PrefixStyle style, int fieldNumber)
		{
			RuntimeTypeModel runtimeTypeModel = RuntimeTypeModel.Default;
			return (T)runtimeTypeModel.DeserializeWithLengthPrefix(source, null, runtimeTypeModel.MapType(typeof(T)), style, fieldNumber);
		}

		public static T MergeWithLengthPrefix<T>(Stream source, T instance, PrefixStyle style)
		{
			RuntimeTypeModel runtimeTypeModel = RuntimeTypeModel.Default;
			return (T)runtimeTypeModel.DeserializeWithLengthPrefix(source, instance, runtimeTypeModel.MapType(typeof(T)), style, 0);
		}

		public static void SerializeWithLengthPrefix<T>(Stream destination, T instance, PrefixStyle style)
		{
			SerializeWithLengthPrefix(destination, instance, style, 0);
		}

		public static void SerializeWithLengthPrefix<T>(Stream destination, T instance, PrefixStyle style, int fieldNumber)
		{
			RuntimeTypeModel runtimeTypeModel = RuntimeTypeModel.Default;
			runtimeTypeModel.SerializeWithLengthPrefix(destination, instance, runtimeTypeModel.MapType(typeof(T)), style, fieldNumber);
		}

		public static bool TryReadLengthPrefix(Stream source, PrefixStyle style, out int length)
		{
			length = ProtoReader.ReadLengthPrefix(source, expectHeader: false, style, out var _, out var bytesRead);
			return bytesRead > 0;
		}

		public static bool TryReadLengthPrefix(byte[] buffer, int index, int count, PrefixStyle style, out int length)
		{
			using Stream source = new MemoryStream(buffer, index, count);
			return TryReadLengthPrefix(source, style, out length);
		}

		public static void FlushPool()
		{
			BufferPool.Flush();
		}
	}
	public readonly struct SubItemToken
	{
		internal readonly long value64;

		internal SubItemToken(int value)
		{
			value64 = value;
		}

		internal SubItemToken(long value)
		{
			value64 = value;
		}
	}
	public enum WireType
	{
		None = -1,
		Variant = 0,
		Fixed64 = 1,
		String = 2,
		StartGroup = 3,
		EndGroup = 4,
		Fixed32 = 5,
		SignedVariant = 8
	}
}
namespace ProtoBuf.ServiceModel
{
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
	public sealed class ProtoBehaviorAttribute : Attribute, IOperationBehavior
	{
		void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
		{
		}

		void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
		{
			IOperationBehavior val = (IOperationBehavior)(object)new ProtoOperationBehavior(operationDescription);
			val.ApplyClientBehavior(operationDescription, clientOperation);
		}

		void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
		{
			IOperationBehavior val = (IOperationBehavior)(object)new ProtoOperationBehavior(operationDescription);
			val.ApplyDispatchBehavior(operationDescription, dispatchOperation);
		}

		void IOperationBehavior.Validate(OperationDescription operationDescription)
		{
		}
	}
	public class ProtoBehaviorExtension : BehaviorExtensionElement
	{
		public override Type BehaviorType => typeof(ProtoEndpointBehavior);

		protected override object CreateBehavior()
		{
			return new ProtoEndpointBehavior();
		}
	}
	public class ProtoEndpointBehavior : IEndpointBehavior
	{
		void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
		{
		}

		void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
		{
			ReplaceDataContractSerializerOperationBehavior(endpoint);
		}

		void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
		{
			ReplaceDataContractSerializerOperationBehavior(endpoint);
		}

		void IEndpointBehavior.Validate(ServiceEndpoint endpoint)
		{
		}

		private static void ReplaceDataContractSerializerOperationBehavior(ServiceEndpoint serviceEndpoint)
		{
			foreach (OperationDescription item in (Collection<OperationDescription>)(object)serviceEndpoint.Contract.Operations)
			{
				ReplaceDataContractSerializerOperationBehavior(item);
			}
		}

		private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
		{
			DataContractSerializerOperationBehavior val = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
			if (val != null)
			{
				((Collection<IOperationBehavior>)(object)description.Behaviors).Remove((IOperationBehavior)(object)val);
				ProtoOperationBehavior protoOperationBehavior = new ProtoOperationBehavior(description);
				((DataContractSerializerOperationBehavior)protoOperationBehavior).MaxItemsInObjectGraph = val.MaxItemsInObjectGraph;
				((Collection<IOperationBehavior>)(object)description.Behaviors).Add((IOperationBehavior)(object)protoOperationBehavior);
			}
		}
	}
	public sealed class ProtoOperationBehavior : DataContractSerializerOperationBehavior
	{
		private TypeModel model;

		public TypeModel Model
		{
			get
			{
				return model;
			}
			set
			{
				model = value ?? throw new ArgumentNullException("value");
			}
		}

		public ProtoOperationBehavior(OperationDescription operation)
			: base(operation)
		{
			model = RuntimeTypeModel.Default;
		}

		public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
		{
			if (model == null)
			{
				throw new InvalidOperationException("No Model instance has been assigned to the ProtoOperationBehavior");
			}
			return (XmlObjectSerializer)(((object)XmlProtoSerializer.TryCreate(model, type)) ?? ((object)((DataContractSerializerOperationBehavior)this).CreateSerializer(type, name, ns, knownTypes)));
		}
	}
	public sealed class XmlProtoSerializer : XmlObjectSerializer
	{
		private readonly TypeModel model;

		private readonly int key;

		private readonly bool isList;

		private readonly bool isEnum;

		private readonly Type type;

		private const string PROTO_ELEMENT = "proto";

		internal XmlProtoSerializer(TypeModel model, int key, Type type, bool isList)
		{
			if (key < 0)
			{
				throw new ArgumentOutOfRangeException("key");
			}
			this.model = model ?? throw new ArgumentNullException("model");
			this.key = key;
			this.isList = isList;
			this.type = type ?? throw new ArgumentOutOfRangeException("type");
			isEnum = Helpers.IsEnum(type);
		}

		public static XmlProtoSerializer TryCreate(TypeModel model, Type type)
		{
			if (model == null)
			{
				throw new ArgumentNullException("model");
			}
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			bool flag;
			int num = GetKey(model, ref type, out flag);
			if (num >= 0)
			{
				return new XmlProtoSerializer(model, num, type, flag);
			}
			return null;
		}

		public XmlProtoSerializer(TypeModel model, Type type)
		{
			if (model == null)
			{
				throw new ArgumentNullException("model");
			}
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			key = GetKey(model, ref type, out isList);
			this.model = model;
			this.type = type;
			isEnum = Helpers.IsEnum(type);
			if (key < 0)
			{
				throw new ArgumentOutOfRangeException("type", "Type not recognised by the model: " + type.FullName);
			}
		}

		private static int GetKey(TypeModel model, ref Type type, out bool isList)
		{
			if (model != null && type != null)
			{
				int num = model.GetKey(ref type);
				if (num >= 0)
				{
					isList = false;
					return num;
				}
				Type listItemType = TypeModel.GetListItemType(model, type);
				if (listItemType != null)
				{
					num = model.GetKey(ref listItemType);
					if (num >= 0)
					{
						isList = true;
						return num;
					}
				}
			}
			isList = false;
			return -1;
		}

		public override void WriteEndObject(XmlDictionaryWriter writer)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			((XmlWriter)writer).WriteEndElement();
		}

		public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			((XmlWriter)writer).WriteStartElement("proto");
		}

		public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
		{
			if (writer == null)
			{
				throw new ArgumentNullException("writer");
			}
			if (graph == null)
			{
				((XmlWriter)writer).WriteAttributeString("nil", "true");
				return;
			}
			using MemoryStream memoryStream = new MemoryStream();
			if (isList)
			{
				model.Serialize(memoryStream, graph, null);
			}
			else
			{
				using ProtoWriter dest = ProtoWriter.Create(memoryStream, model);
				model.Serialize(key, graph, dest);
			}
			byte[] buffer = memoryStream.GetBuffer();
			((XmlWriter)writer).WriteBase64(buffer, 0, (int)memoryStream.Length);
		}

		public override bool IsStartObject(XmlDictionaryReader reader)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			((XmlReader)reader).MoveToContent();
			if ((int)((XmlReader)reader).NodeType == 1)
			{
				return ((XmlReader)reader).Name == "proto";
			}
			return false;
		}

		public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			((XmlReader)reader).MoveToContent();
			bool isEmptyElement = ((XmlReader)reader).IsEmptyElement;
			bool flag = ((XmlReader)reader).GetAttribute("nil") == "true";
			((XmlReader)reader).ReadStartElement("proto");
			if (flag)
			{
				if (!isEmptyElement)
				{
					((XmlReader)reader).ReadEndElement();
				}
				return null;
			}
			if (isEmptyElement)
			{
				if (isList || isEnum)
				{
					return model.Deserialize(Stream.Null, null, type, null);
				}
				ProtoReader protoReader = null;
				try
				{
					protoReader = ProtoReader.Create(Stream.Null, model, null, -1L);
					return model.Deserialize(key, null, protoReader);
				}
				finally
				{
					ProtoReader.Recycle(protoReader);
				}
			}
			object result;
			using (MemoryStream source = new MemoryStream(reader.ReadContentAsBase64()))
			{
				if (isList || isEnum)
				{
					result = model.Deserialize(source, null, type, null);
				}
				else
				{
					ProtoReader protoReader2 = null;
					try
					{
						protoReader2 = ProtoReader.Create(source, model, null, -1L);
						result = model.Deserialize(key, null, protoReader2);
					}
					finally
					{
						ProtoReader.Recycle(protoReader2);
					}
				}
			}
			((XmlReader)reader).ReadEndElement();
			return result;
		}
	}
}
namespace ProtoBuf.Serializers
{
	internal sealed class ArrayDecorator : ProtoDecoratorBase
	{
		private readonly int fieldNumber;

		private const byte OPTIONS_WritePacked = 1;

		private const byte OPTIONS_OverwriteList = 2;

		private const byte OPTIONS_SupportNull = 4;

		private readonly byte options;

		private readonly WireType packedWireType;

		private readonly Type arrayType;

		private readonly Type itemType;

		public override Type ExpectedType => arrayType;

		public override bool RequiresOldValue => AppendToCollection;

		public override bool ReturnsValue => true;

		private bool AppendToCollection => (options & 2) == 0;

		private bool SupportNull => (options & 4) != 0;

		public ArrayDecorator(TypeModel model, IProtoSerializer tail, int fieldNumber, bool writePacked, WireType packedWireType, Type arrayType, bool overwriteList, bool supportNull)
			: base(tail)
		{
			itemType = arrayType.GetElementType();
			Type type = (supportNull ? itemType : (Helpers.GetUnderlyingType(itemType) ?? itemType));
			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 |= 1;
			}
			if (overwriteList)
			{
				options |= 2;
			}
			if (supportNull)
			{
				options |= 4;
			}
			this.arrayType = arrayType;
		}

		private bool CanUsePackedPrefix()
		{
			return CanUsePackedPrefix(packedWireType, itemType);
		}

		internal static bool CanUsePackedPrefix(WireType packedWireType, Type itemType)
		{
			if (packedWireType != WireType.Fixed64 && packedWireType != WireType.Fixed32)
			{
				return false;
			}
			if (!Helpers.IsValueType(itemType))
			{
				return false;
			}
			return Helpers.GetUnderlyingType(itemType) == null;
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			using Local local = ctx.GetLocalWithValue(arrayType, valueFrom);
			using Local i = new Local(ctx, ctx.MapType(typeof(int)));
			bool flag = (options & 1) != 0;
			bool flag2 = flag && CanUsePackedPrefix();
			using Local local2 = ((flag && !flag2) ? new Local(ctx, ctx.MapType(typeof(SubItemToken))) : null);
			Type type = ctx.MapType(typeof(ProtoWriter));
			if (flag)
			{
				ctx.LoadValue(fieldNumber);
				ctx.LoadValue(2);
				ctx.LoadReaderWriter();
				ctx.EmitCall(type.GetMethod("WriteFieldHeader"));
				if (flag2)
				{
					ctx.LoadLength(local, zeroIfNull: false);
					ctx.LoadValue((int)packedWireType);
					ctx.LoadReaderWriter();
					ctx.EmitCall(type.GetMethod("WritePackedPrefix"));
				}
				else
				{
					ctx.LoadValue(local);
					ctx.LoadReaderWriter();
					ctx.EmitCall(type.GetMethod("StartSubItem"));
					ctx.StoreValue(local2);
				}
				ctx.LoadValue(fieldNumber);
				ctx.LoadReaderWriter();
				ctx.EmitCall(type.GetMethod("SetPackedField"));
			}
			EmitWriteArrayLoop(ctx, i, local);
			if (flag)
			{
				if (flag2)
				{
					ctx.LoadValue(fieldNumber);
					ctx.LoadReaderWriter();
					ctx.EmitCall(type.GetMethod("ClearPackedField"));
				}
				else
				{
					ctx.LoadValue(local2);
					ctx.LoadReaderWriter();
					ctx.EmitCall(type.GetMethod("EndSubItem"));
				}
			}
		}

		private void EmitWriteArrayLoop(CompilerContext ctx, Local i, Local arr)
		{
			ctx.LoadValue(0);
			ctx.StoreValue(i);
			CodeLabel label = ctx.DefineLabel();
			CodeLabel label2 = ctx.DefineLabel();
			ctx.Branch(label, @short: false);
			ctx.MarkLabel(label2);
			ctx.LoadArrayValue(arr, i);
			if (SupportNull)
			{
				Tail.EmitWrite(ctx, null);
			}
			else
			{
				ctx.WriteNullCheckedTail(itemType, Tail, null);
			}
			ctx.LoadValue(i);
			ctx.LoadValue(1);
			ctx.Add();
			ctx.StoreValue(i);
			ctx.MarkLabel(label);
			ctx.LoadValue(i);
			ctx.LoadLength(arr, zeroIfNull: false);
			ctx.BranchIfLess(label2, @short: false);
		}

		public override void Write(object value, ProtoWriter dest)
		{
			IList list = (IList)value;
			int count = list.Count;
			bool flag = (options & 1) != 0;
			bool flag2 = flag && CanUsePackedPrefix();
			SubItemToken token;
			if (flag)
			{
				ProtoWriter.WriteFieldHeader(fieldNumber, WireType.String, dest);
				if (flag2)
				{
					ProtoWriter.WritePackedPrefix(list.Count, packedWireType, dest);
					token = default(SubItemToken);
				}
				else
				{
					token = ProtoWriter.StartSubItem(value, dest);
				}
				ProtoWriter.SetPackedField(fieldNumber, dest);
			}
			else
			{
				token = default(SubItemToken);
			}
			bool flag3 = !SupportNull;
			for (int i = 0; i < count; i++)
			{
				object obj = list[i];
				if (flag3 && obj == null)
				{
					throw new NullReferenceException();
				}
				Tail.Write(obj, dest);
			}
			if (flag)
			{
				if (flag2)
				{
					ProtoWriter.ClearPackedField(fieldNumber, dest);
				}
				else
				{
					ProtoWriter.EndSubItem(token, dest);
				}
			}
		}

		public override object Read(object value, ProtoReader source)
		{
			int field = source.FieldNumber;
			BasicList basicList = new BasicList();
			if (packedWireType != WireType.None && source.WireType == WireType.String)
			{
				SubItemToken token = ProtoReader.StartSubItem(source);
				while (ProtoReader.HasSubValue(packedWireType, source))
				{
					basicList.Add(Tail.Read(null, source));
				}
				ProtoReader.EndSubItem(token, source);
			}
			else
			{
				do
				{
					basicList.Add(Tail.Read(null, source));
				}
				while (source.TryReadFieldHeader(field));
			}
			int num = (AppendToCollection ? ((value != null) ? ((Array)value).Length : 0) : 0);
			Array array = Array.CreateInstance(itemType, num + basicList.Count);
			if (num != 0)
			{
				((Array)value).CopyTo(array, 0);
			}
			basicList.CopyTo(array, num);
			return array;
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			Type type = ctx.MapType(typeof(List<>)).MakeGenericType(itemType);
			Type expectedType = ExpectedType;
			using Local local = (AppendToCollection ? ctx.GetLocalWithValue(expectedType, valueFrom) : null);
			using Local local2 = new Local(ctx, expectedType);
			using Local local3 = new Local(ctx, type);
			ctx.EmitCtor(type);
			ctx.StoreValue(local3);
			ListDecorator.EmitReadList(ctx, local3, Tail, type.GetMethod("Add"), packedWireType, castListForAdd: false);
			using (Local local4 = (AppendToCollection ? new Local(ctx, ctx.MapType(typeof(int))) : null))
			{
				Type[] array = new Type[2]
				{
					ctx.MapType(typeof(Array)),
					ctx.MapType(typeof(int))
				};
				if (AppendToCollection)
				{
					ctx.LoadLength(local, zeroIfNull: true);
					ctx.CopyValue();
					ctx.StoreValue(local4);
					ctx.LoadAddress(local3, type);
					ctx.LoadValue(type.GetProperty("Count"));
					ctx.Add();
					ctx.CreateArray(itemType, null);
					ctx.StoreValue(local2);
					ctx.LoadValue(local4);
					CodeLabel label = ctx.DefineLabel();
					ctx.BranchIfFalse(label, @short: true);
					ctx.LoadValue(local);
					ctx.LoadValue(local2);
					ctx.LoadValue(0);
					ctx.EmitCall(expectedType.GetMethod("CopyTo", array));
					ctx.MarkLabel(label);
					ctx.LoadValue(local3);
					ctx.LoadValue(local2);
					ctx.LoadValue(local4);
				}
				else
				{
					ctx.LoadAddress(local3, type);
					ctx.LoadValue(type.GetProperty("Count"));
					ctx.CreateArray(itemType, null);
					ctx.StoreValue(local2);
					ctx.LoadAddress(local3, type);
					ctx.LoadValue(local2);
					ctx.LoadValue(0);
				}
				array[0] = expectedType;
				MethodInfo method = type.GetMethod("CopyTo", array);
				if (method == null)
				{
					array[1] = ctx.MapType(typeof(Array));
					method = type.GetMethod("CopyTo", array);
				}
				ctx.EmitCall(method);
			}
			ctx.LoadValue(local2);
		}
	}
	internal sealed class BlobSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(byte[]);

		private readonly bool overwriteList;

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => !overwriteList;

		bool IProtoSerializer.ReturnsValue => true;

		public BlobSerializer(TypeModel model, bool overwriteList)
		{
			this.overwriteList = 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);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteBytes", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			if (overwriteList)
			{
				ctx.LoadNullRef();
			}
			else
			{
				ctx.LoadValue(valueFrom);
			}
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("AppendBytes"));
		}
	}
	internal sealed class BooleanSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(bool);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public BooleanSerializer(TypeModel model)
		{
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteBoolean((bool)value, dest);
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadBoolean();
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteBoolean", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadBoolean", ExpectedType);
		}
	}
	internal sealed class ByteSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(byte);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public ByteSerializer(TypeModel model)
		{
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteByte((byte)value, dest);
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadByte();
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteByte", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadByte", ExpectedType);
		}
	}
	internal sealed class CharSerializer : UInt16Serializer
	{
		private static readonly Type expectedType = typeof(char);

		public override Type ExpectedType => expectedType;

		public CharSerializer(TypeModel model)
			: base(model)
		{
		}

		public override void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteUInt16((char)value, dest);
		}

		public override object Read(object value, ProtoReader source)
		{
			return (char)source.ReadUInt16();
		}
	}
	internal sealed class CompiledSerializer : IProtoTypeSerializer, IProtoSerializer
	{
		private readonly IProtoTypeSerializer head;

		private readonly ProtoSerializer serializer;

		private readonly ProtoDeserializer deserializer;

		bool IProtoSerializer.RequiresOldValue => head.RequiresOldValue;

		bool IProtoSerializer.ReturnsValue => head.ReturnsValue;

		Type IProtoSerializer.ExpectedType => head.ExpectedType;

		bool IProtoTypeSerializer.HasCallbacks(TypeModel.CallbackType callbackType)
		{
			return head.HasCallbacks(callbackType);
		}

		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);
		}

		public static CompiledSerializer Wrap(IProtoTypeSerializer head, TypeModel model)
		{
			CompiledSerializer compiledSerializer = head as CompiledSerializer;
			if (compiledSerializer == null)
			{
				compiledSerializer = new CompiledSerializer(head, model);
			}
			return compiledSerializer;
		}

		private CompiledSerializer(IProtoTypeSerializer head, TypeModel model)
		{
			this.head = head;
			serializer = CompilerContext.BuildSerializer(head, model);
			deserializer = CompilerContext.BuildDeserializer(head, model);
		}

		void IProtoSerializer.Write(object value, ProtoWriter dest)
		{
			serializer(value, dest);
		}

		object IProtoSerializer.Read(object value, ProtoReader source)
		{
			return deserializer(value, source);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			head.EmitWrite(ctx, valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			head.EmitRead(ctx, valueFrom);
		}

		void IProtoTypeSerializer.EmitCallback(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType)
		{
			head.EmitCallback(ctx, valueFrom, callbackType);
		}

		void IProtoTypeSerializer.EmitCreateInstance(CompilerContext ctx)
		{
			head.EmitCreateInstance(ctx);
		}
	}
	internal sealed class DateTimeSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(DateTime);

		private readonly bool includeKind;

		private readonly bool wellKnown;

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public DateTimeSerializer(DataFormat dataFormat, TypeModel model)
		{
			wellKnown = dataFormat == DataFormat.WellKnown;
			includeKind = model?.SerializeDateTimeKind() ?? false;
		}

		public object Read(object value, ProtoReader source)
		{
			if (wellKnown)
			{
				return BclHelpers.ReadTimestamp(source);
			}
			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);
			}
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitWrite(ctx.MapType(typeof(BclHelpers)), wellKnown ? "WriteTimestamp" : (includeKind ? "WriteDateTimeWithKind" : "WriteDateTime"), valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local entity)
		{
			if (wellKnown)
			{
				ctx.LoadValue(entity);
			}
			ctx.EmitBasicRead(ctx.MapType(typeof(BclHelpers)), wellKnown ? "ReadTimestamp" : "ReadDateTime", ExpectedType);
		}
	}
	internal sealed class DecimalSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(decimal);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public DecimalSerializer(TypeModel model)
		{
		}

		public object Read(object value, ProtoReader source)
		{
			return BclHelpers.ReadDecimal(source);
		}

		public void Write(object value, ProtoWriter dest)
		{
			BclHelpers.WriteDecimal((decimal)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitWrite(ctx.MapType(typeof(BclHelpers)), "WriteDecimal", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead(ctx.MapType(typeof(BclHelpers)), "ReadDecimal", ExpectedType);
		}
	}
	internal sealed class DefaultValueDecorator : ProtoDecoratorBase
	{
		private readonly object defaultValue;

		public override Type ExpectedType => Tail.ExpectedType;

		public override bool RequiresOldValue => Tail.RequiresOldValue;

		public override bool ReturnsValue => Tail.ReturnsValue;

		public DefaultValueDecorator(TypeModel model, object defaultValue, IProtoSerializer tail)
			: base(tail)
		{
			if (defaultValue == null)
			{
				throw new ArgumentNullException("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);
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			CodeLabel label = ctx.DefineLabel();
			if (valueFrom == null)
			{
				ctx.CopyValue();
				CodeLabel label2 = ctx.DefineLabel();
				EmitBranchIfDefaultValue(ctx, label2);
				Tail.EmitWrite(ctx, null);
				ctx.Branch(label, @short: true);
				ctx.MarkLabel(label2);
				ctx.DiscardValue();
			}
			else
			{
				ctx.LoadValue(valueFrom);
				EmitBranchIfDefaultValue(ctx, label);
				Tail.EmitWrite(ctx, valueFrom);
			}
			ctx.MarkLabel(label);
		}

		private void EmitBeq(CompilerContext ctx, CodeLabel label, Type type)
		{
			ProtoTypeCode typeCode = Helpers.GetTypeCode(type);
			if ((uint)(typeCode - 3) <= 11u)
			{
				ctx.BranchIfEqual(label, @short: false);
				return;
			}
			MethodInfo method = type.GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { type, type }, null);
			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, @short: false);
		}

		private void EmitBranchIfDefaultValue(CompilerContext ctx, CodeLabel label)
		{
			Type expectedType = ExpectedType;
			switch (Helpers.GetTypeCode(expectedType))
			{
			case ProtoTypeCode.Boolean:
				if ((bool)defaultValue)
				{
					ctx.BranchIfTrue(label, @short: false);
				}
				else
				{
					ctx.BranchIfFalse(label, @short: false);
				}
				break;
			case ProtoTypeCode.Byte:
				if ((byte)defaultValue == 0)
				{
					ctx.BranchIfFalse(label, @short: false);
					break;
				}
				ctx.LoadValue((byte)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.SByte:
				if ((sbyte)defaultValue == 0)
				{
					ctx.BranchIfFalse(label, @short: false);
					break;
				}
				ctx.LoadValue((sbyte)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.Int16:
				if ((short)defaultValue == 0)
				{
					ctx.BranchIfFalse(label, @short: false);
					break;
				}
				ctx.LoadValue((short)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.UInt16:
				if ((ushort)defaultValue == 0)
				{
					ctx.BranchIfFalse(label, @short: false);
					break;
				}
				ctx.LoadValue((ushort)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.Int32:
				if ((int)defaultValue == 0)
				{
					ctx.BranchIfFalse(label, @short: false);
					break;
				}
				ctx.LoadValue((int)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.UInt32:
				if ((uint)defaultValue == 0)
				{
					ctx.BranchIfFalse(label, @short: false);
					break;
				}
				ctx.LoadValue((int)(uint)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.Char:
				if ((char)defaultValue == '\0')
				{
					ctx.BranchIfFalse(label, @short: false);
					break;
				}
				ctx.LoadValue((char)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.Int64:
				ctx.LoadValue((long)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.UInt64:
				ctx.LoadValue((long)(ulong)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.Double:
				ctx.LoadValue((double)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.Single:
				ctx.LoadValue((float)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.String:
				ctx.LoadValue((string)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.Decimal:
			{
				decimal value = (decimal)defaultValue;
				ctx.LoadValue(value);
				EmitBeq(ctx, label, expectedType);
				break;
			}
			case ProtoTypeCode.TimeSpan:
			{
				TimeSpan timeSpan = (TimeSpan)defaultValue;
				if (timeSpan == TimeSpan.Zero)
				{
					ctx.LoadValue(typeof(TimeSpan).GetField("Zero"));
				}
				else
				{
					ctx.LoadValue(timeSpan.Ticks);
					ctx.EmitCall(ctx.MapType(typeof(TimeSpan)).GetMethod("FromTicks"));
				}
				EmitBeq(ctx, label, expectedType);
				break;
			}
			case ProtoTypeCode.Guid:
				ctx.LoadValue((Guid)defaultValue);
				EmitBeq(ctx, label, expectedType);
				break;
			case ProtoTypeCode.DateTime:
				ctx.LoadValue(((DateTime)defaultValue).ToBinary());
				ctx.EmitCall(ctx.MapType(typeof(DateTime)).GetMethod("FromBinary"));
				EmitBeq(ctx, label, expectedType);
				break;
			default:
				throw new NotSupportedException("Type cannot be represented as a default value: " + expectedType.FullName);
			}
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			Tail.EmitRead(ctx, valueFrom);
		}
	}
	internal sealed class DoubleSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(double);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public DoubleSerializer(TypeModel model)
		{
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadDouble();
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteDouble((double)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteDouble", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadDouble", ExpectedType);
		}
	}
	internal sealed class EnumSerializer : IProtoSerializer
	{
		public readonly struct EnumPair(int wireValue, object raw, Type type)
		{
			public readonly object RawValue = raw;

			public readonly Enum TypedValue = (Enum)Enum.ToObject(type, raw);

			public readonly int WireValue = wireValue;
		}

		private readonly Type enumType;

		private readonly EnumPair[] map;

		public Type ExpectedType => enumType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public EnumSerializer(Type enumType, EnumPair[] map)
		{
			this.enumType = enumType ?? throw new ArgumentNullException("enumType");
			this.map = map;
			if (map == null)
			{
				return;
			}
			for (int i = 1; i < map.Length; i++)
			{
				for (int j = 0; j < i; j++)
				{
					if (map[i].WireValue == map[j].WireValue && !object.Equals(map[i].RawValue, map[j].RawValue))
					{
						int wireValue = map[i].WireValue;
						throw new ProtoException("Multiple enums with wire-value " + wireValue);
					}
					if (object.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 underlyingType = Helpers.GetUnderlyingType(enumType);
			if (underlyingType == null)
			{
				underlyingType = enumType;
			}
			return Helpers.GetTypeCode(underlyingType);
		}

		private int EnumToWire(object value)
		{
			return GetTypeCode() switch
			{
				ProtoTypeCode.Byte => (byte)value, 
				ProtoTypeCode.SByte => (sbyte)value, 
				ProtoTypeCode.Int16 => (short)value, 
				ProtoTypeCode.Int32 => (int)value, 
				ProtoTypeCode.Int64 => (int)(long)value, 
				ProtoTypeCode.UInt16 => (ushort)value, 
				ProtoTypeCode.UInt32 => (int)(uint)value, 
				ProtoTypeCode.UInt64 => (int)(ulong)value, 
				_ => throw new InvalidOperationException(), 
			};
		}

		private object WireToEnum(int value)
		{
			return GetTypeCode() switch
			{
				ProtoTypeCode.Byte => Enum.ToObject(enumType, (byte)value), 
				ProtoTypeCode.SByte => Enum.ToObject(enumType, (sbyte)value), 
				ProtoTypeCode.Int16 => Enum.ToObject(enumType, (short)value), 
				ProtoTypeCode.Int32 => Enum.ToObject(enumType, value), 
				ProtoTypeCode.Int64 => Enum.ToObject(enumType, (long)value), 
				ProtoTypeCode.UInt16 => Enum.ToObject(enumType, (ushort)value), 
				ProtoTypeCode.UInt32 => Enum.ToObject(enumType, (uint)value), 
				ProtoTypeCode.UInt64 => Enum.ToObject(enumType, (ulong)value), 
				_ => throw new InvalidOperationException(), 
			};
		}

		public object Read(object value, ProtoReader source)
		{
			int num = source.ReadInt32();
			if (map == null)
			{
				return WireToEnum(num);
			}
			for (int i = 0; i < map.Length; i++)
			{
				if (map[i].WireValue == num)
				{
					return map[i].TypedValue;
				}
			}
			source.ThrowEnumException(ExpectedType, num);
			return null;
		}

		public void Write(object value, ProtoWriter dest)
		{
			if (map == null)
			{
				ProtoWriter.WriteInt32(EnumToWire(value), dest);
				return;
			}
			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);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ProtoTypeCode typeCode = GetTypeCode();
			if (map == null)
			{
				ctx.LoadValue(valueFrom);
				ctx.ConvertToInt32(typeCode, uint32Overflow: false);
				ctx.EmitBasicWrite("WriteInt32", null);
				return;
			}
			using Local local = ctx.GetLocalWithValue(ExpectedType, valueFrom);
			CodeLabel label = ctx.DefineLabel();
			for (int i = 0; i < map.Length; i++)
			{
				CodeLabel label2 = ctx.DefineLabel();
				CodeLabel label3 = ctx.DefineLabel();
				ctx.LoadValue(local);
				WriteEnumValue(ctx, typeCode, map[i].RawValue);
				ctx.BranchIfEqual(label3, @short: true);
				ctx.Branch(label2, @short: true);
				ctx.MarkLabel(label3);
				ctx.LoadValue(map[i].WireValue);
				ctx.EmitBasicWrite("WriteInt32", null);
				ctx.Branch(label, @short: false);
				ctx.MarkLabel(label2);
			}
			ctx.LoadReaderWriter();
			ctx.LoadValue(local);
			ctx.CastToObject(ExpectedType);
			ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("ThrowEnumException"));
			ctx.MarkLabel(label);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ProtoTypeCode typeCode = GetTypeCode();
			if (map == null)
			{
				ctx.EmitBasicRead("ReadInt32", ctx.MapType(typeof(int)));
				ctx.ConvertFromInt32(typeCode, uint32Overflow: false);
				return;
			}
			int[] array = new int[map.Length];
			object[] array2 = new object[map.Length];
			for (int i = 0; i < map.Length; i++)
			{
				array[i] = map[i].WireValue;
				array2[i] = map[i].RawValue;
			}
			using Local local = new Local(ctx, ExpectedType);
			using Local local2 = new Local(ctx, ctx.MapType(typeof(int)));
			ctx.EmitBasicRead("ReadInt32", ctx.MapType(typeof(int)));
			ctx.StoreValue(local2);
			CodeLabel codeLabel = ctx.DefineLabel();
			BasicList.NodeEnumerator enumerator = BasicList.GetContiguousGroups(array, array2).GetEnumerator();
			while (enumerator.MoveNext())
			{
				BasicList.Group obj = (BasicList.Group)enumerator.Current;
				CodeLabel label = ctx.DefineLabel();
				int count = obj.Items.Count;
				if (count == 1)
				{
					ctx.LoadValue(local2);
					ctx.LoadValue(obj.First);
					CodeLabel codeLabel2 = ctx.DefineLabel();
					ctx.BranchIfEqual(codeLabel2, @short: true);
					ctx.Branch(label, @short: false);
					WriteEnumValue(ctx, typeCode, codeLabel2, codeLabel, obj.Items[0], local);
				}
				else
				{
					ctx.LoadValue(local2);
					ctx.LoadValue(obj.First);
					ctx.Subtract();
					CodeLabel[] array3 = new CodeLabel[count];
					for (int j = 0; j < count; j++)
					{
						array3[j] = ctx.DefineLabel();
					}
					ctx.Switch(array3);
					ctx.Branch(label, @short: false);
					for (int k = 0; k < count; k++)
					{
						WriteEnumValue(ctx, typeCode, array3[k], codeLabel, obj.Items[k], local);
					}
				}
				ctx.MarkLabel(label);
			}
			ctx.LoadReaderWriter();
			ctx.LoadValue(ExpectedType);
			ctx.LoadValue(local2);
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("ThrowEnumException"));
			ctx.MarkLabel(codeLabel);
			ctx.LoadValue(local);
		}

		private static void WriteEnumValue(CompilerContext ctx, ProtoTypeCode typeCode, object value)
		{
			switch (typeCode)
			{
			case ProtoTypeCode.Byte:
				ctx.LoadValue((byte)value);
				break;
			case ProtoTypeCode.SByte:
				ctx.LoadValue((sbyte)value);
				break;
			case ProtoTypeCode.Int16:
				ctx.LoadValue((short)value);
				break;
			case ProtoTypeCode.Int32:
				ctx.LoadValue((int)value);
				break;
			case ProtoTypeCode.Int64:
				ctx.LoadValue((long)value);
				break;
			case ProtoTypeCode.UInt16:
				ctx.LoadValue((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(CompilerContext ctx, ProtoTypeCode typeCode, CodeLabel handler, CodeLabel @continue, object value, Local local)
		{
			ctx.MarkLabel(handler);
			WriteEnumValue(ctx, typeCode, value);
			ctx.StoreValue(local);
			ctx.Branch(@continue, @short: false);
		}
	}
	internal sealed class FieldDecorator : ProtoDecoratorBase
	{
		private readonly FieldInfo field;

		private readonly Type forType;

		public override Type ExpectedType => forType;

		public override bool RequiresOldValue => true;

		public override bool ReturnsValue => false;

		public FieldDecorator(Type forType, FieldInfo field, IProtoSerializer tail)
			: base(tail)
		{
			this.forType = forType;
			this.field = field;
		}

		public override void Write(object value, ProtoWriter dest)
		{
			value = field.GetValue(value);
			if (value != null)
			{
				Tail.Write(value, dest);
			}
		}

		public override object Read(object value, ProtoReader source)
		{
			object obj = Tail.Read(Tail.RequiresOldValue ? field.GetValue(value) : null, source);
			if (obj != null)
			{
				field.SetValue(value, obj);
			}
			return null;
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.LoadAddress(valueFrom, ExpectedType);
			ctx.LoadValue(field);
			ctx.WriteNullCheckedTail(field.FieldType, Tail, null);
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			using Local local = ctx.GetLocalWithValue(ExpectedType, valueFrom);
			if (Tail.RequiresOldValue)
			{
				ctx.LoadAddress(local, ExpectedType);
				ctx.LoadValue(field);
			}
			ctx.ReadNullCheckedTail(field.FieldType, Tail, null);
			MemberInfo member = field;
			ctx.CheckAccessibility(ref member);
			if (member is FieldInfo)
			{
				if (!Tail.ReturnsValue)
				{
					return;
				}
				using Local local2 = new Local(ctx, field.FieldType);
				ctx.StoreValue(local2);
				if (Helpers.IsValueType(field.FieldType))
				{
					ctx.LoadAddress(local, ExpectedType);
					ctx.LoadValue(local2);
					ctx.StoreValue(field);
					return;
				}
				CodeLabel label = ctx.DefineLabel();
				ctx.LoadValue(local2);
				ctx.BranchIfFalse(label, @short: true);
				ctx.LoadAddress(local, ExpectedType);
				ctx.LoadValue(local2);
				ctx.StoreValue(field);
				ctx.MarkLabel(label);
				return;
			}
			if (Tail.ReturnsValue)
			{
				ctx.DiscardValue();
			}
		}
	}
	internal sealed class GuidSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(Guid);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public GuidSerializer(TypeModel model)
		{
		}

		public void Write(object value, ProtoWriter dest)
		{
			BclHelpers.WriteGuid((Guid)value, dest);
		}

		public object Read(object value, ProtoReader source)
		{
			return BclHelpers.ReadGuid(source);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitWrite(ctx.MapType(typeof(BclHelpers)), "WriteGuid", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead(ctx.MapType(typeof(BclHelpers)), "ReadGuid", ExpectedType);
		}
	}
	internal sealed class ImmutableCollectionDecorator : ListDecorator
	{
		private readonly MethodInfo builderFactory;

		private readonly MethodInfo add;

		private readonly MethodInfo addRange;

		private readonly MethodInfo finish;

		private readonly PropertyInfo isEmpty;

		private readonly PropertyInfo length;

		protected override bool RequireAdd => false;

		private static Type ResolveIReadOnlyCollection(Type declaredType, Type t)
		{
			if (CheckIsIReadOnlyCollectionExactly(declaredType))
			{
				return declaredType;
			}
			Type[] interfaces = declaredType.GetInterfaces();
			foreach (Type type in interfaces)
			{
				if (CheckIsIReadOnlyCollectionExactly(type))
				{
					return type;
				}
			}
			return null;
		}

		private static bool CheckIsIReadOnlyCollectionExactly(Type t)
		{
			if (t != null && t.IsGenericType && t.Name.StartsWith("IReadOnlyCollection`"))
			{
				Type[] genericArguments = t.GetGenericArguments();
				if (genericArguments.Length != 1 && genericArguments[0] != t)
				{
					return false;
				}
				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 (!declaredType.IsGenericType)
			{
				return false;
			}
			Type[] genericArguments = declaredType.GetGenericArguments();
			Type[] array;
			switch (genericArguments.Length)
			{
			case 1:
				array = genericArguments;
				break;
			case 2:
			{
				Type type = model.MapType(typeof(KeyValuePair<, >));
				if (type == null)
				{
					return false;
				}
				type = type.MakeGenericType(genericArguments);
				array = new Type[1] { type };
				break;
			}
			default:
				return false;
			}
			if (ResolveIReadOnlyCollection(declaredType, null) == null)
			{
				return false;
			}
			string name = declaredType.Name;
			int num = name.IndexOf('`');
			if (num <= 0)
			{
				return false;
			}
			name = (declaredType.IsInterface ? name.Substring(1, num - 1) : name.Substring(0, num));
			Type type2 = model.GetType(declaredType.Namespace + "." + name, declaredType.Assembly);
			if (type2 == null && name == "ImmutableSet")
			{
				type2 = model.GetType(declaredType.Namespace + ".ImmutableHashSet", declaredType.Assembly);
			}
			if (type2 == null)
			{
				return false;
			}
			MethodInfo[] methods = type2.GetMethods();
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.IsStatic && !(methodInfo.Name != "CreateBuilder") && methodInfo.IsGenericMethodDefinition && methodInfo.GetParameters().Length == 0 && methodInfo.GetGenericArguments().Length == genericArguments.Length)
				{
					builderFactory = methodInfo.MakeGenericMethod(genericArguments);
					break;
				}
			}
			Type type3 = model.MapType(typeof(void));
			if (builderFactory == null || builderFactory.ReturnType == null || builderFactory.ReturnType == type3)
			{
				return false;
			}
			isEmpty = Helpers.GetProperty(declaredType, "IsDefaultOrEmpty", nonPublic: false);
			if (isEmpty == null)
			{
				isEmpty = Helpers.GetProperty(declaredType, "IsEmpty", nonPublic: false);
			}
			if (isEmpty == null)
			{
				length = Helpers.GetProperty(declaredType, "Length", nonPublic: false);
				if (length == null)
				{
					length = Helpers.GetProperty(declaredType, "Count", nonPublic: false);
				}
				if (length == null)
				{
					length = Helpers.GetProperty(ResolveIReadOnlyCollection(declaredType, array[0]), "Count", nonPublic: false);
				}
				if (length == null)
				{
					return false;
				}
			}
			add = Helpers.GetInstanceMethod(builderFactory.ReturnType, "Add", array);
			if (add == null)
			{
				return false;
			}
			finish = Helpers.GetInstanceMethod(builderFactory.ReturnType, "ToImmutable", Helpers.EmptyTypes);
			if (finish == null || finish.ReturnType == null || finish.ReturnType == type3)
			{
				return false;
			}
			if (!(finish.ReturnType == declaredType) && !Helpers.IsAssignableFrom(declaredType, finish.ReturnType))
			{
				return false;
			}
			addRange = Helpers.GetInstanceMethod(builderFactory.ReturnType, "AddRange", new Type[1] { declaredType });
			if (addRange == null)
			{
				Type type4 = model.MapType(typeof(IEnumerable<>), demand: false);
				if (type4 != null)
				{
					addRange = Helpers.GetInstanceMethod(builderFactory.ReturnType, "AddRange", new Type[1] { type4.MakeGenericType(array) });
				}
			}
			return true;
		}

		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 obj = builderFactory.Invoke(null, null);
			int field = source.FieldNumber;
			object[] array = new object[1];
			if (base.AppendToCollection && value != null && ((isEmpty != null) ? (!(bool)isEmpty.GetValue(value, null)) : ((byte)(int)length.GetValue(value, null) != 0)))
			{
				if (addRange != null)
				{
					array[0] = value;
					addRange.Invoke(obj, array);
				}
				else
				{
					foreach (object item in (ICollection)value)
					{
						array[0] = item;
						add.Invoke(obj, array);
					}
				}
			}
			if (packedWireType != WireType.None && source.WireType == WireType.String)
			{
				SubItemToken token = ProtoReader.StartSubItem(source);
				while (ProtoReader.HasSubValue(packedWireType, source))
				{
					array[0] = Tail.Read(null, source);
					add.Invoke(obj, array);
				}
				ProtoReader.EndSubItem(token, source);
			}
			else
			{
				do
				{
					array[0] = Tail.Read(null, source);
					add.Invoke(obj, array);
				}
				while (source.TryReadFieldHeader(field));
			}
			return finish.Invoke(obj, null);
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			using Local local = (base.AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : null);
			using Local local2 = new Local(ctx, builderFactory.ReturnType);
			ctx.EmitCall(builderFactory);
			ctx.StoreValue(local2);
			if (base.AppendToCollection)
			{
				CodeLabel label = ctx.DefineLabel();
				if (!Helpers.IsValueType(ExpectedType))
				{
					ctx.LoadValue(local);
					ctx.BranchIfFalse(label, @short: false);
				}
				ctx.LoadAddress(local, local.Type);
				if (isEmpty != null)
				{
					ctx.EmitCall(Helpers.GetGetMethod(isEmpty, nonPublic: false, allowInternal: false));
					ctx.BranchIfTrue(label, @short: false);
				}
				else
				{
					ctx.EmitCall(Helpers.GetGetMethod(length, nonPublic: false, allowInternal: false));
					ctx.BranchIfFalse(label, @short: false);
				}
				Type type = ctx.MapType(typeof(void));
				if (addRange != null)
				{
					ctx.LoadValue(local2);
					ctx.LoadValue(local);
					ctx.EmitCall(addRange);
					if (addRange.ReturnType != null && add.ReturnType != type)
					{
						ctx.DiscardValue();
					}
				}
				else
				{
					MethodInfo moveNext;
					MethodInfo current;
					MethodInfo enumeratorInfo = GetEnumeratorInfo(ctx.Model, out moveNext, out current);
					Type returnType = enumeratorInfo.ReturnType;
					using Local local3 = new Local(ctx, returnType);
					ctx.LoadAddress(local, ExpectedType);
					ctx.EmitCall(enumeratorInfo);
					ctx.StoreValue(local3);
					using (ctx.Using(local3))
					{
						CodeLabel label2 = ctx.DefineLabel();
						CodeLabel label3 = ctx.DefineLabel();
						ctx.Branch(label3, @short: false);
						ctx.MarkLabel(label2);
						ctx.LoadAddress(local2, local2.Type);
						ctx.LoadAddress(local3, returnType);
						ctx.EmitCall(current);
						ctx.EmitCall(add);
						if (add.ReturnType != null && add.ReturnType != type)
						{
							ctx.DiscardValue();
						}
						ctx.MarkLabel(label3);
						ctx.LoadAddress(local3, returnType);
						ctx.EmitCall(moveNext);
						ctx.BranchIfTrue(label2, @short: false);
					}
				}
				ctx.MarkLabel(label);
			}
			ListDecorator.EmitReadList(ctx, local2, Tail, add, packedWireType, castListForAdd: false);
			ctx.LoadAddress(local2, local2.Type);
			ctx.EmitCall(finish);
			if (ExpectedType != finish.ReturnType)
			{
				ctx.Cast(ExpectedType);
			}
		}
	}
	internal sealed class Int16Serializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(short);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public Int16Serializer(TypeModel model)
		{
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadInt16();
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteInt16((short)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteInt16", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadInt16", ExpectedType);
		}
	}
	internal sealed class Int32Serializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(int);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public Int32Serializer(TypeModel model)
		{
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadInt32();
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteInt32((int)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteInt32", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadInt32", ExpectedType);
		}
	}
	internal sealed class Int64Serializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(long);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public Int64Serializer(TypeModel model)
		{
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadInt64();
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteInt64((long)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteInt64", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadInt64", ExpectedType);
		}
	}
	internal interface IProtoSerializer
	{
		Type ExpectedType { get; }

		bool RequiresOldValue { get; }

		bool ReturnsValue { get; }

		void Write(object value, ProtoWriter dest);

		object Read(object value, ProtoReader source);

		void EmitWrite(CompilerContext ctx, Local valueFrom);

		void EmitRead(CompilerContext ctx, Local entity);
	}
	internal interface IProtoTypeSerializer : IProtoSerializer
	{
		bool HasCallbacks(TypeModel.CallbackType callbackType);

		bool CanCreateInstance();

		object CreateInstance(ProtoReader source);

		void Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context);

		void EmitCallback(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType);

		void EmitCreateInstance(CompilerContext ctx);
	}
	internal interface ISerializerProxy
	{
		IProtoSerializer Serializer { get; }
	}
	internal class ListDecorator : ProtoDecoratorBase
	{
		private readonly byte options;

		private const byte OPTIONS_IsList = 1;

		private const byte OPTIONS_SuppressIList = 2;

		private const byte OPTIONS_WritePacked = 4;

		private const byte OPTIONS_ReturnList = 8;

		private const byte OPTIONS_OverwriteList = 16;

		private const byte OPTIONS_SupportNull = 32;

		private readonly Type declaredType;

		private readonly Type concreteType;

		private readonly MethodInfo add;

		private readonly int fieldNumber;

		protected readonly WireType packedWireType;

		private static readonly Type ienumeratorType = typeof(IEnumerator);

		private static readonly Type ienumerableType = typeof(IEnumerable);

		private bool IsList => (options & 1) != 0;

		private bool SuppressIList => (options & 2) != 0;

		private bool WritePacked => (options & 4) != 0;

		private bool SupportNull => (options & 0x20) != 0;

		private bool ReturnList => (options & 8) != 0;

		protected virtual bool RequireAdd => true;

		public override Type ExpectedType => declaredType;

		public override bool RequiresOldValue => AppendToCollection;

		public override bool ReturnsValue => ReturnList;

		protected bool AppendToCollection => (options & 0x10) == 0;

		internal static bool CanPack(WireType wireType)
		{
			if ((uint)wireType <= 1u || wireType == WireType.Fixed32 || wireType == WireType.SignedVariant)
			{
				return true;
			}
			return false;
		}

		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 var builderFactory, out var isEmpty, out var length, out var methodInfo, out var addRange, out var finish))
			{
				return new ImmutableCollectionDecorator(model, declaredType, concreteType, tail, fieldNumber, writePacked, packedWireType, returnList, overwriteList, supportNull, builderFactory, isEmpty, length, methodInfo, 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 |= 8;
			}
			if (overwriteList)
			{
				options |= 16;
			}
			if (supportNull)
			{
				options |= 32;
			}
			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 |= 4;
			}
			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;
			if (!RequireAdd)
			{
				return;
			}
			add = TypeModel.ResolveListAdd(model, declaredType, tail.ExpectedType, out var isList);
			if (isList)
			{
				options |= 1;
				string fullName = declaredType.FullName;
				if (fullName != null && fullName.StartsWith("System.Data.Linq.EntitySet`1[["))
				{
					options |= 2;
				}
			}
			if (add == null)
			{
				throw new InvalidOperationException("Unable to resolve a suitable Add method for " + declaredType.FullName);
			}
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			bool returnList = ReturnList;
			using Local local = (AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : new Local(ctx, declaredType));
			using Local local2 = ((returnList && AppendToCollection && !Helpers.IsValueType(ExpectedType)) ? new Local(ctx, ExpectedType) : null);
			if (!AppendToCollection)
			{
				ctx.LoadNullRef();
				ctx.StoreValue(local);
			}
			else if (returnList && local2 != null)
			{
				ctx.LoadValue(local);
				ctx.StoreValue(local2);
			}
			if (concreteType != null)
			{
				ctx.LoadValue(local);
				CodeLabel label = ctx.DefineLabel();
				ctx.BranchIfTrue(label, @short: true);
				ctx.EmitCtor(concreteType);
				ctx.StoreValue(local);
				ctx.MarkLabel(label);
			}
			bool castListForAdd = !add.DeclaringType.IsAssignableFrom(declaredType);
			EmitReadList(ctx, local, Tail, add, packedWireType, castListForAdd);
			if (returnList)
			{
				if (AppendToCollection && local2 != null)
				{
					ctx.LoadValue(local2);
					ctx.LoadValue(local);
					CodeLabel label2 = ctx.DefineLabel();
					CodeLabel label3 = ctx.DefineLabel();
					ctx.BranchIfEqual(label2, @short: true);
					ctx.LoadValue(local);
					ctx.Branch(label3, @short: true);
					ctx.MarkLabel(label2);
					ctx.LoadNullRef();
					ctx.MarkLabel(label3);
				}
				else
				{
					ctx.LoadValue(local);
				}
			}
		}

		internal static void EmitReadList(CompilerContext ctx, Local list, IProtoSerializer tail, MethodInfo add, WireType packedWireType, bool castListForAdd)
		{
			using Local local = new Local(ctx, ctx.MapType(typeof(int)));
			CodeLabel label = ((packedWireType == WireType.None) ? default(CodeLabel) : ctx.DefineLabel());
			if (packedWireType != WireType.None)
			{
				ctx.LoadReaderWriter();
				ctx.LoadValue(typeof(ProtoReader).GetProperty("WireType"));
				ctx.LoadValue(2);
				ctx.BranchIfEqual(label, @short: false);
			}
			ctx.LoadReaderWriter();
			ctx.LoadValue(typeof(ProtoReader).GetProperty("FieldNumber"));
			ctx.StoreValue(local);
			CodeLabel label2 = ctx.DefineLabel();
			ctx.MarkLabel(label2);
			EmitReadAndAddItem(ctx, list, tail, add, castListForAdd);
			ctx.LoadReaderWriter();
			ctx.LoadValue(local);
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("TryReadFieldHeader"));
			ctx.BranchIfTrue(label2, @short: false);
			if (packedWireType != WireType.None)
			{
				CodeLabel label3 = ctx.DefineLabel();
				ctx.Branch(label3, @short: false);
				ctx.MarkLabel(label);
				ctx.LoadReaderWriter();
				ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem"));
				CodeLabel label4 = ctx.DefineLabel();
				CodeLabel label5 = ctx.DefineLabel();
				ctx.MarkLabel(label4);
				ctx.LoadValue((int)packedWireType);
				ctx.LoadReaderWriter();
				ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("HasSubValue"));
				ctx.BranchIfFalse(label5, @short: false);
				EmitReadAndAddItem(ctx, list, tail, add, castListForAdd);
				ctx.Branch(label4, @short: false);
				ctx.MarkLabel(label5);
				ctx.LoadReaderWriter();
				ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem"));
				ctx.MarkLabel(label3);
			}
		}

		private static void EmitReadAndAddItem(CompilerContext ctx, Local list, IProtoSerializer tail, MethodInfo add, bool castListForAdd)
		{
			ctx.LoadAddress(list, list.Type);
			if (castListForAdd)
			{
				ctx.Cast(add.DeclaringType);
			}
			Type expectedType = tail.ExpectedType;
			bool returnsValue = tail.ReturnsValue;
			if (tail.RequiresOldValue)
			{
				if (Helpers.IsValueType(expectedType) || !returnsValue)
				{
					using Local local = new Local(ctx, expectedType);
					if (Helpers.IsValueType(expectedType))
					{
						ctx.LoadAddress(local, expectedType);
						ctx.EmitCtor(expectedType);
					}
					else
					{
						ctx.LoadNullRef();
						ctx.StoreValue(local);
					}
					tail.EmitRead(ctx, local);
					if (!returnsValue)
					{
						ctx.LoadValue(local);
					}
				}
				else
				{
					ctx.LoadNullRef();
					tail.EmitRead(ctx, null);
				}
			}
			else
			{
				if (!returnsValue)
				{
					throw new InvalidOperationException();
				}
				tail.EmitRead(ctx, null);
			}
			Type parameterType = add.GetParameters()[0].ParameterType;
			if (parameterType != expectedType)
			{
				if (parameterType == ctx.MapType(typeof(object)))
				{
					ctx.CastToObject(expectedType);
				}
				else
				{
					if (!(Helpers.GetUnderlyingType(parameterType) == expectedType))
					{
						throw new InvalidOperationException("Conflicting item/add type");
					}
					ConstructorInfo constructor = Helpers.GetConstructor(parameterType, new Type[1] { expectedType }, nonPublic: false);
					ctx.EmitCtor(constructor);
				}
			}
			ctx.EmitCall(add, list.Type);
			if (add.ReturnType != ctx.MapType(typeof(void)))
			{
				ctx.DiscardValue();
			}
		}

		protected MethodInfo GetEnumeratorInfo(TypeModel model, out MethodInfo moveNext, out MethodInfo current)
		{
			return 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)
		{
			Type type = null;
			MethodInfo instanceMethod = Helpers.GetInstanceMethod(expectedType, "GetEnumerator", null);
			Type type2 = null;
			Type type3;
			if (instanceMethod != null)
			{
				type2 = instanceMethod.ReturnType;
				type3 = type2;
				moveNext = Helpers.GetInstanceMethod(type3, "MoveNext", null);
				PropertyInfo property = Helpers.GetProperty(type3, "Current", nonPublic: false);
				current = ((property == null) ? null : Helpers.GetGetMethod(property, nonPublic: false, allowInternal: false));
				if (moveNext == null && model.MapType(ienumeratorType).IsAssignableFrom(type3))
				{
					moveNext = Helpers.GetInstanceMethod(model.MapType(ienumeratorType), "MoveNext", null);
				}
				if (moveNext != null && moveNext.ReturnType == model.MapType(typeof(bool)) && current != null && current.ReturnType == itemType)
				{
					return instanceMethod;
				}
				moveNext = (current = (instanceMethod = null));
			}
			Type type4 = model.MapType(typeof(IEnumerable<>), demand: false);
			if (type4 != null)
			{
				type4 = type4.MakeGenericType(itemType);
				type = type4;
			}
			if (type != null && type.IsAssignableFrom(expectedType))
			{
				instanceMethod = Helpers.GetInstanceMethod(type, "GetEnumerator");
				type2 = instanceMethod.ReturnType;
				type3 = type2;
				moveNext = Helpers.GetInstanceMethod(model.MapType(ienumeratorType), "MoveNext");
				current = Helpers.GetGetMethod(Helpers.GetProperty(type3, "Current", nonPublic: false), nonPublic: false, allowInternal: false);
				return instanceMethod;
			}
			type = model.MapType(ienumerableType);
			instanceMethod = Helpers.GetInstanceMethod(type, "GetEnumerator");
			type2 = instanceMethod.ReturnType;
			type3 = type2;
			moveNext = Helpers.GetInstanceMethod(type3, "MoveNext");
			current = Helpers.GetGetMethod(Helpers.GetProperty(type3, "Current", nonPublic: false), nonPublic: false, allowInternal: false);
			return instanceMethod;
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			using Local local = ctx.GetLocalWithValue(ExpectedType, valueFrom);
			MethodInfo moveNext;
			MethodInfo current;
			MethodInfo enumeratorInfo = GetEnumeratorInfo(ctx.Model, out moveNext, out current);
			Type returnType = enumeratorInfo.ReturnType;
			bool writePacked = WritePacked;
			using Local local2 = new Local(ctx, returnType);
			using Local local3 = (writePacked ? new Local(ctx, ctx.MapType(typeof(SubItemToken))) : null);
			if (writePacked)
			{
				ctx.LoadValue(fieldNumber);
				ctx.LoadValue(2);
				ctx.LoadReaderWriter();
				ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("WriteFieldHeader"));
				ctx.LoadValue(local);
				ctx.LoadReaderWriter();
				ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("StartSubItem"));
				ctx.StoreValue(local3);
				ctx.LoadValue(fieldNumber);
				ctx.LoadReaderWriter();
				ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("SetPackedField"));
			}
			ctx.LoadAddress(local, ExpectedType);
			ctx.EmitCall(enumeratorInfo, ExpectedType);
			ctx.StoreValue(local2);
			using (ctx.Using(local2))
			{
				CodeLabel label = ctx.DefineLabel();
				CodeLabel label2 = ctx.DefineLabel();
				ctx.Branch(label2, @short: false);
				ctx.MarkLabel(label);
				ctx.LoadAddress(local2, returnType);
				ctx.EmitCall(current, returnType);
				Type expectedType = Tail.ExpectedType;
				if (expectedType != ctx.MapType(typeof(object)) && current.ReturnType == ctx.MapType(typeof(object)))
				{
					ctx.CastFromObject(expectedType);
				}
				Tail.EmitWrite(ctx, null);
				ctx.MarkLabel(label2);
				ctx.LoadAddress(local2, returnType);
				ctx.EmitCall(moveNext, returnType);
				ctx.BranchIfTrue(label, @short: false);
			}
			if (writePacked)
			{
				ctx.LoadValue(local3);
				ctx.LoadReaderWriter();
				ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem"));
			}
		}

		public override void Write(object value, ProtoWriter dest)
		{
			bool writePacked = WritePacked;
			bool flag = (writePacked & CanUsePackedPrefix(value)) && value is ICollection;
			SubItemToken token;
			if (writePacked)
			{
				ProtoWriter.WriteFieldHeader(fieldNumber, WireType.String, dest);
				if (flag)
				{
					ProtoWriter.WritePackedPrefix(((ICollection)value).Count, packedWireType, dest);
					token = default(SubItemToken);
				}
				else
				{
					token = ProtoWriter.StartSubItem(value, dest);
				}
				ProtoWriter.SetPackedField(fieldNumber, dest);
			}
			else
			{
				token = default(SubItemToken);
			}
			bool flag2 = !SupportNull;
			foreach (object item in (IEnumerable)value)
			{
				if (flag2 && item == null)
				{
					throw new NullReferenceException();
				}
				Tail.Write(item, dest);
			}
			if (writePacked)
			{
				if (flag)
				{
					ProtoWriter.ClearPackedField(fieldNumber, dest);
				}
				else
				{
					ProtoWriter.EndSubItem(token, dest);
				}
			}
		}

		private bool CanUsePackedPrefix(object obj)
		{
			return ArrayDecorator.CanUsePackedPrefix(packedWireType, Tail.ExpectedType);
		}

		public override object Read(object value, ProtoReader source)
		{
			try
			{
				int field = source.FieldNumber;
				object obj = value;
				if (value == null)
				{
					value = Activator.CreateInstance(concreteType);
				}
				bool flag = IsList && !SuppressIList;
				if (packedWireType != WireType.None && source.WireType == WireType.String)
				{
					SubItemToken token = ProtoReader.StartSubItem(source);
					if (flag)
					{
						IList list = (IList)value;
						while (ProtoReader.HasSubValue(packedWireType, source))
						{
							list.Add(Tail.Read(null, source));
						}
					}
					else
					{
						object[] array = new object[1];
						while (ProtoReader.HasSubValue(packedWireType, source))
						{
							array[0] = Tail.Read(null, source);
							add.Invoke(value, array);
						}
					}
					ProtoReader.EndSubItem(token, source);
				}
				else if (flag)
				{
					IList list2 = (IList)value;
					do
					{
						list2.Add(Tail.Read(null, source));
					}
					while (source.TryReadFieldHeader(field));
				}
				else
				{
					object[] array2 = new object[1];
					do
					{
						array2[0] = Tail.Read(null, source);
						add.Invoke(value, array2);
					}
					while (source.TryReadFieldHeader(field));
				}
				return (obj == value) ? null : value;
			}
			catch (TargetInvocationException ex)
			{
				if (ex.InnerException != null)
				{
					throw ex.InnerException;
				}
				throw;
			}
		}
	}
	internal 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;

		private static readonly MethodInfo indexerSet = GetIndexerSetter();

		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; }

		internal MapDecorator(TypeModel model, Type concreteType, IProtoSerializer keyTail, IProtoSerializer valueTail, int fieldNumber, WireType wireType, WireType keyWireType, WireType valueWireType, bool overwriteList)
			: base((DefaultValue == null) ? ((ProtoDecoratorBase)new TagDecorator(2, valueWireType, strict: false, valueTail)) : ((ProtoDecoratorBase)new DefaultValueDecorator(model, DefaultValue, new TagDecorator(2, valueWireType, strict: false, valueTail))))
		{
			this.wireType = wireType;
			this.keyTail = new DefaultValueDecorator(model, DefaultKey, new TagDecorator(1, keyWireType, strict: 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 MethodInfo GetIndexerSetter()
		{
			PropertyInfo[] properties = typeof(TDictionary).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (PropertyInfo propertyInfo in properties)
			{
				if (propertyInfo.Name != "Item" || propertyInfo.PropertyType != typeof(TValue))
				{
					continue;
				}
				ParameterInfo[] indexParameters = propertyInfo.GetIndexParameters();
				if (indexParameters != null && indexParameters.Length == 1 && !(indexParameters[0].ParameterType != typeof(TKey)))
				{
					MethodInfo setMethod = propertyInfo.GetSetMethod(nonPublic: true);
					if (setMethod != null)
					{
						return setMethod;
					}
				}
			}
			throw new InvalidOperationException("Unable to resolve indexer for map");
		}

		public override object Read(object untyped, ProtoReader source)
		{
			TDictionary val = (AppendToCollection ? ((TDictionary)untyped) : null);
			if (val == null)
			{
				val = (TDictionary)Activator.CreateInstance(concreteType);
			}
			do
			{
				TKey key = DefaultKey;
				TValue val2 = DefaultValue;
				SubItemToken token = ProtoReader.StartSubItem(source);
				int num;
				while ((num = source.ReadFieldHeader()) > 0)
				{
					switch (num)
					{
					case 1:
						key = (TKey)keyTail.Read(null, source);
						break;
					case 2:
						val2 = (TValue)Tail.Read(Tail.RequiresOldValue ? ((object)val2) : null, source);
						break;
					default:
						source.SkipField();
						break;
					}
				}
				ProtoReader.EndSubItem(token, source);
				val[key] = val2;
			}
			while (source.TryReadFieldHeader(fieldNumber));
			return val;
		}

		public override void Write(object untyped, ProtoWriter dest)
		{
			foreach (KeyValuePair<TKey, TValue> item in (TDictionary)untyped)
			{
				ProtoWriter.WriteFieldHeader(fieldNumber, wireType, dest);
				SubItemToken token = ProtoWriter.StartSubItem(null, dest);
				if (item.Key != null)
				{
					keyTail.Write(item.Key, dest);
				}
				if (item.Value != null)
				{
					Tail.Write(item.Value, dest);
				}
				ProtoWriter.EndSubItem(token, dest);
			}
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			Type typeFromHandle = typeof(KeyValuePair<TKey, TValue>);
			MethodInfo moveNext;
			MethodInfo current;
			MethodInfo enumeratorInfo = ListDecorator.GetEnumeratorInfo(ctx.Model, ExpectedType, typeFromHandle, out moveNext, out current);
			Type returnType = enumeratorInfo.ReturnType;
			MethodInfo getMethod = typeFromHandle.GetProperty("Key").GetGetMethod();
			MethodInfo getMethod2 = typeFromHandle.GetProperty("Value").GetGetMethod();
			using Local local = ctx.GetLocalWithValue(ExpectedType, valueFrom);
			using Local local2 = new Local(ctx, returnType);
			using Local local3 = new Local(ctx, typeof(SubItemToken));
			using Local local4 = new Local(ctx, typeFromHandle);
			ctx.LoadAddress(local, ExpectedType);
			ctx.EmitCall(enumeratorInfo, ExpectedType);
			ctx.StoreValue(local2);
			using (ctx.Using(local2))
			{
				CodeLabel label = ctx.DefineLabel();
				CodeLabel label2 = ctx.DefineLabel();
				ctx.Branch(label2, @short: false);
				ctx.MarkLabel(label);
				ctx.LoadAddress(local2, returnType);
				ctx.EmitCall(current, returnType);
				if (typeFromHandle != ctx.MapType(typeof(object)) && current.ReturnType == ctx.MapType(typeof(object)))
				{
					ctx.CastFromObject(typeFromHandle);
				}
				ctx.StoreValue(local4);
				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(local3);
				ctx.LoadAddress(local4, typeFromHandle);
				ctx.EmitCall(getMethod, typeFromHandle);
				ctx.WriteNullCheckedTail(typeof(TKey), keyTail, null);
				ctx.LoadAddress(local4, typeFromHandle);
				ctx.EmitCall(getMethod2, typeFromHandle);
				ctx.WriteNullCheckedTail(typeof(TValue), Tail, null);
				ctx.LoadValue(local3);
				ctx.LoadReaderWriter();
				ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem"));
				ctx.MarkLabel(label2);
				ctx.LoadAddress(local2, returnType);
				ctx.EmitCall(moveNext, returnType);
				ctx.BranchIfTrue(label, @short: false);
			}
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			using Local local = (AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : new Local(ctx, typeof(TDictionary)));
			using Local local2 = new Local(ctx, typeof(SubItemToken));
			using Local local3 = new Local(ctx, typeof(TKey));
			using Local local4 = new Local(ctx, typeof(TValue));
			using Local local5 = new Local(ctx, ctx.MapType(typeof(int)));
			if (!AppendToCollection)
			{
				ctx.LoadNullRef();
				ctx.StoreValue(local);
			}
			if (concreteType != null)
			{
				ctx.LoadValue(local);
				CodeLabel label = ctx.DefineLabel();
				ctx.BranchIfTrue(label, @short: true);
				ctx.EmitCtor(concreteType);
				ctx.StoreValue(local);
				ctx.MarkLabel(label);
			}
			CodeLabel label2 = ctx.DefineLabel();
			ctx.MarkLabel(label2);
			if (typeof(TKey) == typeof(string))
			{
				ctx.LoadValue("");
				ctx.StoreValue(local3);
			}
			else
			{
				ctx.InitLocal(typeof(TKey), local3);
			}
			if (typeof(TValue) == typeof(string))
			{
				ctx.LoadValue("");
				ctx.StoreValue(local4);
			}
			else
			{
				ctx.InitLocal(typeof(TValue), local4);
			}
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem"));
			ctx.StoreValue(local2);
			CodeLabel label3 = ctx.DefineLabel();
			CodeLabel label4 = ctx.DefineLabel();
			ctx.Branch(label3, @short: false);
			ctx.MarkLabel(label4);
			ctx.LoadValue(local5);
			CodeLabel codeLabel = ctx.DefineLabel();
			CodeLabel codeLabel2 = ctx.DefineLabel();
			CodeLabel codeLabel3 = ctx.DefineLabel();
			ctx.Switch(new CodeLabel[3] { codeLabel, codeLabel2, codeLabel3 });
			ctx.MarkLabel(codeLabel);
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
			ctx.Branch(label3, @short: false);
			ctx.MarkLabel(codeLabel2);
			keyTail.EmitRead(ctx, null);
			ctx.StoreValue(local3);
			ctx.Branch(label3, @short: false);
			ctx.MarkLabel(codeLabel3);
			Tail.EmitRead(ctx, Tail.RequiresOldValue ? local4 : null);
			ctx.StoreValue(local4);
			ctx.MarkLabel(label3);
			ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
			ctx.CopyValue();
			ctx.StoreValue(local5);
			ctx.LoadValue(0);
			ctx.BranchIfGreater(label4, @short: false);
			ctx.LoadValue(local2);
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem"));
			ctx.LoadAddress(local, ExpectedType);
			ctx.LoadValue(local3);
			ctx.LoadValue(local4);
			ctx.EmitCall(indexerSet);
			ctx.LoadReaderWriter();
			ctx.LoadValue(fieldNumber);
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("TryReadFieldHeader"));
			ctx.BranchIfTrue(label2, @short: false);
			if (ReturnsValue)
			{
				ctx.LoadValue(local);
			}
		}
	}
	internal sealed class MemberSpecifiedDecorator : ProtoDecoratorBase
	{
		private readonly MethodInfo getSpecified;

		private readonly MethodInfo setSpecified;

		public override Type ExpectedType => Tail.ExpectedType;

		public override bool RequiresOldValue => Tail.RequiresOldValue;

		public override bool ReturnsValue => Tail.ReturnsValue;

		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[1] { true });
			}
			return result;
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			if (getSpecified == null)
			{
				Tail.EmitWrite(ctx, valueFrom);
				return;
			}
			using Local local = ctx.GetLocalWithValue(ExpectedType, valueFrom);
			ctx.LoadAddress(local, ExpectedType);
			ctx.EmitCall(getSpecified);
			CodeLabel label = ctx.DefineLabel();
			ctx.BranchIfFalse(label, @short: false);
			Tail.EmitWrite(ctx, local);
			ctx.MarkLabel(label);
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			if (setSpecified == null)
			{
				Tail.EmitRead(ctx, valueFrom);
				return;
			}
			using Local local = ctx.GetLocalWithValue(ExpectedType, valueFrom);
			Tail.EmitRead(ctx, local);
			ctx.LoadAddress(local, ExpectedType);
			ctx.LoadValue(1);
			ctx.EmitCall(setSpecified);
		}
	}
	internal sealed class NetObjectSerializer : IProtoSerializer
	{
		private readonly int key;

		private readonly Type type;

		private readonly BclHelpers.NetObjectOptions options;

		public Type ExpectedType => type;

		public bool ReturnsValue => true;

		public bool RequiresOldValue => true;

		public NetObjectSerializer(TypeModel model, Type type, int key, BclHelpers.NetObjectOptions options)
		{
			bool flag = (options & BclHelpers.NetObjectOptions.DynamicType) != 0;
			this.key = (flag ? (-1) : key);
			this.type = (flag ? model.MapType(typeof(object)) : type);
			this.options = options;
		}

		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);
		}

		public void EmitRead(CompilerContext ctx, 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(CompilerContext ctx, 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"));
		}
	}
	internal sealed class NullDecorator : ProtoDecoratorBase
	{
		private readonly Type expectedType;

		public const int Tag = 1;

		public override Type ExpectedType => expectedType;

		public override bool ReturnsValue => true;

		public override bool RequiresOldValue => true;

		public NullDecorator(TypeModel model, IProtoSerializer tail)
			: base(tail)
		{
			if (!tail.ReturnsValue)
			{
				throw new NotSupportedException("NullDecorator only supports implementations that return values");
			}
			Type type = tail.ExpectedType;
			if (Helpers.IsValueType(type))
			{
				expectedType = model.MapType(typeof(Nullable<>)).MakeGenericType(type);
			}
			else
			{
				expectedType = type;
			}
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			using Local local = ctx.GetLocalWithValue(expectedType, valueFrom);
			using Local local2 = new Local(ctx, ctx.MapType(typeof(SubItemToken)));
			using Local local3 = new Local(ctx, ctx.MapType(typeof(int)));
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem"));
			ctx.StoreValue(local2);
			CodeLabel label = ctx.DefineLabel();
			CodeLabel label2 = ctx.DefineLabel();
			CodeLabel label3 = ctx.DefineLabel();
			ctx.MarkLabel(label);
			ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
			ctx.CopyValue();
			ctx.StoreValue(local3);
			ctx.LoadValue(1);
			ctx.BranchIfEqual(label2, @short: true);
			ctx.LoadValue(local3);
			ctx.LoadValue(1);
			ctx.BranchIfLess(label3, @short: false);
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
			ctx.Branch(label, @short: true);
			ctx.MarkLabel(label2);
			if (Tail.RequiresOldValue)
			{
				if (Helpers.IsValueType(expectedType))
				{
					ctx.LoadAddress(local, expectedType);
					ctx.EmitCall(expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
				}
				else
				{
					ctx.LoadValue(local);
				}
			}
			Tail.EmitRead(ctx, null);
			if (Helpers.IsValueType(expectedType))
			{
				ctx.EmitCtor(expectedType, Tail.ExpectedType);
			}
			ctx.StoreValue(local);
			ctx.Branch(label, @short: false);
			ctx.MarkLabel(label3);
			ctx.LoadValue(local2);
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem"));
			ctx.LoadValue(local);
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			using Local local = ctx.GetLocalWithValue(expectedType, valueFrom);
			using Local local2 = new Local(ctx, ctx.MapType(typeof(SubItemToken)));
			ctx.LoadNullRef();
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("StartSubItem"));
			ctx.StoreValue(local2);
			if (Helpers.IsValueType(expectedType))
			{
				ctx.LoadAddress(local, expectedType);
				ctx.LoadValue(expectedType.GetProperty("HasValue"));
			}
			else
			{
				ctx.LoadValue(local);
			}
			CodeLabel label = ctx.DefineLabel();
			ctx.BranchIfFalse(label, @short: false);
			if (Helpers.IsValueType(expectedType))
			{
				ctx.LoadAddress(local, expectedType);
				ctx.EmitCall(expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
			}
			else
			{
				ctx.LoadValue(local);
			}
			Tail.EmitWrite(ctx, null);
			ctx.MarkLabel(label);
			ctx.LoadValue(local2);
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem"));
		}

		public override object Read(object value, ProtoReader source)
		{
			SubItemToken token = ProtoReader.StartSubItem(source);
			int num;
			while ((num = source.ReadFieldHeader()) > 0)
			{
				if (num == 1)
				{
					value = Tail.Read(value, source);
				}
				else
				{
					source.SkipField();
				}
			}
			ProtoReader.EndSubItem(token, 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);
		}
	}
	internal sealed class ParseableSerializer : IProtoSerializer
	{
		private readonly MethodInfo parse;

		public Type ExpectedType => parse.DeclaringType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public static ParseableSerializer TryCreate(Type type, TypeModel model)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			MethodInfo method = type.GetMethod("Parse", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, null, new Type[1] { model.MapType(typeof(string)) }, null);
			if (method != null && method.ReturnType == type)
			{
				if (Helpers.IsValueType(type))
				{
					MethodInfo customToString = GetCustomToString(type);
					if (customToString == null || customToString.ReturnType != model.MapType(typeof(string)))
					{
						return null;
					}
				}
				return new ParseableSerializer(method);
			}
			return null;
		}

		private static MethodInfo GetCustomToString(Type type)
		{
			return type.GetMethod("ToString", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public, null, Helpers.EmptyTypes, null);
		}

		private ParseableSerializer(MethodInfo parse)
		{
			this.parse = parse;
		}

		public object Read(object value, ProtoReader source)
		{
			return parse.Invoke(null, new object[1] { source.ReadString() });
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteString(value.ToString(), dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			Type expectedType = ExpectedType;
			if (Helpers.IsValueType(expectedType))
			{
				using Local local = ctx.GetLocalWithValue(expectedType, valueFrom);
				ctx.LoadAddress(local, expectedType);
				ctx.EmitCall(GetCustomToString(expectedType));
			}
			else
			{
				ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("ToString"));
			}
			ctx.EmitBasicWrite("WriteString", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadString", ctx.MapType(typeof(string)));
			ctx.EmitCall(parse);
		}
	}
	internal sealed class PropertyDecorator : ProtoDecoratorBase
	{
		private readonly PropertyInfo property;

		private readonly Type forType;

		private readonly bool readOptionsWriteValue;

		private readonly MethodInfo shadowSetter;

		public override Type ExpectedType => forType;

		public override bool RequiresOldValue => true;

		public override bool ReturnsValue => false;

		public PropertyDecorator(TypeModel model, Type forType, PropertyInfo property, IProtoSerializer tail)
			: base(tail)
		{
			this.forType = forType;
			this.property = property;
			SanityCheck(model, property, tail, out readOptionsWriteValue, nonPublic: true, allowInternal: 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)))
			{
				throw new InvalidOperationException("Cannot apply changes to property " + property.DeclaringType.FullName + "." + property.Name);
			}
		}

		private static MethodInfo GetShadowSetter(TypeModel model, PropertyInfo property)
		{
			Type reflectedType = property.ReflectedType;
			MethodInfo instanceMethod = Helpers.GetInstanceMethod(reflectedType, "Set" + property.Name, new Type[1] { property.PropertyType });
			if (instanceMethod == null || !instanceMethod.IsPublic || instanceMethod.ReturnType != model.MapType(typeof(void)))
			{
				return null;
			}
			return instanceMethod;
		}

		public override void Write(object value, ProtoWriter dest)
		{
			value = property.GetValue(value, null);
			if (value != null)
			{
				Tail.Write(value, dest);
			}
		}

		public override object Read(object value, ProtoReader source)
		{
			object value2 = (Tail.RequiresOldValue ? property.GetValue(value, null) : null);
			object obj = Tail.Read(value2, source);
			if (readOptionsWriteValue && obj != null)
			{
				if (shadowSetter == null)
				{
					property.SetValue(value, obj, null);
				}
				else
				{
					shadowSetter.Invoke(value, new object[1] { obj });
				}
			}
			return null;
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.LoadAddress(valueFrom, ExpectedType);
			ctx.LoadValue(property);
			ctx.WriteNullCheckedTail(property.PropertyType, Tail, null);
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			SanityCheck(ctx.Model, property, Tail, out var 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 Local local = ctx.GetLocalWithValue(ExpectedType, valueFrom);
			if (Tail.RequiresOldValue)
			{
				ctx.LoadAddress(local, ExpectedType);
				ctx.LoadValue(property);
			}
			Type propertyType = property.PropertyType;
			ctx.ReadNullCheckedTail(propertyType, Tail, null);
			if (writeValue)
			{
				using (Local local2 = new Local(ctx, property.PropertyType))
				{
					ctx.StoreValue(local2);
					CodeLabel label = default(CodeLabel);
					if (!Helpers.IsValueType(propertyType))
					{
						label = ctx.DefineLabel();
						ctx.LoadValue(local2);
						ctx.BranchIfFalse(label, @short: true);
					}
					ctx.LoadAddress(local, ExpectedType);
					ctx.LoadValue(local2);
					if (shadowSetter == null)
					{
						ctx.StoreValue(property);
					}
					else
					{
						ctx.EmitCall(shadowSetter);
					}
					if (!Helpers.IsValueType(propertyType))
					{
						ctx.MarkLabel(label);
					}
					return;
				}
			}
			if (Tail.ReturnsValue)
			{
				ctx.DiscardValue();
			}
		}

		internal static bool CanWrite(TypeModel model, MemberInfo member)
		{
			if (member == null)
			{
				throw new ArgumentNullException("member");
			}
			if (member is PropertyInfo propertyInfo)
			{
				if (!propertyInfo.CanWrite)
				{
					return GetShadowSetter(model, propertyInfo) != null;
				}
				return true;
			}
			return member is FieldInfo;
		}
	}
	internal abstract class ProtoDecoratorBase : IProtoSerializer
	{
		protected readonly IProtoSerializer Tail;

		public abstract Type ExpectedType { get; }

		public abstract bool ReturnsValue { get; }

		public abstract bool RequiresOldValue { get; }

		protected ProtoDecoratorBase(IProtoSerializer tail)
		{
			Tail = tail;
		}

		public abstract void Write(object value, ProtoWriter dest);

		public abstract object Read(object value, ProtoReader source);

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			EmitWrite(ctx, valueFrom);
		}

		protected abstract void EmitWrite(CompilerContext ctx, Local valueFrom);

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			EmitRead(ctx, valueFrom);
		}

		protected abstract void EmitRead(CompilerContext ctx, Local valueFrom);
	}
	internal sealed class SByteSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(sbyte);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public SByteSerializer(TypeModel model)
		{
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadSByte();
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteSByte((sbyte)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteSByte", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadSByte", ExpectedType);
		}
	}
	internal sealed class SingleSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(float);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public SingleSerializer(TypeModel model)
		{
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadSingle();
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteSingle((float)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteSingle", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadSingle", ExpectedType);
		}
	}
	internal sealed class StringSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(string);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public StringSerializer(TypeModel model)
		{
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteString((string)value, dest);
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadString();
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteString", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadString", ExpectedType);
		}
	}
	internal sealed class SubItemSerializer : IProtoTypeSerializer, IProtoSerializer
	{
		private readonly int key;

		private readonly Type type;

		private readonly ISerializerProxy proxy;

		private readonly bool recursionCheck;

		Type IProtoSerializer.ExpectedType => type;

		bool IProtoSerializer.RequiresOldValue => true;

		bool IProtoSerializer.ReturnsValue => true;

		bool IProtoTypeSerializer.HasCallbacks(TypeModel.CallbackType callbackType)
		{
			return ((IProtoTypeSerializer)proxy.Serializer).HasCallbacks(callbackType);
		}

		bool IProtoTypeSerializer.CanCreateInstance()
		{
			return ((IProtoTypeSerializer)proxy.Serializer).CanCreateInstance();
		}

		void IProtoTypeSerializer.EmitCallback(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType)
		{
			((IProtoTypeSerializer)proxy.Serializer).EmitCallback(ctx, valueFrom, callbackType);
		}

		void IProtoTypeSerializer.EmitCreateInstance(CompilerContext ctx)
		{
			((IProtoTypeSerializer)proxy.Serializer).EmitCreateInstance(ctx);
		}

		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);
		}

		public SubItemSerializer(Type type, int key, ISerializerProxy proxy, bool recursionCheck)
		{
			this.type = type ?? throw new ArgumentNullException("type");
			this.proxy = proxy ?? throw new ArgumentNullException("proxy");
			this.key = key;
			this.recursionCheck = recursionCheck;
		}

		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);
		}

		private bool EmitDedicatedMethod(CompilerContext ctx, Local valueFrom, bool read)
		{
			MethodBuilder dedicatedMethod = ctx.GetDedicatedMethod(key, read);
			if (dedicatedMethod == null)
			{
				return false;
			}
			using (Local local = new Local(ctx, ctx.MapType(typeof(SubItemToken))))
			{
				Type type = ctx.MapType(read ? typeof(ProtoReader) : typeof(ProtoWriter));
				ctx.LoadValue(valueFrom);
				if (!read)
				{
					if (Helpers.IsValueType(this.type) || !recursionCheck)
					{
						ctx.LoadNullRef();
					}
					else
					{
						ctx.CopyValue();
					}
				}
				ctx.LoadReaderWriter();
				ctx.EmitCall(Helpers.GetStaticMethod(type, "StartSubItem", (!read) ? new Type[2]
				{
					ctx.MapType(typeof(object)),
					type
				} : new Type[1] { type }));
				ctx.StoreValue(local);
				ctx.LoadReaderWriter();
				ctx.EmitCall(dedicatedMethod);
				if (read && this.type != dedicatedMethod.ReturnType)
				{
					ctx.Cast(this.type);
				}
				ctx.LoadValue(local);
				ctx.LoadReaderWriter();
				ctx.EmitCall(Helpers.GetStaticMethod(type, "EndSubItem", new Type[2]
				{
					ctx.MapType(typeof(SubItemToken)),
					type
				}));
			}
			return true;
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			if (!EmitDedicatedMethod(ctx, valueFrom, read: false))
			{
				ctx.LoadValue(valueFrom);
				if (Helpers.IsValueType(type))
				{
					ctx.CastToObject(type);
				}
				ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key));
				ctx.LoadReaderWriter();
				ctx.EmitCall(Helpers.GetStaticMethod(ctx.MapType(typeof(ProtoWriter)), recursionCheck ? "WriteObject" : "WriteRecursionSafeObject", new Type[3]
				{
					ctx.MapType(typeof(object)),
					ctx.MapType(typeof(int)),
					ctx.MapType(typeof(ProtoWriter))
				}));
			}
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			if (!EmitDedicatedMethod(ctx, valueFrom, read: true))
			{
				ctx.LoadValue(valueFrom);
				if (Helpers.IsValueType(type))
				{
					ctx.CastToObject(type);
				}
				ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key));
				ctx.LoadReaderWriter();
				ctx.EmitCall(Helpers.GetStaticMethod(ctx.MapType(typeof(ProtoReader)), "ReadObject"));
				ctx.CastFromObject(type);
			}
		}
	}
	internal sealed class SurrogateSerializer : IProtoTypeSerializer, IProtoSerializer
	{
		private readonly Type forType;

		private readonly Type declaredType;

		private readonly MethodInfo toTail;

		private readonly MethodInfo fromTail;

		private IProtoTypeSerializer rootTail;

		public bool ReturnsValue => false;

		public bool RequiresOldValue => true;

		public Type ExpectedType => forType;

		bool IProtoTypeSerializer.HasCallbacks(TypeModel.CallbackType callbackType)
		{
			return false;
		}

		void IProtoTypeSerializer.EmitCallback(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType)
		{
		}

		void IProtoTypeSerializer.EmitCreateInstance(CompilerContext ctx)
		{
			throw new NotSupportedException();
		}

		bool IProtoTypeSerializer.CanCreateInstance()
		{
			return false;
		}

		object IProtoTypeSerializer.CreateInstance(ProtoReader source)
		{
			throw new NotSupportedException();
		}

		void IProtoTypeSerializer.Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
		{
		}

		public SurrogateSerializer(TypeModel model, Type forType, Type declaredType, IProtoTypeSerializer rootTail)
		{
			this.forType = forType;
			this.declaredType = declaredType;
			this.rootTail = rootTail;
			toTail = GetConversion(model, toTail: true);
			fromTail = GetConversion(model, toTail: false);
		}

		private static bool HasCast(TypeModel model, Type type, Type from, Type to, out MethodInfo op)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			Type type2 = null;
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.ReturnType != to)
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length != 1 || !(parameters[0].ParameterType == from))
				{
					continue;
				}
				if (type2 == null)
				{
					type2 = model.MapType(typeof(ProtoConverterAttribute), demand: false);
					if (type2 == null)
					{
						break;
					}
				}
				if (methodInfo.IsDefined(type2, inherit: true))
				{
					op = methodInfo;
					return true;
				}
			}
			foreach (MethodInfo methodInfo2 in methods)
			{
				if ((!(methodInfo2.Name != "op_Implicit") || !(methodInfo2.Name != "op_Explicit")) && !(methodInfo2.ReturnType != to))
				{
					ParameterInfo[] parameters = methodInfo2.GetParameters();
					if (parameters.Length == 1 && parameters[0].ParameterType == from)
					{
						op = methodInfo2;
						return true;
					}
				}
			}
			op = null;
			return false;
		}

		public MethodInfo GetConversion(TypeModel model, bool toTail)
		{
			Type to = (toTail ? declaredType : forType);
			Type type = (toTail ? forType : declaredType);
			if (HasCast(model, declaredType, type, to, out var op) || HasCast(model, forType, type, 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[1] { value }), writer);
		}

		public object Read(object value, ProtoReader source)
		{
			object[] array = new object[1] { value };
			value = toTail.Invoke(null, array);
			array[0] = rootTail.Read(value, source);
			return fromTail.Invoke(null, array);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			using Local local = new Local(ctx, declaredType);
			ctx.LoadValue(valueFrom);
			ctx.EmitCall(toTail);
			ctx.StoreValue(local);
			rootTail.EmitRead(ctx, local);
			ctx.LoadValue(local);
			ctx.EmitCall(fromTail);
			ctx.StoreValue(valueFrom);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.LoadValue(valueFrom);
			ctx.EmitCall(toTail);
			rootTail.EmitWrite(ctx, null);
		}
	}
	internal sealed class SystemTypeSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(Type);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public SystemTypeSerializer(TypeModel model)
		{
		}

		void IProtoSerializer.Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteType((Type)value, dest);
		}

		object IProtoSerializer.Read(object value, ProtoReader source)
		{
			return source.ReadType();
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteType", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadType", ExpectedType);
		}
	}
	internal sealed class TagDecorator : ProtoDecoratorBase, IProtoTypeSerializer, IProtoSerializer
	{
		private readonly bool strict;

		private readonly int fieldNumber;

		private readonly WireType wireType;

		public override Type ExpectedType => Tail.ExpectedType;

		public override bool RequiresOldValue => Tail.RequiresOldValue;

		public override bool ReturnsValue => Tail.ReturnsValue;

		private bool NeedsHint => (wireType & (WireType)(-8)) != 0;

		public bool HasCallbacks(TypeModel.CallbackType callbackType)
		{
			if (Tail is IProtoTypeSerializer protoTypeSerializer)
			{
				return protoTypeSerializer.HasCallbacks(callbackType);
			}
			return false;
		}

		public bool CanCreateInstance()
		{
			if (Tail is IProtoTypeSerializer protoTypeSerializer)
			{
				return protoTypeSerializer.CanCreateInstance();
			}
			return false;
		}

		public object CreateInstance(ProtoReader source)
		{
			return ((IProtoTypeSerializer)Tail).CreateInstance(source);
		}

		public void Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
		{
			if (Tail is IProtoTypeSerializer protoTypeSerializer)
			{
				protoTypeSerializer.Callback(value, callbackType, context);
			}
		}

		public void EmitCallback(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType)
		{
			((IProtoTypeSerializer)Tail).EmitCallback(ctx, valueFrom, callbackType);
		}

		public void EmitCreateInstance(CompilerContext ctx)
		{
			((IProtoTypeSerializer)Tail).EmitCreateInstance(ctx);
		}

		public TagDecorator(int fieldNumber, WireType wireType, bool strict, IProtoSerializer tail)
			: base(tail)
		{
			this.fieldNumber = fieldNumber;
			this.wireType = wireType;
			this.strict = strict;
		}

		public override object Read(object value, ProtoReader source)
		{
			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);
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.LoadValue(fieldNumber);
			ctx.LoadValue((int)wireType);
			ctx.LoadReaderWriter();
			ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("WriteFieldHeader"));
			Tail.EmitWrite(ctx, valueFrom);
		}

		protected override void EmitRead(CompilerContext ctx, 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);
		}
	}
	internal sealed class TimeSpanSerializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(TimeSpan);

		private readonly bool wellKnown;

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public TimeSpanSerializer(DataFormat dataFormat, TypeModel model)
		{
			wellKnown = dataFormat == DataFormat.WellKnown;
		}

		public object Read(object value, ProtoReader source)
		{
			if (wellKnown)
			{
				return BclHelpers.ReadDuration(source);
			}
			return BclHelpers.ReadTimeSpan(source);
		}

		public void Write(object value, ProtoWriter dest)
		{
			if (wellKnown)
			{
				BclHelpers.WriteDuration((TimeSpan)value, dest);
			}
			else
			{
				BclHelpers.WriteTimeSpan((TimeSpan)value, dest);
			}
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitWrite(ctx.MapType(typeof(BclHelpers)), wellKnown ? "WriteDuration" : "WriteTimeSpan", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			if (wellKnown)
			{
				ctx.LoadValue(valueFrom);
			}
			ctx.EmitBasicRead(ctx.MapType(typeof(BclHelpers)), wellKnown ? "ReadDuration" : "ReadTimeSpan", ExpectedType);
		}
	}
	internal sealed class TupleSerializer : IProtoTypeSerializer, IProtoSerializer
	{
		private readonly MemberInfo[] members;

		private readonly ConstructorInfo ctor;

		private IProtoSerializer[] tails;

		public Type ExpectedType => ctor.DeclaringType;

		public bool RequiresOldValue => true;

		public bool ReturnsValue => false;

		public TupleSerializer(RuntimeTypeModel model, ConstructorInfo ctor, MemberInfo[] members)
		{
			this.ctor = ctor ?? throw new ArgumentNullException("ctor");
			this.members = members ?? throw new ArgumentNullException("members");
			tails = new IProtoSerializer[members.Length];
			ParameterInfo[] parameters = ctor.GetParameters();
			for (int i = 0; i < members.Length; i++)
			{
				Type parameterType = parameters[i].ParameterType;
				Type itemType = null;
				Type defaultType = null;
				MetaType.ResolveListTypes(model, parameterType, ref itemType, ref defaultType);
				Type type = ((itemType == null) ? parameterType : itemType);
				bool asReference = false;
				int num = model.FindOrAddAuto(type, demand: false, addWithContractOnly: true, addEvenIfAutoDisabled: false);
				if (num >= 0)
				{
					asReference = model[type].AsReferenceDefault;
				}
				IProtoSerializer protoSerializer = ValueMember.TryGetCoreSerializer(model, DataFormat.Default, type, out var defaultWireType, asReference, dynamicType: false, overwriteList: false, allowComplexTypes: true);
				if (protoSerializer == null)
				{
					throw new InvalidOperationException("No serializer defined for type: " + type.FullName);
				}
				protoSerializer = new TagDecorator(i + 1, defaultWireType, strict: false, protoSerializer);
				IProtoSerializer protoSerializer2 = ((!(itemType == null)) ? ((!parameterType.IsArray) ? ((ProtoDecoratorBase)ListDecorator.Create(model, parameterType, defaultType, protoSerializer, i + 1, writePacked: false, defaultWireType, returnList: true, overwriteList: false, supportNull: false)) : ((ProtoDecoratorBase)new ArrayDecorator(model, protoSerializer, i + 1, writePacked: false, defaultWireType, parameterType, overwriteList: false, supportNull: false))) : protoSerializer);
				tails[i] = protoSerializer2;
			}
		}

		public bool HasCallbacks(TypeModel.CallbackType callbackType)
		{
			return false;
		}

		public void EmitCallback(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType)
		{
		}

		void IProtoTypeSerializer.Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
		{
		}

		object IProtoTypeSerializer.CreateInstance(ProtoReader source)
		{
			throw new NotSupportedException();
		}

		private object GetValue(object obj, int index)
		{
			PropertyInfo propertyInfo;
			if ((propertyInfo = members[index] as PropertyInfo) != null)
			{
				if (obj == null)
				{
					if (!Helpers.IsValueType(propertyInfo.PropertyType))
					{
						return null;
					}
					return Activator.CreateInstance(propertyInfo.PropertyType);
				}
				return propertyInfo.GetValue(obj, null);
			}
			FieldInfo fieldInfo;
			if ((fieldInfo = members[index] as FieldInfo) != null)
			{
				if (obj == null)
				{
					if (!Helpers.IsValueType(fieldInfo.FieldType))
					{
						return null;
					}
					return Activator.CreateInstance(fieldInfo.FieldType);
				}
				return fieldInfo.GetValue(obj);
			}
			throw new InvalidOperationException();
		}

		public object Read(object value, ProtoReader source)
		{
			object[] array = new object[members.Length];
			bool flag = false;
			if (value == null)
			{
				flag = true;
			}
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = GetValue(value, i);
			}
			int num;
			while ((num = source.ReadFieldHeader()) > 0)
			{
				flag = true;
				if (num <= tails.Length)
				{
					IProtoSerializer protoSerializer = tails[num - 1];
					array[num - 1] = tails[num - 1].Read(protoSerializer.RequiresOldValue ? array[num - 1] : null, source);
				}
				else
				{
					source.SkipField();
				}
			}
			if (!flag)
			{
				return value;
			}
			return ctor.Invoke(array);
		}

		public void Write(object value, ProtoWriter dest)
		{
			for (int i = 0; i < tails.Length; i++)
			{
				object value2 = GetValue(value, i);
				if (value2 != null)
				{
					tails[i].Write(value2, dest);
				}
			}
		}

		private Type GetMemberType(int index)
		{
			Type memberType = Helpers.GetMemberType(members[index]);
			if (memberType == null)
			{
				throw new InvalidOperationException();
			}
			return memberType;
		}

		bool IProtoTypeSerializer.CanCreateInstance()
		{
			return false;
		}

		public void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			using Local local = ctx.GetLocalWithValue(ctor.DeclaringType, valueFrom);
			for (int i = 0; i < tails.Length; i++)
			{
				Type memberType = GetMemberType(i);
				ctx.LoadAddress(local, ExpectedType);
				if (members[i] is FieldInfo)
				{
					ctx.LoadValue((FieldInfo)members[i]);
				}
				else if (members[i] is PropertyInfo)
				{
					ctx.LoadValue((PropertyInfo)members[i]);
				}
				ctx.WriteNullCheckedTail(memberType, tails[i], null);
			}
		}

		void IProtoTypeSerializer.EmitCreateInstance(CompilerContext ctx)
		{
			throw new NotSupportedException();
		}

		public void EmitRead(CompilerContext ctx, Local incoming)
		{
			using Local local = ctx.GetLocalWithValue(ExpectedType, incoming);
			Local[] array = new Local[members.Length];
			try
			{
				for (int i = 0; i < array.Length; i++)
				{
					Type memberType = GetMemberType(i);
					bool flag = true;
					array[i] = new Local(ctx, memberType);
					if (Helpers.IsValueType(ExpectedType))
					{
						continue;
					}
					if (Helpers.IsValueType(memberType))
					{
						switch (Helpers.GetTypeCode(memberType))
						{
						case ProtoTypeCode.Boolean:
						case ProtoTypeCode.SByte:
						case ProtoTypeCode.Byte:
						case ProtoTypeCode.Int16:
						case ProtoTypeCode.UInt16:
						case ProtoTypeCode.Int32:
						case ProtoTypeCode.UInt32:
							ctx.LoadValue(0);
							break;
						case ProtoTypeCode.Int64:
						case ProtoTypeCode.UInt64:
							ctx.LoadValue(0L);
							break;
						case ProtoTypeCode.Single:
							ctx.LoadValue(0f);
							break;
						case ProtoTypeCode.Double:
							ctx.LoadValue(0.0);
							break;
						case ProtoTypeCode.Decimal:
							ctx.LoadValue(0m);
							break;
						case ProtoTypeCode.Guid:
							ctx.LoadValue(Guid.Empty);
							break;
						default:
							ctx.LoadAddress(array[i], memberType);
							ctx.EmitCtor(memberType);
							flag = false;
							break;
						}
					}
					else
					{
						ctx.LoadNullRef();
					}
					if (flag)
					{
						ctx.StoreValue(array[i]);
					}
				}
				CodeLabel label = (Helpers.IsValueType(ExpectedType) ? default(CodeLabel) : ctx.DefineLabel());
				if (!Helpers.IsValueType(ExpectedType))
				{
					ctx.LoadAddress(local, ExpectedType);
					ctx.BranchIfFalse(label, @short: false);
				}
				for (int j = 0; j < members.Length; j++)
				{
					ctx.LoadAddress(local, ExpectedType);
					if (members[j] is FieldInfo)
					{
						ctx.LoadValue((FieldInfo)members[j]);
					}
					else if (members[j] is PropertyInfo)
					{
						ctx.LoadValue((PropertyInfo)members[j]);
					}
					ctx.StoreValue(array[j]);
				}
				if (!Helpers.IsValueType(ExpectedType))
				{
					ctx.MarkLabel(label);
				}
				using (Local local2 = new Local(ctx, ctx.MapType(typeof(int))))
				{
					CodeLabel label2 = ctx.DefineLabel();
					CodeLabel label3 = ctx.DefineLabel();
					CodeLabel label4 = ctx.DefineLabel();
					ctx.Branch(label2, @short: false);
					CodeLabel[] array2 = new CodeLabel[members.Length];
					for (int k = 0; k < members.Length; k++)
					{
						array2[k] = ctx.DefineLabel();
					}
					ctx.MarkLabel(label3);
					ctx.LoadValue(local2);
					ctx.LoadValue(1);
					ctx.Subtract();
					ctx.Switch(array2);
					ctx.Branch(label4, @short: false);
					for (int l = 0; l < array2.Length; l++)
					{
						ctx.MarkLabel(array2[l]);
						IProtoSerializer protoSerializer = tails[l];
						Local valueFrom = (protoSerializer.RequiresOldValue ? array[l] : null);
						ctx.ReadNullCheckedTail(array[l].Type, protoSerializer, valueFrom);
						if (protoSerializer.ReturnsValue)
						{
							if (Helpers.IsValueType(array[l].Type))
							{
								ctx.StoreValue(array[l]);
							}
							else
							{
								CodeLabel label5 = ctx.DefineLabel();
								CodeLabel label6 = ctx.DefineLabel();
								ctx.CopyValue();
								ctx.BranchIfTrue(label5, @short: true);
								ctx.DiscardValue();
								ctx.Branch(label6, @short: true);
								ctx.MarkLabel(label5);
								ctx.StoreValue(array[l]);
								ctx.MarkLabel(label6);
							}
						}
						ctx.Branch(label2, @short: false);
					}
					ctx.MarkLabel(label4);
					ctx.LoadReaderWriter();
					ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
					ctx.MarkLabel(label2);
					ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
					ctx.CopyValue();
					ctx.StoreValue(local2);
					ctx.LoadValue(0);
					ctx.BranchIfGreater(label3, @short: false);
				}
				for (int m = 0; m < array.Length; m++)
				{
					ctx.LoadValue(array[m]);
				}
				ctx.EmitCtor(ctor);
				ctx.StoreValue(local);
			}
			finally
			{
				for (int n = 0; n < array.Length; n++)
				{
					if (array[n] != null)
					{
						array[n].Dispose();
					}
				}
			}
		}
	}
	internal sealed class TypeSerializer : IProtoTypeSerializer, IProtoSerializer
	{
		private readonly Type forType;

		private readonly Type constructType;

		private readonly IProtoSerializer[] serializers;

		private readonly int[] fieldNumbers;

		private readonly bool isRootType;

		private readonly bool useConstructor;

		private readonly bool isExtensible;

		private readonly bool hasConstructor;

		private readonly CallbackSet callbacks;

		private readonly MethodInfo[] baseCtorCallbacks;

		private readonly MethodInfo factory;

		private static readonly Type iextensible = typeof(IExtensible);

		public Type ExpectedType => forType;

		private bool CanHaveInheritance
		{
			get
			{
				if (forType.IsClass || forType.IsInterface)
				{
					return !forType.IsSealed;
				}
				return false;
			}
		}

		bool IProtoSerializer.RequiresOldValue => true;

		bool IProtoSerializer.ReturnsValue => false;

		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;
		}

		public TypeSerializer(TypeModel model, Type forType, int[] fieldNumbers, IProtoSerializer[] serializers, MethodInfo[] baseCtorCallbacks, bool isRootType, bool useConstructor, CallbackSet callbacks, Type constructType, MethodInfo factory)
		{
			Helpers.Sort(fieldNumbers, serializers);
			bool flag = 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] + " on: " + forType.FullName);
				}
				if (!flag && serializers[i].ExpectedType != forType)
				{
					flag = true;
				}
			}
			this.forType = forType;
			this.factory = factory;
			if (constructType == null)
			{
				constructType = forType;
			}
			else if (!forType.IsAssignableFrom(constructType))
			{
				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 (model.MapType(iextensible).IsAssignableFrom(forType))
			{
				if (forType.IsValueType || !isRootType || flag)
				{
					throw new NotSupportedException("IExtensible is not supported in structs or classes with inheritance");
				}
				isExtensible = true;
			}
			hasConstructor = !constructType.IsAbstract && Helpers.GetConstructor(constructType, Helpers.EmptyTypes, nonPublic: true) != null;
			if (constructType != forType && useConstructor && !hasConstructor)
			{
				throw new ArgumentException("The supplied default implementation cannot be created: " + constructType.FullName, "constructType");
			}
		}

		bool IProtoTypeSerializer.CanCreateInstance()
		{
			return true;
		}

		object IProtoTypeSerializer.CreateInstance(ProtoReader source)
		{
			return CreateInstance(source, includeLocalCallback: false);
		}

		public void Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
		{
			if (callbacks != null)
			{
				InvokeCallback(callbacks[callbackType], value, context);
			}
			((IProtoTypeSerializer)GetMoreSpecificSerializer(value))?.Callback(value, callbackType, context);
		}

		private IProtoSerializer GetMoreSpecificSerializer(object value)
		{
			if (!CanHaveInheritance)
			{
				return null;
			}
			Type type = value.GetType();
			if (type == forType)
			{
				return null;
			}
			for (int i = 0; i < serializers.Length; i++)
			{
				IProtoSerializer protoSerializer = serializers[i];
				if (protoSerializer.ExpectedType != forType && Helpers.IsAssignableFrom(protoSerializer.ExpectedType, type))
				{
					return protoSerializer;
				}
			}
			if (type == constructType)
			{
				return null;
			}
			TypeModel.ThrowUnexpectedSubtype(forType, type);
			return null;
		}

		public void Write(object value, ProtoWriter dest)
		{
			if (isRootType)
			{
				Callback(value, TypeModel.CallbackType.BeforeSerialize, dest.Context);
			}
			GetMoreSpecificSerializer(value)?.Write(value, dest);
			for (int i = 0; i < serializers.Length; i++)
			{
				IProtoSerializer protoSerializer = serializers[i];
				if (protoSerializer.ExpectedType == forType)
				{
					protoSerializer.Write(value, dest);
				}
			}
			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 num = 0;
			int num2 = 0;
			int num3;
			while ((num3 = source.ReadFieldHeader()) > 0)
			{
				bool flag = false;
				if (num3 < num)
				{
					num = (num2 = 0);
				}
				for (int i = num2; i < fieldNumbers.Length; i++)
				{
					if (fieldNumbers[i] != num3)
					{
						continue;
					}
					IProtoSerializer protoSerializer = serializers[i];
					Type expectedType = protoSerializer.ExpectedType;
					if (value == null)
					{
						if (expectedType == forType)
						{
							value = CreateInstance(source, includeLocalCallback: true);
						}
					}
					else if (expectedType != forType && ((IProtoTypeSerializer)protoSerializer).CanCreateInstance() && expectedType.IsSubclassOf(value.GetType()))
					{
						value = ProtoReader.Merge(source, value, ((IProtoTypeSerializer)protoSerializer).CreateInstance(source));
					}
					if (protoSerializer.ReturnsValue)
					{
						value = protoSerializer.Read(value, source);
					}
					else
					{
						protoSerializer.Read(value, source);
					}
					num2 = i;
					num = num3;
					flag = true;
					break;
				}
				if (!flag)
				{
					if (value == null)
					{
						value = CreateInstance(source, includeLocalCallback: true);
					}
					if (isExtensible)
					{
						source.AppendExtensionData((IExtensible)value);
					}
					else
					{
						source.SkipField();
					}
				}
			}
			if (value == null)
			{
				value = CreateInstance(source, includeLocalCallback: true);
			}
			if (isRootType)
			{
				Callback(value, TypeModel.CallbackType.AfterDeserialize, source.Context);
			}
			return value;
		}

		private object InvokeCallback(MethodInfo method, object obj, SerializationContext context)
		{
			object result = null;
			if (method != null)
			{
				ParameterInfo[] parameters = method.GetParameters();
				object[] array;
				bool flag;
				if (parameters.Length == 0)
				{
					array = null;
					flag = true;
				}
				else
				{
					array = new object[parameters.Length];
					flag = true;
					for (int i = 0; i < array.Length; i++)
					{
						Type parameterType = parameters[i].ParameterType;
						object obj2;
						if (parameterType == typeof(SerializationContext))
						{
							obj2 = context;
						}
						else if (parameterType == typeof(Type))
						{
							obj2 = constructType;
						}
						else if (parameterType == typeof(StreamingContext))
						{
							obj2 = (StreamingContext)context;
						}
						else
						{
							obj2 = null;
							flag = false;
						}
						array[i] = obj2;
					}
				}
				if (!flag)
				{
					throw CallbackSet.CreateInvalidCallbackSignature(method);
				}
				result = method.Invoke(obj, array);
			}
			return result;
		}

		private object CreateInstance(ProtoReader source, bool includeLocalCallback)
		{
			object obj;
			if (factory != null)
			{
				obj = InvokeCallback(factory, null, source.Context);
			}
			else if (useConstructor)
			{
				if (!hasConstructor)
				{
					TypeModel.ThrowCannotCreateInstance(constructType);
				}
				obj = Activator.CreateInstance(constructType, nonPublic: true);
			}
			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;
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			Type expectedType = ExpectedType;
			using Local local = ctx.GetLocalWithValue(expectedType, valueFrom);
			EmitCallbackIfNeeded(ctx, local, TypeModel.CallbackType.BeforeSerialize);
			CodeLabel label = ctx.DefineLabel();
			if (CanHaveInheritance)
			{
				for (int i = 0; i < serializers.Length; i++)
				{
					IProtoSerializer protoSerializer = serializers[i];
					Type expectedType2 = protoSerializer.ExpectedType;
					if (expectedType2 != forType)
					{
						CodeLabel label2 = ctx.DefineLabel();
						CodeLabel label3 = ctx.DefineLabel();
						ctx.LoadValue(local);
						ctx.TryCast(expectedType2);
						ctx.CopyValue();
						ctx.BranchIfTrue(label2, @short: true);
						ctx.DiscardValue();
						ctx.Branch(label3, @short: true);
						ctx.MarkLabel(label2);
						if (Helpers.IsValueType(expectedType2))
						{
							ctx.DiscardValue();
							ctx.LoadValue(local);
							ctx.CastFromObject(expectedType2);
						}
						protoSerializer.EmitWrite(ctx, null);
						ctx.Branch(label, @short: false);
						ctx.MarkLabel(label3);
					}
				}
				if (constructType != null && constructType != forType)
				{
					using Local local2 = new Local(ctx, ctx.MapType(typeof(Type)));
					ctx.LoadValue(local);
					ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType"));
					ctx.CopyValue();
					ctx.StoreValue(local2);
					ctx.LoadValue(forType);
					ctx.BranchIfEqual(label, @short: true);
					ctx.LoadValue(local2);
					ctx.LoadValue(constructType);
					ctx.BranchIfEqual(label, @short: true);
				}
				else
				{
					ctx.LoadValue(local);
					ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType"));
					ctx.LoadValue(forType);
					ctx.BranchIfEqual(label, @short: true);
				}
				ctx.LoadValue(forType);
				ctx.LoadValue(local);
				ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType"));
				ctx.EmitCall(ctx.MapType(typeof(TypeModel)).GetMethod("ThrowUnexpectedSubtype", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
			}
			ctx.MarkLabel(label);
			for (int j = 0; j < serializers.Length; j++)
			{
				IProtoSerializer protoSerializer2 = serializers[j];
				if (protoSerializer2.ExpectedType == forType)
				{
					protoSerializer2.EmitWrite(ctx, local);
				}
			}
			if (isExtensible)
			{
				ctx.LoadValue(local);
				ctx.LoadReaderWriter();
				ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("AppendExtensionData"));
			}
			EmitCallbackIfNeeded(ctx, local, TypeModel.CallbackType.AfterSerialize);
		}

		private static void EmitInvokeCallback(CompilerContext ctx, MethodInfo method, bool copyValue, Type constructType, Type type)
		{
			if (!(method != null))
			{
				return;
			}
			if (copyValue)
			{
				ctx.CopyValue();
			}
			ParameterInfo[] parameters = method.GetParameters();
			bool flag = 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(Type)))
				{
					Type type2 = constructType;
					if (type2 == null)
					{
						type2 = type;
					}
					ctx.LoadValue(type2);
				}
				else if (parameterType == ctx.MapType(typeof(StreamingContext)))
				{
					ctx.LoadSerializationContext();
					MethodInfo method2 = ctx.MapType(typeof(SerializationContext)).GetMethod("op_Implicit", new Type[1] { ctx.MapType(typeof(SerializationContext)) });
					if (method2 != null)
					{
						ctx.EmitCall(method2);
						flag = true;
					}
				}
				else
				{
					flag = false;
				}
			}
			if (flag)
			{
				ctx.EmitCall(method);
				if (constructType != null && method.ReturnType == ctx.MapType(typeof(object)))
				{
					ctx.CastFromObject(type);
				}
				return;
			}
			throw CallbackSet.CreateInvalidCallbackSignature(method);
		}

		private void EmitCallbackIfNeeded(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType)
		{
			if (isRootType && ((IProtoTypeSerializer)this).HasCallbacks(callbackType))
			{
				((IProtoTypeSerializer)this).EmitCallback(ctx, valueFrom, callbackType);
			}
		}

		void IProtoTypeSerializer.EmitCallback(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType)
		{
			bool flag = false;
			if (CanHaveInheritance)
			{
				for (int i = 0; i < serializers.Length; i++)
				{
					IProtoSerializer protoSerializer = serializers[i];
					if (protoSerializer.ExpectedType != forType && ((IProtoTypeSerializer)protoSerializer).HasCallbacks(callbackType))
					{
						flag = true;
					}
				}
			}
			MethodInfo methodInfo = callbacks?[callbackType];
			if (methodInfo == null && !flag)
			{
				return;
			}
			ctx.LoadAddress(valueFrom, ExpectedType);
			EmitInvokeCallback(ctx, methodInfo, flag, null, forType);
			if (!flag)
			{
				return;
			}
			CodeLabel label = ctx.DefineLabel();
			for (int j = 0; j < serializers.Length; j++)
			{
				IProtoSerializer protoSerializer2 = serializers[j];
				Type expectedType = protoSerializer2.ExpectedType;
				IProtoTypeSerializer protoTypeSerializer;
				if (expectedType != forType && (protoTypeSerializer = (IProtoTypeSerializer)protoSerializer2).HasCallbacks(callbackType))
				{
					CodeLabel label2 = ctx.DefineLabel();
					CodeLabel label3 = ctx.DefineLabel();
					ctx.CopyValue();
					ctx.TryCast(expectedType);
					ctx.CopyValue();
					ctx.BranchIfTrue(label2, @short: true);
					ctx.DiscardValue();
					ctx.Branch(label3, @short: false);
					ctx.MarkLabel(label2);
					protoTypeSerializer.EmitCallback(ctx, null, callbackType);
					ctx.Branch(label, @short: false);
					ctx.MarkLabel(label3);
				}
			}
			ctx.MarkLabel(label);
			ctx.DiscardValue();
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			Type expectedType = ExpectedType;
			using Local local = ctx.GetLocalWithValue(expectedType, valueFrom);
			using Local local2 = new Local(ctx, ctx.MapType(typeof(int)));
			if (HasCallbacks(TypeModel.CallbackType.BeforeDeserialize))
			{
				if (Helpers.IsValueType(ExpectedType))
				{
					EmitCallbackIfNeeded(ctx, local, TypeModel.CallbackType.BeforeDeserialize);
				}
				else
				{
					CodeLabel label = ctx.DefineLabel();
					ctx.LoadValue(local);
					ctx.BranchIfFalse(label, @short: false);
					EmitCallbackIfNeeded(ctx, local, TypeModel.CallbackType.BeforeDeserialize);
					ctx.MarkLabel(label);
				}
			}
			CodeLabel codeLabel = ctx.DefineLabel();
			CodeLabel label2 = ctx.DefineLabel();
			ctx.Branch(codeLabel, @short: false);
			ctx.MarkLabel(label2);
			int[] keys = fieldNumbers;
			object[] values = serializers;
			BasicList.NodeEnumerator enumerator = BasicList.GetContiguousGroups(keys, values).GetEnumerator();
			while (enumerator.MoveNext())
			{
				BasicList.Group obj = (BasicList.Group)enumerator.Current;
				CodeLabel label3 = ctx.DefineLabel();
				int count = obj.Items.Count;
				if (count == 1)
				{
					ctx.LoadValue(local2);
					ctx.LoadValue(obj.First);
					CodeLabel codeLabel2 = ctx.DefineLabel();
					ctx.BranchIfEqual(codeLabel2, @short: true);
					ctx.Branch(label3, @short: false);
					WriteFieldHandler(ctx, expectedType, local, codeLabel2, codeLabel, (IProtoSerializer)obj.Items[0]);
				}
				else
				{
					ctx.LoadValue(local2);
					ctx.LoadValue(obj.First);
					ctx.Subtract();
					CodeLabel[] array = new CodeLabel[count];
					for (int i = 0; i < count; i++)
					{
						array[i] = ctx.DefineLabel();
					}
					ctx.Switch(array);
					ctx.Branch(label3, @short: false);
					for (int j = 0; j < count; j++)
					{
						WriteFieldHandler(ctx, expectedType, local, array[j], codeLabel, (IProtoSerializer)obj.Items[j]);
					}
				}
				ctx.MarkLabel(label3);
			}
			EmitCreateIfNull(ctx, local);
			ctx.LoadReaderWriter();
			if (isExtensible)
			{
				ctx.LoadValue(local);
				ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("AppendExtensionData"));
			}
			else
			{
				ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
			}
			ctx.MarkLabel(codeLabel);
			ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
			ctx.CopyValue();
			ctx.StoreValue(local2);
			ctx.LoadValue(0);
			ctx.BranchIfGreater(label2, @short: false);
			EmitCreateIfNull(ctx, local);
			EmitCallbackIfNeeded(ctx, local, TypeModel.CallbackType.AfterDeserialize);
			if (valueFrom != null && !local.IsSame(valueFrom))
			{
				ctx.LoadValue(local);
				ctx.Cast(valueFrom.Type);
				ctx.StoreValue(valueFrom);
			}
		}

		private void WriteFieldHandler(CompilerContext ctx, Type expected, Local loc, CodeLabel handler, CodeLabel @continue, IProtoSerializer serializer)
		{
			ctx.MarkLabel(handler);
			Type expectedType = serializer.ExpectedType;
			if (expectedType == forType)
			{
				EmitCreateIfNull(ctx, loc);
				serializer.EmitRead(ctx, loc);
			}
			else
			{
				if (((IProtoTypeSerializer)serializer).CanCreateInstance())
				{
					CodeLabel label = ctx.DefineLabel();
					ctx.LoadValue(loc);
					ctx.BranchIfFalse(label, @short: false);
					ctx.LoadValue(loc);
					ctx.TryCast(expectedType);
					ctx.BranchIfTrue(label, @short: false);
					ctx.LoadReaderWriter();
					ctx.LoadValue(loc);
					((IProtoTypeSerializer)serializer).EmitCreateInstance(ctx);
					ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("Merge"));
					ctx.Cast(expected);
					ctx.StoreValue(loc);
					ctx.MarkLabel(label);
				}
				if (Helpers.IsValueType(expectedType))
				{
					CodeLabel label2 = ctx.DefineLabel();
					CodeLabel label3 = ctx.DefineLabel();
					using Local local = new Local(ctx, expectedType);
					ctx.LoadValue(loc);
					ctx.BranchIfFalse(label2, @short: false);
					ctx.LoadValue(loc);
					ctx.CastFromObject(expectedType);
					ctx.Branch(label3, @short: false);
					ctx.MarkLabel(label2);
					ctx.InitLocal(expectedType, local);
					ctx.LoadValue(local);
					ctx.MarkLabel(label3);
				}
				else
				{
					ctx.LoadValue(loc);
					ctx.Cast(expectedType);
				}
				serializer.EmitRead(ctx, null);
			}
			if (serializer.ReturnsValue)
			{
				if (Helpers.IsValueType(expectedType))
				{
					ctx.CastToObject(expectedType);
				}
				ctx.StoreValue(loc);
			}
			ctx.Branch(@continue, @short: false);
		}

		void IProtoTypeSerializer.EmitCreateInstance(CompilerContext ctx)
		{
			bool flag = true;
			if (factory != null)
			{
				EmitInvokeCallback(ctx, factory, copyValue: false, constructType, forType);
			}
			else if (!useConstructor)
			{
				ctx.LoadValue(constructType);
				ctx.EmitCall(ctx.MapType(typeof(BclHelpers)).GetMethod("GetUninitializedObject"));
				ctx.Cast(forType);
			}
			else if (Helpers.IsClass(constructType) && hasConstructor)
			{
				ctx.EmitCtor(constructType);
			}
			else
			{
				ctx.LoadValue(ExpectedType);
				ctx.EmitCall(ctx.MapType(typeof(TypeModel)).GetMethod("ThrowCannotCreateInstance", BindingFlags.Static | BindingFlags.Public));
				ctx.LoadNullRef();
				flag = false;
			}
			if (flag)
			{
				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], copyValue: true, null, forType);
				}
			}
		}

		private void EmitCreateIfNull(CompilerContext ctx, Local storage)
		{
			if (!Helpers.IsValueType(ExpectedType))
			{
				CodeLabel label = ctx.DefineLabel();
				ctx.LoadValue(storage);
				ctx.BranchIfTrue(label, @short: false);
				((IProtoTypeSerializer)this).EmitCreateInstance(ctx);
				if (callbacks != null)
				{
					EmitInvokeCallback(ctx, callbacks.BeforeDeserialize, copyValue: true, null, forType);
				}
				ctx.StoreValue(storage);
				ctx.MarkLabel(label);
			}
		}
	}
	internal class UInt16Serializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(ushort);

		public virtual Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public UInt16Serializer(TypeModel model)
		{
		}

		public virtual object Read(object value, ProtoReader source)
		{
			return source.ReadUInt16();
		}

		public virtual void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteUInt16((ushort)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteUInt16", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadUInt16", ctx.MapType(typeof(ushort)));
		}
	}
	internal sealed class UInt32Serializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(uint);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public UInt32Serializer(TypeModel model)
		{
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadUInt32();
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteUInt32((uint)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteUInt32", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadUInt32", ctx.MapType(typeof(uint)));
		}
	}
	internal sealed class UInt64Serializer : IProtoSerializer
	{
		private static readonly Type expectedType = typeof(ulong);

		public Type ExpectedType => expectedType;

		bool IProtoSerializer.RequiresOldValue => false;

		bool IProtoSerializer.ReturnsValue => true;

		public UInt64Serializer(TypeModel model)
		{
		}

		public object Read(object value, ProtoReader source)
		{
			return source.ReadUInt64();
		}

		public void Write(object value, ProtoWriter dest)
		{
			ProtoWriter.WriteUInt64((ulong)value, dest);
		}

		void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicWrite("WriteUInt64", valueFrom);
		}

		void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
		{
			ctx.EmitBasicRead("ReadUInt64", ExpectedType);
		}
	}
	internal sealed class UriDecorator : ProtoDecoratorBase
	{
		private static readonly Type expectedType = typeof(Uri);

		public override Type ExpectedType => expectedType;

		public override bool RequiresOldValue => false;

		public override bool ReturnsValue => true;

		public UriDecorator(TypeModel model, IProtoSerializer tail)
			: base(tail)
		{
		}

		public override void Write(object value, ProtoWriter dest)
		{
			Tail.Write(((Uri)value).OriginalString, dest);
		}

		public override object Read(object value, ProtoReader source)
		{
			string text = (string)Tail.Read(null, source);
			if (text.Length != 0)
			{
				return new Uri(text, UriKind.RelativeOrAbsolute);
			}
			return null;
		}

		protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
		{
			ctx.LoadValue(valueFrom);
			ctx.LoadValue(typeof(Uri).GetProperty("OriginalString"));
			Tail.EmitWrite(ctx, null);
		}

		protected override void EmitRead(CompilerContext ctx, Local valueFrom)
		{
			Tail.EmitRead(ctx, valueFrom);
			ctx.CopyValue();
			CodeLabel label = ctx.DefineLabel();
			CodeLabel label2 = ctx.DefineLabel();
			ctx.LoadValue(typeof(string).GetProperty("Length"));
			ctx.BranchIfTrue(label, @short: true);
			ctx.DiscardValue();
			ctx.LoadNullRef();
			ctx.Branch(label2, @short: true);
			ctx.MarkLabel(label);
			ctx.LoadValue(0);
			ctx.EmitCtor(ctx.MapType(typeof(Uri)), ctx.MapType(typeof(string)), ctx.MapType(typeof(UriKind)));
			ctx.MarkLabel(label2);
		}
	}
}
namespace ProtoBuf.Meta
{
	internal abstract class AttributeMap
	{
		private sealed class ReflectionAttributeMap : AttributeMap
		{
			private readonly Attribute attribute;

			public override object Target => attribute;

			public override Type AttributeType => attribute.GetType();

			public ReflectionAttributeMap(Attribute attribute)
			{
				this.attribute = attribute;
			}

			public override bool TryGet(string key, bool publicOnly, out object value)
			{
				MemberInfo[] instanceFieldsAndProperties = Helpers.GetInstanceFieldsAndProperties(attribute.GetType(), publicOnly);
				MemberInfo[] array = instanceFieldsAndProperties;
				foreach (MemberInfo memberInfo in array)
				{
					if (string.Equals(memberInfo.Name, key, StringComparison.OrdinalIgnoreCase))
					{
						if (memberInfo is PropertyInfo propertyInfo)
						{
							value = propertyInfo.GetValue(attribute, null);
							return true;
						}
						if (memberInfo is FieldInfo fieldInfo)
						{
							value = fieldInfo.GetValue(attribute);
							return true;
						}
						throw new NotSupportedException(memberInfo.GetType().Name);
					}
				}
				value = null;
				return false;
			}
		}

		public abstract Type AttributeType { get; }

		public abstract object Target { get; }

		public override string ToString()
		{
			return AttributeType?.FullName ?? "";
		}

		public abstract bool TryGet(string key, bool publicOnly, out object value);

		public bool TryGet(string key, out object value)
		{
			return TryGet(key, publicOnly: true, out value);
		}

		public static AttributeMap[] Create(TypeModel model, Type type, bool inherit)
		{
			object[] customAttributes = type.GetCustomAttributes(inherit);
			AttributeMap[] array = new AttributeMap[customAttributes.Length];
			for (int i = 0; i < customAttributes.Length; i++)
			{
				array[i] = new ReflectionAttributeMap((Attribute)customAttributes[i]);
			}
			return array;
		}

		public static AttributeMap[] Create(TypeModel model, MemberInfo member, bool inherit)
		{
			object[] customAttributes = member.GetCustomAttributes(inherit);
			AttributeMap[] array = new AttributeMap[customAttributes.Length];
			for (int i = 0; i < customAttributes.Length; i++)
			{
				array[i] = new ReflectionAttributeMap((Attribute)customAttributes[i]);
			}
			return array;
		}

		public static AttributeMap[] Create(TypeModel model, Assembly assembly)
		{
			object[] customAttributes = assembly.GetCustomAttributes(inherit: false);
			AttributeMap[] array = new AttributeMap[customAttributes.Length];
			for (int i = 0; i < customAttributes.Length; i++)
			{
				array[i] = new ReflectionAttributeMap((Attribute)customAttributes[i]);
			}
			return array;
		}
	}
	internal sealed class MutableList : BasicList
	{
		public new object this[int index]
		{
			get
			{
				return head[index];
			}
			set
			{
				head[index] = value;
			}
		}

		public void RemoveLast()
		{
			head.RemoveLastWithMutate();
		}

		public void Clear()
		{
			head.Clear();
		}
	}
	internal class BasicList : IEnumerable
	{
		public struct NodeEnumerator : IEnumerator
		{
			private int position;

			private readonly Node node;

			public object Current => node[position];

			internal NodeEnumerator(Node node)
			{
				position = -1;
				this.node = node;
			}

			void IEnumerator.Reset()
			{
				position = -1;
			}

			public bool MoveNext()
			{
				int length = node.Length;
				if (position <= length)
				{
					return ++position < length;
				}
				return false;
			}
		}

		internal sealed class Node
		{
			private readonly object[] data;

			private int length;

			public object this[int index]
			{
				get
				{
					if (index >= 0 && index < length)
					{
						return data[index];
					}
					throw new ArgumentOutOfRangeException("index");
				}
				set
				{
					if (index >= 0 && index < length)
					{
						data[index] = value;
						return;
					}
					throw new ArgumentOutOfRangeException("index");
				}
			}

			public int Length => length;

			internal Node(object[] data, int length)
			{
				this.data = data;
				this.length = length;
			}

			public void RemoveLastWithMutate()
			{
				if (length == 0)
				{
					throw new InvalidOperationException();
				}
				length--;
			}

			public Node Append(object value)
			{
				int num = length + 1;
				object[] array;
				if (data == null)
				{
					array = new object[10];
				}
				else if (length == data.Length)
				{
					array = new object[data.Length * 2];
					Array.Copy(data, array, length);
				}
				else
				{
					array = data;
				}
				array[length] = value;
				return new Node(array, num);
			}

			public Node Trim()
			{
				if (length == 0 || length == data.Length)
				{
					return this;
				}
				object[] destinationArray = new object[length];
				Array.Copy(data, destinationArray, length);
				return new Node(destinationArray, length);
			}

			internal int IndexOfString(string value)
			{
				for (int i = 0; i < length; i++)
				{
					if (value == (string)data[i])
					{
						return i;
					}
				}
				return -1;
			}

			internal int IndexOfReference(object instance)
			{
				for (int i = 0; i < length; i++)
				{
					if (instance == data[i])
					{
						return i;
					}
				}
				return -1;
			}

			internal int IndexOf(MatchPredicate predicate, object ctx)
			{
				for (int i = 0; i < length; i++)
				{
					if (predicate(data[i], ctx))
					{
						return i;
					}
				}
				return -1;
			}

			internal void CopyTo(Array array, int offset)
			{
				if (length > 0)
				{
					Array.Copy(data, 0, array, offset, length);
				}
			}

			internal void Clear()
			{
				if (data != null)
				{
					Array.Clear(data, 0, data.Length);
				}
				length = 0;
			}
		}

		internal delegate bool MatchPredicate(object value, object ctx);

		internal sealed class Group
		{
			public readonly int First;

			public readonly BasicList Items;

			public Group(int first)
			{
				First = first;
				Items = new BasicList();
			}
		}

		private static readonly Node nil = new Node(null, 0);

		protected Node head = nil;

		public object this[int index] => head[index];

		public int Count => head.Length;

		public void CopyTo(Array array, int offset)
		{
			head.CopyTo(array, offset);
		}

		public int Add(object value)
		{
			return (head = head.Append(value)).Length - 1;
		}

		public void Trim()
		{
			head = head.Trim();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return new NodeEnumerator(head);
		}

		public NodeEnumerator GetEnumerator()
		{
			return new NodeEnumerator(head);
		}

		internal int IndexOf(MatchPredicate predicate, object ctx)
		{
			return head.IndexOf(predicate, ctx);
		}

		internal int IndexOfString(string value)
		{
			return head.IndexOfString(value);
		}

		internal int IndexOfReference(object instance)
		{
			return head.IndexOfReference(instance);
		}

		internal bool Contains(object value)
		{
			NodeEnumerator enumerator = GetEnumerator();
			while (enumerator.MoveNext())
			{
				object current = enumerator.Current;
				if (object.Equals(current, value))
				{
					return true;
				}
			}
			return false;
		}

		internal static BasicList GetContiguousGroups(int[] keys, object[] values)
		{
			if (keys == null)
			{
				throw new ArgumentNullException("keys");
			}
			if (values == null)
			{
				throw new ArgumentNullException("values");
			}
			if (values.Length < keys.Length)
			{
				throw new ArgumentException("Not all keys are covered by values", "values");
			}
			BasicList basicList = new BasicList();
			Group obj = null;
			for (int i = 0; i < keys.Length; i++)
			{
				if (i == 0 || keys[i] != keys[i - 1])
				{
					obj = null;
				}
				if (obj == null)
				{
					obj = new Group(keys[i]);
					basicList.Add(obj);
				}
				obj.Items.Add(values[i]);
			}
			return basicList;
		}
	}
	public class CallbackSet
	{
		private readonly MetaType metaType;

		private MethodInfo beforeSerialize;

		private MethodInfo afterSerialize;

		private MethodInfo beforeDeserialize;

		private MethodInfo afterDeserialize;

		internal MethodInfo this[TypeModel.CallbackType callbackType] => callbackType switch
		{
			TypeModel.CallbackType.BeforeSerialize => beforeSerialize, 
			TypeModel.CallbackType.AfterSerialize => afterSerialize, 
			TypeModel.CallbackType.BeforeDeserialize => beforeDeserialize, 
			TypeModel.CallbackType.AfterDeserialize => afterDeserialize, 
			_ => throw new ArgumentException("Callback type not supported: " + callbackType, "callbackType"), 
		};

		public MethodInfo BeforeSerialize
		{
			get
			{
				return beforeSerialize;
			}
			set
			{
				beforeSerialize = SanityCheckCallback(metaType.Model, value);
			}
		}

		public MethodInfo BeforeDeserialize
		{
			get
			{
				return beforeDeserialize;
			}
			set
			{
				beforeDeserialize = SanityCheckCallback(metaType.Model, value);
			}
		}

		public MethodInfo AfterSerialize
		{
			get
			{
				return afterSerialize;
			}
			set
			{
				afterSerialize = SanityCheckCallback(metaType.Model, value);
			}
		}

		public MethodInfo AfterDeserialize
		{
			get
			{
				return afterDeserialize;
			}
			set
			{
				afterDeserialize = SanityCheckCallback(metaType.Model, value);
			}
		}

		public bool NonTrivial
		{
			get
			{
				if (!(beforeSerialize != null) && !(beforeDeserialize != null) && !(afterSerialize != null))
				{
					return afterDeserialize != null;
				}
				return true;
			}
		}

		internal CallbackSet(MetaType metaType)
		{
			this.metaType = metaType ?? throw new ArgumentNullException("metaType");
		}

		internal static bool CheckCallbackParameters(TypeModel model, MethodInfo method)
		{
			ParameterInfo[] parameters = method.GetParameters();
			for (int i = 0; i < parameters.Length; i++)
			{
				Type parameterType = parameters[i].ParameterType;
				if (!(parameterType == model.MapType(typeof(SerializationContext))) && !(parameterType == model.MapType(typeof(Type))) && !(parameterType == model.MapType(typeof(StreamingContext))))
				{
					return false;
				}
			}
			return true;
		}

		private MethodInfo SanityCheckCallback(TypeModel model, MethodInfo callback)
		{
			metaType.ThrowIfFrozen();
			if (callback == null)
			{
				return callback;
			}
			if (callback.IsStatic)
			{
				throw new ArgumentException("Callbacks cannot be static", "callback");
			}
			if (callback.ReturnType != model.MapType(typeof(void)) || !CheckCallbackParameters(model, callback))
			{
				throw CreateInvalidCallbackSignature(callback);
			}
			return callback;
		}

		internal static Exception CreateInvalidCallbackSignature(MethodInfo method)
		{
			return new NotSupportedException("Invalid callback signature in " + method.DeclaringType.FullName + "." + method.Name);
		}
	}
	public class MetaType : ISerializerProxy
	{
		internal sealed class Comparer : IComparer, IComparer<MetaType>
		{
			public static readonly Comparer Default = new Comparer();

			public int Compare(object x, object y)
			{
				return Compare(x as MetaType, y as MetaType);
			}

			public int Compare(MetaType x, MetaType y)
			{
				if (x == y)
				{
					return 0;
				}
				if (x == null)
				{
					return -1;
				}
				if (y == null)
				{
					return 1;
				}
				return string.Compare(x.GetSchemaTypeName(), y.GetSchemaTypeName(), StringComparison.Ordinal);
			}
		}

		[Flags]
		internal enum AttributeFamily
		{
			None = 0,
			ProtoBuf = 1,
			DataContractSerialier = 2,
			XmlSerializer = 4,
			AutoTuple = 8
		}

		private MetaType baseType;

		private BasicList subTypes;

		internal static readonly Type ienumerable = typeof(IEnumerable);

		private CallbackSet callbacks;

		private string name;

		private MethodInfo factory;

		private readonly RuntimeTypeModel model;

		private readonly Type type;

		private IProtoTypeSerializer serializer;

		private Type constructType;

		private Type surrogate;

		private readonly BasicList fields = new BasicList();

		private const ushort OPTIONS_Pending = 1;

		private const ushort OPTIONS_EnumPassThru = 2;

		private const ushort OPTIONS_Frozen = 4;

		private const ushort OPTIONS_PrivateOnApi = 8;

		private const ushort OPTIONS_SkipConstructor = 16;

		private const ushort OPTIONS_AsReferenceDefault = 32;

		private const ushort OPTIONS_AutoTuple = 64;

		private const ushort OPTIONS_IgnoreListHandling = 128;

		private const ushort OPTIONS_IsGroup = 256;

		private volatile ushort flags;

		IProtoSerializer ISerializerProxy.Serializer => Serializer;

		public MetaType BaseType => baseType;

		internal TypeModel Model => model;

		public bool IncludeSerializerMethod
		{
			get
			{
				return !HasFlag(8);
			}
			set
			{
				SetFlag(8, !value, throwIfFrozen: true);
			}
		}

		public bool AsReferenceDefault
		{
			get
			{
				return HasFlag(32);
			}
			set
			{
				SetFlag(32, value, throwIfFrozen: true);
			}
		}

		public bool HasCallbacks
		{
			get
			{
				if (callbacks != null)
				{
					return callbacks.NonTrivial;
				}
				return false;
			}
		}

		public bool HasSubtypes
		{
			get
			{
				if (subTypes != null)
				{
					return subTypes.Count != 0;
				}
				return false;
			}
		}

		public CallbackSet Callbacks
		{
			get
			{
				if (callbacks == null)
				{
					callbacks = new CallbackSet(this);
				}
				return callbacks;
			}
		}

		private bool IsValueType => type.IsValueType;

		public string Name
		{
			get
			{
				return name;
			}
			set
			{
				ThrowIfFrozen();
				name = value;
			}
		}

		public Type Type => type;

		internal IProtoTypeSerializer Serializer
		{
			get
			{
				if (serializer == null)
				{
					int opaqueToken = 0;
					try
					{
						model.TakeLock(ref opaqueToken);
						if (serializer == null)
						{
							SetFlag(4, value: true, throwIfFrozen: false);
							serializer = BuildSerializer();
							if (model.AutoCompile)
							{
								CompileInPlace();
							}
						}
					}
					finally
					{
						model.ReleaseLock(opaqueToken);
					}
				}
				return serializer;
			}
		}

		internal bool IsList
		{
			get
			{
				Type type = (IgnoreListHandling ? null : TypeModel.GetListItemType(model, this.type));
				return type != null;
			}
		}

		public bool UseConstructor
		{
			get
			{
				return !HasFlag(16);
			}
			set
			{
				SetFlag(16, !value, throwIfFrozen: true);
			}
		}

		public Type ConstructType
		{
			get
			{
				return constructType;
			}
			set
			{
				ThrowIfFrozen();
				constructType = value;
			}
		}

		public ValueMember this[int fieldNumber]
		{
			get
			{
				BasicList.NodeEnumerator enumerator = fields.GetEnumerator();
				while (enumerator.MoveNext())
				{
					ValueMember valueMember = (ValueMember)enumerator.Current;
					if (valueMember.FieldNumber == fieldNumber)
					{
						return valueMember;
					}
				}
				return null;
			}
		}

		public ValueMember this[MemberInfo member]
		{
			get
			{
				if (member == null)
				{
					return null;
				}
				BasicList.NodeEnumerator enumerator = fields.GetEnumerator();
				while (enumerator.MoveNext())
				{
					ValueMember valueMember = (ValueMember)enumerator.Current;
					if (valueMember.Member == member || valueMember.BackingMember == member)
					{
						return valueMember;
					}
				}
				return null;
			}
		}

		public bool EnumPassthru
		{
			get
			{
				return HasFlag(2);
			}
			set
			{
				SetFlag(2, value, throwIfFrozen: true);
			}
		}

		public bool IgnoreListHandling
		{
			get
			{
				return HasFlag(128);
			}
			set
			{
				SetFlag(128, value, throwIfFrozen: true);
			}
		}

		internal bool Pending
		{
			get
			{
				return HasFlag(1);
			}
			set
			{
				SetFlag(1, value, throwIfFrozen: false);
			}
		}

		internal IEnumerable Fields => fields;

		internal bool IsAutoTuple => HasFlag(64);

		public bool IsGroup
		{
			get
			{
				return HasFlag(256);
			}
			set
			{
				SetFlag(256, value, throwIfFrozen: true);
			}
		}

		public override string ToString()
		{
			return type.ToString();
		}

		private bool IsValidSubType(Type subType)
		{
			return type.IsAssignableFrom(subType);
		}

		public MetaType AddSubType(int fieldNumber, Type derivedType)
		{
			return AddSubType(fieldNumber, derivedType, DataFormat.Default);
		}

		public MetaType AddSubType(int fieldNumber, Type derivedType, DataFormat dataFormat)
		{
			if (derivedType == null)
			{
				throw new ArgumentNullException("derivedType");
			}
			if (fieldNumber < 1)
			{
				throw new ArgumentOutOfRangeException("fieldNumber");
			}
			if ((!type.IsClass && !type.IsInterface) || type.IsSealed)
			{
				throw new InvalidOperationException("Sub-types can only be added to non-sealed classes");
			}
			if (!IsValidSubType(derivedType))
			{
				throw new ArgumentException(derivedType.Name + " is not a valid sub-type of " + type.Name, "derivedType");
			}
			MetaType metaType = model[derivedType];
			ThrowIfFrozen();
			metaType.ThrowIfFrozen();
			SubType value = new SubType(fieldNumber, metaType, dataFormat);
			ThrowIfFrozen();
			metaType.SetBaseType(this);
			if (subTypes == null)
			{
				subTypes = new BasicList();
			}
			subTypes.Add(value);
			model.ResetKeyCache();
			return this;
		}

		private void SetBaseType(MetaType baseType)
		{
			if (baseType == null)
			{
				throw new ArgumentNullException("baseType");
			}
			if (this.baseType == baseType)
			{
				return;
			}
			if (this.baseType != null)
			{
				throw new InvalidOperationException("Type '" + this.baseType.Type.FullName + "' can only participate in one inheritance hierarchy");
			}
			for (MetaType metaType = baseType; metaType != null; metaType = metaType.baseType)
			{
				if (metaType == this)
				{
					throw new InvalidOperationException("Cyclic inheritance of '" + this.baseType.Type.FullName + "' is not allowed");
				}
			}
			this.baseType = baseType;
		}

		public MetaType SetCallbacks(MethodInfo beforeSerialize, MethodInfo afterSerialize, MethodInfo beforeDeserialize, MethodInfo afterDeserialize)
		{
			CallbackSet callbackSet = Callbacks;
			callbackSet.BeforeSerialize = beforeSerialize;
			callbackSet.AfterSerialize = afterSerialize;
			callbackSet.BeforeDeserialize = beforeDeserialize;
			callbackSet.AfterDeserialize = afterDeserialize;
			return this;
		}

		public MetaType SetCallbacks(string beforeSerialize, string afterSerialize, string beforeDeserialize, string afterDeserialize)
		{
			if (IsValueType)
			{
				throw new InvalidOperationException();
			}
			CallbackSet callbackSet = Callbacks;
			callbackSet.BeforeSerialize = ResolveMethod(beforeSerialize, instance: true);
			callbackSet.AfterSerialize = ResolveMethod(afterSerialize, instance: true);
			callbackSet.BeforeDeserialize = ResolveMethod(beforeDeserialize, instance: true);
			callbackSet.AfterDeserialize = ResolveMethod(afterDeserialize, instance: true);
			return this;
		}

		public string GetSchemaTypeName()
		{
			if (surrogate != null)
			{
				return model[surrogate].GetSchemaTypeName();
			}
			if (!string.IsNullOrEmpty(name))
			{
				return name;
			}
			string text = this.type.Name;
			if (this.type.IsGenericType)
			{
				StringBuilder stringBuilder = new StringBuilder(text);
				int num = text.IndexOf('`');
				if (num >= 0)
				{
					stringBuilder.Length = num;
				}
				Type[] genericArguments = this.type.GetGenericArguments();
				foreach (Type type in genericArguments)
				{
					stringBuilder.Append('_');
					Type type2 = type;
					int key = model.GetKey(ref type2);
					MetaType metaType;
					if (key >= 0 && (metaType = model[type2]) != null && metaType.surrogate == null)
					{
						stringBuilder.Append(metaType.GetSchemaTypeName());
					}
					else
					{
						stringBuilder.Append(type2.Name);
					}
				}
				return stringBuilder.ToString();
			}
			return text;
		}

		public MetaType SetFactory(MethodInfo factory)
		{
			model.VerifyFactory(factory, type);
			ThrowIfFrozen();
			this.factory = factory;
			return this;
		}

		public MetaType SetFactory(string factory)
		{
			return SetFactory(ResolveMethod(factory, instance: false));
		}

		private MethodInfo ResolveMethod(string name, bool instance)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			if (!instance)
			{
				return Helpers.GetStaticMethod(type, name);
			}
			return Helpers.GetInstanceMethod(type, name);
		}

		internal static Exception InbuiltType(Type type)
		{
			return new ArgumentException("Data of this type has inbuilt behaviour, and cannot be added to a model in this way: " + type.FullName);
		}

		internal MetaType(RuntimeTypeModel model, Type type, MethodInfo factory)
		{
			this.factory = factory;
			if (model == null)
			{
				throw new ArgumentNullException("model");
			}
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			if (type.IsArray)
			{
				throw InbuiltType(type);
			}
			IProtoSerializer protoSerializer = model.TryGetBasicTypeSerializer(type);
			if (protoSerializer != null)
			{
				throw InbuiltType(type);
			}
			this.type = type;
			this.model = model;
			if (Helpers.IsEnum(type))
			{
				EnumPassthru = type.IsDefined(model.MapType(typeof(FlagsAttribute)), inherit: false);
			}
		}

		protected internal void ThrowIfFrozen()
		{
			if ((flags & 4) != 0)
			{
				throw new InvalidOperationException("The type cannot be changed once a serializer has been generated for " + type.FullName);
			}
		}

		private IProtoTypeSerializer BuildSerializer()
		{
			if (Helpers.IsEnum(type))
			{
				return new TagDecorator(1, WireType.Variant, strict: false, new EnumSerializer(type, GetEnumMap()));
			}
			Type itemType = (IgnoreListHandling ? null : TypeModel.GetListItemType(model, type));
			if (itemType != null)
			{
				if (surrogate != null)
				{
					throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot use a surrogate");
				}
				if (subTypes != null && subTypes.Count != 0)
				{
					throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be subclassed");
				}
				Type defaultType = null;
				ResolveListTypes(model, type, ref itemType, ref defaultType);
				ValueMember valueMember = new ValueMember(model, 1, type, itemType, defaultType, DataFormat.Default);
				return new TypeSerializer(model, type, new int[1] { 1 }, new IProtoSerializer[1] { valueMember.Serializer }, null, isRootType: true, useConstructor: true, null, constructType, factory);
			}
			if (surrogate != null)
			{
				MetaType metaType = model[surrogate];
				MetaType metaType2;
				while ((metaType2 = metaType.baseType) != null)
				{
					metaType = metaType2;
				}
				return new SurrogateSerializer(model, type, surrogate, metaType.Serializer);
			}
			if (IsAutoTuple)
			{
				MemberInfo[] mappedMembers;
				ConstructorInfo constructorInfo = ResolveTupleConstructor(type, out mappedMembers);
				if (constructorInfo == null)
				{
					throw new InvalidOperationException();
				}
				return new TupleSerializer(model, constructorInfo, mappedMembers);
			}
			fields.Trim();
			int count = fields.Count;
			int num = ((subTypes != null) ? subTypes.Count : 0);
			int[] array = new int[count + num];
			IProtoSerializer[] array2 = new IProtoSerializer[count + num];
			int num2 = 0;
			if (num != 0)
			{
				BasicList.NodeEnumerator enumerator = subTypes.GetEnumerator();
				while (enumerator.MoveNext())
				{
					SubType subType = (SubType)enumerator.Current;
					if (!subType.DerivedType.IgnoreListHandling && model.MapType(ienumerable).IsAssignableFrom(subType.DerivedType.Type))
					{
						throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be used as a subclass");
					}
					array[num2] = subType.FieldNumber;
					array2[num2++] = subType.Serializer;
				}
			}
			if (count != 0)
			{
				BasicList.NodeEnumerator enumerator2 = fields.GetEnumerator();
				while (enumerator2.MoveNext())
				{
					ValueMember valueMember2 = (ValueMember)enumerator2.Current;
					array[num2] = valueMember2.FieldNumber;
					array2[num2++] = valueMember2.Serializer;
				}
			}
			BasicList basicList = null;
			for (MetaType metaType3 = BaseType; metaType3 != null; metaType3 = metaType3.BaseType)
			{
				MethodInfo methodInfo = (metaType3.HasCallbacks ? metaType3.Callbacks.BeforeDeserialize : null);
				if (methodInfo != null)
				{
					if (basicList == null)
					{
						basicList = new BasicList();
					}
					basicList.Add(methodInfo);
				}
			}
			MethodInfo[] array3 = null;
			if (basicList != null)
			{
				array3 = new MethodInfo[basicList.Count];
				basicList.CopyTo(array3, 0);
				Array.Reverse((Array)array3);
			}
			return new TypeSerializer(model, type, array, array2, array3, baseType == null, UseConstructor, callbacks, constructType, factory);
		}

		private static Type GetBaseType(MetaType type)
		{
			return type.type.BaseType;
		}

		internal static bool GetAsReferenceDefault(RuntimeTypeModel model, Type type)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			if (Helpers.IsEnum(type))
			{
				return false;
			}
			AttributeMap[] array = AttributeMap.Create(model, type, inherit: false);
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i].AttributeType.FullName == "ProtoBuf.ProtoContractAttribute" && array[i].TryGet("AsReferenceDefault", out var value))
				{
					return (bool)value;
				}
			}
			return false;
		}

		internal void ApplyDefaultBehaviour()
		{
			TypeAddedEventArgs args = null;
			RuntimeTypeModel.OnBeforeApplyDefaultBehaviour(this, ref args);
			if (args == null || args.ApplyDefaultBehaviour)
			{
				ApplyDefaultBehaviourImpl();
			}
			RuntimeTypeModel.OnAfterApplyDefaultBehaviour(this, ref args);
		}

		internal void ApplyDefaultBehaviourImpl()
		{
			Type type = GetBaseType(this);
			if (type != null && model.FindWithoutAdd(type) == null && GetContractFamily(model, type, null) != AttributeFamily.None)
			{
				model.FindOrAddAuto(type, demand: true, addWithContractOnly: false, addEvenIfAutoDisabled: false);
			}
			AttributeMap[] array = AttributeMap.Create(model, this.type, inherit: false);
			AttributeFamily attributeFamily = GetContractFamily(model, this.type, array);
			if (attributeFamily == AttributeFamily.AutoTuple)
			{
				SetFlag(64, value: true, throwIfFrozen: true);
			}
			bool flag = !EnumPassthru && Helpers.IsEnum(this.type);
			if (attributeFamily == AttributeFamily.None && !flag)
			{
				return;
			}
			bool flag2 = flag;
			BasicList basicList = null;
			BasicList basicList2 = null;
			int dataMemberOffset = 0;
			int num = 1;
			bool flag3 = model.InferTagFromNameDefault;
			ImplicitFields implicitFields = ImplicitFields.None;
			string text = null;
			foreach (AttributeMap attributeMap in array)
			{
				string fullName = attributeMap.AttributeType.FullName;
				object value;
				if (!flag && fullName == "ProtoBuf.ProtoIncludeAttribute")
				{
					int fieldNumber = 0;
					if (attributeMap.TryGet("tag", out value))
					{
						fieldNumber = (int)value;
					}
					DataFormat dataFormat = DataFormat.Default;
					if (attributeMap.TryGet("DataFormat", out value))
					{
						dataFormat = (DataFormat)(int)value;
					}
					Type type2 = null;
					try
					{
						if (attributeMap.TryGet("knownTypeName", out value))
						{
							type2 = model.GetType((string)value, this.type.Assembly);
						}
						else if (attributeMap.TryGet("knownType", out value))
						{
							type2 = (Type)value;
						}
					}
					catch (Exception innerException)
					{
						throw new InvalidOperationException("Unable to resolve sub-type of: " + this.type.FullName, innerException);
					}
					if (type2 == null)
					{
						throw new InvalidOperationException("Unable to resolve sub-type of: " + this.type.FullName);
					}
					if (IsValidSubType(type2))
					{
						AddSubType(fieldNumber, type2, dataFormat);
					}
				}
				if (fullName == "ProtoBuf.ProtoPartialIgnoreAttribute" && attributeMap.TryGet("MemberName", out value) && value != null)
				{
					if (basicList == null)
					{
						basicList = new BasicList();
					}
					basicList.Add((string)value);
				}
				if (!flag && fullName == "ProtoBuf.ProtoPartialMemberAttribute")
				{
					if (basicList2 == null)
					{
						basicList2 = new BasicList();
					}
					basicList2.Add(attributeMap);
				}
				if (fullName == "ProtoBuf.ProtoContractAttribute")
				{
					if (attributeMap.TryGet("Name", out value))
					{
						text = (string)value;
					}
					if (Helpers.IsEnum(this.type))
					{
						if (attributeMap.TryGet("EnumPassthruHasValue", publicOnly: false, out value) && (bool)value && attributeMap.TryGet("EnumPassthru", out value))
						{
							EnumPassthru = (bool)value;
							flag2 = false;
							if (EnumPassthru)
							{
								flag = false;
							}
						}
					}
					else
					{
						if (attributeMap.TryGet("DataMemberOffset", out value))
						{
							dataMemberOffset = (int)value;
						}
						if (attributeMap.TryGet("InferTagFromNameHasValue", publicOnly: false, out value) && (bool)value && attributeMap.TryGet("InferTagFromName", out value))
						{
							flag3 = (bool)value;
						}
						if (attributeMap.TryGet("ImplicitFields", out value) && value != null)
						{
							implicitFields = (ImplicitFields)(int)value;
						}
						if (attributeMap.TryGet("SkipConstructor", out value))
						{
							UseConstructor = !(bool)value;
						}
						if (attributeMap.TryGet("IgnoreListHandling", out value))
						{
							IgnoreListHandling = (bool)value;
						}
						if (attributeMap.TryGet("AsReferenceDefault", out value))
						{
							AsReferenceDefault = (bool)value;
						}
						if (attributeMap.TryGet("ImplicitFirstTag", out value) && (int)value > 0)
						{
							num = (int)value;
						}
						if (attributeMap.TryGet("IsGroup", out value))
						{
							IsGroup = (bool)value;
						}
						if (attributeMap.TryGet("Surrogate", out value))
						{
							SetSurrogate((Type)value);
						}
					}
				}
				if (fullName == "System.Runtime.Serialization.DataContractAttribute" && text == null && attributeMap.TryGet("Name", out value))
				{
					text = (string)value;
				}
				if (fullName == "System.Xml.Serialization.XmlTypeAttribute" && text == null && attributeMap.TryGet("TypeName", out value))
				{
					text = (string)value;
				}
			}
			if (!string.IsNullOrEmpty(text))
			{
				Name = text;
			}
			if (implicitFields != ImplicitFields.None)
			{
				attributeFamily &= AttributeFamily.ProtoBuf;
			}
			MethodInfo[] array2 = null;
			BasicList basicList3 = new BasicList();
			MemberInfo[] members = this.type.GetMembers(flag ? (BindingFlags.Static | BindingFlags.Public) : (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
			bool hasConflictingEnumValue = false;
			MemberInfo[] array3 = members;
			foreach (MemberInfo memberInfo in array3)
			{
				if (memberInfo.DeclaringType != this.type || memberInfo.IsDefined(model.MapType(typeof(ProtoIgnoreAttribute)), inherit: true) || (basicList != null && basicList.Contains(memberInfo.Name)))
				{
					continue;
				}
				bool forced = false;
				if (memberInfo is PropertyInfo propertyInfo)
				{
					if (flag)
					{
						continue;
					}
					MemberInfo backingMember = null;
					if (!propertyInfo.CanWrite)
					{
						string text2 = "<" + propertyInfo.Name + ">k__BackingField";
						MemberInfo[] array4 = members;
						foreach (MemberInfo memberInfo2 in array4)
						{
							if (memberInfo2 as FieldInfo != null && memberInfo2.Name == text2)
							{
								backingMember = memberInfo2;
								break;
							}
						}
					}
					Type effectiveType = propertyInfo.PropertyType;
					bool isPublic = Helpers.GetGetMethod(propertyInfo, nonPublic: false, allowInternal: false) != null;
					bool isField = false;
					ApplyDefaultBehaviour_AddMembers(model, attributeFamily, flag, basicList2, dataMemberOffset, flag3, implicitFields, basicList3, memberInfo, ref forced, isPublic, isField, ref effectiveType, ref hasConflictingEnumValue, backingMember);
				}
				else if (memberInfo is FieldInfo fieldInfo)
				{
					Type effectiveType = fieldInfo.FieldType;
					bool isPublic = fieldInfo.IsPublic;
					bool isField = true;
					if (!flag || fieldInfo.IsStatic)
					{
						ApplyDefaultBehaviour_AddMembers(model, attributeFamily, flag, basicList2, dataMemberOffset, flag3, implicitFields, basicList3, memberInfo, ref forced, isPublic, isField, ref effectiveType, ref hasConflictingEnumValue);
					}
				}
				else if (memberInfo is MethodInfo methodInfo && !flag)
				{
					AttributeMap[] array5 = AttributeMap.Create(model, methodInfo, inherit: false);
					if (array5 != null && array5.Length != 0)
					{
						CheckForCallback(methodInfo, array5, "ProtoBuf.ProtoBeforeSerializationAttribute", ref array2, 0);
						CheckForCallback(methodInfo, array5, "ProtoBuf.ProtoAfterSerializationAttribute", ref array2, 1);
						CheckForCallback(methodInfo, array5, "ProtoBuf.ProtoBeforeDeserializationAttribute", ref array2, 2);
						CheckForCallback(methodInfo, array5, "ProtoBuf.ProtoAfterDeserializationAttribute", ref array2, 3);
						CheckForCallback(methodInfo, array5, "System.Runtime.Serialization.OnSerializingAttribute", ref array2, 4);
						CheckForCallback(methodInfo, array5, "System.Runtime.Serialization.OnSerializedAttribute", ref array2, 5);
						CheckForCallback(methodInfo, array5, "System.Runtime.Serialization.OnDeserializingAttribute", ref array2, 6);
						CheckForCallback(methodInfo, array5, "System.Runtime.Serialization.OnDeserializedAttribute", ref array2, 7);
					}
				}
			}
			if (flag && flag2 && !hasConflictingEnumValue)
			{
				EnumPassthru = true;
			}
			ProtoMemberAttribute[] array6 = new ProtoMemberAttribute[basicList3.Count];
			basicList3.CopyTo(array6, 0);
			if (flag3 || implicitFields != ImplicitFields.None)
			{
				Array.Sort(array6);
				int num2 = num;
				ProtoMemberAttribute[] array7 = array6;
				foreach (ProtoMemberAttribute protoMemberAttribute in array7)
				{
					if (!protoMemberAttribute.TagIsPinned)
					{
						protoMemberAttribute.Rebase(num2++);
					}
				}
			}
			ProtoMemberAttribute[] array8 = array6;
			foreach (ProtoMemberAttribute normalizedAttribute in array8)
			{
				ValueMember valueMember = ApplyDefaultBehaviour(flag, normalizedAttribute);
				if (valueMember != null)
				{
					Add(valueMember);
				}
			}
			if (array2 != null)
			{
				SetCallbacks(Coalesce(array2, 0, 4), Coalesce(array2, 1, 5), Coalesce(array2, 2, 6), Coalesce(array2, 3, 7));
			}
		}

		private static void ApplyDefaultBehaviour_AddMembers(TypeModel model, AttributeFamily family, bool isEnum, BasicList partialMembers, int dataMemberOffset, bool inferTagByName, ImplicitFields implicitMode, BasicList members, MemberInfo member, ref bool forced, bool isPublic, bool isField, ref Type effectiveType, ref bool hasConflictingEnumValue, MemberInfo backingMember = null)
		{
			switch (implicitMode)
			{
			case ImplicitFields.AllFields:
				if (isField)
				{
					forced = true;
				}
				break;
			case ImplicitFields.AllPublic:
				if (isPublic)
				{
					forced = true;
				}
				break;
			}
			if (effectiveType.IsSubclassOf(model.MapType(typeof(Delegate))))
			{
				effectiveType = null;
			}
			if (effectiveType != null)
			{
				ProtoMemberAttribute protoMemberAttribute = NormalizeProtoMember(model, member, family, forced, isEnum, partialMembers, dataMemberOffset, inferTagByName, ref hasConflictingEnumValue, backingMember);
				if (protoMemberAttribute != null)
				{
					members.Add(protoMemberAttribute);
				}
			}
		}

		private static MethodInfo Coalesce(MethodInfo[] arr, int x, int y)
		{
			MethodInfo methodInfo = arr[x];
			if (methodInfo == null)
			{
				methodInfo = arr[y];
			}
			return methodInfo;
		}

		internal static AttributeFamily GetContractFamily(RuntimeTypeModel model, Type type, AttributeMap[] attributes)
		{
			AttributeFamily attributeFamily = AttributeFamily.None;
			if (attributes == null)
			{
				attributes = AttributeMap.Create(model, type, inherit: false);
			}
			for (int i = 0; i < attributes.Length; i++)
			{
				switch (attributes[i].AttributeType.FullName)
				{
				case "ProtoBuf.ProtoContractAttribute":
				{
					bool value = false;
					GetFieldBoolean(ref value, attributes[i], "UseProtoMembersOnly");
					if (value)
					{
						return AttributeFamily.ProtoBuf;
					}
					attributeFamily |= AttributeFamily.ProtoBuf;
					break;
				}
				case "System.Xml.Serialization.XmlTypeAttribute":
					if (!model.AutoAddProtoContractTypesOnly)
					{
						attributeFamily |= AttributeFamily.XmlSerializer;
					}
					break;
				case "System.Runtime.Serialization.DataContractAttribute":
					if (!model.AutoAddProtoContractTypesOnly)
					{
						attributeFamily |= AttributeFamily.DataContractSerialier;
					}
					break;
				}
			}
			if (attributeFamily == AttributeFamily.None && ResolveTupleConstructor(type, out var _) != null)
			{
				attributeFamily |= AttributeFamily.AutoTuple;
			}
			return attributeFamily;
		}

		internal static ConstructorInfo ResolveTupleConstructor(Type type, out MemberInfo[] mappedMembers)
		{
			mappedMembers = null;
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			if (type.IsAbstract)
			{
				return null;
			}
			ConstructorInfo[] constructors = Helpers.GetConstructors(type, nonPublic: false);
			if (constructors.Length == 0 || (constructors.Length == 1 && constructors[0].GetParameters().Length == 0))
			{
				return null;
			}
			MemberInfo[] instanceFieldsAndProperties = Helpers.GetInstanceFieldsAndProperties(type, publicOnly: true);
			BasicList basicList = new BasicList();
			bool flag = type.Name.IndexOf("Tuple", StringComparison.OrdinalIgnoreCase) < 0;
			for (int i = 0; i < instanceFieldsAndProperties.Length; i++)
			{
				if (instanceFieldsAndProperties[i] is PropertyInfo propertyInfo)
				{
					if (!propertyInfo.CanRead)
					{
						return null;
					}
					if (flag && propertyInfo.CanWrite && Helpers.GetSetMethod(propertyInfo, nonPublic: false, allowInternal: false) != null)
					{
						return null;
					}
					basicList.Add(propertyInfo);
				}
				else if (instanceFieldsAndProperties[i] is FieldInfo fieldInfo)
				{
					if (flag && !fieldInfo.IsInitOnly)
					{
						return null;
					}
					basicList.Add(fieldInfo);
				}
			}
			if (basicList.Count == 0)
			{
				return null;
			}
			MemberInfo[] array = new MemberInfo[basicList.Count];
			basicList.CopyTo(array, 0);
			int[] array2 = new int[array.Length];
			int num = 0;
			ConstructorInfo result = null;
			mappedMembers = new MemberInfo[array2.Length];
			for (int j = 0; j < constructors.Length; j++)
			{
				ParameterInfo[] parameters = constructors[j].GetParameters();
				if (parameters.Length != array.Length)
				{
					continue;
				}
				for (int k = 0; k < array2.Length; k++)
				{
					array2[k] = -1;
				}
				for (int l = 0; l < parameters.Length; l++)
				{
					for (int m = 0; m < array.Length; m++)
					{
						if (string.Compare(parameters[l].Name, array[m].Name, StringComparison.OrdinalIgnoreCase) == 0)
						{
							Type memberType = Helpers.GetMemberType(array[m]);
							if (!(memberType != parameters[l].ParameterType))
							{
								array2[l] = m;
							}
						}
					}
				}
				bool flag2 = false;
				for (int n = 0; n < array2.Length; n++)
				{
					if (array2[n] < 0)
					{
						flag2 = true;
						break;
					}
					mappedMembers[n] = array[array2[n]];
				}
				if (!flag2)
				{
					num++;
					result = constructors[j];
				}
			}
			if (num != 1)
			{
				return null;
			}
			return result;
		}

		private static void CheckForCallback(MethodInfo method, AttributeMap[] attributes, string callbackTypeName, ref MethodInfo[] callbacks, int index)
		{
			for (int i = 0; i < attributes.Length; i++)
			{
				if (attributes[i].AttributeType.FullName == callbackTypeName)
				{
					if (callbacks == null)
					{
						callbacks = new MethodInfo[8];
					}
					else if (callbacks[index] != null)
					{
						Type reflectedType = method.ReflectedType;
						throw new ProtoException("Duplicate " + callbackTypeName + " callbacks on " + reflectedType.FullName);
					}
					callbacks[index] = method;
				}
			}
		}

		private static bool HasFamily(AttributeFamily value, AttributeFamily required)
		{
			return (value & required) == required;
		}

		private static ProtoMemberAttribute NormalizeProtoMember(TypeModel model, MemberInfo member, AttributeFamily family, bool forced, bool isEnum, BasicList partialMembers, int dataMemberOffset, bool inferByTagName, ref bool hasConflictingEnumValue, MemberInfo backingMember = null)
		{
			if (member == null || (family == AttributeFamily.None && !isEnum))
			{
				return null;
			}
			int value = int.MinValue;
			int num = ((!inferByTagName) ? 1 : (-1));
			string text = null;
			bool value2 = false;
			bool ignore = false;
			bool flag = false;
			bool value3 = false;
			bool value4 = false;
			bool value5 = false;
			bool value6 = false;
			bool tagIsPinned = false;
			bool value7 = false;
			DataFormat value8 = DataFormat.Default;
			if (isEnum)
			{
				forced = true;
			}
			AttributeMap[] attribs = AttributeMap.Create(model, member, inherit: true);
			if (isEnum)
			{
				AttributeMap attribute = GetAttribute(attribs, "ProtoBuf.ProtoIgnoreAttribute");
				if (attribute != null)
				{
					ignore = true;
				}
				else
				{
					attribute = GetAttribute(attribs, "ProtoBuf.ProtoEnumAttribute");
					value = Convert.ToInt32(((FieldInfo)member).GetRawConstantValue());
					if (attribute != null)
					{
						GetFieldName(ref text, attribute, "Name");
						if ((bool)Helpers.GetInstanceMethod(attribute.AttributeType, "HasValue").Invoke(attribute.Target, null) && attribute.TryGet("Value", out var value9))
						{
							if (value != (int)value9)
							{
								hasConflictingEnumValue = true;
							}
							value = (int)value9;
						}
					}
				}
				flag = true;
			}
			if (!ignore && !flag)
			{
				AttributeMap attribute = GetAttribute(attribs, "ProtoBuf.ProtoMemberAttribute");
				GetIgnore(ref ignore, attribute, attribs, "ProtoBuf.ProtoIgnoreAttribute");
				if (!ignore && attribute != null)
				{
					GetFieldNumber(ref value, attribute, "Tag");
					GetFieldName(ref text, attribute, "Name");
					GetFieldBoolean(ref value3, attribute, "IsRequired");
					GetFieldBoolean(ref value2, attribute, "IsPacked");
					GetFieldBoolean(ref value7, attribute, "OverwriteList");
					GetDataFormat(ref value8, attribute, "DataFormat");
					GetFieldBoolean(ref value5, attribute, "AsReferenceHasValue", publicOnly: false);
					if (value5)
					{
						value5 = GetFieldBoolean(ref value4, attribute, "AsReference", publicOnly: true);
					}
					GetFieldBoolean(ref value6, attribute, "DynamicType");
					flag = (tagIsPinned = value > 0);
				}
				if (!flag && partialMembers != null)
				{
					BasicList.NodeEnumerator enumerator = partialMembers.GetEnumerator();
					while (enumerator.MoveNext())
					{
						AttributeMap attributeMap = (AttributeMap)enumerator.Current;
						if (attributeMap.TryGet("MemberName", out var value10) && (string)value10 == member.Name)
						{
							GetFieldNumber(ref value, attributeMap, "Tag");
							GetFieldName(ref text, attributeMap, "Name");
							GetFieldBoolean(ref value3, attributeMap, "IsRequired");
							GetFieldBoolean(ref value2, attributeMap, "IsPacked");
							GetFieldBoolean(ref value7, attribute, "OverwriteList");
							GetDataFormat(ref value8, attributeMap, "DataFormat");
							GetFieldBoolean(ref value5, attribute, "AsReferenceHasValue", publicOnly: false);
							if (value5)
							{
								value5 = GetFieldBoolean(ref value4, attributeMap, "AsReference", publicOnly: true);
							}
							GetFieldBoolean(ref value6, attributeMap, "DynamicType");
							if (flag = (tagIsPinned = value > 0))
							{
								break;
							}
						}
					}
				}
			}
			if (!ignore && !flag && HasFamily(family, AttributeFamily.DataContractSerialier))
			{
				AttributeMap attribute = GetAttribute(attribs, "System.Runtime.Serialization.DataMemberAttribute");
				if (attribute != null)
				{
					GetFieldNumber(ref value, attribute, "Order");
					GetFieldName(ref text, attribute, "Name");
					GetFieldBoolean(ref value3, attribute, "IsRequired");
					flag = value >= num;
					if (flag)
					{
						value += dataMemberOffset;
					}
				}
			}
			if (!ignore && !flag && HasFamily(family, AttributeFamily.XmlSerializer))
			{
				AttributeMap attribute = GetAttribute(attribs, "System.Xml.Serialization.XmlElementAttribute");
				if (attribute == null)
				{
					attribute = GetAttribute(attribs, "System.Xml.Serialization.XmlArrayAttribute");
				}
				GetIgnore(ref ignore, attribute, attribs, "System.Xml.Serialization.XmlIgnoreAttribute");
				if (attribute != null && !ignore)
				{
					GetFieldNumber(ref value, attribute, "Order");
					GetFieldName(ref text, attribute, "ElementName");
					flag = value >= num;
				}
			}
			if (!ignore && !flag && GetAttribute(attribs, "System.NonSerializedAttribute") != null)
			{
				ignore = true;
			}
			if (ignore || (value < num && !forced))
			{
				return null;
			}
			return new ProtoMemberAttribute(value, forced || inferByTagName)
			{
				AsReference = value4,
				AsReferenceHasValue = value5,
				DataFormat = value8,
				DynamicType = value6,
				IsPacked = value2,
				OverwriteList = value7,
				IsRequired = value3,
				Name = (string.IsNullOrEmpty(text) ? member.Name : text),
				Member = member,
				BackingMember = backingMember,
				TagIsPinned = tagIsPinned
			};
		}

		private ValueMember ApplyDefaultBehaviour(bool isEnum, ProtoMemberAttribute normalizedAttribute)
		{
			MemberInfo member;
			if (normalizedAttribute == null || (member = normalizedAttribute.Member) == null)
			{
				return null;
			}
			Type memberType = Helpers.GetMemberType(member);
			Type itemType = null;
			Type defaultType = null;
			ResolveListTypes(model, memberType, ref itemType, ref defaultType);
			bool flag = false;
			if (itemType != null)
			{
				int num = model.FindOrAddAuto(memberType, demand: false, addWithContractOnly: true, addEvenIfAutoDisabled: false);
				if (num >= 0 && (flag = model[memberType].IgnoreListHandling))
				{
					itemType = null;
					defaultType = null;
				}
			}
			AttributeMap[] attribs = AttributeMap.Create(model, member, inherit: true);
			object defaultValue = null;
			if (model.UseImplicitZeroDefaults)
			{
				switch (Helpers.GetTypeCode(memberType))
				{
				case ProtoTypeCode.Boolean:
					defaultValue = false;
					break;
				case ProtoTypeCode.Decimal:
					defaultValue = 0m;
					break;
				case ProtoTypeCode.Single:
					defaultValue = 0f;
					break;
				case ProtoTypeCode.Double:
					defaultValue = 0.0;
					break;
				case ProtoTypeCode.Byte:
					defaultValue = (byte)0;
					break;
				case ProtoTypeCode.Char:
					defaultValue = '\0';
					break;
				case ProtoTypeCode.Int16:
					defaultValue = (short)0;
					break;
				case ProtoTypeCode.Int32:
					defaultValue = 0;
					break;
				case ProtoTypeCode.Int64:
					defaultValue = 0L;
					break;
				case ProtoTypeCode.SByte:
					defaultValue = (sbyte)0;
					break;
				case ProtoTypeCode.UInt16:
					defaultValue = (ushort)0;
					break;
				case ProtoTypeCode.UInt32:
					defaultValue = 0u;
					break;
				case ProtoTypeCode.UInt64:
					defaultValue = 0uL;
					break;
				case ProtoTypeCode.TimeSpan:
					defaultValue = TimeSpan.Zero;
					break;
				case ProtoTypeCode.Guid:
					defaultValue = Guid.Empty;
					break;
				}
			}
			AttributeMap attribute;
			if ((attribute = GetAttribute(attribs, "System.ComponentModel.DefaultValueAttribute")) != null && attribute.TryGet("Value", out var value))
			{
				defaultValue = value;
			}
			ValueMember valueMember = ((isEnum || normalizedAttribute.Tag > 0) ? new ValueMember(model, type, normalizedAttribute.Tag, member, memberType, itemType, defaultType, normalizedAttribute.DataFormat, defaultValue) : null);
			if (valueMember != null)
			{
				valueMember.BackingMember = normalizedAttribute.BackingMember;
				Type declaringType = type;
				PropertyInfo propertyInfo = Helpers.GetProperty(declaringType, member.Name + "Specified", nonPublic: true);
				MethodInfo getMethod = Helpers.GetGetMethod(propertyInfo, nonPublic: true, allowInternal: true);
				if (getMethod == null || getMethod.IsStatic)
				{
					propertyInfo = null;
				}
				if (propertyInfo != null)
				{
					valueMember.SetSpecified(getMethod, Helpers.GetSetMethod(propertyInfo, nonPublic: true, allowInternal: true));
				}
				else
				{
					MethodInfo instanceMethod = Helpers.GetInstanceMethod(declaringType, "ShouldSerialize" + member.Name, Helpers.EmptyTypes);
					if (instanceMethod != null && instanceMethod.ReturnType == model.MapType(typeof(bool)))
					{
						valueMember.SetSpecified(instanceMethod, null);
					}
				}
				if (!string.IsNullOrEmpty(normalizedAttribute.Name))
				{
					valueMember.SetName(normalizedAttribute.Name);
				}
				valueMember.IsPacked = normalizedAttribute.IsPacked;
				valueMember.IsRequired = normalizedAttribute.IsRequired;
				valueMember.OverwriteList = normalizedAttribute.OverwriteList;
				if (normalizedAttribute.AsReferenceHasValue)
				{
					valueMember.AsReference = normalizedAttribute.AsReference;
				}
				valueMember.DynamicType = normalizedAttribute.DynamicType;
				valueMember.IsMap = !flag && valueMember.ResolveMapTypes(out var _, out var _, out var _);
				if (valueMember.IsMap && (attribute = GetAttribute(attribs, "ProtoBuf.ProtoMapAttribute")) != null)
				{
					if (attribute.TryGet("DisableMap", out var value2) && (bool)value2)
					{
						valueMember.IsMap = false;
					}
					else
					{
						if (attribute.TryGet("KeyFormat", out value2))
						{
							valueMember.MapKeyFormat = (DataFormat)value2;
						}
						if (attribute.TryGet("ValueFormat", out value2))
						{
							valueMember.MapValueFormat = (DataFormat)value2;
						}
					}
				}
			}
			return valueMember;
		}

		private static void GetDataFormat(ref DataFormat value, AttributeMap attrib, string memberName)
		{
			if (attrib != null && value == DataFormat.Default && attrib.TryGet(memberName, out var value2) && value2 != null)
			{
				value = (DataFormat)value2;
			}
		}

		private static void GetIgnore(ref bool ignore, AttributeMap attrib, AttributeMap[] attribs, string fullName)
		{
			if (!ignore && attrib != null)
			{
				ignore = GetAttribute(attribs, fullName) != null;
			}
		}

		private static void GetFieldBoolean(ref bool value, AttributeMap attrib, string memberName)
		{
			GetFieldBoolean(ref value, attrib, memberName, publicOnly: true);
		}

		private static bool GetFieldBoolean(ref bool value, AttributeMap attrib, string memberName, bool publicOnly)
		{
			if (attrib == null)
			{
				return false;
			}
			if (value)
			{
				return true;
			}
			if (attrib.TryGet(memberName, publicOnly, out var value2) && value2 != null)
			{
				value = (bool)value2;
				return true;
			}
			return false;
		}

		private static void GetFieldNumber(ref int value, AttributeMap attrib, string memberName)
		{
			if (attrib != null && value <= 0 && attrib.TryGet(memberName, out var value2) && value2 != null)
			{
				value = (int)value2;
			}
		}

		private static void GetFieldName(ref string name, AttributeMap attrib, string memberName)
		{
			if (attrib != null && string.IsNullOrEmpty(name) && attrib.TryGet(memberName, out var value) && value != null)
			{
				name = (string)value;
			}
		}

		private static AttributeMap GetAttribute(AttributeMap[] attribs, string fullName)
		{
			foreach (AttributeMap attributeMap in attribs)
			{
				if (attributeMap != null && attributeMap.AttributeType.FullName == fullName)
				{
					return attributeMap;
				}
			}
			return null;
		}

		public MetaType Add(int fieldNumber, string memberName)
		{
			AddField(fieldNumber, memberName, null, null, null);
			return this;
		}

		public ValueMember AddField(int fieldNumber, string memberName)
		{
			return AddField(fieldNumber, memberName, null, null, null);
		}

		public MetaType Add(string memberName)
		{
			Add(GetNextFieldNumber(), memberName);
			return this;
		}

		public void SetSurrogate(Type surrogateType)
		{
			if (surrogateType == type)
			{
				surrogateType = null;
			}
			if (surrogateType != null && surrogateType != null && Helpers.IsAssignableFrom(model.MapType(typeof(IEnumerable)), surrogateType))
			{
				throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be used as a surrogate");
			}
			ThrowIfFrozen();
			surrogate = surrogateType;
		}

		internal MetaType GetSurrogateOrSelf()
		{
			if (surrogate != null)
			{
				return model[surrogate];
			}
			return this;
		}

		internal MetaType GetSurrogateOrBaseOrSelf(bool deep)
		{
			if (surrogate != null)
			{
				return model[surrogate];
			}
			MetaType metaType = baseType;
			if (metaType != null)
			{
				if (deep)
				{
					MetaType result;
					do
					{
						result = metaType;
						metaType = metaType.baseType;
					}
					while (metaType != null);
					return result;
				}
				return metaType;
			}
			return this;
		}

		private int GetNextFieldNumber()
		{
			int num = 0;
			BasicList.NodeEnumerator enumerator = fields.GetEnumerator();
			while (enumerator.MoveNext())
			{
				ValueMember valueMember = (ValueMember)enumerator.Current;
				if (valueMember.FieldNumber > num)
				{
					num = valueMember.FieldNumber;
				}
			}
			if (subTypes != null)
			{
				BasicList.NodeEnumerator enumerator2 = subTypes.GetEnumerator();
				while (enumerator2.MoveNext())
				{
					SubType subType = (SubType)enumerator2.Current;
					if (subType.FieldNumber > num)
					{
						num = subType.FieldNumber;
					}
				}
			}
			return num + 1;
		}

		public MetaType Add(params string[] memberNames)
		{
			if (memberNames == null)
			{
				throw new ArgumentNullException("memberNames");
			}
			int nextFieldNumber = GetNextFieldNumber();
			for (int i = 0; i < memberNames.Length; i++)
			{
				Add(nextFieldNumber++, memberNames[i]);
			}
			return this;
		}

		public MetaType Add(int fieldNumber, string memberName, object defaultValue)
		{
			AddField(fieldNumber, memberName, null, null, defaultValue);
			return this;
		}

		public MetaType Add(int fieldNumber, string memberName, Type itemType, Type defaultType)
		{
			AddField(fieldNumber, memberName, itemType, defaultType, null);
			return this;
		}

		public ValueMember AddField(int fieldNumber, string memberName, Type itemType, Type defaultType)
		{
			return AddField(fieldNumber, memberName, itemType, defaultType, null);
		}

		private ValueMember AddField(int fieldNumber, string memberName, Type itemType, Type defaultType, object defaultValue)
		{
			MemberInfo memberInfo = null;
			MemberInfo[] member = type.GetMember(memberName, Helpers.IsEnum(type) ? (BindingFlags.Static | BindingFlags.Public) : (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
			if (member != null && member.Length == 1)
			{
				memberInfo = member[0];
			}
			if (memberInfo == null)
			{
				throw new ArgumentException("Unable to determine member: " + memberName, "memberName");
			}
			PropertyInfo propertyInfo = null;
			FieldInfo fieldInfo = null;
			Type memberType;
			switch (memberInfo.MemberType)
			{
			case MemberTypes.Field:
				fieldInfo = (FieldInfo)memberInfo;
				memberType = fieldInfo.FieldType;
				break;
			case MemberTypes.Property:
				propertyInfo = (PropertyInfo)memberInfo;
				memberType = propertyInfo.PropertyType;
				break;
			default:
				throw new NotSupportedException(memberInfo.MemberType.ToString());
			}
			ResolveListTypes(model, memberType, ref itemType, ref defaultType);
			MemberInfo memberInfo2 = null;
			if ((object)propertyInfo != null && !propertyInfo.CanWrite)
			{
				string text = "<" + ((PropertyInfo)memberInfo).Name + ">k__BackingField";
				MemberInfo[] member2 = type.GetMember("<" + ((PropertyInfo)memberInfo).Name + ">k__BackingField", Helpers.IsEnum(type) ? (BindingFlags.Static | BindingFlags.Public) : (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
				if (member2 != null && member2.Length == 1 && member2[0] as FieldInfo != null)
				{
					memberInfo2 = member2[0];
				}
			}
			ValueMember valueMember = new ValueMember(model, type, fieldNumber, memberInfo2 ?? memberInfo, memberType, itemType, defaultType, DataFormat.Default, defaultValue);
			if (memberInfo2 != null)
			{
				valueMember.SetName(memberInfo.Name);
			}
			Add(valueMember);
			return valueMember;
		}

		internal static void ResolveListTypes(TypeModel model, Type type, ref Type itemType, ref Type defaultType)
		{
			if (type == null)
			{
				return;
			}
			if (type.IsArray)
			{
				if (type.GetArrayRank() != 1)
				{
					throw new NotSupportedException("Multi-dimensional arrays are not supported");
				}
				itemType = type.GetElementType();
				if (itemType == model.MapType(typeof(byte)))
				{
					defaultType = (itemType = null);
				}
				else
				{
					defaultType = type;
				}
			}
			if (itemType == null)
			{
				itemType = TypeModel.GetListItemType(model, type);
			}
			if (itemType != null)
			{
				Type itemType2 = null;
				Type defaultType2 = null;
				ResolveListTypes(model, itemType, ref itemType2, ref defaultType2);
				if (itemType2 != null)
				{
					throw TypeModel.CreateNestedListsNotSupported(type);
				}
			}
			if (!(itemType != null) || !(defaultType == null))
			{
				return;
			}
			if (type.IsClass && !type.IsAbstract && Helpers.GetConstructor(type, Helpers.EmptyTypes, nonPublic: true) != null)
			{
				defaultType = type;
			}
			if (defaultType == null && type.IsInterface)
			{
				Type[] genericArguments;
				if (type.IsGenericType && type.GetGenericTypeDefinition() == model.MapType(typeof(IDictionary<, >)) && itemType == model.MapType(typeof(KeyValuePair<, >)).MakeGenericType(genericArguments = type.GetGenericArguments()))
				{
					defaultType = model.MapType(typeof(Dictionary<, >)).MakeGenericType(genericArguments);
				}
				else
				{
					defaultType = model.MapType(typeof(List<>)).MakeGenericType(itemType);
				}
			}
			if (defaultType != null && !Helpers.IsAssignableFrom(type, defaultType))
			{
				defaultType = null;
			}
		}

		private void Add(ValueMember member)
		{
			int opaqueToken = 0;
			try
			{
				model.TakeLock(ref opaqueToken);
				ThrowIfFrozen();
				fields.Add(member);
			}
			finally
			{
				model.ReleaseLock(opaqueToken);
			}
		}

		public ValueMember[] GetFields()
		{
			ValueMember[] array = new ValueMember[fields.Count];
			fields.CopyTo(array, 0);
			Array.Sort(array, ValueMember.Comparer.Default);
			return array;
		}

		public SubType[] GetSubtypes()
		{
			if (subTypes == null || subTypes.Count == 0)
			{
				return new SubType[0];
			}
			SubType[] array = new SubType[subTypes.Count];
			subTypes.CopyTo(array, 0);
			Array.Sort(array, SubType.Comparer.Default);
			return array;
		}

		internal IEnumerable<Type> GetAllGenericArguments()
		{
			return GetAllGenericArguments(type);
		}

		private static IEnumerable<Type> GetAllGenericArguments(Type type)
		{
			Type[] genericArguments = type.GetGenericArguments();
			Type[] array = genericArguments;
			foreach (Type arg in array)
			{
				yield return arg;
				foreach (Type allGenericArgument in GetAllGenericArguments(arg))
				{
					yield return allGenericArgument;
				}
			}
		}

		public void CompileInPlace()
		{
			serializer = CompiledSerializer.Wrap(Serializer, model);
		}

		internal bool IsDefined(int fieldNumber)
		{
			BasicList.NodeEnumerator enumerator = fields.GetEnumerator();
			while (enumerator.MoveNext())
			{
				ValueMember valueMember = (ValueMember)enumerator.Current;
				if (valueMember.FieldNumber == fieldNumber)
				{
					return true;
				}
			}
			return false;
		}

		internal int GetKey(bool demand, bool getBaseKey)
		{
			return model.GetKey(type, demand, getBaseKey);
		}

		internal EnumSerializer.EnumPair[] GetEnumMap()
		{
			if (HasFlag(2))
			{
				return null;
			}
			EnumSerializer.EnumPair[] array = new EnumSerializer.EnumPair[fields.Count];
			for (int i = 0; i < array.Length; i++)
			{
				ValueMember valueMember = (ValueMember)fields[i];
				int fieldNumber = valueMember.FieldNumber;
				object rawEnumValue = valueMember.GetRawEnumValue();
				array[i] = new EnumSerializer.EnumPair(fieldNumber, rawEnumValue, valueMember.MemberType);
			}
			return array;
		}

		private bool HasFlag(ushort flag)
		{
			return (flags & flag) == flag;
		}

		private void SetFlag(ushort flag, bool value, bool throwIfFrozen)
		{
			if (throwIfFrozen && HasFlag(flag) != value)
			{
				ThrowIfFrozen();
			}
			if (value)
			{
				flags |= flag;
			}
			else
			{
				flags = (ushort)(flags & ~flag);
			}
		}

		internal static MetaType GetRootType(MetaType source)
		{
			while (source.serializer != null)
			{
				MetaType metaType = source.baseType;
				if (metaType == null)
				{
					return source;
				}
				source = metaType;
			}
			RuntimeTypeModel runtimeTypeModel = source.model;
			int opaqueToken = 0;
			try
			{
				runtimeTypeModel.TakeLock(ref opaqueToken);
				MetaType metaType2;
				while ((metaType2 = source.baseType) != null)
				{
					source = metaType2;
				}
				return source;
			}
			finally
			{
				runtimeTypeModel.ReleaseLock(opaqueToken);
			}
		}

		internal bool IsPrepared()
		{
			return serializer is CompiledSerializer;
		}

		internal static StringBuilder NewLine(StringBuilder builder, int indent)
		{
			return Helpers.AppendLine(builder).Append(' ', indent * 3);
		}

		internal void WriteSchema(StringBuilder builder, int indent, ref RuntimeTypeModel.CommonImports imports, ProtoSyntax syntax)
		{
			if (surrogate != null)
			{
				return;
			}
			ValueMember[] array = new ValueMember[fields.Count];
			fields.CopyTo(array, 0);
			Array.Sort(array, ValueMember.Comparer.Default);
			if (IsList)
			{
				string schemaTypeName = model.GetSchemaTypeName(TypeModel.GetListItemType(model, type), DataFormat.Default, asReference: false, dynamicType: false, ref imports);
				NewLine(builder, indent).Append("message ").Append(GetSchemaTypeName()).Append(" {");
				NewLine(builder, indent + 1).Append("repeated ").Append(schemaTypeName).Append(" items = 1;");
				NewLine(builder, indent).Append('}');
				return;
			}
			if (IsAutoTuple)
			{
				if (!(ResolveTupleConstructor(type, out var mappedMembers) != null))
				{
					return;
				}
				NewLine(builder, indent).Append("message ").Append(GetSchemaTypeName()).Append(" {");
				for (int i = 0; i < mappedMembers.Length; i++)
				{
					Type effectiveType;
					if (mappedMembers[i] is PropertyInfo propertyInfo)
					{
						effectiveType = propertyInfo.PropertyType;
					}
					else
					{
						if (!(mappedMembers[i] is FieldInfo fieldInfo))
						{
							throw new NotSupportedException("Unknown member type: " + mappedMembers[i].GetType().Name);
						}
						effectiveType = fieldInfo.FieldType;
					}
					NewLine(builder, indent + 1).Append((syntax == ProtoSyntax.Proto2) ? "optional " : "").Append(model.GetSchemaTypeName(effectiveType, DataFormat.Default, asReference: false, dynamicType: false, ref imports).Replace('.', '_')).Append(' ')
						.Append(mappedMembers[i].Name)
						.Append(" = ")
						.Append(i + 1)
						.Append(';');
				}
				NewLine(builder, indent).Append('}');
				return;
			}
			if (Helpers.IsEnum(type))
			{
				NewLine(builder, indent).Append("enum ").Append(GetSchemaTypeName()).Append(" {");
				if (array.Length == 0 && EnumPassthru)
				{
					if (type.IsDefined(model.MapType(typeof(FlagsAttribute)), inherit: false))
					{
						NewLine(builder, indent + 1).Append("// this is a composite/flags enumeration");
					}
					else
					{
						NewLine(builder, indent + 1).Append("// this enumeration will be passed as a raw value");
					}
					FieldInfo[] array2 = type.GetFields();
					foreach (FieldInfo fieldInfo2 in array2)
					{
						if (fieldInfo2.IsStatic && fieldInfo2.IsLiteral)
						{
							object rawConstantValue = fieldInfo2.GetRawConstantValue();
							NewLine(builder, indent + 1).Append(fieldInfo2.Name).Append(" = ").Append(rawConstantValue)
								.Append(";");
						}
					}
				}
				else
				{
					Dictionary<int, int> dictionary = new Dictionary<int, int>(array.Length);
					bool flag = false;
					ValueMember[] array3 = array;
					foreach (ValueMember valueMember in array3)
					{
						if (dictionary.ContainsKey(valueMember.FieldNumber))
						{
							flag = true;
							break;
						}
						dictionary.Add(valueMember.FieldNumber, 1);
					}
					if (flag)
					{
						NewLine(builder, indent + 1).Append("option allow_alias = true;");
					}
					bool flag2 = false;
					ValueMember[] array4 = array;
					foreach (ValueMember valueMember2 in array4)
					{
						if (valueMember2.FieldNumber == 0)
						{
							NewLine(builder, indent + 1).Append(valueMember2.Name).Append(" = ").Append(valueMember2.FieldNumber)
								.Append(';');
							flag2 = true;
						}
					}
					if (syntax == ProtoSyntax.Proto3 && !flag2)
					{
						NewLine(builder, indent + 1).Append("ZERO = 0; // proto3 requires a zero value as the first item (it can be named anything)");
					}
					ValueMember[] array5 = array;
					foreach (ValueMember valueMember3 in array5)
					{
						if (valueMember3.FieldNumber != 0)
						{
							NewLine(builder, indent + 1).Append(valueMember3.Name).Append(" = ").Append(valueMember3.FieldNumber)
								.Append(';');
						}
					}
				}
				NewLine(builder, indent).Append('}');
				return;
			}
			NewLine(builder, indent).Append("message ").Append(GetSchemaTypeName()).Append(" {");
			ValueMember[] array6 = array;
			foreach (ValueMember valueMember4 in array6)
			{
				bool hasOption = false;
				string schemaTypeName3;
				if (valueMember4.IsMap)
				{
					valueMember4.ResolveMapTypes(out var _, out var keyType, out var valueType);
					string schemaTypeName2 = model.GetSchemaTypeName(keyType, valueMember4.MapKeyFormat, asReference: false, dynamicType: false, ref imports);
					schemaTypeName3 = model.GetSchemaTypeName(valueType, valueMember4.MapKeyFormat, valueMember4.AsReference, valueMember4.DynamicType, ref imports);
					NewLine(builder, indent + 1).Append("map<").Append(schemaTypeName2).Append(",")
						.Append(schemaTypeName3)
						.Append("> ")
						.Append(valueMember4.Name)
						.Append(" = ")
						.Append(valueMember4.FieldNumber)
						.Append(";");
				}
				else
				{
					string value = ((valueMember4.ItemType != null) ? "repeated " : ((syntax != ProtoSyntax.Proto2) ? "" : (valueMember4.IsRequired ? "required " : "optional ")));
					NewLine(builder, indent + 1).Append(value);
					if (valueMember4.DataFormat == DataFormat.Group)
					{
						builder.Append("group ");
					}
					schemaTypeName3 = valueMember4.GetSchemaTypeName(applyNetObjectProxy: true, ref imports);
					builder.Append(schemaTypeName3).Append(" ").Append(valueMember4.Name)
						.Append(" = ")
						.Append(valueMember4.FieldNumber);
					if (syntax == ProtoSyntax.Proto2 && valueMember4.DefaultValue != null && !valueMember4.IsRequired)
					{
						if (valueMember4.DefaultValue is string)
						{
							AddOption(builder, ref hasOption).Append("default = \"").Append(valueMember4.DefaultValue).Append("\"");
						}
						else if (!(valueMember4.DefaultValue is TimeSpan))
						{
							if (valueMember4.DefaultValue is bool)
							{
								AddOption(builder, ref hasOption).Append(((bool)valueMember4.DefaultValue) ? "default = true" : "default = false");
							}
							else
							{
								AddOption(builder, ref hasOption).Append("default = ").Append(valueMember4.DefaultValue);
							}
						}
					}
					if (CanPack(valueMember4.ItemType))
					{
						if (syntax == ProtoSyntax.Proto2)
						{
							if (valueMember4.IsPacked)
							{
								AddOption(builder, ref hasOption).Append("packed = true");
							}
						}
						else if (!valueMember4.IsPacked)
						{
							AddOption(builder, ref hasOption).Append("packed = false");
						}
					}
					if (valueMember4.AsReference)
					{
						imports |= RuntimeTypeModel.CommonImports.Protogen;
						AddOption(builder, ref hasOption).Append("(.protobuf_net.fieldopt).asRef = true");
					}
					if (valueMember4.DynamicType)
					{
						imports |= RuntimeTypeModel.CommonImports.Protogen;
						AddOption(builder, ref hasOption).Append("(.protobuf_net.fieldopt).dynamicType = true");
					}
					CloseOption(builder, ref hasOption).Append(';');
					if (syntax != ProtoSyntax.Proto2 && valueMember4.DefaultValue != null && !valueMember4.IsRequired && !IsImplicitDefault(valueMember4.DefaultValue))
					{
						builder.Append(" // default value could not be applied: ").Append(valueMember4.DefaultValue);
					}
				}
				if (schemaTypeName3 == ".bcl.NetObjectProxy" && valueMember4.AsReference && !valueMember4.DynamicType)
				{
					builder.Append(" // reference-tracked ").Append(valueMember4.GetSchemaTypeName(applyNetObjectProxy: false, ref imports));
				}
			}
			if (subTypes != null && subTypes.Count != 0)
			{
				SubType[] array7 = new SubType[subTypes.Count];
				subTypes.CopyTo(array7, 0);
				Array.Sort(array7, SubType.Comparer.Default);
				string[] array8 = new string[array7.Length];
				for (int num = 0; num < array7.Length; num++)
				{
					array8[num] = array7[num].DerivedType.GetSchemaTypeName();
				}
				string text = "subtype";
				while (Array.IndexOf(array8, text) >= 0)
				{
					text = "_" + text;
				}
				NewLine(builder, indent + 1).Append("oneof ").Append(text).Append(" {");
				for (int num2 = 0; num2 < array7.Length; num2++)
				{
					string value2 = array8[num2];
					NewLine(builder, indent + 2).Append(value2).Append(" ").Append(value2)
						.Append(" = ")
						.Append(array7[num2].FieldNumber)
						.Append(';');
				}
				NewLine(builder, indent + 1).Append("}");
			}
			NewLine(builder, indent).Append('}');
		}

		private static StringBuilder AddOption(StringBuilder builder, ref bool hasOption)
		{
			if (hasOption)
			{
				return builder.Append(", ");
			}
			hasOption = true;
			return builder.Append(" [");
		}

		private static StringBuilder CloseOption(StringBuilder builder, ref bool hasOption)
		{
			if (hasOption)
			{
				hasOption = false;
				return builder.Append("]");
			}
			return builder;
		}

		private static bool IsImplicitDefault(object value)
		{
			try
			{
				if (value == null)
				{
					return false;
				}
				switch (Helpers.GetTypeCode(value.GetType()))
				{
				case ProtoTypeCode.Boolean:
					return !(bool)value;
				case ProtoTypeCode.Byte:
					return (byte)value == 0;
				case ProtoTypeCode.Char:
					return (char)value == '\0';
				case ProtoTypeCode.DateTime:
					return (DateTime)value == default(DateTime);
				case ProtoTypeCode.Decimal:
					return (decimal)value == 0m;
				case ProtoTypeCode.Double:
					return (double)value == 0.0;
				case ProtoTypeCode.Int16:
					return (short)value == 0;
				case ProtoTypeCode.Int32:
					return (int)value == 0;
				case ProtoTypeCode.Int64:
					return (long)value == 0;
				case ProtoTypeCode.SByte:
					return (sbyte)value == 0;
				case ProtoTypeCode.Single:
					return (float)value == 0f;
				case ProtoTypeCode.String:
					return (string)value == "";
				case ProtoTypeCode.TimeSpan:
					return (TimeSpan)value == TimeSpan.Zero;
				case ProtoTypeCode.UInt16:
					return (ushort)value == 0;
				case ProtoTypeCode.UInt32:
					return (uint)value == 0;
				case ProtoTypeCode.UInt64:
					return (ulong)value == 0;
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool CanPack(Type type)
		{
			if (type == null)
			{
				return false;
			}
			ProtoTypeCode typeCode = Helpers.GetTypeCode(type);
			if ((uint)(typeCode - 3) <= 11u)
			{
				return true;
			}
			return false;
		}

		[Browsable(false)]
		[EditorBrowsable(EditorBrowsableState.Advanced)]
		public void ApplyFieldOffset(int offset)
		{
			if (Helpers.IsEnum(type))
			{
				throw new InvalidOperationException("Cannot apply field-offset to an enum");
			}
			if (offset == 0)
			{
				return;
			}
			int opaqueToken = 0;
			try
			{
				model.TakeLock(ref opaqueToken);
				ThrowIfFrozen();
				if (fields != null)
				{
					BasicList.NodeEnumerator enumerator = fields.GetEnumerator();
					while (enumerator.MoveNext())
					{
						ValueMember valueMember = (ValueMember)enumerator.Current;
						AssertValidFieldNumber(valueMember.FieldNumber + offset);
					}
				}
				if (subTypes != null)
				{
					BasicList.NodeEnumerator enumerator2 = subTypes.GetEnumerator();
					while (enumerator2.MoveNext())
					{
						SubType subType = (SubType)enumerator2.Current;
						AssertValidFieldNumber(subType.FieldNumber + offset);
					}
				}
				if (fields != null)
				{
					BasicList.NodeEnumerator enumerator3 = fields.GetEnumerator();
					while (enumerator3.MoveNext())
					{
						((ValueMember)enumerator3.Current).FieldNumber += offset;
					}
				}
				if (subTypes != null)
				{
					BasicList.NodeEnumerator enumerator4 = subTypes.GetEnumerator();
					while (enumerator4.MoveNext())
					{
						((SubType)enumerator4.Current).FieldNumber += offset;
					}
				}
			}
			finally
			{
				model.ReleaseLock(opaqueToken);
			}
		}

		internal static void AssertValidFieldNumber(int fieldNumber)
		{
			if (fieldNumber < 1)
			{
				throw new ArgumentOutOfRangeException("fieldNumber");
			}
		}
	}
	public enum ProtoSyntax
	{
		Proto2,
		Proto3
	}
	public sealed class RuntimeTypeModel : TypeModel
	{
		private sealed class Singleton
		{
			internal static readonly RuntimeTypeModel Value = new RuntimeTypeModel(isDefault: true);

			private Singleton()
			{
			}
		}

		[Flags]
		internal enum CommonImports
		{
			None = 0,
			Bcl = 1,
			Timestamp = 2,
			Duration = 4,
			Protogen = 8
		}

		private sealed class BasicType
		{
			private readonly Type type;

			private readonly IProtoSerializer serializer;

			public Type Type => type;

			public IProtoSerializer Serializer => serializer;

			public BasicType(Type type, IProtoSerializer serializer)
			{
				this.type = type;
				this.serializer = serializer;
			}
		}

		internal sealed class SerializerPair : IComparable
		{
			public readonly int MetaKey;

			public readonly int BaseKey;

			public readonly MetaType Type;

			public readonly MethodBuilder Serialize;

			public readonly MethodBuilder Deserialize;

			public readonly ILGenerator SerializeBody;

			public readonly ILGenerator DeserializeBody;

			int IComparable.CompareTo(object obj)
			{
				if (obj == null)
				{
					throw new ArgumentException("obj");
				}
				SerializerPair serializerPair = (SerializerPair)obj;
				int metaKey;
				if (BaseKey == MetaKey)
				{
					if (serializerPair.BaseKey == serializerPair.MetaKey)
					{
						metaKey = MetaKey;
						return metaKey.CompareTo(serializerPair.MetaKey);
					}
					return 1;
				}
				if (serializerPair.BaseKey == serializerPair.MetaKey)
				{
					return -1;
				}
				metaKey = BaseKey;
				int num = metaKey.CompareTo(serializerPair.BaseKey);
				if (num == 0)
				{
					metaKey = MetaKey;
					num = metaKey.CompareTo(serializerPair.MetaKey);
				}
				return num;
			}

			public SerializerPair(int metaKey, int baseKey, MetaType type, MethodBuilder serialize, MethodBuilder deserialize, ILGenerator serializeBody, ILGenerator deserializeBody)
			{
				MetaKey = metaKey;
				BaseKey = baseKey;
				Serialize = serialize;
				Deserialize = deserialize;
				SerializeBody = serializeBody;
				DeserializeBody = deserializeBody;
				Type = type;
			}
		}

		public sealed class CompilerOptions
		{
			private string targetFrameworkName;

			private string targetFrameworkDisplayName;

			private string typeName;

			private string outputPath;

			private string imageRuntimeVersion;

			private int metaDataVersion;

			private Accessibility accessibility;

			public string TargetFrameworkName
			{
				get
				{
					return targetFrameworkName;
				}
				set
				{
					targetFrameworkName = value;
				}
			}

			public string TargetFrameworkDisplayName
			{
				get
				{
					return targetFrameworkDisplayName;
				}
				set
				{
					targetFrameworkDisplayName = value;
				}
			}

			public string TypeName
			{
				get
				{
					return typeName;
				}
				set
				{
					typeName = value;
				}
			}

			public string OutputPath
			{
				get
				{
					return outputPath;
				}
				set
				{
					outputPath = value;
				}
			}

			public string ImageRuntimeVersion
			{
				get
				{
					return imageRuntimeVersion;
				}
				set
				{
					imageRuntimeVersion = value;
				}
			}

			public int MetaDataVersion
			{
				get
				{
					return metaDataVersion;
				}
				set
				{
					metaDataVersion = value;
				}
			}

			public string AssemblyCompanyName { get; set; }

			public string AssemblyCopyright { get; set; }

			public string AssemblyDescription { get; set; }

			public string AssemblyProductName { get; set; }

			public string AssemblyTitle { get; set; }

			public string AssemblyTrademark { get; set; }

			public Version AssemblyVersion { get; set; }

			public Version AssemblyProductVersion { get; set; }

			public Accessibility Accessibility
			{
				get
				{
					return accessibility;
				}
				set
				{
					accessibility = value;
				}
			}

			public void SetFrameworkOptions(MetaType from)
			{
				if (from == null)
				{
					throw new ArgumentNullException("from");
				}
				AttributeMap[] array = AttributeMap.Create(from.Model, Helpers.GetAssembly(from.Type));
				AttributeMap[] array2 = array;
				foreach (AttributeMap attributeMap in array2)
				{
					if (attributeMap.AttributeType.FullName == "System.Runtime.Versioning.TargetFrameworkAttribute")
					{
						if (attributeMap.TryGet("FrameworkName", out var value))
						{
							TargetFrameworkName = (string)value;
						}
						if (attributeMap.TryGet("FrameworkDisplayName", out value))
						{
							TargetFrameworkDisplayName = (string)value;
						}
						break;
					}
				}
			}
		}

		public enum Accessibility
		{
			Public,
			Internal
		}

		private ushort options;

		private const ushort OPTIONS_InferTagFromNameDefault = 1;

		private const ushort OPTIONS_IsDefaultModel = 2;

		private const ushort OPTIONS_Frozen = 4;

		private const ushort OPTIONS_AutoAddMissingTypes = 8;

		private const ushort OPTIONS_AutoCompile = 16;

		private const ushort OPTIONS_UseImplicitZeroDefaults = 32;

		private const ushort OPTIONS_AllowParseableTypes = 64;

		private const ushort OPTIONS_AutoAddProtoContractTypesOnly = 128;

		private const ushort OPTIONS_IncludeDateTimeKind = 256;

		private const ushort OPTIONS_DoNotInternStrings = 512;

		private static readonly BasicList.MatchPredicate MetaTypeFinder = MetaTypeFinderImpl;

		private static readonly BasicList.MatchPredicate BasicTypeFinder = BasicTypeFinderImpl;

		private BasicList basicTypes = new BasicList();

		private readonly BasicList types = new BasicList();

		private const int KnownTypes_Array = 1;

		private const int KnownTypes_Dictionary = 2;

		private const int KnownTypes_Hashtable = 3;

		private const int KnownTypes_ArrayCutoff = 20;

		private int metadataTimeoutMilliseconds = 5000;

		private int contentionCounter = 1;

		private MethodInfo defaultFactory;

		public bool InferTagFromNameDefault
		{
			get
			{
				return GetOption(1);
			}
			set
			{
				SetOption(1, value);
			}
		}

		public bool AutoAddProtoContractTypesOnly
		{
			get
			{
				return GetOption(128);
			}
			set
			{
				SetOption(128, value);
			}
		}

		public bool UseImplicitZeroDefaults
		{
			get
			{
				return GetOption(32);
			}
			set
			{
				if (!value && GetOption(2))
				{
					throw new InvalidOperationException("UseImplicitZeroDefaults cannot be disabled on the default model");
				}
				SetOption(32, value);
			}
		}

		public bool AllowParseableTypes
		{
			get
			{
				return GetOption(64);
			}
			set
			{
				SetOption(64, value);
			}
		}

		public bool IncludeDateTimeKind
		{
			get
			{
				return GetOption(256);
			}
			set
			{
				SetOption(256, value);
			}
		}

		public bool InternStrings
		{
			get
			{
				return !GetOption(512);
			}
			set
			{
				SetOption(512, !value);
			}
		}

		public static RuntimeTypeModel Default => Singleton.Value;

		public MetaType this[Type type] => (MetaType)types[FindOrAddAuto(type, demand: true, addWithContractOnly: false, addEvenIfAutoDisabled: false)];

		public bool AutoCompile
		{
			get
			{
				return GetOption(16);
			}
			set
			{
				SetOption(16, value);
			}
		}

		public bool AutoAddMissingTypes
		{
			get
			{
				return GetOption(8);
			}
			set
			{
				if (!value && GetOption(2))
				{
					throw new InvalidOperationException("The default model must allow missing types");
				}
				ThrowIfFrozen();
				SetOption(8, value);
			}
		}

		public int MetadataTimeoutMilliseconds
		{
			get
			{
				return metadataTimeoutMilliseconds;
			}
			set
			{
				if (value <= 0)
				{
					throw new ArgumentOutOfRangeException("MetadataTimeoutMilliseconds");
				}
				metadataTimeoutMilliseconds = value;
			}
		}

		public event LockContentedEventHandler LockContended;

		public event EventHandler<TypeAddedEventArgs> BeforeApplyDefaultBehaviour;

		public event EventHandler<TypeAddedEventArgs> AfterApplyDefaultBehaviour;

		private bool GetOption(ushort option)
		{
			return (options & option) == option;
		}

		private void SetOption(ushort option, bool value)
		{
			if (value)
			{
				options |= option;
			}
			else
			{
				options &= (ushort)(~option);
			}
		}

		protected internal override bool SerializeDateTimeKind()
		{
			return GetOption(256);
		}

		public IEnumerable GetTypes()
		{
			return types;
		}

		public override string GetSchema(Type type, ProtoSyntax syntax)
		{
			BasicList basicList = new BasicList();
			MetaType metaType = null;
			bool flag = false;
			if (type == null)
			{
				BasicList.NodeEnumerator enumerator = types.GetEnumerator();
				while (enumerator.MoveNext())
				{
					MetaType metaType2 = (MetaType)enumerator.Current;
					MetaType surrogateOrBaseOrSelf = metaType2.GetSurrogateOrBaseOrSelf(deep: false);
					if (!basicList.Contains(surrogateOrBaseOrSelf))
					{
						basicList.Add(surrogateOrBaseOrSelf);
						CascadeDependents(basicList, surrogateOrBaseOrSelf);
					}
				}
			}
			else
			{
				Type underlyingType = Helpers.GetUnderlyingType(type);
				if (underlyingType != null)
				{
					type = underlyingType;
				}
				flag = ValueMember.TryGetCoreSerializer(this, DataFormat.Default, type, out var _, asReference: false, dynamicType: false, overwriteList: false, allowComplexTypes: false) != null;
				if (!flag)
				{
					int num = FindOrAddAuto(type, demand: false, addWithContractOnly: false, addEvenIfAutoDisabled: false);
					if (num < 0)
					{
						throw new ArgumentException("The type specified is not a contract-type", "type");
					}
					metaType = ((MetaType)types[num]).GetSurrogateOrBaseOrSelf(deep: false);
					basicList.Add(metaType);
					CascadeDependents(basicList, metaType);
				}
			}
			StringBuilder stringBuilder = new StringBuilder();
			string text = null;
			if (!flag)
			{
				IEnumerable enumerable = ((metaType == null) ? types : basicList);
				foreach (MetaType item in enumerable)
				{
					if (item.IsList)
					{
						continue;
					}
					string text2 = item.Type.Namespace;
					if (!string.IsNullOrEmpty(text2) && !text2.StartsWith("System."))
					{
						if (text == null)
						{
							text = text2;
						}
						else if (!(text == text2))
						{
							text = null;
							break;
						}
					}
				}
			}
			switch (syntax)
			{
			case ProtoSyntax.Proto2:
				stringBuilder.AppendLine("syntax = \"proto2\";");
				break;
			case ProtoSyntax.Proto3:
				stringBuilder.AppendLine("syntax = \"proto3\";");
				break;
			default:
				throw new ArgumentOutOfRangeException("syntax");
			}
			if (!string.IsNullOrEmpty(text))
			{
				stringBuilder.Append("package ").Append(text).Append(';');
				Helpers.AppendLine(stringBuilder);
			}
			CommonImports imports = CommonImports.None;
			StringBuilder stringBuilder2 = new StringBuilder();
			MetaType[] array = new MetaType[basicList.Count];
			basicList.CopyTo(array, 0);
			Array.Sort(array, MetaType.Comparer.Default);
			if (flag)
			{
				Helpers.AppendLine(stringBuilder2).Append("message ").Append(type.Name)
					.Append(" {");
				MetaType.NewLine(stringBuilder2, 1).Append((syntax == ProtoSyntax.Proto2) ? "optional " : "").Append(GetSchemaTypeName(type, DataFormat.Default, asReference: false, dynamicType: false, ref imports))
					.Append(" value = 1;");
				Helpers.AppendLine(stringBuilder2).Append('}');
			}
			else
			{
				foreach (MetaType metaType4 in array)
				{
					if (!metaType4.IsList || metaType4 == metaType)
					{
						metaType4.WriteSchema(stringBuilder2, 0, ref imports, syntax);
					}
				}
			}
			if ((imports & CommonImports.Bcl) != CommonImports.None)
			{
				stringBuilder.Append("import \"protobuf-net/bcl.proto\"; // schema for protobuf-net's handling of core .NET types");
				Helpers.AppendLine(stringBuilder);
			}
			if ((imports & CommonImports.Protogen) != CommonImports.None)
			{
				stringBuilder.Append("import \"protobuf-net/protogen.proto\"; // custom protobuf-net options");
				Helpers.AppendLine(stringBuilder);
			}
			if ((imports & CommonImports.Timestamp) != CommonImports.None)
			{
				stringBuilder.Append("import \"google/protobuf/timestamp.proto\";");
				Helpers.AppendLine(stringBuilder);
			}
			if ((imports & CommonImports.Duration) != CommonImports.None)
			{
				stringBuilder.Append("import \"google/protobuf/duration.proto\";");
				Helpers.AppendLine(stringBuilder);
			}
			return Helpers.AppendLine(stringBuilder.Append((object?)stringBuilder2)).ToString();
		}

		private void CascadeDependents(BasicList list, MetaType metaType)
		{
			if (metaType.IsList)
			{
				Type listItemType = TypeModel.GetListItemType(this, metaType.Type);
				TryGetCoreSerializer(list, listItemType);
				return;
			}
			if (metaType.IsAutoTuple)
			{
				if (MetaType.ResolveTupleConstructor(metaType.Type, out var mappedMembers) != null)
				{
					for (int i = 0; i < mappedMembers.Length; i++)
					{
						Type itemType = null;
						if (mappedMembers[i] is PropertyInfo)
						{
							itemType = ((PropertyInfo)mappedMembers[i]).PropertyType;
						}
						else if (mappedMembers[i] is FieldInfo)
						{
							itemType = ((FieldInfo)mappedMembers[i]).FieldType;
						}
						TryGetCoreSerializer(list, itemType);
					}
				}
			}
			else
			{
				foreach (ValueMember field in metaType.Fields)
				{
					Type valueType = field.ItemType;
					if (field.IsMap)
					{
						field.ResolveMapTypes(out var _, out var _, out valueType);
					}
					if (valueType == null)
					{
						valueType = field.MemberType;
					}
					TryGetCoreSerializer(list, valueType);
				}
			}
			foreach (Type allGenericArgument in metaType.GetAllGenericArguments())
			{
				TryGetCoreSerializer(list, allGenericArgument);
			}
			MetaType surrogateOrSelf;
			if (metaType.HasSubtypes)
			{
				SubType[] subtypes = metaType.GetSubtypes();
				foreach (SubType subType in subtypes)
				{
					surrogateOrSelf = subType.DerivedType.GetSurrogateOrSelf();
					if (!list.Contains(surrogateOrSelf))
					{
						list.Add(surrogateOrSelf);
						CascadeDependents(list, surrogateOrSelf);
					}
				}
			}
			surrogateOrSelf = metaType.BaseType;
			if (surrogateOrSelf != null)
			{
				surrogateOrSelf = surrogateOrSelf.GetSurrogateOrSelf();
			}
			if (surrogateOrSelf != null && !list.Contains(surrogateOrSelf))
			{
				list.Add(surrogateOrSelf);
				CascadeDependents(list, surrogateOrSelf);
			}
		}

		private void TryGetCoreSerializer(BasicList list, Type itemType)
		{
			WireType defaultWireType;
			IProtoSerializer protoSerializer = ValueMember.TryGetCoreSerializer(this, DataFormat.Default, itemType, out defaultWireType, asReference: false, dynamicType: false, overwriteList: false, allowComplexTypes: false);
			if (protoSerializer != null)
			{
				return;
			}
			int num = FindOrAddAuto(itemType, demand: false, addWithContractOnly: false, addEvenIfAutoDisabled: false);
			if (num >= 0)
			{
				MetaType surrogateOrBaseOrSelf = ((MetaType)types[num]).GetSurrogateOrBaseOrSelf(deep: false);
				if (!list.Contains(surrogateOrBaseOrSelf))
				{
					list.Add(surrogateOrBaseOrSelf);
					CascadeDependents(list, surrogateOrBaseOrSelf);
				}
			}
		}

		public static RuntimeTypeModel Create(string name = null)
		{
			return new RuntimeTypeModel(isDefault: false);
		}

		private RuntimeTypeModel(bool isDefault)
		{
			AutoAddMissingTypes = true;
			UseImplicitZeroDefaults = true;
			SetOption(2, isDefault);
			try
			{
				AutoCompile = EnableAutoCompile();
			}
			catch
			{
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static bool EnableAutoCompile()
		{
			try
			{
				DynamicMethod dynamicMethod = new DynamicMethod("CheckCompilerAvailable", typeof(bool), new Type[1] { typeof(int) });
				ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
				iLGenerator.Emit(OpCodes.Ldarg_0);
				iLGenerator.Emit(OpCodes.Ldc_I4, 42);
				iLGenerator.Emit(OpCodes.Ceq);
				iLGenerator.Emit(OpCodes.Ret);
				Predicate<int> predicate = (Predicate<int>)dynamicMethod.CreateDelegate(typeof(Predicate<int>));
				return predicate(42);
			}
			catch (Exception)
			{
				return false;
			}
		}

		internal MetaType FindWithoutAdd(Type type)
		{
			BasicList.NodeEnumerator enumerator = types.GetEnumerator();
			while (enumerator.MoveNext())
			{
				MetaType metaType = (MetaType)enumerator.Current;
				if (metaType.Type == type)
				{
					if (metaType.Pending)
					{
						WaitOnLock(metaType);
					}
					return metaType;
				}
			}
			Type type2 = TypeModel.ResolveProxies(type);
			if (!(type2 == null))
			{
				return FindWithoutAdd(type2);
			}
			return null;
		}

		private static bool MetaTypeFinderImpl(object value, object ctx)
		{
			return ((MetaType)value).Type == (Type)ctx;
		}

		private static bool BasicTypeFinderImpl(object value, object ctx)
		{
			return ((BasicType)value).Type == (Type)ctx;
		}

		private void WaitOnLock(MetaType type)
		{
			int opaqueToken = 0;
			try
			{
				TakeLock(ref opaqueToken);
			}
			finally
			{
				ReleaseLock(opaqueToken);
			}
		}

		internal IProtoSerializer TryGetBasicTypeSerializer(Type type)
		{
			int num = basicTypes.IndexOf(BasicTypeFinder, type);
			if (num >= 0)
			{
				return ((BasicType)basicTypes[num]).Serializer;
			}
			lock (basicTypes)
			{
				num = basicTypes.IndexOf(BasicTypeFinder, type);
				if (num >= 0)
				{
					return ((BasicType)basicTypes[num]).Serializer;
				}
				WireType defaultWireType;
				IProtoSerializer protoSerializer = ((MetaType.GetContractFamily(this, type, null) == MetaType.AttributeFamily.None) ? ValueMember.TryGetCoreSerializer(this, DataFormat.Default, type, out defaultWireType, asReference: false, dynamicType: false, overwriteList: false, allowComplexTypes: false) : null);
				if (protoSerializer != null)
				{
					basicTypes.Add(new BasicType(type, protoSerializer));
				}
				return protoSerializer;
			}
		}

		internal int FindOrAddAuto(Type type, bool demand, bool addWithContractOnly, bool addEvenIfAutoDisabled)
		{
			int num = types.IndexOf(MetaTypeFinder, type);
			if (num >= 0)
			{
				MetaType metaType = (MetaType)types[num];
				if (metaType.Pending)
				{
					WaitOnLock(metaType);
				}
				return num;
			}
			bool flag = AutoAddMissingTypes || addEvenIfAutoDisabled;
			if (!Helpers.IsEnum(type) && TryGetBasicTypeSerializer(type) != null)
			{
				if (flag && !addWithContractOnly)
				{
					throw MetaType.InbuiltType(type);
				}
				return -1;
			}
			Type type2 = TypeModel.ResolveProxies(type);
			if (type2 != null && type2 != type)
			{
				num = types.IndexOf(MetaTypeFinder, type2);
				type = type2;
			}
			if (num < 0)
			{
				int opaqueToken = 0;
				Type type3 = type;
				bool flag2 = false;
				try
				{
					TakeLock(ref opaqueToken);
					MetaType metaType;
					if ((metaType = RecogniseCommonTypes(type)) == null)
					{
						MetaType.AttributeFamily contractFamily = MetaType.GetContractFamily(this, type, null);
						if (contractFamily == MetaType.AttributeFamily.AutoTuple)
						{
							flag = (addEvenIfAutoDisabled = true);
						}
						if (!flag || (!Helpers.IsEnum(type) && addWithContractOnly && contractFamily == MetaType.AttributeFamily.None))
						{
							if (demand)
							{
								TypeModel.ThrowUnexpectedType(type);
							}
							return num;
						}
						metaType = Create(type);
					}
					metaType.Pending = true;
					int num2 = types.IndexOf(MetaTypeFinder, type);
					if (num2 < 0)
					{
						ThrowIfFrozen();
						num = types.Add(metaType);
						flag2 = true;
					}
					else
					{
						num = num2;
					}
					if (flag2)
					{
						metaType.ApplyDefaultBehaviour();
						metaType.Pending = false;
					}
				}
				finally
				{
					ReleaseLock(opaqueToken);
					if (flag2)
					{
						ResetKeyCache();
					}
				}
			}
			return num;
		}

		private MetaType RecogniseCommonTypes(Type type)
		{
			return null;
		}

		private MetaType Create(Type type)
		{
			ThrowIfFrozen();
			return new MetaType(this, type, defaultFactory);
		}

		public MetaType Add(Type type, bool applyDefaultBehaviour)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			MetaType metaType = FindWithoutAdd(type);
			if (metaType != null)
			{
				return metaType;
			}
			int opaqueToken = 0;
			if (type.IsInterface && MapType(MetaType.ienumerable).IsAssignableFrom(type) && TypeModel.GetListItemType(this, type) == null)
			{
				throw new ArgumentException("IEnumerable[<T>] data cannot be used as a meta-type unless an Add method can be resolved");
			}
			try
			{
				metaType = RecogniseCommonTypes(type);
				if (metaType != null)
				{
					if (!applyDefaultBehaviour)
					{
						throw new ArgumentException("Default behaviour must be observed for certain types with special handling; " + type.FullName, "applyDefaultBehaviour");
					}
					applyDefaultBehaviour = false;
				}
				if (metaType == null)
				{
					metaType = Create(type);
				}
				metaType.Pending = true;
				TakeLock(ref opaqueToken);
				if (FindWithoutAdd(type) != null)
				{
					throw new ArgumentException("Duplicate type", "type");
				}
				ThrowIfFrozen();
				types.Add(metaType);
				if (applyDefaultBehaviour)
				{
					metaType.ApplyDefaultBehaviour();
				}
				metaType.Pending = false;
				return metaType;
			}
			finally
			{
				ReleaseLock(opaqueToken);
				ResetKeyCache();
			}
		}

		private void ThrowIfFrozen()
		{
			if (GetOption(4))
			{
				throw new InvalidOperationException("The model cannot be changed once frozen");
			}
		}

		public void Freeze()
		{
			if (GetOption(2))
			{
				throw new InvalidOperationException("The default model cannot be frozen");
			}
			SetOption(4, value: true);
		}

		protected override int GetKeyImpl(Type type)
		{
			return GetKey(type, demand: false, getBaseKey: true);
		}

		internal int GetKey(Type type, bool demand, bool getBaseKey)
		{
			try
			{
				int num = FindOrAddAuto(type, demand, addWithContractOnly: true, addEvenIfAutoDisabled: false);
				if (num >= 0)
				{
					MetaType source = (MetaType)types[num];
					if (getBaseKey)
					{
						source = MetaType.GetRootType(source);
						num = FindOrAddAuto(source.Type, demand: true, addWithContractOnly: true, addEvenIfAutoDisabled: false);
					}
				}
				return num;
			}
			catch (NotSupportedException)
			{
				throw;
			}
			catch (Exception ex2)
			{
				if (ex2.Message.IndexOf(type.FullName) >= 0)
				{
					throw;
				}
				throw new ProtoException(ex2.Message + " (" + type.FullName + ")", ex2);
			}
		}

		protected internal override void Serialize(int key, object value, ProtoWriter dest)
		{
			((MetaType)types[key]).Serializer.Write(value, dest);
		}

		protected internal override object Deserialize(int key, object value, ProtoReader source)
		{
			IProtoSerializer serializer = ((MetaType)types[key]).Serializer;
			if (value == null && Helpers.IsValueType(serializer.ExpectedType))
			{
				if (serializer.RequiresOldValue)
				{
					value = Activator.CreateInstance(serializer.ExpectedType);
				}
				return serializer.Read(value, source);
			}
			return serializer.Read(value, source);
		}

		internal ProtoSerializer GetSerializer(IProtoSerializer serializer, bool compiled)
		{
			if (serializer == null)
			{
				throw new ArgumentNullException("serializer");
			}
			if (compiled)
			{
				return CompilerContext.BuildSerializer(serializer, this);
			}
			return serializer.Write;
		}

		public void CompileInPlace()
		{
			BasicList.NodeEnumerator enumerator = types.GetEnumerator();
			while (enumerator.MoveNext())
			{
				MetaType metaType = (MetaType)enumerator.Current;
				metaType.CompileInPlace();
			}
		}

		private void BuildAllSerializers()
		{
			for (int i = 0; i < types.Count; i++)
			{
				MetaType metaType = (MetaType)types[i];
				if (metaType.Serializer == null)
				{
					throw new InvalidOperationException("No serializer available for " + metaType.Type.Name);
				}
			}
		}

		public TypeModel Compile()
		{
			CompilerOptions compilerOptions = new CompilerOptions();
			return Compile(compilerOptions);
		}

		private static ILGenerator Override(TypeBuilder type, string name)
		{
			MethodInfo method = type.BaseType.GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic);
			ParameterInfo[] parameters = method.GetParameters();
			Type[] array = new Type[parameters.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = parameters[i].ParameterType;
			}
			MethodBuilder methodBuilder = type.DefineMethod(method.Name, (method.Attributes & ~MethodAttributes.Abstract) | MethodAttributes.Final, method.CallingConvention, method.ReturnType, array);
			ILGenerator iLGenerator = methodBuilder.GetILGenerator();
			type.DefineMethodOverride(methodBuilder, method);
			return iLGenerator;
		}

		public TypeModel Compile(string name, string path)
		{
			CompilerOptions compilerOptions = new CompilerOptions();
			compilerOptions.TypeName = name;
			compilerOptions.OutputPath = path;
			return Compile(compilerOptions);
		}

		public TypeModel Compile(CompilerOptions options)
		{
			if (options == null)
			{
				throw new ArgumentNullException("options");
			}
			string text = options.TypeName;
			string outputPath = options.OutputPath;
			BuildAllSerializers();
			Freeze();
			bool flag = !string.IsNullOrEmpty(outputPath);
			if (string.IsNullOrEmpty(text))
			{
				if (flag)
				{
					throw new ArgumentNullException("typeName");
				}
				text = Guid.NewGuid().ToString();
			}
			string text2;
			string text3;
			if (outputPath == null)
			{
				text2 = text;
				text3 = text2 + ".dll";
			}
			else
			{
				text2 = new FileInfo(Path.GetFileNameWithoutExtension(outputPath)).Name;
				text3 = text2 + Path.GetExtension(outputPath);
			}
			AssemblyName assemblyName = new AssemblyName
			{
				Version = options.AssemblyVersion
			};
			assemblyName.Name = text2;
			AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, (!flag) ? AssemblyBuilderAccess.Run : ((AssemblyBuilderAccess)3));
			ModuleBuilder module = (flag ? assemblyBuilder.DefineDynamicModule(text3, outputPath) : assemblyBuilder.DefineDynamicModule(text3));
			WriteAssemblyAttributes(options, text2, assemblyBuilder);
			TypeBuilder typeBuilder = WriteBasicTypeModel(options, text, module);
			WriteSerializers(options, text2, typeBuilder, out var index, out var hasInheritance, out var methodPairs, out var ilVersion);
			WriteGetKeyImpl(typeBuilder, hasInheritance, methodPairs, ilVersion, text2, out var il, out var knownTypesCategory, out var knownTypes, out var knownTypesLookupType);
			il = Override(typeBuilder, "SerializeDateTimeKind");
			il.Emit(IncludeDateTimeKind ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
			il.Emit(OpCodes.Ret);
			CompilerContext ctx = WriteSerializeDeserialize(text2, typeBuilder, methodPairs, ilVersion, ref il);
			WriteConstructors(typeBuilder, ref index, methodPairs, ref il, knownTypesCategory, knownTypes, knownTypesLookupType, ctx);
			Type type = typeBuilder.CreateType();
			if (!string.IsNullOrEmpty(outputPath))
			{
				try
				{
					assemblyBuilder.Save(outputPath);
				}
				catch (IOException ex)
				{
					throw new IOException(outputPath + ", " + ex.Message, ex);
				}
			}
			return (TypeModel)Activator.CreateInstance(type);
		}

		private void WriteConstructors(TypeBuilder type, ref int index, SerializerPair[] methodPairs, ref ILGenerator il, int knownTypesCategory, FieldBuilder knownTypes, Type knownTypesLookupType, CompilerContext ctx)
		{
			type.DefineDefaultConstructor(MethodAttributes.Public);
			il = type.DefineTypeInitializer().GetILGenerator();
			switch (knownTypesCategory)
			{
			case 1:
				CompilerContext.LoadValue(il, types.Count);
				il.Emit(OpCodes.Newarr, ctx.MapType(typeof(Type)));
				index = 0;
				foreach (SerializerPair serializerPair3 in methodPairs)
				{
					il.Emit(OpCodes.Dup);
					CompilerContext.LoadValue(il, index);
					il.Emit(OpCodes.Ldtoken, serializerPair3.Type.Type);
					il.EmitCall(OpCodes.Call, ctx.MapType(typeof(Type)).GetMethod("GetTypeFromHandle"), null);
					il.Emit(OpCodes.Stelem_Ref);
					index++;
				}
				il.Emit(OpCodes.Stsfld, knownTypes);
				il.Emit(OpCodes.Ret);
				break;
			case 2:
			{
				CompilerContext.LoadValue(il, types.Count);
				il.Emit(OpCodes.Newobj, knownTypesLookupType.GetConstructor(new Type[1] { MapType(typeof(int)) }));
				il.Emit(OpCodes.Stsfld, knownTypes);
				int num2 = 0;
				foreach (SerializerPair serializerPair2 in methodPairs)
				{
					il.Emit(OpCodes.Ldsfld, knownTypes);
					il.Emit(OpCodes.Ldtoken, serializerPair2.Type.Type);
					il.EmitCall(OpCodes.Call, ctx.MapType(typeof(Type)).GetMethod("GetTypeFromHandle"), null);
					int value2 = num2++;
					int baseKey2 = serializerPair2.BaseKey;
					if (baseKey2 != serializerPair2.MetaKey)
					{
						value2 = -1;
						for (int l = 0; l < methodPairs.Length; l++)
						{
							if (methodPairs[l].BaseKey == baseKey2 && methodPairs[l].MetaKey == baseKey2)
							{
								value2 = l;
								break;
							}
						}
					}
					CompilerContext.LoadValue(il, value2);
					il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("Add", new Type[2]
					{
						MapType(typeof(Type)),
						MapType(typeof(int))
					}), null);
				}
				il.Emit(OpCodes.Ret);
				break;
			}
			case 3:
			{
				CompilerContext.LoadValue(il, types.Count);
				il.Emit(OpCodes.Newobj, knownTypesLookupType.GetConstructor(new Type[1] { MapType(typeof(int)) }));
				il.Emit(OpCodes.Stsfld, knownTypes);
				int num = 0;
				foreach (SerializerPair serializerPair in methodPairs)
				{
					il.Emit(OpCodes.Ldsfld, knownTypes);
					il.Emit(OpCodes.Ldtoken, serializerPair.Type.Type);
					il.EmitCall(OpCodes.Call, ctx.MapType(typeof(Type)).GetMethod("GetTypeFromHandle"), null);
					int value = num++;
					int baseKey = serializerPair.BaseKey;
					if (baseKey != serializerPair.MetaKey)
					{
						value = -1;
						for (int j = 0; j < methodPairs.Length; j++)
						{
							if (methodPairs[j].BaseKey == baseKey && methodPairs[j].MetaKey == baseKey)
							{
								value = j;
								break;
							}
						}
					}
					CompilerContext.LoadValue(il, value);
					il.Emit(OpCodes.Box, MapType(typeof(int)));
					il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("Add", new Type[2]
					{
						MapType(typeof(object)),
						MapType(typeof(object))
					}), null);
				}
				il.Emit(OpCodes.Ret);
				break;
			}
			default:
				throw new InvalidOperationException();
			}
		}

		private CompilerContext WriteSerializeDeserialize(string assemblyName, TypeBuilder type, SerializerPair[] methodPairs, CompilerContext.ILVersion ilVersion, ref ILGenerator il)
		{
			il = Override(type, "Serialize");
			CompilerContext compilerContext = new CompilerContext(il, isStatic: false, isWriter: true, methodPairs, this, ilVersion, assemblyName, MapType(typeof(object)), "Serialize " + type.Name);
			CodeLabel[] array = new CodeLabel[types.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = compilerContext.DefineLabel();
			}
			il.Emit(OpCodes.Ldarg_1);
			compilerContext.Switch(array);
			compilerContext.Return();
			for (int j = 0; j < array.Length; j++)
			{
				SerializerPair serializerPair = methodPairs[j];
				compilerContext.MarkLabel(array[j]);
				il.Emit(OpCodes.Ldarg_2);
				compilerContext.CastFromObject(serializerPair.Type.Type);
				il.Emit(OpCodes.Ldarg_3);
				il.EmitCall(OpCodes.Call, serializerPair.Serialize, null);
				compilerContext.Return();
			}
			il = Override(type, "Deserialize");
			compilerContext = new CompilerContext(il, isStatic: false, isWriter: false, methodPairs, this, ilVersion, assemblyName, MapType(typeof(object)), "Deserialize " + type.Name);
			for (int k = 0; k < array.Length; k++)
			{
				array[k] = compilerContext.DefineLabel();
			}
			il.Emit(OpCodes.Ldarg_1);
			compilerContext.Switch(array);
			compilerContext.LoadNullRef();
			compilerContext.Return();
			for (int l = 0; l < array.Length; l++)
			{
				SerializerPair serializerPair2 = methodPairs[l];
				compilerContext.MarkLabel(array[l]);
				Type type2 = serializerPair2.Type.Type;
				if (Helpers.IsValueType(type2))
				{
					il.Emit(OpCodes.Ldarg_2);
					il.Emit(OpCodes.Ldarg_3);
					il.EmitCall(OpCodes.Call, EmitBoxedSerializer(type, l, type2, methodPairs, this, ilVersion, assemblyName), null);
					compilerContext.Return();
				}
				else
				{
					il.Emit(OpCodes.Ldarg_2);
					compilerContext.CastFromObject(type2);
					il.Emit(OpCodes.Ldarg_3);
					il.EmitCall(OpCodes.Call, serializerPair2.Deserialize, null);
					compilerContext.Return();
				}
			}
			return compilerContext;
		}

		private void WriteGetKeyImpl(TypeBuilder type, bool hasInheritance, SerializerPair[] methodPairs, CompilerContext.ILVersion ilVersion, string assemblyName, out ILGenerator il, out int knownTypesCategory, out FieldBuilder knownTypes, out Type knownTypesLookupType)
		{
			il = Override(type, "GetKeyImpl");
			CompilerContext compilerContext = new CompilerContext(il, isStatic: false, isWriter: false, methodPairs, this, ilVersion, assemblyName, MapType(typeof(Type), demand: true), "GetKeyImpl");
			if (types.Count <= 20)
			{
				knownTypesCategory = 1;
				knownTypesLookupType = MapType(typeof(Type[]), demand: true);
			}
			else
			{
				knownTypesLookupType = MapType(typeof(Dictionary<Type, int>), demand: false);
				if (knownTypesLookupType == null)
				{
					knownTypesLookupType = MapType(typeof(Hashtable), demand: true);
					knownTypesCategory = 3;
				}
				else
				{
					knownTypesCategory = 2;
				}
			}
			knownTypes = type.DefineField("knownTypes", knownTypesLookupType, FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly);
			switch (knownTypesCategory)
			{
			case 1:
				il.Emit(OpCodes.Ldsfld, knownTypes);
				il.Emit(OpCodes.Ldarg_1);
				il.EmitCall(OpCodes.Callvirt, MapType(typeof(IList)).GetMethod("IndexOf", new Type[1] { MapType(typeof(object)) }), null);
				if (hasInheritance)
				{
					il.DeclareLocal(MapType(typeof(int)));
					il.Emit(OpCodes.Dup);
					il.Emit(OpCodes.Stloc_0);
					BasicList basicList = new BasicList();
					int num = -1;
					for (int i = 0; i < methodPairs.Length && methodPairs[i].MetaKey != methodPairs[i].BaseKey; i++)
					{
						if (num == methodPairs[i].BaseKey)
						{
							basicList.Add(basicList[basicList.Count - 1]);
							continue;
						}
						basicList.Add(compilerContext.DefineLabel());
						num = methodPairs[i].BaseKey;
					}
					CodeLabel[] array = new CodeLabel[basicList.Count];
					basicList.CopyTo(array, 0);
					compilerContext.Switch(array);
					il.Emit(OpCodes.Ldloc_0);
					il.Emit(OpCodes.Ret);
					num = -1;
					for (int num2 = array.Length - 1; num2 >= 0; num2--)
					{
						if (num != methodPairs[num2].BaseKey)
						{
							num = methodPairs[num2].BaseKey;
							int value = -1;
							for (int j = array.Length; j < methodPairs.Length; j++)
							{
								if (methodPairs[j].BaseKey == num && methodPairs[j].MetaKey == num)
								{
									value = j;
									break;
								}
							}
							compilerContext.MarkLabel(array[num2]);
							CompilerContext.LoadValue(il, value);
							il.Emit(OpCodes.Ret);
						}
					}
				}
				else
				{
					il.Emit(OpCodes.Ret);
				}
				break;
			case 2:
			{
				LocalBuilder local = il.DeclareLocal(MapType(typeof(int)));
				Label label2 = il.DefineLabel();
				il.Emit(OpCodes.Ldsfld, knownTypes);
				il.Emit(OpCodes.Ldarg_1);
				il.Emit(OpCodes.Ldloca_S, local);
				il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("TryGetValue", BindingFlags.Instance | BindingFlags.Public), null);
				il.Emit(OpCodes.Brfalse_S, label2);
				il.Emit(OpCodes.Ldloc_S, local);
				il.Emit(OpCodes.Ret);
				il.MarkLabel(label2);
				il.Emit(OpCodes.Ldc_I4_M1);
				il.Emit(OpCodes.Ret);
				break;
			}
			case 3:
			{
				Label label = il.DefineLabel();
				il.Emit(OpCodes.Ldsfld, knownTypes);
				il.Emit(OpCodes.Ldarg_1);
				il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetProperty("Item").GetGetMethod(), null);
				il.Emit(OpCodes.Dup);
				il.Emit(OpCodes.Brfalse_S, label);
				if (ilVersion == CompilerContext.ILVersion.Net1)
				{
					il.Emit(OpCodes.Unbox, MapType(typeof(int)));
					il.Emit(OpCodes.Ldobj, MapType(typeof(int)));
				}
				else
				{
					il.Emit(OpCodes.Unbox_Any, MapType(typeof(int)));
				}
				il.Emit(OpCodes.Ret);
				il.MarkLabel(label);
				il.Emit(OpCodes.Pop);
				il.Emit(OpCodes.Ldc_I4_M1);
				il.Emit(OpCodes.Ret);
				break;
			}
			default:
				throw new InvalidOperationException();
			}
		}

		private void WriteSerializers(CompilerOptions options, string assemblyName, TypeBuilder type, out int index, out bool hasInheritance, out SerializerPair[] methodPairs, out CompilerContext.ILVersion ilVersion)
		{
			index = 0;
			hasInheritance = false;
			methodPairs = new SerializerPair[types.Count];
			BasicList.NodeEnumerator enumerator = types.GetEnumerator();
			while (enumerator.MoveNext())
			{
				MetaType metaType = (MetaType)enumerator.Current;
				MethodBuilder methodBuilder = type.DefineMethod("Write", MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, MapType(typeof(void)), new Type[2]
				{
					metaType.Type,
					MapType(typeof(ProtoWriter))
				});
				MethodBuilder methodBuilder2 = type.DefineMethod("Read", MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, metaType.Type, new Type[2]
				{
					metaType.Type,
					MapType(typeof(ProtoReader))
				});
				SerializerPair serializerPair = new SerializerPair(GetKey(metaType.Type, demand: true, getBaseKey: false), GetKey(metaType.Type, demand: true, getBaseKey: true), metaType, methodBuilder, methodBuilder2, methodBuilder.GetILGenerator(), methodBuilder2.GetILGenerator());
				methodPairs[index++] = serializerPair;
				if (serializerPair.MetaKey != serializerPair.BaseKey)
				{
					hasInheritance = true;
				}
			}
			if (hasInheritance)
			{
				Array.Sort(methodPairs);
			}
			ilVersion = CompilerContext.ILVersion.Net2;
			if (options.MetaDataVersion == 65536)
			{
				ilVersion = CompilerContext.ILVersion.Net1;
			}
			for (index = 0; index < methodPairs.Length; index++)
			{
				SerializerPair serializerPair2 = methodPairs[index];
				CompilerContext compilerContext = new CompilerContext(serializerPair2.SerializeBody, isStatic: true, isWriter: true, methodPairs, this, ilVersion, assemblyName, serializerPair2.Type.Type, "SerializeImpl " + serializerPair2.Type.Type.Name);
				MemberInfo member = serializerPair2.Deserialize.ReturnType;
				compilerContext.CheckAccessibility(ref member);
				serializerPair2.Type.Serializer.EmitWrite(compilerContext, compilerContext.InputValue);
				compilerContext.Return();
				compilerContext = new CompilerContext(serializerPair2.DeserializeBody, isStatic: true, isWriter: false, methodPairs, this, ilVersion, assemblyName, serializerPair2.Type.Type, "DeserializeImpl " + serializerPair2.Type.Type.Name);
				serializerPair2.Type.Serializer.EmitRead(compilerContext, compilerContext.InputValue);
				if (!serializerPair2.Type.Serializer.ReturnsValue)
				{
					compilerContext.LoadValue(compilerContext.InputValue);
				}
				compilerContext.Return();
			}
		}

		private TypeBuilder WriteBasicTypeModel(CompilerOptions options, string typeName, ModuleBuilder module)
		{
			Type type = MapType(typeof(TypeModel));
			TypeAttributes typeAttributes = (type.Attributes & ~TypeAttributes.Abstract) | TypeAttributes.Sealed;
			if (options.Accessibility == Accessibility.Internal)
			{
				typeAttributes &= ~TypeAttributes.Public;
			}
			return module.DefineType(typeName, typeAttributes, type);
		}

		private void WriteAssemblyAttributes(CompilerOptions options, string assemblyName, AssemblyBuilder asm)
		{
			if (!string.IsNullOrEmpty(options.TargetFrameworkName))
			{
				Type type = null;
				try
				{
					type = GetType("System.Runtime.Versioning.TargetFrameworkAttribute", Helpers.GetAssembly(MapType(typeof(string))));
				}
				catch
				{
				}
				if (type != null)
				{
					PropertyInfo[] namedProperties;
					object[] propertyValues;
					if (string.IsNullOrEmpty(options.TargetFrameworkDisplayName))
					{
						namedProperties = new PropertyInfo[0];
						propertyValues = new object[0];
					}
					else
					{
						namedProperties = new PropertyInfo[1] { type.GetProperty("FrameworkDisplayName") };
						propertyValues = new object[1] { options.TargetFrameworkDisplayName };
					}
					CustomAttributeBuilder customAttribute = new CustomAttributeBuilder(type.GetConstructor(new Type[1] { MapType(typeof(string)) }), new object[1] { options.TargetFrameworkName }, namedProperties, propertyValues);
					asm.SetCustomAttribute(customAttribute);
				}
			}
			Type type2 = null;
			try
			{
				type2 = MapType(typeof(InternalsVisibleToAttribute));
			}
			catch
			{
			}
			if (type2 != null)
			{
				BasicList basicList = new BasicList();
				BasicList basicList2 = new BasicList();
				BasicList.NodeEnumerator enumerator = types.GetEnumerator();
				while (enumerator.MoveNext())
				{
					MetaType metaType = (MetaType)enumerator.Current;
					Assembly assembly = Helpers.GetAssembly(metaType.Type);
					if (basicList2.IndexOfReference(assembly) >= 0)
					{
						continue;
					}
					basicList2.Add(assembly);
					AttributeMap[] array = AttributeMap.Create(this, assembly);
					for (int i = 0; i < array.Length; i++)
					{
						if (!(array[i].AttributeType != type2))
						{
							array[i].TryGet("AssemblyName", out var value);
							string text = value as string;
							if (!(text == assemblyName) && !string.IsNullOrEmpty(text) && basicList.IndexOfString(text) < 0)
							{
								basicList.Add(text);
								CustomAttributeBuilder customAttribute2 = new CustomAttributeBuilder(type2.GetConstructor(new Type[1] { MapType(typeof(string)) }), new object[1] { text });
								asm.SetCustomAttribute(customAttribute2);
							}
						}
					}
				}
			}
			WriteAssemblyInfoAttributes(options, asm);
		}

		private void WriteAssemblyInfoAttributes(CompilerOptions options, AssemblyBuilder asm)
		{
			WriteAssemblyInfoAttribute<AssemblyFileVersionAttribute>(options, asm, options.AssemblyVersion?.ToString());
			WriteAssemblyInfoAttribute<AssemblyCompanyAttribute>(options, asm, options.AssemblyCompanyName);
			WriteAssemblyInfoAttribute<AssemblyCopyrightAttribute>(options, asm, options.AssemblyCopyright);
			WriteAssemblyInfoAttribute<AssemblyDescriptionAttribute>(options, asm, options.AssemblyDescription);
			WriteAssemblyInfoAttribute<AssemblyProductAttribute>(options, asm, options.AssemblyProductName);
			WriteAssemblyInfoAttribute<AssemblyTitleAttribute>(options, asm, options.AssemblyTitle);
			WriteAssemblyInfoAttribute<AssemblyTrademarkAttribute>(options, asm, options.AssemblyTrademark);
			WriteAssemblyInfoAttribute<AssemblyInformationalVersionAttribute>(options, asm, options.AssemblyProductVersion?.ToString());
			asm.DefineVersionInfoResource();
		}

		private void WriteAssemblyInfoAttribute<TA>(CompilerOptions options, AssemblyBuilder asm, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				Type typeFromHandle = typeof(TA);
				Type[] array = new Type[1] { typeof(string) };
				ConstructorInfo constructor = typeFromHandle.GetConstructor(array);
				CustomAttributeBuilder customAttribute = new CustomAttributeBuilder(constructor, new object[1] { value });
				asm.SetCustomAttribute(customAttribute);
			}
		}

		private static MethodBuilder EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs, TypeModel model, CompilerContext.ILVersion ilVersion, string assemblyName)
		{
			MethodInfo deserialize = methodPairs[i].Deserialize;
			MethodBuilder methodBuilder = type.DefineMethod("_" + i, MethodAttributes.Static, CallingConventions.Standard, model.MapType(typeof(object)), new Type[2]
			{
				model.MapType(typeof(object)),
				model.MapType(typeof(ProtoReader))
			});
			CompilerContext compilerContext = new CompilerContext(methodBuilder.GetILGenerator(), isStatic: true, isWriter: false, methodPairs, model, ilVersion, assemblyName, model.MapType(typeof(object)), "BoxedSerializer " + valueType.Name);
			compilerContext.LoadValue(compilerContext.InputValue);
			CodeLabel label = compilerContext.DefineLabel();
			compilerContext.BranchIfFalse(label, @short: true);
			compilerContext.LoadValue(compilerContext.InputValue);
			compilerContext.CastFromObject(valueType);
			compilerContext.LoadReaderWriter();
			compilerContext.EmitCall(deserialize);
			compilerContext.CastToObject(valueType);
			compilerContext.Return();
			compilerContext.MarkLabel(label);
			using Local local = new Local(compilerContext, valueType);
			compilerContext.LoadAddress(local, valueType);
			compilerContext.EmitCtor(valueType);
			compilerContext.LoadValue(local);
			compilerContext.LoadReaderWriter();
			compilerContext.EmitCall(deserialize);
			compilerContext.CastToObject(valueType);
			compilerContext.Return();
			return methodBuilder;
		}

		internal bool IsPrepared(Type type)
		{
			return FindWithoutAdd(type)?.IsPrepared() ?? false;
		}

		internal EnumSerializer.EnumPair[] GetEnumMap(Type type)
		{
			int num = FindOrAddAuto(type, demand: false, addWithContractOnly: false, addEvenIfAutoDisabled: false);
			if (num >= 0)
			{
				return ((MetaType)types[num]).GetEnumMap();
			}
			return null;
		}

		internal void TakeLock(ref int opaqueToken)
		{
			opaqueToken = 0;
			if (Monitor.TryEnter(types, metadataTimeoutMilliseconds))
			{
				opaqueToken = GetContention();
				return;
			}
			AddContention();
			throw new TimeoutException("Timeout while inspecting metadata; this may indicate a deadlock. This can often be avoided by preparing necessary serializers during application initialization, rather than allowing multiple threads to perform the initial metadata inspection; please also see the LockContended event");
		}

		private int GetContention()
		{
			return Interlocked.CompareExchange(ref contentionCounter, 0, 0);
		}

		private void AddContention()
		{
			Interlocked.Increment(ref contentionCounter);
		}

		internal void ReleaseLock(int opaqueToken)
		{
			if (opaqueToken == 0)
			{
				return;
			}
			Monitor.Exit(types);
			if (opaqueToken == GetContention())
			{
				return;
			}
			LockContentedEventHandler lockContentedEventHandler = this.LockContended;
			if (lockContentedEventHandler != null)
			{
				string stackTrace;
				try
				{
					throw new ProtoException();
				}
				catch (Exception ex)
				{
					stackTrace = ex.StackTrace;
				}
				lockContentedEventHandler(this, new LockContentedEventArgs(stackTrace));
			}
		}

		internal void ResolveListTypes(Type type, ref Type itemType, ref Type defaultType)
		{
			if (type == null || Helpers.GetTypeCode(type) != ProtoTypeCode.Unknown)
			{
				return;
			}
			if (type.IsArray)
			{
				if (type.GetArrayRank() != 1)
				{
					throw new NotSupportedException("Multi-dimension arrays are supported");
				}
				itemType = type.GetElementType();
				if (itemType == MapType(typeof(byte)))
				{
					defaultType = (itemType = null);
				}
				else
				{
					defaultType = type;
				}
			}
			else if (this[type].IgnoreListHandling)
			{
				return;
			}
			if (itemType == null)
			{
				itemType = TypeModel.GetListItemType(this, type);
			}
			if (itemType != null)
			{
				Type itemType2 = null;
				Type defaultType2 = null;
				ResolveListTypes(itemType, ref itemType2, ref defaultType2);
				if (itemType2 != null)
				{
					throw TypeModel.CreateNestedListsNotSupported(type);
				}
			}
			if (!(itemType != null) || !(defaultType == null))
			{
				return;
			}
			if (type.IsClass && !type.IsAbstract && Helpers.GetConstructor(type, Helpers.EmptyTypes, nonPublic: true) != null)
			{
				defaultType = type;
			}
			if (defaultType == null && type.IsInterface)
			{
				Type[] genericArguments;
				if (type.IsGenericType && type.GetGenericTypeDefinition() == MapType(typeof(IDictionary<, >)) && itemType == MapType(typeof(KeyValuePair<, >)).MakeGenericType(genericArguments = type.GetGenericArguments()))
				{
					defaultType = MapType(typeof(Dictionary<, >)).MakeGenericType(genericArguments);
				}
				else
				{
					defaultType = MapType(typeof(List<>)).MakeGenericType(itemType);
				}
			}
			if (defaultType != null && !Helpers.IsAssignableFrom(type, defaultType))
			{
				defaultType = null;
			}
		}

		internal string GetSchemaTypeName(Type effectiveType, DataFormat dataFormat, bool asReference, bool dynamicType, ref CommonImports imports)
		{
			Type underlyingType = Helpers.GetUnderlyingType(effectiveType);
			if (underlyingType != null)
			{
				effectiveType = underlyingType;
			}
			if (effectiveType == MapType(typeof(byte[])))
			{
				return "bytes";
			}
			WireType defaultWireType;
			IProtoSerializer protoSerializer = ValueMember.TryGetCoreSerializer(this, dataFormat, effectiveType, out defaultWireType, asReference: false, dynamicType: false, overwriteList: false, allowComplexTypes: false);
			if (protoSerializer == null)
			{
				if (asReference || dynamicType)
				{
					imports |= CommonImports.Bcl;
					return ".bcl.NetObjectProxy";
				}
				return this[effectiveType].GetSurrogateOrBaseOrSelf(deep: true).GetSchemaTypeName();
			}
			if (protoSerializer is ParseableSerializer)
			{
				if (asReference)
				{
					imports |= CommonImports.Bcl;
				}
				if (!asReference)
				{
					return "string";
				}
				return ".bcl.NetObjectProxy";
			}
			switch (Helpers.GetTypeCode(effectiveType))
			{
			case ProtoTypeCode.Boolean:
				return "bool";
			case ProtoTypeCode.Single:
				return "float";
			case ProtoTypeCode.Double:
				return "double";
			case ProtoTypeCode.String:
				if (asReference)
				{
					imports |= CommonImports.Bcl;
				}
				if (!asReference)
				{
					return "string";
				}
				return ".bcl.NetObjectProxy";
			case ProtoTypeCode.Char:
			case ProtoTypeCode.Byte:
			case ProtoTypeCode.UInt16:
			case ProtoTypeCode.UInt32:
				if (dataFormat == DataFormat.FixedSize)
				{
					return "fixed32";
				}
				return "uint32";
			case ProtoTypeCode.SByte:
			case ProtoTypeCode.Int16:
			case ProtoTypeCode.Int32:
				return dataFormat switch
				{
					DataFormat.ZigZag => "sint32", 
					DataFormat.FixedSize => "sfixed32", 
					_ => "int32", 
				};
			case ProtoTypeCode.UInt64:
				if (dataFormat == DataFormat.FixedSize)
				{
					return "fixed64";
				}
				return "uint64";
			case ProtoTypeCode.Int64:
				return dataFormat switch
				{
					DataFormat.ZigZag => "sint64", 
					DataFormat.FixedSize => "sfixed64", 
					_ => "int64", 
				};
			case ProtoTypeCode.DateTime:
				switch (dataFormat)
				{
				case DataFormat.FixedSize:
					return "sint64";
				case DataFormat.WellKnown:
					imports |= CommonImports.Timestamp;
					return ".google.protobuf.Timestamp";
				default:
					imports |= CommonImports.Bcl;
					return ".bcl.DateTime";
				}
			case ProtoTypeCode.TimeSpan:
				switch (dataFormat)
				{
				case DataFormat.FixedSize:
					return "sint64";
				case DataFormat.WellKnown:
					imports |= CommonImports.Duration;
					return ".google.protobuf.Duration";
				default:
					imports |= CommonImports.Bcl;
					return ".bcl.TimeSpan";
				}
			case ProtoTypeCode.Decimal:
				imports |= CommonImports.Bcl;
				return ".bcl.Decimal";
			case ProtoTypeCode.Guid:
				imports |= CommonImports.Bcl;
				return ".bcl.Guid";
			case ProtoTypeCode.Type:
				return "string";
			default:
				throw new NotSupportedException("No .proto map found for: " + effectiveType.FullName);
			}
		}

		public void SetDefaultFactory(MethodInfo methodInfo)
		{
			VerifyFactory(methodInfo, null);
			defaultFactory = methodInfo;
		}

		internal void VerifyFactory(MethodInfo factory, Type type)
		{
			if (factory != null)
			{
				if (type != null && Helpers.IsValueType(type))
				{
					throw new InvalidOperationException();
				}
				if (!factory.IsStatic)
				{
					throw new ArgumentException("A factory-method must be static", "factory");
				}
				if (type != null && factory.ReturnType != type && factory.ReturnType != MapType(typeof(object)))
				{
					throw new ArgumentException("The factory-method must return object" + ((type == null) ? "" : (" or " + type.FullName)), "factory");
				}
				if (!CallbackSet.CheckCallbackParameters(this, factory))
				{
					throw new ArgumentException("Invalid factory signature in " + factory.DeclaringType.FullName + "." + factory.Name, "factory");
				}
			}
		}

		internal static void OnBeforeApplyDefaultBehaviour(MetaType metaType, ref TypeAddedEventArgs args)
		{
			OnApplyDefaultBehaviour((metaType?.Model as RuntimeTypeModel)?.BeforeApplyDefaultBehaviour, metaType, ref args);
		}

		internal static void OnAfterApplyDefaultBehaviour(MetaType metaType, ref TypeAddedEventArgs args)
		{
			OnApplyDefaultBehaviour((metaType?.Model as RuntimeTypeModel)?.AfterApplyDefaultBehaviour, metaType, ref args);
		}

		private static void OnApplyDefaultBehaviour(EventHandler<TypeAddedEventArgs> handler, MetaType metaType, ref TypeAddedEventArgs args)
		{
			if (handler != null)
			{
				if (args == null)
				{
					args = new TypeAddedEventArgs(metaType);
				}
				handler(metaType.Model, args);
			}
		}
	}
	public sealed class LockContentedEventArgs : EventArgs
	{
		private readonly string ownerStackTrace;

		public string OwnerStackTrace => ownerStackTrace;

		internal LockContentedEventArgs(string ownerStackTrace)
		{
			this.ownerStackTrace = ownerStackTrace;
		}
	}
	public delegate void LockContentedEventHandler(object sender, LockContentedEventArgs args);
	public sealed class SubType
	{
		internal sealed class Comparer : IComparer, IComparer<SubType>
		{
			public static readonly Comparer Default = new Comparer();

			public int Compare(object x, object y)
			{
				return Compare(x as SubType, y as SubType);
			}

			public int Compare(SubType x, SubType y)
			{
				if (x == y)
				{
					return 0;
				}
				if (x == null)
				{
					return -1;
				}
				if (y == null)
				{
					return 1;
				}
				return x.FieldNumber.CompareTo(y.FieldNumber);
			}
		}

		private int _fieldNumber;

		private readonly MetaType derivedType;

		private readonly DataFormat dataFormat;

		private IProtoSerializer serializer;

		public int FieldNumber
		{
			get
			{
				return _fieldNumber;
			}
			internal set
			{
				if (_fieldNumber != value)
				{
					MetaType.AssertValidFieldNumber(value);
					ThrowIfFrozen();
					_fieldNumber = value;
				}
			}
		}

		public MetaType DerivedType => derivedType;

		internal IProtoSerializer Serializer => serializer ?? (serializer = BuildSerializer());

		private void ThrowIfFrozen()
		{
			if (serializer != null)
			{
				throw new InvalidOperationException("The type cannot be changed once a serializer has been generated");
			}
		}

		public SubType(int fieldNumber, MetaType derivedType, DataFormat format)
		{
			if (derivedType == null)
			{
				throw new ArgumentNullException("derivedType");
			}
			if (fieldNumber <= 0)
			{
				throw new ArgumentOutOfRangeException("fieldNumber");
			}
			_fieldNumber = fieldNumber;
			this.derivedType = derivedType;
			dataFormat = format;
		}

		private IProtoSerializer BuildSerializer()
		{
			WireType wireType = WireType.String;
			if (dataFormat == DataFormat.Group)
			{
				wireType = WireType.StartGroup;
			}
			IProtoSerializer tail = new SubItemSerializer(derivedType.Type, derivedType.GetKey(demand: false, getBaseKey: false), derivedType, recursionCheck: false);
			return new TagDecorator(_fieldNumber, wireType, strict: false, tail);
		}
	}
	public sealed class TypeAddedEventArgs : EventArgs
	{
		public bool ApplyDefaultBehaviour { get; set; }

		public MetaType MetaType { get; }

		public Type Type => MetaType.Type;

		public RuntimeTypeModel Model => MetaType.Model as RuntimeTypeModel;

		internal TypeAddedEventArgs(MetaType metaType)
		{
			MetaType = metaType;
			ApplyDefaultBehaviour = true;
		}
	}
	public class TypeFormatEventArgs : EventArgs
	{
		private Type type;

		private string formattedName;

		private readonly bool typeFixed;

		public Type Type
		{
			get
			{
				return type;
			}
			set
			{
				if (type != value)
				{
					if (typeFixed)
					{
						throw new InvalidOperationException("The type is fixed and cannot be changed");
					}
					type = value;
				}
			}
		}

		public string FormattedName
		{
			get
			{
				return formattedName;
			}
			set
			{
				if (formattedName != value)
				{
					if (!typeFixed)
					{
						throw new InvalidOperationException("The formatted-name is fixed and cannot be changed");
					}
					formattedName = value;
				}
			}
		}

		internal TypeFormatEventArgs(string formattedName)
		{
			if (string.IsNullOrEmpty(formattedName))
			{
				throw new ArgumentNullException("formattedName");
			}
			this.formattedName = formattedName;
		}

		internal TypeFormatEventArgs(Type type)
		{
			this.type = type ?? throw new ArgumentNullException("type");
			typeFixed = true;
		}
	}
	public delegate void TypeFormatEventHandler(object sender, TypeFormatEventArgs args);
	public abstract class TypeModel : IProtoInput<Stream>, IProtoInput<ArraySegment<byte>>, IProtoInput<byte[]>, IProtoOutput<Stream>
	{
		private sealed class DeserializeItemsIterator<T> : DeserializeItemsIterator, IEnumerator<T>, IDisposable, IEnumerator, IEnumerable<T>, IEnumerable
		{
			public new T Current => (T)base.Current;

			IEnumerator<T> IEnumerable<T>.GetEnumerator()
			{
				return this;
			}

			void IDisposable.Dispose()
			{
			}

			public DeserializeItemsIterator(TypeModel model, Stream source, PrefixStyle style, int expectedField, SerializationContext context)
				: base(model, source, model.MapType(typeof(T)), style, expectedField, null, context)
			{
			}
		}

		private class DeserializeItemsIterator : IEnumerator, IEnumerable
		{
			private bool haveObject;

			private object current;

			private readonly Stream source;

			private readonly Type type;

			private readonly PrefixStyle style;

			private readonly int expectedField;

			private readonly Serializer.TypeResolver resolver;

			private readonly TypeModel model;

			private readonly SerializationContext context;

			public object Current => current;

			IEnumerator IEnumerable.GetEnumerator()
			{
				return this;
			}

			public bool MoveNext()
			{
				if (haveObject)
				{
					current = model.DeserializeWithLengthPrefix(source, null, type, style, expectedField, resolver, out var _, out haveObject, context);
				}
				return haveObject;
			}

			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			public DeserializeItemsIterator(TypeModel model, Stream source, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, SerializationContext context)
			{
				haveObject = true;
				this.source = source;
				this.type = type;
				this.style = style;
				this.expectedField = expectedField;
				this.resolver = resolver;
				this.model = model;
				this.context = context;
			}
		}

		private readonly struct KnownTypeKey(Type type, int key)
		{
			public int Key { get; } = key;

			public Type Type { get; } = type;
		}

		protected internal enum CallbackType
		{
			BeforeSerialize,
			AfterSerialize,
			BeforeDeserialize,
			AfterDeserialize
		}

		internal sealed class Formatter : IFormatter
		{
			private readonly TypeModel model;

			private readonly Type type;

			private SerializationBinder binder;

			private StreamingContext context;

			private ISurrogateSelector surrogateSelector;

			public SerializationBinder Binder
			{
				get
				{
					return binder;
				}
				set
				{
					binder = value;
				}
			}

			public StreamingContext Context
			{
				get
				{
					return context;
				}
				set
				{
					context = value;
				}
			}

			public ISurrogateSelector SurrogateSelector
			{
				get
				{
					return surrogateSelector;
				}
				set
				{
					surrogateSelector = value;
				}
			}

			internal Formatter(TypeModel model, Type type)
			{
				this.model = model ?? throw new ArgumentNullException("model");
				this.type = type ?? throw new ArgumentNullException("type");
			}

			public object Deserialize(Stream source)
			{
				return model.Deserialize(source, null, type, -1L, Context);
			}

			public void Serialize(Stream destination, object graph)
			{
				model.Serialize(destination, graph, Context);
			}
		}

		private static readonly Type ilist = typeof(IList);

		private readonly Dictionary<Type, KnownTypeKey> knownKeys = new Dictionary<Type, KnownTypeKey>();

		public event TypeFormatEventHandler DynamicTypeFormatting;

		protected internal virtual bool SerializeDateTimeKind()
		{
			return false;
		}

		protected internal Type MapType(Type type)
		{
			return MapType(type, demand: true);
		}

		protected internal virtual Type MapType(Type type, bool demand)
		{
			return type;
		}

		private WireType GetWireType(ProtoTypeCode code, DataFormat format, ref Type type, out int modelKey)
		{
			modelKey = -1;
			if (Helpers.IsEnum(type))
			{
				modelKey = GetKey(ref type);
				return WireType.Variant;
			}
			switch (code)
			{
			case ProtoTypeCode.Int64:
			case ProtoTypeCode.UInt64:
				if (format != DataFormat.FixedSize)
				{
					return WireType.Variant;
				}
				return WireType.Fixed64;
			case ProtoTypeCode.Boolean:
			case ProtoTypeCode.Char:
			case ProtoTypeCode.SByte:
			case ProtoTypeCode.Byte:
			case ProtoTypeCode.Int16:
			case ProtoTypeCode.UInt16:
			case ProtoTypeCode.Int32:
			case ProtoTypeCode.UInt32:
				if (format != DataFormat.FixedSize)
				{
					return WireType.Variant;
				}
				return WireType.Fixed32;
			case ProtoTypeCode.Double:
				return WireType.Fixed64;
			case ProtoTypeCode.Single:
				return WireType.Fixed32;
			case ProtoTypeCode.Decimal:
			case ProtoTypeCode.DateTime:
			case ProtoTypeCode.String:
			case ProtoTypeCode.TimeSpan:
			case ProtoTypeCode.ByteArray:
			case ProtoTypeCode.Guid:
			case ProtoTypeCode.Uri:
				return WireType.String;
			default:
				if ((modelKey = GetKey(ref type)) >= 0)
				{
					return WireType.String;
				}
				return WireType.None;
			}
		}

		internal bool TrySerializeAuxiliaryType(ProtoWriter writer, Type type, DataFormat format, int tag, object value, bool isInsideList, object parentList)
		{
			if (type == null)
			{
				type = value.GetType();
			}
			ProtoTypeCode typeCode = Helpers.GetTypeCode(type);
			int modelKey;
			WireType wireType = GetWireType(typeCode, format, ref type, out modelKey);
			if (modelKey >= 0)
			{
				if (Helpers.IsEnum(type))
				{
					Serialize(modelKey, value, writer);
					return true;
				}
				ProtoWriter.WriteFieldHeader(tag, wireType, writer);
				switch (wireType)
				{
				case WireType.None:
					throw ProtoWriter.CreateException(writer);
				case WireType.String:
				case WireType.StartGroup:
				{
					SubItemToken token = ProtoWriter.StartSubItem(value, writer);
					Serialize(modelKey, value, writer);
					ProtoWriter.EndSubItem(token, writer);
					return true;
				}
				default:
					Serialize(modelKey, value, writer);
					return true;
				}
			}
			if (wireType != WireType.None)
			{
				ProtoWriter.WriteFieldHeader(tag, wireType, writer);
			}
			switch (typeCode)
			{
			case ProtoTypeCode.Int16:
				ProtoWriter.WriteInt16((short)value, writer);
				return true;
			case ProtoTypeCode.Int32:
				ProtoWriter.WriteInt32((int)value, writer);
				return true;
			case ProtoTypeCode.Int64:
				ProtoWriter.WriteInt64((long)value, writer);
				return true;
			case ProtoTypeCode.UInt16:
				ProtoWriter.WriteUInt16((ushort)value, writer);
				return true;
			case ProtoTypeCode.UInt32:
				ProtoWriter.WriteUInt32((uint)value, writer);
				return true;
			case ProtoTypeCode.UInt64:
				ProtoWriter.WriteUInt64((ulong)value, writer);
				return true;
			case ProtoTypeCode.Boolean:
				ProtoWriter.WriteBoolean((bool)value, writer);
				return true;
			case ProtoTypeCode.SByte:
				ProtoWriter.WriteSByte((sbyte)value, writer);
				return true;
			case ProtoTypeCode.Byte:
				ProtoWriter.WriteByte((byte)value, writer);
				return true;
			case ProtoTypeCode.Char:
				ProtoWriter.WriteUInt16((char)value, writer);
				return true;
			case ProtoTypeCode.Double:
				ProtoWriter.WriteDouble((double)value, writer);
				return true;
			case ProtoTypeCode.Single:
				ProtoWriter.WriteSingle((float)value, writer);
				return true;
			case ProtoTypeCode.DateTime:
				if (SerializeDateTimeKind())
				{
					BclHelpers.WriteDateTimeWithKind((DateTime)value, writer);
				}
				else
				{
					BclHelpers.WriteDateTime((DateTime)value, writer);
				}
				return true;
			case ProtoTypeCode.Decimal:
				BclHelpers.WriteDecimal((decimal)value, writer);
				return true;
			case ProtoTypeCode.String:
				ProtoWriter.WriteString((string)value, writer);
				return true;
			case ProtoTypeCode.ByteArray:
				ProtoWriter.WriteBytes((byte[])value, writer);
				return true;
			case ProtoTypeCode.TimeSpan:
				BclHelpers.WriteTimeSpan((TimeSpan)value, writer);
				return true;
			case ProtoTypeCode.Guid:
				BclHelpers.WriteGuid((Guid)value, writer);
				return true;
			case ProtoTypeCode.Uri:
				ProtoWriter.WriteString(((Uri)value).OriginalString, writer);
				return true;
			default:
				if (value is IEnumerable enumerable)
				{
					if (isInsideList)
					{
						throw CreateNestedListsNotSupported(parentList?.GetType());
					}
					foreach (object item in enumerable)
					{
						if (item == null)
						{
							throw new NullReferenceException();
						}
						if (!TrySerializeAuxiliaryType(writer, null, format, tag, item, isInsideList: true, enumerable))
						{
							ThrowUnexpectedType(item.GetType());
						}
					}
					return true;
				}
				return false;
			}
		}

		private void SerializeCore(ProtoWriter writer, object value)
		{
			if (value == null)
			{
				throw new ArgumentNullException("value");
			}
			Type type = value.GetType();
			int key = GetKey(ref type);
			if (key >= 0)
			{
				Serialize(key, value, writer);
			}
			else if (!TrySerializeAuxiliaryType(writer, type, DataFormat.Default, 1, value, isInsideList: false, null))
			{
				ThrowUnexpectedType(type);
			}
		}

		public void Serialize(Stream dest, object value)
		{
			Serialize(dest, value, null);
		}

		public void Serialize(Stream dest, object value, SerializationContext context)
		{
			using ProtoWriter protoWriter = ProtoWriter.Create(dest, this, context);
			protoWriter.SetRootObject(value);
			SerializeCore(protoWriter, value);
			protoWriter.Close();
		}

		public void Serialize(ProtoWriter dest, object value)
		{
			if (dest == null)
			{
				throw new ArgumentNullException("dest");
			}
			dest.CheckDepthFlushlock();
			dest.SetRootObject(value);
			SerializeCore(dest, value);
			dest.CheckDepthFlushlock();
			ProtoWriter.Flush(dest);
		}

		public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int fieldNumber)
		{
			long bytesRead;
			return DeserializeWithLengthPrefix(source, value, type, style, fieldNumber, (Serializer.TypeResolver)null, out bytesRead);
		}

		public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver)
		{
			long bytesRead;
			return DeserializeWithLengthPrefix(source, value, type, style, expectedField, resolver, out bytesRead);
		}

		public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, out int bytesRead)
		{
			long bytesRead2;
			bool haveObject;
			object result = DeserializeWithLengthPrefix(source, value, type, style, expectedField, resolver, out bytesRead2, out haveObject, null);
			bytesRead = checked((int)bytesRead2);
			return result;
		}

		public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, out long bytesRead)
		{
			bool haveObject;
			return DeserializeWithLengthPrefix(source, value, type, style, expectedField, resolver, out bytesRead, out haveObject, null);
		}

		private object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, out long bytesRead, out bool haveObject, SerializationContext context)
		{
			haveObject = false;
			bytesRead = 0L;
			if (type == null && (style != PrefixStyle.Base128 || resolver == null))
			{
				throw new InvalidOperationException("A type must be provided unless base-128 prefixing is being used in combination with a resolver");
			}
			long num;
			bool flag2;
			do
			{
				bool flag = expectedField > 0 || resolver != null;
				num = ProtoReader.ReadLongLengthPrefix(source, flag, style, out var fieldNumber, out var bytesRead2);
				if (bytesRead2 == 0)
				{
					return value;
				}
				bytesRead += bytesRead2;
				if (num < 0)
				{
					return value;
				}
				if (style == PrefixStyle.Base128)
				{
					if (flag && expectedField == 0 && type == null && resolver != null)
					{
						type = resolver(fieldNumber);
						flag2 = type == null;
					}
					else
					{
						flag2 = expectedField != fieldNumber;
					}
				}
				else
				{
					flag2 = false;
				}
				if (flag2)
				{
					if (num == long.MaxValue)
					{
						throw new InvalidOperationException();
					}
					ProtoReader.Seek(source, num, null);
					bytesRead += num;
				}
			}
			while (flag2);
			ProtoReader protoReader = null;
			try
			{
				protoReader = ProtoReader.Create(source, this, context, num);
				int key = GetKey(ref type);
				if (key >= 0 && !Helpers.IsEnum(type))
				{
					value = Deserialize(key, value, protoReader);
				}
				else if (!TryDeserializeAuxiliaryType(protoReader, DataFormat.Default, 1, type, ref value, skipOtherFields: true, asListItem: false, autoCreate: true, insideList: false, null) && num != 0L)
				{
					ThrowUnexpectedType(type);
				}
				bytesRead += protoReader.LongPosition;
				haveObject = true;
				return value;
			}
			finally
			{
				ProtoReader.Recycle(protoReader);
			}
		}

		public IEnumerable DeserializeItems(Stream source, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver)
		{
			return DeserializeItems(source, type, style, expectedField, resolver, null);
		}

		public IEnumerable DeserializeItems(Stream source, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, SerializationContext context)
		{
			return new DeserializeItemsIterator(this, source, type, style, expectedField, resolver, context);
		}

		public IEnumerable<T> DeserializeItems<T>(Stream source, PrefixStyle style, int expectedField)
		{
			return DeserializeItems<T>(source, style, expectedField, null);
		}

		public IEnumerable<T> DeserializeItems<T>(Stream source, PrefixStyle style, int expectedField, SerializationContext context)
		{
			return new DeserializeItemsIterator<T>(this, source, style, expectedField, context);
		}

		public void SerializeWithLengthPrefix(Stream dest, object value, Type type, PrefixStyle style, int fieldNumber)
		{
			SerializeWithLengthPrefix(dest, value, type, style, fieldNumber, null);
		}

		public void SerializeWithLengthPrefix(Stream dest, object value, Type type, PrefixStyle style, int fieldNumber, SerializationContext context)
		{
			if (type == null)
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}
				type = MapType(value.GetType());
			}
			int key = GetKey(ref type);
			using ProtoWriter protoWriter = ProtoWriter.Create(dest, this, context);
			switch (style)
			{
			case PrefixStyle.None:
				Serialize(key, value, protoWriter);
				break;
			case PrefixStyle.Base128:
			case PrefixStyle.Fixed32:
			case PrefixStyle.Fixed32BigEndian:
				ProtoWriter.WriteObject(value, key, protoWriter, style, fieldNumber);
				break;
			default:
				throw new ArgumentOutOfRangeException("style");
			}
			protoWriter.Close();
		}

		public object Deserialize(Stream source, object value, Type type)
		{
			return Deserialize(source, value, type, null);
		}

		public object Deserialize(Stream source, object value, Type type, SerializationContext context)
		{
			bool noAutoCreate = PrepareDeserialize(value, ref type);
			ProtoReader protoReader = null;
			try
			{
				protoReader = ProtoReader.Create(source, this, context, -1L);
				if (value != null)
				{
					protoReader.SetRootObject(value);
				}
				object result = DeserializeCore(protoReader, type, value, noAutoCreate);
				protoReader.CheckFullyConsumed();
				return result;
			}
			finally
			{
				ProtoReader.Recycle(protoReader);
			}
		}

		private bool PrepareDeserialize(object value, ref Type type)
		{
			if (type == null)
			{
				if (value == null)
				{
					throw new ArgumentNullException("type");
				}
				type = MapType(value.GetType());
			}
			bool result = true;
			Type underlyingType = Helpers.GetUnderlyingType(type);
			if (underlyingType != null)
			{
				type = underlyingType;
				result = false;
			}
			return result;
		}

		public object Deserialize(Stream source, object value, Type type, int length)
		{
			return Deserialize(source, value, type, length, null);
		}

		public object Deserialize(Stream source, object value, Type type, long length)
		{
			return Deserialize(source, value, type, length, null);
		}

		public object Deserialize(Stream source, object value, Type type, int length, SerializationContext context)
		{
			return Deserialize(source, value, type, (length == int.MaxValue) ? long.MaxValue : length, context);
		}

		public object Deserialize(Stream source, object value, Type type, long length, SerializationContext context)
		{
			bool noAutoCreate = PrepareDeserialize(value, ref type);
			ProtoReader protoReader = null;
			try
			{
				protoReader = ProtoReader.Create(source, this, context, length);
				if (value != null)
				{
					protoReader.SetRootObject(value);
				}
				object result = DeserializeCore(protoReader, type, value, noAutoCreate);
				protoReader.CheckFullyConsumed();
				return result;
			}
			finally
			{
				ProtoReader.Recycle(protoReader);
			}
		}

		public object Deserialize(ProtoReader source, object value, Type type)
		{
			if (source == null)
			{
				throw new ArgumentNullException("source");
			}
			bool noAutoCreate = PrepareDeserialize(value, ref type);
			if (value != null)
			{
				source.SetRootObject(value);
			}
			object result = DeserializeCore(source, type, value, noAutoCreate);
			source.CheckFullyConsumed();
			return result;
		}

		private object DeserializeCore(ProtoReader reader, Type type, object value, bool noAutoCreate)
		{
			int key = GetKey(ref type);
			if (key >= 0 && !Helpers.IsEnum(type))
			{
				return Deserialize(key, value, reader);
			}
			TryDeserializeAuxiliaryType(reader, DataFormat.Default, 1, type, ref value, skipOtherFields: true, asListItem: false, noAutoCreate, insideList: false, null);
			return value;
		}

		internal static MethodInfo ResolveListAdd(TypeModel model, Type listType, Type itemType, out bool isList)
		{
			isList = model.MapType(ilist).IsAssignableFrom(listType);
			Type[] array = new Type[1] { itemType };
			MethodInfo instanceMethod = Helpers.GetInstanceMethod(listType, "Add", array);
			if (instanceMethod == null)
			{
				bool flag = listType.IsInterface && model.MapType(typeof(IEnumerable<>)).MakeGenericType(array).IsAssignableFrom(listType);
				Type type = model.MapType(typeof(ICollection<>)).MakeGenericType(array);
				if (flag || type.IsAssignableFrom(listType))
				{
					instanceMethod = Helpers.GetInstanceMethod(type, "Add", array);
				}
			}
			if (instanceMethod == null)
			{
				Type[] interfaces = listType.GetInterfaces();
				foreach (Type type2 in interfaces)
				{
					if (type2.Name == "IProducerConsumerCollection`1" && type2.IsGenericType && type2.GetGenericTypeDefinition().FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1")
					{
						instanceMethod = Helpers.GetInstanceMethod(type2, "TryAdd", array);
						if (instanceMethod != null)
						{
							break;
						}
					}
				}
			}
			if (instanceMethod == null)
			{
				array[0] = model.MapType(typeof(object));
				instanceMethod = Helpers.GetInstanceMethod(listType, "Add", array);
			}
			if ((instanceMethod == null) & isList)
			{
				instanceMethod = Helpers.GetInstanceMethod(model.MapType(ilist), "Add", array);
			}
			return instanceMethod;
		}

		internal static Type GetListItemType(TypeModel model, Type listType)
		{
			if (listType == model.MapType(typeof(string)) || listType.IsArray || !model.MapType(typeof(IEnumerable)).IsAssignableFrom(listType))
			{
				return null;
			}
			BasicList basicList = new BasicList();
			MethodInfo[] methods = listType.GetMethods();
			foreach (MethodInfo methodInfo in methods)
			{
				if (!methodInfo.IsStatic && !(methodInfo.Name != "Add"))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					Type parameterType;
					if (parameters.Length == 1 && !basicList.Contains(parameterType = parameters[0].ParameterType))
					{
						basicList.Add(parameterType);
					}
				}
			}
			string name = listType.Name;
			if (name == null || (name.IndexOf("Queue") < 0 && name.IndexOf("Stack") < 0))
			{
				TestEnumerableListPatterns(model, basicList, listType);
				Type[] interfaces = listType.GetInterfaces();
				foreach (Type iType in interfaces)
				{
					TestEnumerableListPatterns(model, basicList, iType);
				}
			}
			PropertyInfo[] properties = listType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (PropertyInfo propertyInfo in properties)
			{
				if (!(propertyInfo.Name != "Item") && !basicList.Contains(propertyInfo.PropertyType))
				{
					ParameterInfo[] indexParameters = propertyInfo.GetIndexParameters();
					if (indexParameters.Length == 1 && !(indexParameters[0].ParameterType != model.MapType(typeof(int))))
					{
						basicList.Add(propertyInfo.PropertyType);
					}
				}
			}
			switch (basicList.Count)
			{
			case 0:
				return null;
			case 1:
				if ((Type)basicList[0] == listType)
				{
					return null;
				}
				return (Type)basicList[0];
			case 2:
				if ((Type)basicList[0] != listType && CheckDictionaryAccessors(model, (Type)basicList[0], (Type)basicList[1]))
				{
					return (Type)basicList[0];
				}
				if ((Type)basicList[1] != listType && CheckDictionaryAccessors(model, (Type)basicList[1], (Type)basicList[0]))
				{
					return (Type)basicList[1];
				}
				break;
			}
			return null;
		}

		private static void TestEnumerableListPatterns(TypeModel model, BasicList candidates, Type iType)
		{
			if (!iType.IsGenericType)
			{
				return;
			}
			Type genericTypeDefinition = iType.GetGenericTypeDefinition();
			if (genericTypeDefinition == model.MapType(typeof(IEnumerable<>)) || genericTypeDefinition == model.MapType(typeof(ICollection<>)) || genericTypeDefinition.FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1")
			{
				Type[] genericArguments = iType.GetGenericArguments();
				if (!candidates.Contains(genericArguments[0]))
				{
					candidates.Add(genericArguments[0]);
				}
			}
		}

		private static bool CheckDictionaryAccessors(TypeModel model, Type pair, Type value)
		{
			if (pair.IsGenericType && pair.GetGenericTypeDefinition() == model.MapType(typeof(KeyValuePair<, >)))
			{
				return pair.GetGenericArguments()[1] == value;
			}
			return false;
		}

		private bool TryDeserializeList(TypeModel model, ProtoReader reader, DataFormat format, int tag, Type listType, Type itemType, ref object value)
		{
			bool isList;
			MethodInfo methodInfo = ResolveListAdd(model, listType, itemType, out isList);
			if (methodInfo == null)
			{
				throw new NotSupportedException("Unknown list variant: " + listType.FullName);
			}
			bool result = false;
			object value2 = null;
			IList list = value as IList;
			object[] array = (isList ? null : new object[1]);
			BasicList basicList = (listType.IsArray ? new BasicList() : null);
			while (TryDeserializeAuxiliaryType(reader, format, tag, itemType, ref value2, skipOtherFields: true, asListItem: true, autoCreate: true, insideList: true, value ?? listType))
			{
				result = true;
				if (value == null && basicList == null)
				{
					value = CreateListInstance(listType, itemType);
					list = value as IList;
				}
				if (list != null)
				{
					list.Add(value2);
				}
				else if (basicList != null)
				{
					basicList.Add(value2);
				}
				else
				{
					array[0] = value2;
					methodInfo.Invoke(value, array);
				}
				value2 = null;
			}
			if (basicList != null)
			{
				if (value != null)
				{
					if (basicList.Count != 0)
					{
						Array array2 = (Array)value;
						Array array3 = Array.CreateInstance(itemType, array2.Length + basicList.Count);
						Array.Copy(array2, array3, array2.Length);
						basicList.CopyTo(array3, array2.Length);
						value = array3;
					}
				}
				else
				{
					Array array3 = Array.CreateInstance(itemType, basicList.Count);
					basicList.CopyTo(array3, 0);
					value = array3;
				}
			}
			return result;
		}

		private static object CreateListInstance(Type listType, Type itemType)
		{
			Type type = listType;
			if (listType.IsArray)
			{
				return Array.CreateInstance(itemType, 0);
			}
			if (!listType.IsClass || listType.IsAbstract || Helpers.GetConstructor(listType, Helpers.EmptyTypes, nonPublic: true) == null)
			{
				bool flag = false;
				string fullName;
				if (listType.IsInterface && (fullName = listType.FullName) != null && fullName.IndexOf("Dictionary") >= 0)
				{
					if (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(IDictionary<, >))
					{
						Type[] genericArguments = listType.GetGenericArguments();
						type = typeof(Dictionary<, >).MakeGenericType(genericArguments);
						flag = true;
					}
					if (!flag && listType == typeof(IDictionary))
					{
						type = typeof(Hashtable);
						flag = true;
					}
				}
				if (!flag)
				{
					type = typeof(List<>).MakeGenericType(itemType);
					flag = true;
				}
				if (!flag)
				{
					type = typeof(ArrayList);
					flag = true;
				}
			}
			return Activator.CreateInstance(type);
		}

		internal bool TryDeserializeAuxiliaryType(ProtoReader reader, DataFormat format, int tag, Type type, ref object value, bool skipOtherFields, bool asListItem, bool autoCreate, bool insideList, object parentListOrType)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			Type type2 = null;
			ProtoTypeCode typeCode = Helpers.GetTypeCode(type);
			int modelKey;
			WireType wireType = GetWireType(typeCode, format, ref type, out modelKey);
			bool flag = false;
			if (wireType == WireType.None)
			{
				type2 = GetListItemType(this, type);
				if (type2 == null && type.IsArray && type.GetArrayRank() == 1 && type != typeof(byte[]))
				{
					type2 = type.GetElementType();
				}
				if (type2 != null)
				{
					if (insideList)
					{
						throw CreateNestedListsNotSupported((parentListOrType as Type) ?? parentListOrType?.GetType());
					}
					flag = TryDeserializeList(this, reader, format, tag, type, type2, ref value);
					if (!flag && autoCreate)
					{
						value = CreateListInstance(type, type2);
					}
					return flag;
				}
				ThrowUnexpectedType(type);
			}
			while (!(flag && asListItem))
			{
				int num = reader.ReadFieldHeader();
				if (num <= 0)
				{
					break;
				}
				if (num != tag)
				{
					if (skipOtherFields)
					{
						reader.SkipField();
						continue;
					}
					throw ProtoReader.AddErrorData(new InvalidOperationException("Expected field " + tag + ", but found " + num), reader);
				}
				flag = true;
				reader.Hint(wireType);
				if (modelKey >= 0)
				{
					if ((uint)(wireType - 2) <= 1u)
					{
						SubItemToken token = ProtoReader.StartSubItem(reader);
						value = Deserialize(modelKey, value, reader);
						ProtoReader.EndSubItem(token, reader);
					}
					else
					{
						value = Deserialize(modelKey, value, reader);
					}
					continue;
				}
				switch (typeCode)
				{
				case ProtoTypeCode.Int16:
					value = reader.ReadInt16();
					break;
				case ProtoTypeCode.Int32:
					value = reader.ReadInt32();
					break;
				case ProtoTypeCode.Int64:
					value = reader.ReadInt64();
					break;
				case ProtoTypeCode.UInt16:
					value = reader.ReadUInt16();
					break;
				case ProtoTypeCode.UInt32:
					value = reader.ReadUInt32();
					break;
				case ProtoTypeCode.UInt64:
					value = reader.ReadUInt64();
					break;
				case ProtoTypeCode.Boolean:
					value = reader.ReadBoolean();
					break;
				case ProtoTypeCode.SByte:
					value = reader.ReadSByte();
					break;
				case ProtoTypeCode.Byte:
					value = reader.ReadByte();
					break;
				case ProtoTypeCode.Char:
					value = (char)reader.ReadUInt16();
					break;
				case ProtoTypeCode.Double:
					value = reader.ReadDouble();
					break;
				case ProtoTypeCode.Single:
					value = reader.ReadSingle();
					break;
				case ProtoTypeCode.DateTime:
					value = BclHelpers.ReadDateTime(reader);
					break;
				case ProtoTypeCode.Decimal:
					value = BclHelpers.ReadDecimal(reader);
					break;
				case ProtoTypeCode.String:
					value = reader.ReadString();
					break;
				case ProtoTypeCode.ByteArray:
					value = ProtoReader.AppendBytes((byte[])value, reader);
					break;
				case ProtoTypeCode.TimeSpan:
					value = BclHelpers.ReadTimeSpan(reader);
					break;
				case ProtoTypeCode.Guid:
					value = BclHelpers.ReadGuid(reader);
					break;
				case ProtoTypeCode.Uri:
					value = new Uri(reader.ReadString(), UriKind.RelativeOrAbsolute);
					break;
				}
			}
			if (!flag && !asListItem && autoCreate && type != typeof(string))
			{
				value = Activator.CreateInstance(type);
			}
			return flag;
		}

		[Obsolete("Please use RuntimeTypeModel.Create", false)]
		public static RuntimeTypeModel Create()
		{
			return RuntimeTypeModel.Create();
		}

		protected internal static Type ResolveProxies(Type type)
		{
			if (type == null)
			{
				return null;
			}
			if (type.IsGenericParameter)
			{
				return null;
			}
			Type underlyingType = Helpers.GetUnderlyingType(type);
			if (underlyingType != null)
			{
				return underlyingType;
			}
			string fullName = type.FullName;
			if (fullName != null && fullName.StartsWith("System.Data.Entity.DynamicProxies."))
			{
				return type.BaseType;
			}
			Type[] interfaces = type.GetInterfaces();
			Type[] array = interfaces;
			foreach (Type type2 in array)
			{
				switch (type2.FullName)
				{
				case "NHibernate.Proxy.INHibernateProxy":
				case "NHibernate.Proxy.DynamicProxy.IProxy":
				case "NHibernate.Intercept.IFieldInterceptorAccessor":
					return type.BaseType;
				}
			}
			return null;
		}

		public bool IsDefined(Type type)
		{
			return GetKey(ref type) >= 0;
		}

		protected internal int GetKey(ref Type type)
		{
			if (type == null)
			{
				return -1;
			}
			lock (knownKeys)
			{
				if (knownKeys.TryGetValue(type, out var value))
				{
					type = value.Type;
					return value.Key;
				}
			}
			int keyImpl = GetKeyImpl(type);
			Type key = type;
			if (keyImpl < 0)
			{
				Type type2 = ResolveProxies(type);
				if (type2 != null && type2 != type)
				{
					type = type2;
					keyImpl = GetKeyImpl(type);
				}
			}
			lock (knownKeys)
			{
				knownKeys[key] = new KnownTypeKey(type, keyImpl);
				return keyImpl;
			}
		}

		internal void ResetKeyCache()
		{
			lock (knownKeys)
			{
				knownKeys.Clear();
			}
		}

		protected abstract int GetKeyImpl(Type type);

		protected internal abstract void Serialize(int key, object value, ProtoWriter dest);

		protected internal abstract object Deserialize(int key, object value, ProtoReader source);

		public object DeepClone(object value)
		{
			if (value == null)
			{
				return null;
			}
			Type type = value.GetType();
			int key = GetKey(ref type);
			if (key >= 0 && !Helpers.IsEnum(type))
			{
				using (MemoryStream memoryStream = new MemoryStream())
				{
					using (ProtoWriter protoWriter = ProtoWriter.Create(memoryStream, this))
					{
						protoWriter.SetRootObject(value);
						Serialize(key, value, protoWriter);
						protoWriter.Close();
					}
					memoryStream.Position = 0L;
					ProtoReader protoReader = null;
					try
					{
						protoReader = ProtoReader.Create(memoryStream, this, null, -1L);
						return Deserialize(key, null, protoReader);
					}
					finally
					{
						ProtoReader.Recycle(protoReader);
					}
				}
			}
			if (type == typeof(byte[]))
			{
				byte[] array = (byte[])value;
				byte[] array2 = new byte[array.Length];
				Buffer.BlockCopy(array, 0, array2, 0, array.Length);
				return array2;
			}
			if (GetWireType(Helpers.GetTypeCode(type), DataFormat.Default, ref type, out var modelKey) != WireType.None && modelKey < 0)
			{
				return value;
			}
			using MemoryStream memoryStream2 = new MemoryStream();
			using (ProtoWriter protoWriter2 = ProtoWriter.Create(memoryStream2, this))
			{
				if (!TrySerializeAuxiliaryType(protoWriter2, type, DataFormat.Default, 1, value, isInsideList: false, null))
				{
					ThrowUnexpectedType(type);
				}
				protoWriter2.Close();
			}
			memoryStream2.Position = 0L;
			ProtoReader reader = null;
			try
			{
				reader = ProtoReader.Create(memoryStream2, this, null, -1L);
				value = null;
				TryDeserializeAuxiliaryType(reader, DataFormat.Default, 1, type, ref value, skipOtherFields: true, asListItem: false, autoCreate: true, insideList: false, null);
				return value;
			}
			finally
			{
				ProtoReader.Recycle(reader);
			}
		}

		protected internal static void ThrowUnexpectedSubtype(Type expected, Type actual)
		{
			if (expected != ResolveProxies(actual))
			{
				throw new InvalidOperationException("Unexpected sub-type: " + actual.FullName);
			}
		}

		protected internal static void ThrowUnexpectedType(Type type)
		{
			string text = ((type == null) ? "(unknown)" : type.FullName);
			if (type != null)
			{
				Type baseType = type.BaseType;
				if (baseType != null && baseType.IsGenericType && baseType.GetGenericTypeDefinition().Name == "GeneratedMessage`2")
				{
					throw new InvalidOperationException("Are you mixing protobuf-net and protobuf-csharp-port? See https://stackoverflow.com/q/11564914/23354; type: " + text);
				}
			}
			throw new InvalidOperationException("Type is not expected, and no contract can be inferred: " + text);
		}

		internal static Exception CreateNestedListsNotSupported(Type type)
		{
			return new NotSupportedException("Nested or jagged lists and arrays are not supported: " + (type?.FullName ?? "(null)"));
		}

		public static void ThrowCannotCreateInstance(Type type)
		{
			throw new ProtoException("No parameterless constructor found for " + (type?.FullName ?? "(null)"));
		}

		internal static string SerializeType(TypeModel model, Type type)
		{
			if (model != null)
			{
				TypeFormatEventHandler typeFormatEventHandler = model.DynamicTypeFormatting;
				if (typeFormatEventHandler != null)
				{
					TypeFormatEventArgs e = new TypeFormatEventArgs(type);
					typeFormatEventHandler(model, e);
					if (!string.IsNullOrEmpty(e.FormattedName))
					{
						return e.FormattedName;
					}
				}
			}
			return type.AssemblyQualifiedName;
		}

		internal static Type DeserializeType(TypeModel model, string value)
		{
			if (model != null)
			{
				TypeFormatEventHandler typeFormatEventHandler = model.DynamicTypeFormatting;
				if (typeFormatEventHandler != null)
				{
					TypeFormatEventArgs e = new TypeFormatEventArgs(value);
					typeFormatEventHandler(model, e);
					if (e.Type != null)
					{
						return e.Type;
					}
				}
			}
			return Type.GetType(value);
		}

		public bool CanSerializeContractType(Type type)
		{
			return CanSerialize(type, allowBasic: false, allowContract: true, allowLists: true);
		}

		public bool CanSerialize(Type type)
		{
			return CanSerialize(type, allowBasic: true, allowContract: true, allowLists: true);
		}

		public bool CanSerializeBasicType(Type type)
		{
			return CanSerialize(type, allowBasic: true, allowContract: false, allowLists: true);
		}

		private bool CanSerialize(Type type, bool allowBasic, bool allowContract, bool allowLists)
		{
			if (type == null)
			{
				throw new ArgumentNullException("type");
			}
			Type underlyingType = Helpers.GetUnderlyingType(type);
			if (underlyingType != null)
			{
				type = underlyingType;
			}
			ProtoTypeCode typeCode = Helpers.GetTypeCode(type);
			if ((uint)typeCode > 1u)
			{
				return allowBasic;
			}
			int key = GetKey(ref type);
			if (key >= 0)
			{
				return allowContract;
			}
			if (allowLists)
			{
				Type type2 = null;
				if (type.IsArray)
				{
					if (type.GetArrayRank() == 1)
					{
						type2 = type.GetElementType();
					}
				}
				else
				{
					type2 = GetListItemType(this, type);
				}
				if (type2 != null)
				{
					return CanSerialize(type2, allowBasic, allowContract, allowLists: false);
				}
			}
			return false;
		}

		public virtual string GetSchema(Type type)
		{
			return GetSchema(type, ProtoSyntax.Proto2);
		}

		public virtual string GetSchema(Type type, ProtoSyntax syntax)
		{
			throw new NotSupportedException();
		}

		public IFormatter CreateFormatter(Type type)
		{
			return new Formatter(this, type);
		}

		internal virtual Type GetType(string fullName, Assembly context)
		{
			return ResolveKnownType(fullName, this, context);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static Type ResolveKnownType(string name, TypeModel model, Assembly assembly)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			try
			{
				Type type = Type.GetType(name);
				if (type != null)
				{
					return type;
				}
			}
			catch
			{
			}
			try
			{
				int num = name.IndexOf(',');
				string name2 = ((num > 0) ? name.Substring(0, num) : name).Trim();
				if (assembly == null)
				{
					assembly = Assembly.GetCallingAssembly();
				}
				Type type2 = assembly?.GetType(name2);
				if (type2 != null)
				{
					return type2;
				}
			}
			catch
			{
			}
			return null;
		}

		private static SerializationContext CreateContext(object userState)
		{
			if (userState == null)
			{
				return SerializationContext.Default;
			}
			if (userState is SerializationContext result)
			{
				return result;
			}
			SerializationContext serializationContext = new SerializationContext
			{
				Context = userState
			};
			serializationContext.Freeze();
			return serializationContext;
		}

		T IProtoInput<Stream>.Deserialize<T>(Stream source, T value, object userState)
		{
			return (T)Deserialize(source, value, typeof(T), CreateContext(userState));
		}

		T IProtoInput<ArraySegment<byte>>.Deserialize<T>(ArraySegment<byte> source, T value, object userState)
		{
			using MemoryStream source2 = new MemoryStream(source.Array, source.Offset, source.Count);
			return (T)Deserialize(source2, value, typeof(T), CreateContext(userState));
		}

		T IProtoInput<byte[]>.Deserialize<T>(byte[] source, T value, object userState)
		{
			using MemoryStream source2 = new MemoryStream(source);
			return (T)Deserialize(source2, value, typeof(T), CreateContext(userState));
		}

		void IProtoOutput<Stream>.Serialize<T>(Stream destination, T value, object userState)
		{
			Serialize(destination, value, CreateContext(userState));
		}
	}
	public class ValueMember
	{
		internal sealed class Comparer : IComparer, IComparer<ValueMember>
		{
			public static readonly Comparer Default = new Comparer();

			public int Compare(object x, object y)
			{
				return Compare(x as ValueMember, y as ValueMember);
			}

			public int Compare(ValueMember x, ValueMember y)
			{
				if (x == y)
				{
					return 0;
				}
				if (x == null)
				{
					return -1;
				}
				if (y == null)
				{
					return 1;
				}
				return x.FieldNumber.CompareTo(y.FieldNumber);
			}
		}

		private int _fieldNumber;

		private readonly MemberInfo originalMember;

		private MemberInfo backingMember;

		private readonly Type parentType;

		private readonly Type itemType;

		private readonly Type defaultType;

		private readonly Type memberType;

		private object defaultValue;

		private readonly RuntimeTypeModel model;

		private IProtoSerializer serializer;

		private DataFormat dataFormat;

		private DataFormat mapKeyFormat;

		private DataFormat mapValueFormat;

		private MethodInfo getSpecified;

		private MethodInfo setSpecified;

		private string name;

		private const byte OPTIONS_IsStrict = 1;

		private const byte OPTIONS_IsPacked = 2;

		private const byte OPTIONS_IsRequired = 4;

		private const byte OPTIONS_OverwriteList = 8;

		private const byte OPTIONS_SupportNull = 16;

		private const byte OPTIONS_AsReference = 32;

		private const byte OPTIONS_IsMap = 64;

		private const byte OPTIONS_DynamicType = 128;

		private byte flags;

		public int FieldNumber
		{
			get
			{
				return _fieldNumber;
			}
			internal set
			{
				if (_fieldNumber != value)
				{
					MetaType.AssertValidFieldNumber(value);
					ThrowIfFrozen();
					_fieldNumber = value;
				}
			}
		}

		public MemberInfo Member => originalMember;

		public MemberInfo BackingMember
		{
			get
			{
				return backingMember;
			}
			set
			{
				if (backingMember != value)
				{
					ThrowIfFrozen();
					backingMember = value;
				}
			}
		}

		public Type ItemType => itemType;

		public Type MemberType => memberType;

		public Type DefaultType => defaultType;

		public Type ParentType => parentType;

		public object DefaultValue
		{
			get
			{
				return defaultValue;
			}
			set
			{
				if (defaultValue != value)
				{
					ThrowIfFrozen();
					defaultValue = value;
				}
			}
		}

		internal IProtoSerializer Serializer => serializer ?? (serializer = BuildSerializer());

		public DataFormat DataFormat
		{
			get
			{
				return dataFormat;
			}
			set
			{
				if (value != dataFormat)
				{
					ThrowIfFrozen();
					dataFormat = value;
				}
			}
		}

		public bool IsStrict
		{
			get
			{
				return HasFlag(1);
			}
			set
			{
				SetFlag(1, value, throwIfFrozen: true);
			}
		}

		public bool IsPacked
		{
			get
			{
				return HasFlag(2);
			}
			set
			{
				SetFlag(2, value, throwIfFrozen: true);
			}
		}

		public bool OverwriteList
		{
			get
			{
				return HasFlag(8);
			}
			set
			{
				SetFlag(8, value, throwIfFrozen: true);
			}
		}

		public bool IsRequired
		{
			get
			{
				return HasFlag(4);
			}
			set
			{
				SetFlag(4, value, throwIfFrozen: true);
			}
		}

		public bool AsReference
		{
			get
			{
				return HasFlag(32);
			}
			set
			{
				SetFlag(32, value, throwIfFrozen: true);
			}
		}

		public bool DynamicType
		{
			get
			{
				return HasFlag(128);
			}
			set
			{
				SetFlag(128, value, throwIfFrozen: true);
			}
		}

		public bool IsMap
		{
			get
			{
				return HasFlag(64);
			}
			set
			{
				SetFlag(64, value, throwIfFrozen: true);
			}
		}

		public DataFormat MapKeyFormat
		{
			get
			{
				return mapKeyFormat;
			}
			set
			{
				if (mapKeyFormat != value)
				{
					ThrowIfFrozen();
					mapKeyFormat = value;
				}
			}
		}

		public DataFormat MapValueFormat
		{
			get
			{
				return mapValueFormat;
			}
			set
			{
				if (mapValueFormat != value)
				{
					ThrowIfFrozen();
					mapValueFormat = value;
				}
			}
		}

		public string Name
		{
			get
			{
				if (!string.IsNullOrEmpty(name))
				{
					return name;
				}
				return originalMember.Name;
			}
			set
			{
				SetName(value);
			}
		}

		public bool SupportNull
		{
			get
			{
				return HasFlag(16);
			}
			set
			{
				SetFlag(16, value, throwIfFrozen: true);
			}
		}

		public ValueMember(RuntimeTypeModel model, Type parentType, int fieldNumber, MemberInfo member, Type memberType, Type itemType, Type defaultType, DataFormat dataFormat, object defaultValue)
			: this(model, fieldNumber, memberType, itemType, defaultType, dataFormat)
		{
			if (parentType == null)
			{
				throw new ArgumentNullException("parentType");
			}
			if (fieldNumber < 1 && !Helpers.IsEnum(parentType))
			{
				throw new ArgumentOutOfRangeException("fieldNumber");
			}
			originalMember = member ?? throw new ArgumentNullException("member");
			this.parentType = parentType;
			if (fieldNumber < 1 && !Helpers.IsEnum(parentType))
			{
				throw new ArgumentOutOfRangeException("fieldNumber");
			}
			if (defaultValue != null && model.MapType(defaultValue.GetType()) != memberType)
			{
				defaultValue = ParseDefaultValue(memberType, defaultValue);
			}
			this.defaultValue = defaultValue;
			MetaType metaType = model.FindWithoutAdd(memberType);
			if (metaType != null)
			{
				AsReference = metaType.AsReferenceDefault;
			}
			else
			{
				AsReference = MetaType.GetAsReferenceDefault(model, memberType);
			}
		}

		internal ValueMember(RuntimeTypeModel model, int fieldNumber, Type memberType, Type itemType, Type defaultType, DataFormat dataFormat)
		{
			_fieldNumber = fieldNumber;
			this.memberType = memberType ?? throw new ArgumentNullException("memberType");
			this.itemType = itemType;
			this.defaultType = defaultType;
			this.model = model ?? throw new ArgumentNullException("model");
			this.dataFormat = dataFormat;
		}

		internal object GetRawEnumValue()
		{
			return ((FieldInfo)originalMember).GetRawConstantValue();
		}

		private static object ParseDefaultValue(Type type, object value)
		{
			Type underlyingType = Helpers.GetUnderlyingType(type);
			if (underlyingType != null)
			{
				type = underlyingType;
			}
			if (value is string text)
			{
				if (Helpers.IsEnum(type))
				{
					return Helpers.ParseEnum(type, text);
				}
				switch (Helpers.GetTypeCode(type))
				{
				case ProtoTypeCode.Boolean:
					return bool.Parse(text);
				case ProtoTypeCode.Byte:
					return byte.Parse(text, NumberStyles.Integer, CultureInfo.InvariantCulture);
				case ProtoTypeCode.Char:
					if (text.Length == 1)
					{
						return text[0];
					}
					throw new FormatException("Single character expected: \"" + text + "\"");
				case ProtoTypeCode.DateTime:
					return DateTime.Parse(text, CultureInfo.InvariantCulture);
				case ProtoTypeCode.Decimal:
					return decimal.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture);
				case ProtoTypeCode.Double:
					return double.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture);
				case ProtoTypeCode.Int16:
					return short.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture);
				case ProtoTypeCode.Int32:
					return int.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture);
				case ProtoTypeCode.Int64:
					return long.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture);
				case ProtoTypeCode.SByte:
					return sbyte.Parse(text, NumberStyles.Integer, CultureInfo.InvariantCulture);
				case ProtoTypeCode.Single:
					return float.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture);
				case ProtoTypeCode.String:
					return text;
				case ProtoTypeCode.UInt16:
					return ushort.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture);
				case ProtoTypeCode.UInt32:
					return uint.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture);
				case ProtoTypeCode.UInt64:
					return ulong.Parse(text, NumberStyles.Any, CultureInfo.InvariantCulture);
				case ProtoTypeCode.TimeSpan:
					return TimeSpan.Parse(text);
				case ProtoTypeCode.Uri:
					return text;
				case ProtoTypeCode.Guid:
					return new Guid(text);
				}
			}
			if (Helpers.IsEnum(type))
			{
				return Enum.ToObject(type, value);
			}
			return Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
		}

		public void SetSpecified(MethodInfo getSpecified, MethodInfo setSpecified)
		{
			if (this.getSpecified != getSpecified || this.setSpecified != setSpecified)
			{
				if (getSpecified != null && (getSpecified.ReturnType != model.MapType(typeof(bool)) || getSpecified.IsStatic || getSpecified.GetParameters().Length != 0))
				{
					throw new ArgumentException("Invalid pattern for checking member-specified", "getSpecified");
				}
				ParameterInfo[] parameters;
				if (setSpecified != null && (setSpecified.ReturnType != model.MapType(typeof(void)) || setSpecified.IsStatic || (parameters = setSpecified.GetParameters()).Length != 1 || parameters[0].ParameterType != model.MapType(typeof(bool))))
				{
					throw new ArgumentException("Invalid pattern for setting member-specified", "setSpecified");
				}
				ThrowIfFrozen();
				this.getSpecified = getSpecified;
				this.setSpecified = setSpecified;
			}
		}

		private void ThrowIfFrozen()
		{
			if (serializer != null)
			{
				throw new InvalidOperationException("The type cannot be changed once a serializer has been generated");
			}
		}

		internal bool ResolveMapTypes(out Type dictionaryType, out Type keyType, out Type valueType)
		{
			dictionaryType = (keyType = (valueType = null));
			try
			{
				Type type = memberType;
				if (ImmutableCollectionDecorator.IdentifyImmutable(model, MemberType, out var _, out var _, out var _, out var _, out var _, out var _))
				{
					return false;
				}
				if (type.IsInterface && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<, >))
				{
					Type[] genericArguments = memberType.GetGenericArguments();
					if (IsValidMapKeyType(genericArguments[0]))
					{
						keyType = genericArguments[0];
						valueType = genericArguments[1];
						dictionaryType = memberType;
					}
					return false;
				}
				Type[] interfaces = memberType.GetInterfaces();
				foreach (Type type2 in interfaces)
				{
					type = type2;
					if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<, >))
					{
						if (dictionaryType != null)
						{
							throw new InvalidOperationException("Multiple dictionary interfaces implemented by type: " + memberType.FullName);
						}
						Type[] genericArguments2 = type2.GetGenericArguments();
						if (IsValidMapKeyType(genericArguments2[0]))
						{
							keyType = genericArguments2[0];
							valueType = genericArguments2[1];
							dictionaryType = memberType;
						}
					}
				}
				if (dictionaryType == null)
				{
					return false;
				}
				Type type3 = null;
				Type type4 = null;
				model.ResolveListTypes(valueType, ref type3, ref type4);
				if (type3 != null)
				{
					return false;
				}
				return dictionaryType != null;
			}
			catch
			{
				return false;
			}
		}

		private static bool IsValidMapKeyType(Type type)
		{
			if (type == null || Helpers.IsEnum(type))
			{
				return false;
			}
			ProtoTypeCode typeCode = Helpers.GetTypeCode(type);
			if ((uint)(typeCode - 3) <= 9u || typeCode == ProtoTypeCode.String)
			{
				return true;
			}
			return false;
		}

		private IProtoSerializer BuildSerializer()
		{
			int opaqueToken = 0;
			try
			{
				model.TakeLock(ref opaqueToken);
				MemberInfo memberInfo = backingMember ?? originalMember;
				IProtoSerializer protoSerializer3;
				if (IsMap)
				{
					ResolveMapTypes(out var dictionaryType, out var keyType, out var valueType);
					if (dictionaryType == null)
					{
						throw new InvalidOperationException("Unable to resolve map type for type: " + memberType.FullName);
					}
					Type type = defaultType;
					if (type == null && Helpers.IsClass(memberType))
					{
						type = memberType;
					}
					WireType defaultWireType;
					IProtoSerializer protoSerializer = TryGetCoreSerializer(model, MapKeyFormat, keyType, out defaultWireType, asReference: false, dynamicType: false, overwriteList: false, allowComplexTypes: false);
					if (!AsReference)
					{
						AsReference = MetaType.GetAsReferenceDefault(model, valueType);
					}
					WireType defaultWireType2;
					IProtoSerializer protoSerializer2 = TryGetCoreSerializer(model, MapValueFormat, valueType, out defaultWireType2, AsReference, DynamicType, overwriteList: false, allowComplexTypes: true);
					ConstructorInfo[] constructors = typeof(MapDecorator<, , >).MakeGenericType(dictionaryType, keyType, valueType).GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (constructors.Length != 1)
					{
						throw new InvalidOperationException("Unable to resolve MapDecorator constructor");
					}
					protoSerializer3 = (IProtoSerializer)constructors[0].Invoke(new object[9]
					{
						model,
						type,
						protoSerializer,
						protoSerializer2,
						_fieldNumber,
						(DataFormat == DataFormat.Group) ? WireType.StartGroup : WireType.String,
						defaultWireType,
						defaultWireType2,
						OverwriteList
					});
				}
				else
				{
					Type type2 = itemType ?? memberType;
					protoSerializer3 = TryGetCoreSerializer(model, dataFormat, type2, out var defaultWireType3, AsReference, DynamicType, OverwriteList, allowComplexTypes: true);
					if (protoSerializer3 == null)
					{
						throw new InvalidOperationException("No serializer defined for type: " + type2.FullName);
					}
					if (itemType != null && SupportNull)
					{
						if (IsPacked)
						{
							throw new NotSupportedException("Packed encodings cannot support null values");
						}
						protoSerializer3 = new TagDecorator(1, defaultWireType3, IsStrict, protoSerializer3);
						protoSerializer3 = new NullDecorator(model, protoSerializer3);
						protoSerializer3 = new TagDecorator(_fieldNumber, WireType.StartGroup, strict: false, protoSerializer3);
					}
					else
					{
						protoSerializer3 = new TagDecorator(_fieldNumber, defaultWireType3, IsStrict, protoSerializer3);
					}
					if (itemType != null)
					{
						Type type3 = (SupportNull ? itemType : (Helpers.GetUnderlyingType(itemType) ?? itemType));
						protoSerializer3 = ((!memberType.IsArray) ? ((ProtoDecoratorBase)ListDecorator.Create(model, memberType, defaultType, protoSerializer3, _fieldNumber, IsPacked, defaultWireType3, memberInfo != null && PropertyDecorator.CanWrite(model, memberInfo), OverwriteList, SupportNull)) : ((ProtoDecoratorBase)new ArrayDecorator(model, protoSerializer3, _fieldNumber, IsPacked, defaultWireType3, memberType, OverwriteList, SupportNull)));
					}
					else if (defaultValue != null && !IsRequired && getSpecified == null)
					{
						protoSerializer3 = new DefaultValueDecorator(model, defaultValue, protoSerializer3);
					}
					if (memberType == model.MapType(typeof(Uri)))
					{
						protoSerializer3 = new UriDecorator(model, protoSerializer3);
					}
				}
				if (memberInfo != null)
				{
					if (memberInfo is PropertyInfo property)
					{
						protoSerializer3 = new PropertyDecorator(model, parentType, property, protoSerializer3);
					}
					else
					{
						if (!(memberInfo is FieldInfo field))
						{
							throw new InvalidOperationException();
						}
						protoSerializer3 = new FieldDecorator(parentType, field, protoSerializer3);
					}
					if (getSpecified != null || setSpecified != null)
					{
						protoSerializer3 = new MemberSpecifiedDecorator(getSpecified, setSpecified, protoSerializer3);
					}
				}
				return protoSerializer3;
			}
			finally
			{
				model.ReleaseLock(opaqueToken);
			}
		}

		private static WireType GetIntWireType(DataFormat format, int width)
		{
			switch (format)
			{
			case DataFormat.ZigZag:
				return WireType.SignedVariant;
			case DataFormat.FixedSize:
				if (width != 32)
				{
					return WireType.Fixed64;
				}
				return WireType.Fixed32;
			case DataFormat.Default:
			case DataFormat.TwosComplement:
				return WireType.Variant;
			default:
				throw new InvalidOperationException();
			}
		}

		private static WireType GetDateTimeWireType(DataFormat format)
		{
			switch (format)
			{
			case DataFormat.Group:
				return WireType.StartGroup;
			case DataFormat.FixedSize:
				return WireType.Fixed64;
			case DataFormat.Default:
			case DataFormat.WellKnown:
				return WireType.String;
			default:
				throw new InvalidOperationException();
			}
		}

		internal static IProtoSerializer TryGetCoreSerializer(RuntimeTypeModel model, DataFormat dataFormat, Type type, out WireType defaultWireType, bool asReference, bool dynamicType, bool overwriteList, bool allowComplexTypes)
		{
			Type underlyingType = Helpers.GetUnderlyingType(type);
			if (underlyingType != null)
			{
				type = underlyingType;
			}
			if (Helpers.IsEnum(type))
			{
				if (allowComplexTypes && model != null)
				{
					defaultWireType = WireType.Variant;
					return new EnumSerializer(type, model.GetEnumMap(type));
				}
				defaultWireType = WireType.None;
				return null;
			}
			switch (Helpers.GetTypeCode(type))
			{
			case ProtoTypeCode.Int32:
				defaultWireType = GetIntWireType(dataFormat, 32);
				return new Int32Serializer(model);
			case ProtoTypeCode.UInt32:
				defaultWireType = GetIntWireType(dataFormat, 32);
				return new UInt32Serializer(model);
			case ProtoTypeCode.Int64:
				defaultWireType = GetIntWireType(dataFormat, 64);
				return new Int64Serializer(model);
			case ProtoTypeCode.UInt64:
				defaultWireType = GetIntWireType(dataFormat, 64);
				return new UInt64Serializer(model);
			case ProtoTypeCode.String:
				defaultWireType = WireType.String;
				if (asReference)
				{
					return new NetObjectSerializer(model, model.MapType(typeof(string)), 0, BclHelpers.NetObjectOptions.AsReference);
				}
				return new StringSerializer(model);
			case ProtoTypeCode.Single:
				defaultWireType = WireType.Fixed32;
				return new SingleSerializer(model);
			case ProtoTypeCode.Double:
				defaultWireType = WireType.Fixed64;
				return new DoubleSerializer(model);
			case ProtoTypeCode.Boolean:
				defaultWireType = WireType.Variant;
				return new BooleanSerializer(model);
			case ProtoTypeCode.DateTime:
				defaultWireType = GetDateTimeWireType(dataFormat);
				return new DateTimeSerializer(dataFormat, model);
			case ProtoTypeCode.Decimal:
				defaultWireType = WireType.String;
				return new DecimalSerializer(model);
			case ProtoTypeCode.Byte:
				defaultWireType = GetIntWireType(dataFormat, 32);
				return new ByteSerializer(model);
			case ProtoTypeCode.SByte:
				defaultWireType = GetIntWireType(dataFormat, 32);
				return new SByteSerializer(model);
			case ProtoTypeCode.Char:
				defaultWireType = WireType.Variant;
				return new CharSerializer(model);
			case ProtoTypeCode.Int16:
				defaultWireType = GetIntWireType(dataFormat, 32);
				return new Int16Serializer(model);
			case ProtoTypeCode.UInt16:
				defaultWireType = GetIntWireType(dataFormat, 32);
				return new UInt16Serializer(model);
			case ProtoTypeCode.TimeSpan:
				defaultWireType = GetDateTimeWireType(dataFormat);
				return new TimeSpanSerializer(dataFormat, model);
			case ProtoTypeCode.Guid:
				defaultWireType = ((dataFormat == DataFormat.Group) ? WireType.StartGroup : WireType.String);
				return new GuidSerializer(model);
			case ProtoTypeCode.Uri:
				defaultWireType = WireType.String;
				return new StringSerializer(model);
			case ProtoTypeCode.ByteArray:
				defaultWireType = WireType.String;
				return new BlobSerializer(model, overwriteList);
			case ProtoTypeCode.Type:
				defaultWireType = WireType.String;
				return new SystemTypeSerializer(model);
			default:
			{
				IProtoSerializer protoSerializer = (model.AllowParseableTypes ? ParseableSerializer.TryCreate(type, model) : null);
				if (protoSerializer != null)
				{
					defaultWireType = WireType.String;
					return protoSerializer;
				}
				if (allowComplexTypes && model != null)
				{
					int key = model.GetKey(type, demand: false, getBaseKey: true);
					MetaType metaType = null;
					if (key >= 0)
					{
						metaType = model[type];
						if (dataFormat == DataFormat.Default && metaType.IsGroup)
						{
							dataFormat = DataFormat.Group;
						}
					}
					if (asReference || dynamicType)
					{
						BclHelpers.NetObjectOptions netObjectOptions = BclHelpers.NetObjectOptions.None;
						if (asReference)
						{
							netObjectOptions |= BclHelpers.NetObjectOptions.AsReference;
						}
						if (dynamicType)
						{
							netObjectOptions |= BclHelpers.NetObjectOptions.DynamicType;
						}
						if (metaType != null)
						{
							if (asReference && Helpers.IsValueType(type))
							{
								string text = "AsReference cannot be used with value-types";
								text = ((!(type.Name == "KeyValuePair`2")) ? (text + ": " + type.FullName) : (text + "; please see https://stackoverflow.com/q/14436606/23354"));
								throw new InvalidOperationException(text);
							}
							if (asReference && metaType.IsAutoTuple)
							{
								netObjectOptions |= BclHelpers.NetObjectOptions.LateSet;
							}
							if (metaType.UseConstructor)
							{
								netObjectOptions |= BclHelpers.NetObjectOptions.UseConstructor;
							}
						}
						defaultWireType = ((dataFormat == DataFormat.Group) ? WireType.StartGroup : WireType.String);
						return new NetObjectSerializer(model, type, key, netObjectOptions);
					}
					if (key >= 0)
					{
						defaultWireType = ((dataFormat == DataFormat.Group) ? WireType.StartGroup : WireType.String);
						return new SubItemSerializer(type, key, metaType, recursionCheck: true);
					}
				}
				defaultWireType = WireType.None;
				return null;
			}
			}
		}

		internal void SetName(string name)
		{
			if (name != this.name)
			{
				ThrowIfFrozen();
				this.name = name;
			}
		}

		private bool HasFlag(byte flag)
		{
			return (flags & flag) == flag;
		}

		private void SetFlag(byte flag, bool value, bool throwIfFrozen)
		{
			if (throwIfFrozen && HasFlag(flag) != value)
			{
				ThrowIfFrozen();
			}
			if (value)
			{
				flags |= flag;
			}
			else
			{
				flags = (byte)(flags & ~flag);
			}
		}

		internal string GetSchemaTypeName(bool applyNetObjectProxy, ref RuntimeTypeModel.CommonImports imports)
		{
			Type type = ItemType;
			if (type == null)
			{
				type = MemberType;
			}
			return model.GetSchemaTypeName(type, DataFormat, applyNetObjectProxy && AsReference, applyNetObjectProxy && DynamicType, ref imports);
		}
	}
}
namespace ProtoBuf.Compiler
{
	internal readonly struct CodeLabel(Label value, int index)
	{
		public readonly Label Value = value;

		public readonly int Index = index;
	}
	internal sealed class CompilerContext
	{
		private sealed class UsingBlock : IDisposable
		{
			private Local local;

			private CompilerContext ctx;

			private CodeLabel label;

			public UsingBlock(CompilerContext ctx, Local local)
			{
				if (ctx == null)
				{
					throw new ArgumentNullException("ctx");
				}
				if (local == null)
				{
					throw new ArgumentNullException("local");
				}
				Type type = local.Type;
				if ((!Helpers.IsValueType(type) && !Helpers.IsSealed(type)) || ctx.MapType(typeof(IDisposable)).IsAssignableFrom(type))
				{
					this.local = local;
					this.ctx = ctx;
					label = ctx.BeginTry();
				}
			}

			public void Dispose()
			{
				if (this.local == null || ctx == null)
				{
					return;
				}
				ctx.EndTry(label, @short: false);
				ctx.BeginFinally();
				Type type = ctx.MapType(typeof(IDisposable));
				MethodInfo method = type.GetMethod("Dispose");
				Type type2 = this.local.Type;
				if (Helpers.IsValueType(type2))
				{
					ctx.LoadAddress(this.local, type2);
					if (ctx.MetadataVersion == ILVersion.Net1)
					{
						ctx.LoadValue(this.local);
						ctx.CastToObject(type2);
					}
					else
					{
						ctx.Constrain(type2);
					}
					ctx.EmitCall(method);
				}
				else
				{
					CodeLabel codeLabel = ctx.DefineLabel();
					if (type.IsAssignableFrom(type2))
					{
						ctx.LoadValue(this.local);
						ctx.BranchIfFalse(codeLabel, @short: true);
						ctx.LoadAddress(this.local, type2);
					}
					else
					{
						using Local local = new Local(ctx, type);
						ctx.LoadValue(this.local);
						ctx.TryCast(type);
						ctx.CopyValue();
						ctx.StoreValue(local);
						ctx.BranchIfFalse(codeLabel, @short: true);
						ctx.LoadAddress(local, type);
					}
					ctx.EmitCall(method);
					ctx.MarkLabel(codeLabel);
				}
				ctx.EndFinally();
				this.local = null;
				ctx = null;
				label = default(CodeLabel);
			}
		}

		public enum ILVersion
		{
			Net1,
			Net2
		}

		private readonly DynamicMethod method;

		private static int next;

		private readonly bool isStatic;

		private readonly RuntimeTypeModel.SerializerPair[] methodPairs;

		private readonly bool isWriter;

		private readonly bool nonPublic;

		private readonly Local inputValue;

		private readonly string assemblyName;

		private readonly ILGenerator il;

		private MutableList locals = new MutableList();

		private int nextLabel;

		private BasicList knownTrustedAssemblies;

		private BasicList knownUntrustedAssemblies;

		private readonly TypeModel model;

		private readonly ILVersion metadataVersion;

		public TypeModel Model => model;

		internal bool NonPublic => nonPublic;

		public Local InputValue => inputValue;

		public ILVersion MetadataVersion => metadataVersion;

		internal CodeLabel DefineLabel()
		{
			CodeLabel result = new CodeLabel(il.DefineLabel(), nextLabel++);
			return result;
		}

		[Conditional("DEBUG_COMPILE")]
		private void TraceCompile(string value)
		{
		}

		internal void MarkLabel(CodeLabel label)
		{
			il.MarkLabel(label.Value);
		}

		public static ProtoSerializer BuildSerializer(IProtoSerializer head, TypeModel model)
		{
			Type expectedType = head.ExpectedType;
			try
			{
				CompilerContext compilerContext = new CompilerContext(expectedType, isWriter: true, isStatic: true, model, typeof(object));
				compilerContext.LoadValue(compilerContext.InputValue);
				compilerContext.CastFromObject(expectedType);
				compilerContext.WriteNullCheckedTail(expectedType, head, null);
				compilerContext.Emit(OpCodes.Ret);
				return (ProtoSerializer)compilerContext.method.CreateDelegate(typeof(ProtoSerializer));
			}
			catch (Exception innerException)
			{
				string text = expectedType.FullName;
				if (string.IsNullOrEmpty(text))
				{
					text = expectedType.Name;
				}
				throw new InvalidOperationException("It was not possible to prepare a serializer for: " + text, innerException);
			}
		}

		public static ProtoDeserializer BuildDeserializer(IProtoSerializer head, TypeModel model)
		{
			Type expectedType = head.ExpectedType;
			CompilerContext compilerContext = new CompilerContext(expectedType, isWriter: false, isStatic: true, model, typeof(object));
			using (Local local = new Local(compilerContext, expectedType))
			{
				if (!Helpers.IsValueType(expectedType))
				{
					compilerContext.LoadValue(compilerContext.InputValue);
					compilerContext.CastFromObject(expectedType);
					compilerContext.StoreValue(local);
				}
				else
				{
					compilerContext.LoadValue(compilerContext.InputValue);
					CodeLabel label = compilerContext.DefineLabel();
					CodeLabel label2 = compilerContext.DefineLabel();
					compilerContext.BranchIfTrue(label, @short: true);
					compilerContext.LoadAddress(local, expectedType);
					compilerContext.EmitCtor(expectedType);
					compilerContext.Branch(label2, @short: true);
					compilerContext.MarkLabel(label);
					compilerContext.LoadValue(compilerContext.InputValue);
					compilerContext.CastFromObject(expectedType);
					compilerContext.StoreValue(local);
					compilerContext.MarkLabel(label2);
				}
				head.EmitRead(compilerContext, local);
				if (head.ReturnsValue)
				{
					compilerContext.StoreValue(local);
				}
				compilerContext.LoadValue(local);
				compilerContext.CastToObject(expectedType);
			}
			compilerContext.Emit(OpCodes.Ret);
			return (ProtoDeserializer)compilerContext.method.CreateDelegate(typeof(ProtoDeserializer));
		}

		internal void Return()
		{
			Emit(OpCodes.Ret);
		}

		private static bool IsObject(Type type)
		{
			return type == typeof(object);
		}

		internal void CastToObject(Type type)
		{
			if (!IsObject(type))
			{
				if (Helpers.IsValueType(type))
				{
					il.Emit(OpCodes.Box, type);
				}
				else
				{
					il.Emit(OpCodes.Castclass, MapType(typeof(object)));
				}
			}
		}

		internal void CastFromObject(Type type)
		{
			if (IsObject(type))
			{
				return;
			}
			if (Helpers.IsValueType(type))
			{
				if (MetadataVersion == ILVersion.Net1)
				{
					il.Emit(OpCodes.Unbox, type);
					il.Emit(OpCodes.Ldobj, type);
				}
				else
				{
					il.Emit(OpCodes.Unbox_Any, type);
				}
			}
			else
			{
				il.Emit(OpCodes.Castclass, type);
			}
		}

		internal MethodBuilder GetDedicatedMethod(int metaKey, bool read)
		{
			if (methodPairs == null)
			{
				return null;
			}
			for (int i = 0; i < methodPairs.Length; i++)
			{
				if (methodPairs[i].MetaKey == metaKey)
				{
					if (!read)
					{
						return methodPairs[i].Serialize;
					}
					return methodPairs[i].Deserialize;
				}
			}
			throw new ArgumentException("Meta-key not found", "metaKey");
		}

		internal int MapMetaKeyToCompiledKey(int metaKey)
		{
			if (metaKey < 0 || methodPairs == null)
			{
				return metaKey;
			}
			for (int i = 0; i < methodPairs.Length; i++)
			{
				if (methodPairs[i].MetaKey == metaKey)
				{
					return i;
				}
			}
			throw new ArgumentException("Key could not be mapped: " + metaKey, "metaKey");
		}

		internal CompilerContext(ILGenerator il, bool isStatic, bool isWriter, RuntimeTypeModel.SerializerPair[] methodPairs, TypeModel model, ILVersion metadataVersion, string assemblyName, Type inputType, string traceName)
		{
			if (string.IsNullOrEmpty(assemblyName))
			{
				throw new ArgumentNullException("assemblyName");
			}
			this.assemblyName = assemblyName;
			this.isStatic = isStatic;
			this.methodPairs = methodPairs ?? throw new ArgumentNullException("methodPairs");
			this.il = il ?? throw new ArgumentNullException("il");
			this.isWriter = isWriter;
			this.model = model ?? throw new ArgumentNullException("model");
			this.metadataVersion = metadataVersion;
			if (inputType != null)
			{
				inputValue = new Local(null, inputType);
			}
		}

		private CompilerContext(Type associatedType, bool isWriter, bool isStatic, TypeModel model, Type inputType)
		{
			metadataVersion = ILVersion.Net2;
			this.isStatic = isStatic;
			this.isWriter = isWriter;
			this.model = model ?? throw new ArgumentNullException("model");
			nonPublic = true;
			Type typeFromHandle;
			Type[] parameterTypes;
			if (isWriter)
			{
				typeFromHandle = typeof(void);
				parameterTypes = new Type[2]
				{
					typeof(object),
					typeof(ProtoWriter)
				};
			}
			else
			{
				typeFromHandle = typeof(object);
				parameterTypes = new Type[2]
				{
					typeof(object),
					typeof(ProtoReader)
				};
			}
			method = new DynamicMethod("proto_" + Interlocked.Increment(ref next), typeFromHandle, parameterTypes, associatedType.IsInterface ? typeof(object) : associatedType, skipVisibility: true);
			il = method.GetILGenerator();
			if (inputType != null)
			{
				inputValue = new Local(null, inputType);
			}
		}

		private void Emit(OpCode opcode)
		{
			il.Emit(opcode);
		}

		public void LoadValue(string value)
		{
			if (value == null)
			{
				LoadNullRef();
			}
			else
			{
				il.Emit(OpCodes.Ldstr, value);
			}
		}

		public void LoadValue(float value)
		{
			il.Emit(OpCodes.Ldc_R4, value);
		}

		public void LoadValue(double value)
		{
			il.Emit(OpCodes.Ldc_R8, value);
		}

		public void LoadValue(long value)
		{
			il.Emit(OpCodes.Ldc_I8, value);
		}

		public void LoadValue(int value)
		{
			switch (value)
			{
			case 0:
				Emit(OpCodes.Ldc_I4_0);
				return;
			case 1:
				Emit(OpCodes.Ldc_I4_1);
				return;
			case 2:
				Emit(OpCodes.Ldc_I4_2);
				return;
			case 3:
				Emit(OpCodes.Ldc_I4_3);
				return;
			case 4:
				Emit(OpCodes.Ldc_I4_4);
				return;
			case 5:
				Emit(OpCodes.Ldc_I4_5);
				return;
			case 6:
				Emit(OpCodes.Ldc_I4_6);
				return;
			case 7:
				Emit(OpCodes.Ldc_I4_7);
				return;
			case 8:
				Emit(OpCodes.Ldc_I4_8);
				return;
			case -1:
				Emit(OpCodes.Ldc_I4_M1);
				return;
			}
			if (value >= -128 && value <= 127)
			{
				il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
			}
			else
			{
				il.Emit(OpCodes.Ldc_I4, value);
			}
		}

		internal LocalBuilder GetFromPool(Type type)
		{
			int count = locals.Count;
			for (int i = 0; i < count; i++)
			{
				LocalBuilder localBuilder = (LocalBuilder)locals[i];
				if (localBuilder != null && localBuilder.LocalType == type)
				{
					locals[i] = null;
					return localBuilder;
				}
			}
			return il.DeclareLocal(type);
		}

		internal void ReleaseToPool(LocalBuilder value)
		{
			int count = locals.Count;
			for (int i = 0; i < count; i++)
			{
				if (locals[i] == null)
				{
					locals[i] = value;
					return;
				}
			}
			locals.Add(value);
		}

		public void LoadReaderWriter()
		{
			Emit(isStatic ? OpCodes.Ldarg_1 : OpCodes.Ldarg_2);
		}

		public void StoreValue(Local local)
		{
			if (local == InputValue)
			{
				byte arg = ((!isStatic) ? ((byte)1) : ((byte)0));
				il.Emit(OpCodes.Starg_S, arg);
				return;
			}
			switch (local.Value.LocalIndex)
			{
			case 0:
				Emit(OpCodes.Stloc_0);
				break;
			case 1:
				Emit(OpCodes.Stloc_1);
				break;
			case 2:
				Emit(OpCodes.Stloc_2);
				break;
			case 3:
				Emit(OpCodes.Stloc_3);
				break;
			default:
			{
				OpCode opcode = (UseShortForm(local) ? OpCodes.Stloc_S : OpCodes.Stloc);
				il.Emit(opcode, local.Value);
				break;
			}
			}
		}

		public void LoadValue(Local local)
		{
			if (local == null)
			{
				return;
			}
			if (local == InputValue)
			{
				Emit(isStatic ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1);
				return;
			}
			switch (local.Value.LocalIndex)
			{
			case 0:
				Emit(OpCodes.Ldloc_0);
				break;
			case 1:
				Emit(OpCodes.Ldloc_1);
				break;
			case 2:
				Emit(OpCodes.Ldloc_2);
				break;
			case 3:
				Emit(OpCodes.Ldloc_3);
				break;
			default:
			{
				OpCode opcode = (UseShortForm(local) ? OpCodes.Ldloc_S : OpCodes.Ldloc);
				il.Emit(opcode, local.Value);
				break;
			}
			}
		}

		public Local GetLocalWithValue(Type type, Local fromValue)
		{
			if (fromValue != null)
			{
				if (fromValue.Type == type)
				{
					return fromValue.AsCopy();
				}
				LoadValue(fromValue);
				if (!Helpers.IsValueType(type) && (fromValue.Type == null || !type.IsAssignableFrom(fromValue.Type)))
				{
					Cast(type);
				}
			}
			Local local = new Local(this, type);
			StoreValue(local);
			return local;
		}

		internal void EmitBasicRead(string methodName, Type expectedType)
		{
			MethodInfo methodInfo = MapType(typeof(ProtoReader)).GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (methodInfo == null || methodInfo.ReturnType != expectedType || methodInfo.GetParameters().Length != 0)
			{
				throw new ArgumentException("methodName");
			}
			LoadReaderWriter();
			EmitCall(methodInfo);
		}

		internal void EmitBasicRead(Type helperType, string methodName, Type expectedType)
		{
			MethodInfo methodInfo = helperType.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (methodInfo == null || methodInfo.ReturnType != expectedType || methodInfo.GetParameters().Length != 1)
			{
				throw new ArgumentException("methodName");
			}
			LoadReaderWriter();
			EmitCall(methodInfo);
		}

		internal void EmitBasicWrite(string methodName, Local fromValue)
		{
			if (string.IsNullOrEmpty(methodName))
			{
				throw new ArgumentNullException("methodName");
			}
			LoadValue(fromValue);
			LoadReaderWriter();
			EmitCall(GetWriterMethod(methodName));
		}

		private MethodInfo GetWriterMethod(string methodName)
		{
			Type type = MapType(typeof(ProtoWriter));
			MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo[] array = methods;
			foreach (MethodInfo methodInfo in array)
			{
				if (!(methodInfo.Name != methodName))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 2 && parameters[1].ParameterType == type)
					{
						return methodInfo;
					}
				}
			}
			throw new ArgumentException("No suitable method found for: " + methodName, "methodName");
		}

		internal void EmitWrite(Type helperType, string methodName, Local valueFrom)
		{
			if (string.IsNullOrEmpty(methodName))
			{
				throw new ArgumentNullException("methodName");
			}
			MethodInfo methodInfo = helperType.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (methodInfo == null || methodInfo.ReturnType != MapType(typeof(void)))
			{
				throw new ArgumentException("methodName");
			}
			LoadValue(valueFrom);
			LoadReaderWriter();
			EmitCall(methodInfo);
		}

		public void EmitCall(MethodInfo method)
		{
			EmitCall(method, null);
		}

		public void EmitCall(MethodInfo method, Type targetType)
		{
			MemberInfo member = method;
			CheckAccessibility(ref member);
			OpCode opcode;
			if (method.IsStatic || Helpers.IsValueType(method.DeclaringType))
			{
				opcode = OpCodes.Call;
			}
			else
			{
				opcode = OpCodes.Callvirt;
				if (targetType != null && Helpers.IsValueType(targetType) && !Helpers.IsValueType(method.DeclaringType))
				{
					Constrain(targetType);
				}
			}
			il.EmitCall(opcode, method, null);
		}

		public void LoadNullRef()
		{
			Emit(OpCodes.Ldnull);
		}

		internal void WriteNullCheckedTail(Type type, IProtoSerializer tail, Local valueFrom)
		{
			if (Helpers.IsValueType(type))
			{
				Type underlyingType = Helpers.GetUnderlyingType(type);
				if (!(underlyingType == null))
				{
					using (Local local = GetLocalWithValue(type, valueFrom))
					{
						LoadAddress(local, type);
						LoadValue(type.GetProperty("HasValue"));
						CodeLabel label = DefineLabel();
						BranchIfFalse(label, @short: false);
						LoadAddress(local, type);
						EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
						tail.EmitWrite(this, null);
						MarkLabel(label);
						return;
					}
				}
				tail.EmitWrite(this, valueFrom);
			}
			else
			{
				LoadValue(valueFrom);
				CopyValue();
				CodeLabel label2 = DefineLabel();
				CodeLabel label3 = DefineLabel();
				BranchIfTrue(label2, @short: true);
				DiscardValue();
				Branch(label3, @short: false);
				MarkLabel(label2);
				tail.EmitWrite(this, null);
				MarkLabel(label3);
			}
		}

		internal void ReadNullCheckedTail(Type type, IProtoSerializer tail, Local valueFrom)
		{
			Type underlyingType;
			if (Helpers.IsValueType(type) && (underlyingType = Helpers.GetUnderlyingType(type)) != null)
			{
				if (tail.RequiresOldValue)
				{
					using Local local = GetLocalWithValue(type, valueFrom);
					LoadAddress(local, type);
					EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
				}
				tail.EmitRead(this, null);
				if (tail.ReturnsValue)
				{
					EmitCtor(type, underlyingType);
				}
			}
			else
			{
				tail.EmitRead(this, valueFrom);
			}
		}

		public void EmitCtor(Type type)
		{
			EmitCtor(type, Helpers.EmptyTypes);
		}

		public void EmitCtor(ConstructorInfo ctor)
		{
			if (ctor == null)
			{
				throw new ArgumentNullException("ctor");
			}
			MemberInfo member = ctor;
			CheckAccessibility(ref member);
			il.Emit(OpCodes.Newobj, ctor);
		}

		public void InitLocal(Type type, Local target)
		{
			LoadAddress(target, type, evenIfClass: true);
			il.Emit(OpCodes.Initobj, type);
		}

		public void EmitCtor(Type type, params Type[] parameterTypes)
		{
			if (Helpers.IsValueType(type) && parameterTypes.Length == 0)
			{
				il.Emit(OpCodes.Initobj, type);
				return;
			}
			ConstructorInfo constructor = Helpers.GetConstructor(type, parameterTypes, nonPublic: true);
			if (constructor == null)
			{
				throw new InvalidOperationException("No suitable constructor found for " + type.FullName);
			}
			EmitCtor(constructor);
		}

		private bool InternalsVisible(Assembly assembly)
		{
			if (string.IsNullOrEmpty(assemblyName))
			{
				return false;
			}
			if (knownTrustedAssemblies != null && knownTrustedAssemblies.IndexOfReference(assembly) >= 0)
			{
				return true;
			}
			if (knownUntrustedAssemblies != null && knownUntrustedAssemblies.IndexOfReference(assembly) >= 0)
			{
				return false;
			}
			bool flag = false;
			Type type = MapType(typeof(InternalsVisibleToAttribute));
			if (type == null)
			{
				return false;
			}
			object[] customAttributes = assembly.GetCustomAttributes(type, inherit: false);
			for (int i = 0; i < customAttributes.Length; i++)
			{
				InternalsVisibleToAttribute internalsVisibleToAttribute = (InternalsVisibleToAttribute)customAttributes[i];
				if (internalsVisibleToAttribute.AssemblyName == assemblyName || internalsVisibleToAttribute.AssemblyName.StartsWith(assemblyName + ","))
				{
					flag = true;
					break;
				}
			}
			if (flag)
			{
				if (knownTrustedAssemblies == null)
				{
					knownTrustedAssemblies = new BasicList();
				}
				knownTrustedAssemblies.Add(assembly);
			}
			else
			{
				if (knownUntrustedAssemblies == null)
				{
					knownUntrustedAssemblies = new BasicList();
				}
				knownUntrustedAssemblies.Add(assembly);
			}
			return flag;
		}

		internal void CheckAccessibility(ref MemberInfo member)
		{
			if (member == null)
			{
				throw new ArgumentNullException("member");
			}
			if (NonPublic)
			{
				return;
			}
			if (member is FieldInfo && (member.Name.StartsWith("<") & member.Name.EndsWith(">k__BackingField")))
			{
				string name = member.Name.Substring(1, member.Name.Length - 17);
				PropertyInfo property = member.DeclaringType.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
				if (property != null)
				{
					member = property;
				}
			}
			MemberTypes memberType = member.MemberType;
			bool flag;
			switch (memberType)
			{
			case MemberTypes.TypeInfo:
			{
				Type type = (Type)member;
				flag = type.IsPublic || InternalsVisible(type.Assembly);
				break;
			}
			case MemberTypes.NestedType:
			{
				Type type = (Type)member;
				do
				{
					flag = type.IsNestedPublic || type.IsPublic || ((type.DeclaringType == null || type.IsNestedAssembly || type.IsNestedFamORAssem) && InternalsVisible(type.Assembly));
				}
				while (flag && (type = type.DeclaringType) != null);
				break;
			}
			case MemberTypes.Field:
			{
				FieldInfo fieldInfo = (FieldInfo)member;
				flag = fieldInfo.IsPublic || ((fieldInfo.IsAssembly || fieldInfo.IsFamilyOrAssembly) && InternalsVisible(fieldInfo.DeclaringType.Assembly));
				break;
			}
			case MemberTypes.Constructor:
			{
				ConstructorInfo constructorInfo = (ConstructorInfo)member;
				flag = constructorInfo.IsPublic || ((constructorInfo.IsAssembly || constructorInfo.IsFamilyOrAssembly) && InternalsVisible(constructorInfo.DeclaringType.Assembly));
				break;
			}
			case MemberTypes.Method:
			{
				MethodInfo methodInfo = (MethodInfo)member;
				flag = methodInfo.IsPublic || ((methodInfo.IsAssembly || methodInfo.IsFamilyOrAssembly) && InternalsVisible(methodInfo.DeclaringType.Assembly));
				if (!flag && (member is MethodBuilder || member.DeclaringType == MapType(typeof(TypeModel))))
				{
					flag = true;
				}
				break;
			}
			case MemberTypes.Property:
				flag = true;
				break;
			default:
				throw new NotSupportedException(memberType.ToString());
			}
			if (!flag)
			{
				if (memberType == MemberTypes.TypeInfo || memberType == MemberTypes.NestedType)
				{
					throw new InvalidOperationException("Non-public type cannot be used with full dll compilation: " + ((Type)member).FullName);
				}
				throw new InvalidOperationException("Non-public member cannot be used with full dll compilation: " + member.DeclaringType.FullName + "." + member.Name);
			}
		}

		public void LoadValue(FieldInfo field)
		{
			MemberInfo member = field;
			CheckAccessibility(ref member);
			if (member is PropertyInfo)
			{
				LoadValue((PropertyInfo)member);
				return;
			}
			OpCode opcode = (field.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld);
			il.Emit(opcode, field);
		}

		public void StoreValue(FieldInfo field)
		{
			MemberInfo member = field;
			CheckAccessibility(ref member);
			if (member is PropertyInfo)
			{
				StoreValue((PropertyInfo)member);
				return;
			}
			OpCode opcode = (field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld);
			il.Emit(opcode, field);
		}

		public void LoadValue(PropertyInfo property)
		{
			MemberInfo member = property;
			CheckAccessibility(ref member);
			EmitCall(Helpers.GetGetMethod(property, nonPublic: true, allowInternal: true));
		}

		public void StoreValue(PropertyInfo property)
		{
			MemberInfo member = property;
			CheckAccessibility(ref member);
			EmitCall(Helpers.GetSetMethod(property, nonPublic: true, allowInternal: true));
		}

		internal static void LoadValue(ILGenerator il, int value)
		{
			switch (value)
			{
			case 0:
				il.Emit(OpCodes.Ldc_I4_0);
				break;
			case 1:
				il.Emit(OpCodes.Ldc_I4_1);
				break;
			case 2:
				il.Emit(OpCodes.Ldc_I4_2);
				break;
			case 3:
				il.Emit(OpCodes.Ldc_I4_3);
				break;
			case 4:
				il.Emit(OpCodes.Ldc_I4_4);
				break;
			case 5:
				il.Emit(OpCodes.Ldc_I4_5);
				break;
			case 6:
				il.Emit(OpCodes.Ldc_I4_6);
				break;
			case 7:
				il.Emit(OpCodes.Ldc_I4_7);
				break;
			case 8:
				il.Emit(OpCodes.Ldc_I4_8);
				break;
			case -1:
				il.Emit(OpCodes.Ldc_I4_M1);
				break;
			default:
				il.Emit(OpCodes.Ldc_I4, value);
				break;
			}
		}

		private bool UseShortForm(Local local)
		{
			return local.Value.LocalIndex < 256;
		}

		internal void LoadAddress(Local local, Type type, bool evenIfClass = false)
		{
			if (evenIfClass || Helpers.IsValueType(type))
			{
				if (local == null)
				{
					throw new InvalidOperationException("Cannot load the address of the head of the stack");
				}
				if (local == InputValue)
				{
					il.Emit(OpCodes.Ldarga_S, (!isStatic) ? ((byte)1) : ((byte)0));
					return;
				}
				OpCode opcode = (UseShortForm(local) ? OpCodes.Ldloca_S : OpCodes.Ldloca);
				il.Emit(opcode, local.Value);
			}
			else
			{
				LoadValue(local);
			}
		}

		internal void Branch(CodeLabel label, bool @short)
		{
			OpCode opcode = (@short ? OpCodes.Br_S : OpCodes.Br);
			il.Emit(opcode, label.Value);
		}

		internal void BranchIfFalse(CodeLabel label, bool @short)
		{
			OpCode opcode = (@short ? OpCodes.Brfalse_S : OpCodes.Brfalse);
			il.Emit(opcode, label.Value);
		}

		internal void BranchIfTrue(CodeLabel label, bool @short)
		{
			OpCode opcode = (@short ? OpCodes.Brtrue_S : OpCodes.Brtrue);
			il.Emit(opcode, label.Value);
		}

		internal void BranchIfEqual(CodeLabel label, bool @short)
		{
			OpCode opcode = (@short ? OpCodes.Beq_S : OpCodes.Beq);
			il.Emit(opcode, label.Value);
		}

		internal void CopyValue()
		{
			Emit(OpCodes.Dup);
		}

		internal void BranchIfGreater(CodeLabel label, bool @short)
		{
			OpCode opcode = (@short ? OpCodes.Bgt_S : OpCodes.Bgt);
			il.Emit(opcode, label.Value);
		}

		internal void BranchIfLess(CodeLabel label, bool @short)
		{
			OpCode opcode = (@short ? OpCodes.Blt_S : OpCodes.Blt);
			il.Emit(opcode, label.Value);
		}

		internal void DiscardValue()
		{
			Emit(OpCodes.Pop);
		}

		public void Subtract()
		{
			Emit(OpCodes.Sub);
		}

		public void Switch(CodeLabel[] jumpTable)
		{
			if (jumpTable.Length <= 128)
			{
				Label[] array = new Label[jumpTable.Length];
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = jumpTable[i].Value;
				}
				il.Emit(OpCodes.Switch, array);
				return;
			}
			using Local local = GetLocalWithValue(MapType(typeof(int)), null);
			int num = jumpTable.Length;
			int num2 = 0;
			int num3 = num / 128;
			if (num % 128 != 0)
			{
				num3++;
			}
			Label[] array2 = new Label[num3];
			for (int j = 0; j < num3; j++)
			{
				array2[j] = il.DefineLabel();
			}
			CodeLabel label = DefineLabel();
			LoadValue(local);
			LoadValue(128);
			Emit(OpCodes.Div);
			il.Emit(OpCodes.Switch, array2);
			Branch(label, @short: false);
			Label[] array3 = new Label[128];
			for (int k = 0; k < num3; k++)
			{
				il.MarkLabel(array2[k]);
				int num4 = Math.Min(128, num);
				num -= num4;
				if (array3.Length != num4)
				{
					array3 = new Label[num4];
				}
				int num5 = num2;
				for (int l = 0; l < num4; l++)
				{
					array3[l] = jumpTable[num2++].Value;
				}
				LoadValue(local);
				if (num5 != 0)
				{
					LoadValue(num5);
					Emit(OpCodes.Sub);
				}
				il.Emit(OpCodes.Switch, array3);
				if (num != 0)
				{
					Branch(label, @short: false);
				}
			}
			MarkLabel(label);
		}

		internal void EndFinally()
		{
			il.EndExceptionBlock();
		}

		internal void BeginFinally()
		{
			il.BeginFinallyBlock();
		}

		internal void EndTry(CodeLabel label, bool @short)
		{
			OpCode opcode = (@short ? OpCodes.Leave_S : OpCodes.Leave);
			il.Emit(opcode, label.Value);
		}

		internal CodeLabel BeginTry()
		{
			CodeLabel result = new CodeLabel(il.BeginExceptionBlock(), nextLabel++);
			return result;
		}

		internal void Constrain(Type type)
		{
			il.Emit(OpCodes.Constrained, type);
		}

		internal void TryCast(Type type)
		{
			il.Emit(OpCodes.Isinst, type);
		}

		internal void Cast(Type type)
		{
			il.Emit(OpCodes.Castclass, type);
		}

		public IDisposable Using(Local local)
		{
			return new UsingBlock(this, local);
		}

		internal void Add()
		{
			Emit(OpCodes.Add);
		}

		internal void LoadLength(Local arr, bool zeroIfNull)
		{
			if (zeroIfNull)
			{
				CodeLabel label = DefineLabel();
				CodeLabel label2 = DefineLabel();
				LoadValue(arr);
				CopyValue();
				BranchIfTrue(label, @short: true);
				DiscardValue();
				LoadValue(0);
				Branch(label2, @short: true);
				MarkLabel(label);
				Emit(OpCodes.Ldlen);
				Emit(OpCodes.Conv_I4);
				MarkLabel(label2);
			}
			else
			{
				LoadValue(arr);
				Emit(OpCodes.Ldlen);
				Emit(OpCodes.Conv_I4);
			}
		}

		internal void CreateArray(Type elementType, Local length)
		{
			LoadValue(length);
			il.Emit(OpCodes.Newarr, elementType);
		}

		internal void LoadArrayValue(Local arr, Local i)
		{
			Type type = arr.Type;
			type = type.GetElementType();
			LoadValue(arr);
			LoadValue(i);
			switch (Helpers.GetTypeCode(type))
			{
			case ProtoTypeCode.SByte:
				Emit(OpCodes.Ldelem_I1);
				return;
			case ProtoTypeCode.Int16:
				Emit(OpCodes.Ldelem_I2);
				return;
			case ProtoTypeCode.Int32:
				Emit(OpCodes.Ldelem_I4);
				return;
			case ProtoTypeCode.Int64:
				Emit(OpCodes.Ldelem_I8);
				return;
			case ProtoTypeCode.Byte:
				Emit(OpCodes.Ldelem_U1);
				return;
			case ProtoTypeCode.UInt16:
				Emit(OpCodes.Ldelem_U2);
				return;
			case ProtoTypeCode.UInt32:
				Emit(OpCodes.Ldelem_U4);
				return;
			case ProtoTypeCode.UInt64:
				Emit(OpCodes.Ldelem_I8);
				return;
			case ProtoTypeCode.Single:
				Emit(OpCodes.Ldelem_R4);
				return;
			case ProtoTypeCode.Double:
				Emit(OpCodes.Ldelem_R8);
				return;
			}
			if (Helpers.IsValueType(type))
			{
				il.Emit(OpCodes.Ldelema, type);
				il.Emit(OpCodes.Ldobj, type);
			}
			else
			{
				Emit(OpCodes.Ldelem_Ref);
			}
		}

		internal void LoadValue(Type type)
		{
			il.Emit(OpCodes.Ldtoken, type);
			EmitCall(MapType(typeof(Type)).GetMethod("GetTypeFromHandle"));
		}

		internal void ConvertToInt32(ProtoTypeCode typeCode, bool uint32Overflow)
		{
			switch (typeCode)
			{
			case ProtoTypeCode.SByte:
			case ProtoTypeCode.Byte:
			case ProtoTypeCode.Int16:
			case ProtoTypeCode.UInt16:
				Emit(OpCodes.Conv_I4);
				break;
			case ProtoTypeCode.Int64:
				Emit(OpCodes.Conv_Ovf_I4);
				break;
			case ProtoTypeCode.UInt32:
				Emit(uint32Overflow ? OpCodes.Conv_Ovf_I4_Un : OpCodes.Conv_Ovf_I4);
				break;
			case ProtoTypeCode.UInt64:
				Emit(OpCodes.Conv_Ovf_I4_Un);
				break;
			default:
				throw new InvalidOperationException("ConvertToInt32 not implemented for: " + typeCode);
			case ProtoTypeCode.Int32:
				break;
			}
		}

		internal void ConvertFromInt32(ProtoTypeCode typeCode, bool uint32Overflow)
		{
			switch (typeCode)
			{
			case ProtoTypeCode.SByte:
				Emit(OpCodes.Conv_Ovf_I1);
				break;
			case ProtoTypeCode.Byte:
				Emit(OpCodes.Conv_Ovf_U1);
				break;
			case ProtoTypeCode.Int16:
				Emit(OpCodes.Conv_Ovf_I2);
				break;
			case ProtoTypeCode.UInt16:
				Emit(OpCodes.Conv_Ovf_U2);
				break;
			case ProtoTypeCode.UInt32:
				Emit(uint32Overflow ? OpCodes.Conv_Ovf_U4 : OpCodes.Conv_U4);
				break;
			case ProtoTypeCode.Int64:
				Emit(OpCodes.Conv_I8);
				break;
			case ProtoTypeCode.UInt64:
				Emit(OpCodes.Conv_U8);
				break;
			default:
				throw new InvalidOperationException();
			case ProtoTypeCode.Int32:
				break;
			}
		}

		internal void LoadValue(decimal value)
		{
			if (value == 0m)
			{
				LoadValue(typeof(decimal).GetField("Zero"));
				return;
			}
			int[] bits = decimal.GetBits(value);
			LoadValue(bits[0]);
			LoadValue(bits[1]);
			LoadValue(bits[2]);
			LoadValue(bits[3] >>> 31);
			LoadValue((bits[3] >> 16) & 0xFF);
			EmitCtor(MapType(typeof(decimal)), MapType(typeof(int)), MapType(typeof(int)), MapType(typeof(int)), MapType(typeof(bool)), MapType(typeof(byte)));
		}

		internal void LoadValue(Guid value)
		{
			if (value == Guid.Empty)
			{
				LoadValue(typeof(Guid).GetField("Empty"));
				return;
			}
			byte[] array = value.ToByteArray();
			int value2 = array[0] | (array[1] << 8) | (array[2] << 16) | (array[3] << 24);
			LoadValue(value2);
			short value3 = (short)(array[4] | (array[5] << 8));
			LoadValue(value3);
			value3 = (short)(array[6] | (array[7] << 8));
			LoadValue(value3);
			for (value2 = 8; value2 <= 15; value2++)
			{
				LoadValue(array[value2]);
			}
			EmitCtor(MapType(typeof(Guid)), MapType(typeof(int)), MapType(typeof(short)), MapType(typeof(short)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)));
		}

		internal void LoadSerializationContext()
		{
			LoadReaderWriter();
			LoadValue((isWriter ? typeof(ProtoWriter) : typeof(ProtoReader)).GetProperty("Context"));
		}

		internal Type MapType(Type type)
		{
			return model.MapType(type);
		}

		internal bool AllowInternal(PropertyInfo property)
		{
			if (!NonPublic)
			{
				return InternalsVisible(Helpers.GetAssembly(property.DeclaringType));
			}
			return true;
		}
	}
	internal delegate void ProtoSerializer(object value, ProtoWriter dest);
	internal delegate object ProtoDeserializer(object value, ProtoReader source);
	internal sealed class Local : IDisposable
	{
		private LocalBuilder value;

		private readonly Type type;

		private CompilerContext ctx;

		internal LocalBuilder Value => value ?? throw new ObjectDisposedException(GetType().Name);

		public Type Type => type;

		private Local(LocalBuilder value, Type type)
		{
			this.value = value;
			this.type = type;
		}

		internal Local(CompilerContext ctx, Type type)
		{
			this.ctx = ctx;
			if (ctx != null)
			{
				value = ctx.GetFromPool(type);
			}
			this.type = type;
		}

		public Local AsCopy()
		{
			if (ctx == null)
			{
				return this;
			}
			return new Local(value, type);
		}

		public void Dispose()
		{
			if (ctx != null)
			{
				ctx.ReleaseToPool(value);
				value = null;
				ctx = null;
			}
		}

		internal bool IsSame(Local other)
		{
			if (this == other)
			{
				return true;
			}
			object obj = value;
			if (other != null)
			{
				return obj == other.value;
			}
			return false;
		}
	}
}
