712 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C#
		
	
	
		
		
			
		
	
	
			712 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C#
		
	
	
| 
								 | 
							
								using System;
							 | 
						|||
| 
								 | 
							
								using System.Reflection;
							 | 
						|||
| 
								 | 
							
								namespace ProtoBuf
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    internal enum TimeSpanScale
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        Days = 0,
							 | 
						|||
| 
								 | 
							
								        Hours = 1,
							 | 
						|||
| 
								 | 
							
								        Minutes = 2,
							 | 
						|||
| 
								 | 
							
								        Seconds = 3,
							 | 
						|||
| 
								 | 
							
								        Milliseconds = 4,
							 | 
						|||
| 
								 | 
							
								        Ticks = 5,
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        MinMax = 15
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    /// <summary>
							 | 
						|||
| 
								 | 
							
								    /// Provides support for common .NET types that do not have a direct representation
							 | 
						|||
| 
								 | 
							
								    /// in protobuf, using the definitions from bcl.proto
							 | 
						|||
| 
								 | 
							
								    /// </summary>
							 | 
						|||
| 
								 | 
							
								    public static class BclHelpers
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Creates a new instance of the specified type, bypassing the constructor.
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        /// <param name="type">The type to create</param>
							 | 
						|||
| 
								 | 
							
								        /// <returns>The new instance</returns>
							 | 
						|||
| 
								 | 
							
								        /// <exception cref="NotSupportedException">If the platform does not support constructor-skipping</exception>
							 | 
						|||
| 
								 | 
							
								        public static object GetUninitializedObject(Type type)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								#if COREFX
							 | 
						|||
| 
								 | 
							
								            object obj = TryGetUninitializedObjectWithFormatterServices(type);
							 | 
						|||
| 
								 | 
							
								            if (obj != null) return obj;
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								#if PLAT_BINARYFORMATTER && !(COREFX || PROFILE259)
							 | 
						|||
| 
								 | 
							
								            return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type);
							 | 
						|||
| 
								 | 
							
								#else
							 | 
						|||
| 
								 | 
							
								            throw new NotSupportedException("Constructor-skipping is not supported on this platform");
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#if COREFX // this is inspired by DCS: https://github.com/dotnet/corefx/blob/c02d33b18398199f6acc17d375dab154e9a1df66/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs#L854-L894
							 | 
						|||
| 
								 | 
							
								        static Func<Type, object> getUninitializedObject;
							 | 
						|||
