rizin/librz/util/structured_data.c

597 lines
19 KiB
C

// SPDX-FileCopyrightText: 2025 deroad <deroad@kumo.xn--q9jyb4c>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_util.h>
/**
* \file structured_data.c
*
* Generates a structured data which can be converted
* in a human readable output (like json or yaml).
*
*/
#include "structured_data_json.c"
#include "structured_data_yaml.c"
typedef enum {
STRUCTURED_DATA_TYPE_MAP = 0,
STRUCTURED_DATA_TYPE_ARRAY,
STRUCTURED_DATA_TYPE_UNSIGNED,
STRUCTURED_DATA_TYPE_HEXADECIMAL,
STRUCTURED_DATA_TYPE_SIGNED,
STRUCTURED_DATA_TYPE_DOUBLE,
STRUCTURED_DATA_TYPE_BOOL,
STRUCTURED_DATA_TYPE_STRING,
} StructuredDataType;
struct rz_structured_data_t {
StructuredDataType type;
union {
ut64 v_unsigned;
st64 v_signed;
double v_double;
bool v_bool;
char *v_string;
HtSP /*<RzStructuredData *>*/ *v_map;
};
RzPVector /*<void *>*/ *v_array; // used also to keep the map keys in order
};
typedef struct structured_data_iter_over_t {
const RzStructuredDataIterator *fit;
void *user;
} StructuredDataIterOver;
/**
* \brief Free a RzStructuredData pointer
*
* \param sd The RzStructuredData to free.
*/
RZ_API void rz_structured_data_free(RZ_NULLABLE RzStructuredData *sd) {
if (!sd) {
return;
}
if (sd->type == STRUCTURED_DATA_TYPE_MAP) {
ht_sp_free(sd->v_map);
rz_pvector_free(sd->v_array);
} else if (sd->type == STRUCTURED_DATA_TYPE_ARRAY) {
rz_pvector_free(sd->v_array);
} else if (sd->type == STRUCTURED_DATA_TYPE_STRING) {
free(sd->v_string);
}
free(sd);
}
static RzStructuredData *structured_data_new_map() {
RzStructuredData *sd = RZ_NEW0(RzStructuredData);
if (!sd) {
return NULL;
}
sd->type = STRUCTURED_DATA_TYPE_MAP;
sd->v_map = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_structured_data_free);
sd->v_array = rz_pvector_new((RzPVectorFree)free);
if (!sd->v_map || !sd->v_array) {
rz_structured_data_free(sd);
return NULL;
}
return sd;
}
static RzStructuredData *structured_data_new_array() {
RzStructuredData *sd = RZ_NEW0(RzStructuredData);
if (!sd) {
return NULL;
}
sd->type = STRUCTURED_DATA_TYPE_ARRAY;
sd->v_array = rz_pvector_new((RzPVectorFree)rz_structured_data_free);
if (!sd->v_array) {
free(sd);
return NULL;
}
return sd;
}
static RzStructuredData *structured_data_new_unsigned(ut64 v_unsigned, bool hex) {
RzStructuredData *sd = RZ_NEW0(RzStructuredData);
if (!sd) {
return NULL;
}
sd->type = hex ? STRUCTURED_DATA_TYPE_HEXADECIMAL : STRUCTURED_DATA_TYPE_UNSIGNED;
sd->v_unsigned = v_unsigned;
return sd;
}
static RzStructuredData *structured_data_new_signed(st64 v_signed) {
RzStructuredData *sd = RZ_NEW0(RzStructuredData);
if (!sd) {
return NULL;
}
sd->type = STRUCTURED_DATA_TYPE_SIGNED;
sd->v_signed = v_signed;
return sd;
}
static RzStructuredData *structured_data_new_double(double v_double) {
RzStructuredData *sd = RZ_NEW0(RzStructuredData);
if (!sd) {
return NULL;
}
sd->type = STRUCTURED_DATA_TYPE_DOUBLE;
sd->v_double = v_double;
return sd;
}
static RzStructuredData *structured_data_new_bool(bool v_bool) {
RzStructuredData *sd = RZ_NEW0(RzStructuredData);
if (!sd) {
return NULL;
}
sd->type = STRUCTURED_DATA_TYPE_BOOL;
sd->v_bool = v_bool;
return sd;
}
static RzStructuredData *structured_data_new_string(char *v_string) {
RzStructuredData *sd = RZ_NEW0(RzStructuredData);
if (!sd) {
return NULL;
}
sd->type = STRUCTURED_DATA_TYPE_STRING;
sd->v_string = v_string;
return sd;
}
static bool structured_data_map_add(RZ_NONNULL RzStructuredData *parent, const char *key, RZ_NONNULL RZ_OWN RzStructuredData *child) {
RzPVector *pvec = parent->v_array;
HtSP *map = parent->v_map;
if (!map || !pvec || !child || RZ_STR_ISEMPTY(key)) {
rz_structured_data_free(child);
RZ_LOG_ERROR("struct_factory: invalid key: '%s'\n", key);
return false;
}
bool found = false;
ht_sp_find(map, key, &found);
if (found) {
rz_structured_data_free(child);
RZ_LOG_ERROR("struct_factory: found duplicated key: '%s'\n", key);
return false;
}
char *key_copy = rz_str_dup(key);
if (!key_copy || !ht_sp_insert(map, key_copy, child)) {
rz_structured_data_free(child);
free(key_copy);
RZ_LOG_ERROR("struct_factory: cannot add value with key: '%s'\n", key);
return false;
}
return rz_pvector_push(pvec, key_copy);
}
static bool structured_data_array_add(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL RZ_OWN RzStructuredData *child) {
RzPVector *pvec = parent->v_array;
if (!pvec || !child) {
RZ_LOG_ERROR("struct_factory: invalid array value\n");
rz_structured_data_free(child);
return false;
}
rz_pvector_push(pvec, child);
return true;
}
/**
* \brief Creates a new RzStructuredData initialized as a map
*
* \return On success returns a valid opaque pointer to a RzStructuredData
*/
RZ_API RZ_OWN RzStructuredData *rz_structured_data_new_map() {
return structured_data_new_map();
}
/**
* \brief Creates a new RzStructuredData initialized as an array
*
* \return On success returns a valid opaque pointer to a RzStructuredData
*/
RZ_API RZ_OWN RzStructuredData *rz_structured_data_new_array() {
return structured_data_new_array();
}
/**
* \brief Returns a child, map type, RzStructuredData that has been added to the given parent
*
* \param parent Where to add the child RzStructuredData
* \param[in] key The key to assign the child RzStructuredData
*
* \return On success returns a valid pointer to the child RzStructuredData, otherwise NULL
*/
RZ_API RZ_BORROW RzStructuredData *rz_structured_data_map_add_map(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL const char *key) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_MAP && key, false);
RzStructuredData *child = structured_data_new_map();
if (!structured_data_map_add(parent, key, child)) {
return NULL;
}
return child;
}
/**
* \brief Returns a child, array type, RzStructuredData that has been added to the given parent
*
* \param parent Where to add the child RzStructuredData
* \param[in] key The key to assign the child RzStructuredData
*
* \return On success returns a valid pointer to the child RzStructuredData, otherwise NULL
*/
RZ_API RZ_BORROW RzStructuredData *rz_structured_data_map_add_array(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL const char *key) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_MAP && key, false);
RzStructuredData *child = structured_data_new_array();
if (!structured_data_map_add(parent, key, child)) {
return NULL;
}
return child;
}
/**
* \brief Adds an owned RzStructuredData child to a parent and assigns it a map key
*
* \param parent Where to add the child RzStructuredData
* \param[in] key The key to assign the child RzStructuredData
* \param child The child RzStructuredData
*
* \return On success returns true, otherwise the child gets deallocated by the function and return false.
*/
RZ_API bool rz_structured_data_map_add(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL const char *key, RZ_NONNULL RZ_OWN RzStructuredData *child) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_MAP && key && child, false);
return structured_data_map_add(parent, key, child);
}
/**
* \brief Adds unsigned type child to a parent and assigns it a map key
*
* \param parent Where to add the child RzStructuredData
* \param[in] key The key to assign the child RzStructuredData
* \param n The value to assign to the child
* \param hex When true the type is going to be set as base16 number instead of base10 unsigned number.
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_map_add_unsigned(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL const char *key, ut64 n, bool hex) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_MAP && key, false);
RzStructuredData *child = structured_data_new_unsigned(n, hex);
return structured_data_map_add(parent, key, child);
}
/**
* \brief Adds signed type child to a parent and assigns it a map key
*
* \param parent Where to add the child RzStructuredData
* \param[in] key The key to assign the child RzStructuredData
* \param n The value to assign to the child
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_map_add_signed(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL const char *key, st64 n) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_MAP && key, false);
RzStructuredData *child = structured_data_new_signed(n);
return structured_data_map_add(parent, key, child);
}
/**
* \brief Adds double type child to a parent and assigns it a map key
*
* \param parent Where to add the child RzStructuredData
* \param[in] key The key to assign the child RzStructuredData
* \param d The value to assign to the child
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_map_add_double(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL const char *key, double d) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_MAP && key, false);
RzStructuredData *child = structured_data_new_double(d);
return structured_data_map_add(parent, key, child);
}
/**
* \brief Adds boolean type child to a parent and assigns it a map key
*
* \param parent Where to add the child RzStructuredData
* \param[in] key The key to assign the child RzStructuredData
* \param b The value to assign to the child
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_map_add_boolean(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL const char *key, bool b) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_MAP && key, false);
RzStructuredData *child = structured_data_new_bool(b);
return structured_data_map_add(parent, key, child);
}
/**
* \brief Adds a null terminated string type child to a parent and assigns it a map key
*
* \param parent Where to add the child RzStructuredData
* \param[in] key The key to assign the child RzStructuredData
* \param s The value to assign to the child
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_map_add_string(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL const char *key, RZ_NONNULL const char *s) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_MAP && key && s, false);
char *copy = rz_str_dup(s);
if (!copy) {
return false;
}
RzStructuredData *child = structured_data_new_string(copy);
return structured_data_map_add(parent, key, child);
}
/**
* \brief Returns a child, map type, RzStructuredData that has been added to the given parent
*
* \param parent Where to add the child RzStructuredData
*
* \return On success returns a valid pointer to the child RzStructuredData, otherwise NULL
*/
RZ_API RZ_BORROW RzStructuredData *rz_structured_data_array_add_map(RZ_NONNULL RzStructuredData *parent) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_ARRAY, false);
RzStructuredData *child = structured_data_new_map();
if (!structured_data_array_add(parent, child)) {
return NULL;
}
return child;
}
/**
* \brief Returns a child, array type, RzStructuredData that has been added to the given parent
*
* \param parent Where to add the child RzStructuredData
*
* \return On success returns a valid pointer to the child RzStructuredData, otherwise NULL
*/
RZ_API RZ_BORROW RzStructuredData *rz_structured_data_array_add_array(RZ_NONNULL RzStructuredData *parent) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_ARRAY, false);
RzStructuredData *child = structured_data_new_array();
if (!structured_data_array_add(parent, child)) {
return NULL;
}
return child;
}
/**
* \brief Adds an owned RzStructuredData child to an array type parent
*
* \param parent Where to add the child RzStructuredData
* \param child The child RzStructuredData
*
* \return On success returns true, otherwise the child gets deallocated by the function and return false.
*/
RZ_API bool rz_structured_data_array_add(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL RZ_OWN RzStructuredData *child) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_ARRAY && child, false);
return structured_data_array_add(parent, child);
}
/**
* \brief Adds unsigned type child to an array type parent
*
* \param parent Where to add the child RzStructuredData
* \param n The value to assign to the child
* \param hex When true the type is going to be set as base16 number instead of base10 unsigned number.
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_array_add_unsigned(RZ_NONNULL RzStructuredData *parent, ut64 n, bool hex) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_ARRAY, false);
RzStructuredData *child = structured_data_new_unsigned(n, hex);
return structured_data_array_add(parent, child);
}
/**
* \brief Adds signed type child to an array type parent
*
* \param parent Where to add the child RzStructuredData
* \param n The value to assign to the child
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_array_add_signed(RZ_NONNULL RzStructuredData *parent, st64 n) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_ARRAY, false);
RzStructuredData *child = structured_data_new_signed(n);
return structured_data_array_add(parent, child);
}
/**
* \brief Adds double type child to an array type parent
*
* \param parent Where to add the child RzStructuredData
* \param n The value to assign to the child
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_array_add_double(RZ_NONNULL RzStructuredData *parent, double d) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_ARRAY, false);
RzStructuredData *child = structured_data_new_double(d);
return structured_data_array_add(parent, child);
}
/**
* \brief Adds signed type child to an array type parent
*
* \param parent Where to add the child RzStructuredData
* \param n The value to assign to the child
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_array_add_boolean(RZ_NONNULL RzStructuredData *parent, bool b) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_ARRAY, false);
RzStructuredData *child = structured_data_new_bool(b);
return structured_data_array_add(parent, child);
}
/**
* \brief Adds a null terminated string type child to an array type parent
*
* \param parent Where to add the child RzStructuredData
* \param n The value to assign to the child
*
* \return On success returns true, otherwise false.
*/
RZ_API bool rz_structured_data_array_add_string(RZ_NONNULL RzStructuredData *parent, RZ_NONNULL const char *s) {
rz_return_val_if_fail(parent && parent->type == STRUCTURED_DATA_TYPE_ARRAY && s, false);
char *copy = rz_str_dup(s);
if (!copy) {
return false;
}
RzStructuredData *child = structured_data_new_string(copy);
return structured_data_array_add(parent, child);
}
static void structured_data_iterate_over(StructuredDataIterOver *sdio, const RzStructuredData *sd);
static void structured_data_iterate_over_map(StructuredDataIterOver *sdio, const RzStructuredData *sd) {
void **pit = NULL;
size_t n_keys = rz_pvector_len(sd->v_array);
sdio->fit->new_struct(sdio->user, RZ_STRUCTURED_DATA_BLOCK_MAP, n_keys);
rz_pvector_foreach (sd->v_array, pit) {
const char *key = *pit;
const RzStructuredData *elem = (const RzStructuredData *)ht_sp_find(sd->v_map, key, NULL);
sdio->fit->key(sdio->user, key);
structured_data_iterate_over(sdio, elem);
}
sdio->fit->end_struct(sdio->user);
}
static void structured_data_iterate_over_array(StructuredDataIterOver *sdio, const RzStructuredData *sd) {
void **pit = NULL;
size_t array_len = rz_pvector_len(sd->v_array);
sdio->fit->new_struct(sdio->user, RZ_STRUCTURED_DATA_BLOCK_ARRAY, array_len);
rz_pvector_foreach (sd->v_array, pit) {
const RzStructuredData *elem = (const RzStructuredData *)*pit;
structured_data_iterate_over(sdio, elem);
}
sdio->fit->end_struct(sdio->user);
}
static void structured_data_iterate_over(StructuredDataIterOver *sdio, const RzStructuredData *sd) {
if (!sd) {
return;
}
switch (sd->type) {
case STRUCTURED_DATA_TYPE_MAP:
structured_data_iterate_over_map(sdio, sd);
return;
case STRUCTURED_DATA_TYPE_ARRAY:
structured_data_iterate_over_array(sdio, sd);
return;
case STRUCTURED_DATA_TYPE_HEXADECIMAL:
sdio->fit->val_unsigned(sdio->user, sd->v_unsigned, true);
return;
case STRUCTURED_DATA_TYPE_UNSIGNED:
sdio->fit->val_unsigned(sdio->user, sd->v_unsigned, false);
return;
case STRUCTURED_DATA_TYPE_SIGNED:
sdio->fit->val_signed(sdio->user, sd->v_signed);
return;
case STRUCTURED_DATA_TYPE_DOUBLE:
sdio->fit->val_double(sdio->user, sd->v_double);
return;
case STRUCTURED_DATA_TYPE_BOOL:
sdio->fit->val_bool(sdio->user, sd->v_bool);
return;
case STRUCTURED_DATA_TYPE_STRING:
sdio->fit->val_string(sdio->user, sd->v_string);
return;
default:
rz_warn_if_reached();
return;
}
}
/**
* \brief Iterates over a given RzStructuredData using the provided iterator
*
* \param[in] sd The RzStructuredData to iterate over
* \param[in] iterator The iterator to use
* \param user The user ponter (can be null)
*/
RZ_API void rz_structured_data_iterate(RZ_NONNULL const RzStructuredData *sd, RZ_NONNULL const RzStructuredDataIterator *iterator, RZ_NULLABLE void *user) {
rz_return_if_fail(sd && iterator && iterator);
StructuredDataIterOver sdio = {
.fit = iterator,
.user = user,
};
structured_data_iterate_over(&sdio, sd);
}
/**
* \brief Iterates over a given RzStructuredData and generates a json string
*
* \param[in] sd The RzStructuredData to iterate over
* \param[in] pj The PJ to write into
*/
RZ_API void rz_structured_data_to_pj(RZ_NONNULL const RzStructuredData *sd, RZ_NONNULL PJ *pj) {
rz_return_if_fail(sd && pj);
rz_structured_data_iterate(sd, &builder_json_iterator, pj);
}
/**
* \brief Iterates over a given RzStructuredData and generates a json string
*
* \param[in] sd The RzStructuredData to iterate over
*/
RZ_API RZ_OWN char *rz_structured_data_to_json(RZ_NONNULL const RzStructuredData *sd) {
rz_return_val_if_fail(sd, NULL);
PJ *pj = pj_new();
if (!pj) {
return NULL;
}
rz_structured_data_iterate(sd, &builder_json_iterator, pj);
return pj_drain(pj);
}
/**
* \brief Iterates over a given RzStructuredData and generates a yaml string
*
* \param[in] sd The RzStructuredData to iterate over
*/
RZ_API RZ_OWN char *rz_structured_data_to_yaml(RZ_NONNULL const RzStructuredData *sd) {
rz_return_val_if_fail(sd, NULL);
StructYamlPrinter yaml = { 0 };
builder_yaml_init_printer(&yaml);
rz_structured_data_iterate(sd, &builder_yaml_iterator, &yaml);
return builder_yaml_fini_printer(&yaml);
}