Serialize seek history (#3838)
* Save/load seek history ##projects * Add unit test for seek history serialization * Bump project version to 15 for seek history serialization
This commit is contained in:
parent
9872eacdb5
commit
700c7d898b
12 changed files with 538 additions and 3 deletions
|
|
@ -533,7 +533,7 @@ RZ_API bool rz_project_migrate_v12_v13(RzProject *prj, RzSerializeResultInfo *re
|
|||
// --
|
||||
// Migration 13 -> 14
|
||||
//
|
||||
// Changes from <commit hash not yet known>:
|
||||
// Changes from 8e29b959b86a35bbbfed599989f077dba6e0ebd5:
|
||||
// Removed {stack,reg} from "/core/analysis/functions/vars"
|
||||
// and converted into storage object { ..., storage: { type: ... } }
|
||||
|
||||
|
|
@ -592,6 +592,21 @@ RZ_API bool rz_project_migrate_v13_v14(RzProject *prj, RzSerializeResultInfo *re
|
|||
return true;
|
||||
}
|
||||
|
||||
// --
|
||||
// Migration 14 -> 15
|
||||
//
|
||||
// Changes from <commit hash not yet known>:
|
||||
// Added serialization functionality for seek history
|
||||
// New namespace: /core/seek
|
||||
|
||||
RZ_API bool rz_project_migrate_v14_v15(RzProject *prj, RzSerializeResultInfo *res) {
|
||||
Sdb *core_db;
|
||||
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
|
||||
sdb_ns(core_db, "seek", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) = {
|
||||
rz_project_migrate_v1_v2,
|
||||
rz_project_migrate_v2_v3,
|
||||
|
|
@ -606,6 +621,7 @@ static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) =
|
|||
rz_project_migrate_v11_v12,
|
||||
rz_project_migrate_v12_v13,
|
||||
rz_project_migrate_v13_v14,
|
||||
rz_project_migrate_v14_v15,
|
||||
};
|
||||
|
||||
/// Migrate the given project to the current version in-place
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
* /flags => see flag.c
|
||||
* /analysis => see analysis.c
|
||||
* /file => see below
|
||||
* /seek => see serialize_core_seek.c
|
||||
* offset=<offset>
|
||||
* blocksize=<blocksize>
|
||||
*/
|
||||
|
|
@ -26,6 +27,7 @@ RZ_API void rz_serialize_core_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core,
|
|||
rz_serialize_flag_save(sdb_ns(db, "flags", true), core->flags);
|
||||
rz_serialize_analysis_save(sdb_ns(db, "analysis", true), core->analysis);
|
||||
rz_serialize_debug_save(sdb_ns(db, "debug", true), core->dbg);
|
||||
rz_serialize_core_seek_save(sdb_ns(db, "seek", true), core);
|
||||
|
||||
char buf[0x20];
|
||||
if (snprintf(buf, sizeof(buf), "0x%" PFMT64x, core->offset) < 0) {
|
||||
|
|
@ -79,6 +81,7 @@ RZ_API bool rz_serialize_core_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core,
|
|||
SUB("flags", rz_serialize_flag_load(subdb, core->flags, res));
|
||||
SUB("analysis", rz_serialize_analysis_load(subdb, core->analysis, res));
|
||||
SUB("debug", rz_serialize_debug_load(subdb, core->dbg, res));
|
||||
SUB("seek", rz_serialize_core_seek_load(subdb, core, res));
|
||||
|
||||
const char *str = sdb_const_get(db, "offset", 0);
|
||||
if (!str || !*str) {
|
||||
|
|
@ -256,3 +259,220 @@ static bool file_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE c
|
|||
RZ_SERIALIZE_ERR(res, "failed to re-locate file referenced by project");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Serialize seek history state and save to a sdb
|
||||
*
|
||||
* \param db sdb to save the state
|
||||
* \param core RzCore instance to save from
|
||||
*/
|
||||
RZ_API void rz_serialize_core_seek_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core) {
|
||||
rz_return_if_fail(db && core);
|
||||
|
||||
RzList *list = rz_core_seek_list(core);
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
|
||||
RzListIter *iter;
|
||||
RzCoreSeekItem *undo;
|
||||
rz_list_foreach (list, iter, undo) {
|
||||
PJ *j = pj_new();
|
||||
if (!j) {
|
||||
goto err;
|
||||
}
|
||||
pj_o(j);
|
||||
pj_kn(j, "offset", undo->offset);
|
||||
pj_kn(j, "cursor", undo->cursor);
|
||||
pj_kb(j, "current", undo->is_current);
|
||||
pj_end(j);
|
||||
|
||||
char key[12];
|
||||
sdb_set(db, rz_strf(key, "%" PFMT32d, undo->idx), pj_string(j), 0);
|
||||
pj_free(j);
|
||||
}
|
||||
|
||||
err:
|
||||
rz_list_free(list);
|
||||
}
|
||||
|
||||
enum {
|
||||
SEEK_FIELD_OFFSET,
|
||||
SEEK_FIELD_CURSOR,
|
||||
SEEK_FIELD_CURRENT
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Create and initialize a JSON parser for seek history items
|
||||
* \return a RzKeyParser* for seek history items
|
||||
**/
|
||||
static RzKeyParser *seek_parser_new(void) {
|
||||
RzKeyParser *parser = rz_key_parser_new();
|
||||
if (!parser) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rz_key_parser_add(parser, "offset", SEEK_FIELD_OFFSET);
|
||||
rz_key_parser_add(parser, "cursor", SEEK_FIELD_CURSOR);
|
||||
rz_key_parser_add(parser, "current", SEEK_FIELD_CURRENT);
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
RzCore *core;
|
||||
RzKeyParser *parser;
|
||||
char *current_key;
|
||||
RzVector /*<RzCoreSeekItem>*/ *vec;
|
||||
} SeekLoadCtx;
|
||||
|
||||
/**
|
||||
* \brief Load a single seek history item
|
||||
* \param ctx context for loading the item
|
||||
* \param k sdb item key
|
||||
* \param v sdb item value (expected to be JSON)
|
||||
**/
|
||||
static bool seek_load_item(SeekLoadCtx *ctx, const char *k, const char *v) {
|
||||
bool ret = false;
|
||||
char *json_str = strdup(v);
|
||||
if (!json_str) {
|
||||
return true;
|
||||
}
|
||||
RzJson *json = rz_json_parse(json_str);
|
||||
if (!json || json->type != RZ_JSON_OBJECT) {
|
||||
goto out_free_str;
|
||||
}
|
||||
RzCoreSeekItem seek_item = { 0 };
|
||||
|
||||
RZ_KEY_PARSER_JSON(ctx->parser, json, child, {
|
||||
case SEEK_FIELD_OFFSET:
|
||||
if (child->type != RZ_JSON_INTEGER) {
|
||||
break;
|
||||
}
|
||||
seek_item.offset = child->num.u_value;
|
||||
break;
|
||||
case SEEK_FIELD_CURSOR:
|
||||
if (child->type != RZ_JSON_INTEGER) {
|
||||
break;
|
||||
}
|
||||
seek_item.cursor = child->num.s_value;
|
||||
break;
|
||||
case SEEK_FIELD_CURRENT:
|
||||
if (child->type != RZ_JSON_BOOLEAN) {
|
||||
break;
|
||||
}
|
||||
seek_item.is_current = child->num.u_value;
|
||||
break;
|
||||
})
|
||||
|
||||
if (seek_item.is_current && !ctx->current_key) {
|
||||
// The offset is serialized by the core, so ignore the information from the seek history
|
||||
// But the cursor position isn't serialized otherwise
|
||||
ctx->core->print->cur = seek_item.cursor;
|
||||
// Switch to the vector of redos
|
||||
ctx->vec = &ctx->core->seek_history.redos;
|
||||
// Remember we've found the current seek
|
||||
ctx->current_key = strdup(k);
|
||||
} else {
|
||||
if (seek_item.is_current) {
|
||||
// Warn about this additional "current" seek
|
||||
RZ_LOG_WARN("core: Seek history item \"%s\" marked as current, but current already found at \"%s\"!", k, ctx->current_key);
|
||||
}
|
||||
rz_vector_push(ctx->vec, &seek_item);
|
||||
}
|
||||
ret = true;
|
||||
|
||||
rz_json_free(json);
|
||||
out_free_str:
|
||||
free(json_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __cmp_num_asc(const void *a, const void *b) {
|
||||
const SdbKv *ka = a, *kb = b;
|
||||
// Parse as signed ints but don't bother witb error detection, it'll sort bad and that's it
|
||||
long ia = strtol(sdbkv_key(ka), NULL, 10);
|
||||
long ib = strtol(sdbkv_key(kb), NULL, 10);
|
||||
return ia > ib;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Deserialize seek history state from an sdb
|
||||
*
|
||||
* \param db sdb to load state from
|
||||
* \param core RzCore instance to load into
|
||||
* \param res RzSerializeResultInfo to store info/errors/warnings
|
||||
* \return true if successful, false otherwise
|
||||
*/
|
||||
RZ_API bool rz_serialize_core_seek_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE RzSerializeResultInfo *res) {
|
||||
rz_return_val_if_fail(db && core, false);
|
||||
|
||||
bool ret = true;
|
||||
RzKeyParser *seek_parser = seek_parser_new();
|
||||
if (!seek_parser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort by (numeric) key
|
||||
SdbList *db_list = sdb_foreach_list(db, false);
|
||||
if (!db_list) {
|
||||
ret = false;
|
||||
goto out_free_parser;
|
||||
}
|
||||
ls_sort(db_list, __cmp_num_asc);
|
||||
|
||||
// Clear the current history
|
||||
rz_core_seek_reset(core);
|
||||
core->seek_history.saved_set = false;
|
||||
|
||||
SdbKv *kv;
|
||||
SdbListIter *it;
|
||||
SeekLoadCtx ctx = {
|
||||
.core = core,
|
||||
.parser = seek_parser,
|
||||
.current_key = NULL,
|
||||
.vec = &core->seek_history.undos,
|
||||
};
|
||||
bool parsed = true;
|
||||
ls_foreach (db_list, it, kv) {
|
||||
parsed &= seek_load_item(&ctx, sdbkv_key(kv), sdbkv_value(kv));
|
||||
}
|
||||
ret &= parsed;
|
||||
if (!parsed) {
|
||||
RZ_SERIALIZE_ERR(res, "failed to parse seek history offset from json");
|
||||
}
|
||||
|
||||
// Reverse the redo vector, which has been deserialized from oldest to youngest entry
|
||||
// but should be ordered from youngest to oldest
|
||||
// (so the entry closest to the current seek can be pushed/popped)
|
||||
bool reversed = true;
|
||||
size_t rlen = rz_vector_len(&core->seek_history.redos);
|
||||
for (size_t i = 0; i < rlen / 2; i++) {
|
||||
// Swap with the mirror item from the end of the vector
|
||||
reversed &= rz_vector_swap(&core->seek_history.redos, i, rlen - 1 - i);
|
||||
}
|
||||
ret &= reversed;
|
||||
if (!reversed) {
|
||||
RZ_SERIALIZE_ERR(res, "failed to reorder seek history redo items");
|
||||
}
|
||||
|
||||
// Increase cfg.seek.histsize as needed
|
||||
size_t ulen = rz_vector_len(&core->seek_history.undos);
|
||||
if (SZT_ADD_OVFCHK(ulen, rlen)) {
|
||||
ret = false;
|
||||
RZ_SERIALIZE_ERR(res, "failed to adjust cfg.seek.histsize");
|
||||
rz_goto_if_reached(out_free_list);
|
||||
}
|
||||
ut64 histsize = rz_config_get_i(core->config, "cfg.seek.histsize");
|
||||
if (histsize != 0 && histsize < ulen + rlen) {
|
||||
RZ_LOG_WARN("core: Loaded project seek history exceeds cfg.seek.histsize, increasing that limit.");
|
||||
rz_config_set_i(core->config, "cfg.seek.histsize", ulen + rlen);
|
||||
}
|
||||
|
||||
out_free_list:
|
||||
free(ctx.current_key);
|
||||
ls_free(db_list);
|
||||
out_free_parser:
|
||||
rz_key_parser_free(seek_parser);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1279,6 +1279,10 @@ RZ_API void rz_serialize_core_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core,
|
|||
RZ_API bool rz_serialize_core_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, bool load_bin_io,
|
||||
RZ_NULLABLE const char *prj_file, RZ_NULLABLE RzSerializeResultInfo *res);
|
||||
|
||||
RZ_API void rz_serialize_core_seek_save(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core);
|
||||
|
||||
RZ_API bool rz_serialize_core_seek_load(RZ_NONNULL Sdb *db, RZ_NONNULL RzCore *core, RZ_NULLABLE RzSerializeResultInfo *res);
|
||||
|
||||
/**
|
||||
* \brief Load a project and print info and errors
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RZ_PROJECT_VERSION 14
|
||||
#define RZ_PROJECT_VERSION 15
|
||||
|
||||
typedef Sdb RzProject;
|
||||
|
||||
|
|
@ -57,6 +57,9 @@ RZ_API bool rz_project_migrate_v8_v9(RzProject *prj, RzSerializeResultInfo *res)
|
|||
RZ_API bool rz_project_migrate_v9_v10(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate_v10_v11(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate_v11_v12(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate_v12_v13(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate_v13_v14(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate_v14_v15(RzProject *prj, RzSerializeResultInfo *res);
|
||||
RZ_API bool rz_project_migrate(RzProject *prj, unsigned long version, RzSerializeResultInfo *res);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -142,6 +142,14 @@ RZ_API void *rz_vector_push(RzVector *vec, void *x);
|
|||
// like rz_vector_insert for the beginning of vec
|
||||
RZ_API void *rz_vector_push_front(RzVector *vec, void *x);
|
||||
|
||||
/**
|
||||
* \brief Swap two elements of the vector
|
||||
* \param index_a index of the first element to swap
|
||||
* \param index_b index of the second element to swap
|
||||
* \return true if the swap succeeded
|
||||
**/
|
||||
RZ_API bool rz_vector_swap(RzVector *vec, size_t index_a, size_t index_b);
|
||||
|
||||
// make sure the capacity is at least capacity.
|
||||
RZ_API void *rz_vector_reserve(RzVector *vec, size_t capacity);
|
||||
|
||||
|
|
|
|||
|
|
@ -226,6 +226,21 @@ RZ_API void *rz_vector_push_front(RzVector *vec, void *x) {
|
|||
return rz_vector_insert(vec, 0, x);
|
||||
}
|
||||
|
||||
RZ_API bool rz_vector_swap(RzVector *vec, size_t index_a, size_t index_b) {
|
||||
rz_return_val_if_fail(vec && index_a < vec->len && index_b < vec->len, false);
|
||||
ut8 *tmp = malloc(vec->elem_size);
|
||||
if (!tmp) {
|
||||
return false;
|
||||
}
|
||||
void *elem_a = rz_vector_index_ptr(vec, index_a);
|
||||
void *elem_b = rz_vector_index_ptr(vec, index_b);
|
||||
memcpy(tmp, elem_a, vec->elem_size);
|
||||
memcpy(elem_a, elem_b, vec->elem_size);
|
||||
memcpy(elem_b, tmp, vec->elem_size);
|
||||
free(tmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
RZ_API void *rz_vector_reserve(RzVector *vec, size_t capacity) {
|
||||
rz_return_val_if_fail(vec, NULL);
|
||||
if (vec->capacity < capacity) {
|
||||
|
|
|
|||
|
|
@ -372,6 +372,7 @@ Detailed project load info:
|
|||
project migrated from version 11 to 12.
|
||||
project migrated from version 12 to 13.
|
||||
project migrated from version 13 to 14.
|
||||
project migrated from version 14 to 15.
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
|
|
|||
|
|
@ -535,6 +535,23 @@ static bool test_migrate_v9_v10_v11_stack_vars_sp() {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_migrate_v14_v15() {
|
||||
RzProject *prj = rz_project_load_file_raw("prj/v14-float_ex1_hightec.rzdb.gz");
|
||||
mu_assert_notnull(prj, "load raw project");
|
||||
RzSerializeResultInfo *res = rz_serialize_result_info_new();
|
||||
bool s = rz_project_migrate_v14_v15(prj, res);
|
||||
mu_assert_true(s, "migrate success");
|
||||
|
||||
Sdb *core_db = sdb_ns(prj, "core", false);
|
||||
mu_assert_notnull(core_db, "core ns");
|
||||
Sdb *seek_db = sdb_ns(core_db, "seek", false);
|
||||
mu_assert_notnull(seek_db, "seek ns");
|
||||
|
||||
rz_serialize_result_info_free(res);
|
||||
rz_project_free(prj);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
/// Load project of given version from file into core and check the log for migration success messages
|
||||
#define BEGIN_LOAD_TEST(core, version, file) \
|
||||
do { \
|
||||
|
|
@ -845,7 +862,6 @@ static bool test_load_v9_v10_stack_vars_sp(int version, const char *prj_file) {
|
|||
|
||||
rz_core_free(core);
|
||||
mu_end;
|
||||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_load_v12() {
|
||||
|
|
@ -877,6 +893,34 @@ static bool test_load_v14() {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_load_v15_seek_history() {
|
||||
RzCore *core = rz_core_new();
|
||||
|
||||
// enable the cursor so we can check the deserialized value
|
||||
rz_print_set_cursor(core->print, true, 0, 0);
|
||||
|
||||
BEGIN_LOAD_TEST(core, 15, "prj/v15-seek-history.rzdb");
|
||||
|
||||
mu_assert_eq(rz_vector_len(&core->seek_history.undos), 1, "bad number of undos");
|
||||
RzCoreSeekItem *item = rz_vector_index_ptr(&core->seek_history.undos, 0);
|
||||
mu_assert_eq(item->offset, 0x5ae0, "bad undo offset");
|
||||
mu_assert_eq(item->cursor, 1, "bad undo cursor");
|
||||
|
||||
mu_assert_eq(rz_vector_len(&core->seek_history.redos), 2, "bad number of redos");
|
||||
item = rz_vector_index_ptr(&core->seek_history.redos, 1);
|
||||
mu_assert_eq(item->offset, 0x5b00, "bad first redo offset");
|
||||
mu_assert_eq(item->cursor, 3, "bad first redo cursor");
|
||||
item = rz_vector_index_ptr(&core->seek_history.redos, 0);
|
||||
mu_assert_eq(item->offset, 0x5b10, "bad second redo offset");
|
||||
mu_assert_eq(item->cursor, 4, "bad second redo cursor");
|
||||
|
||||
// core offset not restored from current seek history item, so not checked
|
||||
mu_assert_eq(rz_print_get_cursor(core->print), 2, "bad current cursor");
|
||||
|
||||
rz_core_free(core);
|
||||
mu_end;
|
||||
}
|
||||
|
||||
int all_tests() {
|
||||
mu_run_test(test_migrate_v1_v2_noreturn);
|
||||
mu_run_test(test_migrate_v1_v2_noreturn_empty);
|
||||
|
|
@ -894,6 +938,7 @@ int all_tests() {
|
|||
mu_run_test(test_migrate_v9_v10_v11_stack_vars_bp);
|
||||
mu_run_test(test_migrate_v9_v10_v11_stack_vars_sp);
|
||||
mu_run_test(test_migrate_v2_v12);
|
||||
mu_run_test(test_migrate_v14_v15);
|
||||
mu_run_test(test_load_v1_noreturn);
|
||||
mu_run_test(test_load_v1_noreturn_empty);
|
||||
mu_run_test(test_load_v1_unknown_type);
|
||||
|
|
@ -913,6 +958,7 @@ int all_tests() {
|
|||
mu_run_test(test_load_v9_v10_stack_vars_sp, 10, "prj/v10-sp-vars.rzdb");
|
||||
mu_run_test(test_load_v12);
|
||||
mu_run_test(test_load_v14);
|
||||
mu_run_test(test_load_v15_seek_history);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
|
|
|
|||
86
test/prj/v15-seek-history.rzdb
Normal file
86
test/prj/v15-seek-history.rzdb
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/
|
||||
type=rizin rz-db project
|
||||
version=15
|
||||
|
||||
/core
|
||||
blocksize=0x100
|
||||
offset=0x5af0
|
||||
|
||||
/core/analysis
|
||||
|
||||
/core/analysis/blocks
|
||||
|
||||
/core/analysis/callables
|
||||
|
||||
/core/analysis/cc
|
||||
|
||||
/core/analysis/classes
|
||||
|
||||
/core/analysis/classes/attrs
|
||||
|
||||
/core/analysis/functions
|
||||
|
||||
/core/analysis/hints
|
||||
|
||||
/core/analysis/imports
|
||||
|
||||
/core/analysis/meta
|
||||
|
||||
/core/analysis/meta/spaces
|
||||
name=CS
|
||||
spacestack=["*"]
|
||||
|
||||
/core/analysis/meta/spaces/spaces
|
||||
bin=s
|
||||
|
||||
/core/analysis/noreturn
|
||||
|
||||
/core/analysis/types
|
||||
|
||||
/core/analysis/vars
|
||||
|
||||
/core/analysis/xrefs
|
||||
|
||||
/core/config
|
||||
asm.arch=x86
|
||||
asm.bits=64
|
||||
cfg.seek.histsize=63
|
||||
cfg.seek.silent=false
|
||||
cfg.wseek=false
|
||||
prj.compress=false
|
||||
prj.file=./test/prj/v15-seek-history.rzdb
|
||||
|
||||
/core/debug
|
||||
|
||||
/core/debug/breakpoints
|
||||
|
||||
/core/file
|
||||
relative=../bins/elf/ls
|
||||
|
||||
/core/flags
|
||||
base=0
|
||||
realnames=0
|
||||
|
||||
/core/flags/flags
|
||||
|
||||
/core/flags/spaces
|
||||
name=fs
|
||||
spacestack=["*"]
|
||||
|
||||
/core/flags/spaces/spaces
|
||||
classes=s
|
||||
relocs=s
|
||||
sections=s
|
||||
segments=s
|
||||
strings=s
|
||||
symbols=s
|
||||
|
||||
/core/flags/tags
|
||||
|
||||
/core/flags/zones
|
||||
|
||||
/core/seek
|
||||
-1={"offset":23264,"cursor":1,"current":false}
|
||||
0={"offset":23280,"cursor":2,"current":true}
|
||||
1={"offset":23296,"cursor":3,"current":false}
|
||||
2={"offset":23312,"cursor":4,"current":false}
|
||||
|
|
@ -87,6 +87,7 @@ if get_option('enable_tests')
|
|||
'serialize_config',
|
||||
'serialize_debug',
|
||||
'serialize_flag',
|
||||
'serialize_seek',
|
||||
'serialize_spaces',
|
||||
'serialize_types',
|
||||
'skiplist',
|
||||
|
|
|
|||
98
test/unit/test_serialize_seek.c
Normal file
98
test/unit/test_serialize_seek.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// SPDX-FileCopyrightText: 2023 Quentin Minster <quentin@minster.io>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_core.h>
|
||||
#include "minunit.h"
|
||||
#include "test_sdb.h"
|
||||
|
||||
Sdb *get_ref_sdb() {
|
||||
Sdb *ref_sdb = sdb_new0();
|
||||
|
||||
sdb_set(ref_sdb, "-1", "{\"offset\":16,\"cursor\":1,\"current\":false}", 0);
|
||||
sdb_set(ref_sdb, "0", "{\"offset\":32,\"cursor\":2,\"current\":true}", 0);
|
||||
sdb_set(ref_sdb, "1", "{\"offset\":48,\"cursor\":3,\"current\":false}", 0);
|
||||
sdb_set(ref_sdb, "2", "{\"offset\":64,\"cursor\":4,\"current\":false}", 0);
|
||||
|
||||
return ref_sdb;
|
||||
}
|
||||
|
||||
bool test_seek_serialize_save() {
|
||||
RzCore *core = rz_core_new();
|
||||
mu_assert_notnull(core, "core null");
|
||||
rz_core_file_open(core, "malloc://0x3000", RZ_PERM_R, 0);
|
||||
|
||||
bool sought = rz_core_seek(core, 0x10, false);
|
||||
rz_print_set_cursor(core->print, true, 0, 1);
|
||||
mu_assert_true(sought, "failed to seek 0x10");
|
||||
sought = rz_core_seek_and_save(core, 0x20, false);
|
||||
rz_print_set_cursor(core->print, true, 0, 2);
|
||||
mu_assert_true(sought, "failed to seek 0x20");
|
||||
sought = rz_core_seek_and_save(core, 0x30, false);
|
||||
rz_print_set_cursor(core->print, true, 0, 3);
|
||||
mu_assert_true(sought, "failed to seek 0x30");
|
||||
sought = rz_core_seek_and_save(core, 0x40, false);
|
||||
rz_print_set_cursor(core->print, true, 0, 4);
|
||||
mu_assert_true(sought, "failed to seek 0x40");
|
||||
sought = rz_core_seek_undo(core);
|
||||
mu_assert_true(sought, "failed first undo seek");
|
||||
sought = rz_core_seek_undo(core);
|
||||
mu_assert_true(sought, "failed second undo seek");
|
||||
|
||||
Sdb *save_sdb = sdb_new0();
|
||||
mu_assert_notnull(save_sdb, "sdb null");
|
||||
rz_serialize_core_seek_save(save_sdb, core);
|
||||
Sdb *ref = get_ref_sdb();
|
||||
mu_assert_notnull(ref, "ref sdb null");
|
||||
assert_sdb_eq(save_sdb, ref, "saved sdb not same");
|
||||
|
||||
rz_core_file_close(core->file);
|
||||
rz_core_free(core);
|
||||
sdb_free(save_sdb);
|
||||
sdb_free(ref);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
bool test_seek_serialize_load() {
|
||||
RzCore *core = rz_core_new();
|
||||
mu_assert_notnull(core, "core null");
|
||||
rz_core_file_open(core, "malloc://0x3000", RZ_PERM_R, 0);
|
||||
|
||||
// enable the cursor so we can check the deserialized value
|
||||
rz_print_set_cursor(core->print, true, 0, 0);
|
||||
|
||||
Sdb *ref = get_ref_sdb();
|
||||
Sdb *load_sdb = sdb_new0();
|
||||
rz_serialize_core_seek_load(ref, core, NULL);
|
||||
|
||||
mu_assert_eq(rz_vector_len(&core->seek_history.undos), 1, "bad number of undos");
|
||||
RzCoreSeekItem *item = rz_vector_index_ptr(&core->seek_history.undos, 0);
|
||||
mu_assert_eq(item->offset, 0x10, "bad undo offset");
|
||||
mu_assert_eq(item->cursor, 1, "bad undo cursor");
|
||||
|
||||
mu_assert_eq(rz_vector_len(&core->seek_history.redos), 2, "bad number of redos");
|
||||
item = rz_vector_index_ptr(&core->seek_history.redos, 1);
|
||||
mu_assert_eq(item->offset, 0x30, "bad first redo offset");
|
||||
mu_assert_eq(item->cursor, 3, "bad first redo cursor");
|
||||
item = rz_vector_index_ptr(&core->seek_history.redos, 0);
|
||||
mu_assert_eq(item->offset, 0x40, "bad second redo offset");
|
||||
mu_assert_eq(item->cursor, 4, "bad second redo cursor");
|
||||
|
||||
// core offset not restored from current seek history item, so not checked
|
||||
mu_assert_eq(rz_print_get_cursor(core->print), 2, "bad current cursor");
|
||||
|
||||
rz_core_file_close(core->file);
|
||||
rz_core_free(core);
|
||||
sdb_free(load_sdb);
|
||||
sdb_free(ref);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
int all_tests() {
|
||||
mu_run_test(test_seek_serialize_save);
|
||||
mu_run_test(test_seek_serialize_load);
|
||||
return tests_passed != tests_run;
|
||||
}
|
||||
|
||||
mu_main(all_tests)
|
||||
|
|
@ -629,6 +629,42 @@ static bool test_vector_push_front(void) {
|
|||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_vector_swap(void) {
|
||||
RzVector v;
|
||||
init_test_vector(&v, 3, 0, NULL, NULL);
|
||||
|
||||
rz_vector_swap(&v, 0, 2);
|
||||
mu_assert_eq(v.len, 3UL, "rz_vector_swap (valid indexes) => len == 3");
|
||||
ut32 e = *((ut32 *)rz_vector_index_ptr(&v, 0));
|
||||
mu_assert_eq(e, 2, "rz_vector_swap (valid indexes) => content");
|
||||
e = *((ut32 *)rz_vector_index_ptr(&v, 1));
|
||||
mu_assert_eq(e, 1, "rz_vector_swap (valid indexes) => old content");
|
||||
e = *((ut32 *)rz_vector_index_ptr(&v, 2));
|
||||
mu_assert_eq(e, 0, "rz_vector_swap (valid indexes) => content");
|
||||
|
||||
rz_vector_swap(&v, 2, 2);
|
||||
mu_assert_eq(v.len, 3UL, "rz_vector_swap (same index) => len == 3");
|
||||
e = *((ut32 *)rz_vector_index_ptr(&v, 0));
|
||||
mu_assert_eq(e, 2, "rz_vector_swap (same index) => old content");
|
||||
e = *((ut32 *)rz_vector_index_ptr(&v, 1));
|
||||
mu_assert_eq(e, 1, "rz_vector_swap (same index) => old content");
|
||||
e = *((ut32 *)rz_vector_index_ptr(&v, 2));
|
||||
mu_assert_eq(e, 0, "rz_vector_swap (same index) => content");
|
||||
|
||||
rz_vector_swap(&v, 3, 2);
|
||||
mu_assert_eq(v.len, 3UL, "rz_vector_swap (bad index) => len == 3");
|
||||
e = *((ut32 *)rz_vector_index_ptr(&v, 0));
|
||||
mu_assert_eq(e, 2, "rz_vector_swap (bad index) => old content");
|
||||
e = *((ut32 *)rz_vector_index_ptr(&v, 1));
|
||||
mu_assert_eq(e, 1, "rz_vector_swap (bad index) => old content");
|
||||
e = *((ut32 *)rz_vector_index_ptr(&v, 2));
|
||||
mu_assert_eq(e, 0, "rz_vector_swap (bad index) => old content");
|
||||
|
||||
rz_vector_clear(&v);
|
||||
|
||||
mu_end;
|
||||
}
|
||||
|
||||
static bool test_vector_reserve(void) {
|
||||
RzVector v;
|
||||
rz_vector_init(&v, 4, NULL, NULL);
|
||||
|
|
@ -1343,6 +1379,7 @@ static int all_tests(void) {
|
|||
mu_run_test(test_vector_pop_front);
|
||||
mu_run_test(test_vector_push);
|
||||
mu_run_test(test_vector_push_front);
|
||||
mu_run_test(test_vector_swap);
|
||||
mu_run_test(test_vector_reserve);
|
||||
mu_run_test(test_vector_shrink);
|
||||
mu_run_test(test_vector_flush);
|
||||
|
|
|
|||
Loading…
Reference in a new issue