820 lines
22 KiB
C++
820 lines
22 KiB
C++
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
|
|
// License: Boost Software License See LICENSE.txt for the full license.
|
|
#ifndef DLIB_HASH_TABLE_KERNEl_1_
|
|
#define DLIB_HASH_TABLE_KERNEl_1_
|
|
|
|
#include "hash_table_kernel_abstract.h"
|
|
#include "../general_hash/general_hash.h"
|
|
#include "../algs.h"
|
|
#include "../interfaces/map_pair.h"
|
|
#include "../interfaces/enumerable.h"
|
|
#include "../interfaces/remover.h"
|
|
#include "../assert.h"
|
|
#include "../serialize.h"
|
|
#include <functional>
|
|
|
|
|
|
namespace dlib
|
|
{
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager = default_memory_manager,
|
|
typename compare = std::less<domain>
|
|
>
|
|
class hash_table_kernel_1 : public enumerable<map_pair<domain, range> >,
|
|
public pair_remover<domain,range>
|
|
{
|
|
|
|
/*!
|
|
INITIAL VALUE
|
|
hash_size == 0
|
|
table == pointer to an array of num_of_buckets node pointers
|
|
num_of_buckets == the number of buckets in the hash table
|
|
current_element == 0
|
|
at_start_ == true
|
|
mask == num_of_buckets-1
|
|
|
|
CONVENTION
|
|
current_element_valid() == (current_element != 0)
|
|
element() == current_element->d and current_element->r
|
|
at_start_ == at_start()
|
|
if (current_element != 0) then
|
|
table[current_bucket] == a pointer to the linked list that contains
|
|
the node pointed to by current_element
|
|
|
|
mask == num_of_buckets-1
|
|
|
|
|
|
|
|
hash_size = size() == the number of elements in the hash_table and
|
|
table == pointer to an array of num_of_buckets node pointers and
|
|
num_of_buckets == the number of buckets in the hash table and
|
|
for all i:
|
|
table[i] == pointer to the first node in a linked list or
|
|
table[i] == 0 if this bucket is currently not in use
|
|
|
|
|
|
for all nodes:
|
|
d == the domain element stored in this node
|
|
r == the range element stored in this node which is associated with
|
|
d.
|
|
next == pointer to the next node in the linked list or
|
|
next == 0 if this is the last node in the linked list
|
|
|
|
!*/
|
|
|
|
struct node
|
|
{
|
|
node* next;
|
|
domain d;
|
|
range r;
|
|
};
|
|
|
|
|
|
class mpair : public map_pair<domain,range>
|
|
{
|
|
public:
|
|
const domain* d;
|
|
range* r;
|
|
|
|
const domain& key(
|
|
) const { return *d; }
|
|
|
|
const range& value(
|
|
) const { return *r; }
|
|
|
|
range& value(
|
|
) { return *r; }
|
|
};
|
|
|
|
|
|
public:
|
|
|
|
typedef domain domain_type;
|
|
typedef range range_type;
|
|
typedef compare compare_type;
|
|
typedef mem_manager mem_manager_type;
|
|
|
|
explicit hash_table_kernel_1(
|
|
unsigned long expnum
|
|
);
|
|
|
|
virtual ~hash_table_kernel_1(
|
|
);
|
|
|
|
void clear(
|
|
);
|
|
|
|
unsigned long count (
|
|
const domain& item
|
|
) const;
|
|
|
|
void add (
|
|
domain& d,
|
|
range& r
|
|
);
|
|
|
|
void remove (
|
|
const domain& d,
|
|
domain& d_copy,
|
|
range& r
|
|
);
|
|
|
|
void destroy (
|
|
const domain& d
|
|
);
|
|
|
|
const range* operator[] (
|
|
const domain& d
|
|
) const;
|
|
|
|
range* operator[] (
|
|
const domain& d
|
|
);
|
|
|
|
void swap (
|
|
hash_table_kernel_1& item
|
|
);
|
|
|
|
// functions from the remover interface
|
|
void remove_any (
|
|
domain& d,
|
|
range& r
|
|
);
|
|
|
|
// functions from the enumerable interface
|
|
inline size_t size (
|
|
) const;
|
|
|
|
bool at_start (
|
|
) const;
|
|
|
|
inline void reset (
|
|
) const;
|
|
|
|
bool current_element_valid (
|
|
) const;
|
|
|
|
const map_pair<domain,range>& element (
|
|
) const;
|
|
|
|
map_pair<domain,range>& element (
|
|
);
|
|
|
|
bool move_next (
|
|
) const;
|
|
|
|
private:
|
|
|
|
// data members
|
|
typename mem_manager::template rebind<node>::other pool;
|
|
typename mem_manager::template rebind<node*>::other ppool;
|
|
unsigned long hash_size;
|
|
node** table;
|
|
general_hash<domain> hash;
|
|
unsigned long num_of_buckets;
|
|
unsigned long mask;
|
|
|
|
mutable mpair p;
|
|
|
|
mutable unsigned long current_bucket;
|
|
mutable node* current_element;
|
|
mutable bool at_start_;
|
|
compare comp;
|
|
|
|
// restricted functions
|
|
hash_table_kernel_1(hash_table_kernel_1&);
|
|
hash_table_kernel_1& operator=(hash_table_kernel_1&);
|
|
|
|
};
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
inline void swap (
|
|
hash_table_kernel_1<domain,range,mem_manager,compare>& a,
|
|
hash_table_kernel_1<domain,range,mem_manager,compare>& b
|
|
) { a.swap(b); }
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
void deserialize (
|
|
hash_table_kernel_1<domain,range,mem_manager,compare>& item,
|
|
std::istream& in
|
|
)
|
|
{
|
|
try
|
|
{
|
|
item.clear();
|
|
unsigned long size;
|
|
deserialize(size,in);
|
|
domain d;
|
|
range r;
|
|
for (unsigned long i = 0; i < size; ++i)
|
|
{
|
|
deserialize(d,in);
|
|
deserialize(r,in);
|
|
item.add(d,r);
|
|
}
|
|
}
|
|
catch (serialization_error& e)
|
|
{
|
|
item.clear();
|
|
throw serialization_error(e.info + "\n while deserializing object of type hash_table_kernel_1");
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// member function definitions
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
hash_table_kernel_1(
|
|
unsigned long expnum
|
|
) :
|
|
hash_size(0),
|
|
current_element(0),
|
|
at_start_(true)
|
|
{
|
|
|
|
num_of_buckets = 1;
|
|
while (expnum != 0)
|
|
{
|
|
--expnum;
|
|
num_of_buckets <<= 1;
|
|
}
|
|
mask = num_of_buckets-1;
|
|
|
|
table = ppool.allocate_array(num_of_buckets);
|
|
for (unsigned long i = 0; i < num_of_buckets; ++i)
|
|
{
|
|
table[i] = 0;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
~hash_table_kernel_1(
|
|
)
|
|
{
|
|
for (unsigned long i = 0; i < num_of_buckets; ++i)
|
|
{
|
|
// delete this linked list
|
|
node* temp = table[i];
|
|
while (temp)
|
|
{
|
|
node* t = temp;
|
|
temp = temp->next;
|
|
pool.deallocate(t);
|
|
}
|
|
table[i] = 0;
|
|
}
|
|
ppool.deallocate_array(table);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
void hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
clear(
|
|
)
|
|
{
|
|
if (hash_size > 0)
|
|
{
|
|
for (unsigned long i = 0; i < num_of_buckets; ++i)
|
|
{
|
|
// delete this linked list
|
|
node* temp = table[i];
|
|
while (temp)
|
|
{
|
|
node* t = temp;
|
|
temp = temp->next;
|
|
pool.deallocate(t);
|
|
}
|
|
table[i] = 0;
|
|
}
|
|
hash_size = 0;
|
|
}
|
|
// reset the enumerator
|
|
reset();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
size_t hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
size(
|
|
) const
|
|
{
|
|
return hash_size;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
unsigned long hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
count(
|
|
const domain& d
|
|
) const
|
|
{
|
|
unsigned long items_found = 0;
|
|
node* temp = table[hash(d)&mask];
|
|
|
|
while (temp != 0)
|
|
{
|
|
// look for an element equivalent to d
|
|
if ( !(comp(temp->d , d) || comp(d , temp->d)) )
|
|
{
|
|
++items_found;
|
|
}
|
|
temp = temp->next;
|
|
}
|
|
|
|
return items_found;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
void hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
add(
|
|
domain& d,
|
|
range& r
|
|
)
|
|
{
|
|
unsigned long hash_value = hash(d)&mask;
|
|
|
|
// make a new node for this item
|
|
node& temp = *(pool.allocate());
|
|
exchange(d,temp.d);
|
|
exchange(r,temp.r);
|
|
|
|
// add this new node to the head of the linked list in bucket number hash_value
|
|
temp.next = table[hash_value];
|
|
table[hash_value] = &temp;
|
|
|
|
++hash_size;
|
|
|
|
// reset the enumerator
|
|
reset();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
void hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
destroy(
|
|
const domain& d
|
|
)
|
|
{
|
|
node* last;
|
|
const unsigned long hash_value = hash(d)&mask;
|
|
node* temp = table[hash_value];
|
|
|
|
// if there is more than one thing in this bucket
|
|
if (temp->next != 0)
|
|
{
|
|
// start looking with the second item in the list
|
|
last = temp;
|
|
temp = temp->next;
|
|
while (true)
|
|
{
|
|
// if we hit the end of the list without finding item then it must
|
|
// be the first element in the list so splice it out
|
|
if (temp == 0)
|
|
{
|
|
temp = table[hash_value];
|
|
table[hash_value] = temp->next;
|
|
|
|
break;
|
|
}
|
|
|
|
// look for an element equivalent to item
|
|
if ( !(comp(temp->d , d) || comp(d , temp->d)) )
|
|
{
|
|
// splice out the node we want to remove
|
|
last->next = temp->next;
|
|
break;
|
|
}
|
|
|
|
last = temp;
|
|
temp = temp->next;
|
|
}
|
|
|
|
}
|
|
// else there is only one node in this linked list
|
|
else
|
|
{
|
|
table[hash_value] = 0;
|
|
}
|
|
|
|
pool.deallocate(temp);
|
|
|
|
--hash_size;
|
|
|
|
// reset the enumerator
|
|
reset();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
void hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
remove(
|
|
const domain& d,
|
|
domain& d_copy,
|
|
range& r
|
|
)
|
|
{
|
|
node* last;
|
|
const unsigned long hash_value = hash(d)&mask;
|
|
node* temp = table[hash_value];
|
|
|
|
// if there is more than one thing in this bucket
|
|
if (temp->next != 0)
|
|
{
|
|
// start looking with the second item in the list
|
|
last = temp;
|
|
temp = temp->next;
|
|
while (true)
|
|
{
|
|
// if we hit the end of the list without finding item then it must
|
|
// be the first element in the list so splice it out
|
|
if (temp == 0)
|
|
{
|
|
temp = table[hash_value];
|
|
table[hash_value] = temp->next;
|
|
|
|
break;
|
|
}
|
|
|
|
// look for an element equivalent to item
|
|
if ( !(comp(temp->d , d) || comp(d , temp->d)) )
|
|
{
|
|
// splice out the node we want to remove
|
|
last->next = temp->next;
|
|
break;
|
|
}
|
|
|
|
last = temp;
|
|
temp = temp->next;
|
|
}
|
|
|
|
}
|
|
// else there is only one node in this linked list
|
|
else
|
|
{
|
|
table[hash_value] = 0;
|
|
}
|
|
|
|
|
|
exchange(d_copy,temp->d);
|
|
exchange(r,temp->r);
|
|
pool.deallocate(temp);
|
|
|
|
--hash_size;
|
|
|
|
// reset the enumerator
|
|
reset();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
void hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
remove_any(
|
|
domain& d,
|
|
range& r
|
|
)
|
|
{
|
|
unsigned long i = 0;
|
|
|
|
// while the ith bucket is empty keep looking
|
|
while (table[i] == 0)
|
|
{
|
|
++i;
|
|
}
|
|
|
|
// remove the first node in the linked list in the ith bucket
|
|
node& temp = *(table[i]);
|
|
|
|
exchange(temp.d,d);
|
|
exchange(temp.r,r);
|
|
table[i] = temp.next;
|
|
|
|
pool.deallocate(&temp);
|
|
|
|
--hash_size;
|
|
|
|
// reset the enumerator
|
|
reset();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
const range* hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
operator[](
|
|
const domain& d
|
|
) const
|
|
{
|
|
node* temp = table[hash(d)&mask];
|
|
|
|
while (temp != 0)
|
|
{
|
|
// look for an element equivalent to item
|
|
if ( !(comp(temp->d , d) || comp(d , temp->d)) )
|
|
return &(temp->r);
|
|
|
|
temp = temp->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
range* hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
operator[](
|
|
const domain& d
|
|
)
|
|
{
|
|
node* temp = table[hash(d)&mask];
|
|
|
|
while (temp != 0)
|
|
{
|
|
// look for an element equivalent to item
|
|
if ( !(comp(temp->d , d) || comp(d , temp->d)) )
|
|
return &(temp->r);
|
|
|
|
temp = temp->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
void hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
swap(
|
|
hash_table_kernel_1<domain,range,mem_manager,compare>& item
|
|
)
|
|
{
|
|
exchange(mask,item.mask);
|
|
exchange(table,item.table);
|
|
exchange(hash_size,item.hash_size);
|
|
exchange(num_of_buckets,item.num_of_buckets);
|
|
exchange(current_bucket,item.current_bucket);
|
|
exchange(current_element,item.current_element);
|
|
exchange(at_start_,item.at_start_);
|
|
pool.swap(item.pool);
|
|
ppool.swap(item.ppool);
|
|
exchange(p,item.p);
|
|
exchange(comp,item.comp);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// enumerable function definitions
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
bool hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
at_start (
|
|
) const
|
|
{
|
|
return at_start_;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
void hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
reset (
|
|
) const
|
|
{
|
|
at_start_ = true;
|
|
current_element = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
bool hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
current_element_valid (
|
|
) const
|
|
{
|
|
return (current_element != 0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
const map_pair<domain,range>& hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
element (
|
|
) const
|
|
{
|
|
p.d = &(current_element->d);
|
|
p.r = &(current_element->r);
|
|
return p;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
map_pair<domain,range>& hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
element (
|
|
)
|
|
{
|
|
p.d = &(current_element->d);
|
|
p.r = &(current_element->r);
|
|
return p;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename domain,
|
|
typename range,
|
|
typename mem_manager,
|
|
typename compare
|
|
>
|
|
bool hash_table_kernel_1<domain,range,mem_manager,compare>::
|
|
move_next (
|
|
) const
|
|
{
|
|
if (at_start_)
|
|
{
|
|
at_start_ = false;
|
|
// if the queue is empty then there is nothing to do
|
|
if (hash_size == 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// find the first element in the hash table
|
|
for (current_bucket = 0; true ; ++current_bucket)
|
|
{
|
|
if (table[current_bucket] != 0)
|
|
{
|
|
current_element = table[current_bucket];
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we have already enumerated every element
|
|
if (current_element == 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// find the next element if it exists
|
|
if (current_element->next != 0)
|
|
{
|
|
current_element = current_element->next;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// find next bucket with something in it
|
|
for (current_bucket+=1; current_bucket<num_of_buckets; ++current_bucket)
|
|
{
|
|
if (table[current_bucket] != 0)
|
|
{
|
|
// we just found the next bucket
|
|
current_element = table[current_bucket];
|
|
break;
|
|
}
|
|
}
|
|
// make sure we actually found another nonempty bucket
|
|
if (current_bucket == num_of_buckets)
|
|
{
|
|
// we didn't find anything
|
|
current_element = 0;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// we found another bucket
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
}
|
|
|
|
#endif // DLIB_HASH_TABLE_KERNEl_1_
|
|
|