From 5a4c94584c82cf8dc9bb92cd485d8f844d4f0061 Mon Sep 17 00:00:00 2001 From: saad0105050 Date: Mon, 15 Sep 2014 23:54:45 -0400 Subject: [PATCH] Fixed LinearProbingHashTable allocation issue. Now the table stores objects of key-value pairs, not pointers to them. The class is still heavy, in the sense that it uses a lot of inline functions to hide actual storage policy (pointers/objects). An efficient implementation (or future rewrite) can replace these inline functions with code to directly access/change table elements. Important: Now we use malloc/free to allocate/deallocate table memory. This is because we cannot use memset() to zero-initialize the memory allocated by new, because then delete[] fails. (It destroys internal pointers at the begniing of each allocated object.) Signed-off-by: saad0105050 --- YASI_12/ds.LinearProbingHashTable.h | 72 +++++++++++++++-------------- YASI_12/ds.hashtablebase.h | 33 +++++++++---- YASI_12/main.cpp | 18 ++++---- 3 files changed, 69 insertions(+), 54 deletions(-) diff --git a/YASI_12/ds.LinearProbingHashTable.h b/YASI_12/ds.LinearProbingHashTable.h index bebc374..c8a8f0a 100644 --- a/YASI_12/ds.LinearProbingHashTable.h +++ b/YASI_12/ds.LinearProbingHashTable.h @@ -19,7 +19,7 @@ namespace ds{ class LinearProbingHashTable : public HashTableBase < Key, Value, - EntryType*, // storing objects, not pointers + EntryType, // storing objects, not pointers HashFunction > { ///////////////// enable testing /////////////////// friend class LinearProbingHashTableTest; @@ -28,7 +28,7 @@ namespace ds{ //typedef KVPair Pair; //typedef EntryType Pair; // technically, this pair may not have a value - typedef EntryType* BucketType; // each bucket holds an EntryType object + typedef EntryType BucketType; // each bucket holds an EntryType object typedef EntryType Pair; // which is actually a key-value pair typedef EntryType* BucketEntryPtr; // ptr to an entry object typedef Key KeyType; @@ -40,7 +40,7 @@ namespace ds{ char* _pZeroKey; // all zeros up to #bytes of a Key; used to check if a Key is zero char* _pZeroValue; // all zeros up to #bytes of a Value; used to set a Value to zero -#if 0 +#if 1 inline unsigned int circularNext(const int index) const{ return modSize(index + 1); } @@ -168,7 +168,10 @@ namespace ds{ // forecefully provide a table (and associated state values) // for test purpose only void forceTable(BucketType* newTable, const unsigned int newLogSize, const unsigned int newPopulation, HashFunction& newHashFunction){ + // deallocate current table clear(); + deallocTable(table); + // set new table table = newTable; _logSize = newLogSize; _size = 1 << _logSize; @@ -227,20 +230,20 @@ namespace ds{ return false; // return isNull(bucket); } virtual void copyTable(BucketType* oldTable, const unsigned int oldSize) override { - //// the zeroPair stays intact because it is determined by key, not hash value - //// copy table elements - ////BucketType* oldTable = (BucketType*)prevTable; - //_population = _zeroUsed ? 1 : 0; - // - //for (unsigned int i = 0; i < oldSize; i++){ - // EntryType p = oldTable[i]; - // if ( !isKeyZero(&p.key)){ - // insert(p.key, p.value); - // // no need to delete because it is not pointer anymore - // //DELETE_SAFE(oldTable[i]); - // } - //} - //// count the zero element + // the zeroPair stays intact because it is determined by key, not hash value + // copy table elements + //BucketType* oldTable = (BucketType*)prevTable; + _population = _zeroUsed ? 1 : 0; + + for (unsigned int i = 0; i < oldSize; i++){ + EntryType p = oldTable[i]; + if ( !isKeyZero(&p.key)){ + insert(p.key, p.value); + // no need to delete because it is not pointer anymore + //DELETE_SAFE(oldTable[i]); + } + } + // count the zero element } @@ -258,17 +261,17 @@ namespace ds{ public: LinearProbingHashTable(unsigned int logSize = INIT_LOGSIZE) : _zeroUsed(false), HashTableBase(logSize){ - //_pZeroKey = new char[sizeof(Key)]; - //_pZeroValue = new char[sizeof(Value)]; + _pZeroKey = new char[sizeof(Key)]; + _pZeroValue = new char[sizeof(Value)]; - //memset(_pZeroKey, 0, sizeof(Key)); + memset(_pZeroKey, 0, sizeof(Key)); - //setKeyZero(&_zeroKeyEntry.key); + setKeyZero(&_zeroKeyEntry.key); } virtual ~LinearProbingHashTable(){ - //clear(); - //DELETE_SAFE(_pZeroKey); - //DELETE_SAFE(_pZeroValue); + clear(); + DELETE_SAFE(_pZeroKey); + DELETE_SAFE(_pZeroValue); } virtual Value* get(const Key& key) const override { //Pair* kv = lookupKey(key); @@ -278,13 +281,13 @@ namespace ds{ return NULL; } virtual void put(const Key& key, const Value& value) override { - //return insert(key, value); + return insert(key, value); } virtual bool contains(const Key& key) const override { - return false; // lookupKey(key) != NULL; + return lookupKey(key) != NULL; } virtual void remove(const Key& k) override { -#if 0 +#if 1 // zero key if (isKeyZero(&k)){ if (_zeroUsed) { @@ -440,7 +443,7 @@ namespace ds{ public: -#if 0 +#if 1 void insert(){ IntHashTable h(4); @@ -813,12 +816,11 @@ namespace ds{ h.remove(0); ASSERT_EQ(false, h._zeroUsed) << "_zeroUsed true"; ASSERT_EQ(0, h._zeroKeyEntry.key) << "key of zero element non-zero"; - ASSERT_EQ(0, h._zeroKeyEntry.value) << "value of zero element non-zero"; } { SCOPED_TRACE("remove from an all-filled table"); // create a pathological table - BucketType* badTable = new BucketType[8]; // 8 entries in the table + BucketType* badTable = h.allocTable(8); // 8 entries in the table for (int i = 0; i < 8; i++){ badTable[i] = Pair(5, i); // all keys are 5 } @@ -982,12 +984,12 @@ namespace ds{ } }; - //ADD_TEST_F(LinearProbingHashTableTest, insert); - //ADD_TEST_F(LinearProbingHashTableTest, copyTable); - //ADD_TEST_F(LinearProbingHashTableTest, growCondition); - //ADD_TEST_F(LinearProbingHashTableTest, remove); - //ADD_TEST_F(LinearProbingHashTableTest, shrinkCondition); + ADD_TEST_F(LinearProbingHashTableTest, insert); + ADD_TEST_F(LinearProbingHashTableTest, copyTable); + ADD_TEST_F(LinearProbingHashTableTest, growCondition); + ADD_TEST_F(LinearProbingHashTableTest, shrinkCondition); ADD_TEST_F(LinearProbingHashTableTest, isKeyZero); + ADD_TEST_F(LinearProbingHashTableTest, remove); } } \ No newline at end of file diff --git a/YASI_12/ds.hashtablebase.h b/YASI_12/ds.hashtablebase.h index 5f8c290..5b9f13a 100644 --- a/YASI_12/ds.hashtablebase.h +++ b/YASI_12/ds.hashtablebase.h @@ -86,8 +86,7 @@ class HashTableBase : public IKeyValueStore < Key, Value >{ } void initTable(BucketType* pTable, const int numElems){ // initialize to zero - const int sizeOfEntry = sizeof(BucketType); - memset(pTable, 0, numElems * sizeOfEntry); + memset(pTable, 0, numElems * sizeof(BucketType)); } inline float density(){ return ((float) _population) / _size; } // true if the specified population would be too dense for current table @@ -95,6 +94,9 @@ class HashTableBase : public IKeyValueStore < Key, Value >{ // true if the specified population would be too sparse for current table inline float needShrink(const int pop) const { return (((float)pop) / _size) <= DENSITY_MIN; } + // pure virtual function + virtual inline bool isBucketEmpty(const int bucket) const = 0; + inline unsigned int maxPopulationWithoutGrow() const{ return (unsigned int)(_size * DENSITY_MAX) - 1; } @@ -102,8 +104,6 @@ class HashTableBase : public IKeyValueStore < Key, Value >{ return (unsigned int)(_size * DENSITY_MIN) + 1; } - virtual bool isBucketEmpty(const int bucket) const = 0; - virtual void copyTable(BucketType* oldTable, const unsigned int oldSize) = 0; // grows/shrinks by specified logFactor // that is, newSize is either oldSize << logFactor (grow) @@ -132,26 +132,39 @@ class HashTableBase : public IKeyValueStore < Key, Value >{ _logSize = newLogSize; _size = newSize; _population = 0; - table = new BucketType[newSize]; // twice the current size + + table = allocTable(newSize); // twice the current size + initTable(table, newSize); // initialize with zero // copy old elements // copy table elements + copyTable(oldTable, oldSize); // now delete oldTable - DELETE_ARR_SAFE(oldTable); + deallocTable(oldTable); } // method resize + + // allocates memory for hashtable + BucketType* allocTable(const int numElems){ + // *pTable = new BucketType[newSize]; // twice the current size + BucketType* pTable = (BucketType*)malloc(numElems * sizeof(BucketType)); + return pTable; + } + void deallocTable(BucketType* pTable){ + // DELETE_ARR_SAFE(pTable); + FREE_SAFE(pTable); + } public: // the type of the entries in the hash table HashTableBase():HashTableBase(INIT_LOGSIZE){} HashTableBase(unsigned int logSize = INIT_LOGSIZE) : _logSize(logSize), _size(1 << logSize), _population(0), DENSITY_MAX(0.75), DENSITY_MIN(0.25){ assert(_logSize > 0); - //table = new BucketType[_size]; - table = (BucketType*)malloc(_size * sizeof(BucketType)); + // table = new BucketType[_size]; + table = allocTable(_size); initTable(table, _size); } virtual ~HashTableBase(){ - //DELETE_ARR_SAFE(table); - FREE_SAFE(table); + deallocTable(table); _size = _population = _logSize = 0; } diff --git a/YASI_12/main.cpp b/YASI_12/main.cpp index 203ff2f..1c2fa49 100644 --- a/YASI_12/main.cpp +++ b/YASI_12/main.cpp @@ -1,13 +1,13 @@ #include "common.h" -//#include "ds.singlylinkedlist.h" -//#include "ds.doublylinkedlist.h" -//#include "ds.arraybinarytree.h" -//#include "ds.binaryheap.h" -//#include "ds.binarytree.h" -//#include "ds.binarysearchtree.h" -//#include "ds.priorityqueue.h" -//#include "ds.BSTDictionary.h" -//#include "ds.separatechaininghashtable.h" +#include "ds.singlylinkedlist.h" +#include "ds.doublylinkedlist.h" +#include "ds.arraybinarytree.h" +#include "ds.binaryheap.h" +#include "ds.binarytree.h" +#include "ds.binarysearchtree.h" +#include "ds.priorityqueue.h" +#include "ds.BSTDictionary.h" +#include "ds.separatechaininghashtable.h" #include "ds.linearprobinghashtable.h" //#include "Sorter.h"