blob: 992a7fa26ad86ff43d9be962bcb378ace15fb099 [file] [log] [blame] [edit]
/*****************************************************************************\
* data.c - generic data_t structures
*****************************************************************************
* Copyright (C) 2019 SchedMD LLC.
* Written by Nathan Rini <nate@schedmd.com>
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm 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.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include "config.h"
#include <math.h>
#include <regex.h>
#include "slurm/slurm.h"
#include "src/common/list.h"
#include "src/common/log.h"
#include "src/common/read_config.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/common/data.h"
/*
* Regex matches based on YAML 1.1 section 5.5.
* Honors ~ as YAML 1.1 allows for null fields.
*/
static const char *bool_pattern_null = "^(\\~|[Nn][uU][lL][lL])$";
static regex_t bool_pattern_null_re;
static const char *bool_pattern_true = "^([Yy](|[eE][sS])|[tT]([rR][uU][eE]|)|[Oo][nN])$";
static regex_t bool_pattern_true_re;
static const char *bool_pattern_false = "^([nN]([Oo]|)|[fF](|[aA][lL][sS][eE])|[oO][fF][fF])$";
static regex_t bool_pattern_false_re;
static const char *bool_pattern_int = "^([+-]?[0-9]+)$";
static regex_t bool_pattern_int_re;
static const char *bool_pattern_float = "^([+-]?[0-9]*[.][0-9]*(|[eE][+-]?[0-9]+))$";
static regex_t bool_pattern_float_re;
static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
static bool initialized = false; /* protected by init_mutex */
#define DATA_MAGIC 0x1992189F
#define DATA_LIST_MAGIC 0x1992F89F
#define DATA_LIST_NODE_MAGIC 0x1921F89F
typedef struct data_list_node_s data_list_node_t;
struct data_list_node_s {
int magic;
data_list_node_t *next;
data_t *data;
char *key; /* key for dictionary (only) */
};
/* single forward linked list */
struct data_list_s {
int magic;
size_t count;
data_list_node_t *begin;
data_list_node_t *end;
};
static void _check_magic(const data_t *data);
static void _release(data_t *data);
static void _release_data_list_node(data_list_t *dl, data_list_node_t *dn);
static int _dump_regex_error(int rc, const regex_t *preg)
{
char *buffer = NULL;
size_t len = regerror(rc, preg, NULL, 0);
if (len == 0) {
error("%s: unknown regex error code: %d", __func__, rc);
return SLURM_ERROR;
}
buffer = xmalloc(len);
len = regerror(rc, preg, buffer, len);
if (len)
error("%s: regex error: %s", __func__, buffer);
else
error("%s: unexpected failure to get regex error", __func__);
xfree(buffer);
return SLURM_ERROR;
}
static bool _regex_quick_match(const char *str, const regex_t *preg)
{
int rc;
// TODO: nmatch may safely be able to be 0 for this function
size_t nmatch = 1;
regmatch_t pmatch[nmatch];
/* not possible to match a NULL string */
if (!str)
return false;
rc = regexec(preg, str, nmatch, pmatch, 0);
if (!rc) { /* matched */
return true;
} else if (rc == REG_NOMATCH) {
return false;
} else { /* other error */
_dump_regex_error(rc, preg);
return false;
}
}
extern void data_destroy_static(void)
{
slurm_mutex_lock(&init_mutex);
if (initialized) {
regfree(&bool_pattern_null_re);
regfree(&bool_pattern_true_re);
regfree(&bool_pattern_false_re);
regfree(&bool_pattern_int_re);
regfree(&bool_pattern_float_re);
}
slurm_mutex_unlock(&init_mutex);
}
extern int data_init_static(void)
{
int rc = SLURM_SUCCESS;
int reg_rc; /* regex rc */
slurm_mutex_lock(&init_mutex);
if (initialized)
goto cleanup;
initialized = true;
if (!rc && (reg_rc = regcomp(&bool_pattern_null_re, bool_pattern_null,
REG_EXTENDED)) != 0) {
rc = _dump_regex_error(reg_rc, &bool_pattern_null_re);
}
if (!rc && (reg_rc = regcomp(&bool_pattern_true_re, bool_pattern_true,
REG_EXTENDED)) != 0) {
rc = _dump_regex_error(reg_rc, &bool_pattern_true_re);
}
if (!rc && (reg_rc = regcomp(&bool_pattern_false_re, bool_pattern_false,
REG_EXTENDED)) != 0) {
rc = _dump_regex_error(reg_rc, &bool_pattern_false_re);
}
if (!rc && (reg_rc = regcomp(&bool_pattern_int_re, bool_pattern_int,
REG_EXTENDED)) != 0) {
rc = _dump_regex_error(reg_rc, &bool_pattern_int_re);
}
if (!rc && (reg_rc = regcomp(&bool_pattern_float_re, bool_pattern_float,
REG_EXTENDED)) != 0) {
rc = _dump_regex_error(reg_rc, &bool_pattern_float_re);
}
cleanup:
slurm_mutex_unlock(&init_mutex);
return rc;
}
static data_list_t *_data_list_new(void)
{
data_list_t *dl = xmalloc(sizeof(*dl));
xassert((dl->magic = DATA_LIST_MAGIC));
log_flag(DATA, "%s: new data list (0x%"PRIXPTR")",
__func__, (uintptr_t) dl);
return dl;
}
static void _check_data_list_node_magic(const data_list_node_t *dn)
{
xassert(dn);
xassert(dn->magic == DATA_LIST_NODE_MAGIC);
/* make sure not linking to self */
xassert(dn->next != dn);
/* key can be NULL for list, but not NULL length string */
xassert(!dn->key || dn->key[0]);
}
static void _check_data_list_magic(const data_list_t *dl)
{
#ifndef NDEBUG
data_list_node_t *end = NULL;
xassert(dl);
xassert(dl->magic == DATA_LIST_MAGIC);
if (dl->begin) {
/* walk forwards verify */
int c = 0;
data_list_node_t *i = dl->begin;
while (i) {
c++;
_check_data_list_node_magic(i);
end = i;
i = i->next;
}
xassert(c == dl->count);
}
xassert(end == dl->end);
#endif /* !NDEBUG */
}
/* verify node is in parent list */
static void _check_data_list_node_parent(const data_list_t *dl,
const data_list_node_t *dn)
{
#ifndef NDEBUG
data_list_node_t *i = dl->begin;
while (i) {
if (i == dn)
return;
i = i->next;
}
/* found an orphan? */
fatal_abort("%s: unexpected orphan node", __func__);
#endif /* !NDEBUG */
}
static void _release_data_list_node(data_list_t *dl, data_list_node_t *dn)
{
_check_data_list_magic(dl);
_check_data_list_node_magic(dn);
_check_data_list_node_parent(dl, dn);
data_list_node_t *prev;
/* walk list to find new previous */
for (prev = dl->begin; prev && prev->next != dn; ) {
_check_data_list_node_magic(prev);
prev = prev->next;
if (prev)
_check_data_list_node_magic(prev);
}
if (dn == dl->begin) {
/* at the beginning */
dl->begin = dn->next;
if (dl->end == dn) {
dl->end = NULL;
xassert(!dn->next);
}
} else if (dn == dl->end) {
/* at the end */
xassert(!dn->next);
dl->end = prev;
prev->next = NULL;
} else {
/* somewhere in middle */
xassert(prev != dn);
xassert(prev->next == dn);
xassert(dl->begin != dn);
xassert(dl->end != dn);
prev->next = dn->next;
}
dl->count--;
FREE_NULL_DATA(dn->data);
xfree(dn->key);
xassert((dn->magic = ~DATA_LIST_NODE_MAGIC));
xfree(dn);
}
static void _release_data_list(data_list_t *dl)
{
data_list_node_t *n = dl->begin, *i;
#ifndef NDEBUG
int count = 0;
const int init_count = dl->count;
#endif
_check_data_list_magic(dl);
if (!n) {
xassert(!dl->end);
return;
}
xassert(dl->end);
while((i = n)) {
n = i->next;
_release_data_list_node(dl, i);
#ifndef NDEBUG
count++;
#endif
}
#ifndef NDEBUG
xassert(count == init_count);
xassert(!(dl->count = 0));
xassert((dl->magic = ~DATA_LIST_MAGIC));
#endif
xfree(dl);
}
/*
* Create new data list node entry
* IN d - data type to take ownership of
* IN key - dictionary key to dup or NULL
*/
static data_list_node_t *_new_data_list_node(data_t *d, const char *key)
{
data_list_node_t *dn = xmalloc(sizeof(*dn));
xassert((dn->magic = DATA_LIST_NODE_MAGIC));
_check_magic(d);
dn->data = d;
if (key)
dn->key = xstrdup(key);
log_flag(DATA, "%s: new data list node (0x%"PRIXPTR")",
__func__, (uintptr_t) dn);
return dn;
}
static void _data_list_append(data_list_t *dl, data_t *d, const char *key)
{
data_list_node_t *n = _new_data_list_node(d, key);
_check_data_list_magic(dl);
_check_magic(d);
if (dl->end) {
xassert(!dl->end->next);
_check_data_list_node_magic(dl->end);
_check_data_list_node_magic(dl->begin);
dl->end->next = n;
dl->end = n;
} else {
xassert(!dl->count);
dl->end = n;
dl->begin = n;
}
dl->count++;
}
static void _data_list_prepend(data_list_t *dl, data_t *d, const char *key)
{
data_list_node_t *n = _new_data_list_node(d, key);
_check_data_list_magic(dl);
_check_magic(d);
if (dl->begin) {
_check_data_list_node_magic(dl->begin);
n->next = dl->begin;
dl->begin = n;
} else {
xassert(!dl->count);
dl->begin = n;
dl->end = n;
}
dl->count++;
}
data_t *data_new(void)
{
data_t *data = xmalloc(sizeof(*data));
xassert((data->magic = DATA_MAGIC));
data->type = DATA_TYPE_NULL;
log_flag(DATA, "%s: new data (0x%"PRIXPTR")",
__func__, (uintptr_t) data);
return data;
}
static void _check_magic(const data_t *data)
{
xassert(data);
xassert(data->type > DATA_TYPE_NONE);
xassert(data->type < DATA_TYPE_MAX);
xassert(data->magic == DATA_MAGIC);
if (data->type == DATA_TYPE_NULL)
/* make sure NULL type has a NULL value */
xassert(data->data.list_u == NULL);
if (data->type == DATA_TYPE_LIST)
_check_data_list_magic(data->data.list_u);
if (data->type == DATA_TYPE_DICT)
_check_data_list_magic(data->data.dict_u);
}
static void _release(data_t *data)
{
_check_magic(data);
switch (data->type) {
case DATA_TYPE_LIST:
_release_data_list(data->data.list_u);
xassert(!(data->data.list_u = 0));
break;
case DATA_TYPE_DICT:
_release_data_list(data->data.dict_u);
xassert(!(data->data.dict_u = 0));
break;
case DATA_TYPE_STRING:
xfree(data->data.string_u);
break;
default:
/* other types don't need to be freed */
xassert(!(data->data.int_u = 0));
break;
}
data->type = DATA_TYPE_NONE;
}
extern void data_free(data_t *data)
{
if (!data)
return;
log_flag(DATA, "%s: free data (0x%"PRIXPTR")",
__func__, (uintptr_t) data);
_check_magic(data);
_release(data);
xassert((data->magic = 0) == 0);
xfree(data);
}
extern data_type_t data_get_type(const data_t *data)
{
_check_magic(data);
if (data)
return data->type;
else
return DATA_TYPE_NONE;
}
extern data_t *data_set_float(data_t *data, double value)
{
_check_magic(data);
if (!data)
return NULL;
log_flag(DATA, "%s: set data (0x%"PRIXPTR") to float: %lf",
__func__, (uintptr_t) data, value);
data->type = DATA_TYPE_FLOAT;
data->data.float_u = value;
return data;
}
extern data_t *data_set_null(data_t *data)
{
_check_magic(data);
if (!data)
return NULL;
_release(data);
log_flag(DATA, "%s: set data (0x%"PRIXPTR") to null",
__func__, (uintptr_t) data);
data->type = DATA_TYPE_NULL;
xassert((memset(&data->data, 0, sizeof(data->data))));
return data;
}
extern data_t *data_set_bool(data_t *data, bool value)
{
_check_magic(data);
if (!data)
return NULL;
_release(data);
log_flag(DATA, "%s: set data (0x%"PRIXPTR") to bool: %d",
__func__, (uintptr_t) data, value);
data->type = DATA_TYPE_BOOL;
data->data.bool_u = value;
return data;
}
extern data_t *data_set_int(data_t *data, int64_t value)
{
_check_magic(data);
if (!data)
return NULL;
_release(data);
log_flag(DATA, "%s: set data (0x%"PRIXPTR") to int64_t: %"PRId64,
__func__, (uintptr_t) data, value);
data->type = DATA_TYPE_INT_64;
data->data.int_u = value;
return data;
}
extern data_t *data_set_string(data_t *data, const char *value)
{
_check_magic(data);
if (!data)
return NULL;
_release(data);
log_flag(DATA, "%s: set data (0x%"PRIXPTR") to string: %s",
__func__, (uintptr_t) data, value);
data->type = DATA_TYPE_STRING;
data->data.string_u = xstrdup(value);
return data;
}
extern data_t *data_set_dict(data_t *data)
{
_check_magic(data);
if (!data)
return NULL;
_release(data);
log_flag(DATA, "%s: set data (0x%"PRIXPTR") to dictionary",
__func__, (uintptr_t) data);
data->type = DATA_TYPE_DICT;
data->data.dict_u = _data_list_new();
return data;
}
extern data_t *data_set_list(data_t *data)
{
_check_magic(data);
if (!data)
return NULL;
_release(data);
log_flag(DATA, "%s: set data (0x%"PRIXPTR") to list",
__func__, (uintptr_t) data);
data->type = DATA_TYPE_LIST;
data->data.dict_u = _data_list_new();
return data;
}
extern data_t *data_list_append(data_t *data)
{
data_t *ndata = NULL;
_check_magic(data);
xassert(data->type == DATA_TYPE_LIST);
if (!data || data->type != DATA_TYPE_LIST)
return NULL;
ndata = data_new();
_data_list_append(data->data.list_u, ndata, NULL);
log_flag(DATA, "%s: list append data (0x%"PRIXPTR") to (0x%"PRIXPTR")",
__func__, (uintptr_t) ndata, (uintptr_t) data);
return ndata;
}
extern data_t *data_list_prepend(data_t *data)
{
data_t *ndata = NULL;
_check_magic(data);
xassert(data->type == DATA_TYPE_LIST);
if (!data || data->type != DATA_TYPE_LIST)
return NULL;
ndata = data_new();
_data_list_prepend(data->data.list_u, ndata, NULL);
log_flag(DATA, "%s: list prepend data (0x%"PRIXPTR") to (0x%"PRIXPTR")",
__func__, (uintptr_t) ndata, (uintptr_t) data);
return ndata;
}
const data_t *data_key_get_const(const data_t *data, const char *key)
{
const data_list_node_t *i;
_check_magic(data);
xassert(data->type == DATA_TYPE_DICT);
if (!key || data->type != DATA_TYPE_DICT)
return NULL;
/* don't bother searching empty dictionary */
if (!data->data.dict_u->count)
return NULL;
_check_data_list_magic(data->data.dict_u);
i = data->data.dict_u->begin;
while (i) {
_check_data_list_node_magic(i);
if (!xstrcmp(key, i->key))
break;
i = i->next;
}
if (i)
return i->data;
else
return NULL;
}
data_t *data_key_get(data_t *data, const char *key)
{
data_list_node_t *i;
_check_magic(data);
xassert(data->type == DATA_TYPE_DICT);
if (!key || data->type != DATA_TYPE_DICT)
return NULL;
/* don't bother searching empty dictionary */
if (!data->data.dict_u->count)
return NULL;
_check_data_list_magic(data->data.dict_u);
i = data->data.dict_u->begin;
while (i) {
_check_data_list_node_magic(i);
if (!xstrcmp(key, i->key))
break;
i = i->next;
}
if (i)
return i->data;
else
return NULL;
}
data_t *data_key_set(data_t *data, const char *key)
{
data_t *d;
_check_magic(data);
xassert(data->type == DATA_TYPE_DICT);
xassert(key && key[0]);
if (!key || !key[0] || data->type != DATA_TYPE_DICT)
return NULL;
if ((d = data_key_get(data, key))) {
log_flag(DATA, "%s: set existing key in data (0x%"PRIXPTR") key: %s data (0x%"PRIXPTR")",
__func__, (uintptr_t) data, key, (uintptr_t) d);
return d;
}
d = data_new();
_data_list_append(data->data.dict_u, d, key);
log_flag(DATA, "%s: set new key in data (0x%"PRIXPTR") key: %s data (0x%"PRIXPTR")",
__func__, (uintptr_t) data, key, (uintptr_t) d);
return d;
}
data_t *data_key_set_int(data_t *data, int64_t key)
{
char *key_str = xstrdup_printf("%"PRId64, key);
data_t *node = data_key_set(data, key_str);
xfree(key_str);
return node;
}
bool data_key_unset(data_t *data, const char *key)
{
data_list_node_t *i;
_check_magic(data);
xassert(data->type == DATA_TYPE_DICT);
if (!key || data->type != DATA_TYPE_DICT)
return NULL;
_check_data_list_magic(data->data.dict_u);
i = data->data.dict_u->begin;
while (i) {
_check_data_list_node_magic(i);
if (!xstrcmp(key, i->key))
break;
i = i->next;
}
if (!i) {
log_flag(DATA, "%s: remove non-existent key in data (0x%"PRIXPTR") key: %s",
__func__, (uintptr_t) data, key);
return false;
}
_release_data_list_node(data->data.dict_u, i);
log_flag(DATA, "%s: remove existing key in data (0x%"PRIXPTR") key: %s",
__func__, (uintptr_t) data, key);
return true;
}
double data_get_float(const data_t *data)
{
_check_magic(data);
if (!data)
return NAN;
xassert(data->type == DATA_TYPE_FLOAT);
return data->data.float_u;
}
bool data_get_bool(const data_t *data)
{
_check_magic(data);
if (!data)
return false;
xassert(data->type == DATA_TYPE_BOOL);
return data->data.bool_u;
}
int64_t data_get_int(const data_t *data)
{
_check_magic(data);
if (!data)
return 0;
xassert(data->type == DATA_TYPE_INT_64);
return data->data.int_u;
}
const char *data_get_string(const data_t *data)
{
_check_magic(data);
if (!data)
return NULL;
xassert(data->type == DATA_TYPE_STRING);
return data->data.string_u;
}
int data_get_string_converted(const data_t *d, char **buffer)
{
_check_magic(d);
char *_buffer = NULL;
if (!d || !buffer)
return SLURM_ERROR;
if (data_get_type(d) != DATA_TYPE_STRING) {
/* copy the data and then convert it to a string type */
data_t *dclone = data_new();
data_copy(dclone, d);
if (data_convert_type(dclone, DATA_TYPE_STRING) ==
DATA_TYPE_STRING)
_buffer = xstrdup(data_get_string(dclone));
FREE_NULL_DATA(dclone);
} else
_buffer = xstrdup(data_get_string(d));
if (_buffer) {
*buffer = _buffer;
return SLURM_SUCCESS;
}
return SLURM_ERROR;
}
extern int data_copy_bool_converted(const data_t *d, bool *buffer)
{
_check_magic(d);
int rc = SLURM_ERROR;
if (!d || !buffer)
return rc;
if (data_get_type(d) != DATA_TYPE_BOOL) {
data_t *dclone = data_new();
data_copy(dclone, d);
if (data_convert_type(dclone, DATA_TYPE_BOOL) ==
DATA_TYPE_BOOL) {
*buffer = data_get_bool(dclone);
rc = SLURM_SUCCESS;
}
FREE_NULL_DATA(dclone);
return rc;
}
*buffer = data_get_bool(d);
return SLURM_SUCCESS;
}
extern int data_get_bool_converted(data_t *d, bool *buffer)
{
int rc;
_check_magic(d);
if (!d || !buffer)
return SLURM_ERROR;
/* assign value if converted successfully */
rc = data_copy_bool_converted(d, buffer);
if (!rc)
data_set_bool(d, *buffer);
return rc;
}
extern int data_get_int_converted(const data_t *d, int64_t *buffer)
{
_check_magic(d);
int rc = SLURM_SUCCESS;
if (!d || !buffer)
return SLURM_ERROR;
if (data_get_type(d) != DATA_TYPE_INT_64) {
data_t *dclone = data_new();
data_copy(dclone, d);
if (data_convert_type(dclone, DATA_TYPE_INT_64) ==
DATA_TYPE_INT_64)
*buffer = data_get_int(dclone);
else
rc = SLURM_ERROR;
FREE_NULL_DATA(dclone);
} else {
*buffer = data_get_int(d);
}
return rc;
}
size_t data_get_dict_length(const data_t *data)
{
_check_magic(data);
if (!data)
return 0;
xassert(data->type == DATA_TYPE_DICT);
return data->data.dict_u->count;
}
size_t data_get_list_length(const data_t *data)
{
_check_magic(data);
if (!data)
return 0;
xassert(data->type == DATA_TYPE_LIST);
return data->data.list_u->count;
}
extern int data_list_for_each_const(const data_t *d, DataListForFConst f, void *arg)
{
int count = 0;
const data_list_node_t *i;
_check_magic(d);
if (!d || data_get_type(d) != DATA_TYPE_LIST) {
error("%s: for each attempted on non-list object (0x%"PRIXPTR")",
__func__, (uintptr_t) d);
return -1;
}
i = d->data.list_u->begin;
_check_data_list_magic(d->data.list_u);
while (i) {
_check_data_list_node_magic(i);
xassert(!i->key);
data_for_each_cmd_t cmd = f(i->data, arg);
count++;
xassert(cmd > DATA_FOR_EACH_INVALID);
xassert(cmd < DATA_FOR_EACH_MAX);
switch (cmd) {
case DATA_FOR_EACH_CONT:
break;
case DATA_FOR_EACH_DELETE:
fatal_abort("%s: delete attempted against const",
__func__);
break;
case DATA_FOR_EACH_FAIL:
count *= -1;
/* fall through */
case DATA_FOR_EACH_STOP:
i = NULL;
break;
default:
fatal_abort("%s: invalid cmd", __func__);
}
if (i)
i = i->next;
}
return count;
}
extern int data_list_for_each(data_t *d, DataListForF f, void *arg)
{
int count = 0;
data_list_node_t *i;
_check_magic(d);
if (!d || data_get_type(d) != DATA_TYPE_LIST) {
error("%s: for each attempted on non-list object (0x%"PRIXPTR")",
__func__, (uintptr_t) d);
return -1;
}
i = d->data.list_u->begin;
_check_data_list_magic(d->data.list_u);
while (i) {
_check_data_list_node_magic(i);
xassert(!i->key);
data_for_each_cmd_t cmd = f(i->data, arg);
count++;
xassert(cmd > DATA_FOR_EACH_INVALID);
xassert(cmd < DATA_FOR_EACH_MAX);
switch (cmd) {
case DATA_FOR_EACH_CONT:
break;
case DATA_FOR_EACH_DELETE:
_release_data_list_node(d->data.list_u, i);
break;
case DATA_FOR_EACH_FAIL:
count *= -1;
/* fall through */
case DATA_FOR_EACH_STOP:
i = NULL;
break;
default:
fatal_abort("%s: invalid cmd", __func__);
}
if (i)
i = i->next;
}
return count;
}
extern int data_dict_for_each_const(const data_t *d, DataDictForFConst f, void *arg)
{
int count = 0;
const data_list_node_t *i;
_check_magic(d);
if (!d || data_get_type(d) != DATA_TYPE_DICT) {
error("%s: for each attempted on non-dict object (0x%"PRIXPTR")",
__func__, (uintptr_t) d);
return -1;
}
i = d->data.dict_u->begin;
_check_data_list_magic(d->data.dict_u);
while (i) {
data_for_each_cmd_t cmd;
_check_data_list_node_magic(i);
cmd = f(i->key, i->data, arg);
count++;
xassert(cmd > DATA_FOR_EACH_INVALID);
xassert(cmd < DATA_FOR_EACH_MAX);
switch (cmd) {
case DATA_FOR_EACH_CONT:
break;
case DATA_FOR_EACH_DELETE:
fatal_abort("%s: delete attempted against const",
__func__);
break;
case DATA_FOR_EACH_FAIL:
count *= -1;
/* fall through */
case DATA_FOR_EACH_STOP:
i = NULL;
break;
default:
fatal_abort("%s: invalid cmd", __func__);
}
if (i)
i = i->next;
}
return count;
}
extern int data_dict_for_each(data_t *d, DataDictForF f, void *arg)
{
int count = 0;
data_list_node_t *i;
_check_magic(d);
if (!d || data_get_type(d) != DATA_TYPE_DICT) {
error("%s: for each attempted on non-dict object (0x%"PRIXPTR")",
__func__, (uintptr_t) d);
return -1;
}
i = d->data.dict_u->begin;
_check_data_list_magic(d->data.dict_u);
while (i) {
_check_data_list_node_magic(i);
data_for_each_cmd_t cmd = f(i->key, i->data, arg);
count++;
xassert(cmd > DATA_FOR_EACH_INVALID);
xassert(cmd < DATA_FOR_EACH_MAX);
switch (cmd) {
case DATA_FOR_EACH_CONT:
break;
case DATA_FOR_EACH_DELETE:
_release_data_list_node(d->data.dict_u, i);
break;
case DATA_FOR_EACH_FAIL:
count *= -1;
/* fall through */
case DATA_FOR_EACH_STOP:
i = NULL;
break;
default:
fatal_abort("%s: invalid cmd", __func__);
}
if (i)
i = i->next;
}
return count;
}
static int _convert_data_string(data_t *data)
{
_check_magic(data);
switch (data->type) {
case DATA_TYPE_STRING:
return SLURM_SUCCESS;
case DATA_TYPE_BOOL:
data_set_string(data, (data->data.bool_u ? "true" : "false"));
return SLURM_SUCCESS;
case DATA_TYPE_NULL:
data_set_string(data, "null");
return SLURM_SUCCESS;
case DATA_TYPE_FLOAT:
{
char *str = xstrdup_printf("%lf", data->data.float_u);
data_set_string(data, str);
xfree(str);
return SLURM_SUCCESS;
}
case DATA_TYPE_INT_64:
{
char *str = xstrdup_printf("%"PRId64, data->data.int_u);
data_set_string(data, str);
xfree(str);
return SLURM_SUCCESS;
}
default:
return SLURM_ERROR;
}
return SLURM_ERROR;
}
static int _convert_data_force_bool(data_t *data)
{
_check_magic(data);
switch (data->type) {
case DATA_TYPE_STRING:
if (data->data.string_u == NULL ||
data->data.string_u[0] == '\0')
data_set_bool(data, false);
else if (_regex_quick_match(data->data.string_u,
&bool_pattern_true_re))
data_set_bool(data, true);
else { /* try to auto detect the type and try again */
if (data_convert_type(data, DATA_TYPE_NONE)
!= DATA_TYPE_NONE)
return _convert_data_force_bool(data);
else {
/*
* not NULL or empty and unknown type,
* so it must be true
*/
data_set_bool(data, true);
}
}
return SLURM_SUCCESS;
case DATA_TYPE_BOOL:
return SLURM_SUCCESS;
case DATA_TYPE_NULL:
data_set_bool(data, false);
return SLURM_SUCCESS;
case DATA_TYPE_FLOAT:
data_set_bool(data, data->data.float_u != 0);
return SLURM_SUCCESS;
case DATA_TYPE_INT_64:
data_set_bool(data, data->data.int_u != 0);
return SLURM_SUCCESS;
default:
return SLURM_ERROR;
}
return SLURM_ERROR;
}
static int _convert_data_null(data_t *data)
{
_check_magic(data);
switch (data->type) {
case DATA_TYPE_STRING:
if (_regex_quick_match(data->data.string_u,
&bool_pattern_null_re)) {
log_flag(DATA, "%s: convert data (0x%"PRIXPTR") to null: %s->null",
__func__, (uintptr_t) data,
data->data.string_u);
data_set_null(data);
return SLURM_SUCCESS;
} else {
return SLURM_ERROR;
}
case DATA_TYPE_NULL:
return SLURM_SUCCESS;
default:
return SLURM_ERROR;
}
return SLURM_ERROR;
}
static int _convert_data_bool(data_t *data)
{
_check_magic(data);
switch (data->type) {
case DATA_TYPE_STRING:
if (_regex_quick_match(data->data.string_u,
&bool_pattern_true_re)) {
log_flag(DATA, "%s: convert data (0x%"PRIXPTR") to bool: %s->true",
__func__, (uintptr_t) data,
data->data.string_u);
data_set_bool(data, true);
return SLURM_SUCCESS;
} else if (_regex_quick_match(data->data.string_u,
&bool_pattern_false_re)) {
log_flag(DATA, "%s: convert data (0x%"PRIXPTR") to bool: %s->false",
__func__, (uintptr_t) data,
data->data.string_u);
data_set_bool(data, false);
return SLURM_SUCCESS;
} else {
return SLURM_ERROR;
}
case DATA_TYPE_BOOL:
return SLURM_SUCCESS;
default:
return SLURM_ERROR;
}
return SLURM_ERROR;
}
static int _convert_data_int(data_t *data)
{
_check_magic(data);
switch (data->type) {
case DATA_TYPE_STRING:
if (_regex_quick_match(data->data.string_u,
&bool_pattern_int_re)) {
int64_t x;
if (sscanf(data->data.string_u, "%"SCNd64, &x) == 1) {
log_flag(DATA, "%s: converted data (0x%"PRIXPTR") to int: %s->%"PRId64,
__func__, (uintptr_t) data,
data->data.string_u, x);
data_set_int(data, x);
return SLURM_SUCCESS;
} else { /* failed */
debug2("%s: sscanf of int failed: %s", __func__,
data->data.string_u);
return SLURM_ERROR;
}
} else {
return SLURM_ERROR;
}
case DATA_TYPE_INT_64:
return SLURM_SUCCESS;
default:
return SLURM_ERROR;
}
return SLURM_ERROR;
}
static int _convert_data_float(data_t *data)
{
_check_magic(data);
switch (data->type) {
case DATA_TYPE_STRING:
if (_regex_quick_match(data->data.string_u,
&bool_pattern_float_re)) {
double x;
if (sscanf(data->data.string_u, "%lf", &x) == 1) {
log_flag(DATA, "%s: convert data (0x%"PRIXPTR") to float: %s->%lf",
__func__, (uintptr_t) data,
data->data.string_u, x);
data_set_float(data, x);
return SLURM_SUCCESS;
} else { /* failed */
error("%s: sscanf of double failed: %s",
__func__, data->data.string_u);
return SLURM_ERROR;
}
} else {
return SLURM_ERROR;
}
case DATA_TYPE_FLOAT:
return SLURM_SUCCESS;
default:
return SLURM_ERROR;
}
return SLURM_ERROR;
}
extern data_type_t data_convert_type(data_t *data, data_type_t match)
{
_check_magic(data);
/*
* This currently only works on primitive types and doesn't
* apply to dictionaries or lists.
*/
xassert(data_get_type(data) != DATA_TYPE_DICT);
xassert(data_get_type(data) != DATA_TYPE_LIST);
switch (match) {
case DATA_TYPE_STRING:
return _convert_data_string(data) ? DATA_TYPE_NONE :
DATA_TYPE_STRING;
case DATA_TYPE_BOOL:
return _convert_data_force_bool(data) ? DATA_TYPE_NONE :
DATA_TYPE_BOOL;
case DATA_TYPE_INT_64:
return _convert_data_int(data) ? DATA_TYPE_NONE :
DATA_TYPE_INT_64;
case DATA_TYPE_FLOAT:
return _convert_data_float(data) ? DATA_TYPE_NONE :
DATA_TYPE_FLOAT;
case DATA_TYPE_NULL:
return _convert_data_null(data) ? DATA_TYPE_NONE :
DATA_TYPE_NULL;
case DATA_TYPE_NONE:
if (!_convert_data_null(data))
return DATA_TYPE_NULL;
if (!_convert_data_bool(data))
return DATA_TYPE_BOOL;
if (!_convert_data_int(data))
return DATA_TYPE_INT_64;
if (!_convert_data_float(data))
return DATA_TYPE_FLOAT;
default:
break;
}
return DATA_TYPE_NONE;
}
typedef struct {
size_t count;
data_type_t match;
} convert_args_t;
data_for_each_cmd_t _convert_list_entry(data_t *data, void *arg)
{
convert_args_t *args = arg;
args->count += data_convert_tree(data, args->match);
return DATA_FOR_EACH_CONT;
}
data_for_each_cmd_t _convert_dict_entry(const char *key, data_t *data,
void *arg)
{
convert_args_t *args = arg;
args->count += data_convert_tree(data, args->match);
return DATA_FOR_EACH_CONT;
}
extern size_t data_convert_tree(data_t *data, const data_type_t match)
{
convert_args_t args = { .match = match };
_check_magic(data);
if (!data)
return 0;
switch (data_get_type(data)) {
case DATA_TYPE_DICT:
data_dict_for_each(data, _convert_dict_entry, &args);
break;
case DATA_TYPE_LIST:
data_list_for_each(data, _convert_list_entry, &args);
break;
default:
if (match == data_convert_type(data, match))
args.count++;
break;
}
return args.count;
}
typedef struct {
const data_t *b;
bool mask;
} find_dict_match_t;
data_for_each_cmd_t _find_dict_match(const char *key, const data_t *a,
void *arg)
{
find_dict_match_t *p = arg;
const data_t *b = data_key_get_const(p->b, key);
if (data_check_match(a, b, p->mask))
return DATA_FOR_EACH_CONT;
else
return DATA_FOR_EACH_FAIL;
}
static bool _data_match_dict(const data_t *a, const data_t *b, bool mask)
{
find_dict_match_t p = {
.mask = mask,
.b = b,
};
if (!a || data_get_type(a) != DATA_TYPE_DICT)
return false;
if (!b || data_get_type(b) != DATA_TYPE_DICT)
return false;
_check_magic(a);
_check_magic(b);
if (a->data.dict_u->count != b->data.dict_u->count)
return false;
/* match by key and not order with dictionary */
return (data_dict_for_each_const(a, _find_dict_match, &p) >= 0);
}
static bool _data_match_lists(const data_t *a, const data_t *b, bool mask)
{
bool fail = false;
const data_list_node_t *ptr_a;
const data_list_node_t *ptr_b;
if (!a || data_get_type(a) != DATA_TYPE_LIST)
return false;
if (!b || data_get_type(b) != DATA_TYPE_LIST)
return false;
_check_magic(a);
_check_magic(b);
if (a->data.list_u->count != b->data.list_u->count)
return false;
ptr_a = a->data.list_u->begin;
ptr_b = b->data.list_u->begin;
while (ptr_a && !fail) {
_check_data_list_node_magic(ptr_a);
if (!ptr_b && mask)
/* ignore a if b is NULL when masking */
continue;
_check_data_list_node_magic(ptr_b);
if (data_check_match(ptr_a->data, ptr_b->data, mask)) {
ptr_a = ptr_a->next;
ptr_b = ptr_b->next;
} else
fail = true;
}
return !fail;
}
extern bool data_check_match(const data_t *a, const data_t *b, bool mask)
{
if (a == NULL && b == NULL)
return true;
if (a == NULL || b == NULL)
return false;
_check_magic(a);
_check_magic(b);
if (data_get_type(a) != data_get_type(b))
return false;
switch (data_get_type(a)) {
case DATA_TYPE_NULL:
return (data_get_type(b) == DATA_TYPE_NULL);
case DATA_TYPE_STRING:
// TODO: should we have a case insensitive compare?
return !xstrcmp(data_get_string(a), data_get_string(b));
case DATA_TYPE_BOOL:
return (data_get_bool(a) == data_get_bool(b));
case DATA_TYPE_INT_64:
return data_get_int(a) == data_get_int(b);
case DATA_TYPE_FLOAT:
return fuzzy_equal(data_get_float(a), data_get_float(b));
case DATA_TYPE_DICT:
return _data_match_dict(a, b, mask);
case DATA_TYPE_LIST:
return _data_match_lists(a, b, mask);
default:
fatal_abort("%s: unexpected data type", __func__);
}
}
extern const data_t *data_resolve_dict_path(const data_t *data, const char *path)
{
const data_t *found = data;
char *save_ptr = NULL;
char *token = NULL;
char *str = xstrdup(path);
_check_magic(data);
token = strtok_r(str, "/", &save_ptr);
while (token && found) {
xstrtrim(token);
if (data_get_type(found) != DATA_TYPE_DICT)
found = NULL;
if (found) {
found = data_key_get_const(found, token);
token = strtok_r(NULL, "/", &save_ptr);
}
}
xfree(str);
if (found)
log_flag(DATA, "%s: data (0x%"PRIXPTR") resolved dictionary path \"%s\" to (0x%"PRIXPTR")",
__func__, (uintptr_t) data, path, (uintptr_t) found);
else
log_flag(DATA, "%s: data (0x%"PRIXPTR") failed to resolve dictionary path \"%s\"",
__func__, (uintptr_t) data, path);
return found;
}
extern data_t *data_define_dict_path(data_t *data, const char *path)
{
data_t *found = data;
char *save_ptr = NULL;
char *token = NULL;
char *str = xstrdup(path);
_check_magic(data);
token = strtok_r(str, "/", &save_ptr);
while (token && found) {
xstrtrim(token);
if (data_get_type(found) == DATA_TYPE_NULL)
data_set_dict(found);
else if (data_get_type(found) != DATA_TYPE_DICT)
found = NULL;
if (found) {
found = data_key_set(found, token);
token = strtok_r(NULL, "/", &save_ptr);
}
}
xfree(str);
if (found)
log_flag(DATA, "%s: data (0x%"PRIXPTR") defined dictionary path \"%s\" to (0x%"PRIXPTR")",
__func__, (uintptr_t) data, path, (uintptr_t) found);
else
log_flag(DATA, "%s: data (0x%"PRIXPTR") failed to define dictionary path \"%s\"",
__func__, (uintptr_t) data, path);
return found;
}
data_t *data_copy(data_t *dest, const data_t *src)
{
_check_magic(src);
_check_magic(dest);
log_flag(DATA, "%s: copy data (0x%"PRIXPTR") to (0x%"PRIXPTR")",
__func__, (uintptr_t) src, (uintptr_t) dest);
switch (data_get_type(src)) {
case DATA_TYPE_STRING:
return data_set_string(dest, data_get_string(src));
case DATA_TYPE_BOOL:
return data_set_bool(dest, data_get_bool(src));
case DATA_TYPE_INT_64:
return data_set_int(dest, data_get_int(src));
case DATA_TYPE_FLOAT:
return data_set_float(dest, data_get_float(src));
case DATA_TYPE_NULL:
return data_set_null(dest);
case DATA_TYPE_LIST:
{
data_list_node_t *i = src->data.list_u->begin;
data_set_list(dest);
while (i) {
_check_data_list_node_magic(i);
xassert(!i->key);
data_copy(data_list_append(dest), i->data);
i = i->next;
}
return dest;
}
case DATA_TYPE_DICT:
{
data_list_node_t *i = src->data.dict_u->begin;
data_set_dict(dest);
while (i) {
_check_data_list_node_magic(i);
data_copy(data_key_set(dest, i->key), i->data);
i = i->next;
}
return dest;
}
default:
fatal_abort("%s: unexpected data type", __func__);
return NULL;
}
}
extern int data_retrieve_dict_path_string(const data_t *data, const char *path,
char **ptr_buffer)
{
const data_t *d = NULL;
int rc;
_check_magic(data);
if (!(d = data_resolve_dict_path(data, path)))
return SLURM_ERROR;
rc = data_get_string_converted(d, ptr_buffer);
log_flag(DATA, "%s: data (0x%"PRIXPTR") resolved string at path %s to \"%s\"",
__func__, (uintptr_t) data, path, *ptr_buffer);
return rc;
}
extern int data_retrieve_dict_path_bool(const data_t *data, const char *path,
bool *ptr_buffer)
{
const data_t *d = NULL;
int rc;
_check_magic(data);
if (!(d = data_resolve_dict_path(data, path)))
return SLURM_ERROR;
rc = data_copy_bool_converted(d, ptr_buffer);
log_flag(DATA, "%s: data (0x%"PRIXPTR") resolved string at path %s to %s",
__func__, (uintptr_t) data, path,
(*ptr_buffer ? "true" : "false"));
return rc;
}
extern int data_retrieve_dict_path_int(const data_t *data, const char *path,
int64_t *ptr_buffer)
{
const data_t *d = NULL;
int rc;
_check_magic(data);
if (!(d = data_resolve_dict_path(data, path)))
return SLURM_ERROR;
rc = data_get_int_converted(d, ptr_buffer);
log_flag(DATA, "%s: data (0x%"PRIXPTR") resolved string at path %s to %"PRId64,
__func__, (uintptr_t) data, path, *ptr_buffer);
return rc;
}
extern char *data_type_to_string(data_type_t type)
{
switch(type) {
case DATA_TYPE_NULL:
return "null";
case DATA_TYPE_LIST:
return "list";
case DATA_TYPE_DICT:
return "dictionary";
case DATA_TYPE_INT_64:
return "int 64bits";
case DATA_TYPE_STRING:
return "string";
case DATA_TYPE_FLOAT:
return "floating point";
case DATA_TYPE_BOOL:
return "boolean";
default:
return "INVALID";
}
}