From 8aeff2c287aea1a8673d78574a5709510a789640 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 3 Oct 2024 17:06:27 -0400 Subject: [PATCH 1/5] line-log: use diff_line_prefix() instead of custom helper Our local output_prefix() is exactly the same as the public diff_line_prefix() function. Let's just use that one, saving us a little bit of code. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- line-log.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/line-log.c b/line-log.c index 29cf66bdd1..63945c4729 100644 --- a/line-log.c +++ b/line-log.c @@ -897,16 +897,6 @@ static void print_line(const char *prefix, char first, fputs("\\ No newline at end of file\n", file); } -static const char *output_prefix(struct diff_options *opt) -{ - if (opt->output_prefix) { - struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data); - return sb->buf; - } else { - return ""; - } -} - static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range) { unsigned int i, j = 0; @@ -916,7 +906,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang struct diff_ranges *diff = &range->diff; struct diff_options *opt = &rev->diffopt; - const char *prefix = output_prefix(opt); + const char *prefix = diff_line_prefix(opt); const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET); const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO); const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO); @@ -1011,7 +1001,7 @@ out: */ static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range) { - const char *prefix = output_prefix(&rev->diffopt); + const char *prefix = diff_line_prefix(&rev->diffopt); fprintf(rev->diffopt.file, "%s\n", prefix); From 2011bb4f34d773a7de2d64769ca9f508feba8089 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 3 Oct 2024 17:06:54 -0400 Subject: [PATCH 2/5] diff: drop line_prefix_length field The diff_options structure holds a line_prefix string and an associated length. But the length is always just the strlen() of the NUL-terminated string. Let's simplify the code by just storing the string pointer and assuming it is NUL-terminated when we use it. This will cause us to compute the string length in a few extra spots, but I don't think any of these are particularly hot code paths. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff.c | 1 - diff.h | 1 - graph.c | 8 ++------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/diff.c b/diff.c index a83409944b..f725d217de 100644 --- a/diff.c +++ b/diff.c @@ -5395,7 +5395,6 @@ static int diff_opt_line_prefix(const struct option *opt, BUG_ON_OPT_NEG(unset); options->line_prefix = optarg; - options->line_prefix_length = strlen(options->line_prefix); graph_setup_line_prefix(options); return 0; } diff --git a/diff.h b/diff.h index 9901c8ca8c..f816d3b12b 100644 --- a/diff.h +++ b/diff.h @@ -274,7 +274,6 @@ struct diff_options { const char *single_follow; const char *a_prefix, *b_prefix; const char *line_prefix; - size_t line_prefix_length; /** * collection of boolean options that affects the operation, but some do diff --git a/graph.c b/graph.c index 1ca34770ee..34b18a80d4 100644 --- a/graph.c +++ b/graph.c @@ -74,10 +74,7 @@ static void graph_show_line_prefix(const struct diff_options *diffopt) if (!diffopt || !diffopt->line_prefix) return; - fwrite(diffopt->line_prefix, - sizeof(char), - diffopt->line_prefix_length, - diffopt->file); + fputs(diffopt->line_prefix, diffopt->file); } static const char **column_colors; @@ -321,8 +318,7 @@ static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void strbuf_reset(&msgbuf); if (opt->line_prefix) - strbuf_add(&msgbuf, opt->line_prefix, - opt->line_prefix_length); + strbuf_addstr(&msgbuf, opt->line_prefix); if (graph) graph_padding_line(graph, &msgbuf); return &msgbuf; From 436728fe9d75d05fa2439f867ca2039012b86e69 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 3 Oct 2024 17:09:24 -0400 Subject: [PATCH 3/5] diff: return const char from output_prefix callback The diff_options structure has an output_prefix callback for returning a prefix string, but it does so by returning a pointer to a strbuf. This makes the interface awkward. There's no reason the callback should need to use a strbuf, and it creates questions about whether the ownership of the resulting buffer should be transferred to the caller (it should not be, but a recent attempt to clean up this code led to a double-free in some cases). The one advantage we get is that the strbuf contains a ptr/len pair, so we could in theory have a prefix with embedded NULs. But we can observe that none of the existing callbacks would ever produce such a NUL (they are usually just indentation or graph symbols, and even the "--line-prefix" option takes a NUL-terminated string). And anyway, only one caller (the one in log_tree_diff_flush) actually looks at the strbuf length. In every other case we use a helper function which discards the length and just returns the NUL-terminated string. So let's just have the callback return a "const char *" pointer. It's up to the callbacks themselves if they want to use a strbuf under the hood. And now the caller in log_tree_diff_flush() can just use the helper function along with everybody else. That lets us even simplify out the function pointer check, since the helper returns an empty string (technically this does mean we'll sometimes issue an empty fputs() call, but I don't think this code path is hot enough to care about that). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- diff-lib.c | 4 ++-- diff.c | 9 +++------ diff.h | 2 +- graph.c | 4 ++-- log-tree.c | 7 +------ range-diff.c | 4 ++-- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/diff-lib.c b/diff-lib.c index 7a1eb63757..1cbf983a9c 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -704,7 +704,7 @@ int index_differs_from(struct repository *r, return (has_changes != 0); } -static struct strbuf *idiff_prefix_cb(struct diff_options *opt UNUSED, void *data) +static const char *idiff_prefix_cb(struct diff_options *opt UNUSED, void *data) { return data; } @@ -719,7 +719,7 @@ void show_interdiff(const struct object_id *oid1, const struct object_id *oid2, opts.output_format = DIFF_FORMAT_PATCH; opts.output_prefix = idiff_prefix_cb; strbuf_addchars(&prefix, ' ', indent); - opts.output_prefix_data = &prefix; + opts.output_prefix_data = prefix.buf; diff_setup_done(&opts); diff_tree_oid(oid1, oid2, "", &opts); diff --git a/diff.c b/diff.c index f725d217de..a7bfe73fe6 100644 --- a/diff.c +++ b/diff.c @@ -2313,12 +2313,9 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix) const char *diff_line_prefix(struct diff_options *opt) { - struct strbuf *msgbuf; - if (!opt->output_prefix) - return ""; - - msgbuf = opt->output_prefix(opt, opt->output_prefix_data); - return msgbuf->buf; + return opt->output_prefix ? + opt->output_prefix(opt, opt->output_prefix_data) : + ""; } static unsigned long sane_truncate_line(char *line, unsigned long len) diff --git a/diff.h b/diff.h index f816d3b12b..ed91c92e3a 100644 --- a/diff.h +++ b/diff.h @@ -94,7 +94,7 @@ typedef void (*add_remove_fn_t)(struct diff_options *options, typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, struct diff_options *options, void *data); -typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data); +typedef const char *(*diff_prefix_fn_t)(struct diff_options *opt, void *data); #define DIFF_FORMAT_RAW 0x0001 #define DIFF_FORMAT_DIFFSTAT 0x0002 diff --git a/graph.c b/graph.c index 34b18a80d4..6a4db229bb 100644 --- a/graph.c +++ b/graph.c @@ -309,7 +309,7 @@ struct git_graph { unsigned short default_column_color; }; -static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void *data) +static const char *diff_output_prefix_callback(struct diff_options *opt, void *data) { struct git_graph *graph = data; static struct strbuf msgbuf = STRBUF_INIT; @@ -321,7 +321,7 @@ static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void strbuf_addstr(&msgbuf, opt->line_prefix); if (graph) graph_padding_line(graph, &msgbuf); - return &msgbuf; + return msgbuf.buf; } static const struct diff_options *default_diffopt; diff --git a/log-tree.c b/log-tree.c index 13524bc888..7a5828ce63 100644 --- a/log-tree.c +++ b/log-tree.c @@ -922,12 +922,7 @@ int log_tree_diff_flush(struct rev_info *opt) * diff/diffstat output for readability. */ int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH; - if (opt->diffopt.output_prefix) { - struct strbuf *msg = NULL; - msg = opt->diffopt.output_prefix(&opt->diffopt, - opt->diffopt.output_prefix_data); - fwrite(msg->buf, msg->len, 1, opt->diffopt.file); - } + fputs(diff_line_prefix(&opt->diffopt), opt->diffopt.file); /* * We may have shown three-dashes line early diff --git a/range-diff.c b/range-diff.c index 5f01605550..f28a388fb8 100644 --- a/range-diff.c +++ b/range-diff.c @@ -478,7 +478,7 @@ static void patch_diff(const char *a, const char *b, diff_flush(diffopt); } -static struct strbuf *output_prefix_cb(struct diff_options *opt UNUSED, void *data) +static const char *output_prefix_cb(struct diff_options *opt UNUSED, void *data) { return data; } @@ -506,7 +506,7 @@ static void output(struct string_list *a, struct string_list *b, opts.flags.suppress_hunk_header_line_count = 1; opts.output_prefix = output_prefix_cb; strbuf_addstr(&indent, " "); - opts.output_prefix_data = &indent; + opts.output_prefix_data = indent.buf; diff_setup_done(&opts); /* From 19752d9c912478b9eef0bd83c2cf6da98974f536 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 3 Oct 2024 17:11:31 -0400 Subject: [PATCH 4/5] diff: return line_prefix directly when possible We may point our output_prefix callback to diff_output_prefix_callback() in any of these cases: 1. we have a user-provided line_prefix 2. we have a graph prefix to show 3. both (1) and (2) The function combines the available elements into a strbuf and returns its pointer. In the case that we just have the line_prefix, though, there is no need for the strbuf. We can return the string directly. This is a minor optimization by itself, but also will allow us to clean up some memory ownership issues on top. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- graph.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/graph.c b/graph.c index 6a4db229bb..c8833ecc3e 100644 --- a/graph.c +++ b/graph.c @@ -316,6 +316,9 @@ static const char *diff_output_prefix_callback(struct diff_options *opt, void *d assert(opt); + if (!graph) + return opt->line_prefix; + strbuf_reset(&msgbuf); if (opt->line_prefix) strbuf_addstr(&msgbuf, opt->line_prefix); From 1164e270b5af80516625b628945ec7365d992055 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 3 Oct 2024 17:13:17 -0400 Subject: [PATCH 5/5] diff: store graph prefix buf in git_graph struct The diffopt output_prefix interface makes it the callback's job to handle ownership of the memory it returns, keeping it valid while callers are using it and then eventually freeing it when we are done diffing. In diff_output_prefix_callback() we handle this with a static strbuf, effectively "leaking" it when the diff is done (but not triggering any leak detectors because it's technically still reachable). This has not been a big problem in practice, but it is a problem for libification: two diffs running in the same process could stomp on each other's prefix buffers. Since we only need the strbuf when we are formatting graph padding, we can give ownership of the strbuf to the git_graph struct, letting us free it when that struct is no longer in use. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- graph.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/graph.c b/graph.c index c8833ecc3e..b828a67c89 100644 --- a/graph.c +++ b/graph.c @@ -307,24 +307,28 @@ struct git_graph { * stored as an index into the array column_colors. */ unsigned short default_column_color; + + /* + * Scratch buffer for generating prefixes to be used with + * diff_output_prefix_callback(). + */ + struct strbuf prefix_buf; }; static const char *diff_output_prefix_callback(struct diff_options *opt, void *data) { struct git_graph *graph = data; - static struct strbuf msgbuf = STRBUF_INIT; assert(opt); if (!graph) return opt->line_prefix; - strbuf_reset(&msgbuf); + strbuf_reset(&graph->prefix_buf); if (opt->line_prefix) - strbuf_addstr(&msgbuf, opt->line_prefix); - if (graph) - graph_padding_line(graph, &msgbuf); - return msgbuf.buf; + strbuf_addstr(&graph->prefix_buf, opt->line_prefix); + graph_padding_line(graph, &graph->prefix_buf); + return graph->prefix_buf.buf; } static const struct diff_options *default_diffopt; @@ -394,6 +398,7 @@ struct git_graph *graph_init(struct rev_info *opt) * The diff output prefix callback, with this we can make * all the diff output to align with the graph lines. */ + strbuf_init(&graph->prefix_buf, 0); opt->diffopt.output_prefix = diff_output_prefix_callback; opt->diffopt.output_prefix_data = graph; @@ -409,6 +414,7 @@ void graph_clear(struct git_graph *graph) free(graph->new_columns); free(graph->mapping); free(graph->old_mapping); + strbuf_release(&graph->prefix_buf); free(graph); }