324 lines
7.7 KiB
C
324 lines
7.7 KiB
C
// SPDX-FileCopyrightText: 2020 Florian Märkl <info@florianmaerkl.de>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_util/rz_serialize.h>
|
|
#include <rz_flag.h>
|
|
|
|
/*
|
|
* SDB Format:
|
|
*
|
|
* /
|
|
* base=<base>
|
|
* realnames=<realnames?"1":"0">
|
|
* /spaces
|
|
* see spaces.c
|
|
* /tags
|
|
* like RzFlag.tags
|
|
* /zones
|
|
* <zone name>={"from":<from>,"to":<to>}
|
|
* /flags
|
|
* <flag name>={"realname":<str>,"demangled":<bool>,"offset":<uint>,"size":<uint>,"space":<str>,"color":<str>,"comment":<str>,"alias":<str>}
|
|
*
|
|
*/
|
|
|
|
RZ_API void rz_serialize_flag_zones_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzList /*<RzFlagZoneItem *>*/ *zones) {
|
|
RzListIter *it;
|
|
RzFlagZoneItem *item;
|
|
rz_list_foreach (zones, it, item) {
|
|
PJ *j = pj_new();
|
|
if (!j) {
|
|
return;
|
|
}
|
|
pj_o(j);
|
|
pj_kn(j, "from", item->from);
|
|
pj_kn(j, "to", item->to);
|
|
pj_end(j);
|
|
sdb_set(db, item->name, pj_string(j), 0);
|
|
pj_free(j);
|
|
}
|
|
}
|
|
|
|
static bool zone_load_cb(void *user, const char *k, const char *v) {
|
|
RzList *list = user;
|
|
char *json_str = strdup(v);
|
|
if (!json_str) {
|
|
return true;
|
|
}
|
|
RzJson *json = rz_json_parse(json_str);
|
|
if (!json) {
|
|
free(json_str);
|
|
return true;
|
|
}
|
|
if (json->type != RZ_JSON_OBJECT) {
|
|
goto beach;
|
|
}
|
|
const RzJson *child;
|
|
RzFlagZoneItem *item = RZ_NEW0(RzFlagZoneItem);
|
|
if (!item) {
|
|
goto beach;
|
|
}
|
|
item->name = strdup(k);
|
|
if (!item->name) {
|
|
free(item);
|
|
goto beach;
|
|
}
|
|
for (child = json->children.first; child; child = child->next) {
|
|
if (child->type != RZ_JSON_INTEGER) {
|
|
continue;
|
|
}
|
|
if (strcmp(child->key, "from") == 0) {
|
|
item->from = child->num.u_value;
|
|
} else if (strcmp(child->key, "to") == 0) {
|
|
item->to = child->num.u_value;
|
|
}
|
|
}
|
|
rz_list_append(list, item);
|
|
beach:
|
|
rz_json_free(json);
|
|
free(json_str);
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_serialize_flag_zones_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzList /*<RzFlagZoneItem *>*/ *zones, RZ_NULLABLE RzSerializeResultInfo *res) {
|
|
rz_return_val_if_fail(zones, false);
|
|
rz_list_purge(zones);
|
|
bool r = sdb_foreach(db, zone_load_cb, zones);
|
|
if (!r) {
|
|
RZ_SERIALIZE_ERR(res, "failed to parse a flag zone json");
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static bool flag_save_cb(RzFlagItem *flag, void *user) {
|
|
Sdb *db = user;
|
|
PJ *j = pj_new();
|
|
if (!j) {
|
|
return false;
|
|
}
|
|
pj_o(j);
|
|
if (flag->realname) {
|
|
pj_ks(j, "realname", flag->realname);
|
|
}
|
|
pj_kb(j, "demangled", flag->demangled);
|
|
pj_kn(j, "offset", flag->offset);
|
|
pj_kn(j, "size", flag->size);
|
|
if (flag->space) {
|
|
pj_ks(j, "space", flag->space->name);
|
|
}
|
|
if (flag->color) {
|
|
pj_ks(j, "color", flag->color);
|
|
}
|
|
if (flag->comment) {
|
|
pj_ks(j, "comment", flag->comment);
|
|
}
|
|
if (flag->alias) {
|
|
pj_ks(j, "alias", flag->alias);
|
|
}
|
|
pj_end(j);
|
|
sdb_set(db, flag->name, pj_string(j), 0);
|
|
pj_free(j);
|
|
return true;
|
|
}
|
|
|
|
RZ_API void rz_serialize_flag_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzFlag *flag) {
|
|
rz_serialize_spaces_save(sdb_ns(db, "spaces", true), &flag->spaces);
|
|
char buf[32];
|
|
if (snprintf(buf, sizeof(buf), "%" PFMT64d, flag->base) < 0) {
|
|
return;
|
|
}
|
|
sdb_set(db, "base", buf, 0);
|
|
sdb_set(db, "realnames", flag->realnames ? "1" : "0", 0);
|
|
sdb_copy(flag->tags, sdb_ns(db, "tags", true));
|
|
rz_serialize_flag_zones_save(sdb_ns(db, "zones", true), flag->zones);
|
|
rz_flag_foreach(flag, flag_save_cb, sdb_ns(db, "flags", true));
|
|
}
|
|
|
|
typedef enum {
|
|
FLAG_FIELD_REALNAME,
|
|
FLAG_FIELD_DEMANGLED,
|
|
FLAG_FIELD_OFFSET,
|
|
FLAG_FIELD_SIZE,
|
|
FLAG_FIELD_SPACE,
|
|
FLAG_FIELD_COLOR,
|
|
FLAG_FIELD_COMMENT,
|
|
FLAG_FIELD_ALIAS
|
|
} FlagField;
|
|
|
|
typedef struct {
|
|
RzFlag *flag;
|
|
RzKeyParser *parser;
|
|
} FlagLoadCtx;
|
|
|
|
static bool flag_load_cb(void *user, const char *k, const char *v) {
|
|
FlagLoadCtx *ctx = user;
|
|
|
|
char *json_str = strdup(v);
|
|
if (!json_str) {
|
|
return true;
|
|
}
|
|
RzJson *json = rz_json_parse(json_str);
|
|
if (!json || json->type != RZ_JSON_OBJECT) {
|
|
free(json_str);
|
|
return false;
|
|
}
|
|
|
|
RzFlagItem proto = { 0 };
|
|
bool offset_set = false;
|
|
bool size_set = false;
|
|
|
|
RZ_KEY_PARSER_JSON(ctx->parser, json, child, {
|
|
case FLAG_FIELD_REALNAME:
|
|
if (child->type != RZ_JSON_STRING) {
|
|
break;
|
|
}
|
|
proto.realname = (char *)child->str_value;
|
|
break;
|
|
case FLAG_FIELD_DEMANGLED:
|
|
if (child->type != RZ_JSON_BOOLEAN) {
|
|
break;
|
|
}
|
|
proto.demangled = child->num.u_value != 0;
|
|
break;
|
|
case FLAG_FIELD_OFFSET:
|
|
if (child->type != RZ_JSON_INTEGER) {
|
|
break;
|
|
}
|
|
proto.offset = child->num.u_value;
|
|
offset_set = true;
|
|
break;
|
|
case FLAG_FIELD_SIZE:
|
|
if (child->type != RZ_JSON_INTEGER) {
|
|
break;
|
|
}
|
|
proto.size = child->num.u_value;
|
|
size_set = true;
|
|
break;
|
|
case FLAG_FIELD_SPACE:
|
|
if (child->type != RZ_JSON_STRING) {
|
|
break;
|
|
}
|
|
proto.space = rz_flag_space_get(ctx->flag, child->str_value);
|
|
break;
|
|
case FLAG_FIELD_COLOR:
|
|
if (child->type != RZ_JSON_STRING) {
|
|
break;
|
|
}
|
|
proto.color = (char *)child->str_value;
|
|
break;
|
|
case FLAG_FIELD_COMMENT:
|
|
if (child->type != RZ_JSON_STRING) {
|
|
break;
|
|
}
|
|
proto.comment = (char *)child->str_value;
|
|
break;
|
|
case FLAG_FIELD_ALIAS:
|
|
if (child->type != RZ_JSON_STRING) {
|
|
break;
|
|
}
|
|
proto.alias = (char *)child->str_value;
|
|
break;
|
|
default:
|
|
break;
|
|
});
|
|
|
|
bool res = true;
|
|
if (!offset_set || !size_set) {
|
|
res = false;
|
|
goto beach;
|
|
}
|
|
|
|
RzFlagItem *item = rz_flag_set(ctx->flag, k, proto.offset - ctx->flag->base, proto.size);
|
|
if (proto.realname) {
|
|
rz_flag_item_set_realname(item, proto.realname);
|
|
}
|
|
item->demangled = proto.demangled;
|
|
item->space = proto.space;
|
|
if (proto.color) {
|
|
rz_flag_item_set_color(item, proto.color);
|
|
}
|
|
if (proto.comment) {
|
|
rz_flag_item_set_comment(item, proto.comment);
|
|
}
|
|
if (proto.alias) {
|
|
rz_flag_item_set_alias(item, proto.alias);
|
|
}
|
|
|
|
beach:
|
|
rz_json_free(json);
|
|
free(json_str);
|
|
return res;
|
|
}
|
|
|
|
static bool load_flags(RZ_NONNULL Sdb *flags_db, RZ_NONNULL RzFlag *flag) {
|
|
FlagLoadCtx ctx = { flag, rz_key_parser_new() };
|
|
if (!ctx.parser) {
|
|
return false;
|
|
}
|
|
rz_key_parser_add(ctx.parser, "realname", FLAG_FIELD_REALNAME);
|
|
rz_key_parser_add(ctx.parser, "demangled", FLAG_FIELD_DEMANGLED);
|
|
rz_key_parser_add(ctx.parser, "offset", FLAG_FIELD_OFFSET);
|
|
rz_key_parser_add(ctx.parser, "size", FLAG_FIELD_SIZE);
|
|
rz_key_parser_add(ctx.parser, "space", FLAG_FIELD_SPACE);
|
|
rz_key_parser_add(ctx.parser, "color", FLAG_FIELD_COLOR);
|
|
rz_key_parser_add(ctx.parser, "comment", FLAG_FIELD_COMMENT);
|
|
rz_key_parser_add(ctx.parser, "alias", FLAG_FIELD_ALIAS);
|
|
bool r = sdb_foreach(flags_db, flag_load_cb, &ctx);
|
|
rz_key_parser_free(ctx.parser);
|
|
return r;
|
|
}
|
|
|
|
RZ_API bool rz_serialize_flag_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzFlag *flag, RZ_NULLABLE RzSerializeResultInfo *res) {
|
|
rz_flag_unset_all(flag);
|
|
|
|
const char *str = sdb_const_get(db, "base", NULL);
|
|
if (!str) {
|
|
RZ_SERIALIZE_ERR(res, "flag base key is missing");
|
|
return false;
|
|
}
|
|
flag->base = strtoll(str, NULL, 0);
|
|
|
|
str = sdb_const_get(db, "realnames", 0);
|
|
if (!str) {
|
|
RZ_SERIALIZE_ERR(res, "flag realnames key is missing");
|
|
return false;
|
|
}
|
|
flag->realnames = strtoul(str, NULL, 0) ? true : false;
|
|
|
|
Sdb *spaces_db = sdb_ns(db, "spaces", false);
|
|
if (!spaces_db) {
|
|
RZ_SERIALIZE_ERR(res, "missing spaces namespace");
|
|
return false;
|
|
}
|
|
if (!rz_serialize_spaces_load(spaces_db, &flag->spaces, false, res)) {
|
|
return false;
|
|
}
|
|
|
|
Sdb *tags_db = sdb_ns(db, "tags", false);
|
|
if (!tags_db) {
|
|
RZ_SERIALIZE_ERR(res, "missing tags namespace");
|
|
return false;
|
|
}
|
|
sdb_copy(tags_db, flag->tags);
|
|
|
|
Sdb *zones_db = sdb_ns(db, "zones", false);
|
|
if (!zones_db) {
|
|
RZ_SERIALIZE_ERR(res, "missing zones namespace");
|
|
return false;
|
|
}
|
|
rz_flag_zone_reset(flag);
|
|
if (!rz_serialize_flag_zones_load(zones_db, flag->zones, res)) {
|
|
return false;
|
|
}
|
|
|
|
Sdb *flags_db = sdb_ns(db, "flags", false);
|
|
if (!flags_db) {
|
|
RZ_SERIALIZE_ERR(res, "missing flags sub-namespace");
|
|
return false;
|
|
}
|
|
if (!load_flags(flags_db, flag)) {
|
|
RZ_SERIALIZE_ERR(res, "failed to parse a flag json");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|