Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

Table.H

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 
00003 // This file is part of Vesta.
00004 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 // Last modified on Thu Apr 21 09:31:02 EDT 2005 by irina.furman@intel.com
00020 //      modified on Wed Jun  2 18:16:55 EDT 2004 by ken@xorian.net
00021 //      modified on Mon May  3 16:04:43 PDT 1999 by heydon
00022 
00023 #ifndef _TABLE_H
00024 #define _TABLE_H
00025 
00026 #include <Basics.H>
00027 
00028 /* This interface defines a template for a generic "Table" class. The
00029    "Table<K,V>" class is a map from keys of type "K" to values of type "V".
00030 
00031    The interface and implementation are based on Modula-3 tables. In
00032    particular, it defines the classes "Table<K,V>::T", "Table<K,V>::Default",
00033    and "Table<K,V>::Iterator".
00034 
00035    The class "T" is a class prototype with pure methods, and should not be
00036    instantiated. In fact, since the class "T" does not provide any state or
00037    methods, it is not even subclassed.
00038 
00039    The class "Default" provides a default implementation of tables using
00040    chained hashing.
00041 
00042    The class "Iterator" is for iterating over the elements of a
00043    "Table<K,V>::Default".
00044 
00045    For efficiency, the default tables and their iterators are unmonitored, so
00046    a client accessing a table from multiple threads must ensure that if two
00047    operations are active concurrently, then neither of them has side effects
00048    on the same table or iterator. The read-only methods are declared "const".
00049 
00050    "K" is required to be a class with the following methods:
00051 
00052      Class "K":
00053        Word Hash() throw ()
00054        K& operator= (const K& k) throw ()
00055        friend bool operator == (const K& k1, const K& k2) throw ()
00056 
00057    "V" may be any type so long as its values can be assigned. If it is a
00058    class, it should have an assignment method:
00059   
00060      Class "V":
00061        V& operator= (const V& v) throw ()
00062 
00063    To instantiate a table, you use cxx's "define_template" pragma. For this
00064    to work, you must first include the source file "Table.C", which provides
00065    implementations for the generic table methods. However, the "Table.C" code
00066    must be linked into your executable exactly once. Therefore, the
00067    recommended procedure is to write an implementation (.C) file of the form:
00068 
00069      #include "Table.C"
00070      <template-instantiation>
00071      <template-instantiation>
00072      ...
00073 
00074    Each <template-instantiation> is a one-line pragma, of the form:
00075 
00076      #pragma define_template Table<K,V>
00077 
00078    where "K" and "V" are the key and value types, respectively, of the
00079    instantiated table.
00080 
00081    To link the Table code successfully, you need to link your program against
00082    the standard math library, "libm.a".
00083 */
00084 
00085 template <class K, class V>
00086 class Table {
00087   public:
00088     // Iterator class defined below
00089     class Iterator;
00090 
00091     // main table type (prototype)
00092     // this definition is purely for documentation purposes
00093     class T {
00094       public:
00095         bool Get(const K& k, /*OUT*/ V& v) const throw ();
00096         /* If the key "k" exists in the table, then set "v" to the
00097            associated value and return "true". Otherwise, leave "v"
00098            unchanged and return "false". */
00099 
00100         bool Put(const K& k, const V& v, bool resize = true) throw ();
00101         /* Set the value associated with key "k" in the table to "v". Return
00102            "true" iff there was already a value associated with "k" in the
00103            table before the method was invoked. */
00104 
00105         bool Delete(const K& k, /*OUT*/ V& v, bool resize = true) throw ();
00106         /* If the key "k" exists in the table, set "v" to its associated
00107            value, remove the association from the table, and return "true".
00108            Otherwise, leave "v" unchanged and return "false". */
00109 
00110         int Size() const throw ();
00111         /* Return the number of <k,v> associations in the table. */
00112     };
00113 
00114     // The default implementation of tables uses chained hashing
00115     class Default /* : public T */ {
00116       public:
00117         // new methods
00118         Default(int sizeHint = 0, bool useGC = false) throw ()
00119           : maxEntries(!useGC), buckets(NULL) { Init(sizeHint); }
00120         /* Return a new, empty table. See the "Init" method below for a
00121            description of the "sizeHint" argument. If "useGC" is "false",
00122            then the client is assumed not to be using a garbage collector;
00123            in this case, structures allocated by the table are freed so
00124            as not to produce any memory leaks. Note that if "V" is a pointer
00125            type, the values pointed to in the table are *not* freed; freeing
00126            them is the responsibility of the client. If the client is using a
00127            garbage collector, passing "useGC = false" will not cause an error,
00128            but passing "useGC = true" will be slightly more efficient. */
00129 
00130         Default(const Default *tbl, bool useGC = false) throw ()
00131           : maxEntries(!useGC), buckets(NULL)
00132           { Init(tbl->numEntries); Copy(tbl); }
00133         /* Initialize this table to be a copy of "*tbl". The meaning of the
00134            "useGC" argument is the same as for the above constructor. */
00135 
00136         ~Default() throw ();
00137         /* Free any memory allocated by this table. */
00138 
00139         void Init(Bit32 sizeHint = 0U) throw ();
00140         /* Reset the table to be empty. If "sizeHint" is greater than zero,
00141            the table is initialized with the expectation that "sizeHint"
00142            different keys will eventually be "Put" into the table. These calls
00143            may execute somewhat faster than if "sizeHint" was 0. */
00144 
00145         Default& operator = (const Default& tbl) throw ()
00146             { Init(tbl.numEntries); Copy(&tbl); return *this; }
00147         /* Make this table a copy of "tbl". */
00148 
00149         // method overrides (see specifications above)
00150         int Size() const throw () { return numEntries; }
00151         bool Get(const K& k, /*OUT*/ V& v) const throw ();
00152         bool Put(const K& k, const V& v, bool resize = true) throw ();
00153         bool Delete(const K& k, /*OUT*/ V& v, bool resize = true) throw ();
00154         /* If the "Put" and "Delete" methods are passed "resize = false",
00155            the underlying bucket array will not be resized. This allows
00156            modifications to be made to the table while it is being enumerated
00157            by an iterator (see the "Iterator" class below). Once the iteration
00158            is complete, you should use the "Resize" method below. */
00159 
00160         // explicit resize
00161         void Resize() throw ();
00162         /* Grow or shrink the table as necessary after performing a sequence
00163            of "Put" or "Delete" operations all of which were passed "resize =
00164            false". */
00165         
00166       private:
00167         // bucket entries
00168         class EntryList {
00169           public:
00170             EntryList(const K& k, const V& v, EntryList *next)
00171               : key(k), val(v), tail(next) { /*SKIP*/ }
00172             EntryList *tail;
00173             K key;
00174             V val;
00175         };
00176         typedef EntryList *EntryListPtr;
00177 
00178         Bit32 numEntries;      // current number of entries in table
00179         Bit32 minEntries;      // minimum number of entries
00180         Bit32 maxEntries;      // maximum number of entries
00181         Bit16 logBuckets;      // Log_2(numBuckets)
00182         Bit16 minLogBuckets;   // minimum value for Log_2(initial size)
00183         EntryListPtr *buckets; // buckets array
00184 
00185         /* Invariants of an initialized table:
00186              I1. buckets != NULL => NUMBER(*buckets) = 2^logBuckets
00187              I2. minEntries < maxEntries
00188              I3. maxEntries & 0x1 = ``do deletions explicitly''
00189            Note: it is possible for "numEntries < minEntries" or for
00190            "maxEntries < numEntries". This can happen when "resize = false"
00191            is used, or when the minimum or maximum bucket size is reached.
00192            By I3, the low-order bit of "maxEntries" is used to store whether
00193            explicit deletions are required or not. */
00194 
00195         void NewBuckets(bool allocBuckets) throw ();
00196         /* Initialize the "minEntries" and "maxEntries" fields from the
00197            "this->logBuckets" field. If "allocBuckets" is true, allocate
00198            and initialize the "buckets" field to point to an array of
00199            "2^(logBuckets)" buckets. */
00200 
00201         void AllocBuckets(Bit32 numBuckets) throw ();
00202         /* Allocate and initialize the "buckets" field to point to an array
00203            array of "2^(logBuckets)" buckets. */
00204 
00205         void EmptyBuckets(Bit32 numBuckets) throw ();
00206         /* Forall "0 <= i < numBuckets", set "buckets[i]" to "NULL". */
00207 
00208         void Rehash(Bit16 logBuckets) throw ();
00209         /* Reallocate "2^logBuckets" buckets, and rehash the entries into
00210            the new table. */
00211 
00212         inline Bit32 Bucket(const K &k) const throw ();
00213         // Return the bucket index for key "k".
00214 
00215         void Copy(const Default* tbl) throw ();
00216         /* Copy the (key,value) pairs in "tbl" to this table. This table is
00217            not initially emptied, so any pairs originally in this table will
00218            be maintained (or have their values overwritten). */
00219 
00220         // friends
00221         friend class Iterator;
00222 
00223         // hide copy constructor
00224         Default(const Default&);
00225     };
00226 
00227     class Iterator {
00228       public:
00229         Iterator(const Default* tbl) throw ()
00230           : tbl(tbl) { Reset(); }
00231         /* Return a new iterator on the "Default" table "tbl". While the
00232            iterator is in use, the client is only allowed to modify the table
00233            by invoking the "Put" and "Delete" methods with "resize = false".
00234            Once the iteration is complete, it is recommended that the "Resize"
00235            method be called to adjust the size of the table if necessary. */
00236 
00237         void Reset() throw ();
00238         /* Resets the iterator so that it will iterate over all the elements
00239            in its associated table. This can be used to iterate over the
00240            elements of the same table multiple times. */
00241 
00242         bool Next(/*OUT*/ K& k, /*OUT*/ V& v) throw ();
00243         /* If there is an association in this iterator's table that has not
00244            already been enumerated, select one, set "k" to its key, set "v" to
00245            its value, and return "true". Otherwise, leave "k" and "v"
00246            unchanged, and return "false". */
00247       private:
00248         // fields
00249         const Default *tbl;       // this iterator's corresponding table
00250         typedef typename Default::EntryList EntryList;
00251         EntryList *curr;          // next entry to visit if non-NULL
00252         Bit32 bucket;             // next bucket if < "tbl->numBuckets"
00253         bool done;                // true if next() has returned "FALSE"
00254 
00255         // hide copy constructor
00256         Iterator(const Iterator&);
00257     };
00258 };
00259 
00260 // ========== template member definitions ==========
00261 
00262 #include "TableUtils.H"
00263 #include "TableConsts.H"
00264 
00265 // ----------------------------------------------- Default table methods -----
00266 
00267 template <class K, class V>
00268 void Table<K,V>::Default::Copy(const Default *tbl) throw ()
00269 /* Iterate over the elements of "tbl", and put each (key,value) pair
00270    in this table. This could probably be done more efficiently without
00271    using an iterator. */
00272 {
00273     Iterator iter(tbl);
00274     K key; V value;
00275     while (iter.Next(/*OUT*/ key, /*OUT*/ value)) {
00276         // add the pair to this table
00277         (void) Put(key, value, /*resize=*/ false);
00278     }
00279     Resize();
00280 }
00281 
00282 template <class K, class V>
00283 void Table<K,V>::Default::Init(Bit32 sizeHint) throw ()
00284 {
00285     Bit32 idealBuckets;
00286     float idealF = (float)sizeHint / TableConsts::IdealDensity;
00287     idealBuckets = (idealF > (float)TableConsts::MaxBuckets) ? TableConsts::MaxBuckets : Ceiling(idealF);
00288     idealBuckets = max(TableConsts::MinBuckets, idealBuckets);
00289     this->numEntries = 0U;
00290     if (this->buckets != NULL && idealBuckets < (TableConsts::One32U << this->logBuckets)) {
00291         // enough buckets already exist; just empty them
00292         EmptyBuckets(TableConsts::One32U << this->logBuckets);
00293     } else {
00294         // allocate new buckets
00295         this->logBuckets = this->minLogBuckets = Log_2(idealBuckets);
00296         NewBuckets(sizeHint > 0);
00297     }
00298 }
00299 
00300 template <class K, class V>
00301 Table<K,V>::Default::~Default() throw ()
00302 {
00303     if (this->buckets != (EntryListPtr *)NULL) {
00304         if ((this->maxEntries & TableConsts::DoDeletions) != 0) {
00305             Bit32 numBuckets = (TableConsts::One32U << this->logBuckets);
00306             for (Bit32 i = 0U; i < numBuckets; i++) {
00307                 EntryList *curr = this->buckets[i];
00308                 while (curr != (EntryList *)NULL) {
00309                     EntryList *prev = curr;
00310                     curr = curr->tail;
00311                     prev->tail = (EntryList *)NULL; // drop on floor for GC
00312                     delete prev;
00313                 }
00314             }
00315         }
00316         delete[] this->buckets;
00317         this->buckets = (EntryListPtr *)NULL; // drop on floor for GC
00318     }
00319 }
00320 
00321 template <class K, class V>
00322 void Table<K,V>::Default::NewBuckets(bool allocBuckets) throw ()
00323 {
00324     // calculate "minEntries" and "maxEntries"
00325     Bit32 numBuckets = (TableConsts::One32U << this->logBuckets);
00326     this->minEntries = Round(TableConsts::MinDensity * (float)numBuckets);
00327     Bit32 doDeletions = this->maxEntries & TableConsts::DoDeletions;
00328     this->maxEntries = TableConsts::DoDeletionsBar &
00329       Round(TableConsts::MaxDensity * (float)numBuckets);
00330     this->maxEntries |= doDeletions;
00331 
00332     // allocate buckets array if necessary
00333     if (allocBuckets) {
00334         AllocBuckets(numBuckets);
00335     } else {
00336         this->buckets = NULL;
00337     }
00338 }
00339 
00340 template <class K, class V>
00341 void Table<K,V>::Default::AllocBuckets(Bit32 numBuckets) throw ()
00342 {
00343     this->buckets = NEW_ARRAY(EntryListPtr, numBuckets);
00344     assert(this->buckets != NULL);
00345     EmptyBuckets(numBuckets);
00346 }
00347 
00348 template <class K, class V>
00349 void Table<K,V>::Default::EmptyBuckets(Bit32 numBuckets) throw ()
00350 {
00351     for (Bit32 i = 0U; i < numBuckets; i++) {
00352         this->buckets[i] = (EntryList *)NULL;
00353     }
00354 }
00355 
00356 template <class K, class V>
00357 inline Bit32 Table<K,V>::Default::Bucket(const K& k) const throw ()
00358 {
00359     return ((TableConsts::Multiplier * k.Hash()) >> (TableConsts::BitsPerWd - this->logBuckets));
00360 }
00361 
00362 template <class K, class V>
00363 void Table<K,V>::Default::Rehash(Bit16 logBuckets) throw ()
00364 {
00365     assert(logBuckets <= TableConsts::MaxLogBuckets);
00366     assert(logBuckets >= this->minLogBuckets);
00367 
00368     EntryListPtr *ob = this->buckets;                // old buckets
00369     Bit16 oldLogBuckets = this->logBuckets;          // log of their old size
00370     Bit32 oldNumBuckets = (TableConsts::One32U << oldLogBuckets); // their old size
00371 
00372     // allocate new buckets
00373     this->logBuckets = logBuckets;
00374     NewBuckets(ob != NULL);
00375 
00376     if (ob != NULL) {
00377         // rehash entries from old table into new table
00378         for (Bit32 i = 0U; i < oldNumBuckets; i++) {
00379             EntryList *curr = ob[i], *tail;
00380             ob[i] = (EntryList *)NULL;
00381             while (curr != (EntryList *)NULL) {
00382                 int ix = Bucket(curr->key);
00383                 tail = curr->tail;
00384                 curr->tail = this->buckets[ix];
00385                 this->buckets[ix] = curr;
00386                 curr = tail;
00387             }
00388         }
00389 
00390         // delete old buckets array
00391         if (this->maxEntries & TableConsts::DoDeletions)
00392             delete[] ob;
00393     }
00394 }
00395 
00396 template <class K, class V>
00397 void Table<K,V>::Default::Resize() throw ()
00398 {
00399     if (this->numEntries < this->minEntries
00400         && this->logBuckets > this->minLogBuckets) {
00401         // too sparse
00402         Rehash(this->logBuckets - 1);
00403     } else if (this->numEntries > this->maxEntries
00404                && this->logBuckets < TableConsts::MaxLogBuckets) {
00405         // too crowded
00406         Rehash(this->logBuckets + 1);
00407     }
00408 }
00409 
00410 template <class K, class V>
00411 bool Table<K,V>::Default::Get(const K& k, /*OUT*/ V& v) const throw ()
00412 {
00413     if (this->buckets == NULL) return false;
00414     register EntryList *curr = this->buckets[Bucket(k)];
00415 
00416     while (curr != (EntryList *)NULL && !(curr->key == k)) {
00417         curr = curr->tail;
00418     }
00419     if (curr != (EntryList *)NULL) {
00420         v = curr->val;
00421         return true;
00422     } else {
00423         return false;
00424     }
00425 }
00426 
00427 template <class K, class V>
00428 bool Table<K,V>::Default::Put(const K& k, const V& v, bool resize) throw ()
00429 {
00430     Bit32 b = Bucket(k);
00431     if (this->buckets == NULL) AllocBuckets(TableConsts::One32U << this->logBuckets);
00432     register EntryList *curr = this->buckets[b];
00433 
00434     while (curr != (EntryList *)NULL && !(curr->key == k)) {
00435         curr = curr->tail;
00436     }
00437     if (curr != (EntryList *)NULL) {
00438         curr->val = v;
00439         return true;
00440     } else {
00441         this->buckets[b] = NEW_CONSTR(EntryList, (k, v, this->buckets[b]));
00442         this->numEntries++;
00443         if (resize && this->numEntries > this->maxEntries
00444             && this->logBuckets < TableConsts::MaxLogBuckets) {
00445             // too crowded
00446             Rehash(this->logBuckets + 1);
00447         }
00448         return false;
00449     }
00450 }
00451 
00452 template <class K, class V>
00453 bool Table<K,V>::Default::Delete(const K& k, /*OUT*/ V& v, bool resize)
00454   throw ()
00455 {
00456     if (this->buckets == NULL) return false;
00457     Bit32 b = Bucket(k);
00458     EntryList *curr =  this->buckets[b];
00459     EntryList *prev = (EntryList *)NULL;
00460 
00461     while (curr != (EntryList *)NULL && !(curr->key == k)) {
00462         prev = curr;
00463         curr = curr->tail;
00464     }
00465     if (curr != (EntryList *)NULL) {
00466         v = curr->val;
00467         if (prev == (EntryList *)NULL)
00468             this->buckets[b] = curr->tail;
00469         else
00470             prev->tail = curr->tail;
00471         this->numEntries--;
00472         if (this->maxEntries & TableConsts::DoDeletions) delete curr;
00473         if (resize && this->numEntries < this->minEntries
00474             && this->logBuckets > this->minLogBuckets) {
00475             // too sparse
00476             Rehash(this->logBuckets - 1);
00477         }
00478         return true;
00479     } else {
00480         return false;
00481     }
00482 }
00483 
00484 // ---------------------------------------------------- Iterator methods -----
00485 
00486 template <class K, class V>
00487 void Table<K,V>::Iterator::Reset() throw ()
00488 {
00489     this->curr = (EntryList *)NULL;
00490     this->bucket = 0U;
00491     this->done = false;
00492 }
00493 
00494 template <class K, class V>
00495 bool Table<K,V>::Iterator::Next(/*OUT*/ K& k, /*OUT*/ V& v) throw ()
00496 {
00497     if (this->tbl->buckets != NULL) {
00498         while (this->curr == (EntryList *)NULL
00499                && this->bucket < (TableConsts::One32U << tbl->logBuckets)) {
00500             this->curr = this->tbl->buckets[this->bucket];
00501             this->bucket++;
00502         }
00503     }
00504     if (this->curr != (EntryList *)NULL) {
00505         k = curr->key;
00506         v = curr->val;
00507         this->curr = this->curr->tail;
00508         return true;
00509     } else {
00510         assert(!(this->done));
00511         this->done = true;
00512         return false;
00513     }
00514 }
00515 
00516 #endif // _TABLE_H

Generated on Mon May 8 00:48:29 2006 for Vesta by  doxygen 1.4.2