added Pair and HashTable
This commit is contained in:
parent
67a5f4f5f5
commit
6e92bb2985
@ -52,7 +52,19 @@ struct HashListComparator
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename MPOOL = Mpool, typename HASHER = HashListHasher<T>, typename COMPARATOR = HashListComparator<T> >
|
struct HashListResizer
|
||||||
|
{
|
||||||
|
qse_size_t operator() (qse_size_t current) const
|
||||||
|
{
|
||||||
|
return (current < 5000)? (current + current):
|
||||||
|
(current < 50000)? (current + (current / 2)):
|
||||||
|
(current < 100000)? (current + (current / 4)):
|
||||||
|
(current < 150000)? (current + (current / 8)):
|
||||||
|
(current + (current / 16));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename MPOOL = Mpool, typename HASHER = HashListHasher<T>, typename COMPARATOR = HashListComparator<T>, typename RESIZER = HashListResizer >
|
||||||
class HashList: public Mmged
|
class HashList: public Mmged
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -114,6 +126,10 @@ public:
|
|||||||
|
|
||||||
this->load_factor = load_factor;
|
this->load_factor = load_factor;
|
||||||
this->threshold = node_capacity * load_factor / 100;
|
this->threshold = node_capacity * load_factor / 100;
|
||||||
|
|
||||||
|
// the memory manager for the linked list must raise an exception
|
||||||
|
// upon memory allocation error.
|
||||||
|
QSE_ASSERT (this->getMmgr()->isExceptionRaising());
|
||||||
}
|
}
|
||||||
|
|
||||||
HashList (const SelfType& list): Mmged (list)
|
HashList (const SelfType& list): Mmged (list)
|
||||||
@ -428,13 +444,14 @@ protected:
|
|||||||
qse_size_t load_factor;
|
qse_size_t load_factor;
|
||||||
HASHER hasher;
|
HASHER hasher;
|
||||||
COMPARATOR comparator;
|
COMPARATOR comparator;
|
||||||
|
RESIZER resizer;
|
||||||
|
|
||||||
void rehash ()
|
void rehash ()
|
||||||
{
|
{
|
||||||
// Move nodes around instead of values to prevent
|
// Move nodes around instead of values to prevent
|
||||||
// existing values from being copied over and destroyed.
|
// existing values from being copied over and destroyed.
|
||||||
// this incurs less number of memory allocations also.
|
// this incurs less number of memory allocations also.
|
||||||
SelfType temp (this->getMmgr(), this->node_capacity << 1, this->load_factor, this->datum_list->getMPBlockSize());
|
SelfType temp (this->getMmgr(), this->resizer(this->node_capacity), this->load_factor, this->datum_list->getMPBlockSize());
|
||||||
Node* p = this->datum_list->getHeadNode();
|
Node* p = this->datum_list->getHeadNode();
|
||||||
while (p)
|
while (p)
|
||||||
{
|
{
|
||||||
|
630
qse/include/qse/cmn/HashTable.hpp
Normal file
630
qse/include/qse/cmn/HashTable.hpp
Normal file
@ -0,0 +1,630 @@
|
|||||||
|
/*
|
||||||
|
* $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_HASHTABLE_HPP_
|
||||||
|
#define _QSE_CMN_HASHTABLE_HPP_
|
||||||
|
|
||||||
|
#include <qse/Hashable.hpp>
|
||||||
|
#include <qse/cmn/LinkedList.hpp>
|
||||||
|
#include <qse/cmn/Pair.hpp>
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
QSE_BEGIN_NAMESPACE(QSE)
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct HashTableHasher
|
||||||
|
{
|
||||||
|
qse_size_t operator() (const T& v) const
|
||||||
|
{
|
||||||
|
return v.hashCode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashTableResizer
|
||||||
|
{
|
||||||
|
qse_size_t operator() (qse_size_t current) const
|
||||||
|
{
|
||||||
|
return (current < 5000)? (current + current):
|
||||||
|
(current < 50000)? (current + (current / 2)):
|
||||||
|
(current < 100000)? (current + (current / 4)):
|
||||||
|
(current < 150000)? (current + (current / 8)):
|
||||||
|
(current + (current / 16));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename K, typename V, typename HASHER = HashTableHasher<K>, typename RESIZER = HashTableResizer>
|
||||||
|
class HashTable: public Mmged
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef Pair<K,V> Entry;
|
||||||
|
typedef LinkedList<Entry> Bucket;
|
||||||
|
typedef HashTable<K,V,HASHER,RESIZER> SelfType;
|
||||||
|
|
||||||
|
HashTable (Mmgr* mmgr, qse_size_t bucket_size = 10, qse_size_t load_factor = 75): Mmged(mmgr)
|
||||||
|
{
|
||||||
|
this->entry_count = 0;
|
||||||
|
this->bucket_size = bucket_size;
|
||||||
|
this->buckets = new Bucket[bucket_size];
|
||||||
|
this->load_factor = load_factor;
|
||||||
|
this->threshold = bucket_size * load_factor / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashTable (const SelfType& table)
|
||||||
|
{
|
||||||
|
this->entry_count = 0;
|
||||||
|
this->bucket_size = table.bucket_size;
|
||||||
|
this->buckets = new Bucket[table.bucket_size];
|
||||||
|
this->load_factor = table.load_factor;
|
||||||
|
this->threshold = table.bucket_size * table.load_factor / 100;
|
||||||
|
|
||||||
|
for (qse_size_t i = 0; i < table.bucket_size; i++)
|
||||||
|
{
|
||||||
|
Bucket& b = table.buckets[i];
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = b.head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
qse_size_t hc = this->hasher(e.key) % this->bucket_size;
|
||||||
|
this->buckets[hc].append (e);
|
||||||
|
this->entry_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// doesn't need to rehash in the copy constructor.
|
||||||
|
//if (entry_count >= threshold) rehash ();
|
||||||
|
}
|
||||||
|
|
||||||
|
~HashTable ()
|
||||||
|
{
|
||||||
|
this->clear ();
|
||||||
|
if (this->buckets) delete[] this->buckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelfType& operator= (const SelfType& table)
|
||||||
|
{
|
||||||
|
this->clear ();
|
||||||
|
|
||||||
|
for (qse_size_t i = 0; i < table.bucket_size; i++)
|
||||||
|
{
|
||||||
|
Bucket& b = table.buckets[i];
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = b.head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
qse_size_t hc = this->hasher(e.key) % this->bucket_size;
|
||||||
|
this->buckets[hc].append (e);
|
||||||
|
entry_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->entry_count >= this->threshold) this->rehash ();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
qse_size_t getSize () const
|
||||||
|
{
|
||||||
|
return this->entry_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEmpty () const
|
||||||
|
{
|
||||||
|
return this->entry_count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
qse_size_t getBucketSize () const
|
||||||
|
{
|
||||||
|
return this->bucket_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bucket& getBucket (qse_size_t index) const
|
||||||
|
{
|
||||||
|
qse_assert (index < this->bucket_size);
|
||||||
|
return this->buckets[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
V& operator[] (const K& key)
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % this->bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) return e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_count >= threshold)
|
||||||
|
{
|
||||||
|
this->rehash ();
|
||||||
|
hc = this->hasher(key) % bucket_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry& e2 = buckets[hc].append (Entry(key));
|
||||||
|
entry_count++;
|
||||||
|
return e2.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const V& operator[] (const K& key) const
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % this->bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) return e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_count >= threshold)
|
||||||
|
{
|
||||||
|
this->rehash ();
|
||||||
|
hc = this->hasher(key) % bucket_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry& e2 = buckets[hc].append (Entry(key));
|
||||||
|
entry_count++;
|
||||||
|
return e2.value;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// NOTE: getConstWithCustomKey() and getWithCustomKey() would
|
||||||
|
// not need to have different names if compilers were smarter.
|
||||||
|
//
|
||||||
|
|
||||||
|
template <typename MK, typename MHASHER>
|
||||||
|
const V* getConstWithCustomKey (const MK& key) const
|
||||||
|
{
|
||||||
|
MHASHER h;
|
||||||
|
qse_size_t hc = h(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward()) {
|
||||||
|
const Entry& e = np->value;
|
||||||
|
if (key == e.key) return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QSE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename MK, typename MHASHER>
|
||||||
|
V* getWithCustomKey (const MK& key) const
|
||||||
|
{
|
||||||
|
MHASHER h;
|
||||||
|
qse_size_t hc = h(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward()) {
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QSE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
V* get (const K& key)
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward()) {
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QSE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const V* get (const K& key) const
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward()) {
|
||||||
|
const Entry& e = np->value;
|
||||||
|
if (key == e.key) return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QSE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
V* get (const K& key, qse_size_t* hash_code, typename Bucket::Node** node)
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward()) {
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) {
|
||||||
|
*hash_code = hc;
|
||||||
|
*node = np;
|
||||||
|
return &e.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QSE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const V* get (const K& key, qse_size_t* hash_code, typename Bucket::Node** node) const
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key)
|
||||||
|
{
|
||||||
|
*hash_code = hc;
|
||||||
|
*node = np;
|
||||||
|
return &e.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QSE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int put (const K& key, const V& value)
|
||||||
|
{
|
||||||
|
upsert (key, value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int putNew (const K& key, const V& value)
|
||||||
|
{
|
||||||
|
return (insertNew(key,value) == QSE_NULL)? -1: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
V* insert (const K& key)
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_count >= threshold)
|
||||||
|
{
|
||||||
|
rehash ();
|
||||||
|
hc = this->hasher(key) % bucket_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry& e = buckets[hc].append (Entry(key));
|
||||||
|
entry_count++;
|
||||||
|
return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
V* insert (const K& key, const V& value)
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_count >= threshold)
|
||||||
|
{
|
||||||
|
rehash ();
|
||||||
|
hc = this->hasher(key) % bucket_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry& e = buckets[hc].append (Entry(key,value));
|
||||||
|
entry_count++;
|
||||||
|
return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
V* insertNew (const K& key)
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) return QSE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_count >= threshold)
|
||||||
|
{
|
||||||
|
rehash ();
|
||||||
|
hc = this->hasher(key) % bucket_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry& e = buckets[hc].append (Entry(key));
|
||||||
|
entry_count++;
|
||||||
|
return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
V* insertNew (const K& key, const V& value)
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) return QSE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_count >= threshold)
|
||||||
|
{
|
||||||
|
rehash ();
|
||||||
|
hc = this->hasher(key) % bucket_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry& e = buckets[hc].append (Entry(key, value));
|
||||||
|
entry_count++;
|
||||||
|
return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
V* upsert (const K& key, const V& value)
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key)
|
||||||
|
{
|
||||||
|
e.value = value;
|
||||||
|
return &e.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry_count >= threshold)
|
||||||
|
{
|
||||||
|
rehash ();
|
||||||
|
hc = this->hasher(key) % bucket_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry& e = buckets[hc].append (Entry(key,value));
|
||||||
|
entry_count++;
|
||||||
|
return &e.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename MK, typename MHASHER>
|
||||||
|
int removeWithCustomKey (const MK& key)
|
||||||
|
{
|
||||||
|
MHASHER h;
|
||||||
|
qse_size_t hc = h(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key)
|
||||||
|
{
|
||||||
|
this->buckets[hc].remove (np);
|
||||||
|
this->entry_count--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int remove (const K& key)
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key)
|
||||||
|
{
|
||||||
|
this->buckets[hc].remove (np);
|
||||||
|
this->entry_count--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int remove (qse_size_t hc, typename Bucket::Node* np)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// WARNING: this method should be used with extra care.
|
||||||
|
//
|
||||||
|
this->buckets[hc].remove (np);
|
||||||
|
this->entry_count--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
qse_size_t removeValue (const V& value)
|
||||||
|
{
|
||||||
|
qse_size_t count = 0;
|
||||||
|
|
||||||
|
for (qse_size_t i = 0; i < this->bucket_size; i++)
|
||||||
|
{
|
||||||
|
typename Bucket::Node* np, * np2;
|
||||||
|
np = buckets[i].head();
|
||||||
|
while (np != QSE_NULL) {
|
||||||
|
Entry& e = np->value;
|
||||||
|
np2 = np->forward ();
|
||||||
|
if (value == e.value) {
|
||||||
|
this->remove (i, np);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
np = np2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsKey (const K& key) const
|
||||||
|
{
|
||||||
|
qse_size_t hc = this->hasher(key) % bucket_size;
|
||||||
|
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[hc].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (key == e.key) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsValue (const K& value) const
|
||||||
|
{
|
||||||
|
for (qse_size_t i = 0; i < bucket_size; i++)
|
||||||
|
{
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[i].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (value == e.value) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear (int new_bucket_size = 0)
|
||||||
|
{
|
||||||
|
for (qse_size_t i = 0; i < bucket_size; i++) buckets[i].clear ();
|
||||||
|
entry_count = 0;
|
||||||
|
|
||||||
|
if (new_bucket_size > 0)
|
||||||
|
{
|
||||||
|
Bucket* tmp = new Bucket[new_bucket_size];
|
||||||
|
bucket_size = new_bucket_size;
|
||||||
|
threshold = bucket_size * load_factor / 100;
|
||||||
|
delete[] buckets;
|
||||||
|
buckets = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (SelfType::*TraverseCallback)
|
||||||
|
(const Entry& entry, void* user_data) const;
|
||||||
|
|
||||||
|
int traverse (TraverseCallback callback, void* user_data) const
|
||||||
|
{
|
||||||
|
for (qse_size_t i = 0; i < this->bucket_size; i++)
|
||||||
|
{
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[i].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
const Entry& e = np->value;
|
||||||
|
if ((this->*callback)(e,user_data) == -1) return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (*StaticTraverseCallback)
|
||||||
|
(SelfType* table, Entry& entry, void* user_data);
|
||||||
|
|
||||||
|
int traverse (StaticTraverseCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
for (qse_size_t i = 0; i < this->bucket_size; i++)
|
||||||
|
{
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[i].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
Entry& e = np->value;
|
||||||
|
if (callback(this,e,user_data) == -1) return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mutable qse_size_t entry_count;
|
||||||
|
mutable qse_size_t bucket_size;
|
||||||
|
mutable Bucket* buckets;
|
||||||
|
mutable qse_size_t threshold;
|
||||||
|
qse_size_t load_factor;
|
||||||
|
HASHER hasher;
|
||||||
|
RESIZER resizer;
|
||||||
|
|
||||||
|
void rehash () const
|
||||||
|
{
|
||||||
|
qse_size_t new_bucket_size = this->resizer (this->bucket_size);
|
||||||
|
Bucket* new_buckets = new Bucket[new_bucket_size];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (qse_size_t i = 0; i < this->bucket_size; i++)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
typename Bucket::Node* np;
|
||||||
|
for (np = buckets[i].head(); np; np = np->forward())
|
||||||
|
{
|
||||||
|
const Entry& e = np->value;
|
||||||
|
qse_size_t hc = e.key.hashCode() % new_bucket_size;
|
||||||
|
new_buckets[hc].append (e);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// this approach save redundant memory allocation
|
||||||
|
// and retains the previous pointers before rehashing.
|
||||||
|
// if the bucket uses a memory pool, this would not
|
||||||
|
// work. fortunately, the hash table doesn't use it
|
||||||
|
// for a bucket.
|
||||||
|
typename Bucket::Node* np = buckets[i].head();
|
||||||
|
while (np)
|
||||||
|
{
|
||||||
|
typename Bucket::Node* next = np->forward();
|
||||||
|
const Entry& e = np->value;
|
||||||
|
qse_size_t hc = this->hasher(e.key) % new_bucket_size;
|
||||||
|
new_buckets[hc].appendNode (buckets[i].yield(np));
|
||||||
|
np = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
delete[] new_buckets;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] this->buckets;
|
||||||
|
this->buckets = new_buckets;
|
||||||
|
this->bucket_size = new_bucket_size;
|
||||||
|
this->threshold = this->load_factor * this->bucket_size / 100;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
QSE_END_NAMESPACE(QSE)
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
#endif
|
@ -35,13 +35,29 @@
|
|||||||
QSE_BEGIN_NAMESPACE(QSE)
|
QSE_BEGIN_NAMESPACE(QSE)
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
|
||||||
/// The HeapMmgr class implements the heap-based memory manager interface.
|
/// The HeapMmgr class implements a memory management interface that
|
||||||
/// It is a memory manager that's memory managed by another memory manager.
|
/// handles a memory heap of a given size. Notably, it is a memory manager
|
||||||
|
/// managed by another memory manager.
|
||||||
|
///
|
||||||
|
/// \code
|
||||||
|
/// QSE::HeapMmgr heap_mmgr (QSE::Mmgr::getDFL(), 30000);
|
||||||
|
/// QSE::LinkedList<int> int_list (&heap_mmgr);
|
||||||
|
/// int_list.append (10);
|
||||||
|
/// int_list.append (20);
|
||||||
|
/// \endcode
|
||||||
|
|
||||||
class QSE_EXPORT HeapMmgr: public Mmgr, public Mmged
|
class QSE_EXPORT HeapMmgr: public Mmgr, public Mmged
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/// The constructor function accepts an memory manager \a mmgr that
|
||||||
|
/// is used to create a heap of the size \a heap_size. Optionally,
|
||||||
|
/// you can ask the memory manager to return #QSE_NULL upon allocation
|
||||||
|
/// failure by setting \a raise_exception to false.
|
||||||
HeapMmgr (Mmgr* mmgr, qse_size_t heap_size, bool raise_exception = true);
|
HeapMmgr (Mmgr* mmgr, qse_size_t heap_size, bool raise_exception = true);
|
||||||
|
|
||||||
|
/// The destructor function frees the heap. Memory areas returned by
|
||||||
|
/// allocate(), reallocate(), allocMem(), reallocMem() are invalidated
|
||||||
|
/// all at once.
|
||||||
~HeapMmgr ();
|
~HeapMmgr ();
|
||||||
|
|
||||||
void* allocMem (qse_size_t n);
|
void* allocMem (qse_size_t n);
|
||||||
|
@ -103,6 +103,10 @@ public:
|
|||||||
this->node_count = 0;
|
this->node_count = 0;
|
||||||
this->head_node = QSE_NULL;
|
this->head_node = QSE_NULL;
|
||||||
this->tail_node = QSE_NULL;
|
this->tail_node = QSE_NULL;
|
||||||
|
|
||||||
|
// the memory manager for the linked list must raise an exception
|
||||||
|
// upon memory allocation error.
|
||||||
|
QSE_ASSERT (this->getMmgr()->isExceptionRaising());
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkedList (const SelfType& ll): Mmged(ll.getMmgr()), mp (ll.getMmgr(), ll.mp.getDatumSize(), ll.mp.getBlockSize())
|
LinkedList (const SelfType& ll): Mmged(ll.getMmgr()), mp (ll.getMmgr(), ll.mp.getDatumSize(), ll.mp.getBlockSize())
|
||||||
|
@ -52,6 +52,6 @@ pkginclude_HEADERS = \
|
|||||||
if ENABLE_CXX
|
if ENABLE_CXX
|
||||||
pkginclude_HEADERS += \
|
pkginclude_HEADERS += \
|
||||||
Mmgr.hpp StdMmgr.hpp HeapMmgr.hpp Mmged.hpp \
|
Mmgr.hpp StdMmgr.hpp HeapMmgr.hpp Mmged.hpp \
|
||||||
Mpool.hpp Mpoolable.hpp LinkedList.hpp HashList.hpp
|
Mpool.hpp Pair.hpp LinkedList.hpp HashList.hpp HashTable.hpp
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ build_triplet = @build@
|
|||||||
host_triplet = @host@
|
host_triplet = @host@
|
||||||
@ENABLE_CXX_TRUE@am__append_1 = \
|
@ENABLE_CXX_TRUE@am__append_1 = \
|
||||||
@ENABLE_CXX_TRUE@ Mmgr.hpp StdMmgr.hpp HeapMmgr.hpp Mmged.hpp \
|
@ENABLE_CXX_TRUE@ Mmgr.hpp StdMmgr.hpp HeapMmgr.hpp Mmged.hpp \
|
||||||
@ENABLE_CXX_TRUE@ Mpool.hpp Mpoolable.hpp LinkedList.hpp HashList.hpp
|
@ENABLE_CXX_TRUE@ Mpool.hpp Pair.hpp LinkedList.hpp HashList.hpp HashTable.hpp
|
||||||
|
|
||||||
subdir = include/qse/cmn
|
subdir = include/qse/cmn
|
||||||
DIST_COMMON = $(am__pkginclude_HEADERS_DIST) $(srcdir)/Makefile.am \
|
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 \
|
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 \
|
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 HeapMmgr.hpp Mmged.hpp \
|
utf8.h xma.h Mmgr.hpp StdMmgr.hpp HeapMmgr.hpp Mmged.hpp \
|
||||||
Mpool.hpp Mpoolable.hpp LinkedList.hpp HashList.hpp
|
Mpool.hpp Pair.hpp LinkedList.hpp HashList.hpp HashTable.hpp
|
||||||
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
|
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
|
||||||
am__vpath_adj = case $$p in \
|
am__vpath_adj = case $$p in \
|
||||||
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
|
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
|
||||||
|
@ -81,6 +81,15 @@ public:
|
|||||||
///
|
///
|
||||||
virtual ~Mmgr () {}
|
virtual ~Mmgr () {}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The isExceptionRaising() function tells if the memory manager
|
||||||
|
/// throws an exception upon a memory allocation error.
|
||||||
|
///
|
||||||
|
bool isExceptionRaising () const
|
||||||
|
{
|
||||||
|
return this->raise_exception;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// The allocate() function calls allocMem() for memory
|
/// The allocate() function calls allocMem() for memory
|
||||||
/// allocation. if it fails, it raise an exception if it's
|
/// allocation. if it fails, it raise an exception if it's
|
||||||
|
@ -24,52 +24,34 @@
|
|||||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _QSE_CMN_MPOOLABLE_HPP_
|
#ifndef _QSE_CMN_PAIR_HPP_
|
||||||
#define _QSE_CMN_MPOOLABLE_HPP_
|
#define _QSE_CMN_PAIR_HPP_
|
||||||
|
|
||||||
#include <qse/cmn/Mpool.hpp>
|
#include <qse/types.h>
|
||||||
|
#include <qse/macros.h>
|
||||||
// use size_t as some compilers complain about qse_size_t used in new().
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
QSE_BEGIN_NAMESPACE(QSE)
|
QSE_BEGIN_NAMESPACE(QSE)
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
|
||||||
class QSE_EXPORT Mpoolable
|
template <typename KEY, typename VALUE> class Pair
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
KEY key;
|
||||||
|
VALUE value;
|
||||||
|
|
||||||
/*
|
Pair () {}
|
||||||
inline void* operator new (mp_size_t size)
|
Pair (const KEY& key): key (key) {}
|
||||||
{
|
Pair (const KEY& key, const VALUE& value): key (key), value (value) {}
|
||||||
return ::operator new (size);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator delete (void* ptr)
|
KEY& getKey () { return this->key; }
|
||||||
{
|
const KEY& getKey () const { return this->key; }
|
||||||
::operator delete (ptr);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
inline void* operator new (size_t size, Mpool* mp)
|
VALUE& getValue () { return this->value; }
|
||||||
{
|
const VALUE& getValue () const { return this->value; }
|
||||||
return mp->isEnabled()? mp->allocate (): ::operator new (size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
void setKey (const KEY& key) { this->key = key; }
|
||||||
void operator delete (void* ptr, Mpool* mp)
|
void setValue (const VALUE& value) { this->value = value; }
|
||||||
{
|
|
||||||
if (mp->isEnabled()) mp->dispose (ptr);
|
|
||||||
else ::operator delete (mp);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
inline void dispose (void* ptr, Mpool* mp)
|
|
||||||
{
|
|
||||||
if (mp->isEnabled()) mp->dispose (ptr);
|
|
||||||
else ::operator delete (mp);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
@ -80,10 +80,7 @@ void* HeapMmgr::reallocMem (void* ptr, qse_size_t n)
|
|||||||
|
|
||||||
void HeapMmgr::freeMem (void* ptr)
|
void HeapMmgr::freeMem (void* ptr)
|
||||||
{
|
{
|
||||||
if (this->xma)
|
if (this->xma) qse_xma_free (this->xma, ptr);
|
||||||
{
|
|
||||||
qse_xma_free (this->xma, ptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
Loading…
x
Reference in New Issue
Block a user