Files
mir_server/sdk/srvlib/include/CustomHashTable.h
aixianling 5c9f1dae4a init
2025-01-09 17:45:40 +08:00

270 lines
5.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
template <class T>
class HashNode
{
public:
unsigned int hash1; //哈希值1
unsigned int hash2; //哈希值2
unsigned int hash3; //哈希值3
T value; //数据值
};
#include "bzhash.h"
template <class T>
class CCustomHashTable
{
public:
typedef CCustomHashTable<T> ClassType;
typedef HashNode<T> NodeType;
public:
CCustomHashTable(size_t len = 0)
{
m_pTable = NULL;
m_nLen = m_nFree = 0;
m_nInitSize = len;
if (len > MiniSize)
{
//限制长度必须是2的次方数否则哈希下标算法无法工作
size_t val;
for (int i=0; i<32; ++i)
{
val = size_t(1 << i);
if (len <= val)
{
m_nInitSize = val;
break;
}
}
}
else m_nInitSize = MiniSize;//限制长度必须是2的次方数否则哈希下标算法无法工作
}
virtual ~CCustomHashTable()
{
clear();
}
//清空哈希表
void clear()
{
//循环调用析构函数
for (INT_PTR i=m_nLen-1; i>-1; --i)
{
if (m_pTable[i].hash1)
{
m_pTable[i].value.~T();
}
}
//释放内存
if (m_pTable) realloc(m_pTable, 0);
m_pTable = NULL;
m_nLen = m_nFree = 0;
}
//获取有效对象数量
inline size_t count() const { return m_nLen - m_nFree; }
public:
//通过键查找值
inline T* get(const char* sKey)
{
INT_PTR idx = getIndex(sKey);
return (idx >= 0) ? &m_pTable[idx].value : NULL;
}
//通过键查找值
inline const T* get(const char* sKey) const
{
INT_PTR idx = getIndex(sKey);
return (idx >= 0) ? &m_pTable[idx].value : NULL;
}
/* 通过键添加值
* 如果一个hash索引被使用则向后递推索引到达最大索
* 引则从头开始查找空位,知直到找到空位或完成一次表遍历。
*/
inline T* put(const char* sKey)
{
unsigned int hash1, hash2, hash3, idx, start;
NodeType *pNode;
//内存空间不足,增长内存空间
if (m_nFree <= 0)
{
size_t oldlen = m_nLen;
m_nLen = (oldlen <= 0) ? m_nInitSize : m_nLen << 1;//表长度必须是2的次方
m_nFree = m_nLen - oldlen;
m_pTable = (NodeType *)realloc(m_pTable, m_nLen * sizeof(m_pTable[0]));
memset(&m_pTable[oldlen], 0, m_nFree * sizeof(m_pTable[0]));
}
hash1 = bzhashstr(sKey, 0);
hash2 = bzhashstr(sKey, 1);
hash3 = bzhashstr(sKey, 2);
start = idx = hash1 & (m_nLen - 1);//表长度必须是2的次方
do
{
pNode = &m_pTable[idx];
//如果该位置没有值,则设置到该位置,否则向后找到一个空位置
if (!pNode->hash1)
{
pNode->hash1 = hash1;
pNode->hash2 = bzhashstr(sKey, 1);
pNode->hash3 = bzhashstr(sKey, 2);
m_nFree--;
new (&pNode->value)T();
return &pNode->value;
}
#ifdef _DEBUG
else if (pNode->hash1 == hash1 && pNode->hash2 == hash2 && pNode->hash3 == hash3)
{
//调用者重复添加,或者确实出现错误!
DebugBreak();
}
#endif
idx = (idx + 1) & (m_nLen - 1);//表长度必须是2的次方
}
while (start != idx);
return NULL;
}
//通过键更新值
inline INT_PTR update(const char* sKey, const T &value)
{
INT_PTR idx = getIndex(sKey);
if (idx >= 0)
m_pTable[idx].value = value;
return idx;
}
//通过键移除值,没有找到则返回-1
inline INT_PTR remove(const char* sKey)
{
INT_PTR idx = getIndex(sKey);
NodeType *pNode;
if (idx >= 0)
{
pNode = &m_pTable[idx];
pNode->hash1 = pNode->hash2 = pNode->hash3 = 0;
m_nFree++;
pNode->value.~T();
return idx;
}
return -1;
}
//获取键在表中的索引
INT_PTR getIndex(const char* sKey) const
{
unsigned int hash1, hash2, hash3;
unsigned int idx, start;
size_t len;
NodeType *pNode;
if (m_nLen <= 0)
return -1;
hash1 = bzhashstr(sKey, 0);
hash2 = bzhashstr(sKey, 1);
hash3 = bzhashstr(sKey, 2);
//首先开始折半查找
len = m_nLen;
while (len >= m_nInitSize)
{
idx = hash1 & (len - 1);//表长度必须是2的次方
pNode = &m_pTable[idx];
if (pNode->hash1 == hash1 && pNode->hash2 == hash2 && pNode->hash3 == hash3)
{
return idx;
}
len >>= 1;
}
//折半查找不到则从hash位置开始遍历整个表
start = idx = hash1 & (m_nLen - 1);//表长度必须是2的次方
do
{
pNode = &m_pTable[idx];
if (pNode->hash1 == hash1 && pNode->hash2 == hash2 && pNode->hash3 == hash3)
{
return idx;
}
idx = (idx + 1) & (m_nLen - 1);//表长度必须是2的次方
}
while (start != idx);
return -1;
}
protected:
//内存申请函数作用与c函数中的realloc实现相同实现申请、扩展以及释放内存
virtual void* realloc(void* p, size_t s)
{
return ::realloc(p, s);
}
protected:
size_t m_nInitSize;//表初始长度
size_t m_nFree; //空闲节点数量
public:
static const size_t MiniSize = 16;//哈希表最小长度必须是2的次方否则哈希下标算法无法工作
HashNode<T> *m_pTable; //哈希表
size_t m_nLen; //哈希表的长度,必须是2的次方否则哈希下标算法无法工作
};
template <typename T>
class CHashTableIterator
{
friend class CCustomHashTable<T>;
public:
CHashTableIterator()
{
m_pTable = NULL;
m_nIndex = 0;
}
CHashTableIterator(const CCustomHashTable<T> &table)
{
setTable(table);
}
inline void setTable(const CCustomHashTable<T> &table)
{
m_pTable = &table;
m_nIndex = 0;
}
inline T* first()
{
INT_PTR nLen = m_pTable->m_nLen;
HashNode<T> *pNode;
m_nIndex = 0;
while (m_nIndex < nLen)
{
pNode = &m_pTable->m_pTable[m_nIndex];
m_nIndex++;
if (pNode->hash1)
return &pNode->value;
}
return NULL;
}
inline T* next()
{
INT_PTR nLen = m_pTable->m_nLen;
HashNode<T> *pNode;
while (m_nIndex < nLen)
{
pNode = &m_pTable->m_pTable[m_nIndex];
m_nIndex++;
if (pNode->hash1)
return &pNode->value;
}
return NULL;
}
private:
const CCustomHashTable<T>*m_pTable;
INT_PTR m_nIndex;
};