Skip to content

Commit

Permalink
Abstracted the interactions aorund the table
Browse files Browse the repository at this point in the history
now algorithms does not depend on data type of the table; for example, we could store either pointers or objects.

The table is an array of BucketEntryType.

We need to properly define these interface methods:
isKeyNull(), setKeyNull(), isNull(), makeNull(), removeEntry()
key(),value(), keyptr(), entryptr()
  • Loading branch information
saad0105050 committed Sep 14, 2014
1 parent 02f9e44 commit 1f66ce3
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 36 deletions.
143 changes: 109 additions & 34 deletions YASI_12/ds.LinearProbingHashTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,31 @@ namespace ds{
Value _zeroValue;
Pair _zeroPair; // 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

inline unsigned int circularNext(const int index) const{
return modSize(index + 1);
}
inline unsigned int circularPrev(const int index) const{
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 @@ -57,9 +72,9 @@ namespace ds{

// the key must be present in the table

Pair* lookupKey(const Key& k) const {
BucketEntryPtr lookupKey(const Key& k) const {
// test the zero key
if (k == 0){
if (isKeyZero(&k)){
if (_zeroUsed) return const_cast<Pair*>(&_zeroPair);
else return NULL;
}
Expand All @@ -69,19 +84,18 @@ namespace ds{
unsigned int firstBucket = index(k);
int cur = firstBucket;
do{
Pair* pEntry = table[cur];

if (pEntry == NULL){
if (isNull(cur)){
// this slot must be empty
// because we started from the firstBucket,
// the key is not present in the table
return NULL;
}
else{
// this slot is occupied; check key
if (keyEquals(k, pEntry->key)){
if (keyEquals(k, key(cur))){
// found match
return pEntry;
return entryptr(cur);
}
else{
// move on to the next slot
Expand All @@ -94,10 +108,10 @@ namespace ds{
}
}

Pair* insertKey(const Key& k) {
BucketEntryPtr insertKey(const Key& k) {

// insert/update the entry with hashcode 0
if ( ((int)k) == 0){
if ( isKeyZero(&k)){
// key is zero
// we will use a special slot for this entry
if (_zeroUsed == false)
Expand All @@ -119,9 +133,9 @@ namespace ds{
unsigned int firstBucket = index(k);
int cur = firstBucket;
do{
Pair* pEntry = table[cur];
//Pair* pEntry = table[cur];

if (pEntry == NULL){
if (isNull(cur)){
// if this is the zero slot, we should skip it
// this slot must be empty,
// we can insert here
Expand All @@ -133,16 +147,18 @@ namespace ds{
continue;
}
else{
pEntry = table[cur] = new Pair(k);
// create a new entry
table[cur] = new Pair(k);
_population++;
return pEntry;
// return the pointer to the entry
return entryptr(cur);
}
}
else{
// this slot is occupied; check key
if (keyEquals(k, pEntry->key)){
if (keyEquals(k, key(cur))){
// found match
return pEntry;
return entryptr(cur);
}
else{
// move on to the next slot
Expand Down Expand Up @@ -188,7 +204,7 @@ namespace ds{
void clear(){
if (table){
for (unsigned int i = 0; i < _size; i++){
DELETE_SAFE(table[i]);
removeEntry(i);
}
}
DELETE_ARR_SAFE(table);
Expand All @@ -203,22 +219,39 @@ namespace ds{
inline Key key(const int bucket) const {
return table[bucket]->key;
}
inline Key* keyptr(const int bucket) const {
return &table[bucket]->key;
}
inline BucketEntryPtr entryptr(const int bucket) const {
return table[bucket];
}
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;
}
inline void makeNull(const int bucket) const{
setKeyZero(keyptr(bucket));
}
inline void removeEntry(const int bucket) const{
DELETE_SAFE(table[bucket]);
}
public:

LinearProbingHashTable(unsigned int logSize = INIT_LOGSIZE) : _zeroUsed(false), _zeroPair(0,0), HashTableBase(logSize){
LinearProbingHashTable(unsigned int logSize = INIT_LOGSIZE) : _zeroUsed(false), HashTableBase(logSize){
_pZeroKey = new char[sizeof(Key)];
_pZeroValue = new char[sizeof(Value)];

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

setKeyZero(&_zeroPair.key);
}
virtual ~LinearProbingHashTable(){
clear();
DELETE_SAFE(_pZeroKey);
DELETE_SAFE(_pZeroValue);
}
virtual Value* get(const Key& key) const override {
Pair* kv = lookupKey(key);
Expand All @@ -232,10 +265,10 @@ namespace ds{
virtual bool contains(const Key& key) const override { return lookupKey(key) != NULL; }
virtual void remove(const Key& k) override {
// zero key
if (k == 0){
if (isKeyZero(&k)){
if (_zeroUsed) {
_zeroUsed = false;
_zeroPair.value = 0;

_population--;
if (needShrink(_population))
shrink();
Expand All @@ -248,19 +281,19 @@ namespace ds{
int cur = curFirstBucket;
const int searchStart = curFirstBucket; // remember our first posti
do{
Pair* pEntry = table[cur];
//Pair* pEntry = table[cur];

if (pEntry == NULL){
if (isNull(cur)){
// this slot must be empty
// because we started from the firstBucket,
// the key is not present in the table
return;
}
else{
// this slot is occupied; check key
if (keyEquals(k, pEntry->key)){
if (keyEquals(k, key(cur))){
// remove
DELETE_SAFE(table[cur]);
removeEntry(cur);
_population--;
if (needShrink(_population))
shrink();
Expand All @@ -272,7 +305,7 @@ namespace ds{
//bool crossedBoundary = false; // search crossed the table end and now at the beginning of the table due to mod operation
unsigned int neighbor = circularNext(cur);
while (neighbor != searchStart && // we have not checked all buckets
table[neighbor] != NULL )// there is an entry at the neighboring bucket and
!isNull(neighbor) )// there is an entry at the neighboring bucket and
{
//if (!crossedBoundary && neighbor < cur) {
// // our search just wrapped across the table boundary
Expand All @@ -296,7 +329,7 @@ namespace ds{

// move it to the left
table[cur] = table[neighbor];
table[neighbor] = NULL;
makeNull(neighbor);
// prepare for the next hop
cur = neighbor;
neighbor = circularNext(neighbor);
Expand Down Expand Up @@ -330,31 +363,31 @@ namespace ds{
template<class Key>
class IdentityFunction : public IHashFunction<Key>{
public:
virtual int operator()(Key n) const override{
virtual int operator()(const Key& n) const override{
return n;
}
};
template<class Key>
class Mod8Function : public IHashFunction<Key>{
public:
static const unsigned int modulus = 8;
virtual int operator()(Key n) const override{
virtual int operator()(const Key& n) const override{
return n % 8;
}
};
template<class Key>
class Mod16Function : public IHashFunction<Key>{
public:
static const unsigned int modulus = 16;
virtual int operator()(Key n) const override{
virtual int operator()(const Key& n) const override{
return n % 16;
}
};
template<class Key>
class Mod32Function : public IHashFunction<Key>{
public:
static const unsigned int modulus = 32;
virtual int operator()(Key n) const override{
virtual int operator()(const Key& n) const override{
return n % 32;
}
};
Expand All @@ -364,7 +397,7 @@ namespace ds{
public:
unsigned int modulus;
ModFunction() : modulus(16){}
virtual int operator()(Key n) const override{
virtual int operator()(const Key& n) const override{
return n % modulus;
}
};
Expand Down Expand Up @@ -844,13 +877,55 @@ 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";

}
};

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, remove);
//ADD_TEST_F(LinearProbingHashTableTest, shrinkCondition);
ADD_TEST_F(LinearProbingHashTableTest, isKeyZero);

}
}
4 changes: 2 additions & 2 deletions YASI_12/ds.hashtablebase.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ class IKeyValueStore{
template<class Key>
class IHashFunction{
public:
virtual int operator()(Key n) const = 0;
virtual int operator()(const Key& n) const = 0;
};

template<class Key>
class IntHashFunction : public IHashFunction<Key>{
public:
virtual int operator()(Key n) const override{
virtual int operator()(const Key& n) const override{
return n ^ (~n << 11) ^ (n << 3) ^ (~n << 27);
}
};
Expand Down

0 comments on commit 1f66ce3

Please sign in to comment.