/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #ifndef _HASH_TABLE_ #define _HASH_TABLE_ #include // for strdup, strcmp #include "Fundamentals.h" #include "DoublyLinkedList.h" #include "Pool.h" #include "Vector.h" #define _NBUCKETS_ 128 /* Operations on HashTable keys, when the keys are (non-interned) strings */ struct StringKeyOps { static bool equals(const char * userKey, const char * hashTableKey) { return (strcmp(userKey, hashTableKey) == 0); } static char *copyKey(Pool &pool, const char * userKey) { char *hashTableKey = new (pool) char[strlen(userKey)+1]; strcpy(hashTableKey, userKey); return hashTableKey; } static Uint32 hashCode(const char * userKey) { const char *ptr; Uint32 hashCode; for (hashCode = 0, ptr = userKey; *ptr != '\0'; ptr++) { hashCode = (hashCode * 7) + *ptr; } return hashCode; } }; typedef const char * DefaultKeyClass; template struct HashTableEntry : public DoublyLinkedEntry > { HashTableEntry(KEY k, N val):key(k),value(val) {}; KEY key; N value; }; template class HashTable { protected: /* Adds key and returns pointer to interned version of the key */ KEY add(KEY key, const N &value, int hashIndex); /* if found, returns the interned version of the key, NULL otherwise. * Returns hash index of bucket(key) via hashIndex, and * if value is non-null, returns matched value on success */ KEY get(KEY key, N *value, int &hashIndex) const; /* If found, sets the value corresponding to given key to N. Does * nothing if an entry corresponding to key was not found. */ void set(KEY key, N *value, int hashIndex); /* Function that will compare user's key to key in hash-table. * equals returns true if the userKey is "equal" to hashTableKey, * false otherwise. */ Uint32 hashIndexOf(KEY key) const; DoublyLinkedList > buckets[_NBUCKETS_]; Pool &pool; /* Pool used to allocate hash-table entries */ private: HashTable(const HashTable&); // Copying forbidden void operator=(const HashTable&); // Copying forbidden public: explicit HashTable(Pool &p): pool(p) {} void add(KEY key, const N &value) { (void) add(key, value, hashIndexOf(key)); } bool exists(KEY key) { int hashIndex; return (get(key, (N *) 0, hashIndex) != 0); } /* return true if key is valid, and corresponding data; * else return false */ bool get(KEY key, N *data) { int hashIndex; return (get(key, data, hashIndex) != 0); } N &operator[](KEY key) const; // Vector& operator ()(); operator Vector& () const; // Get all entries matching given key and append them into vector. // Return number of matching entries found. Int32 getAll(KEY key, Vector &vector) const; void remove(KEY key); }; // Implementation template Uint32 HashTable::hashIndexOf(KEY key) const { Uint32 hashCode = KEYOPS::hashCode(key); hashCode = hashCode ^ (hashCode >> 16); hashCode = hashCode ^ (hashCode >> 8); return (hashCode % _NBUCKETS_); } template KEY HashTable::add(KEY key, const N& value, int hashIndex) { HashTableEntry *newEntry = new (pool) HashTableEntry(KEYOPS::copyKey(pool, key), value); buckets[hashIndex].addLast(*newEntry); return newEntry->key; } template KEY HashTable::get(KEY key, N *data, int &hashIndex) const { hashIndex = hashIndexOf(key); const DoublyLinkedList >& list = buckets[hashIndex]; for (DoublyLinkedNode *i = list.begin(); !list.done(i); i = list.advance(i)) { KEY candidateKey = list.get(i).key; if (KEYOPS::equals(candidateKey, key)) { if (data) *data = list.get(i).value; return candidateKey; } } return 0; } template void HashTable::set(KEY key, N *data, int hashIndex) { hashIndex = hashIndexOf(key); DoublyLinkedList >& list = buckets[hashIndex]; for (DoublyLinkedNode *i = list.begin(); !list.done(i); i = list.advance(i)) { KEY candidateKey = list.get(i).key; if (KEYOPS::equals(candidateKey, key)) { if (data) list.get(i).value = *data; break; } } } template Int32 HashTable::getAll(KEY key, Vector &vector) const { Int32 hashIndex = hashIndexOf(key); Int32 numMatches = 0; const DoublyLinkedList >& list = buckets[hashIndex]; for (DoublyLinkedNode *i = list.begin(); !list.done(i); i = list.advance(i)) { KEY candidateKey = list.get(i).key; if (KEYOPS::equals(candidateKey, key)) { vector.append(list.get(i).value); numMatches++; } } return numMatches; } template N& HashTable::operator[](KEY key) const { const DoublyLinkedList >& list = buckets[hashIndexOf(key)]; for (DoublyLinkedNode *i = list.begin(); !list.done(i); i = list.advance(i)) { if (KEYOPS::equals(list.get(i).key, key)) return list.get(i).value; } // should never get here. return list.get(list.begin()).value; } //template Vector &HashTable::operator()() template HashTable::operator Vector& () const { Vector *vector = new Vector; for (Uint32 i = 0; i < _NBUCKETS_; i++) { const DoublyLinkedList >& list = buckets[i]; if (!list.empty()) for (DoublyLinkedNode *j = list.begin(); !list.done(j); j = list.advance(j)) { vector->append(list.get(j).value); } } return *vector; } template void HashTable::remove(KEY key) { DoublyLinkedList >& list = buckets[hashIndexOf(key)]; for (DoublyLinkedNode *i = list.begin(); !list.done(i); i = list.advance(i)) { if (KEYOPS::equals(list.get(i).key, key)) { list.get(i).remove();; return; } } } #endif