From 4dd31de76614cb8ea1fb975676bd9b44179ac5a3 Mon Sep 17 00:00:00 2001 From: NewtronReal Date: Sat, 11 Jul 2026 00:08:18 +0530 Subject: [PATCH 1/3] adding test cases cleanup Cleanup Cleanup Format fix --- librz/diff/unified_diff.c | 113 ++++++++++++++++++++--- librz/include/rz_diff.h | 2 + test/unit/test_diff.c | 183 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+), 12 deletions(-) diff --git a/librz/diff/unified_diff.c b/librz/diff/unified_diff.c index 332f2dd4537..b0288cc8089 100644 --- a/librz/diff/unified_diff.c +++ b/librz/diff/unified_diff.c @@ -109,19 +109,13 @@ static inline void diff_unified_append_data(RzDiff *diff, const void *array, st3 rz_strbuf_appendf(sb, "%s\n", ecol); } -// Assumes that color is true, diffing lines, op->type is RZ_DIFF_OP_REPLACE -// and that the number of inserted lines is equal to the number of deleted -// lines. -static inline void diff_unified_lines_hl(RzDiff *diff, RzDiffOp *op, RzStrBuf *sb, char del_prefix, char ins_prefix) { +static inline void fill_char_bounds(RzDiff *diff, RzDiffOp *op, st32 *char_bounds, int num_bounds) { RzDiffMethodElemAt elem_at = diff->methods.elem_at; RzDiffMethodStringify stringify = diff->methods.stringify; int len = 0; const char *p; const void *elem; RzStrBuf tmp, tmp2; - const char *ecol = Color_RESET; - const char *ebgcol = Color_RESET_BG; - const void *a_array = diff->a; st32 a_beg = op->a_beg; if (a_beg < 0) { @@ -135,11 +129,6 @@ static inline void diff_unified_lines_hl(RzDiff *diff, RzDiffOp *op, RzStrBuf *s b_beg = 0; } st32 b_end = op->b_end; - - ut32 num_nl = count_newlines(diff, a_array, a_beg, a_end); - // + 1 just in case there's no nl at end - ut32 num_bounds = num_nl + 1; - st32 *char_bounds = malloc(sizeof(st32) * 2 * num_bounds); if (!char_bounds) { return; } @@ -202,6 +191,46 @@ static inline void diff_unified_lines_hl(RzDiff *diff, RzDiffOp *op, RzStrBuf *s } rz_strbuf_fini(&tmp); } +} + +// Assumes that color is true, diffing lines, op->type is RZ_DIFF_OP_REPLACE +// and that the number of inserted lines is equal to the number of deleted +// lines. +static inline void diff_unified_lines_hl(RzDiff *diff, RzDiffOp *op, RzStrBuf *sb, char del_prefix, char ins_prefix) { + RzDiffMethodElemAt elem_at = diff->methods.elem_at; + RzDiffMethodStringify stringify = diff->methods.stringify; + int len = 0; + const char *p; + const void *elem; + RzStrBuf tmp /*, tmp2*/; + const char *ecol = Color_RESET; + const char *ebgcol = Color_RESET_BG; + + const void *a_array = diff->a; + st32 a_beg = op->a_beg; + if (a_beg < 0) { + a_beg = 0; + } + st32 a_end = op->a_end; + + const void *b_array = diff->b; + st32 b_beg = op->b_beg; + if (b_beg < 0) { + b_beg = 0; + } + st32 b_end = op->b_end; + + ut32 num_nl = count_newlines(diff, a_array, a_beg, a_end); + // + 1 just in case there's no nl at end + ut32 num_bounds = num_nl + 1; + st32 *char_bounds = malloc(sizeof(st32) * 2 * num_bounds); + if (!char_bounds) { + return; + } + fill_char_bounds(diff, op, char_bounds, num_bounds); + + ut32 bounds_idx = 0; + bool newline = false; // Show deleted lines char prefix = del_prefix; @@ -408,6 +437,66 @@ RZ_API RZ_OWN char *rz_diff_unified_text(RZ_NONNULL RzDiff *diff, RZ_NULLABLE co return NULL; } +/** + * \brief stringifies the DiffOpLines + */ +RZ_API RZ_OWN char *rz_diff_op_stringify(RZ_NONNULL RzDiff *diff, RZ_NONNULL RzDiffOp *op, bool is_a) { + rz_return_val_if_fail(diff && op, rz_str_dup("")); + RzDiffMethodElemAt elem_at = diff->methods.elem_at; + RzDiffMethodStringify stringify = diff->methods.stringify; + int len = 0; + ut32 count = 0; + const char *p; + const void *elem; + RzStrBuf sb, tmp; + bool newline = false; + bool is_bytes = DIFF_IS_BYTES_METHOD(diff->methods); + const void *array = is_a ? diff->a : diff->b; + st32 beg = is_a ? op->a_beg : op->b_beg; + st32 end = is_a ? op->a_end : op->b_end; + size_t array_size = is_a ? diff->a_size : diff->b_size; + + if (beg < 0 || end < 0 || (size_t)beg >= array_size || (size_t)end >= array_size || end < beg) { + return rz_str_dup(""); + } + rz_strbuf_init(&sb); + for (st32 i = beg; i < end; ++i) { + if (newline || (is_bytes && count > 0 && !FAST_MOD64(count))) { + rz_strbuf_append(&sb, "\n"); + newline = false; + } + rz_strbuf_init(&tmp); + elem = elem_at(array, i); + stringify(elem, &tmp); + len = rz_strbuf_length(&tmp); + p = rz_strbuf_get(&tmp); + count += len; + if (len > 0 && p[len - 1] == '\n') { + len--; + newline = true; + } + rz_strbuf_append_n(&sb, p, len); + rz_strbuf_fini(&tmp); + } + rz_strbuf_appendf(&sb, "\n"); + char *res = rz_strbuf_drain_nofree(&sb); + rz_strbuf_fini(&sb); + return res; +} + +/** + * \brief Produces a list of grouped RzDiffOp based on the diff->method + */ +RZ_API RZ_OWN RzList /* *>*/ *rz_diff_unified_text_grouped(RZ_NONNULL RzDiff *diff) { + rz_return_val_if_fail(diff && diff->methods.elem_at && diff->methods.stringify, NULL); + RzList *groups = NULL; + + bool is_bytes = DIFF_IS_BYTES_METHOD(diff->methods); + + groups = rz_diff_opcodes_grouped_new(diff, is_bytes ? RZ_DIFF_DEFAULT_N_GROUPS_BYTES : RZ_DIFF_DEFAULT_N_GROUPS); + return groups; +} + /** * \brief Produces a diff output to convert A in B in a JSON format. * diff --git a/librz/include/rz_diff.h b/librz/include/rz_diff.h index 03f43aa3915..df2963842e3 100644 --- a/librz/include/rz_diff.h +++ b/librz/include/rz_diff.h @@ -88,6 +88,8 @@ RZ_API bool rz_diff_ratio(RZ_NONNULL RzDiff *diff, RZ_NONNULL double *result); RZ_API bool rz_diff_sizes_ratio(RZ_NONNULL RzDiff *diff, RZ_NONNULL double *result); RZ_API RZ_OWN char *rz_diff_unified_text(RZ_NONNULL RzDiff *diff, RZ_NULLABLE const char *from, RZ_NULLABLE const char *to, bool show_time, bool color); +RZ_API RZ_OWN RzList /* *>*/ *rz_diff_unified_text_grouped(RZ_NONNULL RzDiff *diff); +RZ_API RZ_OWN char *rz_diff_op_stringify(RZ_NONNULL RzDiff *diff, RZ_NONNULL RzDiffOp *op, bool is_a); RZ_API RZ_OWN PJ *rz_diff_unified_json(RZ_NONNULL RzDiff *diff, RZ_NULLABLE const char *from, RZ_NULLABLE const char *to, bool show_time); /* Distances algorithms */ diff --git a/test/unit/test_diff.c b/test/unit/test_diff.c index fe72c1f7c88..dc5674d52d6 100644 --- a/test/unit/test_diff.c +++ b/test/unit/test_diff.c @@ -1163,6 +1163,188 @@ bool test_rz_diff_long_common_prefix(void) { mu_end; } +bool test_rz_diff_op_stringify(void) { + + // clang-format off + const char *a = "" + "This part of the\n" + "document has stayed the\n" + "same from version to\n" + "version. It shouldn't\n" + "be shown if it doesn't\n" + "change. Otherwise, that\n" + "would not be helping to\n" + "compress the size of the\n" + "changes.\n" + "\n" + "This paragraph contains\n" + "text that is outdated.\n" + "It will be deleted in the\n" + "near future.\n" + "\n" + "It is important to spell\n" + "check this dokument. On\n" + "the other hand, a\n" + "misspelled word isn't\n" + "the end of the world.\n" + "Nothing in the rest of\n" + "this paragraph needs to\n" + "be changed. Things can\n" + "be added after it."; + + const char *b = "" + "This is an important\n" + "notice! It should\n" + "therefore be located at\n" + "the beginning of this\n" + "document!\n" + "\n" + "This part of the\n" + "document has stayed the\n" + "same from version to\n" + "version. It shouldn't\n" + "be shown if it doesn't\n" + "change. Otherwise, that\n" + "would not be helping to\n" + "compress the size of the\n" + "changes.\n" + "\n" + "It is important to spell\n" + "check this document. On\n" + "the other hand, a\n" + "misspelled word isn't\n" + "the end of the world.\n" + "Nothing in the rest of\n" + "this paragraph needs to\n" + "be changed. Things can\n" + "be added after it.\n" + "\n" + "This paragraph contains\n" + "important new additions\n" + "to this document."; + + const char *expected = + "--INSERTED--\n" + "This is an important\n" + "notice! It should\n" + "therefore be located at\n" + "the beginning of this\n" + "document!\n" + "\n" + "\n" + "-----\n" + "--EQUAL--\n" + "This part of the\n" + "document has stayed the\n" + "same from version to\n" + "\n" + "-----\n" + "--EQUAL--\n" + "compress the size of the\n" + "changes.\n" + "\n" + "\n" + "-----\n" + "--REMOVED--\n" + "This paragraph contains\n" + "text that is outdated.\n" + "It will be deleted in the\n" + "near future.\n" + "\n" + "\n" + "-----\n" + "--EQUAL--\n" + "It is important to spell\n" + "\n" + "-----\n" + "--REPLACED--\n" + "actual check this dokument. On\n" + "\n" + "replaced check this document. On\n" + "\n" + "-----\n" + "--EQUAL--\n" + "the other hand, a\n" + "misspelled word isn't\n" + "the end of the world.\n" + "Nothing in the rest of\n" + "this paragraph needs to\n" + "be changed. Things can\n" + "\n" + "-----\n" + "--REPLACED--\n" + "actual \n" + "replaced \n" + "-----\n"; + + // clang-format on + + RzDiff *diff = rz_diff_lines_new(a, b, NULL); + mu_assert_notnull(diff, "rz_diff_lines_new returned NULL"); + + RzList *groups = rz_diff_unified_text_grouped(diff); + mu_assert_notnull(groups, "rz_diff_unified_text_grouped returned NULL"); + + RzList *ops = NULL; + RzDiffOp *op = NULL; + RzListIter *itg = NULL; + RzListIter *ito = NULL; + + RzStrBuf result; + rz_strbuf_init(&result); + + rz_list_foreach (groups, itg, ops) { + rz_list_foreach (ops, ito, op) { + char *stringified = NULL; + + switch (op->type) { + case RZ_DIFF_OP_DELETE: { + stringified = rz_diff_op_stringify(diff, op, true); + rz_strbuf_appendf(&result, "--REMOVED--\n%s\n-----\n", stringified); + break; + } + + case RZ_DIFF_OP_EQUAL: { + stringified = rz_diff_op_stringify(diff, op, true); + rz_strbuf_appendf(&result, "--EQUAL--\n%s\n-----\n", stringified); + break; + } + + case RZ_DIFF_OP_INSERT: + stringified = rz_diff_op_stringify(diff, op, false); + rz_strbuf_appendf(&result, "--INSERTED--\n%s\n-----\n", stringified); + break; + + case RZ_DIFF_OP_REPLACE: + stringified = rz_diff_op_stringify(diff, op, true); + rz_strbuf_appendf(&result, "--REPLACED--\nactual %s\n", stringified); + free(stringified); + + stringified = rz_diff_op_stringify(diff, op, false); + rz_strbuf_appendf(&result, "replaced %s\n-----\n", stringified); + break; + + default: + break; + } + + free(stringified); + } + } + + char *res = rz_strbuf_drain_nofree(&result); + + mu_assert_notnull(res, "rz result null"); + mu_assert_streq(res, expected, "rz result not equal"); + + free(res); + rz_strbuf_fini(&result); + rz_list_free(groups); + rz_diff_free(diff); + + mu_end; +} + int all_tests() { mu_run_test(test_rz_diff_distances); mu_run_test(test_rz_diff_unified_lines); @@ -1192,6 +1374,7 @@ int all_tests() { mu_run_test(test_rz_diff_block_shift); mu_run_test(test_rz_diff_growing_buffer); mu_run_test(test_rz_diff_long_common_prefix); + mu_run_test(test_rz_diff_op_stringify); return tests_passed != tests_run; } From 7f4a22e26ac536907bab30db45c81297d8539c64 Mon Sep 17 00:00:00 2001 From: NewtronReal Date: Sat, 11 Jul 2026 22:48:10 +0530 Subject: [PATCH 2/3] Added partial and perfect gui colors changes changes --- librz/cons/d/sepia | 3 +++ librz/cons/pal.c | 4 ++++ librz/core/cmd_descs/cmd_descs.c | 2 ++ librz/core/cmd_descs/cmd_eval.yaml | 4 ++++ librz/include/rz_cons.h | 6 ++++++ test/db/cmd/cmd_ec | 6 +++++- 6 files changed, 24 insertions(+), 1 deletion(-) diff --git a/librz/cons/d/sepia b/librz/cons/d/sepia index d126bc31af1..822bab67342 100644 --- a/librz/cons/d/sepia +++ b/librz/cons/d/sepia @@ -79,3 +79,6 @@ ec diff.unknown rgb:881798 ec diff.new rgb:c50f1f ec diff.match rgb:767676 ec diff.unmatch rgb:c19c00 + +ec gui.match.perfect rgb:00ff00 +ec gui.match.partial rgb:ff0000 diff --git a/librz/cons/pal.c b/librz/cons/pal.c index 902db3c1e86..bd04a2c5da7 100644 --- a/librz/cons/pal.c +++ b/librz/cons/pal.c @@ -79,6 +79,8 @@ static struct { { "diff.new", rz_offsetof(RzConsPrintablePalette, diff_new), rz_offsetof(RzConsPalette, diff_new) }, { "diff.match", rz_offsetof(RzConsPrintablePalette, diff_match), rz_offsetof(RzConsPalette, diff_match) }, { "diff.unmatch", rz_offsetof(RzConsPrintablePalette, diff_unmatch), rz_offsetof(RzConsPalette, diff_unmatch) }, + { "gui.match.perfect", rz_offsetof(RzConsPrintablePalette, gui_match_perfect), rz_offsetof(RzConsPalette, gui_match_perfect) }, + { "gui.match.partial", rz_offsetof(RzConsPrintablePalette, gui_match_partial), rz_offsetof(RzConsPalette, gui_match_partial) }, { "gui.cflow", rz_offsetof(RzConsPrintablePalette, gui_cflow), rz_offsetof(RzConsPalette, gui_cflow) }, { "gui.dataoffset", rz_offsetof(RzConsPrintablePalette, gui_dataoffset), rz_offsetof(RzConsPalette, gui_dataoffset) }, @@ -242,6 +244,8 @@ RZ_API void rz_cons_pal_init(RzConsContext *ctx) { ctx->cpal.diff_new = (RzColor)RzColor_RED; ctx->cpal.diff_match = (RzColor)RzColor_GRAY; ctx->cpal.diff_unmatch = (RzColor)RzColor_YELLOW; + ctx->cpal.gui_match_partial = (RzColor)RzColor_RED; + ctx->cpal.gui_match_perfect = (RzColor)RzColor_GREEN; rz_cons_pal_free(ctx); ctx->pal.reset = Color_RESET; // reset is not user accessible, const char* is ok diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index 01391ad1207..58f5b1c01ae 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -12280,6 +12280,8 @@ static const RzCmdDescDetailEntry cmd_eval_color_list_help_Color_space_Palette_s { .text = "diff.new", .arg_str = NULL, .comment = "Color for new diff" }, { .text = "diff.match", .arg_str = NULL, .comment = "Color for matched diff" }, { .text = "diff.unmatch", .arg_str = NULL, .comment = "Color for unmatched diff" }, + { .text = "gui.match.perfect", .arg_str = NULL, .comment = "Color for perfectly matched GUI diff" }, + { .text = "gui.match.partial", .arg_str = NULL, .comment = "Color for partially matched GUI diff" }, { .text = "gui.cflow", .arg_str = NULL, .comment = "Color for GUI control flow" }, { .text = "gui.dataoffset", .arg_str = NULL, .comment = "Color for GUI data offset" }, { .text = "gui.background", .arg_str = NULL, .comment = "Color for GUI background" }, diff --git a/librz/core/cmd_descs/cmd_eval.yaml b/librz/core/cmd_descs/cmd_eval.yaml index cf4ad4f0991..48d15ca7243 100644 --- a/librz/core/cmd_descs/cmd_eval.yaml +++ b/librz/core/cmd_descs/cmd_eval.yaml @@ -200,6 +200,10 @@ commands: comment: "Color for matched diff" - text: "diff.unmatch" comment: "Color for unmatched diff" + - text: "gui.match.perfect" + comment: "Color for perfectly matched GUI diff" + - text: "gui.match.partial" + comment: "Color for partially matched GUI diff" - text: "gui.cflow" comment: "Color for GUI control flow" - text: "gui.dataoffset" diff --git a/librz/include/rz_cons.h b/librz/include/rz_cons.h index 9a4cfc120e9..352130ba323 100644 --- a/librz/include/rz_cons.h +++ b/librz/include/rz_cons.h @@ -266,10 +266,14 @@ typedef struct rz_cons_palette_t { RzColor graph_ujump; RzColor graph_traced; RzColor graph_current; + + /* Diff colors */ RzColor diff_match; RzColor diff_unmatch; RzColor diff_unknown; RzColor diff_new; + RzColor gui_match_partial; + RzColor gui_match_perfect; } RzConsPalette; typedef struct rz_cons_printable_palette_t { @@ -342,6 +346,8 @@ typedef struct rz_cons_printable_palette_t { char *diff_unmatch; char *diff_unknown; char *diff_new; + char *gui_match_perfect; + char *gui_match_partial; char *graph_true; char *graph_false; char *graph_ujump; diff --git a/test/db/cmd/cmd_ec b/test/db/cmd/cmd_ec index 177e54c8a10..8f25f67d320 100644 --- a/test/db/cmd/cmd_ec +++ b/test/db/cmd/cmd_ec @@ -7,7 +7,7 @@ ecc~gui_background ecc foo_~gui_background EOF EXPECT=< Date: Sun, 12 Jul 2026 19:53:17 +0530 Subject: [PATCH 3/3] clipping fixed chaged test files --- librz/diff/unified_diff.c | 6 +- test/unit/test_diff.c | 112 ++++++++++++++++++++------------------ 2 files changed, 62 insertions(+), 56 deletions(-) diff --git a/librz/diff/unified_diff.c b/librz/diff/unified_diff.c index b0288cc8089..f8aa68168f6 100644 --- a/librz/diff/unified_diff.c +++ b/librz/diff/unified_diff.c @@ -454,11 +454,11 @@ RZ_API RZ_OWN char *rz_diff_op_stringify(RZ_NONNULL RzDiff *diff, RZ_NONNULL RzD const void *array = is_a ? diff->a : diff->b; st32 beg = is_a ? op->a_beg : op->b_beg; st32 end = is_a ? op->a_end : op->b_end; - size_t array_size = is_a ? diff->a_size : diff->b_size; - if (beg < 0 || end < 0 || (size_t)beg >= array_size || (size_t)end >= array_size || end < beg) { - return rz_str_dup(""); + if (beg < 0) { + beg = 0; } + rz_strbuf_init(&sb); for (st32 i = beg; i < end; ++i) { if (newline || (is_bytes && count > 0 && !FAST_MOD64(count))) { diff --git a/test/unit/test_diff.c b/test/unit/test_diff.c index dc5674d52d6..2166595171b 100644 --- a/test/unit/test_diff.c +++ b/test/unit/test_diff.c @@ -1223,59 +1223,65 @@ bool test_rz_diff_op_stringify(void) { "important new additions\n" "to this document."; - const char *expected = - "--INSERTED--\n" - "This is an important\n" - "notice! It should\n" - "therefore be located at\n" - "the beginning of this\n" - "document!\n" - "\n" - "\n" - "-----\n" - "--EQUAL--\n" - "This part of the\n" - "document has stayed the\n" - "same from version to\n" - "\n" - "-----\n" - "--EQUAL--\n" - "compress the size of the\n" - "changes.\n" - "\n" - "\n" - "-----\n" - "--REMOVED--\n" - "This paragraph contains\n" - "text that is outdated.\n" - "It will be deleted in the\n" - "near future.\n" - "\n" - "\n" - "-----\n" - "--EQUAL--\n" - "It is important to spell\n" - "\n" - "-----\n" - "--REPLACED--\n" - "actual check this dokument. On\n" - "\n" - "replaced check this document. On\n" - "\n" - "-----\n" - "--EQUAL--\n" - "the other hand, a\n" - "misspelled word isn't\n" - "the end of the world.\n" - "Nothing in the rest of\n" - "this paragraph needs to\n" - "be changed. Things can\n" - "\n" - "-----\n" - "--REPLACED--\n" - "actual \n" - "replaced \n" - "-----\n"; + const char *expected = + "--INSERTED--\n" + "This is an important\n" + "notice! It should\n" + "therefore be located at\n" + "the beginning of this\n" + "document!\n" + "\n" + "\n" + "-----\n" + "--EQUAL--\n" + "This part of the\n" + "document has stayed the\n" + "same from version to\n" + "\n" + "-----\n" + "--EQUAL--\n" + "compress the size of the\n" + "changes.\n" + "\n" + "\n" + "-----\n" + "--REMOVED--\n" + "This paragraph contains\n" + "text that is outdated.\n" + "It will be deleted in the\n" + "near future.\n" + "\n" + "\n" + "-----\n" + "--EQUAL--\n" + "It is important to spell\n" + "\n" + "-----\n" + "--REPLACED--\n" + "actual check this dokument. On\n" + "\n" + "replaced check this document. On\n" + "\n" + "-----\n" + "--EQUAL--\n" + "the other hand, a\n" + "misspelled word isn't\n" + "the end of the world.\n" + "Nothing in the rest of\n" + "this paragraph needs to\n" + "be changed. Things can\n" + "\n" + "-----\n" + "--REPLACED--\n" + "actual be added after it.\n" + "\n" + "replaced be added after it.\n" + "\n" + "This paragraph contains\n" + "important new additions\n" + "to this document.\n" + "\n" + "-----\n"; // clang-format on