#include "dapl_hash.h"
typedef struct DAPL_HASH_ELEM
{
struct DAPL_HASH_ELEM *next_element;
DAPL_HASH_KEY key;
void *datum;
} DAPL_HASH_ELEM;
struct dapl_hash_table
{
unsigned long num_entries;
unsigned long tbl_size;
DAPL_HASH_ELEM *table;
DAT_BOOLEAN locking_required;
DAPL_OS_LOCK lock;
unsigned long iterator_bucket;
DAPL_HASH_ELEM *iterator;
uint64_t hash_tbl_inserts;
uint64_t hash_tbl_max;
uint64_t hash_tbl_total;
uint64_t hash_chn_max;
uint64_t hash_chn_total;
};
#define DAPL_DOHASH(key, hashsize) ((uint64_t)((key) % (hashsize)))
#define NO_DATUM_VALUE ((void *) 0UL)
#define NO_DATUM(value) ((value) == NO_DATUM_VALUE)
#define DAPL_HASHLOOKUP(p_table, in_key, out_datum, bucket_head) \
{ \
DAPL_HASH_ELEM *element = \
&((p_table)->table)[DAPL_DOHASH(in_key, (p_table)->tbl_size)]; \
if (NO_DATUM(element->datum)) { \
(bucket_head) = (void *)0; \
} else if (element->key == (DAPL_HASH_KEY) (in_key)) { \
(out_datum) = element->datum; \
(bucket_head) = (void *)element; \
} else if (element->next_element) { \
dapli_hash_rehash(element, \
(in_key), \
(void **)&(out_datum), \
(DAPL_HASH_ELEM **)&(bucket_head)); \
} else { \
(bucket_head) = (void *)0; \
}\
}
static void
dapli_hash_rehash(
DAPL_HASH_ELEM *element,
DAPL_HASH_KEY key,
void **datum,
DAPL_HASH_ELEM ** head)
{
dapl_os_assert(element->next_element);
dapl_os_assert(!NO_DATUM(element->datum));
*head = element;
while (1) {
element = element->next_element;
if (!element) {
break;
}
if (element->key == key) {
*datum = element->datum;
return;
}
}
*head = 0;
}
static DAT_BOOLEAN
dapli_hash_add(
DAPL_HASH_TABLEP p_table,
DAPL_HASH_KEY key,
void *datum,
DAT_BOOLEAN allow_dup,
DAT_BOOLEAN *report_dup)
{
void *olddatum;
DAPL_HASH_KEY hashValue;
DAPL_HASH_ELEM *found;
DAT_BOOLEAN status = DAT_FALSE;
unsigned int chain_len = 0;
if (report_dup) {
(*report_dup) = DAT_FALSE;
}
if (NO_DATUM(datum)) {
dapl_dbg_log(
DAPL_DBG_TYPE_ERR,
"dapli_hash_add () called with magic NO_DATA value "
"(%p) used as datum!\n", datum);
return (DAT_FALSE);
}
DAPL_HASHLOOKUP(p_table, key, olddatum, found);
if (found) {
if (report_dup) {
*report_dup = DAT_TRUE;
}
if (!allow_dup) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"dapli_hash_add () called with duplicate key "
"(" F64x ")\n", key);
return (DAT_FALSE);
}
}
hashValue = DAPL_DOHASH(key, p_table->tbl_size);
if (NO_DATUM(p_table->table[hashValue].datum)) {
p_table->table[hashValue].key = key;
p_table->table[hashValue].datum = datum;
p_table->table[hashValue].next_element = 0;
p_table->num_entries++;
status = DAT_TRUE;
} else {
DAPL_HASH_ELEM *newelement = (DAPL_HASH_ELEM *)
dapl_os_alloc(sizeof (DAPL_HASH_ELEM));
if (newelement) {
DAPL_HASH_ELEM *lastelement;
newelement->key = key;
newelement->datum = datum;
newelement->next_element = 0;
for (lastelement = &p_table->table[hashValue];
lastelement->next_element;
lastelement = lastelement->next_element) {
chain_len++;
}
lastelement->next_element = newelement;
p_table->num_entries++;
status = DAT_TRUE;
} else {
status = DAT_FALSE;
}
}
chain_len++;
p_table->hash_tbl_inserts++;
p_table->hash_tbl_total += p_table->num_entries;
p_table->hash_chn_total += chain_len;
if (p_table->num_entries > p_table->hash_tbl_max) {
p_table->hash_tbl_max = p_table->num_entries;
}
if (chain_len > p_table->hash_chn_max) {
p_table->hash_chn_max = chain_len;
}
return (status);
}
static DAT_BOOLEAN
dapl_hash_delete_element(DAPL_HASH_ELEM * element,
DAPL_HASH_KEY key,
DAPL_HASH_DATA *p_datum)
{
DAPL_HASH_ELEM *curelement;
DAPL_HASH_ELEM *lastelement;
lastelement = NULL;
for (curelement = element; curelement;
lastelement = curelement,
curelement = curelement->next_element) {
if (curelement->key == key) {
if (p_datum) {
*p_datum = curelement->datum;
}
if (lastelement) {
lastelement->next_element =
curelement->next_element;
dapl_os_free((void *) curelement,
sizeof (DAPL_HASH_ELEM));
} else {
DAPL_HASH_ELEM *n = curelement->next_element;
if (n) {
curelement->key = n->key;
curelement->datum = n->datum;
curelement->next_element =
n->next_element;
dapl_os_free((void *) n,
sizeof (DAPL_HASH_ELEM));
} else {
curelement->datum = NO_DATUM_VALUE;
}
}
break;
}
}
return (curelement != NULL ? DAT_TRUE : DAT_FALSE);
}
DAT_RETURN
dapls_hash_create(
IN DAT_COUNT table_size,
IN DAT_BOOLEAN locking_required,
OUT DAPL_HASH_TABLE **pp_table)
{
DAPL_HASH_TABLE *p_table;
DAT_COUNT table_length = table_size * sizeof (DAPL_HASH_ELEM);
DAT_RETURN dat_status;
DAT_COUNT i;
dapl_os_assert(pp_table);
dat_status = DAT_SUCCESS;
p_table = dapl_os_alloc(sizeof (DAPL_HASH_TABLE));
if (NULL == p_table) {
dat_status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
DAT_RESOURCE_MEMORY);
goto bail;
}
(void) dapl_os_memzero(p_table, sizeof (DAPL_HASH_TABLE));
p_table->tbl_size = table_size;
p_table->table = (DAPL_HASH_ELEM *)dapl_os_alloc(table_length);
if (NULL == p_table->table) {
dapl_os_free(p_table, sizeof (DAPL_HASH_TABLE));
dat_status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
DAT_RESOURCE_MEMORY);
goto bail;
}
dapl_os_lock_init(&p_table->lock);
p_table->locking_required = locking_required;
for (i = 0; i < table_size; i++) {
p_table->table[i].datum = NO_DATUM_VALUE;
p_table->table[i].key = 0;
p_table->table[i].next_element = 0;
}
*pp_table = p_table;
bail:
return (dat_status);
}
DAT_RETURN
dapls_hash_free(
IN DAPL_HASH_TABLE *p_table)
{
dapl_os_assert(p_table && p_table->table);
dapl_os_lock_destroy(&p_table->lock);
dapl_os_free(p_table->table,
sizeof (DAPL_HASH_ELEM) * p_table->tbl_size);
dapl_os_free(p_table, sizeof (DAPL_HASH_TABLE));
return (DAT_SUCCESS);
}
DAT_RETURN
dapls_hash_size(
IN DAPL_HASH_TABLE *p_table,
OUT DAT_COUNT *p_size)
{
dapl_os_assert(p_table && p_size);
*p_size = p_table->num_entries;
return (DAT_SUCCESS);
}
DAT_RETURN
dapls_hash_insert(
IN DAPL_HASH_TABLE *p_table,
IN DAPL_HASH_KEY key,
IN DAPL_HASH_DATA data)
{
DAT_RETURN dat_status;
dapl_os_assert(p_table);
dat_status = DAT_SUCCESS;
if (p_table->locking_required) {
dapl_os_lock(&p_table->lock);
}
if (!dapli_hash_add(p_table, key, data, DAT_FALSE, NULL)) {
dat_status = DAT_ERROR(DAT_INSUFFICIENT_RESOURCES,
DAT_RESOURCE_MEMORY);
}
if (p_table->locking_required) {
dapl_os_unlock(&p_table->lock);
}
return (dat_status);
}
DAT_RETURN
dapls_hash_search(
IN DAPL_HASH_TABLE *p_table,
IN DAPL_HASH_KEY key,
OUT DAPL_HASH_DATA *p_data)
{
DAT_RETURN dat_status;
void *olddatum;
DAPL_HASH_ELEM *found;
dapl_os_assert(p_table);
dat_status = DAT_ERROR(DAT_INVALID_PARAMETER, 0);
if (p_table->locking_required) {
dapl_os_lock(&p_table->lock);
DAPL_HASHLOOKUP(p_table, key, olddatum, found);
dapl_os_unlock(&p_table->lock);
} else {
DAPL_HASHLOOKUP(p_table, key, olddatum, found);
}
if (found) {
if (p_data) {
*p_data = olddatum;
}
dat_status = DAT_SUCCESS;
}
return (dat_status);
}
DAT_RETURN
dapls_hash_remove(
IN DAPL_HASH_TABLE *p_table,
IN DAPL_HASH_KEY key,
OUT DAPL_HASH_DATA *p_data)
{
DAT_RETURN dat_status;
DAPL_HASH_KEY hashValue;
dapl_os_assert(p_table);
dat_status = DAT_ERROR(DAT_INVALID_PARAMETER, 0);
if (p_table->num_entries == 0) {
dapl_dbg_log(DAPL_DBG_TYPE_ERR,
"dapls_hash_remove () called on empty hash table!\n");
return (dat_status);
}
hashValue = DAPL_DOHASH(key, p_table->tbl_size);
if (p_table->locking_required) {
dapl_os_lock(&p_table->lock);
}
if (dapl_hash_delete_element(&p_table->table[hashValue], key, p_data)) {
p_table->num_entries--;
dat_status = DAT_SUCCESS;
}
if (p_table->locking_required) {
dapl_os_unlock(&p_table->lock);
}
return (dat_status);
}
DAT_RETURN
dapls_hash_iterate(
IN DAPL_HASH_TABLE *p_table,
IN DAPL_HASH_ITERATOR op,
OUT DAPL_HASH_DATA *p_data)
{
DAPL_HASH_ELEM *curr;
dapl_os_assert(p_table);
if (p_table->locking_required) {
return (DAT_ERROR(DAT_INVALID_PARAMETER, 0));
}
if (op == DAPL_HASH_ITERATE_INIT) {
if (p_table->num_entries == 0) {
*p_data = NULL;
return (DAT_SUCCESS);
}
p_table->iterator_bucket = 0;
while (p_table->iterator_bucket < p_table->tbl_size) {
curr = &p_table->table[p_table->iterator_bucket];
if (NO_DATUM(curr->datum)) {
p_table->iterator_bucket++;
} else {
break;
}
}
dapl_os_assert(!NO_DATUM(curr->datum));
if (p_table->iterator_bucket == p_table->tbl_size) {
p_table->iterator = NULL;
} else {
p_table->iterator = curr;
}
} else {
dapl_os_assert(op == DAPL_HASH_ITERATE_NEXT);
}
if (p_table->iterator == NULL) {
*p_data = NULL;
return (DAT_SUCCESS);
}
dapl_dbg_log(DAPL_DBG_TYPE_EP,
"dapls_hash_iterate: entry found=(%p), bucket(%d)\n",
p_table->iterator, p_table->iterator_bucket);
*p_data = p_table->iterator->datum;
curr = p_table->iterator;
if (curr->next_element != NULL) {
p_table->iterator = curr->next_element;
dapl_os_assert(!NO_DATUM(p_table->iterator->datum));
} else {
p_table->iterator = NULL;
p_table->iterator_bucket++;
while (p_table->iterator_bucket < p_table->tbl_size) {
curr = &p_table->table[p_table->iterator_bucket];
if (NO_DATUM(curr->datum)) {
p_table->iterator_bucket++;
} else {
p_table->iterator = curr;
break;
}
}
}
return (DAT_SUCCESS);
}