mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-06 20:43:44 +02:00
Hash table bug fix, and add copy constructor and assignment operator for principle of least surprise.
This commit is contained in:
parent
4838cbc350
commit
da9a720c3f
2 changed files with 102 additions and 11 deletions
|
@ -29,6 +29,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
@ -47,9 +48,11 @@ private:
|
||||||
{
|
{
|
||||||
_Bucket(const K &k,const V &v) : k(k),v(v) {}
|
_Bucket(const K &k,const V &v) : k(k),v(v) {}
|
||||||
_Bucket(const K &k) : k(k),v() {}
|
_Bucket(const K &k) : k(k),v() {}
|
||||||
|
_Bucket(const _Bucket &b) : k(b.k),v(b.v) {}
|
||||||
|
inline _Bucket &operator=(const _Bucket &b) { k = b.k; v = b.v; return *this; }
|
||||||
K k;
|
K k;
|
||||||
V v;
|
V v;
|
||||||
_Bucket *next;
|
_Bucket *next; // must be set manually for each _Bucket
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -115,12 +118,47 @@ public:
|
||||||
_t[i] = (_Bucket *)0;
|
_t[i] = (_Bucket *)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Hashtable(const Hashtable<K,V> &ht) :
|
||||||
|
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * ht._bc))),
|
||||||
|
_bc(ht._bc),
|
||||||
|
_s(ht._s)
|
||||||
|
{
|
||||||
|
if (!_t)
|
||||||
|
throw std::bad_alloc();
|
||||||
|
for(unsigned long i=0;i<_bc;++i)
|
||||||
|
_t[i] = (_Bucket *)0;
|
||||||
|
for(unsigned long i=0;i<_bc;++i) {
|
||||||
|
const _Bucket *b = ht._t[i];
|
||||||
|
while (b) {
|
||||||
|
_Bucket *nb = new _Bucket(*b);
|
||||||
|
nb->next = _t[i];
|
||||||
|
_t[i] = nb;
|
||||||
|
b = b->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
~Hashtable()
|
~Hashtable()
|
||||||
{
|
{
|
||||||
clear();
|
this->clear();
|
||||||
::free(_t);
|
::free(_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Hashtable &operator=(const Hashtable<K,V> &ht)
|
||||||
|
{
|
||||||
|
this->clear();
|
||||||
|
if (ht._s) {
|
||||||
|
for(unsigned long i=0;i<ht._bc;++i) {
|
||||||
|
const _Bucket *b = ht._t[i];
|
||||||
|
while (b) {
|
||||||
|
this->set(b->k,b->v);
|
||||||
|
b = b->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erase all entries
|
* Erase all entries
|
||||||
*/
|
*/
|
||||||
|
@ -140,6 +178,24 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Vector of all keys
|
||||||
|
*/
|
||||||
|
inline typename std::vector<K> keys()
|
||||||
|
{
|
||||||
|
typename std::vector<K> k;
|
||||||
|
if (_s) {
|
||||||
|
for(unsigned long i=0;i<_bc;++i) {
|
||||||
|
_Bucket *b = _t[i];
|
||||||
|
while (b) {
|
||||||
|
k.push_back(b->k);
|
||||||
|
b = b->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param k Key
|
* @param k Key
|
||||||
* @return Pointer to value or NULL if not found
|
* @return Pointer to value or NULL if not found
|
||||||
|
@ -187,7 +243,8 @@ public:
|
||||||
*/
|
*/
|
||||||
inline V &set(const K &k,const V &v)
|
inline V &set(const K &k,const V &v)
|
||||||
{
|
{
|
||||||
const unsigned long bidx = _hc(k) % _bc;
|
const unsigned long h = _hc(k);
|
||||||
|
unsigned long bidx = h % _bc;
|
||||||
|
|
||||||
_Bucket *b = _t[bidx];
|
_Bucket *b = _t[bidx];
|
||||||
while (b) {
|
while (b) {
|
||||||
|
@ -198,8 +255,10 @@ public:
|
||||||
b = b->next;
|
b = b->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_s >= _bc)
|
if (_s >= _bc) {
|
||||||
_grow();
|
_grow();
|
||||||
|
bidx = h % _bc;
|
||||||
|
}
|
||||||
|
|
||||||
b = new _Bucket(k,v);
|
b = new _Bucket(k,v);
|
||||||
b->next = _t[bidx];
|
b->next = _t[bidx];
|
||||||
|
@ -215,7 +274,8 @@ public:
|
||||||
*/
|
*/
|
||||||
inline V &operator[](const K &k)
|
inline V &operator[](const K &k)
|
||||||
{
|
{
|
||||||
const unsigned long bidx = _hc(k) % _bc;
|
const unsigned long h = _hc(k);
|
||||||
|
unsigned long bidx = h % _bc;
|
||||||
|
|
||||||
_Bucket *b = _t[bidx];
|
_Bucket *b = _t[bidx];
|
||||||
while (b) {
|
while (b) {
|
||||||
|
@ -224,8 +284,10 @@ public:
|
||||||
b = b->next;
|
b = b->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_s >= _bc)
|
if (_s >= _bc) {
|
||||||
_grow();
|
_grow();
|
||||||
|
bidx = h % _bc;
|
||||||
|
}
|
||||||
|
|
||||||
b = new _Bucket(k);
|
b = new _Bucket(k);
|
||||||
b->next = _t[bidx];
|
b->next = _t[bidx];
|
||||||
|
|
39
selftest.cpp
39
selftest.cpp
|
@ -581,31 +581,60 @@ static int testOther()
|
||||||
{
|
{
|
||||||
std::cout << "[other] Testing Hashtable... "; std::cout.flush();
|
std::cout << "[other] Testing Hashtable... "; std::cout.flush();
|
||||||
{
|
{
|
||||||
Hashtable<uint64_t,std::string> ht(128);
|
Hashtable<uint64_t,std::string> ht;
|
||||||
|
Hashtable<uint64_t,std::string> ht2;
|
||||||
std::map<uint64_t,std::string> ref; // assume std::map works correctly :)
|
std::map<uint64_t,std::string> ref; // assume std::map works correctly :)
|
||||||
for(int x=0;x<2;++x) {
|
for(int x=0;x<2;++x) {
|
||||||
for(int i=0;i<25000;++i) {
|
for(int i=0;i<25000;++i) {
|
||||||
uint64_t k = rand();
|
uint64_t k = rand();
|
||||||
while ((k == 0)||(ref.count(k) > 0))
|
while ((k == 0)||(ref.count(k) > 0))
|
||||||
++k;
|
++k;
|
||||||
std::string v;
|
std::string v("!");
|
||||||
for(int j=0;j<(int)(k % 64);++j)
|
for(int j=0;j<(int)(k % 64);++j)
|
||||||
v.push_back("0123456789"[rand() % 10]);
|
v.push_back("0123456789"[rand() % 10]);
|
||||||
ht.set(k,v);
|
ht.set(k,v);
|
||||||
ref[k] = v;
|
ref[k] = v;
|
||||||
}
|
}
|
||||||
if (ht.size() != ref.size()) {
|
if (ht.size() != ref.size()) {
|
||||||
std::cout << "FAILED! (size mismatch)" << std::endl;
|
std::cout << "FAILED! (size mismatch, original)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ht2 = ht;
|
||||||
|
Hashtable<uint64_t,std::string> ht3(ht2);
|
||||||
|
if (ht2.size() != ref.size()) {
|
||||||
|
std::cout << "FAILED! (size mismatch, assigned)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (ht3.size() != ref.size()) {
|
||||||
|
std::cout << "FAILED! (size mismatch, copied)" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
for(std::map<uint64_t,std::string>::iterator i(ref.begin());i!=ref.end();++i) {
|
for(std::map<uint64_t,std::string>::iterator i(ref.begin());i!=ref.end();++i) {
|
||||||
std::string *v = ht.get(i->first);
|
std::string *v = ht.get(i->first);
|
||||||
if (!v) {
|
if (!v) {
|
||||||
std::cout << "FAILED! (key not found)" << std::endl;
|
std::cout << "FAILED! (key " << i->first << " not found, original)" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (*v != i->second) {
|
if (*v != i->second) {
|
||||||
std::cout << "FAILED! (key not equal)" << std::endl;
|
std::cout << "FAILED! (key " << i->first << " not equal, original)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
v = ht2.get(i->first);
|
||||||
|
if (!v) {
|
||||||
|
std::cout << "FAILED! (key " << i->first << " not found, assigned)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (*v != i->second) {
|
||||||
|
std::cout << "FAILED! (key " << i->first << " not equal, assigned)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
v = ht3.get(i->first);
|
||||||
|
if (!v) {
|
||||||
|
std::cout << "FAILED! (key " << i->first << " not found, copied)" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (*v != i->second) {
|
||||||
|
std::cout << "FAILED! (key " << i->first << " not equal, copied)" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue