// Hash tables with 2 keys and a value #ifndef _CL_HASH2_H #define _CL_HASH2_H #include "cl_hash.h" #include "cl_iterator.h" // Requirements: // - function bool equal (key1_type,key1_type); // - function bool equal (key2_type,key2_type); // - function unsigned long hashcode (key1_type,key2_type); template struct cl_htentry2 { ALLOCATE_ANYWHERE(cl_htentry2) key1_type key1; key2_type key2; value_type val; const value_type& htvalue () { return val; } cl_htentry2 (const key1_type& k1, const key2_type& k2, const value_type& v) : key1 (k1), key2 (k2), val (v) {} #if (defined(__rs6000__) && !defined(__GNUC__)) cl_htentry2 () {} #endif }; template struct cl_heap_hashtable_2 : public cl_heap_hashtable > { // Allocation. void* operator new (size_t size) { return cl_malloc_hook(size); } // Deallocation. void operator delete (void* ptr) { cl_free_hook(ptr); } public: // Lookup (htref alias gethash). // Returns a pointer which you should immediately dereference // if it is not NULL. value_type* get (const key1_type& key1, const key2_type& key2) { var long index = _slots[hashcode(key1,key2) % _modulus] - 1; while (index >= 0) { if (!(index < _size)) cl_abort(); if (equal(key1,_entries[index].entry.key1) && equal(key2,_entries[index].entry.key2)) return &_entries[index].entry.val; index = _entries[index].next - 1; } return NULL; } // Store (htset alias puthash). void put (const key1_type& key1, const key2_type& key2, const value_type& val) { var unsigned long hcode = hashcode(key1,key2); // Search whether it is already there. { var long index = _slots[hcode % _modulus] - 1; while (index >= 0) { if (!(index < _size)) cl_abort(); if (equal(key1,_entries[index].entry.key1) && equal(key2,_entries[index].entry.key2)) { _entries[index].entry.val = val; return; } index = _entries[index].next - 1; } } // Put it into the table. prepare_store(); var long hindex = hcode % _modulus; // _modulus may have changed! var long index = get_free_index(); new (&_entries[index].entry) cl_htentry2 (key1,key2,val); _entries[index].next = _slots[hindex]; _slots[hindex] = 1+index; _count++; } // Remove (htrem alias remhash). void remove (const key1_type& key1, const key2_type& key2) { var long* _index = &_slots[hashcode(key1,key2) % _modulus]; while (*_index > 0) { var long index = *_index - 1; if (!(index < _size)) cl_abort(); if (equal(key1,_entries[index].entry.key1) && equal(key2,_entries[index].entry.key2)) { // Remove _entries[index].entry *_index = _entries[index].next; _entries[index].~htxentry(); // The entry is now free. put_free_index(index); // That's it. _count--; return; } _index = &_entries[index].next; } } // Iterate through the table. // No stuff should be inserted into the table during the iteration, // or you may find yourself iterating over an entry vector which has // already been freed! // ?? private: // Prepare a store operation: make sure that the free list is non-empty. // This may change the table's size! void prepare_store () { #if !(defined(__sparc__) && !defined(__GNUC__)) if (_freelist < -1) return; // Can we make room? if (_garcol_fun(this)) if (_freelist < -1) return; // No! Have to grow the hash table. grow(); #else // workaround Sun C++ 4.1 inline function compiler bug if (_freelist >= -1) { if (!_garcol_fun(this) || (_freelist >= -1)) grow(); } #endif } void grow () { var long new_size = _size + (_size >> 1) + 1; // _size*1.5 var long new_modulus = compute_modulus(new_size); var void* new_total_vector = cl_malloc_hook(new_modulus*sizeof(long) + new_size*sizeof(htxentry)); var long* new_slots = (long*) ((char*)new_total_vector + 0); var htxentry* new_entries = (htxentry *) ((char*)new_total_vector + new_modulus*sizeof(long)); for (var long hi = new_modulus-1; hi >= 0; hi--) new_slots[hi] = 0; var long free_list_head = -1; for (var long i = new_size-1; i >= 0; i--) { new_entries[i].next = free_list_head; free_list_head = -2-i; } var htxentry* old_entries = _entries; for (var long old_index = 0; old_index < _size; old_index++) if (old_entries[old_index].next >= 0) { var key1_type& key1 = old_entries[old_index].entry.key1; var key2_type& key2 = old_entries[old_index].entry.key2; var value_type& val = old_entries[old_index].entry.val; var long hindex = hashcode(key1,key2) % new_modulus; var long index = -2-free_list_head; free_list_head = new_entries[index].next; new (&new_entries[index].entry) cl_htentry2 (key1,key2,val); new_entries[index].next = new_slots[hindex]; new_slots[hindex] = 1+index; old_entries[old_index].~htxentry(); } cl_free_hook(_total_vector); _modulus = new_modulus; _size = new_size; _freelist = free_list_head; _slots = new_slots; _entries = new_entries; _total_vector = new_total_vector; } }; #endif /* _CL_HASH2_H */