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
|
||||
{
|
||||
public:
|
||||
@ -114,6 +126,10 @@ public:
|
||||
|
||||
this->load_factor = load_factor;
|
||||
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)
|
||||
@ -428,13 +444,14 @@ protected:
|
||||
qse_size_t load_factor;
|
||||
HASHER hasher;
|
||||
COMPARATOR comparator;
|
||||
RESIZER resizer;
|
||||
|
||||
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->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();
|
||||
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)
|
||||
/////////////////////////////////
|
||||
|
||||
/// The HeapMmgr class implements the heap-based memory manager interface.
|
||||
/// It is a memory manager that's memory managed by another memory manager.
|
||||
/// The HeapMmgr class implements a memory management interface that
|
||||
/// 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
|
||||
{
|
||||
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);
|
||||
|
||||
/// The destructor function frees the heap. Memory areas returned by
|
||||
/// allocate(), reallocate(), allocMem(), reallocMem() are invalidated
|
||||
/// all at once.
|
||||
~HeapMmgr ();
|
||||
|
||||
void* allocMem (qse_size_t n);
|
||||
|
@ -103,6 +103,10 @@ public:
|
||||
this->node_count = 0;
|
||||
this->head_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())
|
||||
|
@ -52,6 +52,6 @@ pkginclude_HEADERS = \
|
||||
if ENABLE_CXX
|
||||
pkginclude_HEADERS += \
|
||||
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
|
||||
|
||||
|
@ -52,7 +52,7 @@ build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
@ENABLE_CXX_TRUE@am__append_1 = \
|
||||
@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
|
||||
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 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 = case $$p in \
|
||||
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
|
@ -81,6 +81,15 @@ public:
|
||||
///
|
||||
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
|
||||
/// 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.
|
||||
*/
|
||||
|
||||
#ifndef _QSE_CMN_MPOOLABLE_HPP_
|
||||
#define _QSE_CMN_MPOOLABLE_HPP_
|
||||
#ifndef _QSE_CMN_PAIR_HPP_
|
||||
#define _QSE_CMN_PAIR_HPP_
|
||||
|
||||
#include <qse/cmn/Mpool.hpp>
|
||||
|
||||
// use size_t as some compilers complain about qse_size_t used in new().
|
||||
#include <stddef.h>
|
||||
#include <qse/types.h>
|
||||
#include <qse/macros.h>
|
||||
|
||||
/////////////////////////////////
|
||||
QSE_BEGIN_NAMESPACE(QSE)
|
||||
/////////////////////////////////
|
||||
|
||||
class QSE_EXPORT Mpoolable
|
||||
template <typename KEY, typename VALUE> class Pair
|
||||
{
|
||||
public:
|
||||
KEY key;
|
||||
VALUE value;
|
||||
|
||||
/*
|
||||
inline void* operator new (mp_size_t size)
|
||||
{
|
||||
return ::operator new (size);
|
||||
}
|
||||
Pair () {}
|
||||
Pair (const KEY& key): key (key) {}
|
||||
Pair (const KEY& key, const VALUE& value): key (key), value (value) {}
|
||||
|
||||
inline void operator delete (void* ptr)
|
||||
{
|
||||
::operator delete (ptr);
|
||||
}
|
||||
*/
|
||||
KEY& getKey () { return this->key; }
|
||||
const KEY& getKey () const { return this->key; }
|
||||
|
||||
inline void* operator new (size_t size, Mpool* mp)
|
||||
{
|
||||
return mp->isEnabled()? mp->allocate (): ::operator new (size);
|
||||
}
|
||||
VALUE& getValue () { return this->value; }
|
||||
const VALUE& getValue () const { return this->value; }
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
void operator delete (void* ptr, Mpool* mp)
|
||||
{
|
||||
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
|
||||
void setKey (const KEY& key) { this->key = key; }
|
||||
void setValue (const VALUE& value) { this->value = value; }
|
||||
};
|
||||
|
||||
/////////////////////////////////
|
@ -80,10 +80,7 @@ void* HeapMmgr::reallocMem (void* ptr, qse_size_t n)
|
||||
|
||||
void HeapMmgr::freeMem (void* ptr)
|
||||
{
|
||||
if (this->xma)
|
||||
{
|
||||
qse_xma_free (this->xma, ptr);
|
||||
}
|
||||
if (this->xma) qse_xma_free (this->xma, ptr);
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
|
Loading…
x
Reference in New Issue
Block a user