#include "src/include/pmix_config.h"
#include "src/include/pmix_hash_string.h"
#include "src/include/pmix_stdint.h"
#include <string.h>
#include "src/class/pmix_hash_table.h"
#include "src/class/pmix_pointer_array.h"
#include "src/include/pmix_dictionary.h"
#include "src/include/pmix_globals.h"
#include "src/include/pmix_hash_string.h"
#include "src/mca/bfrops/bfrops.h"
#include "src/mca/bfrops/base/bfrop_base_tma.h"
#include "src/mca/gds/base/base.h"
#include "src/util/pmix_error.h"
#include "src/util/pmix_output.h"
#include "src/util/pmix_hash.h"
static inline pmix_keyindex_t *get_keyindex_ptr(pmix_keyindex_t *input)
{
return input ? input : &pmix_globals.keyindex;
}
typedef struct {
pmix_object_t super;
pmix_pointer_array_t *data;
pmix_pointer_array_t *quals;
} pmix_proc_data_t;
static void pdcon(pmix_proc_data_t *p)
{
pmix_tma_t *const tma = pmix_obj_get_tma(&p->super);
p->data = PMIX_NEW(pmix_pointer_array_t, tma);
pmix_pointer_array_init(p->data, 128, INT_MAX, 128);
p->quals = PMIX_NEW(pmix_pointer_array_t, tma);
pmix_pointer_array_init(p->quals, 1, INT_MAX, 1);
}
static void pddes(pmix_proc_data_t *p)
{
int n;
size_t nq;
pmix_dstor_t *d;
pmix_qual_t *q;
pmix_data_array_t *darray;
pmix_tma_t *const tma = pmix_obj_get_tma(&p->super);
for (n=0; n < p->data->size; n++) {
d = (pmix_dstor_t*)pmix_pointer_array_get_item(p->data, n);
if (NULL != d) {
pmix_dstor_release_tma(d, tma);
pmix_pointer_array_set_item(p->data, n, NULL);
}
}
PMIX_RELEASE(p->data);
for (n=0; n < p->quals->size; n++) {
darray = (pmix_data_array_t*)pmix_pointer_array_get_item(p->quals, n);
if (NULL != darray) {
q = (pmix_qual_t*)darray->array;
for (nq=0; nq < darray->size; nq++) {
if (NULL != q[nq].value) {
pmix_bfrops_base_tma_value_release(&q[nq].value, tma);
}
}
pmix_tma_free(tma, darray->array);
pmix_tma_free(tma, darray);
}
pmix_pointer_array_set_item(p->quals, n, NULL);
}
PMIX_RELEASE(p->quals);
}
static PMIX_CLASS_INSTANCE(pmix_proc_data_t, pmix_object_t, pdcon, pddes);
static pmix_dstor_t *lookup_keyval(pmix_proc_data_t *proc, uint32_t kid,
pmix_info_t *qualifiers, size_t nquals,
pmix_keyindex_t *kidx);
static pmix_proc_data_t *lookup_proc(pmix_hash_table_t *jtable, uint32_t id, bool create);
static void erase_qualifiers(pmix_proc_data_t *proc,
uint32_t index);
pmix_status_t pmix_hash_store(pmix_hash_table_t *table,
pmix_rank_t rank, pmix_kval_t *kin,
pmix_info_t *qualifiers, size_t nquals,
pmix_keyindex_t *kidx)
{
pmix_proc_data_t *proc_data;
uint32_t kid;
pmix_dstor_t *hv;
pmix_regattr_input_t *p;
pmix_status_t rc;
pmix_data_array_t *darray;
pmix_qual_t *qarray;
size_t n, m = 0;
pmix_tma_t *const tma = pmix_obj_get_tma(&table->super);
pmix_keyindex_t *const keyindex = get_keyindex_ptr(kidx);
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"%s HASH:STORE:QUAL table %s rank %s key %s",
PMIX_NAME_PRINT(&pmix_globals.myid),
(NULL == table->ht_label) ? "UNKNOWN" : table->ht_label,
PMIX_RANK_PRINT(rank), (NULL == kin) ? "NULL KVAL" : kin->key);
if (PMIX_UNLIKELY(NULL == kin)) {
return PMIX_ERR_BAD_PARAM;
}
p = pmix_hash_lookup_key(UINT32_MAX, kin->key, keyindex);
if (PMIX_UNLIKELY(NULL == p)) {
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"%s UNKNOWN KEY: %s",
PMIX_NAME_PRINT(&pmix_globals.myid),
kin->key);
return PMIX_ERR_BAD_PARAM;
}
kid = p->index;
if (PMIX_UNLIKELY(NULL == (proc_data = lookup_proc(table, rank, true)))) {
return PMIX_ERR_NOMEM;
}
hv = lookup_keyval(proc_data, kid, qualifiers, nquals, keyindex);
if (NULL != hv) {
if (PMIX_UNLIKELY(9 < pmix_output_get_verbosity(pmix_gds_base_framework.framework_output))) {
char *tmp;
tmp = PMIx_Value_string(hv->value);
pmix_output(0, "%s PREEXISTING ENTRY FOR PROC %s KEY %s: %s",
PMIX_NAME_PRINT(&pmix_globals.myid),
PMIX_RANK_PRINT(rank), kin->key, tmp);
free(tmp);
}
if (NULL != hv->value) {
if (PMIX_EQUAL == PMIx_Value_compare(hv->value, kin->value)) {
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"EQUAL VALUE - IGNORING");
return PMIX_SUCCESS;
}
if (PMIX_UNLIKELY(9 < pmix_output_get_verbosity(pmix_gds_base_framework.framework_output))) {
char *tmp;
tmp = PMIx_Value_string(kin->value);
pmix_output(0, "%s KEY %s VALUE UPDATING TO: %s",
PMIX_NAME_PRINT(&pmix_globals.myid), kin->key, tmp);
free(tmp);
}
pmix_bfrops_base_tma_value_release(&hv->value, tma);
}
rc = pmix_bfrops_base_tma_copy_value(&hv->value, kin->value, PMIX_VALUE, tma);
if (PMIX_UNLIKELY(PMIX_SUCCESS != rc)) {
PMIX_ERROR_LOG(rc);
return rc;
}
return PMIX_SUCCESS;
}
hv = pmix_dstor_new_tma(kid, tma);
if (PMIX_UNLIKELY(NULL == hv)) {
return PMIX_ERR_NOMEM;
}
if (NULL != qualifiers) {
for (n=0, m=0; n < nquals; n++) {
if (PMIX_INFO_IS_QUALIFIER(&qualifiers[n])) {
++m;
}
}
if (0 < m) {
darray = (pmix_data_array_t*)pmix_tma_malloc(tma, sizeof(pmix_data_array_t));
darray->array = (pmix_qual_t*)pmix_tma_malloc(tma, m * sizeof(pmix_qual_t));
darray->size = m;
hv->qualindex = pmix_pointer_array_add(proc_data->quals, darray);
qarray = (pmix_qual_t*)darray->array;
for (n=0, m=0; n < nquals; n++) {
if (PMIX_INFO_IS_QUALIFIER(&qualifiers[n])) {
p = pmix_hash_lookup_key(UINT32_MAX, qualifiers[n].key, keyindex);
if (PMIX_UNLIKELY(NULL == p)) {
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"%s UNKNOWN KEY: %s",
PMIX_NAME_PRINT(&pmix_globals.myid),
kin->key);
erase_qualifiers(proc_data, hv->qualindex);
pmix_dstor_release_tma(hv, tma);
return PMIX_ERR_BAD_PARAM;
}
qarray[n].index = p->index;
rc = pmix_bfrops_base_tma_copy_value(&qarray[m].value, &qualifiers[n].value, PMIX_VALUE, tma);
if (PMIX_UNLIKELY(PMIX_SUCCESS != rc)) {
PMIX_ERROR_LOG(rc);
erase_qualifiers(proc_data, hv->qualindex);
pmix_dstor_release_tma(hv, tma);
return rc;
}
++m;
}
}
}
}
rc = pmix_bfrops_base_tma_copy_value(&hv->value, kin->value, PMIX_VALUE, tma);
if (PMIX_UNLIKELY(PMIX_SUCCESS != rc)) {
PMIX_ERROR_LOG(rc);
if (UINT32_MAX != hv->qualindex) {
erase_qualifiers(proc_data, hv->qualindex);
}
pmix_dstor_release_tma(hv, tma);
return rc;
}
if (PMIX_UNLIKELY(9 < pmix_output_get_verbosity(pmix_gds_base_framework.framework_output))) {
char *v = PMIx_Value_string(kin->value);
pmix_output(0, "%s ADDING KEY %s VALUE %s FOR RANK %s WITH %u QUALS TO TABLE %s",
PMIX_NAME_PRINT(&pmix_globals.myid),
kin->key, v,
PMIX_RANK_PRINT(rank), (unsigned)m,
(NULL == table->ht_label) ? "UNKNOWN" : table->ht_label);
free(v);
}
pmix_pointer_array_add(proc_data->data, hv);
return PMIX_SUCCESS;
}
pmix_status_t pmix_hash_fetch(pmix_hash_table_t *table,
pmix_rank_t rank,
const char *key,
pmix_info_t *qualifiers, size_t nquals,
pmix_list_t *kvals,
pmix_keyindex_t *kidx)
{
pmix_status_t rc;
pmix_proc_data_t *proc_data;
pmix_dstor_t *hv;
uint32_t id, kid=UINT32_MAX;
char *node;
pmix_regattr_input_t *p;
pmix_info_t *iptr;
size_t nq, m;
int n;
pmix_kval_t *kv;
bool fullsearch = false;
pmix_data_array_t *darray;
pmix_qual_t *quals;
pmix_keyindex_t *const keyindex = get_keyindex_ptr(kidx);
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"%s HASH:FETCH table %s id %s key %s",
PMIX_NAME_PRINT(&pmix_globals.myid),
(NULL == table->ht_label) ? "UNKNOWN" : table->ht_label,
PMIX_RANK_PRINT(rank), (NULL == key) ? "NULL" : key);
if (PMIX_RANK_UNDEF == rank) {
rc = pmix_hash_table_get_first_key_uint32(table, &id, (void **) &proc_data,
(void **) &node);
if (PMIX_SUCCESS != rc) {
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"HASH:FETCH[%s:%d] proc data for rank %s not found",
__func__, __LINE__, PMIX_RANK_PRINT(rank));
return PMIX_ERR_NOT_FOUND;
}
fullsearch = true;
} else {
id = rank;
}
if (NULL != key) {
p = pmix_hash_lookup_key(UINT32_MAX, key, keyindex);
if (NULL == p) {
return PMIX_ERR_BAD_PARAM;
}
kid = p->index;
}
rc = PMIX_SUCCESS;
while (PMIX_SUCCESS == rc) {
proc_data = lookup_proc(table, id, false);
if (NULL == proc_data) {
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"HASH:FETCH[%s:%d] proc data for rank %s not found - key %s",
__func__, __LINE__,
PMIX_RANK_PRINT(rank), key);
return PMIX_ERR_NOT_FOUND;
}
if (NULL == key) {
for (n=0; n < proc_data->data->size; n++) {
hv = (pmix_dstor_t*)pmix_pointer_array_get_item(proc_data->data, n);
if (NULL != hv) {
p = pmix_hash_lookup_key(hv->index, NULL, keyindex);
if (NULL == p) {
return PMIX_ERR_NOT_FOUND;
}
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"%s FETCH NULL LOOKING AT %s",
PMIX_NAME_PRINT(&pmix_globals.myid), p->name);
if (PMIX_RANK_UNDEF == rank &&
PMIX_CHECK_RESERVED_KEY(p->string)) {
continue;
}
if (UINT32_MAX != hv->qualindex) {
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"%s INCLUDE %s VALUE %s FROM TABLE %s FOR RANK %s",
PMIX_NAME_PRINT(&pmix_globals.myid), p->name,
PMIx_Value_string(hv->value),
(NULL == table->ht_label) ? "UNKNOWN" : table->ht_label,
PMIX_RANK_PRINT(rank));
PMIX_KVAL_NEW(kv, PMIX_QUALIFIED_VALUE);
darray = (pmix_data_array_t*)pmix_pointer_array_get_item(proc_data->quals, hv->qualindex);
quals = (pmix_qual_t*)darray->array;
nq = darray->size;
PMIX_DATA_ARRAY_CREATE(darray, nq+1, PMIX_INFO);
iptr = (pmix_info_t*)darray->array;
PMIX_LOAD_KEY(iptr[0].key, p->string);
PMIx_Value_xfer(&iptr[0].value, hv->value);
for (m=0; m < nq; m++) {
p = pmix_hash_lookup_key(quals[m].index, NULL, keyindex);
if (NULL == p) {
PMIX_RELEASE(kv);
PMIX_DATA_ARRAY_FREE(darray);
return PMIX_ERR_BAD_PARAM;
}
PMIX_LOAD_KEY(iptr[m+1].key, p->string);
PMIx_Value_xfer(&iptr[m+1].value, quals[m].value);
PMIX_INFO_SET_QUALIFIER(&iptr[m+1]);
}
kv->value->type = PMIX_DATA_ARRAY;
kv->value->data.darray = darray;
pmix_list_append(kvals, &kv->super);
} else {
PMIX_KVAL_NEW(kv, p->string);
PMIx_Value_xfer(kv->value, hv->value);
pmix_list_append(kvals, &kv->super);
}
}
}
return PMIX_SUCCESS;
} else {
hv = lookup_keyval(proc_data, kid, qualifiers, nquals, keyindex);
if (NULL != hv) {
PMIX_KVAL_NEW(kv, key);
PMIx_Value_xfer(kv->value, hv->value);
pmix_list_append(kvals, &kv->super);
break;
} else if (!fullsearch) {
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"HASH:FETCH data for key %s not found", key);
return PMIX_ERR_NOT_FOUND;
}
}
rc = pmix_hash_table_get_next_key_uint32(table, &id, (void **) &proc_data, node,
(void **) &node);
if (PMIX_SUCCESS != rc) {
pmix_output_verbose(10, pmix_gds_base_framework.framework_output,
"%s:%d HASH:FETCH data for key %s not found",
__func__, __LINE__, key);
return PMIX_ERR_NOT_FOUND;
}
}
return rc;
}
pmix_status_t pmix_hash_remove_data(pmix_hash_table_t *table,
pmix_rank_t rank, const char *key,
pmix_keyindex_t *kidx)
{
pmix_status_t rc = PMIX_SUCCESS;
pmix_proc_data_t *proc_data;
pmix_dstor_t *d;
uint32_t id, kid=UINT32_MAX;
int n;
char *node;
pmix_regattr_input_t *p;
pmix_tma_t *const tma = pmix_obj_get_tma(&table->super);
pmix_keyindex_t *const keyindex = get_keyindex_ptr(kidx);
if (NULL != key) {
p = pmix_hash_lookup_key(UINT32_MAX, key, keyindex);
if (PMIX_UNLIKELY(NULL == p)) {
return PMIX_ERR_BAD_PARAM;
}
kid = p->index;
}
if (PMIX_RANK_WILDCARD == rank) {
rc = pmix_hash_table_get_first_key_uint32(table, &id, (void **) &proc_data,
(void **) &node);
while (PMIX_SUCCESS == rc) {
if (NULL != proc_data) {
if (NULL == key) {
PMIX_RELEASE(proc_data);
} else {
for (n=0; n < proc_data->data->size; n++) {
d = (pmix_dstor_t*)pmix_pointer_array_get_item(proc_data->data, n);
if (NULL != d && kid == d->index) {
if (NULL != d->value) {
pmix_bfrops_base_tma_value_release(&d->value, tma);
}
if (UINT32_MAX != d->qualindex) {
erase_qualifiers(proc_data, d->qualindex);
}
pmix_tma_free(tma, d);
pmix_pointer_array_set_item(proc_data->data, n, NULL);
break;
}
}
}
}
rc = pmix_hash_table_get_next_key_uint32(table, &id, (void **) &proc_data, node,
(void **) &node);
}
return PMIX_SUCCESS;
}
if (NULL == (proc_data = lookup_proc(table, rank, false))) {
return PMIX_SUCCESS;
}
if (NULL == key) {
for (n=0; n < proc_data->data->size; n++) {
d = (pmix_dstor_t*)pmix_pointer_array_get_item(proc_data->data, n);
if (NULL != d) {
if (NULL != d->value) {
pmix_bfrops_base_tma_value_release(&d->value, tma);
}
if (UINT32_MAX != d->qualindex) {
erase_qualifiers(proc_data, d->qualindex);
}
pmix_tma_free(tma, d);
pmix_pointer_array_set_item(proc_data->data, n, NULL);
}
}
pmix_hash_table_remove_value_uint32(table, rank);
PMIX_RELEASE(proc_data);
return PMIX_SUCCESS;
}
for (n=0; n < proc_data->data->size; n++) {
d = (pmix_dstor_t*)pmix_pointer_array_get_item(proc_data->data, n);
if (NULL != d && kid == d->index) {
if (NULL != d->value) {
pmix_bfrops_base_tma_value_release(&d->value, tma);
}
if (UINT32_MAX != d->qualindex) {
erase_qualifiers(proc_data, d->qualindex);
}
pmix_tma_free(tma, d);
pmix_pointer_array_set_item(proc_data->data, n, NULL);
break;
}
}
return PMIX_SUCCESS;
}
static pmix_dstor_t *lookup_keyval(pmix_proc_data_t *proc_data, uint32_t kid,
pmix_info_t *qualifiers, size_t nquals,
pmix_keyindex_t *kidx)
{
pmix_dstor_t *d;
pmix_data_array_t *darray;
pmix_qual_t *qarray;
pmix_regattr_input_t *p;
size_t m, numquals = 0, nq, nfound;
int n;
pmix_keyindex_t *const keyindex = get_keyindex_ptr(kidx);
p = pmix_hash_lookup_key(kid, NULL, keyindex);
if (NULL != qualifiers) {
for (m=0; m < nquals; m++) {
if (PMIX_INFO_IS_QUALIFIER(&qualifiers[m])) {
++numquals;
}
}
}
for (n=0; n < proc_data->data->size; n++) {
d = (pmix_dstor_t*)pmix_pointer_array_get_item(proc_data->data, n);
if (NULL == d) {
continue;
}
if (kid == d->index) {
if (0 < numquals) {
if (UINT32_MAX == d->qualindex) {
continue;
}
darray = (pmix_data_array_t*)pmix_pointer_array_get_item(proc_data->quals, d->qualindex);
qarray = (pmix_qual_t*)darray->array;
nfound = 0;
for (m=0; m < nquals; m++) {
if (!PMIX_INFO_IS_QUALIFIER(&qualifiers[m])) {
continue;
}
p = pmix_hash_lookup_key(UINT32_MAX, qualifiers[m].key, keyindex);
if (NULL == p) {
return NULL;
}
for (nq=0; nq < darray->size; nq++) {
if (qarray[nq].index == p->index) {
if (PMIX_EQUAL == PMIx_Value_compare(&qualifiers[m].value, qarray[nq].value)) {
++nfound;
break;
}
}
}
}
if (nfound == numquals) {
return d;
}
} else {
if (UINT32_MAX == d->qualindex) {
return d;
}
}
}
}
return NULL;
}
static pmix_proc_data_t *lookup_proc(pmix_hash_table_t *jtable, uint32_t id, bool create)
{
pmix_proc_data_t *proc_data = NULL;
pmix_tma_t *const tma = pmix_obj_get_tma(&jtable->super);
pmix_hash_table_get_value_uint32(jtable, id, (void **) &proc_data);
if (NULL == proc_data && create) {
proc_data = PMIX_NEW(pmix_proc_data_t, tma);
if (PMIX_UNLIKELY(NULL == proc_data)) {
return NULL;
}
pmix_hash_table_set_value_uint32(jtable, id, proc_data);
}
return proc_data;
}
void pmix_hash_register_key(uint32_t inid,
pmix_regattr_input_t *ptr,
pmix_keyindex_t *kidx)
{
pmix_regattr_input_t *p = NULL;
pmix_keyindex_t *const keyindex = get_keyindex_ptr(kidx);
if (UINT32_MAX == inid) {
pmix_pointer_array_set_item(keyindex->table, (int)keyindex->next_id, ptr);
ptr->index = keyindex->next_id;
keyindex->next_id += 1;
return;
}
p = pmix_pointer_array_get_item(keyindex->table, inid);
if (NULL != p) {
return;
}
pmix_pointer_array_set_item(keyindex->table, inid, ptr);
}
pmix_regattr_input_t* pmix_hash_lookup_key(uint32_t inid,
const char *key,
pmix_keyindex_t *kidx)
{
int id;
pmix_regattr_input_t *ptr = NULL;
pmix_keyindex_t *const keyindex = get_keyindex_ptr(kidx);
if (UINT32_MAX == inid) {
if (NULL == key) {
return NULL;
}
for (id = 0; id < keyindex->table->size; id++) {
ptr = pmix_pointer_array_get_item(keyindex->table, id);
if (NULL != ptr) {
if (0 == strcmp(key, ptr->string)) {
return ptr;
}
}
}
ptr = (pmix_regattr_input_t*)pmix_malloc(sizeof(pmix_regattr_input_t));
ptr->name = strdup(key);
ptr->string = strdup(key);
ptr->type = PMIX_UNDEF; ptr->description = (char**)pmix_malloc(2 * sizeof(char*));
ptr->description[0] = strdup("USER DEFINED");
ptr->description[1] = NULL;
pmix_hash_register_key(UINT32_MAX, ptr, keyindex);
return ptr;
}
ptr = pmix_pointer_array_get_item(keyindex->table, inid);
return ptr;
}
static void erase_qualifiers(pmix_proc_data_t *proc,
uint32_t index)
{
pmix_data_array_t *darray;
pmix_qual_t *qarray;
size_t n;
pmix_tma_t *const tma = pmix_obj_get_tma(&proc->super);
darray = (pmix_data_array_t*)pmix_pointer_array_get_item(proc->quals, index);
if (NULL == darray || NULL == darray->array) {
return;
}
qarray = (pmix_qual_t*)darray->array;
for (n=0; n < darray->size; n++) {
if (NULL != qarray[n].value) {
pmix_bfrops_base_tma_value_release(&qarray[n].value, tma);
}
}
pmix_tma_free(tma, qarray);
pmix_tma_free(tma, darray);
pmix_pointer_array_set_item(proc->quals, index, NULL);
}