NetObjectCache.cs 7.7 KB

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