#define HASH_TABLE_SIZE 14976001
#define HASH_SEGMENT_SIZE 20
#define HASH_ENTRY_EXPIRE_TIME (60*60*24*30) //30 days
#define HASH_AUDIT_BKT_NUM_PER_TIME (HASH_TABLE_SIZE/239) //every 5 days finish on audit cycle
#define HASH_AUDIT_INTERVAL (10*60) //do audit every 30 min
template <class T> class HashTable;
template <class T>
class HashEntry
{
friend class HashTable<T>;
private:
string key;
T value;
time_t timestamp;
HashEntry *next;
public:
HashEntry():key(0), timestamp(0), next(NULL)
{
}
HashEntry(const string &_key, const T &_val):key(_key), value(_val), next(NULL)
{
timestamp = time(NULL);
}
~HashEntry()
{
}
};
template <class T>
class HashTable
{
private:
HashEntry<T> **bucket; //hash bucket
uint32_t entry_num; //current entry node number
uint32_t bkt_cnt; //hash bucket count
uint32_t bkt_used; //hash bucket used
uint32_t cur_audit_bkt; //current audit bucket index
uint32_t purge_num; //entry number purged by audit
pthread_rwlock_t *bkt_lock; //rw lock
public:
HashTable(uint32_t _size = HASH_TABLE_SIZE):entry_num(0), bkt_cnt(_size), bkt_used(0), cur_audit_bkt(0), purge_num(0)
{
init(bkt_cnt);
}
~HashTable()
{
clear();
}
uint32_t get_entry_num() { return __sync_fetch_and_add(&entry_num,0); }
uint32_t get_bkt_count() { return __sync_fetch_and_add(&bkt_cnt,0); }
uint32_t get_bkt_used() { return __sync_fetch_and_add(&bkt_used,0); }
/*
* Insert the pair of key and value
*
*/
bool insert(const char *key, const T &value)
{
if (NULL == key)
{
ASRT_RPT_XD(ASBAD_DATA, NULL_PTR, 0, "Invalid key!");
return false;
}
return insert((const string&)key, value);
}
bool insert(const string &key, const T &value)
{
uint32_t idx = get_bkt_idx(key);
wlock_bucket(idx);
HashEntry<T>* entry = bucket[idx];
if (NULL == entry)
{
__sync_add_and_fetch(&bkt_used,1);
}
while (entry)
{
/* If find the key, update the old value with the new one */
if (entry->key == key)
{
memcpy(&entry->value, &value, sizeof(T));
entry->timestamp = time(NULL);
unlock_bucket(idx);
return true;
}
entry = entry->next;
}
HashEntry<T>* new_entry = new HashEntry<T>(key, value);
if (new_entry == NULL)
{
ASRT_RPT_XD(ASBAD_DATA, NULL_PTR, 0, "allocate memory failed! size=%d",sizeof(HashEntry<T>));
return false;
}
new_entry->next = bucket[idx];
bucket[idx] = new_entry;
__sync_add_and_fetch(&entry_num,1);
unlock_bucket(idx);
return true;
}
/*
* find one entry
*
*/
T* find(const char *key, time_t *time_stamp = NULL)
{
if (NULL == key)
{
ASRT_RPT_XD(ASBAD_DATA, NULL_PTR, 0, "Invalid key!");
return NULL;
}
return find((const string&)key, time_stamp);
}
T* find(const string &key, time_t *time_stamp = NULL)
{
uint32_t idx = get_bkt_idx(key);
rlock_bucket(idx);
HashEntry<T>* entry = bucket[idx];
while (entry)
{
if (entry->key == key)
{
if (NULL != time_stamp)
{
*time_stamp = entry->timestamp;
}
entry->timestamp = time(NULL);
unlock_bucket(idx);
return &entry->value;
}
entry = entry->next;
}
unlock_bucket(idx);
return NULL;
}
/*
* update one entry
*
*/
bool update(const char *key, const T &value)
{
if (NULL == key)
{
ASRT_RPT_XD(ASBAD_DATA, NULL_PTR, 0, "Invalid key!");
return false;
}
return update((const string&)key, value);
}
bool update(const string &key, const T &value)
{
uint32_t idx = get_bkt_idx(key);
wlock_bucket(idx);
HashEntry<T>* entry = bucket[idx];
while (entry)
{
if (entry->key == key)
{
memcpy(&entry->value, &value, sizeof(T));
entry->timestamp = time(NULL);
unlock_bucket(idx);
return true;
}
else
{
entry = entry->next;
}
}
unlock_bucket(idx);
return false;
}
/*
* Delete hashEntry for given key
*
*/
bool del(const char *key)
{
if (NULL == key)
{
ASRT_RPT_XD(ASBAD_DATA, NULL_PTR, 0, "Invalid key!");
return false;
}
return del((const string&)key);
}
bool del(const string &key)
{
uint32_t idx = get_bkt_idx(key);
wlock_bucket(idx);
HashEntry<T>* entry = bucket[idx];
HashEntry<T>* pre_entry = NULL;
while (entry)
{
if (entry->key == key)
{
if (pre_entry)
{
pre_entry->next = entry->next;
}
else
{
bucket[idx] = entry->next;
if (NULL == entry->next)
{
__sync_sub_and_fetch(&bkt_used,1);
}
}
delete entry;
__sync_sub_and_fetch(&entry_num,1);
unlock_bucket(idx);
return true;
}
else
{
pre_entry = entry;
entry = entry->next;
}
}
unlock_bucket(idx);
return false;
}
private:
/*
* JS hash algorithm
*/
uint64_t js_hash(const string &str)
{
uint64_t hash = 1315423911;
for (uint8_t i = 0; i < str.length(); i++)
{
hash ^= ((hash << 5) + str[i] + (hash >> 2));
}
return hash;
}
void init(uint32_t _size)
{
uint32_t lock_size = ((bkt_cnt+HASH_SEGMENT_SIZE-1) / HASH_SEGMENT_SIZE);
bucket = new HashEntry<T>*[_size];
if (bucket == NULL)
{
ASRT_RPT_XD(ASBAD_DATA, NULL_PTR, 0, "allocate memory failed! size=%d",sizeof(HashEntry<T>*)*_size);
return;
}
memset(bucket, 0, sizeof(HashEntry<T>*)*_size);
bkt_lock = new pthread_rwlock_t[lock_size];
if (bkt_lock == NULL)
{
ASRT_RPT_XD(ASBAD_DATA, NULL_PTR, 0, "allocate memory failed! size=%d",sizeof(pthread_rwlock_t)*lock_size);
return;
}
for (uint32_t i = 0; i < lock_size; i++)
{
pthread_rwlock_init(&bkt_lock[i],NULL);
}
}
void clear()
{
uint32_t lock_size = ((bkt_cnt+HASH_SEGMENT_SIZE-1) / HASH_SEGMENT_SIZE);
for (uint32_t i = 0; i < bkt_cnt; i++)
{
HashEntry<T>* entry = bucket[i];
while (entry)
{
HashEntry<T>* next = entry->next;
delete entry;
entry = next;
}
}
delete [] bucket;
for (uint32_t i = 0; i < lock_size; i++)
{
pthread_rwlock_destroy(&bkt_lock[i]);
}
delete [] bkt_lock;
}
uint32_t get_bkt_idx(const string &key)
{
uint64_t hash;
uint32_t idx;
hash = js_hash(key);
idx = hash % bkt_cnt;
return idx;
}
void wlock_bucket(uint32_t bkt_idx)
{
pthread_rwlock_wrlock(&bkt_lock[bkt_idx/HASH_SEGMENT_SIZE]);
}
void rlock_bucket(uint32_t bkt_idx)
{
pthread_rwlock_rdlock(&bkt_lock[bkt_idx/HASH_SEGMENT_SIZE]);
}
void unlock_bucket(uint32_t bkt_idx)
{
pthread_rwlock_unlock(&bkt_lock[bkt_idx/HASH_SEGMENT_SIZE]);
}
};
#endif /* _SFED_HASHTABLE_H_ */
hash table
最新推荐文章于 2025-08-16 11:44:53 发布