BasicUsageEnvironment/BasicHashTable.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // Copyright (c) 1996-2013 Live Networks, Inc.  All rights reserved.
00017 // Basic Hash Table implementation
00018 // Implementation
00019 
00020 #include "BasicHashTable.hh"
00021 #include "strDup.hh"
00022 
00023 #if defined(__WIN32__) || defined(_WIN32)
00024 #else
00025 #include <stddef.h>
00026 #endif
00027 #include <string.h>
00028 #include <stdio.h>
00029 
00030 // When there are this many entries per bucket, on average, rebuild
00031 // the table to increase the number of buckets
00032 #define REBUILD_MULTIPLIER 3
00033 
00034 BasicHashTable::BasicHashTable(int keyType)
00035   : fBuckets(fStaticBuckets), fNumBuckets(SMALL_HASH_TABLE_SIZE),
00036     fNumEntries(0), fRebuildSize(SMALL_HASH_TABLE_SIZE*REBUILD_MULTIPLIER),
00037     fDownShift(28), fMask(0x3), fKeyType(keyType) {
00038   for (unsigned i = 0; i < SMALL_HASH_TABLE_SIZE; ++i) {
00039     fStaticBuckets[i] = NULL;
00040   }
00041 }
00042 
00043 BasicHashTable::~BasicHashTable() {
00044   // Free all the entries in the table:
00045   for (unsigned i = 0; i < fNumBuckets; ++i) {
00046     TableEntry* entry;
00047     while ((entry = fBuckets[i]) != NULL) {
00048       deleteEntry(i, entry);
00049     }
00050   }
00051 
00052   // Also free the bucket array, if it was dynamically allocated:
00053   if (fBuckets != fStaticBuckets) delete[] fBuckets;
00054 }
00055 
00056 void* BasicHashTable::Add(char const* key, void* value) {
00057   void* oldValue;
00058   unsigned index;
00059   TableEntry* entry = lookupKey(key, index);
00060   if (entry != NULL) {
00061     // There's already an item with this key
00062     oldValue = entry->value;
00063   } else {
00064     // There's no existing entry; create a new one:
00065     entry = insertNewEntry(index, key);
00066     oldValue = NULL;
00067   }
00068   entry->value = value;
00069 
00070   // If the table has become too large, rebuild it with more buckets:
00071   if (fNumEntries >= fRebuildSize) rebuild();
00072 
00073   return oldValue;
00074 }
00075 
00076 Boolean BasicHashTable::Remove(char const* key) {
00077   unsigned index;
00078   TableEntry* entry = lookupKey(key, index);
00079   if (entry == NULL) return False; // no such entry
00080 
00081   deleteEntry(index, entry);
00082 
00083   return True;
00084 }
00085 
00086 void* BasicHashTable::Lookup(char const* key) const {
00087   unsigned index;
00088   TableEntry* entry = lookupKey(key, index);
00089   if (entry == NULL) return NULL; // no such entry
00090 
00091   return entry->value;
00092 }
00093 
00094 unsigned BasicHashTable::numEntries() const {
00095   return fNumEntries;
00096 }
00097 
00098 BasicHashTable::Iterator::Iterator(BasicHashTable const& table)
00099   : fTable(table), fNextIndex(0), fNextEntry(NULL) {
00100 }
00101 
00102 void* BasicHashTable::Iterator::next(char const*& key) {
00103   while (fNextEntry == NULL) {
00104     if (fNextIndex >= fTable.fNumBuckets) return NULL;
00105 
00106     fNextEntry = fTable.fBuckets[fNextIndex++];
00107   }
00108 
00109   BasicHashTable::TableEntry* entry = fNextEntry;
00110   fNextEntry = entry->fNext;
00111 
00112   key = entry->key;
00113   return entry->value;
00114 }
00115 
00117 
00118 HashTable* HashTable::create(int keyType) {
00119   return new BasicHashTable(keyType);
00120 }
00121 
00122 HashTable::Iterator* HashTable::Iterator::create(HashTable const& hashTable) {
00123   // "hashTable" is assumed to be a BasicHashTable
00124   return new BasicHashTable::Iterator((BasicHashTable const&)hashTable);
00125 }
00126 
00128 
00129 BasicHashTable::TableEntry* BasicHashTable
00130 ::lookupKey(char const* key, unsigned& index) const {
00131   TableEntry* entry;
00132   index = hashIndexFromKey(key);
00133 
00134   for (entry = fBuckets[index]; entry != NULL; entry = entry->fNext) {
00135     if (keyMatches(key, entry->key)) break;
00136   }
00137 
00138   return entry;
00139 }
00140 
00141 Boolean BasicHashTable
00142 ::keyMatches(char const* key1, char const* key2) const {
00143   // The way we check the keys for a match depends upon their type:
00144   if (fKeyType == STRING_HASH_KEYS) {
00145     return (strcmp(key1, key2) == 0);
00146   } else if (fKeyType == ONE_WORD_HASH_KEYS) {
00147     return (key1 == key2);
00148   } else {
00149     unsigned* k1 = (unsigned*)key1;
00150     unsigned* k2 = (unsigned*)key2;
00151 
00152     for (int i = 0; i < fKeyType; ++i) {
00153       if (k1[i] != k2[i]) return False; // keys differ
00154     }
00155     return True;
00156   }
00157 }
00158 
00159 BasicHashTable::TableEntry* BasicHashTable
00160 ::insertNewEntry(unsigned index, char const* key) {
00161   TableEntry* entry = new TableEntry();
00162   entry->fNext = fBuckets[index];
00163   fBuckets[index] = entry;
00164 
00165   ++fNumEntries;
00166   assignKey(entry, key);
00167 
00168   return entry;
00169 }
00170 
00171 void BasicHashTable::assignKey(TableEntry* entry, char const* key) {
00172   // The way we assign the key depends upon its type:
00173   if (fKeyType == STRING_HASH_KEYS) {
00174     entry->key = strDup(key);
00175   } else if (fKeyType == ONE_WORD_HASH_KEYS) {
00176     entry->key = key;
00177   } else if (fKeyType > 0) {
00178     unsigned* keyFrom = (unsigned*)key;
00179     unsigned* keyTo = new unsigned[fKeyType];
00180     for (int i = 0; i < fKeyType; ++i) keyTo[i] = keyFrom[i];
00181 
00182     entry->key = (char const*)keyTo;
00183   }
00184 }
00185 
00186 void BasicHashTable::deleteEntry(unsigned index, TableEntry* entry) {
00187   TableEntry** ep = &fBuckets[index];
00188 
00189   Boolean foundIt = False;
00190   while (*ep != NULL) {
00191     if (*ep == entry) {
00192       foundIt = True;
00193       *ep = entry->fNext;
00194       break;
00195     }
00196     ep = &((*ep)->fNext);
00197   }
00198 
00199   if (!foundIt) { // shouldn't happen
00200 #ifdef DEBUG
00201     fprintf(stderr, "BasicHashTable[%p]::deleteEntry(%d,%p): internal error - not found (first entry %p", this, index, entry, fBuckets[index]);
00202     if (fBuckets[index] != NULL) fprintf(stderr, ", next entry %p", fBuckets[index]->fNext);
00203     fprintf(stderr, ")\n");
00204 #endif
00205   }
00206 
00207   --fNumEntries;
00208   deleteKey(entry);
00209   delete entry;
00210 }
00211 
00212 void BasicHashTable::deleteKey(TableEntry* entry) {
00213   // The way we delete the key depends upon its type:
00214   if (fKeyType == ONE_WORD_HASH_KEYS) {
00215     entry->key = NULL;
00216   } else {
00217     delete[] (char*)entry->key;
00218     entry->key = NULL;
00219   }
00220 }
00221 
00222 void BasicHashTable::rebuild() {
00223   // Remember the existing table size:
00224   unsigned oldSize = fNumBuckets;
00225   TableEntry** oldBuckets = fBuckets;
00226 
00227   // Create the new sized table:
00228   fNumBuckets *= 4;
00229   fBuckets = new TableEntry*[fNumBuckets];
00230   for (unsigned i = 0; i < fNumBuckets; ++i) {
00231     fBuckets[i] = NULL;
00232   }
00233   fRebuildSize *= 4;
00234   fDownShift -= 2;
00235   fMask = (fMask<<2)|0x3;
00236 
00237   // Rehash the existing entries into the new table:
00238   for (TableEntry** oldChainPtr = oldBuckets; oldSize > 0;
00239        --oldSize, ++oldChainPtr) {
00240     for (TableEntry* hPtr = *oldChainPtr; hPtr != NULL;
00241          hPtr = *oldChainPtr) {
00242       *oldChainPtr = hPtr->fNext;
00243 
00244       unsigned index = hashIndexFromKey(hPtr->key);
00245 
00246       hPtr->fNext = fBuckets[index];
00247       fBuckets[index] = hPtr;
00248     }
00249   }
00250 
00251   // Free the old bucket array, if it was dynamically allocated:
00252   if (oldBuckets != fStaticBuckets) delete[] oldBuckets;
00253 }
00254 
00255 unsigned BasicHashTable::hashIndexFromKey(char const* key) const {
00256   unsigned result = 0;
00257 
00258   if (fKeyType == STRING_HASH_KEYS) {
00259     while (1) {
00260       char c = *key++;
00261       if (c == 0) break;
00262       result += (result<<3) + (unsigned)c;
00263     }
00264     result &= fMask;
00265   } else if (fKeyType == ONE_WORD_HASH_KEYS) {
00266     result = randomIndex((uintptr_t)key);
00267   } else {
00268     unsigned* k = (unsigned*)key;
00269     uintptr_t sum = 0;
00270     for (int i = 0; i < fKeyType; ++i) {
00271       sum += k[i];
00272     }
00273     result = randomIndex(sum);
00274   }
00275 
00276   return result;
00277 }

Generated on Mon Apr 29 13:28:00 2013 for live by  doxygen 1.5.2