Open64 (mfef90, whirl2f, and IR tools)  TAG: version-openad; SVN changeset: 916
id_map.h
Go to the documentation of this file.
00001 /*
00002 
00003   Copyright (C) 2000, 2001 Silicon Graphics, Inc.  All Rights Reserved.
00004 
00005   This program is free software; you can redistribute it and/or modify it
00006   under the terms of version 2 of the GNU General Public License as
00007   published by the Free Software Foundation.
00008 
00009   This program is distributed in the hope that it would be useful, but
00010   WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
00012 
00013   Further, this software is distributed without any warranty that it is
00014   free of the rightful claim of any third person regarding infringement 
00015   or the like.  Any license provided herein, whether implied or 
00016   otherwise, applies only to this software file.  Patent licenses, if 
00017   any, provided herein do not apply to combinations of this program with 
00018   other software, or any other product whatsoever.  
00019 
00020   You should have received a copy of the GNU General Public License along
00021   with this program; if not, write the Free Software Foundation, Inc., 59
00022   Temple Place - Suite 330, Boston MA 02111-1307, USA.
00023 
00024   Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pky,
00025   Mountain View, CA 94043, or:
00026 
00027   http://www.sgi.com
00028 
00029   For further information regarding this notice, see:
00030 
00031   http://oss.sgi.com/projects/GenInfo/NoticeExplan
00032 
00033 */
00034 
00035 
00036 //-*-c++-*-
00037 // A class to support sparse maps based on ID's. An example would be a
00038 // mapping from the CODEREP ID space to EXP_OCCURS pointers. Indeed
00039 // the need for this example was the original motivation for creating
00040 // this class.
00041 //
00042 // Original author: Robert Kennedy
00043 //
00044 // Interface description:
00045 //     All of the following are member functions of
00046 //     template <class NODE_TYPE, class KEY_TYPE> class ID_MAP.
00047 //     NODE_TYPE can be anything; often it is a pointer to some type
00048 //        of node in a data structure.
00049 //     KEY_TYPE  must be something that can participate in integer
00050 //        arithmetic. This restriction could easily be lifted by
00051 //        expanding the implementation of the member function Hash().
00052 //
00053 // ID_MAP<NODE_TYPE, KEY_TYPE> id_map(UINT32     initial_tbl_capacity,
00054 //                                    NODE_TYPE  not_found_value,
00055 //                                    MEM_POOL  *pool,
00056 //                                    BOOL       tracing)
00057 //     (constructor) The parameters to the constructor specify
00058 //     respectively an estimate of the number of entries that will be
00059 //     made in the mapping, the value that should be returned by a
00060 //     failed table-lookup operation, the memory pool from which to
00061 //     allocate space for the mapping, and a tracing flag.
00062 //
00063 // NODE_TYPE Lookup(KEY_TYPE key) const
00064 //     Returns not_found_value (specified in the constructor call) if
00065 //     *this does not contain an entry for key; otherwise returns the
00066 //     entry for key.
00067 //
00068 // void       Insert(KEY_TYPE key, NODE_TYPE node)
00069 //     Adds an entry to the mapping for the given (key, node)
00070 //     pair. Assumes that the mapping does not already contain an
00071 //     entry for the given key.
00072 //
00073 // void       Print(FILE *fp) const
00074 //     Prints detailed, implementation-dependent information about the
00075 //     mapping for debugging purposes.
00076 //
00077 //
00078 // Implementation description:
00079 //     This class is implemented as a hash table using an in-place
00080 //     scheme for resolving collisions. In order to support deletions
00081 //     from the table in a coherent way, we maintain the invariant
00082 //     that the entries in a collision list all share the same hash
00083 //     value, and each hash value corresponds to at most one collision
00084 //     list. The hash table is a dynamically-allocated array of
00085 //     template class ID_MAP_HASH_ENTRY. When the table expands beyond
00086 //     a load factor defined by the private member function
00087 //     Capacity(), it is grown (using OPT_POOL_Realloc). Growing the
00088 //     table necessitates a full rebuild, because the hash function
00089 //     (member function Hash()) obviously should depend on the size of
00090 //     the table. The private member function Enlarge() implements the
00091 //     rebuild process. Because Enlarge() can take quite a bit of time
00092 //     in passing over the entire table, it can be very important to
00093 //     pass a good initial size to the constructor when the ID_MAP is
00094 //     created.
00095 //
00096 //     The implementation maintains a doubly-linked list by table
00097 //     index of free positions in the table; when an entry is
00098 //     required, it is unhooked from this list and used. The head of
00099 //     the free list is the private member _free_list. The special
00100 //     value (-1) denotes the end of a linked list (see the _next and
00101 //     _prev fields in the template class ID_MAP_HASH_ENTRY).
00102 //
00103 //     To perform a lookup operation in the hash table (member
00104 //     function Lookup()), we hash the given key value (member
00105 //     function Hash()) to get a hash value H. We begin a linear
00106 //     search for an ID_MAP_HASH_ENTRY at the table entry _table[H],
00107 //     and proceed by following the _next fields until we find either
00108 //     the end of the collision list (denoted by a _next field
00109 //     containing -1) or an entry containing the requested key.
00110 //
00111 //     When we add an entry (member function Insert()), we grow the
00112 //     table if necessary, and then hash the given key to get a table
00113 //     index H. If _table[H] is not free (a condition denoted by a
00114 //     non-not_found_value _node field), we displace the entry from
00115 //     _table[H] into an entry allocated from the free list. If there
00116 //     is a displaced entry due to the insertion, we maintain the
00117 //     invariant that each nonempty collision list contains keys that
00118 //     hash to exactly one location in the following way: If the
00119 //     displaced entry hashes to a different location from the
00120 //     inserted entry, we traverse the collision list corresponding to
00121 //     the displaced entry, and route the appropriate _next index to
00122 //     the displaced entry's new location. If the displaced entry
00123 //     hashes to the same location as the inserted entry, we simply
00124 //     insert the new entry at the head of the collision list. We
00125 //     insert the given (key, node) pair into _table[H], and chain up
00126 //     _table[H].next to the entry we displaced, if any.
00127 //
00128 //     The most complicated part of the implementation is the part
00129 //     that rehashes the entire table in place when the table
00130 //     grows. This happens in the private member function
00131 //     Enlarge(). If you can think of a simpler way to do an in-place
00132 //     rehash of this sort of table, please come talk to me (Robert
00133 //     Kennedy). The present implementation works by determining the
00134 //     (necessarily large enough) set of table entries that no element
00135 //     currently in the table will hash to, and using that set of
00136 //     entries as auxilliary storage during the global rehash
00137 //     operation. Any scheme that doesn't do something like this
00138 //     appears doomed to have old (pre-growth) structures mingling
00139 //     fatally with new (post-growth, post-rehash) structures in the
00140 //     table.
00141 //
00142 
00143 
00144 #ifndef id_map_INCLUDED
00145 #define id_map_INCLUDED "id_map.h"
00146 
00147 #include <math.h>
00148 #include "defs.h"
00149 #include "cxx_template.h"
00150 #include "tracing.h"
00151 #include "opt_defs.h"
00152 #include "erglob.h"      // For EC_xxx error codes
00153 
00154 #define MIN_TABLE_SIZE 16
00155 
00156 #define CAPACITY_FACTOR 0.75
00157 
00158 // GROWTH_FACTOR must be at least as large as 2.0 * CAPACITY_FACTOR
00159 // for correctness!
00160 #define GROWTH_FACTOR   2.0
00161 
00162 template <class NODE_TYPE, class KEY_TYPE> class ID_MAP;
00163 
00164 template <class NODE_TYPE, class KEY_TYPE>
00165 class ID_MAP_HASH_ENTRY {
00166 friend class ID_MAP<NODE_TYPE, KEY_TYPE>;
00167   NODE_TYPE  _node;     // _not_found_value for entries in the free list
00168   union {
00169     KEY_TYPE _key;      // For entries in use
00170     mINT32   _prev;     // For entries in the free list
00171   };
00172   mINT32     _next;     // Next in the collision list or the free list
00173 
00174 #ifdef DEBUG_ID_MAP
00175   enum {
00176     IMHE_UNKNOWN,
00177     IMHE_FREE,
00178     IMHE_USED
00179   } _state;
00180 #endif // DEBUG_ID_MAP
00181 };
00182 
00183 template <class NODE_TYPE, class KEY_TYPE>
00184 class ID_MAP {
00185 private:
00186   NODE_TYPE                                     _not_found_value;
00187   BOOL                                          _constructed;
00188   const BOOL                                    _tracing;
00189   const BOOL                                    _verbose;
00190   MEM_POOL                               *const _pool;
00191   ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE> *      _table;
00192 
00193   // If (_table == NULL), then _table_size indicates the desired
00194   // minimum table size that was set at constructor time, and that is
00195   // used by Init() to guide allocation of the _table.
00196   // If (_table != NULL), then _table_size indicates the true size of
00197   // the _table that's allocated to this ID_MAP.
00198   UINT32                                        _table_size;
00199   UINT32                                        _num_entries;
00200   mINT32                                        _free_list;
00201 
00202   // How many elements are we willing to tolerate in a table of a
00203   // given size before reallocating?
00204   static UINT32 Capacity(UINT32 size)
00205     {
00206       return (UINT32) floor((double) size * CAPACITY_FACTOR);
00207     }
00208 
00209   // Given a number of elements, how big a table should we allocate to
00210   // hold them?
00211   static UINT32 Size(UINT32 capacity)
00212     {
00213       return (UINT32) ceil((double) capacity / CAPACITY_FACTOR);
00214     }
00215 
00216   void Alloc_table_space(UINT32 table_size);
00217   void Initialize_table(void);
00218 
00219   mINT32 Hash(const KEY_TYPE key, const UINT32 tbl_size) const
00220     {
00221       // First part: convert key into a 32-bit key
00222       UINT32 key_uint32;
00223       if (sizeof(key) == sizeof(UINT32)) {
00224         key_uint32 = *((const UINT32 *) &key);
00225       }
00226       else if (sizeof(key) < sizeof(UINT32)) {
00227         key_uint32 = (*((const UINT32 *) &key) &
00228                       ((1 << ((sizeof(key) & (sizeof(UINT32)-1)) * 8)) - 1)); 
00229         // added  "& (sizeof(UINT32)-1)" to suppress shift count warning
00230         // Performance should not be affected because any reasonable
00231         // compiler can simpilfy ((sizeof(key) & 0x3) * 8) into a
00232         // constant.  -Raymond  5/27/98.
00233       }
00234       else {
00235         const UINT32 *p = (const UINT32 *) &key;
00236         INT i;
00237         key_uint32 = 0;
00238         for (i = 0; i < (sizeof(key) / sizeof(UINT32)); i++) {
00239           key_uint32 = (key_uint32 << 19) + (key_uint32 >> 13);  // rotate left 19 bits
00240           key_uint32 ^= *p++;
00241         }
00242       }
00243       
00244       // Second part: hashing the 32-bit key
00245       const UINT64 multiplier = 2654435769LL; // 2^32 * (sqrt(5) - 1) / 2
00246 
00247       mINT32 retval = (tbl_size * ((key_uint32 * multiplier) %
00248                                    0x100000000LL)) >> 32;
00249       return retval;
00250     }
00251 
00252   void Add_to_free_list(const mINT32 idx)
00253     {
00254       Is_Trace(_tracing && _verbose,
00255                (TFile, "ID_MAP::Add_to_free_list(%d)\n", idx));
00256 
00257       if (_free_list != -1) {
00258         _table[_free_list]._prev = idx;
00259       }
00260 
00261       _table[idx]._next = _free_list;
00262       _table[idx]._node = _not_found_value;
00263 
00264       Is_True(Check(_free_list != idx),
00265               ("ID_MAP::Add_to_free_list: Must not introduce loop"));
00266 
00267       _free_list = idx;
00268 
00269     }
00270 
00271   void Enlarge(void);
00272 
00273   void Remove_from_free_list(const mINT32 idx)
00274     {
00275       Is_Trace(_tracing && _verbose,
00276                (TFile, "ID_MAP::Remove_from_free_list(%d)\n", idx));
00277       Is_True(Check(_table[idx]._node == _not_found_value),
00278               ("ID_MAP::Remove_from_free_list: Node must be free: %ld", idx));
00279 
00280       if (_free_list == idx) {
00281         _free_list = _table[idx]._next;
00282         Is_True(Check(_free_list != idx),
00283                 ("ID_MAP::Remove_from_free_list: Inconsistent free list: %lu",
00284                  _free_list));
00285         Is_True(Check(_table[idx]._node == _not_found_value),
00286                 ("ID_MAP::Remove_from_free_list: Node must be free: %ld", idx));
00287       }
00288       else {
00289         Is_True(Check(_table[idx]._prev != -1),
00290                 ("ID_MAP::Remove_from_free_list: Inconsistent free list"));
00291         Is_True(Check(_table[idx]._next != idx),
00292                 ("ID_MAP::Remove_from_free_list: Loop in free list : %ld",
00293                  idx));
00294         _table[_table[idx]._prev]._next = _table[idx]._next;
00295       }
00296       if (_table[idx]._next != -1) {
00297         _table[_table[idx]._next]._prev = _table[idx]._prev;
00298         _table[idx]._next = -1;
00299       }
00300     }
00301 
00302   mINT32 Alloc_from_free_list(void)
00303     {
00304       Is_Trace(_tracing && _verbose,
00305                (TFile, "ID_MAP::Alloc_from_free_list()..."));
00306       Is_True(_free_list != -1,
00307               ("ID_MAP::Displace: free list must be nonempty; %lu / %lu",
00308                _num_entries, _table_size));
00309       mINT32 idx = _free_list;
00310 
00311       Is_True(_table[_free_list]._node == _not_found_value,
00312               ("ID_MAP::Alloc_from_free_list: free list inconsistency"));
00313 
00314       _free_list = _table[_free_list]._next;
00315 
00316       Is_True(Check(idx != _free_list),
00317               ("ID_MAP::Alloc_from_free_list: loop in free list"));
00318 
00319       Is_True(_table[_free_list]._node == _not_found_value,
00320               ("ID_MAP:Alloc_from_free_list: free list inconsistency"));
00321 
00322       Is_Trace(_tracing && _verbose,
00323                (TFile, " returning %d\n", idx));
00324       return idx;
00325     }
00326 
00327   BOOL Check(BOOL cond) const
00328     {
00329       if (!cond) 
00330         Print(TFile);
00331       return cond;
00332     }
00333 
00334   void Verify(void) const
00335     {
00336 #ifdef DEBUG_ID_MAP
00337       for (mINT32 idx = 0; idx < _table_size; idx++) {
00338         _table[idx]._state =
00339           ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>::IMHE_UNKNOWN;
00340       }
00341       for (idx = _free_list; idx != -1; idx = _table[idx]._next) {
00342         Is_True(Check(_table[idx]._state ==
00343                       ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>::IMHE_UNKNOWN),
00344                 ("ID_MAP::Verify: Loop in free list"));
00345         _table[idx]._state =
00346           ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>::IMHE_FREE;
00347       }
00348       for (idx = 0; idx < _table_size; idx++) {
00349         if (_table[idx]._state !=
00350             ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>::IMHE_FREE) {
00351           // Don't check entries that are in transition from free to
00352           // used.
00353           Is_True(Check(idx == Entry_lookup(_table[idx]._key)),
00354                   ("ID_MAP::Verify: Dangling node: idx == %ld", idx));
00355           _table[idx]._state =
00356             ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>::IMHE_USED;
00357         }
00358       }
00359 #endif // DEBUG_ID_MAP
00360     }
00361 
00362   mINT32 Entry_lookup(const KEY_TYPE) const;
00363 
00364 public:
00365   ID_MAP(const UINT32     initial_capacity,
00366          const NODE_TYPE  not_found_value,
00367          MEM_POOL  *const pool,
00368          const BOOL       tracing);
00369   ~ID_MAP(void);
00370 
00371   void       Init(void);
00372   void       Init(UINT32);  // init with overriding table size
00373 
00374   NODE_TYPE  Lookup(const KEY_TYPE) const;
00375 
00376   void Insert(const KEY_TYPE, const NODE_TYPE);
00377 
00378   void Delete(const KEY_TYPE);
00379 
00380   void Print(FILE *) const;
00381 };
00382 
00383 
00384 // here starts templatized function declarations
00385 
00386 template <class KEY_TYPE>
00387 UINT64 Key_as_llu(const KEY_TYPE k)
00388 {
00389    UINT64 llu;
00390    
00391    switch (sizeof(k))
00392    {
00393    case 1:
00394       llu = *(UINT8 *)&k;
00395       break;
00396    case 2:
00397       llu = *(UINT16 *)&k;
00398       break;
00399    case 4:
00400       llu = *(UINT32 *)&k;
00401       break;
00402    default:
00403       llu = *(UINT64 *)&k;
00404       break;
00405    }
00406    return llu;
00407 } // Key_as_llu
00408 
00409       
00410 
00411 // The constructor only squirrels away the information required to
00412 // build the object. The object is built only by the Init() function,
00413 // which must be called separately. We implement things this way so we
00414 // can avoid building the object as an automatic variable when we know
00415 // it won't be used.
00416 
00417 template <class NODE_TYPE, class KEY_TYPE>
00418 ID_MAP<NODE_TYPE, KEY_TYPE>::ID_MAP(const UINT32          init_capacity,
00419                                     const NODE_TYPE       not_found_value,
00420                                           MEM_POOL *const pool,
00421                                     const BOOL            tracing) :
00422                                  _not_found_value(not_found_value),
00423                                  _pool(pool),
00424                                  _tracing(tracing),
00425                                  _verbose(FALSE),       // change this?
00426                                  _num_entries(0),
00427                                  _constructed(FALSE)
00428 {
00429   _table      = NULL;
00430   _table_size = Size(init_capacity);
00431 }
00432 
00433 template <class NODE_TYPE, class KEY_TYPE> void
00434 ID_MAP<NODE_TYPE, KEY_TYPE>::Alloc_table_space(UINT32 table_size)
00435 {
00436   if (_table == NULL) {
00437     if (table_size < MIN_TABLE_SIZE) {
00438       table_size = MIN_TABLE_SIZE;
00439     }
00440     _table_size = table_size;
00441     // First allocation of space for _table.
00442     _table = (ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE> *)
00443       MEM_POOL_Alloc(_pool,
00444                      (table_size *
00445                       sizeof(ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>)));
00446   }
00447   else {
00448     // This ID_MAP is being re-initialized. Use the existing _table if
00449     // it's big enough, otherwise realloc to the required size.
00450     if (_table_size < table_size) {
00451       _table = (ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE> *)
00452         MEM_POOL_Realloc(_pool,
00453                          _table,
00454                          _table_size *
00455                          sizeof(ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>),
00456                          table_size *
00457                          sizeof(ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>));
00458       _table_size = table_size;
00459     }
00460   }
00461 
00462   if (_table == NULL) ErrMsg(EC_No_Mem, "ID_MAP::ID_MAP");
00463 
00464   Is_Trace(_tracing,
00465            (TFile, "ID_MAP::ID_MAP: allocated %u entries\n",
00466             _table_size));
00467 }
00468 
00469 template <class NODE_TYPE, class KEY_TYPE> void
00470 ID_MAP<NODE_TYPE, KEY_TYPE>::Initialize_table(void)
00471 {
00472   // Put the entire table into the free list.
00473   _free_list = 0;
00474   for (mINT32 i = 0; i < _table_size; i++) {
00475     _table[i]._node = _not_found_value;
00476     _table[i]._prev = i - 1;
00477     _table[i]._next = i + 1;
00478   }
00479   _table[_table_size - 1]._next = -1;
00480 }
00481 
00482 template <class NODE_TYPE, class KEY_TYPE> void
00483 ID_MAP<NODE_TYPE, KEY_TYPE>::Init(UINT32 capacity)
00484 {
00485   Alloc_table_space(Size(capacity));
00486   Initialize_table();
00487 }
00488 
00489 template <class NODE_TYPE, class KEY_TYPE> void
00490 ID_MAP<NODE_TYPE, KEY_TYPE>::Init(void)
00491 {
00492   _constructed = TRUE;
00493 
00494   Alloc_table_space(_table_size);
00495   Initialize_table();
00496 }
00497 
00498 template <class X>
00499 inline void Id_map_fprint(FILE *fp, X *x)
00500 {
00501   x->Print(fp);
00502 }
00503 
00504 inline void Id_map_fprint(FILE *fp, IDTYPE *x)
00505 {
00506   fprintf(fp, "%d\n", *x);
00507 }
00508 
00509 inline void Id_map_fprint(FILE *fp, INT x)
00510 {
00511   fprintf(fp, "%d\n", x);
00512 }
00513 
00514 template <class NODE_TYPE, class KEY_TYPE> void
00515 ID_MAP<NODE_TYPE, KEY_TYPE>::Print(FILE *fp) const
00516 {
00517   fprintf(fp, "Number of entries: %u\n", _num_entries);
00518   fprintf(fp, "Free list --> %d\n", _free_list);
00519 
00520   for (mINT32 i = 0; i < _table_size; i++) {
00521     fprintf(fp, "ID_MAP table[%d] : ", i);
00522     if (_table[i]._node != _not_found_value) {
00523       fprintf(fp, "[H(%llu)=%d; %d -->] ",
00524               Key_as_llu(_table[i]._key),
00525               Hash(_table[i]._key, _table_size),
00526               _table[i]._next);
00527       Id_map_fprint(fp, _table[i]._node);
00528     }
00529     else {
00530       fprintf(fp, "<-- %d, 0x%lx, %d -->\n",
00531               _table[i]._prev, (INTPTR) _table[i]._node, _table[i]._next);
00532     }
00533   }
00534 }
00535 
00536 template <class NODE_TYPE, class KEY_TYPE>
00537 ID_MAP<NODE_TYPE, KEY_TYPE>::~ID_MAP(void)
00538 {
00539   if (_constructed) {
00540     Verify();
00541     if (_tracing) {
00542       // dump out the table before destructing it.
00543       Is_Trace(TRUE, (TFile, "--- ID_MAP pre-destruct table dump:\n"));
00544       Print(TFile);
00545       // Really should call the destructor on the table elements here,
00546       // and then free the table memory.
00547     }
00548     _constructed = FALSE;
00549   }
00550 }
00551 
00552 template <class NODE_TYPE, class KEY_TYPE> void
00553 ID_MAP<NODE_TYPE, KEY_TYPE>::Enlarge(void)
00554 {
00555   // Enlarge the hash table; here's how we do it:
00556   // 1. Realloc the table;
00557   // 2. Decide which nodes in the table will be occupied by entries
00558   //    (by marking those whose indices are hash values and building
00559   //     up a free list containing an arbitrary set of entries to be
00560   //     occupied; the number of used hash values plus the length of
00561   //     this free list will be the number of entries.)
00562   // 3. Copy the table's node/key pairs into the set of nodes that
00563   //    won't be occupied; the enlargement factor must be great enough
00564   //    that we're guaranteed enough space to do this. Two is always
00565   //    enough.
00566   // 4. For each node/key pair in a to-be-unoccupied entry, rehash it,
00567   //    displacing into a free list node if necessary, and
00568   //    simultaneously rebuild the free list to contain all unoccupied
00569   //    entries.
00570   mINT32 first_new_idx = _table_size;
00571   UINT32 save_num_entries = _num_entries;
00572 
00573   mINT32 i;     // general-purpose running index
00574 
00575   Is_Trace(_tracing, (TFile, "ID_MAP enlarging from size %d\n",
00576                       _table_size));
00577   Is_Trace_cmd(_tracing && _verbose, Print(TFile));
00578 
00579   UINT32 new_table_size = (UINT32) ceil(GROWTH_FACTOR * (double) _table_size);
00580 
00581   _table = (ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE> *)
00582     MEM_POOL_Realloc(_pool,
00583                      _table,
00584                      _table_size *
00585                      sizeof(ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>),
00586                      new_table_size *
00587                      sizeof(ID_MAP_HASH_ENTRY<NODE_TYPE, KEY_TYPE>));
00588 
00589   if (_table == NULL) ErrMsg(EC_No_Mem, "ID_MAP::Enlarge");
00590 
00591   _table_size = new_table_size;
00592 
00593   // Use the _next field of each entry to say whether the entry will
00594   // be occupied or not. Unoccupied <--> -1.
00595   for (i = _table_size - 1; i >= first_new_idx; i--) {
00596     _table[i]._next = -1;
00597     _table[i]._node = _not_found_value;
00598   }
00599 
00600   for (; i >= 0; i--) {
00601     _table[i]._next = -1;
00602   }
00603 
00604   // Use save_num_entries to count the number of distinct hash values
00605   // represented.
00606   for (i = 0; i < first_new_idx; i++) {
00607     if (_table[i]._node != _not_found_value) {
00608       mINT32 h = Hash(_table[i]._key, _table_size);
00609       if (_table[h]._next == -1) {
00610         Is_True(Check(save_num_entries != 0),
00611                 ("ID_MAP::Enlarge: Bad save_num_entries logic"));
00612         --save_num_entries;
00613         _table[h]._next = 0;
00614       }
00615     }
00616   }
00617 
00618   // Now grab an occupied for every hash collision. After the
00619   // following loop, there will be exactly one instance of _next == 0
00620   // for each entry in the table.
00621   for (i = 0; save_num_entries > 0; i++) {
00622     Is_True(i < _table_size,
00623             ("ID_MAP::Enlarge: Bad logic"));
00624     if (_table[i]._next == -1) {
00625       Is_True(Check(save_num_entries != 0),
00626               ("ID_MAP::Enlarge: Bad save_num_entries logic"));
00627       --save_num_entries;
00628       _table[i]._next = 0;
00629     }
00630   }
00631 
00632   // Find the head of the unoccupied list.
00633   i = _table_size;
00634   mINT32 unocc_head = -1;
00635 
00636   while (TRUE) {
00637     --i;
00638     if (_table[i]._next == -1) {
00639       unocc_head = i;
00640       break;
00641     }
00642     Is_True(i > 0, ("ID_MAP::Enlarge: Bad logic"));
00643   }
00644 
00645   // Put all the entries destined to be unoccupied into the unoccupied
00646   // list.
00647   while (i > 0) {
00648     --i;
00649     if (_table[i]._next == -1) {
00650       _table[i]._next = -unocc_head - 2;
00651       unocc_head = i;
00652     }
00653   }
00654 
00655   // Find the first to-be-unoccupied place that isn't already holding
00656   // a pair.
00657   mINT32 unocc = unocc_head;
00658   while (_table[unocc]._node != _not_found_value) {
00659     Is_True(0 <= unocc && unocc < _table_size,
00660             ("ID_MAP::Enlarge: Bad logic"));
00661     unocc = -_table[unocc]._next - 2;
00662   }
00663 
00664   _free_list = -1;
00665 
00666   // Now move all node/key pairs into to-be-unoccupied entries and put
00667   // all to-be-occupied entries into the free list.
00668   for (i = _table_size - 1; i >= 0; i--) {
00669     if (_table[i]._next == 0) {
00670       // This entry will be occupied.
00671       if (_table[i]._node != _not_found_value) {
00672         FmtAssert(unocc != -1,
00673                   ("ID_MAP::Enlarge: Insufficient unoccupied entries.\n"
00674                    "                 GROWTH_FACTOR too small WRT "
00675                    "CAPACITY_FACTOR"));
00676         Is_True(Check(0 <= unocc && unocc < _table_size &&
00677                       _table[unocc]._node == _not_found_value),
00678                 ("ID_MAP::Enlarge: Bad logic"));
00679         _table[unocc]._node = _table[i]._node;
00680         _table[unocc]._key  = _table[i]._key;
00681         do {
00682           Is_True(Check(0 <= unocc && unocc < _table_size),
00683                   ("ID_MAP::Enlarge: Bad logic"));
00684           unocc = -_table[unocc]._next - 2;
00685         } while (_table[unocc]._node != _not_found_value);
00686       }
00687       Add_to_free_list(i);
00688     }
00689   }
00690 
00691   save_num_entries = _num_entries;
00692 
00693   // Now go through the node/key pairs stored in the unoccupied list
00694   // and for every pair we find, add its entry to the free list and
00695   // rehash the pair.
00696   for (i = unocc_head; i != -1; i = unocc) {
00697     KEY_TYPE  key  = _table[i]._key;
00698     NODE_TYPE node = _table[i]._node;
00699 
00700     unocc = -_table[i]._next - 2;
00701     // There is much room for performance enhancement in the
00702     // following five lines!
00703     Add_to_free_list(i);
00704 
00705     if (node != _not_found_value) {
00706       // Avoid recursive enlargement of the table!
00707       _num_entries = 0;
00708       Insert(key, node);
00709     }
00710   }
00711 
00712   _num_entries = save_num_entries;
00713 
00714   Is_Trace(_tracing, (TFile, "ID_MAP::Enlarge: Starting verification\n"));
00715   Verify();
00716   Is_Trace(_tracing, (TFile, "ID_MAP::Enlarge: Verification passed\n"));
00717 }
00718 
00719 template <class NODE_TYPE, class KEY_TYPE> void
00720 ID_MAP<NODE_TYPE, KEY_TYPE>::Insert(const KEY_TYPE  key,
00721                                     const NODE_TYPE node)
00722 {
00723   Is_Trace(_tracing && _verbose,
00724            (TFile, "ID_MAP::Insert(%llu, ...) ...\n", Key_as_llu(key)));
00725 
00726   if (_num_entries + 1 > Capacity(_table_size)) {
00727     Is_Trace(_tracing,
00728              (TFile, "Enlarging: will have %u entries\n"
00729               "           table size: %u\n"
00730               "       table capacity: %u\n",
00731               _num_entries + 1, _table_size,
00732               Capacity(_table_size)));
00733     Enlarge();
00734     Is_True(_num_entries <= Capacity(_table_size),
00735             ("ID_MAP::Insert: Enlarge didn't get enough capacity"));
00736   }
00737   else {
00738     Is_Trace(_tracing && _verbose,
00739              (TFile, "Not enlarging: Capacity(%u) = %u\n",
00740               _table_size, Capacity(_table_size)));
00741   }
00742 
00743   mINT32 idx = Hash(key, _table_size);
00744 
00745   Is_Trace(_tracing && _verbose,
00746            (TFile, "     ID_MAP::Insert: inserting at %d\n", idx));
00747 
00748   if (_table[idx]._node != _not_found_value) {
00749     Is_True(Check(_table[idx]._next != _free_list),
00750             ("ID_MAP::Insert: inconsistent relocation"));
00751 
00752     mINT32 displaced = Alloc_from_free_list();
00753 
00754     Is_Trace(_tracing && _verbose,
00755              (TFile, "    displacing [H(%llu)=%d; %d] to %d\n",
00756               Key_as_llu(_table[idx]._key),
00757               Hash(_table[idx]._key, _table_size),
00758               _table[idx]._next, displaced));
00759 
00760     _table[displaced]._node = _table[idx]._node;
00761     _table[displaced]._key  = _table[idx]._key;
00762 
00763     Is_True(Check(_table[idx]._next != displaced),
00764             ("ID_MAP::Insert: inconsistent relocation"));
00765 
00766     _table[displaced]._next = _table[idx]._next;
00767 
00768     Is_True(Check(idx != displaced),
00769             ("ID_MAP::Insert: inconsistent relocation"));
00770     Is_True(Check(displaced != _free_list),
00771             ("ID_MAP::Insert: Bug in Alloc_from_free_list"));
00772 
00773     mINT32 temp_idx = Hash(_table[displaced]._key, _table_size);
00774     if (idx == temp_idx) {
00775       // same hash bucket
00776       _table[idx]._next = displaced;
00777     }
00778     else {
00779       _table[idx]._next = -1;
00780 
00781       // Look for the dangling link to the displaced item
00782       while (temp_idx != -1 && _table[temp_idx]._next != idx) {
00783         temp_idx = _table[temp_idx]._next;
00784       }
00785 
00786 #if Is_True_On
00787       if (temp_idx == -1 || _table[temp_idx]._next != idx) {
00788         fprintf(TFile, "Insert %llu at idx = %d (%d)\n",
00789                 Key_as_llu(key), idx,
00790                 Hash(key, _table_size));
00791         fprintf(TFile, " -> displaced = %d\n", displaced);
00792         fprintf(TFile, "start [H(%llu)=%d]\n", 
00793                 Key_as_llu(_table[displaced]._key),
00794                 Hash(_table[displaced]._key, _table_size));
00795       }
00796 #endif
00797       Is_True(Check(temp_idx != -1 && _table[temp_idx]._next == idx),
00798               ("ID_MAP::Insert: displaced item not found in hash table."));
00799       FmtAssert(temp_idx != -1 && _table[temp_idx]._next == idx,
00800                 ("ID_MAP::Insert: displaced item not found in hash table."));
00801 
00802       _table[temp_idx]._next = displaced;
00803     }
00804   }
00805   else {
00806     Remove_from_free_list(idx);
00807     _table[idx]._next = -1;
00808   }
00809 
00810   _table[idx]._node = node;
00811   _table[idx]._key  = key;
00812   ++_num_entries;
00813 }
00814 
00815 
00816 template <class NODE_TYPE, class KEY_TYPE> void
00817 ID_MAP<NODE_TYPE, KEY_TYPE>::Delete(const KEY_TYPE key)
00818 {
00819   mINT32 idx = Hash(key, _table_size);
00820   mINT32 prev_idx = -1;
00821 
00822   while (idx != -1 &&
00823          _table[idx]._node != _not_found_value &&
00824          _table[idx]._key != key) {
00825     prev_idx = idx;
00826     idx = _table[idx]._next;
00827   }
00828 
00829   FmtAssert(idx != -1 && _table[idx]._node != _not_found_value,
00830             ("ID_MAP::Delete: item not found in hash table."));
00831 
00832   if (prev_idx != -1) {
00833     _table[prev_idx]._next = _table[idx]._next;
00834   }
00835   else {
00836     mINT32 next_idx = _table[idx]._next;
00837     if (next_idx != -1) {
00838       _table[idx]._node = _table[next_idx]._node;
00839       _table[idx]._key  = _table[next_idx]._key;
00840       _table[idx]._next = _table[next_idx]._next;
00841       idx = next_idx;
00842     }
00843   }
00844   Add_to_free_list(idx);
00845   --_num_entries;
00846 }
00847 
00848 
00849 template <class NODE_TYPE, class KEY_TYPE> NODE_TYPE
00850 ID_MAP<NODE_TYPE, KEY_TYPE>::Lookup(const KEY_TYPE key) const
00851 {
00852   mINT32 idx = Entry_lookup(key);
00853   if (idx == -1) {
00854     return _not_found_value;
00855   }
00856   else {
00857     return _table[idx]._node;
00858   }
00859 }
00860 
00861 template <class NODE_TYPE, class KEY_TYPE> mINT32
00862 ID_MAP<NODE_TYPE, KEY_TYPE>::Entry_lookup(const KEY_TYPE key) const
00863 {
00864   mINT32 idx = Hash(key, _table_size);
00865 
00866 #if Is_True_On
00867   mINT32 loopcheck = idx;
00868 #endif
00869 
00870   while (idx != -1 &&
00871          _table[idx]._node != _not_found_value &&
00872          _table[idx]._key != key) {
00873     idx = _table[idx]._next;
00874 
00875 #if Is_True_On
00876     if (loopcheck != -1) {
00877       loopcheck = _table[loopcheck]._next;
00878     }
00879     if (loopcheck != -1) {
00880       loopcheck = _table[loopcheck]._next;
00881     }
00882     if (loopcheck != -1) {
00883       Is_True(Check(loopcheck != idx),
00884               ("ID_MAP::Lookup: loop in hash bucket %lu", idx));
00885     }
00886 #endif
00887   }
00888   if (idx == -1 || _table[idx]._node == _not_found_value) {
00889     return -1;
00890   }
00891   else {
00892     return idx;
00893   }
00894 }
00895 
00896 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines