/***************************************************************************
file : hash.cpp
created : Sat Dec 14 16:40:15 CET 2002
copyright : (C) 2002 by Eric Espié
email : eric.espie@torcs.org
version : $Id: hash.cpp,v 1.4 2004/11/11 12:02:17 torcs Exp $
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/** @file
This is the hash computation API.
@author Eric Espie
@version $Id: hash.cpp,v 1.4 2004/11/11 12:02:17 torcs Exp $
@ingroup hash
*/
#include
typedef struct HashElem
{
char *key;
int size;
void *data;
GF_TAILQ_ENTRY(struct HashElem) link;
} tHashElem;
GF_TAILQ_HEAD(HashHead, tHashElem);
typedef struct HashHeader
{
int type;
int size;
int nbElem;
/* for table traversal */
int curIndex;
tHashElem *curElem;
tHashHead *hashHead;
} tHashHeader;
#define HASH_BYTE(x, y) (((y) + ((x) << 4) + ((x) >> 4)) * 11)
#define DEFAULT_SIZE 32
static unsigned int
hash_str (tHashHeader *hash, char *sstr)
{
unsigned char *str = (unsigned char *)sstr;
unsigned int val = 0;
if (!str)
return 0;
/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
while (*str)
{
val = (val + (*str >> 4) + (*str << 4)) * 11;
str++;
}
return val % hash->size;
}
static unsigned int
hash_buf (tHashHeader *hash, char *sdata, int len)
{
unsigned int val = 0;
unsigned char *data = (unsigned char *)sdata;
int i;
if (!data)
return 0;
/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
for (i = 0; i < len; i++)
val = (val + (data[i] >> 4) + (data[i] << 4)) * 11;
return val % hash->size;
}
/** Create a new hash table
@ingroup hash
@param type Type of key used (GF_HASH_TYPE_STR or GF_HASH_TYPE_BUF)
@return handle on new hash table
0 if Error
*/
void *
GfHashCreate(int type)
{
tHashHeader *curHeader;
int i;
curHeader = (tHashHeader*)malloc(sizeof(tHashHeader));
if (!curHeader) {
return NULL;
}
curHeader->type = type;
curHeader->size = DEFAULT_SIZE;
curHeader->nbElem = 0;
curHeader->curIndex = 0;
curHeader->curElem = NULL;
curHeader->hashHead = (tHashHead *)malloc(DEFAULT_SIZE * sizeof(tHashHead));
for (i = 0; i < DEFAULT_SIZE; i++) {
GF_TAILQ_INIT(&(curHeader->hashHead[i]));
}
return (void*)curHeader;
}
/* Double the size of the hash table */
static void
gfIncreaseHash(tHashHeader *curHeader)
{
tHashHead *oldHashHead;
tHashElem *curElem;
int oldSize;
int hindex;
int i;
oldHashHead = curHeader->hashHead;
oldSize = curHeader->size;
curHeader->size *= 2;
curHeader->hashHead = (tHashHead *)malloc(curHeader->size * sizeof(tHashHead));
for (i = 0; i < curHeader->size; i++) {
GF_TAILQ_INIT(&(curHeader->hashHead[i]));
}
/* copy the elements */
for (i = 0; i < oldSize; i++) {
while ((curElem = GF_TAILQ_FIRST(&(oldHashHead[i]))) != NULL) {
/* remove from old list */
GF_TAILQ_REMOVE(&(oldHashHead[i]), curElem, link);
/* insert in new list */
switch (curHeader->type) {
case GF_HASH_TYPE_STR:
hindex = hash_str(curHeader, curElem->key);
break;
case GF_HASH_TYPE_BUF:
hindex = hash_buf(curHeader, curElem->key, curElem->size);
break;
default:
hindex = 0; /* for the compiler... */
break;
}
GF_TAILQ_INSERT_TAIL(&(curHeader->hashHead[hindex]), curElem, link);
}
}
free(oldHashHead);
}
/** Add an element with a string key to a hash table.
@ingroup hash
@param hash Current hash table handle.
@param key key string to hash.
@param data user data.
@return 0 OK, 1 NOK.
*/
int
GfHashAddStr(void *hash, char *key, void *data)
{
tHashHeader *curHeader = (tHashHeader *)hash;
tHashElem *newElem;
unsigned int index;
if (curHeader->type != GF_HASH_TYPE_STR) {
return 1;
}
if ((curHeader->nbElem + 1) > (2 * curHeader->size)) {
gfIncreaseHash(curHeader);
}
index = hash_str(curHeader, key);
newElem = (tHashElem*)malloc(sizeof(tHashElem));
if (!newElem) {
return 1;
}
newElem->key = strdup(key);
newElem->size = strlen(key) + 1;
newElem->data = data;
GF_TAILQ_INSERT_TAIL(&(curHeader->hashHead[index]), newElem, link);
curHeader->nbElem++;
return 0;
}
/* Remove a table element */
static void *
gfRemElem(tHashHead *hashHead, tHashElem *elem)
{
void *data;
data = elem->data;
free(elem->key);
GF_TAILQ_REMOVE(hashHead, elem, link);
free(elem);
return data;
}
/** Remove an element with a string key from a hash table.
@ingroup hash
@param hash Current hash table handle.
@param key key string to hash.
@return User data.
*/
void *
GfHashRemStr(void *hash, char *key)
{
tHashHeader *curHeader = (tHashHeader *)hash;
tHashElem *curElem;
unsigned int index;
index = hash_str(curHeader, key);
curElem = GF_TAILQ_FIRST(&(curHeader->hashHead[index]));
while (curElem) {
if (!strcmp(curElem->key, key)) {
curHeader->nbElem--;
return gfRemElem(&(curHeader->hashHead[index]), curElem);
}
curElem = GF_TAILQ_NEXT(curElem, link);
}
return NULL;
}
/** Get the user data associated with a string key.
@ingroup hash
@param hash Current hash table handle.
@param key key string to hash.
@return User data.
*/
void *
GfHashGetStr(void *hash, char *key)
{
tHashHeader *curHeader = (tHashHeader *)hash;
tHashElem *curElem;
unsigned int index;
index = hash_str(curHeader, key);
curElem = GF_TAILQ_FIRST(&(curHeader->hashHead[index]));
while (curElem) {
if (!strcmp(curElem->key, key)) {
return curElem->data;
}
curElem = GF_TAILQ_NEXT(curElem, link);
}
return NULL;
}
/** Add an element with a memory buffer key to a hash table.
@ingroup hash
@param hash Current hash table handle.
@param key key buffer to hash.
@param sz size of the buffer.
@param data user data.
@return none
*/
void
GfHashAddBuf(void *hash, char *key, size_t sz, void *data)
{
tHashHeader *curHeader = (tHashHeader *)hash;
tHashElem *newElem;
unsigned int index;
if (curHeader->type != GF_HASH_TYPE_BUF) {
return;
}
if ((curHeader->nbElem + 1) > (2 * curHeader->size)) {
gfIncreaseHash(curHeader);
}
index = hash_buf(curHeader, key, sz);
newElem = (tHashElem*)malloc(sizeof(tHashElem));
newElem->key = (char *)malloc(sz);
memcpy(newElem->key, key, sz);
newElem->size = sz;
newElem->data = data;
GF_TAILQ_INSERT_TAIL(&(curHeader->hashHead[index]), newElem, link);
curHeader->nbElem++;
}
/** Remove an element with a memory buffer key from a hash table.
@ingroup hash
@param hash Current hash table handle.
@param key key buffer to hash.
@param sz size of the buffer.
@return User data.
*/
void *
GfHashRemBuf(void *hash, char *key, size_t sz)
{
tHashHeader *curHeader = (tHashHeader *)hash;
tHashElem *curElem;
unsigned int index;
index = hash_buf(curHeader, key, sz);
curElem = GF_TAILQ_FIRST(&(curHeader->hashHead[index]));
while (curElem) {
if (!memcmp(curElem->key, key, sz)) {
curHeader->nbElem--;
return gfRemElem(&(curHeader->hashHead[index]), curElem);
}
curElem = GF_TAILQ_NEXT(curElem, link);
}
return NULL;
}
/** Get the user data associated with a memory buffer key.
@ingroup hash
@param hash Current hash table handle.
@param key key buffer to hash.
@param sz size of the buffer.
@return User data.
*/
void *
GfHashGetBuf(void *hash, char *key, size_t sz)
{
tHashHeader *curHeader = (tHashHeader *)hash;
tHashElem *curElem;
unsigned int index;
index = hash_buf(curHeader, key, sz);
curElem = GF_TAILQ_FIRST(&(curHeader->hashHead[index]));
while (curElem) {
if (!memcmp(curElem->key, key, sz)) {
return curElem->data;
}
curElem = GF_TAILQ_NEXT(curElem, link);
}
return NULL;
}
/** Release a hash table.
@ingroup hash
@param hash Current hash table handle.
@param hashFree pointer on user function used to free the user data (NULL if not used).
@return none
*/
void
GfHashRelease(void *hash, tfHashFree hashFree)
{
tHashHeader *curHeader = (tHashHeader *)hash;
tHashElem *curElem;
void *data;
int i;
for (i = 0; i < curHeader->size; i++) {
while ((curElem = GF_TAILQ_FIRST(&(curHeader->hashHead[i]))) != NULL) {
data = gfRemElem(&(curHeader->hashHead[i]), curElem);
if (hashFree) {
hashFree(data);
}
}
}
free(curHeader->hashHead);
free(curHeader);
}
/** Get the first user data of a hash table.
This is used for table scan.
@ingroup hash
@param hash Current hash table handle.
@return User data.
@see GfHashGetNext
*/
void *
GfHashGetFirst(void *hash)
{
tHashHeader *curHeader = (tHashHeader *)hash;
curHeader->curIndex = -1;
curHeader->curElem = NULL;
return GfHashGetNext(hash);
}
/** Get the next user data of a hash table.
This is used for table scan.
@ingroup hash
@param hash Current hash table handle.
@return User data.
@see GfHashGetFirst
*/
void *
GfHashGetNext(void *hash)
{
tHashHeader *curHeader = (tHashHeader *)hash;
if (curHeader->curElem) {
curHeader->curElem = GF_TAILQ_NEXT(curHeader->curElem, link);
}
while (!curHeader->curElem) {
curHeader->curIndex++;
if (curHeader->curIndex == curHeader->size) {
return NULL;
}
curHeader->curElem = GF_TAILQ_FIRST(&(curHeader->hashHead[curHeader->curIndex]));
}
return curHeader->curElem->data;
}