added the HashList class
This commit is contained in:
parent
36829a55d7
commit
dc118785c7
507
qse/include/qse/cmn/HashList.hpp
Normal file
507
qse/include/qse/cmn/HashList.hpp
Normal file
@ -0,0 +1,507 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
Copyright (c) 2006-2014 Chung, Hyung-Hwan. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _QSE_CMN_HASHLIST_CLASS_
|
||||
#define _QSE_CMN_HASHLIST_CLASS_
|
||||
|
||||
#include <qse/Hashable.hpp>
|
||||
#include <qse/cmn/LinkedList.hpp>
|
||||
|
||||
/////////////////////////////////
|
||||
QSE_BEGIN_NAMESPACE(QSE)
|
||||
/////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
struct HashListHasher
|
||||
{
|
||||
qse_size_t operator() (const T& v) const
|
||||
{
|
||||
return v.hashCode();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct HashListComparator
|
||||
{
|
||||
bool operator() (const T& v1, const T& v2) const
|
||||
{
|
||||
return v1 == v2;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: use MPOLL for nodes???
|
||||
|
||||
template <typename T, typename MPOOL = Mpool, typename HASHER = HashListHasher<T>, typename COMPARATOR = HashListComparator<T> >
|
||||
class HashList
|
||||
{
|
||||
public:
|
||||
typedef LinkedList<T,MPOOL> DatumList;
|
||||
typedef typename DatumList::Node Node;
|
||||
typedef HashList<T,MPOOL,HASHER,COMPARATOR> SelfType;
|
||||
|
||||
HashList (
|
||||
qse_size_t node_capacity = 10,
|
||||
qse_size_t load_factor = 75,
|
||||
qse_size_t mpb_size = 0)/*: datum_list (mpb_size)*/
|
||||
{
|
||||
this->nodes = QSE_NULL;
|
||||
this->node_capacity = 0;
|
||||
this->datum_list = QSE_NULL;
|
||||
|
||||
try
|
||||
{
|
||||
this->nodes = new Node*[node_capacity << 1];
|
||||
|
||||
this->node_capacity = node_capacity;
|
||||
for (qse_size_t i = 0; i < (node_capacity << 1); i++)
|
||||
{
|
||||
this->nodes[i] = QSE_NULL;
|
||||
}
|
||||
|
||||
this->datum_list = new DatumList (mpb_size);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (this->nodes)
|
||||
{
|
||||
delete[] this->nodes;
|
||||
this->node_capacity = 0;
|
||||
this->nodes = QSE_NULL;
|
||||
}
|
||||
|
||||
if (this->datum_list)
|
||||
{
|
||||
delete this->datum_list;
|
||||
this->datum_list = QSE_NULL;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
this->load_factor = load_factor;
|
||||
this->threshold = node_capacity * load_factor / 100;
|
||||
}
|
||||
|
||||
HashList (const SelfType& list)/*: datum_list (list.datum_list.getMPBlockSize()) */
|
||||
{
|
||||
this->nodes = QSE_NULL;
|
||||
this->node_capacity = 0;
|
||||
this->datum_list = QSE_NULL;
|
||||
|
||||
try
|
||||
{
|
||||
this->nodes = new Node*[list.node_capacity << 1];
|
||||
this->node_capacity = list.node_capacity;
|
||||
for (qse_size_t i = 0; i < list.node_capacity << 1; i++)
|
||||
{
|
||||
this->nodes[i] = QSE_NULL;
|
||||
}
|
||||
|
||||
this->datum_list = new DatumList (list.datum_list->getMPBlockSize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (this->nodes)
|
||||
{
|
||||
delete[] this->nodes;
|
||||
this->node_capacity = 0;
|
||||
this->nodes = QSE_NULL;
|
||||
}
|
||||
if (this->datum_list)
|
||||
{
|
||||
delete this->datum_list;
|
||||
this->datum_list = QSE_NULL;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
this->load_factor = list.load_factor;
|
||||
this->threshold = list.threshold;
|
||||
|
||||
for (qse_size_t i = 0; i < list.node_capacity; i++)
|
||||
{
|
||||
qse_size_t head = i << 1;
|
||||
qse_size_t tail = head + 1;
|
||||
|
||||
Node* np = list.nodes[head];
|
||||
if (!np) continue;
|
||||
|
||||
do
|
||||
{
|
||||
copy_datum (np, this->node_capacity, this->nodes, this->datum_list);
|
||||
if (np == list.nodes[tail]) break;
|
||||
np = np->getNextNode ();
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
}
|
||||
|
||||
~HashList ()
|
||||
{
|
||||
this->clear ();
|
||||
if (this->nodes) delete[] this->nodes;
|
||||
if (this->datum_list) delete this->datum_list;
|
||||
}
|
||||
|
||||
SelfType& operator= (const SelfType& list)
|
||||
{
|
||||
this->clear ();
|
||||
|
||||
for (qse_size_t i = 0; i < list.node_capacity; i++)
|
||||
{
|
||||
qse_size_t head = i << 1;
|
||||
qse_size_t tail = head + 1;
|
||||
|
||||
Node* np = list.nodes[head];
|
||||
if (np == QSE_NULL) continue;
|
||||
|
||||
do
|
||||
{
|
||||
copy_datum (np, this->node_capacity, this->nodes, this->datum_list);
|
||||
if (np == list.nodes[tail]) break;
|
||||
np = np->getNextNode ();
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
Node* insert_value (const T& datum, bool overwrite = true)
|
||||
{
|
||||
qse_size_t hc, head, tail;
|
||||
Node* np;
|
||||
|
||||
hc = this->hasher(datum) % this->node_capacity;
|
||||
head = hc << 1; tail = head + 1;
|
||||
|
||||
np = this->nodes[head];
|
||||
if (np)
|
||||
{
|
||||
do
|
||||
{
|
||||
T& t = np->value;
|
||||
if (this->comparator(datum, t))
|
||||
{
|
||||
if (!overwrite) return QSE_NULL;
|
||||
t = datum;
|
||||
return np;
|
||||
}
|
||||
|
||||
if (np == this->nodes[tail]) break;
|
||||
np = np->getNextNode ();
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
|
||||
if (datum_list->getSize() >= threshold)
|
||||
{
|
||||
this->rehash ();
|
||||
hc = this->hasher(datum) % this->node_capacity;
|
||||
head = hc << 1; tail = head + 1;
|
||||
}
|
||||
|
||||
if (nodes[head] == QSE_NULL)
|
||||
{
|
||||
this->nodes[head] = this->datum_list->insertValue (QSE_NULL, datum);
|
||||
this->nodes[tail] = this->nodes[head];
|
||||
}
|
||||
else
|
||||
{
|
||||
this->nodes[head] = datum_list->insertValue (this->nodes[head], datum);
|
||||
}
|
||||
|
||||
return this->nodes[head];
|
||||
}
|
||||
|
||||
public:
|
||||
Node* insert (const T& datum)
|
||||
{
|
||||
return this->insert_value (datum, false);
|
||||
}
|
||||
|
||||
Node* upsert (const T& datum)
|
||||
{
|
||||
return this->insert_value (datum, true);
|
||||
}
|
||||
|
||||
protected:
|
||||
const Node* find_node (const T& datum) const
|
||||
{
|
||||
qse_size_t hc, head, tail;
|
||||
Node* np;
|
||||
|
||||
hc = this->hasher(datum) % this->node_capacity;
|
||||
head = hc << 1; tail = head + 1;
|
||||
|
||||
np = this->nodes[head];
|
||||
if (np)
|
||||
{
|
||||
do
|
||||
{
|
||||
T& t = np->value;
|
||||
if (datum == t) return np;
|
||||
if (np == this->nodes[tail]) break;
|
||||
np = np->getNextNode ();
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
Node* findNode (const T& datum)
|
||||
{
|
||||
return (Node*)this->find_node (datum);
|
||||
}
|
||||
|
||||
const Node* findNode (const T& datum) const
|
||||
{
|
||||
return this->find_node (datum);
|
||||
}
|
||||
|
||||
T* findValue (const T& datum)
|
||||
{
|
||||
Node* b = this->findNode (datum);
|
||||
if (!b) return QSE_NULL;
|
||||
return &b->value;
|
||||
}
|
||||
|
||||
const T* findValue (const T& datum) const
|
||||
{
|
||||
const Node* b = this->findNode (datum);
|
||||
if (!b) return QSE_NULL;
|
||||
return &b->value;
|
||||
}
|
||||
|
||||
bool isEmpty () const
|
||||
{
|
||||
return this->datum_list->isEmpty();
|
||||
}
|
||||
|
||||
bool contains (const T& datum) const
|
||||
{
|
||||
return this->findNode (datum) != QSE_NULL;
|
||||
}
|
||||
|
||||
int remove (const T& datum)
|
||||
{
|
||||
qse_size_t hc, head, tail;
|
||||
Node* np;
|
||||
|
||||
hc = this->hasher(datum) % this->node_capacity;
|
||||
head = hc << 1; tail = head + 1;
|
||||
|
||||
np = this->nodes[head];
|
||||
if (np)
|
||||
{
|
||||
do
|
||||
{
|
||||
T& t = np->value;
|
||||
if (this->comparator(datum, t))
|
||||
{
|
||||
if (this->nodes[head] == this->nodes[tail])
|
||||
{
|
||||
QSE_ASSERT (np == this->nodes[head]);
|
||||
this->nodes[head] = this->nodes[tail] = QSE_NULL;
|
||||
}
|
||||
else if (np == this->nodes[head])
|
||||
{
|
||||
this->nodes[head] = np->getNextNode();
|
||||
}
|
||||
else if (np == this->nodes[tail])
|
||||
{
|
||||
this->nodes[tail] = np->getPrevNode();
|
||||
}
|
||||
|
||||
this->datum_list->remove (np);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (np == this->nodes[tail]) break;
|
||||
np = np->getNextNode ();
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
for (qse_size_t i = 0; i < (this->node_capacity << 1); i++)
|
||||
{
|
||||
this->nodes[i] = QSE_NULL;
|
||||
}
|
||||
if (this->datum_list) this->datum_list->clear ();
|
||||
}
|
||||
|
||||
Node* getHeadNode () const
|
||||
{
|
||||
return datum_list->getHeadNode();
|
||||
}
|
||||
|
||||
Node* getTaileNode () const
|
||||
{
|
||||
return datum_list->getTailNode();
|
||||
}
|
||||
|
||||
typedef int (SelfType::*TraverseCallback) (Node* start, Node* cur);
|
||||
|
||||
void traverse (TraverseCallback callback, Node* start)
|
||||
{
|
||||
Node* cur, *prev, * next;
|
||||
|
||||
cur = start;
|
||||
while (cur)
|
||||
{
|
||||
prev = cur->getPrevNode ();
|
||||
next = cur->getNextNode ();
|
||||
|
||||
int n = (this->*callback) (start, cur);
|
||||
|
||||
if (n > 0) cur = next;
|
||||
else if (n < 0) cur = prev;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
qse_size_t getCapacity() const
|
||||
{
|
||||
return this->node_capacity;
|
||||
}
|
||||
|
||||
qse_size_t getSize () const
|
||||
{
|
||||
return this->datum_list->getSize();
|
||||
}
|
||||
|
||||
protected:
|
||||
mutable qse_size_t node_capacity;
|
||||
mutable Node** nodes;
|
||||
mutable DatumList* datum_list;
|
||||
mutable qse_size_t threshold;
|
||||
qse_size_t load_factor;
|
||||
HASHER hasher;
|
||||
COMPARATOR comparator;
|
||||
|
||||
void rehash ()
|
||||
{
|
||||
// Move nodes around instead of values to prevent
|
||||
// existing values from being copied over and destroyed.
|
||||
// this incurs less number of memory allocations also.
|
||||
SelfType temp (this->node_capacity << 1, this->load_factor, this->datum_list->getMPBlockSize());
|
||||
Node* p = this->datum_list->getHeadNode();
|
||||
while (p)
|
||||
{
|
||||
Node* next = p->getNextNode();
|
||||
|
||||
// p->value must be a unique value in the existing hashed list.
|
||||
// i can safely skip checking existing values.
|
||||
|
||||
// detach the existing node.
|
||||
Node* pp = this->datum_list->yield (p, true);
|
||||
|
||||
// get the hash code using the new capacity
|
||||
qse_size_t hc, head, tail;
|
||||
hc = this->hasher(pp->value) % temp.node_capacity;
|
||||
head = hc << 1; tail = head + 1;
|
||||
|
||||
// insert the detached node to the new temporary list
|
||||
if (temp.nodes[head])
|
||||
{
|
||||
temp.nodes[head] = temp.datum_list->insertNode (temp.nodes[head], pp);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp.nodes[head] = temp.datum_list->insertNode (QSE_NULL, pp);
|
||||
temp.nodes[tail] = temp.nodes[head];
|
||||
}
|
||||
|
||||
p = next;
|
||||
}
|
||||
|
||||
// all nodes must have been popped out.
|
||||
QSE_ASSERT (this->datum_list->getSize() <= 0);
|
||||
|
||||
// clear the node pointers by force as no nodes exist in the old list.
|
||||
for (qse_size_t i = 0; i < (this->node_capacity << 1); i++)
|
||||
{
|
||||
this->nodes[i] = QSE_NULL;
|
||||
}
|
||||
|
||||
// swap the contents
|
||||
qse_size_t temp_capa = temp.node_capacity;
|
||||
Node** temp_nodes = temp.nodes;
|
||||
DatumList* temp_datum_list = temp.datum_list;
|
||||
qse_size_t temp_threshold = temp.threshold;
|
||||
qse_size_t temp_load_factor = temp.load_factor;
|
||||
|
||||
temp.node_capacity = this->node_capacity;
|
||||
temp.nodes = this->nodes;
|
||||
temp.datum_list = this->datum_list;
|
||||
temp.threshold = this->threshold;
|
||||
temp.load_factor = this->load_factor;
|
||||
|
||||
this->node_capacity = temp_capa;
|
||||
this->nodes = temp_nodes;
|
||||
this->datum_list = temp_datum_list;
|
||||
this->threshold = temp_threshold;
|
||||
this->load_factor = temp_load_factor;
|
||||
}
|
||||
|
||||
void copy_datum (
|
||||
Node* np, qse_size_t new_node_capa,
|
||||
Node** new_nodes, DatumList* new_datum_list) const
|
||||
{
|
||||
T& t = np->value;
|
||||
|
||||
qse_size_t hc = this->hasher(t) % new_node_capa;
|
||||
qse_size_t head = hc << 1;
|
||||
qse_size_t tail = head + 1;
|
||||
|
||||
if (new_nodes[head] == QSE_NULL)
|
||||
{
|
||||
new_nodes[head] = new_datum_list->insertValue (QSE_NULL, t);
|
||||
new_nodes[tail] = new_nodes[head];
|
||||
}
|
||||
else
|
||||
{
|
||||
new_nodes[head] = new_datum_list->insertValue (new_nodes[head], t);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////
|
||||
QSE_END_NAMESPACE(QSE)
|
||||
/////////////////////////////////
|
||||
|
||||
#endif
|
@ -141,7 +141,7 @@ public:
|
||||
|
||||
qse_size_t getMPBlockSize() const
|
||||
{
|
||||
return this->mp.blockSize();
|
||||
return this->mp.getBlockSize();
|
||||
}
|
||||
|
||||
bool isMPEnabled () const
|
||||
@ -280,7 +280,7 @@ public:
|
||||
QSE_ASSERT (this->node_count > 0);
|
||||
|
||||
if (node->next)
|
||||
node->next->prev = node->next;
|
||||
node->next->prev = node->prev;
|
||||
else
|
||||
this->tail_node = node->prev;
|
||||
|
||||
|
@ -52,6 +52,6 @@ pkginclude_HEADERS = \
|
||||
if ENABLE_CXX
|
||||
pkginclude_HEADERS += \
|
||||
Mmgr.hpp StdMmgr.hpp ExcMmgr.hpp Mmged.hpp Mpool.hpp Mpoolable.hpp \
|
||||
LinkedList.hpp
|
||||
LinkedList.hpp HashList.hpp
|
||||
endif
|
||||
|
||||
|
@ -52,7 +52,7 @@ build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
@ENABLE_CXX_TRUE@am__append_1 = \
|
||||
@ENABLE_CXX_TRUE@ Mmgr.hpp StdMmgr.hpp ExcMmgr.hpp Mmged.hpp Mpool.hpp Mpoolable.hpp \
|
||||
@ENABLE_CXX_TRUE@ LinkedList.hpp
|
||||
@ENABLE_CXX_TRUE@ LinkedList.hpp HashList.hpp
|
||||
|
||||
subdir = include/qse/cmn
|
||||
DIST_COMMON = $(am__pkginclude_HEADERS_DIST) $(srcdir)/Makefile.am \
|
||||
@ -91,7 +91,7 @@ am__pkginclude_HEADERS_DIST = alg.h chr.h cp949.h cp950.h dir.h dll.h \
|
||||
nwio.h oht.h opt.h path.h pio.h pma.h rbt.h rex.h sck.h sio.h \
|
||||
sll.h slmb.h str.h task.h time.h tio.h tmr.h tre.h uni.h uri.h \
|
||||
utf8.h xma.h Mmgr.hpp StdMmgr.hpp ExcMmgr.hpp Mmged.hpp \
|
||||
Mpool.hpp Mpoolable.hpp LinkedList.hpp
|
||||
Mpool.hpp Mpoolable.hpp LinkedList.hpp HashList.hpp
|
||||
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
|
||||
am__vpath_adj = case $$p in \
|
||||
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
|
Loading…
x
Reference in New Issue
Block a user