NetObjectCache.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. using System;
  2. using System.Collections;
  3. using ProtoBuf.Meta;
  4. namespace ProtoBuf
  5. {
  6. internal sealed class NetObjectCache
  7. {
  8. internal const int Root = 0;
  9. private MutableList underlyingList;
  10. private MutableList List
  11. {
  12. get
  13. {
  14. if (underlyingList == null) underlyingList = new MutableList();
  15. return underlyingList;
  16. }
  17. }
  18. internal object GetKeyedObject(int key)
  19. {
  20. if (key-- == Root)
  21. {
  22. if (rootObject == null) throw new ProtoException("No root object assigned");
  23. return rootObject;
  24. }
  25. BasicList list = List;
  26. if (key < 0 || key >= list.Count)
  27. {
  28. Helpers.DebugWriteLine("Missing key: " + key);
  29. throw new ProtoException("Internal error; a missing key occurred");
  30. }
  31. object tmp = list[key];
  32. if (tmp == null)
  33. {
  34. throw new ProtoException("A deferred key does not have a value yet");
  35. }
  36. return tmp;
  37. }
  38. internal void SetKeyedObject(int key, object value)
  39. {
  40. if (key-- == Root)
  41. {
  42. if (value == null) throw new ArgumentNullException("value");
  43. if (rootObject != null && ((object)rootObject != (object)value)) throw new ProtoException("The root object cannot be reassigned");
  44. rootObject = value;
  45. }
  46. else
  47. {
  48. MutableList list = List;
  49. if (key < list.Count)
  50. {
  51. object oldVal = list[key];
  52. if (oldVal == null)
  53. {
  54. list[key] = value;
  55. }
  56. else if (!ReferenceEquals(oldVal, value) )
  57. {
  58. throw new ProtoException("Reference-tracked objects cannot change reference");
  59. } // otherwise was the same; nothing to do
  60. }
  61. else if (key != list.Add(value))
  62. {
  63. throw new ProtoException("Internal error; a key mismatch occurred");
  64. }
  65. }
  66. }
  67. private object rootObject;
  68. internal int AddObjectKey(object value, out bool existing)
  69. {
  70. if (value == null) throw new ArgumentNullException("value");
  71. if ((object)value == (object)rootObject) // (object) here is no-op, but should be
  72. { // preserved even if this was typed - needs ref-check
  73. existing = true;
  74. return Root;
  75. }
  76. string s = value as string;
  77. BasicList list = List;
  78. int index;
  79. #if NO_GENERICS
  80. if(s == null)
  81. {
  82. if (objectKeys == null)
  83. {
  84. objectKeys = new ReferenceHashtable();
  85. index = -1;
  86. }
  87. else
  88. {
  89. object tmp = objectKeys[value];
  90. index = tmp == null ? -1 : (int) tmp;
  91. }
  92. }
  93. else
  94. {
  95. if (stringKeys == null)
  96. {
  97. stringKeys = new Hashtable();
  98. index = -1;
  99. }
  100. else
  101. {
  102. object tmp = stringKeys[s];
  103. index = tmp == null ? -1 : (int) tmp;
  104. }
  105. }
  106. #else
  107. if(s == null)
  108. {
  109. #if CF || PORTABLE // CF has very limited proper object ref-tracking; so instead, we'll search it the hard way
  110. index = list.IndexOfReference(value);
  111. #else
  112. if (objectKeys == null)
  113. {
  114. objectKeys = new System.Collections.Generic.Dictionary<object, int>(ReferenceComparer.Default);
  115. index = -1;
  116. }
  117. else
  118. {
  119. if (!objectKeys.TryGetValue(value, out index)) index = -1;
  120. }
  121. #endif
  122. }
  123. else
  124. {
  125. if (stringKeys == null)
  126. {
  127. stringKeys = new System.Collections.Generic.Dictionary<string, int>();
  128. index = -1;
  129. }
  130. else
  131. {
  132. if (!stringKeys.TryGetValue(s, out index)) index = -1;
  133. }
  134. }
  135. #endif
  136. if (!(existing = index >= 0))
  137. {
  138. index = list.Add(value);
  139. if (s == null)
  140. {
  141. #if !CF && !PORTABLE // CF can't handle the object keys very well
  142. objectKeys.Add(value, index);
  143. #endif
  144. }
  145. else
  146. {
  147. stringKeys.Add(s, index);
  148. }
  149. }
  150. return index + 1;
  151. }
  152. private int trapStartIndex; // defaults to 0 - optimization for RegisterTrappedObject
  153. // to make it faster at seeking to find deferred-objects
  154. internal void RegisterTrappedObject(object value)
  155. {
  156. if (rootObject == null)
  157. {
  158. rootObject = value;
  159. }
  160. else
  161. {
  162. if(underlyingList != null)
  163. {
  164. for (int i = trapStartIndex; i < underlyingList.Count; i++)
  165. {
  166. trapStartIndex = i + 1; // things never *become* null; whether or
  167. // not the next item is null, it will never
  168. // need to be checked again
  169. if(underlyingList[i] == null)
  170. {
  171. underlyingList[i] = value;
  172. break;
  173. }
  174. }
  175. }
  176. }
  177. }
  178. #if NO_GENERICS
  179. private ReferenceHashtable objectKeys;
  180. private System.Collections.Hashtable stringKeys;
  181. private class ReferenceHashtable : System.Collections.Hashtable
  182. {
  183. protected override int GetHash(object key)
  184. {
  185. return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(key);
  186. }
  187. protected override bool KeyEquals(object item, object key)
  188. {
  189. return item == key;
  190. }
  191. }
  192. #else
  193. private System.Collections.Generic.Dictionary<string, int> stringKeys;
  194. #if !CF && !PORTABLE // CF lacks the ability to get a robust reference-based hash-code, so we'll do it the harder way instead
  195. private System.Collections.Generic.Dictionary<object, int> objectKeys;
  196. private sealed class ReferenceComparer : System.Collections.Generic.IEqualityComparer<object>
  197. {
  198. public readonly static ReferenceComparer Default = new ReferenceComparer();
  199. private ReferenceComparer() {}
  200. bool System.Collections.Generic.IEqualityComparer<object>.Equals(object x, object y)
  201. {
  202. return x == y; // ref equality
  203. }
  204. int System.Collections.Generic.IEqualityComparer<object>.GetHashCode(object obj)
  205. {
  206. return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
  207. }
  208. }
  209. #endif
  210. #endif
  211. internal void Clear()
  212. {
  213. trapStartIndex = 0;
  214. rootObject = null;
  215. if (underlyingList != null) underlyingList.Clear();
  216. if (stringKeys != null) stringKeys.Clear();
  217. #if !CF && !PORTABLE
  218. if (objectKeys != null) objectKeys.Clear();
  219. #endif
  220. }
  221. }
  222. }