#include "src/include/pmix_config.h"
#include <stdlib.h>
#include <string.h>
#include "src/class/pmix_hash_table.h"
#include "src/class/pmix_list.h"
#include "src/util/pmix_output.h"
#include "pmix_common.h"
#define HASH_MULTIPLIER 31
struct pmix_hash_element_t {
int valid;
union {
uint32_t u32;
uint64_t u64;
struct {
const void *key;
size_t key_size;
} ptr;
} key;
pmix_tma_t *tma;
void *value;
};
typedef struct pmix_hash_element_t pmix_hash_element_t;
struct pmix_hash_type_methods_t {
void (*elt_destructor)(pmix_hash_element_t *elt);
uint64_t (*hash_elt)(pmix_hash_element_t *elt);
};
static void pmix_hash_table_construct(pmix_hash_table_t *ht);
static void pmix_hash_table_destruct(pmix_hash_table_t *ht);
PMIX_CLASS_INSTANCE(pmix_hash_table_t, pmix_object_t, pmix_hash_table_construct,
pmix_hash_table_destruct);
static void pmix_hash_table_construct(pmix_hash_table_t *ht)
{
ht->ht_table = NULL;
ht->ht_capacity = ht->ht_size = ht->ht_growth_trigger = 0;
ht->ht_density_numer = ht->ht_density_denom = 0;
ht->ht_growth_numer = ht->ht_growth_denom = 0;
ht->ht_type_methods = NULL;
}
static void pmix_hash_table_destruct(pmix_hash_table_t *ht)
{
pmix_tma_t *const tma = pmix_obj_get_tma(&ht->super);
pmix_hash_table_remove_all(ht);
pmix_tma_free(tma, ht->ht_table);
}
static size_t pmix_hash_round_capacity_up(size_t capacity)
{
return ((capacity + 29) / 30 * 30 + 1);
}
int
pmix_hash_table_init2(pmix_hash_table_t *ht, size_t estimated_max_size, int density_numer,
int density_denom, int growth_numer, int growth_denom)
{
pmix_tma_t *const tma = pmix_obj_get_tma(&ht->super);
size_t est_capacity = estimated_max_size * density_denom / density_numer;
size_t capacity = pmix_hash_round_capacity_up(est_capacity);
ht->ht_table = (pmix_hash_element_t *)pmix_tma_calloc(tma, capacity, sizeof(pmix_hash_element_t));
if (PMIX_UNLIKELY(NULL == ht->ht_table)) {
return PMIX_ERR_OUT_OF_RESOURCE;
}
ht->ht_capacity = capacity;
ht->ht_density_numer = density_numer;
ht->ht_density_denom = density_denom;
ht->ht_growth_numer = growth_numer;
ht->ht_growth_denom = growth_denom;
ht->ht_growth_trigger = capacity * density_numer / density_denom;
ht->ht_type_methods = NULL;
return PMIX_SUCCESS;
}
int
pmix_hash_table_init(pmix_hash_table_t *ht, size_t table_size)
{
return pmix_hash_table_init2(ht, table_size, 1, 2, 2, 1);
}
int
pmix_hash_table_remove_all(pmix_hash_table_t *ht)
{
size_t ii;
for (ii = 0; ii < ht->ht_capacity; ii += 1) {
pmix_hash_element_t *elt = &ht->ht_table[ii];
if (elt->valid && ht->ht_type_methods && ht->ht_type_methods->elt_destructor) {
ht->ht_type_methods->elt_destructor(elt);
}
elt->valid = 0;
elt->value = NULL;
}
ht->ht_size = 0;
ht->ht_type_methods = NULL;
return PMIX_SUCCESS;
}
static int
pmix_hash_grow(pmix_hash_table_t *ht)
{
pmix_tma_t *const tma = pmix_obj_get_tma(&ht->super);
size_t jj, ii;
pmix_hash_element_t *old_table;
pmix_hash_element_t *new_table;
size_t old_capacity;
size_t new_capacity;
old_table = ht->ht_table;
old_capacity = ht->ht_capacity;
new_capacity = old_capacity * ht->ht_growth_numer / ht->ht_growth_denom;
new_capacity = pmix_hash_round_capacity_up(new_capacity);
new_table = (pmix_hash_element_t *)pmix_tma_calloc(tma, new_capacity, sizeof(new_table[0]));
if (PMIX_UNLIKELY(NULL == new_table)) {
return PMIX_ERR_OUT_OF_RESOURCE;
}
for (jj = 0; jj < old_capacity; jj += 1) {
pmix_hash_element_t *old_elt;
pmix_hash_element_t *new_elt;
old_elt = &old_table[jj];
if (old_elt->valid) {
for (ii = (ht->ht_type_methods->hash_elt(old_elt) % new_capacity);; ii += 1) {
if (ii == new_capacity) {
ii = 0;
}
new_elt = &new_table[ii];
if (!new_elt->valid) {
*new_elt = *old_elt;
break;
}
}
}
}
ht->ht_table = new_table;
ht->ht_capacity = new_capacity;
ht->ht_growth_trigger = new_capacity * ht->ht_density_numer / ht->ht_density_denom;
pmix_tma_free(tma, old_table);
return PMIX_SUCCESS;
}
static int
pmix_hash_table_remove_elt_at(pmix_hash_table_t *ht, size_t ii)
{
size_t jj, capacity = ht->ht_capacity;
pmix_hash_element_t *elts = ht->ht_table;
pmix_hash_element_t *elt;
elt = &elts[ii];
if (!elt->valid) {
return PMIX_ERROR;
}
elt->valid = 0;
if (ht->ht_type_methods->elt_destructor) {
ht->ht_type_methods->elt_destructor(elt);
}
for (ii = ii + 1;; ii += 1) {
if (ii == capacity) {
ii = 0;
}
elt = &elts[ii];
if (!elt->valid) {
break;
}
for (jj = ht->ht_type_methods->hash_elt(elt) % capacity;; jj += 1) {
if (jj == capacity) {
jj = 0;
}
if (jj == ii) {
break;
} else if (!elts[jj].valid) {
elts[jj] = elts[ii];
elts[ii].valid = 0;
break;
} else {
}
}
}
ht->ht_size -= 1;
return PMIX_SUCCESS;
}
static uint64_t pmix_hash_hash_elt_uint32(pmix_hash_element_t *elt)
{
return elt->key.u32;
}
static const struct pmix_hash_type_methods_t pmix_hash_type_methods_uint32
= {NULL, pmix_hash_hash_elt_uint32};
int
pmix_hash_table_get_value_uint32(pmix_hash_table_t *ht, uint32_t key, void **value)
{
size_t ii, capacity = ht->ht_capacity;
pmix_hash_element_t *elt;
#if PMIX_ENABLE_DEBUG
if (capacity == 0) {
pmix_output(0, "pmix_hash_table_get_value_uint32:"
"pmix_hash_table_init() has not been called");
return PMIX_ERROR;
}
if (NULL == pmix_obj_get_tma(&ht->super) &&
NULL != ht->ht_type_methods && &pmix_hash_type_methods_uint32 != ht->ht_type_methods) {
pmix_output(0, "pmix_hash_table_get_value_uint32:"
"hash table is for a different key type");
return PMIX_ERROR;
}
#endif
ht->ht_type_methods = &pmix_hash_type_methods_uint32;
for (ii = key % capacity;; ii += 1) {
if (ii == capacity) {
ii = 0;
}
elt = &ht->ht_table[ii];
if (!elt->valid) {
return PMIX_ERR_NOT_FOUND;
} else if (elt->key.u32 == key) {
*value = elt->value;
return PMIX_SUCCESS;
} else {
}
}
}
int
pmix_hash_table_set_value_uint32(pmix_hash_table_t *ht, uint32_t key, void *value)
{
int rc;
size_t ii, capacity = ht->ht_capacity;
pmix_hash_element_t *elt;
pmix_tma_t *const tma = pmix_obj_get_tma(&ht->super);
#if PMIX_ENABLE_DEBUG
if (capacity == 0) {
pmix_output(0, "pmix_hash_table_set_value_uint32:"
"pmix_hash_table_init() has not been called");
return PMIX_ERR_BAD_PARAM;
}
if (NULL == pmix_obj_get_tma(&ht->super) &&
NULL != ht->ht_type_methods && &pmix_hash_type_methods_uint32 != ht->ht_type_methods) {
pmix_output(0, "pmix_hash_table_set_value_uint32:"
"hash table is for a different key type");
return PMIX_ERROR;
}
#endif
ht->ht_type_methods = &pmix_hash_type_methods_uint32;
for (ii = key % capacity;; ii += 1) {
if (ii == capacity) {
ii = 0;
}
elt = &ht->ht_table[ii];
if (!elt->valid) {
elt->key.u32 = key;
elt->value = value;
elt->valid = 1;
elt->tma = tma;
ht->ht_size += 1;
if (ht->ht_size >= ht->ht_growth_trigger) {
if (PMIX_SUCCESS != (rc = pmix_hash_grow(ht))) {
return rc;
}
}
return PMIX_SUCCESS;
} else if (elt->key.u32 == key) {
elt->value = value;
return PMIX_SUCCESS;
} else {
}
}
}
int pmix_hash_table_remove_value_uint32(pmix_hash_table_t *ht, uint32_t key)
{
size_t ii, capacity = ht->ht_capacity;
#if PMIX_ENABLE_DEBUG
if (capacity == 0) {
pmix_output(0, "pmix_hash_table_get_value_uint32:"
"pmix_hash_table_init() has not been called");
return PMIX_ERROR;
}
if (NULL == pmix_obj_get_tma(&ht->super) &&
NULL != ht->ht_type_methods && &pmix_hash_type_methods_uint32 != ht->ht_type_methods) {
pmix_output(0, "pmix_hash_table_remove_value_uint32:"
"hash table is for a different key type");
return PMIX_ERROR;
}
#endif
ht->ht_type_methods = &pmix_hash_type_methods_uint32;
for (ii = key % capacity;; ii += 1) {
pmix_hash_element_t *elt;
if (ii == capacity)
ii = 0;
elt = &ht->ht_table[ii];
if (!elt->valid) {
return PMIX_ERR_NOT_FOUND;
} else if (elt->key.u32 == key) {
return pmix_hash_table_remove_elt_at(ht, ii);
} else {
}
}
}
static uint64_t pmix_hash_hash_elt_uint64(pmix_hash_element_t *elt)
{
return elt->key.u64;
}
static const struct pmix_hash_type_methods_t pmix_hash_type_methods_uint64
= {NULL, pmix_hash_hash_elt_uint64};
int
pmix_hash_table_get_value_uint64(pmix_hash_table_t *ht, uint64_t key, void **value)
{
size_t ii;
size_t capacity = ht->ht_capacity;
pmix_hash_element_t *elt;
#if PMIX_ENABLE_DEBUG
if (capacity == 0) {
pmix_output(0, "pmix_hash_table_get_value_uint64:"
"pmix_hash_table_init() has not been called");
return PMIX_ERROR;
}
if (NULL == pmix_obj_get_tma(&ht->super) &&
NULL != ht->ht_type_methods && &pmix_hash_type_methods_uint64 != ht->ht_type_methods) {
pmix_output(0, "pmix_hash_table_get_value_uint64:"
"hash table is for a different key type");
return PMIX_ERROR;
}
#endif
ht->ht_type_methods = &pmix_hash_type_methods_uint64;
for (ii = key % capacity;; ii += 1) {
if (ii == capacity) {
ii = 0;
}
elt = &ht->ht_table[ii];
if (!elt->valid) {
return PMIX_ERR_NOT_FOUND;
} else if (elt->key.u64 == key) {
*value = elt->value;
return PMIX_SUCCESS;
} else {
}
}
}
int
pmix_hash_table_set_value_uint64(pmix_hash_table_t *ht, uint64_t key, void *value)
{
int rc;
size_t ii, capacity = ht->ht_capacity;
pmix_hash_element_t *elt;
pmix_tma_t *const tma = pmix_obj_get_tma(&ht->super);
#if PMIX_ENABLE_DEBUG
if (capacity == 0) {
pmix_output(0, "pmix_hash_table_set_value_uint64:"
"pmix_hash_table_init() has not been called");
return PMIX_ERR_BAD_PARAM;
}
if (NULL == pmix_obj_get_tma(&ht->super) &&
NULL != ht->ht_type_methods && &pmix_hash_type_methods_uint64 != ht->ht_type_methods) {
pmix_output(0, "pmix_hash_table_set_value_uint64:"
"hash table is for a different key type");
return PMIX_ERROR;
}
#endif
ht->ht_type_methods = &pmix_hash_type_methods_uint64;
for (ii = key % capacity;; ii += 1) {
if (ii == capacity) {
ii = 0;
}
elt = &ht->ht_table[ii];
if (!elt->valid) {
elt->key.u64 = key;
elt->value = value;
elt->valid = 1;
elt->tma = tma;
ht->ht_size += 1;
if (ht->ht_size >= ht->ht_growth_trigger) {
if (PMIX_SUCCESS != (rc = pmix_hash_grow(ht))) {
return rc;
}
}
return PMIX_SUCCESS;
} else if (elt->key.u64 == key) {
elt->value = value;
return PMIX_SUCCESS;
} else {
}
}
}
int
pmix_hash_table_remove_value_uint64(pmix_hash_table_t *ht, uint64_t key)
{
size_t ii, capacity = ht->ht_capacity;
#if PMIX_ENABLE_DEBUG
if (capacity == 0) {
pmix_output(0, "pmix_hash_table_get_value_uint64:"
"pmix_hash_table_init() has not been called");
return PMIX_ERROR;
}
if (NULL == pmix_obj_get_tma(&ht->super) &&
NULL != ht->ht_type_methods && &pmix_hash_type_methods_uint64 != ht->ht_type_methods) {
pmix_output(0, "pmix_hash_table_remove_value_uint64:"
"hash table is for a different key type");
return PMIX_ERROR;
}
#endif
ht->ht_type_methods = &pmix_hash_type_methods_uint64;
for (ii = key % capacity;; ii += 1) {
pmix_hash_element_t *elt;
if (ii == capacity) {
ii = 0;
}
elt = &ht->ht_table[ii];
if (!elt->valid) {
return PMIX_ERR_NOT_FOUND;
} else if (elt->key.u64 == key) {
return pmix_hash_table_remove_elt_at(ht, ii);
} else {
}
}
}
static uint64_t pmix_hash_hash_key_ptr(const void *key, size_t key_size)
{
uint64_t hash;
const unsigned char *scanner;
size_t ii;
hash = 0;
scanner = (const unsigned char *) key;
for (ii = 0; ii < key_size; ii += 1) {
hash = HASH_MULTIPLIER * hash + *scanner++;
}
return hash;
}
static void pmix_hash_destruct_elt_ptr(pmix_hash_element_t *elt)
{
elt->key.ptr.key_size = 0;
void *key = (void *) elt->key.ptr.key;
if (NULL != key) {
elt->key.ptr.key = NULL;
pmix_tma_free(elt->tma, key);
}
}
static uint64_t pmix_hash_hash_elt_ptr(pmix_hash_element_t *elt)
{
return pmix_hash_hash_key_ptr(elt->key.ptr.key, elt->key.ptr.key_size);
}
static const struct pmix_hash_type_methods_t pmix_hash_type_methods_ptr
= {pmix_hash_destruct_elt_ptr, pmix_hash_hash_elt_ptr};
int
pmix_hash_table_get_value_ptr(pmix_hash_table_t *ht, const void *key, size_t key_size, void **value)
{
size_t ii, capacity = ht->ht_capacity;
pmix_hash_element_t *elt;
#if PMIX_ENABLE_DEBUG
if (capacity == 0) {
pmix_output(0, "pmix_hash_table_get_value_ptr:"
"pmix_hash_table_init() has not been called");
return PMIX_ERROR;
}
if (NULL == pmix_obj_get_tma(&ht->super) &&
NULL != ht->ht_type_methods && &pmix_hash_type_methods_ptr != ht->ht_type_methods) {
pmix_output(0, "pmix_hash_table_get_value_ptr:"
"hash table is for a different key type");
return PMIX_ERROR;
}
#endif
ht->ht_type_methods = &pmix_hash_type_methods_ptr;
for (ii = pmix_hash_hash_key_ptr(key, key_size) % capacity;; ii += 1) {
if (ii == capacity) {
ii = 0;
}
elt = &ht->ht_table[ii];
if (!elt->valid) {
return PMIX_ERR_NOT_FOUND;
} else if (elt->key.ptr.key_size == key_size
&& 0 == memcmp(elt->key.ptr.key, key, key_size)) {
*value = elt->value;
return PMIX_SUCCESS;
} else {
}
}
}
int
pmix_hash_table_set_value_ptr(pmix_hash_table_t *ht, const void *key, size_t key_size, void *value)
{
int rc;
size_t ii, capacity = ht->ht_capacity;
pmix_hash_element_t *elt;
pmix_tma_t *const tma = pmix_obj_get_tma(&ht->super);
#if PMIX_ENABLE_DEBUG
if (capacity == 0) {
pmix_output(0, "pmix_hash_table_set_value_ptr:"
"pmix_hash_table_init() has not been called");
return PMIX_ERR_BAD_PARAM;
}
if (NULL == pmix_obj_get_tma(&ht->super) &&
NULL != ht->ht_type_methods && &pmix_hash_type_methods_ptr != ht->ht_type_methods) {
pmix_output(0, "pmix_hash_table_set_value_ptr:"
"hash table is for a different key type");
return PMIX_ERROR;
}
#endif
ht->ht_type_methods = &pmix_hash_type_methods_ptr;
for (ii = pmix_hash_hash_key_ptr(key, key_size) % capacity;; ii += 1) {
if (ii == capacity) {
ii = 0;
}
elt = &ht->ht_table[ii];
if (!elt->valid) {
void *key_local = pmix_tma_malloc(tma, key_size);
memcpy(key_local, key, key_size);
elt->key.ptr.key = key_local;
elt->key.ptr.key_size = key_size;
elt->value = value;
elt->valid = 1;
elt->tma = tma;
ht->ht_size += 1;
if (ht->ht_size >= ht->ht_growth_trigger) {
if (PMIX_SUCCESS != (rc = pmix_hash_grow(ht))) {
return rc;
}
}
return PMIX_SUCCESS;
} else if (elt->key.ptr.key_size == key_size
&& 0 == memcmp(elt->key.ptr.key, key, key_size)) {
elt->value = value;
return PMIX_SUCCESS;
} else {
}
}
}
int
pmix_hash_table_remove_value_ptr(pmix_hash_table_t *ht, const void *key, size_t key_size)
{
size_t ii, capacity = ht->ht_capacity;
#if PMIX_ENABLE_DEBUG
if (capacity == 0) {
pmix_output(0, "pmix_hash_table_get_value_ptr:"
"pmix_hash_table_init() has not been called");
return PMIX_ERROR;
}
if (NULL == pmix_obj_get_tma(&ht->super) &&
NULL != ht->ht_type_methods && &pmix_hash_type_methods_ptr != ht->ht_type_methods) {
pmix_output(0, "pmix_hash_table_remove_value_ptr:"
"hash table is for a different key type");
return PMIX_ERROR;
}
#endif
ht->ht_type_methods = &pmix_hash_type_methods_ptr;
for (ii = pmix_hash_hash_key_ptr(key, key_size) % capacity;; ii += 1) {
pmix_hash_element_t *elt;
if (ii == capacity) {
ii = 0;
}
elt = &ht->ht_table[ii];
if (!elt->valid) {
return PMIX_ERR_NOT_FOUND;
} else if (elt->key.ptr.key_size == key_size
&& 0 == memcmp(elt->key.ptr.key, key, key_size)) {
return pmix_hash_table_remove_elt_at(ht, ii);
} else {
}
}
}
static int
pmix_hash_table_get_next_elt(pmix_hash_table_t *ht,
pmix_hash_element_t *prev_elt,
pmix_hash_element_t **next_elt)
{
pmix_hash_element_t *elts = ht->ht_table;
size_t ii, capacity = ht->ht_capacity;
for (ii = (NULL == prev_elt ? 0 : (prev_elt - elts) + 1); ii < capacity; ii += 1) {
pmix_hash_element_t *elt = &elts[ii];
if (elt->valid) {
*next_elt = elt;
return PMIX_SUCCESS;
}
}
return PMIX_ERROR;
}
int
pmix_hash_table_get_first_key_uint32(pmix_hash_table_t *ht, uint32_t *key, void **value,
void **node)
{
return pmix_hash_table_get_next_key_uint32(ht, key, value, NULL, node);
}
int
pmix_hash_table_get_next_key_uint32(pmix_hash_table_t *ht, uint32_t *key, void **value,
void *in_node, void **out_node)
{
pmix_hash_element_t *elt;
if (PMIX_SUCCESS == pmix_hash_table_get_next_elt(ht, (pmix_hash_element_t *) in_node, &elt)) {
*key = elt->key.u32;
*value = elt->value;
*out_node = elt;
return PMIX_SUCCESS;
}
return PMIX_ERROR;
}
int
pmix_hash_table_get_first_key_ptr(pmix_hash_table_t *ht, void **key, size_t *key_size, void **value,
void **node)
{
return pmix_hash_table_get_next_key_ptr(ht, key, key_size, value, NULL, node);
}
int
pmix_hash_table_get_next_key_ptr(pmix_hash_table_t *ht, void **key, size_t *key_size, void **value,
void *in_node, void **out_node)
{
pmix_hash_element_t *elt;
if (PMIX_SUCCESS == pmix_hash_table_get_next_elt(ht, (pmix_hash_element_t *) in_node, &elt)) {
*key = (void *) elt->key.ptr.key;
*key_size = elt->key.ptr.key_size;
*value = elt->value;
*out_node = elt;
return PMIX_SUCCESS;
}
return PMIX_ERROR;
}
int
pmix_hash_table_get_first_key_uint64(pmix_hash_table_t *ht, uint64_t *key, void **value,
void **node)
{
return pmix_hash_table_get_next_key_uint64(ht, key, value, NULL, node);
}
int
pmix_hash_table_get_next_key_uint64(pmix_hash_table_t *ht, uint64_t *key, void **value,
void *in_node, void **out_node)
{
pmix_hash_element_t *elt;
if (PMIX_SUCCESS == pmix_hash_table_get_next_elt(ht, (pmix_hash_element_t *) in_node, &elt)) {
*key = elt->key.u64;
*value = elt->value;
*out_node = elt;
return PMIX_SUCCESS;
}
return PMIX_ERROR;
}
size_t
pmix_hash_table_sizeof_hash_element(void)
{
return sizeof(pmix_hash_element_t);
}