replace strbuf_expand() with strbuf_expand_step()

Avoid the overhead of passing context to a callback function of strbuf_expand() by using strbuf_expand_step() in a loop instead. It requires explicit handling of %% and unrecognized placeholders, but is simpler, more direct and avoids void pointers. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Jun 17, 2023 at 22:43 UTC 6f1e2d52797161c7effe4d1388765de3dbd80bbf
7 files changed +169 -272
builtin/cat-file.c
+17 -18
@@ -309,10 +309,8 @@ static int is_atom(const char *atom, const char *s, int slen)
309 }
310
311 static void expand_atom(struct strbuf *sb, const char *atom, int len,
312 - void *vdata)
312 + struct expand_data *data)
313 {
314 - struct expand_data *data = vdata;
315 -
314 if (is_atom("objectname", atom, len)) {
315 if (!data->mark_query)
316 strbuf_addstr(sb, oid_to_hex(&data->oid));
@@ -346,19 +344,21 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
344 die("unknown format element: %.*s", len, atom);
345 }
346
349 -static size_t expand_format(struct strbuf *sb, const char *start, void *data)
347 +static void expand_format(struct strbuf *sb, const char *start,
348 + struct expand_data *data)
349 {
351 - const char *end;
352 -
353 - if (*start != '(')
354 - return 0;
355 - end = strchr(start + 1, ')');
356 - if (!end)
357 - die("format element '%s' does not end in ')'", start);
358 -
359 - expand_atom(sb, start + 1, end - start - 1, data);
360 -
361 - return end - start + 1;
350 + while (strbuf_expand_step(sb, &start)) {
351 + const char *end;
352 +
353 + if (skip_prefix(start, "%", &start) || *start != '(')
354 + strbuf_addch(sb, '%');
355 + else if (!(end = strchr(start + 1, ')')))
356 + die("format element '%s' does not end in ')'", start);
357 + else {
358 + expand_atom(sb, start + 1, end - start - 1, data);
359 + start = end + 1;
360 + }
361 + }
362 }
363
364 static void batch_write(struct batch_options *opt, const void *data, int len)
@@ -494,7 +494,7 @@ static void batch_object_write(const char *obj_name,
494 if (!opt->format) {
495 print_default_format(scratch, data);
496 } else {
497 - strbuf_expand(scratch, opt->format, expand_format, data);
497 + expand_format(scratch, opt->format, data);
498 strbuf_addch(scratch, '\n');
499 }
500
@@ -777,9 +777,8 @@ static int batch_objects(struct batch_options *opt)
777 */
778 memset(&data, 0, sizeof(data));
779 data.mark_query = 1;
780 - strbuf_expand(&output,
780 + expand_format(&output,
781 opt->format ? opt->format : DEFAULT_FORMAT,
782 - expand_format,
782 &data);
783 data.mark_query = 0;
784 strbuf_release(&output);
builtin/ls-files.c
+46 -63
@@ -262,74 +262,57 @@ static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
262 strbuf_addstr(line, "-");
263 }
264 }
265 -struct show_index_data {
266 - const char *pathname;
267 - struct index_state *istate;
268 - const struct cache_entry *ce;
269 -};
270 -
271 -static size_t expand_show_index(struct strbuf *sb, const char *start,
272 - void *context)
273 -{
274 - struct show_index_data *data = context;
275 - const char *end;
276 - const char *p;
277 - size_t len = strbuf_expand_literal_cb(sb, start, NULL);
278 - struct stat st;
279 -
280 - if (len)
281 - return len;
282 - if (*start != '(')
283 - die(_("bad ls-files format: element '%s' "
284 - "does not start with '('"), start);
285 -
286 - end = strchr(start + 1, ')');
287 - if (!end)
288 - die(_("bad ls-files format: element '%s' "
289 - "does not end in ')'"), start);
290 -
291 - len = end - start + 1;
292 - if (skip_prefix(start, "(objectmode)", &p))
293 - strbuf_addf(sb, "%06o", data->ce->ce_mode);
294 - else if (skip_prefix(start, "(objectname)", &p))
295 - strbuf_add_unique_abbrev(sb, &data->ce->oid, abbrev);
296 - else if (skip_prefix(start, "(objecttype)", &p))
297 - strbuf_addstr(sb, type_name(object_type(data->ce->ce_mode)));
298 - else if (skip_prefix(start, "(objectsize:padded)", &p))
299 - expand_objectsize(sb, &data->ce->oid, object_type(data->ce->ce_mode), 1);
300 - else if (skip_prefix(start, "(objectsize)", &p))
301 - expand_objectsize(sb, &data->ce->oid, object_type(data->ce->ce_mode), 0);
302 - else if (skip_prefix(start, "(stage)", &p))
303 - strbuf_addf(sb, "%d", ce_stage(data->ce));
304 - else if (skip_prefix(start, "(eolinfo:index)", &p))
305 - strbuf_addstr(sb, S_ISREG(data->ce->ce_mode) ?
306 - get_cached_convert_stats_ascii(data->istate,
307 - data->ce->name) : "");
308 - else if (skip_prefix(start, "(eolinfo:worktree)", &p))
309 - strbuf_addstr(sb, !lstat(data->pathname, &st) &&
310 - S_ISREG(st.st_mode) ?
311 - get_wt_convert_stats_ascii(data->pathname) : "");
312 - else if (skip_prefix(start, "(eolattr)", &p))
313 - strbuf_addstr(sb, get_convert_attr_ascii(data->istate,
314 - data->pathname));
315 - else if (skip_prefix(start, "(path)", &p))
316 - write_name_to_buf(sb, data->pathname);
317 - else
318 - die(_("bad ls-files format: %%%.*s"), (int)len, start);
319 -
320 - return len;
321 -}
265
266 static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
267 const char *format, const char *fullname) {
325 - struct show_index_data data = {
326 - .pathname = fullname,
327 - .istate = repo->index,
328 - .ce = ce,
329 - };
268 struct strbuf sb = STRBUF_INIT;
269
332 - strbuf_expand(&sb, format, expand_show_index, &data);
270 + while (strbuf_expand_step(&sb, &format)) {
271 + const char *end;
272 + size_t len;
273 + struct stat st;
274 +
275 + if (skip_prefix(format, "%", &format))
276 + strbuf_addch(&sb, '%');
277 + else if ((len = strbuf_expand_literal_cb(&sb, format, NULL)))
278 + format += len;
279 + else if (*format != '(')
280 + die(_("bad ls-files format: element '%s' "
281 + "does not start with '('"), format);
282 + else if (!(end = strchr(format + 1, ')')))
283 + die(_("bad ls-files format: element '%s' "
284 + "does not end in ')'"), format);
285 + else if (skip_prefix(format, "(objectmode)", &format))
286 + strbuf_addf(&sb, "%06o", ce->ce_mode);
287 + else if (skip_prefix(format, "(objectname)", &format))
288 + strbuf_add_unique_abbrev(&sb, &ce->oid, abbrev);
289 + else if (skip_prefix(format, "(objecttype)", &format))
290 + strbuf_addstr(&sb, type_name(object_type(ce->ce_mode)));
291 + else if (skip_prefix(format, "(objectsize:padded)", &format))
292 + expand_objectsize(&sb, &ce->oid,
293 + object_type(ce->ce_mode), 1);
294 + else if (skip_prefix(format, "(objectsize)", &format))
295 + expand_objectsize(&sb, &ce->oid,
296 + object_type(ce->ce_mode), 0);
297 + else if (skip_prefix(format, "(stage)", &format))
298 + strbuf_addf(&sb, "%d", ce_stage(ce));
299 + else if (skip_prefix(format, "(eolinfo:index)", &format))
300 + strbuf_addstr(&sb, S_ISREG(ce->ce_mode) ?
301 + get_cached_convert_stats_ascii(repo->index,
302 + ce->name) : "");
303 + else if (skip_prefix(format, "(eolinfo:worktree)", &format))
304 + strbuf_addstr(&sb, !lstat(fullname, &st) &&
305 + S_ISREG(st.st_mode) ?
306 + get_wt_convert_stats_ascii(fullname) : "");
307 + else if (skip_prefix(format, "(eolattr)", &format))
308 + strbuf_addstr(&sb, get_convert_attr_ascii(repo->index,
309 + fullname));
310 + else if (skip_prefix(format, "(path)", &format))
311 + write_name_to_buf(&sb, fullname);
312 + else
313 + die(_("bad ls-files format: %%%.*s"),
314 + (int)(end - format + 1), format);
315 + }
316 strbuf_addch(&sb, line_terminator);
317 fwrite(sb.buf, sb.len, 1, stdout);
318 strbuf_release(&sb);
builtin/ls-tree.c
+41 -66
@@ -55,63 +55,6 @@ struct ls_tree_options {
55 const char *format;
56 };
57
58 -struct show_tree_data {
59 - struct ls_tree_options *options;
60 - unsigned mode;
61 - enum object_type type;
62 - const struct object_id *oid;
63 - const char *pathname;
64 - struct strbuf *base;
65 -};
66 -
67 -static size_t expand_show_tree(struct strbuf *sb, const char *start,
68 - void *context)
69 -{
70 - struct show_tree_data *data = context;
71 - struct ls_tree_options *options = data->options;
72 - const char *end;
73 - const char *p;
74 - unsigned int errlen;
75 - size_t len = strbuf_expand_literal_cb(sb, start, NULL);
76 -
77 - if (len)
78 - return len;
79 - if (*start != '(')
80 - die(_("bad ls-tree format: element '%s' does not start with '('"), start);
81 -
82 - end = strchr(start + 1, ')');
83 - if (!end)
84 - die(_("bad ls-tree format: element '%s' does not end in ')'"), start);
85 -
86 - len = end - start + 1;
87 - if (skip_prefix(start, "(objectmode)", &p)) {
88 - strbuf_addf(sb, "%06o", data->mode);
89 - } else if (skip_prefix(start, "(objecttype)", &p)) {
90 - strbuf_addstr(sb, type_name(data->type));
91 - } else if (skip_prefix(start, "(objectsize:padded)", &p)) {
92 - expand_objectsize(sb, data->oid, data->type, 1);
93 - } else if (skip_prefix(start, "(objectsize)", &p)) {
94 - expand_objectsize(sb, data->oid, data->type, 0);
95 - } else if (skip_prefix(start, "(objectname)", &p)) {
96 - strbuf_add_unique_abbrev(sb, data->oid, options->abbrev);
97 - } else if (skip_prefix(start, "(path)", &p)) {
98 - const char *name = data->base->buf;
99 - const char *prefix = options->chomp_prefix ? options->ls_tree_prefix : NULL;
100 - struct strbuf sbuf = STRBUF_INIT;
101 - size_t baselen = data->base->len;
102 -
103 - strbuf_addstr(data->base, data->pathname);
104 - name = relative_path(data->base->buf, prefix, &sbuf);
105 - quote_c_style(name, sb, NULL, 0);
106 - strbuf_setlen(data->base, baselen);
107 - strbuf_release(&sbuf);
108 - } else {
109 - errlen = (unsigned long)len;
110 - die(_("bad ls-tree format: %%%.*s"), errlen, start);
111 - }
112 - return len;
113 -}
114 -
58 static int show_recursive(struct ls_tree_options *options, const char *base,
59 size_t baselen, const char *pathname)
60 {
@@ -150,14 +93,7 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
93 int recurse = 0;
94 struct strbuf sb = STRBUF_INIT;
95 enum object_type type = object_type(mode);
153 - struct show_tree_data cb_data = {
154 - .options = options,
155 - .mode = mode,
156 - .type = type,
157 - .oid = oid,
158 - .pathname = pathname,
159 - .base = base,
160 - };
96 + const char *format = options->format;
97
98 if (type == OBJ_TREE && show_recursive(options, base->buf, base->len, pathname))
99 recurse = READ_TREE_RECURSIVE;
@@ -166,7 +102,46 @@ static int show_tree_fmt(const struct object_id *oid, struct strbuf *base,
102 if (type == OBJ_BLOB && (options->ls_options & LS_TREE_ONLY))
103 return 0;
104
169 - strbuf_expand(&sb, options->format, expand_show_tree, &cb_data);
105 + while (strbuf_expand_step(&sb, &format)) {
106 + const char *end;
107 + size_t len;
108 +
109 + if (skip_prefix(format, "%", &format))
110 + strbuf_addch(&sb, '%');
111 + else if ((len = strbuf_expand_literal_cb(&sb, format, NULL)))
112 + format += len;
113 + else if (*format != '(')
114 + die(_("bad ls-tree format: element '%s' "
115 + "does not start with '('"), format);
116 + else if (!(end = strchr(format + 1, ')')))
117 + die(_("bad ls-tree format: element '%s' "
118 + "does not end in ')'"), format);
119 + else if (skip_prefix(format, "(objectmode)", &format))
120 + strbuf_addf(&sb, "%06o", mode);
121 + else if (skip_prefix(format, "(objecttype)", &format))
122 + strbuf_addstr(&sb, type_name(type));
123 + else if (skip_prefix(format, "(objectsize:padded)", &format))
124 + expand_objectsize(&sb, oid, type, 1);
125 + else if (skip_prefix(format, "(objectsize)", &format))
126 + expand_objectsize(&sb, oid, type, 0);
127 + else if (skip_prefix(format, "(objectname)", &format))
128 + strbuf_add_unique_abbrev(&sb, oid, options->abbrev);
129 + else if (skip_prefix(format, "(path)", &format)) {
130 + const char *name;
131 + const char *prefix = options->chomp_prefix ?
132 + options->ls_tree_prefix : NULL;
133 + struct strbuf sbuf = STRBUF_INIT;
134 + size_t baselen = base->len;
135 +
136 + strbuf_addstr(base, pathname);
137 + name = relative_path(base->buf, prefix, &sbuf);
138 + quote_c_style(name, &sb, NULL, 0);
139 + strbuf_setlen(base, baselen);
140 + strbuf_release(&sbuf);
141 + } else
142 + die(_("bad ls-tree format: %%%.*s"),
143 + (int)(end - format + 1), format);
144 + }
145 strbuf_addch(&sb, options->null_termination ? '\0' : '\n');
146 fwrite(sb.buf, sb.len, 1, stdout);
147 strbuf_release(&sb);
daemon.c
+19 -42
@@ -144,42 +144,6 @@ static void NORETURN daemon_die(const char *err, va_list params)
144 exit(1);
145 }
146
147 -struct expand_path_context {
148 - const char *directory;
149 - struct hostinfo *hostinfo;
150 -};
151 -
152 -static size_t expand_path(struct strbuf *sb, const char *placeholder, void *ctx)
153 -{
154 - struct expand_path_context *context = ctx;
155 - struct hostinfo *hi = context->hostinfo;
156 -
157 - switch (placeholder[0]) {
158 - case 'H':
159 - strbuf_addbuf(sb, &hi->hostname);
160 - return 1;
161 - case 'C':
162 - if (placeholder[1] == 'H') {
163 - strbuf_addstr(sb, get_canon_hostname(hi));
164 - return 2;
165 - }
166 - break;
167 - case 'I':
168 - if (placeholder[1] == 'P') {
169 - strbuf_addstr(sb, get_ip_address(hi));
170 - return 2;
171 - }
172 - break;
173 - case 'P':
174 - strbuf_addbuf(sb, &hi->tcp_port);
175 - return 1;
176 - case 'D':
177 - strbuf_addstr(sb, context->directory);
178 - return 1;
179 - }
180 - return 0;
181 -}
182 -
147 static const char *path_ok(const char *directory, struct hostinfo *hi)
148 {
149 static char rpath[PATH_MAX];
@@ -223,10 +187,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
187 }
188 else if (interpolated_path && hi->saw_extended_args) {
189 struct strbuf expanded_path = STRBUF_INIT;
226 - struct expand_path_context context;
227 -
228 - context.directory = directory;
229 - context.hostinfo = hi;
190 + const char *format = interpolated_path;
191
192 if (*dir != '/') {
193 /* Allow only absolute */
@@ -234,8 +195,24 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
195 return NULL;
196 }
197
237 - strbuf_expand(&expanded_path, interpolated_path,
238 - expand_path, &context);
198 + while (strbuf_expand_step(&expanded_path, &format)) {
199 + if (skip_prefix(format, "%", &format))
200 + strbuf_addch(&expanded_path, '%');
201 + else if (skip_prefix(format, "H", &format))
202 + strbuf_addbuf(&expanded_path, &hi->hostname);
203 + else if (skip_prefix(format, "CH", &format))
204 + strbuf_addstr(&expanded_path,
205 + get_canon_hostname(hi));
206 + else if (skip_prefix(format, "IP", &format))
207 + strbuf_addstr(&expanded_path,
208 + get_ip_address(hi));
209 + else if (skip_prefix(format, "P", &format))
210 + strbuf_addbuf(&expanded_path, &hi->tcp_port);
211 + else if (skip_prefix(format, "D", &format))
212 + strbuf_addstr(&expanded_path, directory);
213 + else
214 + strbuf_addch(&expanded_path, '%');
215 + }
216
217 rlen = strlcpy(interp_path, expanded_path.buf,
218 sizeof(interp_path));
pretty.c
+43 -29
@@ -1254,9 +1254,19 @@ static struct strbuf *expand_separator(struct strbuf *sb,
1254 const char *argval, size_t arglen)
1255 {
1256 char *fmt = xstrndup(argval, arglen);
1257 + const char *format = fmt;
1258
1259 strbuf_reset(sb);
1259 - strbuf_expand(sb, fmt, strbuf_expand_literal_cb, NULL);
1260 + while (strbuf_expand_step(sb, &format)) {
1261 + size_t len;
1262 +
1263 + if (skip_prefix(format, "%", &format))
1264 + strbuf_addch(sb, '%');
1265 + else if ((len = strbuf_expand_literal_cb(sb, format, NULL)))
1266 + format += len;
1267 + else
1268 + strbuf_addch(sb, '%');
1269 + }
1270 free(fmt);
1271 return sb;
1272 }
@@ -1803,7 +1813,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
1813
1814 static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
1815 const char *placeholder,
1806 - void *context)
1816 + struct format_commit_context *context)
1817 {
1818 size_t consumed, orig_len;
1819 enum {
@@ -1842,7 +1852,7 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
1852 }
1853
1854 orig_len = sb->len;
1845 - if (((struct format_commit_context *)context)->flush_type != no_flush)
1855 + if ((context)->flush_type != no_flush)
1856 consumed = format_and_pad_commit(sb, placeholder, context);
1857 else
1858 consumed = format_commit_one(sb, placeholder, context);
@@ -1861,30 +1871,6 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
1871 return consumed + 1;
1872 }
1873
1864 -static size_t userformat_want_item(struct strbuf *sb UNUSED,
1865 - const char *placeholder,
1866 - void *context)
1867 -{
1868 - struct userformat_want *w = context;
1869 -
1870 - if (*placeholder == '+' || *placeholder == '-' || *placeholder == ' ')
1871 - placeholder++;
1872 -
1873 - switch (*placeholder) {
1874 - case 'N':
1875 - w->notes = 1;
1876 - break;
1877 - case 'S':
1878 - w->source = 1;
1879 - break;
1880 - case 'd':
1881 - case 'D':
1882 - w->decorate = 1;
1883 - break;
1884 - }
1885 - return 0;
1886 -}
1887 -
1874 void userformat_find_requirements(const char *fmt, struct userformat_want *w)
1875 {
1876 struct strbuf dummy = STRBUF_INIT;
@@ -1894,7 +1880,26 @@ void userformat_find_requirements(const char *fmt, struct userformat_want *w)
1880 return;
1881 fmt = user_format;
1882 }
1897 - strbuf_expand(&dummy, fmt, userformat_want_item, w);
1883 + while (strbuf_expand_step(&dummy, &fmt)) {
1884 + if (skip_prefix(fmt, "%", &fmt))
1885 + continue;
1886 +
1887 + if (*fmt == '+' || *fmt == '-' || *fmt == ' ')
1888 + fmt++;
1889 +
1890 + switch (*fmt) {
1891 + case 'N':
1892 + w->notes = 1;
1893 + break;
1894 + case 'S':
1895 + w->source = 1;
1896 + break;
1897 + case 'd':
1898 + case 'D':
1899 + w->decorate = 1;
1900 + break;
1901 + }
1902 + }
1903 strbuf_release(&dummy);
1904 }
1905
@@ -1912,7 +1917,16 @@ void repo_format_commit_message(struct repository *r,
1917 const char *output_enc = pretty_ctx->output_encoding;
1918 const char *utf8 = "UTF-8";
1919
1915 - strbuf_expand(sb, format, format_commit_item, &context);
1920 + while (strbuf_expand_step(sb, &format)) {
1921 + size_t len;
1922 +
1923 + if (skip_prefix(format, "%", &format))
1924 + strbuf_addch(sb, '%');
1925 + else if ((len = format_commit_item(sb, format, &context)))
1926 + format += len;
1927 + else
1928 + strbuf_addch(sb, '%');
1929 + }
1930 rewrap_message_tail(sb, &context, 0, 0, 0);
1931
1932 /*
strbuf.c
-20
@@ -427,26 +427,6 @@ int strbuf_expand_step(struct strbuf *sb, const char **formatp)
427 return 1;
428 }
429
430 -void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
431 - void *context)
432 -{
433 - while (strbuf_expand_step(sb, &format)) {
434 - size_t consumed;
435 -
436 - if (*format == '%') {
437 - strbuf_addch(sb, '%');
438 - format++;
439 - continue;
440 - }
441 -
442 - consumed = fn(sb, format, context);
443 - if (consumed)
444 - format += consumed;
445 - else
446 - strbuf_addch(sb, '%');
447 - }
448 -}
449 -
430 size_t strbuf_expand_literal_cb(struct strbuf *sb,
431 const char *placeholder,
432 void *context UNUSED)
strbuf.h
+3 -34
@@ -318,40 +318,9 @@ const char *strbuf_join_argv(struct strbuf *buf, int argc,
318 const char **argv, char delim);
319
320 /**
321 - * This function can be used to expand a format string containing
322 - * placeholders. To that end, it parses the string and calls the specified
323 - * function for every percent sign found.
324 - *
325 - * The callback function is given a pointer to the character after the `%`
326 - * and a pointer to the struct strbuf. It is expected to add the expanded
327 - * version of the placeholder to the strbuf, e.g. to add a newline
328 - * character if the letter `n` appears after a `%`. The function returns
329 - * the length of the placeholder recognized and `strbuf_expand()` skips
330 - * over it.
331 - *
332 - * The format `%%` is automatically expanded to a single `%` as a quoting
333 - * mechanism; callers do not need to handle the `%` placeholder themselves,
334 - * and the callback function will not be invoked for this placeholder.
335 - *
336 - * All other characters (non-percent and not skipped ones) are copied
337 - * verbatim to the strbuf. If the callback returned zero, meaning that the
338 - * placeholder is unknown, then the percent sign is copied, too.
339 - *
340 - * In order to facilitate caching and to make it possible to give
341 - * parameters to the callback, `strbuf_expand()` passes a context
342 - * pointer with any kind of data.
343 - */
344 -typedef size_t (*expand_fn_t) (struct strbuf *sb,
345 - const char *placeholder,
346 - void *context);
347 -void strbuf_expand(struct strbuf *sb,
348 - const char *format,
349 - expand_fn_t fn,
350 - void *context);
351 -
352 -/**
353 - * Used as callback for `strbuf_expand` to only expand literals
354 - * (i.e. %n and %xNN). The context argument is ignored.
321 + * Used with `strbuf_expand_step` to expand the literals %n and %x
322 + * followed by two hexadecimal digits. Returns the number of recognized
323 + * characters. The context argument is ignored.
324 */
325 size_t strbuf_expand_literal_cb(struct strbuf *sb,
326 const char *placeholder,