190 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C#
		
	
	
		
		
			
		
	
	
			190 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C#
		
	
	
| 
								 | 
							
								using System;
							 | 
						|||
| 
								 | 
							
								using System.Collections.Generic;
							 | 
						|||
| 
								 | 
							
								using ProtoBuf.Meta;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								namespace ProtoBuf
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    internal sealed class NetObjectCache
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        internal const int Root = 0;
							 | 
						|||
| 
								 | 
							
								        private MutableList underlyingList;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private MutableList List => underlyingList ?? (underlyingList = new MutableList());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal object GetKeyedObject(int key)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (key-- == Root)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (rootObject == null) throw new ProtoException("No root object assigned");
							 | 
						|||
| 
								 | 
							
								                return rootObject;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            BasicList list = List;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (key < 0 || key >= list.Count)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                Helpers.DebugWriteLine("Missing key: " + key);
							 | 
						|||
| 
								 | 
							
								                throw new ProtoException("Internal error; a missing key occurred");
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            object tmp = list[key];
							 | 
						|||
| 
								 | 
							
								            if (tmp == null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                throw new ProtoException("A deferred key does not have a value yet");
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return tmp;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal void SetKeyedObject(int key, object value)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (key-- == Root)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (value == null) throw new ArgumentNullException(nameof(value));
							 | 
						|||
| 
								 | 
							
								                if (rootObject != null && ((object)rootObject != (object)value)) throw new ProtoException("The root object cannot be reassigned");
							 | 
						|||
| 
								 | 
							
								                rootObject = value;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                MutableList list = List;
							 | 
						|||
| 
								 | 
							
								                if (key < list.Count)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    object oldVal = list[key];
							 | 
						|||
| 
								 | 
							
								                    if (oldVal == null)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        list[key] = value;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    else if (!ReferenceEquals(oldVal, value))
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        throw new ProtoException("Reference-tracked objects cannot change reference");
							 | 
						|||
| 
								 | 
							
								                    } // otherwise was the same; nothing to do
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else if (key != list.Add(value))
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    throw new ProtoException("Internal error; a key mismatch occurred");
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private object rootObject;
							 | 
						|||
| 
								 | 
							
								        internal int AddObjectKey(object value, out bool existing)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (value == null) throw new ArgumentNullException(nameof(value));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if ((object)value == (object)rootObject) // (object) here is no-op, but should be
							 | 
						|||
| 
								 | 
							
								            {                                        // preserved even if this was typed - needs ref-check
							 | 
						|||
| 
								 | 
							
								                existing = true;
							 | 
						|||
| 
								 | 
							
								                return Root;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            string s = value as string;
							 | 
						|||
| 
								 | 
							
								            BasicList list = List;
							 | 
						|||
| 
								 | 
							
								            int index;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (s == null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								#if CF || PORTABLE // CF has very limited proper object ref-tracking; so instead, we'll search it the hard way
							 | 
						|||
| 
								 | 
							
								                index = list.IndexOfReference(value);
							 | 
						|||
| 
								 | 
							
								#else
							 | 
						|||
| 
								 | 
							
								                if (objectKeys == null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    objectKeys = new Dictionary<object, int>(ReferenceComparer.Default);
							 | 
						|||
| 
								 | 
							
								                    index = -1;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    if (!objectKeys.TryGetValue(value, out index)) index = -1;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (stringKeys == null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    stringKeys = new Dictionary<string, int>();
							 | 
						|||
| 
								 | 
							
								                    index = -1;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    if (!stringKeys.TryGetValue(s, out index)) index = -1;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            if (!(existing = index >= 0))
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                index = list.Add(value);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                if (s == null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								#if !CF && !PORTABLE // CF can't handle the object keys very well
							 | 
						|||
| 
								 | 
							
								                    objectKeys.Add(value, index);
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                else
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    stringKeys.Add(s, index);
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            return index + 1;
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private int trapStartIndex; // defaults to 0 - optimization for RegisterTrappedObject
							 | 
						|||
| 
								 | 
							
								                                    // to make it faster at seeking to find deferred-objects
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal void RegisterTrappedObject(object value)
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            if (rootObject == null)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                rootObject = value;
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								            else
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                if (underlyingList != null)
							 | 
						|||
| 
								 | 
							
								                {
							 | 
						|||
| 
								 | 
							
								                    for (int i = trapStartIndex; i < underlyingList.Count; i++)
							 | 
						|||
| 
								 | 
							
								                    {
							 | 
						|||
| 
								 | 
							
								                        trapStartIndex = i + 1; // things never *become* null; whether or
							 | 
						|||
| 
								 | 
							
								                                                // not the next item is null, it will never
							 | 
						|||
| 
								 | 
							
								                                                // need to be checked again
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								                        if (underlyingList[i] == null)
							 | 
						|||
| 
								 | 
							
								                        {
							 | 
						|||
| 
								 | 
							
								                            underlyingList[i] = value;
							 | 
						|||
| 
								 | 
							
								                            break;
							 | 
						|||
| 
								 | 
							
								                        }
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        private Dictionary<string, int> stringKeys;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#if !CF && !PORTABLE // CF lacks the ability to get a robust reference-based hash-code, so we'll do it the harder way instead
							 | 
						|||
| 
								 | 
							
								        private System.Collections.Generic.Dictionary<object, int> objectKeys;
							 | 
						|||
| 
								 | 
							
								        private sealed class ReferenceComparer : IEqualityComparer<object>
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            public readonly static ReferenceComparer Default = new ReferenceComparer();
							 | 
						|||
| 
								 | 
							
								            private ReferenceComparer() { }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            bool IEqualityComparer<object>.Equals(object x, object y)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                return x == y; // ref equality
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								            int IEqualityComparer<object>.GetHashCode(object obj)
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								                return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
							 | 
						|||
| 
								 | 
							
								            }
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								        internal void Clear()
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								            trapStartIndex = 0;
							 | 
						|||
| 
								 | 
							
								            rootObject = null;
							 | 
						|||
| 
								 | 
							
								            if (underlyingList != null) underlyingList.Clear();
							 | 
						|||
| 
								 | 
							
								            if (stringKeys != null) stringKeys.Clear();
							 | 
						|||
| 
								 | 
							
								#if !CF && !PORTABLE
							 | 
						|||
| 
								 | 
							
								            if (objectKeys != null) objectKeys.Clear();
							 | 
						|||
| 
								 | 
							
								#endif
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 |