哈希表是一个用于快速查找键值的数据结构序列,本文的哈希表的实现用于构造字符串键和字符串值的哈希序列。主要应用于对象的属性集合的存取。
哈希表的结构有主散列和子序列构成,主散列是一线性数组,数组的大小为size,是一个素数,也是哈希函数的模基,数组序号[0...size]即哈希函数的值域,数组存储的是根连接件,由他维护着相同的哈希值(哈希函数值相同)的节点链表。子序列是存储节点的有序序列,是按各节点关键码升序排列的。哈希值和关键码都是由键值通过不同的函数生成的整型值。
查找的路径为,根据键值的哈希函数值在主散列中定位取得根连接件,然后在此子序列中根据键值的关键码筛选,最终通过键值的比较来定位到节点。
以下为哈希表的定义和实现:
/*************************************************************
EasySoft xdl v3.0
(c) 2003-2006 EasySoft Corporation. All Rights Reserved.
@doc xdl.rtf: hash table
@module hashtable.h | hash table interface file
@devnote PowerSuite 2003.01 - 2006.12
*************************************************************/
#ifndef _HASHTABLE_H
#define _HASHTABLE_H
#include "xdl/link.h"
#include "xdl/xdldef.h"
#ifdef __cplusplus
extern "C" {
#endif
/*define enum hash entity callback function */
typedef int (*EnumHashEntityPtr)(LINKPTR ent,void* pv);
/*
功能:枚举哈希表的键集的回调函数
参数:ent为本次枚举的键,pv回传的参数
返回:零值继续枚举,非零值终止枚举
*/
XDL_API LINKPTR CreateHashTable(int nSize);
/*
功能:创建哈希文档
参数:表大小,可以为MIN_PRIM,MID_PRM,MAX_PRIM
返回:哈希表文档连接件指针
*/
XDL_API void DestroyHashTable(LINKPTR ptr);
/*
功能:销毁哈希文档
参数:哈希表文档连接件指针
返回:无
*/
XDL_API void CopyHashTable(LINKPTR ptrDest,LINKPTR ptrSrc);
/*
功能:拷贝哈希文档
参数:ptrDest为目的哈希文档指针,ptrSrc为源哈希文档指针
返回:无
*/
XDL_API void ClearHashTable(LINKPTR ptr);
/*
功能:清除哈希文档键值集合
参数:哈希表文档连接件指针
返回:无
*/
XDL_API LINKPTR AddHashEntity(LINKPTR ptr,const TCHAR* szKey,int keylen,const TCHAR* szVal,int vallen);
/*
功能:添加一个键集到哈希文档
参数:ptr为哈希表文档连接件指针,szKey为键名,keylen为键长度,szVal为键值,vallen为值长度
返回:键连接件指针,不存在的键名则添加新键,已存在的键名,键值将被覆盖
*/
XDL_API LINKPTR GetHashEntity(LINKPTR ptr,const TCHAR* szKey,int keylen);
/*
功能:由键名检索键
参数:ptr为哈希表文档连接件指针,szKey为键名,keylen为键长度
返回:键的连接件指针,不存在则返回NULL
*/
XDL_API void DeleteHashEntity(LINKPTR elk);
/*
功能:删除键
参数:键的连接件指针
返回:无
*/
XDL_API const TCHAR* GetHashEntityKeyPtr(LINKPTR elk);
/*
功能:取键的名称指针
参数:键的连接件指针
返回:键名的指针
*/
XDL_API const TCHAR* GetHashEntityValPtr(LINKPTR elk);
/*
功能:取键值指针
参数:键的连接件指针
返回:键值的指针
*/
XDL_API int GetHashEntityKey(LINKPTR elk,TCHAR* buf,int max);
/*
功能:拷贝键的名称
参数:elk为键的连接件指针,buf为缓冲区指针,max为缓冲区大小,不包括终结符
返回:实际拷贝的字节数
*/
XDL_API int GetHashEntityVal(LINKPTR elk,TCHAR* buf,int max);
/*
功能:拷贝键值
参数:elk为键的连接件指针,buf为缓冲区指针,max为缓冲区大小,不包括终结符
返回:实际拷贝的字节数
*/
XDL_API void SetHashEntityData(LINKPTR elk,unsigned long data);
/*
功能:设置键的额外数据
参数:elk为键的连接件指针,data为额外数据
返回:无
*/
XDL_API unsigned long GetHashEntityData(LINKPTR elk);
/*
功能:取得键的额外数据
参数:elk为键的连接件指针
返回:额外数据
*/
XDL_API int EnumHashEntity(LINKPTR ptr,EnumHashEntityPtr pf,void* pv);
/*
功能:枚举哈希文档的键
参数:ptr为哈希文档指针,pf为回调函数,pv为传入参数
返回:被枚举的键的计数
*/
XDL_API void HashTableParseOptions(LINKPTR ptr,const TCHAR* options,int len,TCHAR itemfeed,TCHAR linefeed);
/*
功能:从选项串中解析键与值集合
参数:ptr为哈希文档指针,options为选项串,格式如: 0~false;1~true;,len为选项串长度
itemfeed为名与值的隔离符号,如:~,linefeed为条目的隔离符号,如:;
返回:无
*/
XDL_API int HashTableFormatOptions(LINKPTR ptr,TCHAR* buf,int max,TCHAR itemfeed,TCHAR linefeed);
/*
功能:将哈希文档的键值集合格式化成选项串
参数:ptr为哈希文档指针,buf为缓冲区指针,max为缓冲区长度,itemfeed为名与值的隔离符号,如:~,linefeed为条目的隔离符号,如:;
返回:无
*/
XDL_API int HashTableFormatOptionsSize(LINKPTR ptr,TCHAR itemfeed,TCHAR linefeed);
/*
功能:测试将哈希文档的键值集合格式化成选项串所需要的缓冲区大小,不包括终结符
参数:ptr为哈希文档指针,itemfeed为名与值的隔离符号,如:~,linefeed为条目的隔离符号,如:;
返回:无
*/
#ifdef __cplusplus
}
#endif
#endif /*_HASHTABLE_H*/
/*************************************************************
EasySoft xdl v3.0
(c) 2003-2006 EasySoft Corporation. All Rights Reserved.
@doc xdl.rtf: hash table
@module hashtable.c | hash table implement file
@devnote PowerSuite 2003.01 - 2006.12
*************************************************************/
/************************************************************
*************************************************************/
#include "xdl/hashtable.h"
#include "xdl/xdlutil.h"
typedef struct _HashEntity{
LINK lk; /* entity self link component*/
TCHAR* szKey; /* entity key, can not be null*/
TCHAR* szVal; /* entity value*/
unsigned short code; /* entity key code*/
unsigned long data; /* entiry data for XdlFree use*/
}HashEntity;
typedef struct _HashTable{
LINK lk; /* hash table self link component*/
LINKPTR pp; /* hash table master root link ptr array*/
int size; /* root link ptr array size, is prim value*/
}HashTable;
/* restore hash table struct ptr from link ptr*/
#define HashTableFromLink(p) ((HashTable*)((unsigned int)p - (unsigned int)&(((HashTable*)0)->lk)))
/* restore hash entity struct ptr from link ptr*/
#define HashEntityFromLink(p) ((HashEntity*)((unsigned int)p - (unsigned int)&(((HashEntity*)0)->lk)))
static int HashCode(const TCHAR* szKey,int keylen,int nPrim)
{
unsigned int sum;
int i;
assert(szKey);
assert(nPrim > 0);
if(keylen == -1)
keylen = _tcslen(szKey);
sum = 0;
for(i=0;i<keylen;i++)
{
sum += (unsigned int)(szKey[i]);
}
return sum % nPrim;
}
static unsigned short KeyCode(const TCHAR* szKey,int keylen)
{
unsigned short code;
int i;
assert(szKey);
if(keylen == -1)
keylen = _tcslen(szKey);
code = 0;
for(i=0;i<keylen;i++)
{
code += (unsigned short)(szKey[i]);
code = code << 1;
}
return code ;
}
void _InsertEntity(LINKPTR root,LINKPTR ptrEntity)
{
HashEntity* pnew;
HashEntity* phe;
LINKPTR plk;
assert(root && root->tag == lkRoot);
assert(ptrEntity && ptrEntity->tag == lkHashEntity);
pnew = HashEntityFromLink(ptrEntity);
plk = GetFirstLink(root);
while(plk)
{
phe = HashEntityFromLink(plk);
if(phe->code > pnew->code)
break;/*find the prev link*/
plk = GetNextLink(plk);
}
if(plk == NULL)
{
InsertLink(root,LINK_LAST,ptrEntity);
}else
{
InsertLinkBefore(root,plk,ptrEntity);
}
}
LINKPTR CreateHashTable(int nSize)
{
HashTable* pht;
int i;
assert(nSize > 0);
pht = (HashTable*)XdlAlloc(1,sizeof(HashTable));
pht->lk.tag = lkHashTable;
pht->size = nSize;
pht->pp = (LINKPTR)XdlAlloc(nSize,sizeof(LINK));
for(i = 0;i<pht->size;i++)
InitRootLink(pht->pp + i); /*initialize each master root link ptr*/
return &(pht->lk);
}
void DestroyHashTable(LINKPTR ptr)
{
HashTable* pht;
assert(ptr);
assert(ptr->tag == lkHashTable);
pht = HashTableFromLink(ptr);
ClearHashTable(ptr); /*XdlFree all hash eitity*/
XdlFree(pht->pp);
XdlFree(pht);
}
void CopyHashTable(LINKPTR ptrDest,LINKPTR ptrSrc)
{
HashTable* pht;
HashEntity* phe;
LINKPTR plk;
int i;
assert(ptrDest && ptrDest->tag == lkHashTable);
assert(ptrSrc && ptrSrc->tag == lkHashTable);
pht = HashTableFromLink(ptrSrc);
for(i=0;i<pht->size;i++)
{
plk = GetFirstLink(&((pht->pp)[i]));
while(plk != NULL)
{
phe = HashEntityFromLink(plk);
AddHashEntity(ptrDest,phe->szKey,-1,phe->szVal,-1);
plk = GetNextLink(plk);
}
}
}
void ClearHashTable(LINKPTR ptr)
{
HashTable* pht;
HashEntity* phe;
LINKPTR plk,pre;
int i;
assert(ptr);
assert(ptr->tag == lkHashTable);
pht = HashTableFromLink(ptr);
for(i=0;i<pht->size;i++)
{
plk = GetFirstLink(&(pht->pp[i]));
while(plk)
{
pre = plk;
plk = GetNextLink(plk);
phe = HashEntityFromLink(pre);
XdlFree(phe->szKey);
if(phe->szVal)
XdlFree(phe->szVal);
XdlFree(phe);
}
}
for(i = 0;i<pht->size;i++)
InitRootLink(pht->pp + i); /*set each master root link ptr to initialized state*/
}
LINKPTR AddHashEntity(LINKPTR ptr,const TCHAR* szKey,int keylen,const TCHAR* szVal,int vallen)
{
HashTable* pht;
HashEntity* phe;
LINKPTR plk;
int nIndex;
assert(ptr);
assert(ptr->tag == lkHashTable);
if(IsNullStr(szKey) || keylen == 0)
return NULL;
pht = HashTableFromLink(ptr);
/*first to find entity with the key*/
plk = GetHashEntity(ptr,szKey,keylen);
if(plk != NULL) /*if exist then to replace the entiry value*/
{
phe = HashEntityFromLink(plk);
if(phe->szVal != NULL)
{
XdlFree(phe->szVal);
phe->szVal = NULL;
}
if(!IsNullStr(szVal) && vallen)
{
if(vallen == -1)
vallen = _tcslen(szVal);
phe->szVal = (TCHAR*)XdlAlloc(vallen + 1,sizeof(TCHAR));
_tcsncpy(phe->szVal,szVal,vallen);
}
}else /*if not exist then to add new entity with key and value*/
{
phe = (HashEntity*)XdlAlloc(1,sizeof(HashEntity));
phe->lk.tag = lkHashEntity;
if(keylen == -1)
keylen = _tcslen(szKey);
phe->szKey = (TCHAR*)XdlAlloc(keylen + 1,sizeof(TCHAR));
_tcsncpy(phe->szKey,szKey,keylen);
phe->code = KeyCode(szKey,keylen);
if(!IsNullStr(szVal) && vallen)
{
if(vallen == -1)
vallen = _tcslen(szVal);
phe->szVal = (TCHAR*)XdlAlloc(vallen + 1,sizeof(TCHAR));
_tcsncpy(phe->szVal,szVal,vallen);
}
nIndex = HashCode(phe->szKey,keylen,pht->size);
plk = &((pht->pp)[nIndex]);
/*insert entity to list by key's asc order*/
_InsertEntity(plk,&phe->lk);
}
return &(phe->lk);
}
LINKPTR GetHashEntity(LINKPTR ptr,const TCHAR* szKey,int keylen)
{
int nIndex;
unsigned short code;
HashEntity* phe;
HashTable* pht;
LINKPTR plk;
assert(ptr);
assert(ptr->tag == lkHashTable);
if(IsNullStr(szKey) || keylen == 0)
return NULL;
pht = HashTableFromLink(ptr);
if(keylen == -1)
keylen = _tcslen(szKey);
/*first to calc master root link ptr array position by the key's hash code */
nIndex = HashCode(szKey,keylen,pht->size);
code = KeyCode(szKey,keylen);
/*then to compare the entity key in the ordered list*/
plk = GetFirstLink(&((pht->pp)[nIndex]));
while(plk != NULL)
{
phe = HashEntityFromLink(plk);
if(phe->code == code)
{
if(0 == _tcsncmp(phe->szKey,szKey,keylen))
{
if((int)_tcslen(phe->szKey) == keylen)
return plk;
}
}else if(phe->code > code)
return NULL;
plk = GetNextLink(plk);
}
return NULL;
}
void DeleteHashEntity(LINKPTR elk)
{
HashEntity* phe;
assert(elk);
assert(elk->tag == lkHashEntity);
phe = HashEntityFromLink(elk);
/*delete link ptr from list*/
DeleteLink(GetRootLink(elk),elk);
XdlFree(phe->szKey);
if(phe->szVal)
XdlFree(phe->szVal);
XdlFree(phe);
}
const TCHAR* GetHashEntityKeyPtr(LINKPTR elk)
{
HashEntity* phe;
assert(elk);
assert(elk->tag == lkHashEntity);
phe = HashEntityFromLink(elk);
return phe->szKey;
}
const TCHAR* GetHashEntityValPtr(LINKPTR elk)
{
HashEntity* phe;
assert(elk);
assert(elk->tag == lkHashEntity);
phe = HashEntityFromLink(elk);
return phe->szVal;
}
int GetHashEntityKey(LINKPTR elk,TCHAR* buf,int max)
{
HashEntity* phe;
int len;
assert(elk);
assert(elk->tag == lkHashEntity);
assert(buf && max >= 0);
phe = HashEntityFromLink(elk);
len = _tcslen(phe->szKey);
len = (len < max)? len : max;
_tcsncpy(buf,phe->szKey,len);
buf[len] = _T('/0');
return len;
}
int GetHashEntityVal(LINKPTR elk,TCHAR* buf,int max)
{
HashEntity* phe;
int len;
assert(elk);
assert(elk->tag == lkHashEntity);
assert(buf && max >= 0);
phe = HashEntityFromLink(elk);
if(phe->szVal == NULL)
{
buf[0] = _T('/0');
return 0;
}else
{
len = _tcslen(phe->szVal);
len = (len < max)? len : max;
_tcsncpy(buf,phe->szVal,len);
buf[len] = _T('/0');
return len;
}
}
void SetHashEntityData(LINKPTR elk,unsigned long data)
{
HashEntity* phe;
assert(elk);
assert(elk->tag == lkHashEntity);
phe = HashEntityFromLink(elk);
phe->data = data;
}
unsigned long GetHashEntityData(LINKPTR elk)
{
HashEntity* phe;
assert(elk);
assert(elk->tag == lkHashEntity);
phe = HashEntityFromLink(elk);
return phe->data;
}
int EnumHashEntity(LINKPTR ptr,EnumHashEntityPtr pf,void* pv)
{
HashTable* pht;
HashEntity* phe;
LINKPTR plk;
int i,count;
assert(ptr);
assert(ptr->tag == lkHashTable);
pht = HashTableFromLink(ptr);
count = 0;
for(i=0;i<pht->size;i++)
{
plk = GetFirstLink(&((pht->pp)[i]));
while(plk != NULL)
{
phe = HashEntityFromLink(plk);
count ++;
if(pf == NULL)
{
plk = GetNextLink(plk);
continue;
}
/*if callback function return none zero value then to break enum*/
if((*pf)(plk,pv))
return count;
plk = GetNextLink(plk);
}
}
return count;
}
XDL_API void HashTableParseOptions(LINKPTR ptr,const TCHAR* options,int len, TCHAR itemfeed,TCHAR linefeed)
{
TCHAR* token;
TCHAR *key,*val;
int keylen,vallen;
assert(ptr && ptr->tag == lkHashTable);
ClearHashTable(ptr);
if(options == NULL || len == 0)
return;
if(len == -1)
len = _tcslen(options);
token = (TCHAR*)options;
while(token != options + len)
{
key = token;
while(*token != itemfeed && *token != _T('/0'))
token ++;
keylen = token - key;
if(token == _T('/0'))
break;
token ++; /*skip item feed*/
val = token;
while(*token != linefeed && *token != _T('/0'))
token ++;
vallen = token - val;
AddHashEntity(ptr,key,keylen,val,vallen);
if(*token == _T('/0'))
break;
token ++; /*skip line feed*/
}
}
int HashTableFormatOptions(LINKPTR ptr,TCHAR* buf,int max,TCHAR itemfeed,TCHAR linefeed)
{
HashTable* pht;
HashEntity* phe;
LINKPTR plk;
int i,total,len;
assert(ptr);
assert(ptr->tag == lkHashTable);
pht = HashTableFromLink(ptr);
total = 0;
for(i=0;i<pht->size;i++)
{
plk = GetFirstLink(&((pht->pp)[i]));
while(plk != NULL)
{
phe = HashEntityFromLink(plk);
len = _tcslen(phe->szKey) + ((phe->szVal)? _tcslen(phe->szVal) : 0) + 2; /*include itemfeed and linefeed*/
if(total + len > max)
return -1;
if(buf)
_stprintf(buf + total,_T("%s%c%s%c"),phe->szKey,itemfeed,((phe->szVal)? phe->szVal : _T("")),linefeed);
total += len;
plk = GetNextLink(plk);
}
}
return total;
}
int HashTableFormatOptionsSize(LINKPTR ptr,TCHAR itemfeed,TCHAR linefeed)
{
return HashTableFormatOptions(ptr,NULL,MAX_INT,itemfeed,linefeed);
}
collected by barenx