| 
								 | 
							
								        static internal object TryGetUninitializedObjectWithFormatterServices(Type type)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (getUninitializedObject == null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                try {
							 | 
						|||
| 
								 | 
							
								                    var formatterServiceType = typeof(string).GetTypeInfo().Assembly.GetType("System.Runtime.Serialization.FormatterServices");
							 | 
						|||
| 
								 | 
							
								                    if (formatterServiceType == null)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        // fallback for .Net Core 3.0
							 | 
						|||
| 
								 | 
							
								                        var formatterAssembly = Assembly.Load(new AssemblyName("System.Runtime.Serialization.Formatters"));
							 | 
						|||
| 
								 | 
							
								                        formatterServiceType = formatterAssembly.GetType("System.Runtime.Serialization.FormatterServices");
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    MethodInfo method = formatterServiceType?.GetMethod("GetUninitializedObject", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
							 | 
						|||
| 
								 | 
							
								                    if (method != null)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        getUninitializedObject = (Func<Type, object>)method.CreateDelegate(typeof(Func<Type, object>));
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                catch  { /* best efforts only */ }
							 | 
						|||
| 
								 | 
							
								                if(getUninitializedObject == null) getUninitializedObject = x => null;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return getUninitializedObject(type);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        const int FieldTimeSpanValue = 0x01, FieldTimeSpanScale = 0x02, FieldTimeSpanKind = 0x03;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal static readonly DateTime[] EpochOrigin = {
							 | 
						|||
| 
								 | 
							
								            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)
							 | 
						|||
| 
								 | 
							
								        };
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// The default value for dates that are following google.protobuf.Timestamp semantics
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        private static readonly DateTime TimestampEpoch = EpochOrigin[(int)DateTimeKind.Utc];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Writes a TimeSpan to a protobuf stream using protobuf-net's own representation, bcl.TimeSpan
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        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(nameof(dest));
							 | 
						|||
| 
								 | 
							
								            long value;
							 | 
						|||
| 
								 | 
							
								            switch (dest.WireType)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case WireType.String:
							 | 
						|||
| 
								 | 
							
								                case WireType.StartGroup:
							 | 
						|||
| 
								 | 
							
								                    TimeSpanScale scale;
							 | 
						|||
| 
								 | 
							
								                    value = timeSpan.Ticks;
							 | 
						|||
| 
								 | 
							
								                    if (timeSpan == TimeSpan.MaxValue)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        value = 1;
							 | 
						|||
| 
								 | 
							
								                        scale = TimeSpanScale.MinMax;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (timeSpan == TimeSpan.MinValue)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        value = -1;
							 | 
						|||
| 
								 | 
							
								                        scale = TimeSpanScale.MinMax;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (value % TimeSpan.TicksPerDay == 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        scale = TimeSpanScale.Days;
							 | 
						|||
| 
								 | 
							
								                        value /= TimeSpan.TicksPerDay;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (value % TimeSpan.TicksPerHour == 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        scale = TimeSpanScale.Hours;
							 | 
						|||
| 
								 | 
							
								                        value /= TimeSpan.TicksPerHour;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (value % TimeSpan.TicksPerMinute == 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        scale = TimeSpanScale.Minutes;
							 | 
						|||
| 
								 | 
							
								                        value /= TimeSpan.TicksPerMinute;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (value % TimeSpan.TicksPerSecond == 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        scale = TimeSpanScale.Seconds;
							 | 
						|||
| 
								 | 
							
								                        value /= TimeSpan.TicksPerSecond;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (value % TimeSpan.TicksPerMillisecond == 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        scale = TimeSpanScale.Milliseconds;
							 | 
						|||
| 
								 | 
							
								                        value /= TimeSpan.TicksPerMillisecond;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        scale = TimeSpanScale.Ticks;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    SubItemToken token = ProtoWriter.StartSubItem(null, dest);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                    if (value != 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ProtoWriter.WriteFieldHeader(FieldTimeSpanValue, WireType.SignedVariant, dest);
							 | 
						|||
| 
								 | 
							
								                        ProtoWriter.WriteInt64(value, dest);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    if (scale != TimeSpanScale.Days)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ProtoWriter.WriteFieldHeader(FieldTimeSpanScale, WireType.Variant, dest);
							 | 
						|||
| 
								 | 
							
								                        ProtoWriter.WriteInt32((int)scale, dest);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    if (kind != DateTimeKind.Unspecified)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ProtoWriter.WriteFieldHeader(FieldTimeSpanKind, 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.ToString());
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Parses a TimeSpan from a protobuf stream using protobuf-net's own representation, bcl.TimeSpan
							 | 
						|||
| 
								 | 
							
								        /// </summary>        
							 | 
						|||
| 
								 | 
							
								        public static TimeSpan ReadTimeSpan(ProtoReader source)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            long ticks = ReadTimeSpanTicks(source, out DateTimeKind kind);
							 | 
						|||
| 
								 | 
							
								            if (ticks == long.MinValue) return TimeSpan.MinValue;
							 | 
						|||
| 
								 | 
							
								            if (ticks == long.MaxValue) return TimeSpan.MaxValue;
							 | 
						|||
| 
								 | 
							
								            return TimeSpan.FromTicks(ticks);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Parses a TimeSpan from a protobuf stream using the standardized format, google.protobuf.Duration
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static TimeSpan ReadDuration(ProtoReader source)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            long seconds = 0;
							 | 
						|||
| 
								 | 
							
								            int nanos = 0;
							 | 
						|||
| 
								 | 
							
								            SubItemToken token = ProtoReader.StartSubItem(source);
							 | 
						|||
| 
								 | 
							
								            int fieldNumber;
							 | 
						|||
| 
								 | 
							
								            while ((fieldNumber = source.ReadFieldHeader()) > 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                switch (fieldNumber)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    case 1:
							 | 
						|||
| 
								 | 
							
								                        seconds = source.ReadInt64();
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    case 2:
							 | 
						|||
| 
								 | 
							
								                        nanos = source.ReadInt32();
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    default:
							 | 
						|||
| 
								 | 
							
								                        source.SkipField();
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ProtoReader.EndSubItem(token, source);
							 | 
						|||
| 
								 | 
							
								            return FromDurationSeconds(seconds, nanos);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Writes a TimeSpan to a protobuf stream using the standardized format, google.protobuf.Duration
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static void WriteDuration(TimeSpan value, ProtoWriter dest)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var seconds = ToDurationSeconds(value, out int nanos);
							 | 
						|||
| 
								 | 
							
								            WriteSecondsNanos(seconds, nanos, dest);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private static void WriteSecondsNanos(long seconds, int nanos, ProtoWriter dest)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            SubItemToken token = ProtoWriter.StartSubItem(null, dest);
							 | 
						|||
| 
								 | 
							
								            if (seconds != 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                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);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Parses a DateTime from a protobuf stream using the standardized format, google.protobuf.Timestamp
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static DateTime ReadTimestamp(ProtoReader source)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            // note: DateTime is only defined for just over 0000 to just below 10000;
							 | 
						|||
| 
								 | 
							
								            // TimeSpan has a range of +/- 10,675,199 days === 29k years;
							 | 
						|||
| 
								 | 
							
								            // so we can just use epoch time delta
							 | 
						|||
| 
								 | 
							
								            return TimestampEpoch + ReadDuration(source);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Writes a DateTime to a protobuf stream using the standardized format, google.protobuf.Timestamp
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static void WriteTimestamp(DateTime value, ProtoWriter dest)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            var seconds = ToDurationSeconds(value - TimestampEpoch, out int nanos);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (nanos < 0)
							 | 
						|||
| 
								 | 
							
								            {   // from Timestamp.proto:
							 | 
						|||
| 
								 | 
							
								                // "Negative second values with fractions must still have
							 | 
						|||
| 
								 | 
							
								                // non -negative nanos values that count forward in time."
							 | 
						|||
| 
								 | 
							
								                seconds--;
							 | 
						|||
| 
								 | 
							
								                nanos += 1000000000;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            WriteSecondsNanos(seconds, nanos, dest);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        static TimeSpan FromDurationSeconds(long seconds, int nanos)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            long ticks = checked((seconds * TimeSpan.TicksPerSecond)
							 | 
						|||
| 
								 | 
							
								                + (nanos * TimeSpan.TicksPerMillisecond) / 1000000);
							 | 
						|||
| 
								 | 
							
								            return TimeSpan.FromTicks(ticks);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        static long ToDurationSeconds(TimeSpan value, out int nanos)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            nanos = (int)(((value.Ticks % TimeSpan.TicksPerSecond) * 1000000)
							 | 
						|||
| 
								 | 
							
								                / TimeSpan.TicksPerMillisecond);
							 | 
						|||
| 
								 | 
							
								            return value.Ticks / TimeSpan.TicksPerSecond;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Parses a DateTime from a protobuf stream
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static DateTime ReadDateTime(ProtoReader source)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            long ticks = ReadTimeSpanTicks(source, out DateTimeKind kind);
							 | 
						|||
| 
								 | 
							
								            if (ticks == long.MinValue) return DateTime.MinValue;
							 | 
						|||
| 
								 | 
							
								            if (ticks == long.MaxValue) return DateTime.MaxValue;
							 | 
						|||
| 
								 | 
							
								            return EpochOrigin[(int)kind].AddTicks(ticks);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Writes a DateTime to a protobuf stream, excluding the <c>Kind</c>
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static void WriteDateTime(DateTime value, ProtoWriter dest)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            WriteDateTimeImpl(value, dest, false);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Writes a DateTime to a protobuf stream, including the <c>Kind</c>
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static void WriteDateTimeWithKind(DateTime value, ProtoWriter dest)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            WriteDateTimeImpl(value, dest, true);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private static void WriteDateTimeImpl(DateTime value, ProtoWriter dest, bool includeKind)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (dest == null) throw new ArgumentNullException(nameof(dest));
							 | 
						|||
| 
								 | 
							
								            TimeSpan delta;
							 | 
						|||
| 
								 | 
							
								            switch (dest.WireType)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                case WireType.StartGroup:
							 | 
						|||
| 
								 | 
							
								                case WireType.String:
							 | 
						|||
| 
								 | 
							
								                    if (value == DateTime.MaxValue)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        delta = TimeSpan.MaxValue;
							 | 
						|||
| 
								 | 
							
								                        includeKind = false;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (value == DateTime.MinValue)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        delta = TimeSpan.MinValue;
							 | 
						|||
| 
								 | 
							
								                        includeKind = false;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        delta = value - EpochOrigin[0];
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								                default:
							 | 
						|||
| 
								 | 
							
								                    delta = value - EpochOrigin[0];
							 | 
						|||
| 
								 | 
							
								                    break;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            WriteTimeSpanImpl(delta, 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);
							 | 
						|||
| 
								 | 
							
								                    int fieldNumber;
							 | 
						|||
| 
								 | 
							
								                    TimeSpanScale scale = TimeSpanScale.Days;
							 | 
						|||
| 
								 | 
							
								                    long value = 0;
							 | 
						|||
| 
								 | 
							
								                    while ((fieldNumber = source.ReadFieldHeader()) > 0)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        switch (fieldNumber)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            case FieldTimeSpanScale:
							 | 
						|||
| 
								 | 
							
								                                scale = (TimeSpanScale)source.ReadInt32();
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldTimeSpanValue:
							 | 
						|||
| 
								 | 
							
								                                source.Assert(WireType.SignedVariant);
							 | 
						|||
| 
								 | 
							
								                                value = source.ReadInt64();
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            case FieldTimeSpanKind:
							 | 
						|||
| 
								 | 
							
								                                kind = (DateTimeKind)source.ReadInt32();
							 | 
						|||
| 
								 | 
							
								                                switch (kind)
							 | 
						|||
| 
								 | 
							
								                                {
							 | 
						|||
| 
								 | 
							
								                                    case DateTimeKind.Unspecified:
							 | 
						|||
| 
								 | 
							
								                                    case DateTimeKind.Utc:
							 | 
						|||
| 
								 | 
							
								                                    case DateTimeKind.Local:
							 | 
						|||
| 
								 | 
							
								                                        break; // fine
							 | 
						|||
| 
								 | 
							
								                                    default:
							 | 
						|||
| 
								 | 
							
								                                        throw new ProtoException("Invalid date/time kind: " + kind.ToString());
							 | 
						|||
| 
								 | 
							
								                                }
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                            default:
							 | 
						|||
| 
								 | 
							
								                                source.SkipField();
							 | 
						|||
| 
								 | 
							
								                                break;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    ProtoReader.EndSubItem(token, source);
							 | 
						|||
| 
								 | 
							
								                    switch (scale)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        case TimeSpanScale.Days:
							 | 
						|||
| 
								 | 
							
								                            return value * TimeSpan.TicksPerDay;
							 | 
						|||
| 
								 | 
							
								                        case TimeSpanScale.Hours:
							 | 
						|||
| 
								 | 
							
								                            return value * TimeSpan.TicksPerHour;
							 | 
						|||
| 
								 | 
							
								                        case TimeSpanScale.Minutes:
							 | 
						|||
| 
								 | 
							
								                            return value * TimeSpan.TicksPerMinute;
							 | 
						|||
| 
								 | 
							
								                        case TimeSpanScale.Seconds:
							 | 
						|||
| 
								 | 
							
								                            return value * TimeSpan.TicksPerSecond;
							 | 
						|||
| 
								 | 
							
								                        case TimeSpanScale.Milliseconds:
							 | 
						|||
| 
								 | 
							
								                            return value * TimeSpan.TicksPerMillisecond;
							 | 
						|||
| 
								 | 
							
								                        case TimeSpanScale.Ticks:
							 | 
						|||
| 
								 | 
							
								                            return value;
							 | 
						|||
| 
								 | 
							
								                        case TimeSpanScale.MinMax:
							 | 
						|||
| 
								 | 
							
								                            switch (value)
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                case 1: return long.MaxValue;
							 | 
						|||
| 
								 | 
							
								                                case -1: return long.MinValue;
							 | 
						|||
| 
								 | 
							
								                                default: throw new ProtoException("Unknown min/max value: " + value.ToString());
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                        default:
							 | 
						|||
| 
								 | 
							
								                            throw new ProtoException("Unknown timescale: " + scale.ToString());
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                case WireType.Fixed64:
							 | 
						|||
| 
								 | 
							
								                    return source.ReadInt64();
							 | 
						|||
| 
								 | 
							
								                default:
							 | 
						|||
| 
								 | 
							
								                    throw new ProtoException("Unexpected wire-type: " + source.WireType.ToString());
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        const int FieldDecimalLow = 0x01, FieldDecimalHigh = 0x02, FieldDecimalSignScale = 0x03;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Parses a decimal from a protobuf stream
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static decimal ReadDecimal(ProtoReader reader)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ulong low = 0;
							 | 
						|||
| 
								 | 
							
								            uint high = 0;
							 | 
						|||
| 
								 | 
							
								            uint signScale = 0;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            int fieldNumber;
							 | 
						|||
| 
								 | 
							
								            SubItemToken token = ProtoReader.StartSubItem(reader);
							 | 
						|||
| 
								 | 
							
								            while ((fieldNumber = reader.ReadFieldHeader()) > 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                switch (fieldNumber)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    case FieldDecimalLow: low = reader.ReadUInt64(); break;
							 | 
						|||
| 
								 | 
							
								                    case FieldDecimalHigh: high = reader.ReadUInt32(); break;
							 | 
						|||
| 
								 | 
							
								                    case FieldDecimalSignScale: signScale = reader.ReadUInt32(); break;
							 | 
						|||
| 
								 | 
							
								                    default: reader.SkipField(); break;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ProtoReader.EndSubItem(token, reader);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            int lo = (int)(low & 0xFFFFFFFFL),
							 | 
						|||
| 
								 | 
							
								                mid = (int)((low >> 32) & 0xFFFFFFFFL),
							 | 
						|||
| 
								 | 
							
								                hi = (int)high;
							 | 
						|||
| 
								 | 
							
								            bool isNeg = (signScale & 0x0001) == 0x0001;
							 | 
						|||
| 
								 | 
							
								            byte scale = (byte)((signScale & 0x01FE) >> 1);
							 | 
						|||
| 
								 | 
							
								            return new decimal(lo, mid, hi, isNeg, scale);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Writes a decimal to a protobuf stream
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static void WriteDecimal(decimal value, ProtoWriter writer)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            int[] bits = decimal.GetBits(value);
							 | 
						|||
| 
								 | 
							
								            ulong a = ((ulong)bits[1]) << 32, b = ((ulong)bits[0]) & 0xFFFFFFFFL;
							 | 
						|||
| 
								 | 
							
								            ulong low = a | b;
							 | 
						|||
| 
								 | 
							
								            uint high = (uint)bits[2];
							 | 
						|||
| 
								 | 
							
								            uint signScale = (uint)(((bits[3] >> 15) & 0x01FE) | ((bits[3] >> 31) & 0x0001));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            SubItemToken token = ProtoWriter.StartSubItem(null, writer);
							 | 
						|||
| 
								 | 
							
								            if (low != 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteFieldHeader(FieldDecimalLow, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteUInt64(low, writer);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            if (high != 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteFieldHeader(FieldDecimalHigh, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteUInt32(high, writer);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            if (signScale != 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteFieldHeader(FieldDecimalSignScale, WireType.Variant, writer);
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteUInt32(signScale, writer);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ProtoWriter.EndSubItem(token, writer);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        const int FieldGuidLow = 1, FieldGuidHigh = 2;
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Writes a Guid to a protobuf stream
							 | 
						|||
| 
								 | 
							
								        /// </summary>        
							 | 
						|||
| 
								 | 
							
								        public static void WriteGuid(Guid value, ProtoWriter dest)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            byte[] blob = value.ToByteArray();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            SubItemToken token = ProtoWriter.StartSubItem(null, dest);
							 | 
						|||
| 
								 | 
							
								            if (value != Guid.Empty)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteFieldHeader(FieldGuidLow, WireType.Fixed64, dest);
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteBytes(blob, 0, 8, dest);
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteFieldHeader(FieldGuidHigh, WireType.Fixed64, dest);
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteBytes(blob, 8, 8, dest);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ProtoWriter.EndSubItem(token, dest);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Parses a Guid from a protobuf stream
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static Guid ReadGuid(ProtoReader source)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            ulong low = 0, high = 0;
							 | 
						|||
| 
								 | 
							
								            int fieldNumber;
							 | 
						|||
| 
								 | 
							
								            SubItemToken token = ProtoReader.StartSubItem(source);
							 | 
						|||
| 
								 | 
							
								            while ((fieldNumber = source.ReadFieldHeader()) > 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                switch (fieldNumber)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    case FieldGuidLow: low = source.ReadUInt64(); break;
							 | 
						|||
| 
								 | 
							
								                    case FieldGuidHigh: high = source.ReadUInt64(); break;
							 | 
						|||
| 
								 | 
							
								                    default: source.SkipField(); break;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ProtoReader.EndSubItem(token, source);
							 | 
						|||
| 
								 | 
							
								            if (low == 0 && high == 0) return Guid.Empty;
							 | 
						|||
| 
								 | 
							
								            uint a = (uint)(low >> 32), b = (uint)low, c = (uint)(high >> 32), d = (uint)high;
							 | 
						|||
| 
								 | 
							
								            return new Guid((int)b, (short)a, (short)(a >> 16),
							 | 
						|||
| 
								 | 
							
								                (byte)d, (byte)(d >> 8), (byte)(d >> 16), (byte)(d >> 24),
							 | 
						|||
| 
								 | 
							
								                (byte)c, (byte)(c >> 8), (byte)(c >> 16), (byte)(c >> 24));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private const int
							 | 
						|||
| 
								 | 
							
								            FieldExistingObjectKey = 1,
							 | 
						|||
| 
								 | 
							
								            FieldNewObjectKey = 2,
							 | 
						|||
| 
								 | 
							
								            FieldExistingTypeKey = 3,
							 | 
						|||
| 
								 | 
							
								            FieldNewTypeKey = 4,
							 | 
						|||
| 
								 | 
							
								            FieldTypeName = 8,
							 | 
						|||
| 
								 | 
							
								            FieldObject = 10;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Optional behaviours that introduce .NET-specific functionality
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        [Flags]
							 | 
						|||
| 
								 | 
							
								        public enum NetObjectOptions : byte
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// No special behaviour
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            None = 0,
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Enables full object-tracking/full-graph support.
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            AsReference = 1,
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Embeds the type information into the stream, allowing usage with types not known in advance.
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            DynamicType = 2,
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// If false, the constructor for the type is bypassed during deserialization, meaning any field initializers
							 | 
						|||
| 
								 | 
							
								            /// or other initialization code is skipped.
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            UseConstructor = 4,
							 | 
						|||
| 
								 | 
							
								            /// <summary>
							 | 
						|||
| 
								 | 
							
								            /// Should the object index be reserved, rather than creating an object promptly
							 | 
						|||
| 
								 | 
							
								            /// </summary>
							 | 
						|||
| 
								 | 
							
								            LateSet = 8
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Reads an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc.
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static object ReadNetObject(object value, ProtoReader source, int key, Type type, NetObjectOptions options)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            SubItemToken token = ProtoReader.StartSubItem(source);
							 | 
						|||
| 
								 | 
							
								            int fieldNumber;
							 | 
						|||
| 
								 | 
							
								            int newObjectKey = -1, newTypeKey = -1, tmp;
							 | 
						|||
| 
								 | 
							
								            while ((fieldNumber = source.ReadFieldHeader()) > 0)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                switch (fieldNumber)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    case FieldExistingObjectKey:
							 | 
						|||
| 
								 | 
							
								                        tmp = source.ReadInt32();
							 | 
						|||
| 
								 | 
							
								                        value = source.NetCache.GetKeyedObject(tmp);
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    case FieldNewObjectKey:
							 | 
						|||
| 
								 | 
							
								                        newObjectKey = source.ReadInt32();
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    case FieldExistingTypeKey:
							 | 
						|||
| 
								 | 
							
								                        tmp = source.ReadInt32();
							 | 
						|||
| 
								 | 
							
								                        type = (Type)source.NetCache.GetKeyedObject(tmp);
							 | 
						|||
| 
								 | 
							
								                        key = source.GetTypeKey(ref type);
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    case FieldNewTypeKey:
							 | 
						|||
| 
								 | 
							
								                        newTypeKey = source.ReadInt32();
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    case FieldTypeName:
							 | 
						|||
| 
								 | 
							
								                        string typeName = source.ReadString();
							 | 
						|||
| 
								 | 
							
								                        type = source.DeserializeType(typeName);
							 | 
						|||
| 
								 | 
							
								                        if (type == null)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            throw new ProtoException("Unable to resolve type: " + typeName + " (you can use the TypeModel.DynamicTypeFormatting event to provide a custom mapping)");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        if (type == typeof(string))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            key = -1;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            key = source.GetTypeKey(ref type);
							 | 
						|||
| 
								 | 
							
								                            if (key < 0)
							 | 
						|||
| 
								 | 
							
								                                throw new InvalidOperationException("Dynamic type is not a contract-type: " + type.Name);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    case FieldObject:
							 | 
						|||
| 
								 | 
							
								                        bool isString = type == typeof(string);
							 | 
						|||
| 
								 | 
							
								                        bool wasNull = value == null;
							 | 
						|||
| 
								 | 
							
								                        bool lateSet = wasNull && (isString || ((options & NetObjectOptions.LateSet) != 0));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        if (newObjectKey >= 0 && !lateSet)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            if (value == null)
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                source.TrapNextObject(newObjectKey);
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                            else
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                source.NetCache.SetKeyedObject(newObjectKey, value);
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                            if (newTypeKey >= 0) source.NetCache.SetKeyedObject(newTypeKey, type);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        object oldValue = value;
							 | 
						|||
| 
								 | 
							
								                        if (isString)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            value = source.ReadString();
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        else
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            value = ProtoReader.ReadTypedObject(oldValue, key, source, type);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        if (newObjectKey >= 0)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            if (wasNull && !lateSet)
							 | 
						|||
| 
								 | 
							
								                            { // this both ensures (via exception) that it *was* set, and makes sure we don't shout
							 | 
						|||
| 
								 | 
							
								                                // about changed references
							 | 
						|||
| 
								 | 
							
								                                oldValue = source.NetCache.GetKeyedObject(newObjectKey);
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                            if (lateSet)
							 | 
						|||
| 
								 | 
							
								                            {
							 | 
						|||
| 
								 | 
							
								                                source.NetCache.SetKeyedObject(newObjectKey, value);
							 | 
						|||
| 
								 | 
							
								                                if (newTypeKey >= 0) source.NetCache.SetKeyedObject(newTypeKey, type);
							 | 
						|||
| 
								 | 
							
								                            }
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        if (newObjectKey >= 0 && !lateSet && !ReferenceEquals(oldValue, value))
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            throw new ProtoException("A reference-tracked object changed reference during deserialization");
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        if (newObjectKey < 0 && newTypeKey >= 0)
							 | 
						|||
| 
								 | 
							
								                        {  // have a new type, but not a new object
							 | 
						|||
| 
								 | 
							
								                            source.NetCache.SetKeyedObject(newTypeKey, type);
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                    default:
							 | 
						|||
| 
								 | 
							
								                        source.SkipField();
							 | 
						|||
| 
								 | 
							
								                        break;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            if (newObjectKey >= 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;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        /// <summary>
							 | 
						|||
| 
								 | 
							
								        /// Writes an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc.
							 | 
						|||
| 
								 | 
							
								        /// </summary>
							 | 
						|||
| 
								 | 
							
								        public static void WriteNetObject(object value, ProtoWriter dest, int key, NetObjectOptions options)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (dest == null) throw new ArgumentNullException("dest");
							 | 
						|||
| 
								 | 
							
								            bool dynamicType = (options & NetObjectOptions.DynamicType) != 0,
							 | 
						|||
| 
								 | 
							
								                 asReference = (options & NetObjectOptions.AsReference) != 0;
							 | 
						|||
| 
								 | 
							
								            WireType wireType = dest.WireType;
							 | 
						|||
| 
								 | 
							
								            SubItemToken token = ProtoWriter.StartSubItem(null, dest);
							 | 
						|||
| 
								 | 
							
								            bool writeObject = true;
							 | 
						|||
| 
								 | 
							
								            if (asReference)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                int objectKey = dest.NetCache.AddObjectKey(value, out bool existing);
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteFieldHeader(existing ? FieldExistingObjectKey : FieldNewObjectKey, WireType.Variant, dest);
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteInt32(objectKey, dest);
							 | 
						|||
| 
								 | 
							
								                if (existing)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    writeObject = false;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (writeObject)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (dynamicType)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    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);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    int typeKey = dest.NetCache.AddObjectKey(type, out bool existing);
							 | 
						|||
| 
								 | 
							
								                    ProtoWriter.WriteFieldHeader(existing ? FieldExistingTypeKey : FieldNewTypeKey, WireType.Variant, dest);
							 | 
						|||
| 
								 | 
							
								                    ProtoWriter.WriteInt32(typeKey, dest);
							 | 
						|||
| 
								 | 
							
								                    if (!existing)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        ProtoWriter.WriteFieldHeader(FieldTypeName, WireType.String, dest);
							 | 
						|||
| 
								 | 
							
								                        ProtoWriter.WriteString(dest.SerializeType(type), dest);
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                ProtoWriter.WriteFieldHeader(FieldObject, wireType, dest);
							 | 
						|||
| 
								 | 
							
								                if (value is string)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ProtoWriter.WriteString((string)value, dest);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    ProtoWriter.WriteObject(value, key, dest);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            ProtoWriter.EndSubItem(token, dest);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 |