Beispiel #1
0
        /// <summary>
        /// Finds the key's location in the table (returns 0 if not found).
        /// Note that the value at that key may be nil.
        /// </summary>
        internal int FindValue(CompactValue key)
        {
            if (key.Val == null)
            {
                throw new ArgumentNullException("key");
            }

            if (array != null)
            {
                int arrIdx = ValueToInt(key);
                if (arrIdx > 0 && arrIdx <= array.Length)
                {
                    return(arrIdx);
                }
            }

            int i = GetMainPosition(key);

            while (i != -1)
            {
                var node = nodes[i];
                if (node.Key.Equals(key))
                {
                    return(-(i + 1));
                }

                i = node.Next;
            }

            return(0);
        }
Beispiel #2
0
        /// <summary>
        /// Take care not to clone the returned value, as you may
        /// accidentally turn a number into a reference type!
        /// </summary>
        internal bool ReadValue(int loc, out CompactValue value)
        {
            if (loc == 0)
            {
                value = new CompactValue();
                return(false);
            }

            value = loc > 0 ? array[loc - 1] : nodes[-loc - 1].Value;
            return(true);
        }
Beispiel #3
0
        private static int ValueToInt(CompactValue key)
        {
            var asNum = key.Val as NumBox;

            if (asNum == null)
            {
                return(-1);
            }

            var num = asNum.Value;
            var ret = (int)num;

            return((double)ret == num ? ret : -1);
        }
Beispiel #4
0
        public static bool Equals(CompactValue a, CompactValue b)
        {
            if (a.Val == b.Val)
            {
                return(true);
            }

            var asNumA = a.Val as NumBox;

            if (asNumA != null)
            {
                var asNumB = b.Val as NumBox;
                return(asNumB != null && asNumA.Value == asNumB.Value);
            }

            var asStrA = a.Val as byte[];

            if (asStrA != null)
            {
                return(LString.InternalEquals(asStrA, b.Val as byte[]));
            }

            return(false);
        }
Beispiel #5
0
        internal void Resize(int numArraySlots, int numNodes)
        {
#if DEBUG
            Debug.Assert(!isResizing);
            isResizing = true;
#endif
            Debug.Assert(numArraySlots >= 0 && numNodes >= 0);

            var oldArray      = array;
            int oldArraySlots = oldArray != null ? oldArray.Length : 0;
            array = numArraySlots != 0 ? new CompactValue[numArraySlots] : null;

            var oldNodes = nodes;
            if (numNodes > 0)
            {
                int logNumNodes = Helpers.CeilLog2(numNodes);
                if (logNumNodes > MaxBits)
                {
                    throw new InvalidOperationException("Table overflow");
                }

                numNodes = 1 << logNumNodes;

                nodes = new Node[numNodes];
                for (int i = 0; i < nodes.Length; i++)
                {
                    nodes[i].Next = -1;
                }
            }
            else
            {
                nodes = EmptyNodes;
            }

            lastFreeNode = numNodes;

            //copy from the old array to the new

            int copyArraySlots = oldArraySlots < numArraySlots ? oldArraySlots : numArraySlots;

            if (copyArraySlots != 0)
            {
                Array.Copy(oldArray, array, copyArraySlots);
            }

            //move the remaining elements to the new nodes array

            if (oldArray != null)
            {
                //we know that none of these values will land in the array,
                //so we temporarily null it out in order to prevent InsertNewKey
                //from doing an unnecessary "does it land in the array part" check

                var newArr = array;
                array = null;

                for (int i = copyArraySlots; i < oldArray.Length; i++)
                {
                    var val = oldArray[i];
                    if (val.Val == null)
                    {
                        continue;
                    }

                    var key = new CompactValue(i);
                    var loc = InsertNewKey(key, true);

                    Debug.Assert(loc < 0);
                    nodes[-loc - 1].Value = val;
                }

                array    = newArr;
                oldArray = null;
            }

            //on to the nodes!

            for (int i = oldNodes.Length; i-- != 0;)
            {
                var node = oldNodes[i];
                if (node.Value.Val == null)
                {
                    continue;
                }

                int loc = InsertNewKey(node.Key, true);

                if (loc > 0)
                {
                    array[loc - 1] = node.Value;
                }
                else
                {
                    nodes[-loc - 1].Value = node.Value;
                }
            }

#if DEBUG
            isResizing = false;
#endif
        }
