From 1f66ce3a31b9e870bed9c5f82c65e3dda8e68f14 Mon Sep 17 00:00:00 2001 From: saad0105050 Date: Sun, 14 Sep 2014 18:33:14 -0400 Subject: [PATCH] Abstracted the interactions aorund the table 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() --- YASI_12/ds.LinearProbingHashTable.h | 143 +++++++++++++++++++++------- YASI_12/ds.hashtablebase.h | 4 +- 2 files changed, 111 insertions(+), 36 deletions(-) diff --git a/YASI_12/ds.LinearProbingHashTable.h b/YASI_12/ds.LinearProbingHashTable.h index c2cc248..97d26a3 100644 --- a/YASI_12/ds.LinearProbingHashTable.h +++ b/YASI_12/ds.LinearProbingHashTable.h @@ -33,6 +33,7 @@ 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); @@ -40,9 +41,23 @@ namespace ds{ 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){ @@ -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(&_zeroPair); else return NULL; } @@ -69,9 +84,8 @@ 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 @@ -79,9 +93,9 @@ namespace ds{ } 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 @@ -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) @@ -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 @@ -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 @@ -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); @@ -203,6 +219,12 @@ 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; } @@ -210,15 +232,26 @@ namespace ds{ 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); @@ -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(); @@ -248,9 +281,9 @@ 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 @@ -258,9 +291,9 @@ namespace ds{ } 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(); @@ -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 @@ -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); @@ -330,7 +363,7 @@ namespace ds{ template class IdentityFunction : public IHashFunction{ public: - virtual int operator()(Key n) const override{ + virtual int operator()(const Key& n) const override{ return n; } }; @@ -338,7 +371,7 @@ namespace ds{ class Mod8Function : public IHashFunction{ 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; } }; @@ -346,7 +379,7 @@ namespace ds{ class Mod16Function : public IHashFunction{ 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; } }; @@ -354,7 +387,7 @@ namespace ds{ class Mod32Function : public IHashFunction{ 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; } }; @@ -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; } }; @@ -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::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,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 h1(1); + LinearProbingHashTable 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); } } \ No newline at end of file diff --git a/YASI_12/ds.hashtablebase.h b/YASI_12/ds.hashtablebase.h index 1a17038..33a9d0a 100644 --- a/YASI_12/ds.hashtablebase.h +++ b/YASI_12/ds.hashtablebase.h @@ -30,13 +30,13 @@ class IKeyValueStore{ template class IHashFunction{ public: - virtual int operator()(Key n) const = 0; + virtual int operator()(const Key& n) const = 0; }; template class IntHashFunction : public IHashFunction{ public: - virtual int operator()(Key n) const override{ + virtual int operator()(const Key& n) const override{ return n ^ (~n << 11) ^ (n << 3) ^ (~n << 27); } };