You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
417 lines
8.4 KiB
417 lines
8.4 KiB
//
|
|
// 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 <http://www.htdig.org/>
|
|
// 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
|
|
// <http://www.gnu.org/copyleft/lgpl.html>
|
|
//
|
|
// $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 <stdlib.h>
|
|
|
|
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;
|
|
}
|