Skip to content

Commit

Permalink
Modified implementation to store air-objects instead of pair-pointers.
Browse files Browse the repository at this point in the history
Major issue: deleting LinearProbingHashTable throws exception violation.
  • Loading branch information
saad0105050 committed Sep 15, 2014
1 parent 1f66ce3 commit 7b7002b
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 110 deletions.
229 changes: 122 additions & 107 deletions YASI_12/ds.LinearProbingHashTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,24 @@ namespace ds{
class LinearProbingHashTable : public HashTableBase <
Key,
Value,
EntryType*,
EntryType, // storing objects, not pointers
HashFunction > {
///////////////// enable testing ///////////////////
friend class LinearProbingHashTableTest;
public:
// an EntryType object is stored in the table

//typedef KVPair<Key, Value> Pair;
typedef EntryType Pair; // technically, this pair may not have a value
typedef Pair* BucketType;
//typedef EntryType Pair; // technically, this pair may not have a value
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;
typedef Value ValueType;
typedef BucketType BucketEntryPtr; // need to change
protected:
// flag about the usage of the first [0] slot
bool _zeroUsed; // true if an entry with key=0 exists
Value _zeroValue;
Pair _zeroPair; // the entry that was mapped to zero index
EntryType _zeroKeyEntry; // the entry that was mapped to zero index
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

Expand All @@ -42,22 +44,6 @@ namespace ds{
return modSize(index - 1);
}

// only fundamental key types are checked to be zero
// non-fundamental types are never zero
// we do this because sizeof(Key) includes the padding bytes for non-fundamental types;
// we cannot predict value at those bytes
inline bool isKeyZero(const Key* pKey) const{
return memcmp(pKey, _pZeroKey, sizeof(Key)) == 0;
}
inline bool isValueZero(const Value* pValue) const{
return memcmp(pValue, _pZeroValue, sizeof(Value)) == 0;
}
inline void setKeyZero(Key* pKey) const{
memcpy(pKey, _pZeroKey, sizeof(Key));
}
inline void setValueZero(Value* pValue) const{
memcpy(pValue, _pZeroValue, sizeof(Value));
}

// if a key is already there, it is updated
void insert(const Key& k, const Value& v){
Expand All @@ -75,7 +61,7 @@ namespace ds{
BucketEntryPtr lookupKey(const Key& k) const {
// test the zero key
if (isKeyZero(&k)){
if (_zeroUsed) return const_cast<Pair*>(&_zeroPair);
if (_zeroUsed) return const_cast<Pair*>(&_zeroKeyEntry);
else return NULL;
}
else{
Expand Down Expand Up @@ -116,12 +102,12 @@ namespace ds{
// we will use a special slot for this entry
if (_zeroUsed == false)
_zeroUsed = true;
_zeroPair.key = k;
_zeroKeyEntry.key = k;
_population++;
// see if we need to size up
if (needGrow(_population))
grow();
return &_zeroPair;
return &_zeroKeyEntry;
}
else{
// key is non-zero
Expand All @@ -148,7 +134,7 @@ namespace ds{
}
else{
// create a new entry
table[cur] = new Pair(k);
makeEntry(cur,k);
_population++;
// return the pointer to the entry
return entryptr(cur);
Expand Down Expand Up @@ -181,10 +167,11 @@ namespace ds{
_population = _zeroUsed ? 1 : 0;

for (unsigned int i = 0; i < oldSize; i++){
Pair* p = oldTable[i];
if (p){
insert(p->key, p->value);
DELETE_SAFE(oldTable[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 @@ -202,41 +189,62 @@ namespace ds{
hash = newHashFunction;
}
void clear(){
if (table){
for (unsigned int i = 0; i < _size; i++){
removeEntry(i);
}
}
DELETE_ARR_SAFE(table);
//if (table){
// for (unsigned int i = 0; i < _size; i++){
// removeEntry(i);
// }
//}
// DELETE_ARR_SAFE(table);
_zeroUsed = false;
_size = _population = _logSize = 0;
}

// for test only
inline BucketType entry(const int bucket) const {
////////////////////////////////////////////////
//// abstracting the data type of the table
////////////////////////////////////////////////
virtual inline BucketType& entry(const int bucket) const {
return table[bucket];
}
inline Key key(const int bucket) const {
return table[bucket]->key;
virtual inline Key key(const int bucket) const {
return table[bucket].key;
}
inline Key* keyptr(const int bucket) const {
return &table[bucket]->key;
virtual inline Key* keyptr(const int bucket) const {
return &table[bucket].key;
}
inline BucketEntryPtr entryptr(const int bucket) const {
return table[bucket];
virtual inline BucketEntryPtr entryptr(const int bucket) const {
return &table[bucket];
}
inline Value value(const int bucket) const {
return table[bucket]->value;
virtual inline Value value(const int bucket) const {
return table[bucket].value;
}
inline BucketEntryPtr entryPtr(const int bucket) const{ return table[bucket]; }
inline bool isNull(const int bucket) const{
return table[bucket] == NULL;
virtual inline bool isNull(const int bucket) const{
return isKeyZero(keyptr(bucket));
}
inline void makeNull(const int bucket) const{
virtual inline void makeNull(const int bucket) const{
setKeyZero(keyptr(bucket));
}
inline void removeEntry(const int bucket) const{
DELETE_SAFE(table[bucket]);
virtual inline void removeEntry(const int bucket) const{
//DELETE_SAFE(table[bucket]);
makeNull(bucket);
}
virtual inline bool isKeyZero(const Key* pKey) const{
return memcmp(pKey, _pZeroKey, sizeof(Key)) == 0;
}
virtual inline bool isValueZero(const Value* pValue) const{
return memcmp(pValue, _pZeroValue, sizeof(Value)) == 0;
}
virtual inline void setKeyZero(Key* pKey) const{
memcpy(pKey, _pZeroKey, sizeof(Key));
}
virtual inline void setValueZero(Value* pValue) const{
memcpy(pValue, _pZeroValue, sizeof(Value));
}
virtual inline void makeEntry(const int bucket, const Key& k){
table[bucket] = Pair(k);
// if we stored pointers, we would have done new Pair(k)
}
virtual bool isBucketEmpty(const int bucket) const override {
return isNull(bucket);
}
public:

Expand All @@ -246,7 +254,7 @@ namespace ds{

memset(_pZeroKey, 0, sizeof(Key));

setKeyZero(&_zeroPair.key);
setKeyZero(&_zeroKeyEntry.key);
}
virtual ~LinearProbingHashTable(){
clear();
Expand Down Expand Up @@ -312,7 +320,7 @@ namespace ds{
// crossedBoundary = true;
//}

unsigned int neighborFirstBucket = index(table[neighbor]->key);
unsigned int neighborFirstBucket = index(key(neighbor));
if (neighborFirstBucket == neighbor || // is the neighbor at its own first bucket? then it should not move
(
(curFirstBucket <= cur) // search did not wrap around the end
Expand Down Expand Up @@ -442,7 +450,7 @@ namespace ds{
p->value = key; // assign the value
cout << str << endl << h;
ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
ASSERT_EQ(p, h.entryPtr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
}
{
Expand All @@ -454,7 +462,7 @@ namespace ds{
p->value = key; // assign the value
cout << str << endl << h;
ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
ASSERT_EQ(p, h.entryPtr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
}
{
Expand All @@ -466,7 +474,7 @@ namespace ds{
BucketEntryPtr p = h.insertKey(key);
cout << str << endl << h;
ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
ASSERT_EQ(p, h.entryPtr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(key, h.value(bucket)) << key << " not in bucket " << bucket;
}
Expand All @@ -483,7 +491,7 @@ namespace ds{
p->value = key; // assign the value
cout << str << endl << h;
ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
ASSERT_EQ(p, h.entryPtr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;

}
Expand All @@ -496,7 +504,7 @@ namespace ds{
p->value = key; // assign the value
cout << str << endl << h;
ASSERT_NE(true, h.isNull(bucket)) << "bucket " << bucket << " NULL";
ASSERT_EQ(p, h.entryPtr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(p, h.entryptr(bucket)) << key << " not in bucket " << bucket;
ASSERT_EQ(key, h.key(bucket)) << key << " not in bucket " << bucket;

}
Expand All @@ -510,9 +518,9 @@ namespace ds{
p->value = 100; // assign the value
cout << str << endl << h;
ASSERT_EQ(true, h._zeroUsed) << "_zeroUsed false";
ASSERT_EQ(p, &h._zeroPair) << " zero key not in zeroPair";
ASSERT_EQ(p, &h._zeroKeyEntry) << " zero key not in zeroPair";
ASSERT_EQ(prevPop+1, h.population()) << "population did not increase";
ASSERT_EQ(key, h._zeroPair.key) << key << " not in zeroPair";
ASSERT_EQ(key, h._zeroKeyEntry.key) << key << " not in zeroPair";
}

}
Expand Down Expand Up @@ -544,7 +552,7 @@ namespace ds{
// check that all items exist
// items are rehashed mod 32
for (int i = 0; i < h1._size; i++){
if (h1.table[i]){
if (!h1.isNull(i)){
int key = h1.key(i);
int value = h1.value(i);
BucketEntryPtr p = h2.lookupKey(key);
Expand Down Expand Up @@ -782,21 +790,21 @@ namespace ds{
ASSERT_EQ(false, h._zeroUsed) << "_zeroUsed true";
h.insert(0, 5);
ASSERT_EQ(true, h._zeroUsed) << "_zeroUsed false";
ASSERT_EQ(0, h._zeroPair.key) << "key of zero element non-zero";
ASSERT_EQ(5, h._zeroPair.value) << "key of zero element non-zero";
ASSERT_EQ(0, h._zeroKeyEntry.key) << "key of zero element non-zero";
ASSERT_EQ(5, h._zeroKeyEntry.value) << "key of zero element non-zero";

// now delete 0
h.remove(0);
ASSERT_EQ(false, h._zeroUsed) << "_zeroUsed true";
ASSERT_EQ(0, h._zeroPair.key) << "key of zero element non-zero";
ASSERT_EQ(0, h._zeroPair.value) << "value of zero element non-zero";
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
BucketEntryPtr* badTable = new BucketEntryPtr[8]; // 8 entries in the table
BucketType* badTable = new BucketType[8]; // 8 entries in the table
for (int i = 0; i < 8; i++){
badTable[i] = new Pair(5, i); // all keys are 5
badTable[i] = Pair(5, i); // all keys are 5
}
IntHashTable8 h2(3); // 8 elements to begin
h2.forceTable(badTable, 3, 8, h2.hash); // make the table full
Expand Down Expand Up @@ -878,45 +886,52 @@ namespace ds{
}

void isKeyZero(){
struct MyStruct{
int a;
float b;
char c;
bool operator==(const MyStruct& other) const { return a == other.a && b == other.b && c == other.c; }
bool operator!=(const MyStruct& other) const { return !(*this == other); }
bool operator<(const MyStruct& other) const { return a < other.a && b < other.b && c < other.c; }
bool operator<=(const MyStruct& other) const { return a <= other.a && b <= other.b && c <= other.c; }
};
struct MyHashFunction{
public:
virtual int operator()(const MyStruct& n)const{ return n.a + (int)n.b + (int)n.c; }
};
ASSERT_EQ(false, std::is_fundamental<MyStruct>::value) << "MyStruct fundamental";
MyStruct z = { 0, 0, 0 }, nz1 = { 0, 5, 0 }, nz2 = { 0, 0, 6 }, nz3 = { 10, 0, 0 };
ASSERT_EQ(sizeof(int) * 3, sizeof(z)) << "struct size mismatch"; // note the word alignment for char

// create a hashtable with MyStruct as Key
LinearProbingHashTable<MyStruct,MyStruct,KVPair<MyStruct>,MyHashFunction> h(1);
// now test
ASSERT_EQ(false, h.isKeyZero(&z)) << "z not zero"; // compound-types are always non-zero
ASSERT_EQ(false, h.isKeyZero(&nz1)) << "nz1 zero";

int k1 = 0, k11 = 2;
char k2 = 0, k22 = 'c';
LinearProbingHashTable<int> h1(1);
LinearProbingHashTable<char> h2(1);
ASSERT_EQ(true, h1.isKeyZero(&k1)) << "k1 not zero";
ASSERT_EQ(false, h1.isKeyZero(&k11)) << "k11 zero";
ASSERT_EQ(true, h2.isKeyZero(&k2)) << "k2 not zero";
ASSERT_EQ(false, h2.isKeyZero(&k22)) << "k22 zero";

// test setKeyZero and setValueZero
h.setKeyZero(&nz1);
ASSERT_EQ(true, h.isKeyZero(&nz1)) << "nz1 not zero";
// both key and value of h are of type MyStruct
h.setValueZero(&nz2);
ASSERT_EQ(true, h.isValueZero(&nz2)) << "nz2 not zero";

//struct MyStruct{
// int a;
// float b;
// char c;
// bool operator==(const MyStruct& other) const { return a == other.a && b == other.b && c == other.c; }
// bool operator!=(const MyStruct& other) const { return !(*this == other); }
// bool operator<(const MyStruct& other) const { return a < other.a && b < other.b && c < other.c; }
// bool operator<=(const MyStruct& other) const { return a <= other.a && b <= other.b && c <= other.c; }
//};
//struct MyHashFunction{
//public:
// virtual int operator()(const MyStruct& n)const{ return n.a + (int)n.b + (int)n.c; }
//};
//
//// create a hashtable with MyStruct as Key
//LinearProbingHashTable<MyStruct,MyStruct,KVPair<MyStruct>,MyHashFunction> h(1);

IntHashTable h;
IntHashTable* pH = new IntHashTable();
delete pH;

//h.insert(4, 4);
//h.insert(5, 5);
//h.insert(6, 6);

//// now test
//ASSERT_EQ(false, h.isNull(4)) << "4 zero";
//ASSERT_EQ(false, h.isNull(5)) << "5 zero";
//ASSERT_EQ(false, h.isNull(6)) << "6 zero";
//ASSERT_EQ(true, h.isNull(3)) << "3 not zero";
//ASSERT_EQ(true, h.isNull(0)) << "0 not zero";
//
//h.setKeyZero(h.keyptr(4));
//ASSERT_EQ(true, h.isNull(4)) << "4 not null";
//h.makeNull(5);
//ASSERT_EQ(true, h.isNull(5)) << "5 not null";
//*h.keyptr(6) = 0;
//ASSERT_EQ(true, h.isNull(6)) << "6 not null";
//h.entryptr(2)->key = 2;
//ASSERT_EQ(2, h.key(2)) << "[2] not 2";
//h.removeEntry(2);
//ASSERT_EQ(true, h.isNull(2)) << "[2] not NULL";
//h.makeEntry(4, 44);
//ASSERT_EQ(44, h.key(4)) << "[4] not 44";

ASSERT_EQ(0, 1) << "uncomment other tests";
}
};

Expand Down
Loading

0 comments on commit 7b7002b

Please sign in to comment.