// add overwrites public void Add(uint key, T value) { uint firstHash = GetHash(key); InnerHashTable innerHashed = inner[firstHash]; uint secondHash = innerHashed.GetHash(key); // overwrite if (innerHashed.table[secondHash] != null && innerHashed.table[secondHash].Value.Key == key) { throw new ArgumentException(); } count++; if (++pseudoCount > limit) { RehashAll(new KeyValuePair <uint, T>(key, value)); return; } // empty spot - just plop the value there and call it a day if (innerHashed.IsDeleted((int)secondHash)) { innerHashed.count++; innerHashed.table[secondHash] = new KeyValuePair <uint, T>(key, value); } // We've got collision now, do something about it else { // Note that original algorithm does this in a roundabout way due to their weird data structures if (++innerHashed.count <= innerHashed.limit) { // Rehash second level innerHashed.RehashWith(key, value, this, innerHashed.table, innerHashed.table.Length); } else { // Grow the second level uint newLimit = limit = 2 * Math.Max(1, innerHashed.limit); uint newSize = BitHacks.RoundToPower(2 * newLimit * (newLimit - 1)); innerHashed.limit = newLimit; innerHashed.width = BitHacks.Power2MSB(newSize); innerHashed.RehashWith(key, value, this, innerHashed.table, (int)newSize); } } }
/* * internal Func<uint, uint> GetRandomHashMethod(uint size) * { * System.Diagnostics.Debug.Assert(size == BitHacks.RoundToPower(size)); * uint a = (uint)random.Next(); * uint b = (uint)(random.Next(65536) << 16); * int shift = 31 - (int)BitHacks.Log2Ceiling(size); * // weird shifting because c# can't shift uint by more than 31 bits * return (x) => ((a * x + b) >> shift) >> 1; * } * */ private void RehashAll(KeyValuePair <uint, T>?newValue) { List <KeyValuePair <uint, T> > elements = new List <KeyValuePair <uint, T> >((int)(newValue == null ? pseudoCount : pseudoCount + 1)); if (inner != null) { int j = 0; foreach (InnerHashTable table in inner) { for (int i = 0; i < table.AllocatedSize; i++) { if (table.IsContained(i)) { elements.Add(table.table[i].Value); j++; } } } if (newValue.HasValue) { elements.Add(newValue.Value); } } pseudoCount = (uint)elements.Count; float newLimit = (1.0f + Fill) * Math.Max(pseudoCount, 4.0f); limit = (uint)newLimit; // hashSize = s(M) uint hashSize = BitHacks.RoundToPower(limit << 1); width = BitHacks.Power2MSB(hashSize); List <KeyValuePair <uint, T> >[] hashList = null; // find suitable higher level function for (bool injective = false; !injective;) { InitializeRandomHash(out a, out b); hashList = new List <KeyValuePair <uint, T> > [hashSize]; // initialize provisional list of elemnts going into second level table for (int i = 0; i < hashList.Length; i++) { hashList[i] = new List <KeyValuePair <uint, T> >(); } // run first level hashes foreach (var elm in elements) { hashList[GetHash(elm.Key)].Add(elm); } var testTable = new InnerHashTable[hashSize]; injective = SatisfiesMagicalCondition(hashList, limit); } // find suitable lower level function inner = new InnerHashTable[hashSize]; for (int i = 0; i < hashSize; i++) { // We deviate from original algorithm here, // if we've got empty second level we initialize it to size one to avoid out-of-bounds access in other functions. inner[i] = new InnerHashTable(Math.Max((uint)hashList[i].Count, 1)); if (hashList[i].Count == 0) { inner[i].count = 0; } while (true) { inner[i].Clear(); inner[i].InitializeRandomHash(this); for (int j = 0; j < hashList[i].Count; j++) { uint hash = inner[i].GetHash(hashList[i][j].Key); if (inner[i].IsContained((int)hash)) { // don't judge me goto Failed; } inner[i].table[hash] = hashList[i][j]; } break; Failed: continue; } } }