123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- using System;
- using System.Collections;
- using ProtoBuf.Meta;
- namespace ProtoBuf
- {
- internal sealed class NetObjectCache
- {
- internal const int Root = 0;
- private MutableList underlyingList;
- private MutableList List
- {
- get
- {
- if (underlyingList == null) underlyingList = new MutableList();
- return underlyingList;
- }
- }
- 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("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("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 NO_GENERICS
-
- if(s == null)
- {
- if (objectKeys == null)
- {
- objectKeys = new ReferenceHashtable();
- index = -1;
- }
- else
- {
- object tmp = objectKeys[value];
- index = tmp == null ? -1 : (int) tmp;
- }
- }
- else
- {
- if (stringKeys == null)
- {
- stringKeys = new Hashtable();
- index = -1;
- }
- else
- {
- object tmp = stringKeys[s];
- index = tmp == null ? -1 : (int) tmp;
- }
- }
- #else
- 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 System.Collections.Generic.Dictionary<object, int>(ReferenceComparer.Default);
- index = -1;
- }
- else
- {
- if (!objectKeys.TryGetValue(value, out index)) index = -1;
- }
- #endif
- }
- else
- {
- if (stringKeys == null)
- {
- stringKeys = new System.Collections.Generic.Dictionary<string, int>();
- index = -1;
- }
- else
- {
- if (!stringKeys.TryGetValue(s, out index)) index = -1;
- }
- }
- #endif
- 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;
- }
- }
- }
- }
- }
- #if NO_GENERICS
- private ReferenceHashtable objectKeys;
- private System.Collections.Hashtable stringKeys;
- private class ReferenceHashtable : System.Collections.Hashtable
- {
- protected override int GetHash(object key)
- {
- return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(key);
- }
- protected override bool KeyEquals(object item, object key)
- {
- return item == key;
- }
- }
- #else
- private System.Collections.Generic.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 : System.Collections.Generic.IEqualityComparer<object>
- {
- public readonly static ReferenceComparer Default = new ReferenceComparer();
- private ReferenceComparer() {}
- bool System.Collections.Generic.IEqualityComparer<object>.Equals(object x, object y)
- {
- return x == y; // ref equality
- }
- int System.Collections.Generic.IEqualityComparer<object>.GetHashCode(object obj)
- {
- return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
- }
- }
- #endif
- #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
- }
- }
- }
|