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