You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
5.8 KiB
158 lines
5.8 KiB
// Hash tables for making objects unique
|
|
|
|
#ifndef _CL_HASHUNIQ_H
|
|
#define _CL_HASHUNIQ_H
|
|
|
|
#include "base/hash/cl_hash.h"
|
|
#include "base/cl_iterator.h"
|
|
|
|
namespace cln {
|
|
|
|
// In such a hash table an entry's key is determined by its value
|
|
// and not stored explicitly.
|
|
|
|
// Requirements:
|
|
// - function bool equal (key1_type,key1_type);
|
|
// - function unsigned long hashcode (key1_type);
|
|
// - function key1_type hashkey (value_type);
|
|
// - constructor value_type::value_type (struct hashuniq *, key1_type);
|
|
|
|
template <class key1_type, class value_type>
|
|
struct cl_htuniqentry {
|
|
ALLOCATE_ANYWHERE(cl_htuniqentry)
|
|
value_type val;
|
|
const value_type& htvalue () { return val; }
|
|
cl_htuniqentry (const value_type& v)
|
|
: val (v) {}
|
|
};
|
|
|
|
template <class key1_type, class value_type>
|
|
struct cl_heap_hashtable_uniq : public cl_heap_hashtable <cl_htuniqentry <key1_type,value_type> > {
|
|
protected:
|
|
// Abbreviations.
|
|
typedef cl_heap_hashtable <cl_htuniqentry <key1_type,value_type> > inherited;
|
|
typedef typename inherited::htxentry htxentry;
|
|
public:
|
|
// Allocation.
|
|
void* operator new (size_t size) { return malloc_hook(size); }
|
|
// Deallocation.
|
|
void operator delete (void* ptr) { 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& key)
|
|
{
|
|
var long index = this->_slots[hashcode(key) % this->_modulus] - 1;
|
|
while (index >= 0) {
|
|
if (!(index < this->_size))
|
|
throw runtime_exception();
|
|
if (equal(key,hashkey(this->_entries[index].entry.val)))
|
|
return &this->_entries[index].entry.val;
|
|
index = this->_entries[index].next - 1;
|
|
}
|
|
return NULL;
|
|
}
|
|
// Store (htset alias puthash).
|
|
void put (const key1_type& key)
|
|
{
|
|
var unsigned long hcode = hashcode(key);
|
|
// Search whether it is already there.
|
|
{
|
|
var long index = this->_slots[hcode % this->_modulus] - 1;
|
|
while (index >= 0) {
|
|
if (!(index < this->_size))
|
|
throw runtime_exception();
|
|
if (equal(key,hashkey(this->_entries[index].entry.val)))
|
|
return;
|
|
index = this->_entries[index].next - 1;
|
|
}
|
|
}
|
|
// Put it into the table.
|
|
prepare_store();
|
|
var long hindex = hcode % this->_modulus; // _modulus may have changed!
|
|
var long index = this->get_free_index();
|
|
new (&this->_entries[index].entry) cl_htuniqentry<key1_type,value_type> (value_type((struct hashuniq *)0, key));
|
|
this->_entries[index].next = this->_slots[hindex];
|
|
this->_slots[hindex] = 1+index;
|
|
this->_count++;
|
|
}
|
|
// Remove (htrem alias remhash).
|
|
void remove (const key1_type& key)
|
|
{
|
|
var long* _index = &this->_slots[hashcode(key) % this->_modulus];
|
|
while (*_index > 0) {
|
|
var long index = *_index - 1;
|
|
if (!(index < this->_size))
|
|
throw runtime_exception();
|
|
if (equal(key,hashkey(this->_entries[index].entry.val))) {
|
|
// Remove _entries[index].entry
|
|
*_index = this->_entries[index].next;
|
|
this->_entries[index].~htxentry();
|
|
// The entry is now free.
|
|
this->put_free_index(index);
|
|
// That's it.
|
|
this->_count--;
|
|
return;
|
|
}
|
|
_index = &this->_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 (this->_freelist < -1)
|
|
return;
|
|
// Can we make room?
|
|
if (this->_garcol_fun(this))
|
|
if (this->_freelist < -1)
|
|
return;
|
|
// No! Have to grow the hash table.
|
|
grow();
|
|
}
|
|
void grow ()
|
|
{
|
|
var long new_size = this->_size + (this->_size >> 1) + 1; // _size*1.5
|
|
var long new_modulus = inherited::compute_modulus(new_size);
|
|
var void* new_total_vector = 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 = this->_entries;
|
|
for (var long old_index = 0; old_index < this->_size; old_index++)
|
|
if (old_entries[old_index].next >= 0) {
|
|
var value_type& val = old_entries[old_index].entry.val;
|
|
var long hindex = hashcode(hashkey(val)) % new_modulus;
|
|
var long index = -2-free_list_head;
|
|
free_list_head = new_entries[index].next;
|
|
new (&new_entries[index].entry) cl_htuniqentry<key1_type,value_type> (val);
|
|
new_entries[index].next = new_slots[hindex];
|
|
new_slots[hindex] = 1+index;
|
|
old_entries[old_index].~htxentry();
|
|
}
|
|
free_hook(this->_total_vector);
|
|
this->_modulus = new_modulus;
|
|
this->_size = new_size;
|
|
this->_freelist = free_list_head;
|
|
this->_slots = new_slots;
|
|
this->_entries = new_entries;
|
|
this->_total_vector = new_total_vector;
|
|
}
|
|
};
|
|
|
|
} // namespace cln
|
|
|
|
#endif /* _CL_HASHUNIQ_H */
|