Beispiel #6
0
        private void Grow(CompactValue newKey)
        {
            var arrayHist = new int[MaxBits + 1];

            //count the used slots in the array part

            int numArrayKeys = 0;

            if (array != null)
            {
                for (int lg = 0, ttlg = 1, i = 1; lg <= MaxBits; lg++, ttlg *= 2)
                {
                    int lc  = 0;
                    int lim = ttlg;

                    if (lim > array.Length)
                    {
                        lim = array.Length;
                        if (i > lim)
                        {
                            break;
                        }
                    }

                    for ( ; i <= lim; i++)
                    {
                        if (array[i - 1].Val != null)
                        {
                            lc++;
                        }
                    }

                    arrayHist[lg] += lc;
                    numArrayKeys  += lc;
                }
            }

            //and sum up the rest

            int asIdx;

            int numNodeKeys = 0, numNodeIndexKeys = 0;

            for (int i = 0; i < nodes.Length; i++)
            {
                if (nodes[i].Value.Val == null)
                {
                    continue;
                }

                numNodeKeys++;

                asIdx = ValueToInt(nodes[i].Key);
                if (asIdx > 0 && asIdx <= MaxSize)
                {
                    arrayHist[Helpers.CeilLog2(asIdx)]++;
                    numNodeIndexKeys++;
                }
            }

            //and figure out the sizes

            int totalKeys      = numArrayKeys + numNodeKeys;
            int totalIndexKeys = numArrayKeys + numNodeIndexKeys;

            //including the new key

            totalKeys++;

            asIdx = ValueToInt(newKey);
            if (asIdx > 0 && asIdx <= MaxSize)
            {
                arrayHist[Helpers.CeilLog2(asIdx)]++;
                totalIndexKeys++;
            }

            //moving on...

            int newArrayLen = 0, newArrayKeys = 0;

            for (int i = 0, a = 0, twoToI = 1;
                 twoToI / 2 < totalIndexKeys;
                 i++, twoToI *= 2)
            {
                int hist = arrayHist[i];

                if (hist != 0)
                {
                    a += hist;

                    if (a > twoToI / 2)
                    {
                        newArrayLen  = twoToI;
                        newArrayKeys = a;
                    }
                }

                if (a == totalIndexKeys)
                {
                    //we've counted everything
                    break;
                }
            }

            Debug.Assert(newArrayLen / 2 <= newArrayKeys && newArrayKeys <= newArrayLen);

            //aaaaand, resize!

            Resize(newArrayLen, totalKeys - newArrayKeys);
        }
Beispiel #7
0
        internal int InsertNewKey(CompactValue key, bool isResizing = false)
        {
            if (key.Val == null)
            {
                throw new ArgumentNullException("key");
            }

            var asNumKey = key.Val as NumBox;

            if (asNumKey != null && double.IsNaN(asNumKey.Value))
            {
                throw new ArgumentException("key is NaN", "key");
            }

#if DEBUG
            Debug.Assert(this.isResizing == isResizing);
#endif

            if (nodes == EmptyNodes && !isResizing)
            {
                //the resizing case happens when we grow a
                //node-only table into an array-only table
                Grow(key);
            }

insert:

            if (array != null && asNumKey != null)
            {
                int asArrIdx = ValueToInt(asNumKey.Value);
                if (asArrIdx > 0 && asArrIdx <= array.Length)
                {
                    return(asArrIdx);
                }
            }

            int mainPos = GetMainPosition(key);
            if (nodes[mainPos].Value.Val != null)
            {
                //we've got a collision, need to handle it

                int freePos = GetFreePosition();
                if (freePos == -1)
                {
                    Grow(key);
                    goto insert;
                }

                Debug.Assert(nodes[freePos].Key.Val == null);
                Debug.Assert(nodes[freePos].Value.Val == null);
                Debug.Assert(nodes[freePos].Next == -1);

                int otherMainPos = GetMainPosition(nodes[mainPos].Key);
                if (mainPos == otherMainPos)
                {
                    //the colliding node is already in its main position
                    //this relegates the new node to the free position

                    nodes[freePos].Next = nodes[mainPos].Next;
                    nodes[mainPos].Next = freePos;

                    mainPos = freePos;
                }
                else
                {
                    //the colliding node isn't in its main position
                    //shove it into the free slot instead

                    //otherMainPos is the head of a chain that the
                    //colliding node was injected into, scan until
                    //we find the previous node in the chain (we will
                    //need to relink the nodes)

                    int otherPrevPos = otherMainPos;
                    for ( ; ;)
                    {
                        int next = nodes[otherPrevPos].Next;
                        if (next == mainPos)
                        {
                            break;
                        }
                        otherPrevPos = next;
                    }

                    //and relink

                    nodes[otherPrevPos].Next = freePos;
                    nodes[freePos]           = nodes[mainPos];           //takes next along for the ride
                    nodes[mainPos].Next      = -1;

                    nodes[mainPos].Value.Val = null;
                }
            }

            nodes[mainPos].Key = key;

            Debug.Assert(nodes[mainPos].Value.Val == null);

            return(-(mainPos + 1));
        }
Beispiel #8
0
        private int GetMainPosition(CompactValue key)
        {
            int hash = key.GetHashCode();

            return((hash & 0x7FFFFFFF) % nodes.Length);
        }
Beispiel #9
0
 public bool Equals(CompactValue other)
 {
     return(Equals(this, other));
 }