// 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 namespace dlib { template < typename domain, typename range, typename mem_manager = default_memory_manager, typename compare = std::less > class hash_table_kernel_1 : public enumerable >, public pair_remover { /*! 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 { 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& element ( ) const; map_pair& element ( ); bool move_next ( ) const; private: // data members typename mem_manager::template rebind::other pool; typename mem_manager::template rebind::other ppool; unsigned long hash_size; node** table; general_hash 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& a, hash_table_kernel_1& b ) { a.swap(b); } template < typename domain, typename range, typename mem_manager, typename compare > void deserialize ( hash_table_kernel_1& 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:: 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:: ~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:: 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:: size( ) const { return hash_size; } // ---------------------------------------------------------------------------------------- template < typename domain, typename range, typename mem_manager, typename compare > unsigned long hash_table_kernel_1:: 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:: 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:: 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:: 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:: 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:: 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:: 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:: swap( hash_table_kernel_1& 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:: at_start ( ) const { return at_start_; } // ---------------------------------------------------------------------------------------- template < typename domain, typename range, typename mem_manager, typename compare > void hash_table_kernel_1:: reset ( ) const { at_start_ = true; current_element = 0; } // ---------------------------------------------------------------------------------------- template < typename domain, typename range, typename mem_manager, typename compare > bool hash_table_kernel_1:: current_element_valid ( ) const { return (current_element != 0); } // ---------------------------------------------------------------------------------------- template < typename domain, typename range, typename mem_manager, typename compare > const map_pair& hash_table_kernel_1:: 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& hash_table_kernel_1:: 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:: 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