Skip to content

Commit

Permalink
Fixed LinearProbingHashTable allocation issue.
Browse files Browse the repository at this point in the history
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 <saad0105050@gmail.com>
  • Loading branch information
saad0105050 committed Sep 16, 2014
1 parent a490f54 commit 5a4c945
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 54 deletions.
72 changes: 37 additions & 35 deletions YASI_12/ds.LinearProbingHashTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,7 +28,7 @@ namespace ds{

//typedef KVPair<Key, Value> 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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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

}

Expand All @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -440,7 +443,7 @@ namespace ds{


public:
#if 0
#if 1
void insert(){

IntHashTable h(4);
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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);

}
}
33 changes: 23 additions & 10 deletions YASI_12/ds.hashtablebase.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,24 +86,24 @@ 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
inline float needGrow(const int pop) const { return (((float)pop) / _size) >= DENSITY_MAX; }
// 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;
}
inline unsigned int minPopulationWithoutShrink() const{
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)
Expand Down Expand Up @@ -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;
}

Expand Down
18 changes: 9 additions & 9 deletions YASI_12/main.cpp
Original file line number Diff line number Diff line change
@@ -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"

Expand Down

0 comments on commit 5a4c945

Please sign in to comment.