// // Dictionary.cc // // Dictionary: This class provides an object lookup table. // Each object in the dictionary is indexed with a string. // The objects can be returned by mentioning their // string index. // // Part of the ht://Dig package // Copyright (c) 1995-2004 The ht://Dig Group // For copyright details, see the file COPYING in your distribution // or the GNU Library General Public License (LGPL) version 2 or later // // // $Id: Dictionary.cc,v 1.16 2004/05/28 13:15:20 lha Exp $ // #ifdef HAVE_CONFIG_H #include "htconfig.h" #endif /* HAVE_CONFIG_H */ #include "Dictionary.h" #include class DictionaryEntry { public: unsigned int hash; char *key; Object *value; DictionaryEntry *next; ~DictionaryEntry(); void release(); }; DictionaryEntry::~DictionaryEntry() { free(key); delete value; } void DictionaryEntry::release() { value = NULL; // Prevent the value from being deleted } //********************************************************************* // Dictionary::Dictionary() { init(101, 10.0f); } Dictionary::Dictionary(int initialCapacity, float loadFactor) { init(initialCapacity, loadFactor); } Dictionary::Dictionary(int initialCapacity) { init(initialCapacity, 0.75f); } Dictionary::Dictionary(const Dictionary& other) { init(other.initialCapacity, other.loadFactor); DictionaryCursor cursor; const char* key; for(other.Start_Get(cursor); (key = other.Get_Next(cursor));) { Add(key, other[key]); } } //********************************************************************* // Dictionary::~Dictionary() { Destroy(); delete [] table; } //********************************************************************* // void Dictionary::Destroy() { DictionaryEntry *t, *n; for (int i = 0; i < tableLength; i++) { if (table[i] != NULL) { t = table[i]; do { // clear out hash chain n = t->next; delete t; t = n; } while (n); table[i] = NULL; } } count = 0; } //********************************************************************* // void Dictionary::Release() { DictionaryEntry *t, *n; for (int i = 0; i < tableLength; i++) { if (table[i] != NULL) { t = table[i]; do { // clear out hash chain n = t->next; t->release(); delete t; t = n; } while (n); table[i] = NULL; } } count = 0; } //********************************************************************* // void Dictionary::init(int initialCapacity, float loadFactor) { if (initialCapacity <= 0) initialCapacity = 101; if (loadFactor <= 0.0) loadFactor = 0.75f; Dictionary::loadFactor = loadFactor; table = new DictionaryEntry*[initialCapacity]; for (int i = 0; i < initialCapacity; i++) { table[i] = NULL; } threshold = (int)(initialCapacity * loadFactor); tableLength = initialCapacity; count = 0; } //********************************************************************* // unsigned int Dictionary::hashCode(const char *key) const { char *test; long conv_key = strtol(key, &test, 10); if (key && *key && !*test) // Conversion succeeded return conv_key; char *base = (char*)malloc(strlen(key) + 2); char *tmp_key = base; strcpy(tmp_key, key); unsigned int h = 0; int length = strlen(tmp_key); if (length >= 16) { tmp_key += strlen(tmp_key) - 15; length = strlen(tmp_key); } for (int i = length; i > 0; i--) { h = (h*37) + *tmp_key++; } free(base); return h; } //********************************************************************* // Add an entry to the hash table. This will replace the // data associated with an already existing key. // void Dictionary::Add(const String& name, Object *obj) { unsigned int hash = hashCode(name); int index = hash % tableLength; DictionaryEntry *e; for (e = table[index]; e != NULL; e = e->next) { if (e->hash == hash && strcmp(e->key, name) == 0) { delete e->value; e->value = obj; return; } } if (count >= threshold) { rehash(); Add(name, obj); return; } e = new DictionaryEntry(); e->hash = hash; e->key = strdup(name); e->value = obj; e->next = table[index]; table[index] = e; count++; } //********************************************************************* // Remove an entry from the hash table. // int Dictionary::Remove(const String& name) { if (!count) return 0; unsigned int hash = hashCode(name); int index = hash % tableLength; DictionaryEntry *e, *prev; for (e = table[index], prev = NULL; e != NULL; prev = e, e = e->next) { if (hash == e->hash && strcmp(e->key, name) == 0) { if (prev != NULL) { prev->next = e->next; } else { table[index] = e->next; } count--; delete e; return 1; } } return 0; } //********************************************************************* // Object *Dictionary::Find(const String& name) const { if (!count) return NULL; unsigned int hash = hashCode(name); int index = hash % tableLength; DictionaryEntry *e; for (e = table[index]; e != NULL; e = e->next) { if (e->hash == hash && strcmp(e->key, name) == 0) { return e->value; } } return NULL; } //********************************************************************* // Object *Dictionary::operator[](const String& name) const { return Find(name); } //********************************************************************* // int Dictionary::Exists(const String& name) const { if (!count) return 0; unsigned int hash = hashCode(name); int index = hash % tableLength; DictionaryEntry *e; for (e = table[index]; e != NULL; e = e->next) { if (e->hash == hash && strcmp(e->key, name) == 0) { return 1; } } return 0; } //********************************************************************* // void Dictionary::rehash() { DictionaryEntry **oldTable = table; int oldCapacity = tableLength; int newCapacity; DictionaryEntry *e; int i, index; newCapacity = count > oldCapacity ? count * 2 + 1 : oldCapacity * 2 + 1; DictionaryEntry **newTable = new DictionaryEntry*[newCapacity]; for (i = 0; i < newCapacity; i++) { newTable[i] = NULL; } threshold = (int) (newCapacity * loadFactor); table = newTable; tableLength = newCapacity; for (i = oldCapacity; i-- > 0;) { for (DictionaryEntry *old = oldTable[i]; old != NULL;) { e = old; old = old->next; index = e->hash % newCapacity; e->next = newTable[index]; newTable[index] = e; } } delete [] oldTable; } //********************************************************************* // void Dictionary::Start_Get(DictionaryCursor& cursor) const { cursor.currentTableIndex = -1; cursor.currentDictionaryEntry = NULL; } //********************************************************************* // char * Dictionary::Get_Next(DictionaryCursor& cursor) const { while (cursor.currentDictionaryEntry == NULL || cursor.currentDictionaryEntry->next == NULL) { cursor.currentTableIndex++; if (cursor.currentTableIndex >= tableLength) { cursor.currentTableIndex--; return NULL; } cursor.currentDictionaryEntry = table[cursor.currentTableIndex]; if (cursor.currentDictionaryEntry != NULL) { return cursor.currentDictionaryEntry->key; } } cursor.currentDictionaryEntry = cursor.currentDictionaryEntry->next; return cursor.currentDictionaryEntry->key; } //********************************************************************* // Object * Dictionary::Get_NextElement(DictionaryCursor& cursor) const { while (cursor.currentDictionaryEntry == NULL || cursor.currentDictionaryEntry->next == NULL) { cursor.currentTableIndex++; if (cursor.currentTableIndex >= tableLength) { cursor.currentTableIndex--; return NULL; } cursor.currentDictionaryEntry = table[cursor.currentTableIndex]; if (cursor.currentDictionaryEntry != NULL) { return cursor.currentDictionaryEntry->value; } } cursor.currentDictionaryEntry = cursor.currentDictionaryEntry->next; return cursor.currentDictionaryEntry->value; }