File: FrameworkFork\System.Runtime.Serialization\System\Runtime\Serialization\ObjectToIdCache.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Security;
 
 
namespace System.Runtime.Serialization
{
    internal class ObjectToIdCache
    {
        internal int m_currentCount;
        internal int[] m_ids;
        internal Object[] m_objs;
 
        public ObjectToIdCache()
        {
            m_currentCount = 1;
            m_ids = new int[GetPrime(1)];
            m_objs = new Object[m_ids.Length];
        }
 
        public int GetId(object obj, ref bool newId)
        {
            bool isEmpty;
            int pos = FindElement(obj, out isEmpty);
            if (!isEmpty)
            {
                newId = false;
                return m_ids[pos];
            }
            if (!newId)
                return -1;
            int id = m_currentCount++;
            m_objs[pos] = obj;
            m_ids[pos] = id;
            if (m_currentCount >= (m_objs.Length - 1))
                Rehash();
            return id;
        }
 
 
        // (oldObjId, oldObj-id, newObj-newObjId) => (oldObj-oldObjId, newObj-id, newObjId )
        public int ReassignId(int oldObjId, object oldObj, object newObj)
        {
            bool isEmpty;
            int pos = FindElement(oldObj, out isEmpty);
            if (isEmpty)
                return 0;
            int id = m_ids[pos];
            if (oldObjId > 0)
                m_ids[pos] = oldObjId;
            else
                RemoveAt(pos);
            pos = FindElement(newObj, out isEmpty);
            int newObjId = 0;
            if (!isEmpty)
                newObjId = m_ids[pos];
            m_objs[pos] = newObj;
            m_ids[pos] = id;
            return newObjId;
        }
 
        private int FindElement(object obj, out bool isEmpty)
        {
            int hashcode = RuntimeHelpers.GetHashCode(obj);
            int pos = ((hashcode & 0x7FFFFFFF) % m_objs.Length);
            for (int i = pos; i != (pos - 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))
                    i = -1;
            }
            // m_obj must ALWAYS have atleast one slot empty (null).
            DiagnosticUtility.DebugAssert("Object table overflow");
            throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(string.Format(SRSerialization.ObjectTableOverflow)));
        }
 
        private void RemoveAt(int pos)
        {
            int hashcode = RuntimeHelpers.GetHashCode(m_objs[pos]);
            for (int i = pos, j; i != (pos - 1); i = j)
            {
                j = (i + 1) % m_objs.Length;
                if (m_objs[j] == null || RuntimeHelpers.GetHashCode(m_objs[j]) != hashcode)
                {
                    m_objs[pos] = m_objs[i];
                    m_ids[pos] = m_ids[i];
                    m_objs[i] = null;
                    m_ids[i] = 0;
                    return;
                }
            }
            // m_obj must ALWAYS have atleast one slot empty (null).
            DiagnosticUtility.DebugAssert("Object table overflow");
            throw /*System.Runtime.Serialization.*/DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(string.Format(SRSerialization.ObjectTableOverflow)));
        }
 
        private void Rehash()
        {
            int size = GetPrime(m_objs.Length + 1); // The lookup does an inherent doubling
            int[] oldIds = m_ids;
            object[] oldObjs = m_objs;
            m_ids = new int[size];
            m_objs = new Object[size];
 
            for (int j = 0; j < oldObjs.Length; j++)
            {
                object obj = oldObjs[j];
                if (obj != null)
                {
                    bool found;
                    int pos = FindElement(obj, out found);
                    m_objs[pos] = obj;
                    m_ids[pos] = oldIds[j];
                }
            }
        }
 
        private static int GetPrime(int min)
        {
            for (int i = 0; i < primes.Length; i++)
            {
                int prime = primes[i];
                if (prime >= min) return prime;
            }
 
            return min;
        }
 
 
        /// <SecurityNote>
        /// Review - Static fields are marked SecurityCritical or readonly to prevent
        ///          data from being modified or leaked to other components in appdomain.
        /// </SecurityNote>
        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,
            11998949, 23997907, 47995853, 95991737, 191983481, 383966977, 767933981, 1535867969,
            2146435069, 0X7FEFFFFF
            // 0X7FEFFFFF is not prime, but it is the largest possible array size. There's nowhere to go from here.
        };
    }
}