ObjectToIdCache.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.Runtime.Serialization
  5. {
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Runtime.CompilerServices;
  9. using System.Security;
  10. class ObjectToIdCache
  11. {
  12. internal int m_currentCount;
  13. internal int[] m_ids;
  14. internal Object[] m_objs;
  15. bool[] m_isWrapped;
  16. public ObjectToIdCache()
  17. {
  18. m_currentCount = 1;
  19. m_ids = new int[GetPrime(1)];
  20. m_objs = new Object[m_ids.Length];
  21. m_isWrapped = new bool[m_ids.Length];
  22. }
  23. public int GetId(object obj, ref bool newId)
  24. {
  25. bool isEmpty, isWrapped;
  26. int position = FindElement(obj, out isEmpty, out isWrapped);
  27. if (!isEmpty)
  28. {
  29. newId = false;
  30. return m_ids[position];
  31. }
  32. if (!newId)
  33. return -1;
  34. int id = m_currentCount++;
  35. m_objs[position] = obj;
  36. m_ids[position] = id;
  37. m_isWrapped[position] = isWrapped;
  38. if (m_currentCount >= (m_objs.Length - 1))
  39. Rehash();
  40. return id;
  41. }
  42. #if NotUsed
  43. public bool Remove(object obj)
  44. {
  45. bool isEmpty;
  46. int position = FindElement(obj, out isEmpty);
  47. if(isEmpty)
  48. return false;
  49. RemoveAt(position);
  50. return true;
  51. }
  52. #endif
  53. // (oldObjId, oldObj-id, newObj-newObjId) => (oldObj-oldObjId, newObj-id, newObjId )
  54. public int ReassignId(int oldObjId, object oldObj, object newObj)
  55. {
  56. bool isEmpty, isWrapped;
  57. int position = FindElement(oldObj, out isEmpty, out isWrapped);
  58. if (isEmpty)
  59. return 0;
  60. int id = m_ids[position];
  61. if (oldObjId > 0)
  62. m_ids[position] = oldObjId;
  63. else
  64. RemoveAt(position);
  65. position = FindElement(newObj, out isEmpty, out isWrapped);
  66. int newObjId = 0;
  67. if (!isEmpty)
  68. newObjId = m_ids[position];
  69. m_objs[position] = newObj;
  70. m_ids[position] = id;
  71. m_isWrapped[position] = isWrapped;
  72. return newObjId;
  73. }
  74. private int FindElement(object obj, out bool isEmpty, out bool isWrapped)
  75. {
  76. isWrapped = false;
  77. int position = ComputeStartPosition(obj);
  78. for (int i = position; i != (position - 1); i++)
  79. {
  80. if (m_objs[i] == null)
  81. {
  82. isEmpty = true;
  83. return i;
  84. }
  85. if (m_objs[i] == obj)
  86. {
  87. isEmpty = false;
  88. return i;
  89. }
  90. if (i == (m_objs.Length - 1))
  91. {
  92. isWrapped = true;
  93. i = -1;
  94. }
  95. }
  96. // m_obj must ALWAYS have atleast one slot empty (null).
  97. Fx.Assert("Object table overflow");
  98. throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ObjectTableOverflow)));
  99. }
  100. private void RemoveAt(int position)
  101. {
  102. int cacheSize = m_objs.Length;
  103. int lastVacantPosition = position;
  104. for (int next = (position == cacheSize - 1) ? 0 : position + 1; next != position; next++)
  105. {
  106. if (m_objs[next] == null)
  107. {
  108. m_objs[lastVacantPosition] = null;
  109. m_ids[lastVacantPosition] = 0;
  110. m_isWrapped[lastVacantPosition] = false;
  111. return;
  112. }
  113. int nextStartPosition = ComputeStartPosition(m_objs[next]);
  114. // If we wrapped while placing an object, then it must be that the start position wasn't wrapped to begin with
  115. bool isNextStartPositionWrapped = next < position && !m_isWrapped[next];
  116. bool isLastVacantPositionWrapped = lastVacantPosition < position;
  117. // We want to avoid moving objects in the cache if the next bucket position is wrapped, but the last vacant position isn't
  118. // and we want to make sure to move objects in the cache when the last vacant position is wrapped but the next bucket position isn't
  119. if ((nextStartPosition <= lastVacantPosition && !(isNextStartPositionWrapped && !isLastVacantPositionWrapped)) ||
  120. (isLastVacantPositionWrapped && !isNextStartPositionWrapped))
  121. {
  122. m_objs[lastVacantPosition] = m_objs[next];
  123. m_ids[lastVacantPosition] = m_ids[next];
  124. // A wrapped object might become unwrapped if it moves from the front of the array to the end of the array
  125. m_isWrapped[lastVacantPosition] = m_isWrapped[next] && next > lastVacantPosition;
  126. lastVacantPosition = next;
  127. }
  128. if (next == (cacheSize - 1))
  129. {
  130. next = -1;
  131. }
  132. }
  133. // m_obj must ALWAYS have atleast one slot empty (null).
  134. Fx.Assert("Object table overflow");
  135. throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ObjectTableOverflow)));
  136. }
  137. private int ComputeStartPosition(object o)
  138. {
  139. return (RuntimeHelpers.GetHashCode(o) & 0x7FFFFFFF) % m_objs.Length;
  140. }
  141. private void Rehash()
  142. {
  143. int size = GetPrime(m_objs.Length * 2);
  144. int[] oldIds = m_ids;
  145. object[] oldObjs = m_objs;
  146. m_ids = new int[size];
  147. m_objs = new Object[size];
  148. m_isWrapped = new bool[size];
  149. for (int j = 0; j < oldObjs.Length; j++)
  150. {
  151. object obj = oldObjs[j];
  152. if (obj != null)
  153. {
  154. bool found, isWrapped;
  155. int position = FindElement(obj, out found, out isWrapped);
  156. m_objs[position] = obj;
  157. m_ids[position] = oldIds[j];
  158. m_isWrapped[position] = isWrapped;
  159. }
  160. }
  161. }
  162. static int GetPrime(int min)
  163. {
  164. for (int i = 0; i < primes.Length; i++)
  165. {
  166. int prime = primes[i];
  167. if (prime >= min) return prime;
  168. }
  169. //outside of our predefined table.
  170. //compute the hard way.
  171. for (int i = (min | 1); i < Int32.MaxValue; i += 2)
  172. {
  173. if (IsPrime(i))
  174. return i;
  175. }
  176. return min;
  177. }
  178. static bool IsPrime(int candidate)
  179. {
  180. if ((candidate & 1) != 0)
  181. {
  182. int limit = (int)Math.Sqrt(candidate);
  183. for (int divisor = 3; divisor <= limit; divisor += 2)
  184. {
  185. if ((candidate % divisor) == 0)
  186. return false;
  187. }
  188. return true;
  189. }
  190. return (candidate == 2);
  191. }
  192. [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Static fields are marked SecurityCritical or readonly to prevent"
  193. + " data from being modified or leaked to other components in appdomain.")]
  194. internal static readonly int[] primes =
  195. {
  196. 3, 7, 17, 37, 89, 197, 431, 919, 1931, 4049, 8419, 17519, 36353,
  197. 75431, 156437, 324449, 672827, 1395263, 2893249, 5999471,
  198. };
  199. }
  200. }