using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using ProtoBuf.Meta; namespace ProtoBuf; internal sealed class NetObjectCache { private sealed class ReferenceComparer : IEqualityComparer { public static readonly ReferenceComparer Default = new ReferenceComparer(); private ReferenceComparer() { } bool IEqualityComparer.Equals(object x, object y) { return x == y; } int IEqualityComparer.GetHashCode(object obj) { return RuntimeHelpers.GetHashCode(obj); } } internal const int Root = 0; private MutableList underlyingList; private object rootObject; private int trapStartIndex; private Dictionary stringKeys; private Dictionary objectKeys; private MutableList List => underlyingList ?? (underlyingList = new MutableList()); internal object GetKeyedObject(int key) { if (key-- == 0) { if (rootObject == null) { throw new ProtoException("No root object assigned"); } return rootObject; } BasicList list = List; if (key < 0 || key >= list.Count) { throw new ProtoException("Internal error; a missing key occurred"); } object obj = list[key]; if (obj == null) { throw new ProtoException("A deferred key does not have a value yet"); } return obj; } internal void SetKeyedObject(int key, object value) { if (key-- == 0) { if (value == null) { throw new ArgumentNullException("value"); } if (rootObject != null && rootObject != value) { throw new ProtoException("The root object cannot be reassigned"); } rootObject = value; return; } MutableList list = List; if (key < list.Count) { object obj = list[key]; if (obj == null) { list[key] = value; } else if (obj != value) { throw new ProtoException("Reference-tracked objects cannot change reference"); } } else if (key != list.Add(value)) { throw new ProtoException("Internal error; a key mismatch occurred"); } } internal int AddObjectKey(object value, out bool existing) { if (value == null) { throw new ArgumentNullException("value"); } if (value == rootObject) { existing = true; return 0; } string text = value as string; BasicList list = List; int value2; if (text == null) { if (objectKeys == null) { objectKeys = new Dictionary(ReferenceComparer.Default); value2 = -1; } else if (!objectKeys.TryGetValue(value, out value2)) { value2 = -1; } } else if (stringKeys == null) { stringKeys = new Dictionary(); value2 = -1; } else if (!stringKeys.TryGetValue(text, out value2)) { value2 = -1; } if (!(existing = value2 >= 0)) { value2 = list.Add(value); if (text == null) { objectKeys.Add(value, value2); } else { stringKeys.Add(text, value2); } } return value2 + 1; } internal void RegisterTrappedObject(object value) { if (rootObject == null) { rootObject = value; } else { if (underlyingList == null) { return; } for (int i = trapStartIndex; i < underlyingList.Count; i++) { trapStartIndex = i + 1; if (underlyingList[i] == null) { underlyingList[i] = value; break; } } } } internal void Clear() { trapStartIndex = 0; rootObject = null; if (underlyingList != null) { underlyingList.Clear(); } if (stringKeys != null) { stringKeys.Clear(); } if (objectKeys != null) { objectKeys.Clear(); } } }