File: System\Runtime\Serialization\ObjectIDGenerator.cs
Web Access
Project: src\src\libraries\System.Runtime.Serialization.Formatters\src\System.Runtime.Serialization.Formatters.csproj (System.Runtime.Serialization.Formatters)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Runtime.CompilerServices;
 
namespace System.Runtime.Serialization
{
    [Obsolete(Obsoletions.LegacyFormatterMessage, DiagnosticId = Obsoletions.LegacyFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
    public class ObjectIDGenerator
    {
        private const int NumBins = 4;
 
        internal int _currentCount;
        private int _currentSize;
        private long[] _ids;
        private object[] _objs;
 
        // Constructs a new ObjectID generator, initializing all of the necessary variables.
        public ObjectIDGenerator()
        {
            _currentCount = 1;
            _currentSize = 3; // HashHelpers.Primes[0]
            _ids = new long[_currentSize * NumBins];
            _objs = new object[_currentSize * NumBins];
        }
 
        // Determines where element obj lives, or should live,
        // within the table. It calculates the hashcode and searches all of the
        // bins where the given object could live.  If it's not found within the bin,
        // we rehash and go look for it in another bin.  If we find the object, we
        // set found to true and return it's position.  If we can't find the object,
        // we set found to false and return the position where the object should be inserted.
        private int FindElement(object obj, out bool found)
        {
            int hashcode = RuntimeHelpers.GetHashCode(obj);
            int hashIncrement = (1 + ((hashcode & 0x7FFFFFFF) % (_currentSize - 2)));
            do
            {
                int pos = ((hashcode & 0x7FFFFFFF) % _currentSize) * NumBins;
                for (int i = pos; i < pos + NumBins; i++)
                {
                    if (_objs[i] == null)
                    {
                        found = false;
                        return i;
                    }
                    if (_objs[i] == obj)
                    {
                        found = true;
                        return i;
                    }
                }
                hashcode += hashIncrement;
                //the seemingly infinite loop must be revisited later. Currently it is assumed that
                //always the array will be expanded (Rehash) when it is half full
            } while (true);
        }
 
        // Gets the id for a particular object, generating one if needed.  GetID calls
        // FindElement to find out where the object lives or should live.  If we didn't
        // find the element, we generate an object id for it and insert the pair into the
        // table.  We return an Int64 for the object id.  The out parameter firstTime
        // is set to true if this is the first time that we have seen this object.
        public virtual long GetId(object obj, out bool firstTime)
        {
            ArgumentNullException.ThrowIfNull(obj);
 
            bool found;
            int pos = FindElement(obj, out found);
 
            //We pull out foundID so that rehashing doesn't cause us to lose track of the id that we just found.
            long foundID;
            if (!found)
            {
                //We didn't actually find the object, so we should need to insert it into
                //the array and assign it an object id.
                _objs[pos] = obj;
                _ids[pos] = _currentCount++;
                foundID = _ids[pos];
                if (_currentCount > (_currentSize * NumBins) / 2)
                {
                    Rehash();
                }
            }
            else
            {
                foundID = _ids[pos];
            }
            firstTime = !found;
 
            return foundID;
        }
 
        // Checks to see if obj has already been assigned an id.  If it has,
        // we return that id, otherwise we return 0.
        public virtual long HasId(object obj, out bool firstTime)
        {
            ArgumentNullException.ThrowIfNull(obj);
 
            bool found;
            int pos = FindElement(obj, out found);
            if (found)
            {
                firstTime = false;
                return _ids[pos];
            }
 
            firstTime = true;
            return 0;
        }
 
        // Rehashes the table by finding the next larger size in the list provided,
        // allocating two new arrays of that size and rehashing all of the elements in
        // the old arrays into the new ones.  Expensive but necessary.
        private void Rehash()
        {
            int currSize = _currentSize;
            int newSize = HashHelpers.ExpandPrime(currSize);
            if (newSize == currSize)
            {
                // We just walked off the end of the array.
                throw new SerializationException(SR.Serialization_TooManyElements);
            }
            _currentSize = newSize;
 
            long[] newIds = new long[_currentSize * NumBins];
            object[] newObjs = new object[_currentSize * NumBins];
 
            long[] oldIds = _ids;
            object[] oldObjs = _objs;
 
            _ids = newIds;
            _objs = newObjs;
 
            for (int j = 0; j < oldObjs.Length; j++)
            {
                if (oldObjs[j] != null)
                {
                    int pos = FindElement(oldObjs[j], out _);
                    _objs[pos] = oldObjs[j];
                    _ids[pos] = oldIds[j];
                }
            }
        }
    }
}