/// <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); }
/// <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); }
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); }
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); }
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 }
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); }
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)); }
private int GetMainPosition(CompactValue key) { int hash = key.GetHashCode(); return((hash & 0x7FFFFFFF) % nodes.Length); }
public bool Equals(CompactValue other) { return(Equals(this, other)); }