added Pair and HashTable
This commit is contained in:
		| @ -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); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| ///////////////////////////////// | ///////////////////////////////// | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user