Open64 (mfef90, whirl2f, and IR tools)
TAG: version-openad; SVN changeset: 916
|
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