/// <summary> /// Try to store the entry into its Hash2 location. If that /// fails then kick the entry in Hash2 to that entry's Hash2 /// location. Recurse until we either find an empty Hash2 slot /// or try to kick an entry that is already in its Hash2 /// location into its Hash2 location (which would cause an /// infinite loop). /// </summary> /// <param name="hashEntry">The hash entry to store.</param> /// <returns>true if the hash entry was stored at its Hash2 /// location; false if storing the hash entry at its Hash2 /// location would cause an infinite loop.</returns> private bool TryAssignAndKickOutIfNeeded(CuckooHashEntry hashEntry) { // Success is immediate if the Hash2 location is unused. if (!this.hashTable.ContainsKey(hashEntry.Hash2)) { this.NumValuesStoredAtSecondHash++; this.hashTable[hashEntry.Hash2] = hashEntry; return(true); } // Get the kicked out entry and fail if we have already seen // that entry. var kickedOutEntry = this.hashTable[hashEntry.Hash2]; if (kickedOutEntry.Seen) { return(false); } // Put the new word in the kicked out word's spot. this.hashTable[hashEntry.Hash2] = hashEntry; hashEntry.Seen = true; this.NumValuesStoredAtFirstHash--; this.NumValuesStoredAtSecondHash++; // Try to store the kicked out entry in its Hash2 location, // kicking out additional words as necessary. kickedOutEntry.Seen = true; return(this.TryAssignAndKickOutIfNeeded(kickedOutEntry)); }
/// <summary> /// Try to add a value to the hash table. The hash functions /// must be rebuilt if the add operation fails (and assuming /// that you want to be able to add this value to the table). /// The table must be cleared after rebuilding the hash /// functions if the same CuckooHashTable object will be reused. /// </summary> /// <param name="value">The value to add to the hash /// table.</param> /// <returns>true if the value was added to the table; false if /// trying to add the value caused an infinite loop.</returns> public bool TryAddValue(TValue value) { // Wrap the value in a CuckooHashEntry. var hashEntry = new CuckooHashEntry(this.chf1(value), this.chf2(value), value); // Try to put the value in its first position. if (!this.hashTable.ContainsKey(hashEntry.Hash1)) { this.hashTable[hashEntry.Hash1] = hashEntry; this.NumValuesStoredAtFirstHash++; } else { // We need to kick the entry in our Hash1 position into // its Hash2 position. This could cause other entries // to be kicked out of position, which could be an // infinite loop. As such, we track the seen state of // all of the entries and exit the loop if we try to // kick out an object that has already been seen. The // first step in this process is thus to clear the seen // state for all of the entries. foreach (var entry in this.hashTable.Values) { entry.Seen = false; } // Try to put the entry into its Hash2 location. If // that fails then kick the entry in Hash2 to that // entry's Hash2 location. Repeat until we either find // an empty Hash2 slot or try to kick an entry that is // already in its Hash2 location into its Hash2 location // (which would cause an infinite loop). if (!this.TryAssignAndKickOutIfNeeded(hashEntry)) { return(false); } } // We were successful in adding this entry to the hash table. return(true); }