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