Enhance `string_list_split_in_place()` to accept multiple characters as
delimiters instead of a single character.
Instead of using `strchr(2)` to locate the first occurrence of the given
delimiter character, `string_list_split_in_place_multi()` uses
`strcspn(2)` to move past the initial segment of characters comprised of
any characters in the delimiting set.
When only a single delimiting character is provided, `strpbrk(2)` (which
is implemented with `strcspn(2)`) has equivalent performance to
`strchr(2)`. Modern `strcspn(2)` implementations treat an empty
delimiter or the singleton delimiter as a special case and fall back to
calling strchrnul(). Both glibc[1] and musl[2] implement `strcspn(2)`
this way.
This change is one step to removing `strtok(2)` from the tree. Note that
`string_list_split_in_place()` is not a strict replacement for
`strtok()`, since it will happily turn sequential delimiter characters
into empty entries in the resulting string_list. For example:
string_list_split_in_place(&xs, "foo:;:bar:;:baz", ":;", -1)
would yield a string list of:
["foo", "", "", "bar", "", "", "baz"]
Callers that wish to emulate the behavior of strtok(2) more directly
should call `string_list_remove_empty_items()` after splitting.
To avoid regressions for the new multi-character delimter cases, update
t0063 in this patch as well.
[1]: https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcspn.c;hb=glibc-2.37#l35
[2]: https://git.musl-libc.org/cgit/musl/tree/src/string/strcspn.c?h=v1.2.3#n11
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Taylor Blau committedApr 24, 2023 at 18:20 UTC52acddf36c8cb3778ab2098a0d95cc2e375a4069
8 files changed+61-10
builtin/gc.c
+2-2
index edd98d35a5..f68e976704 100644--- a/builtin/gc.c+++ b/builtin/gc.c@@ -1687,11 +1687,11 @@ static int get_schedule_cmd(const char **cmd, int *is_available) if (is_available) *is_available = 0;- string_list_split_in_place(&list, testing, ',', -1);+ string_list_split_in_place(&list, testing, ",", -1); for_each_string_list_item(item, &list) { struct string_list pair = STRING_LIST_INIT_NODUP;- if (string_list_split_in_place(&pair, item->string, ':', 2) != 2)+ if (string_list_split_in_place(&pair, item->string, ":", 2) != 2) continue; if (!strcmp(*cmd, pair.items[0].string)) {
diff.c
+1-1
index 78b0fdd8ca..378a0248e1 100644--- a/diff.c+++ b/diff.c@@ -134,7 +134,7 @@ static int parse_dirstat_params(struct diff_options *options, const char *params int i; if (*params_copy)- string_list_split_in_place(¶ms, params_copy, ',', -1);+ string_list_split_in_place(¶ms, params_copy, ",", -1); for (i = 0; i < params.nr; i++) { const char *p = params.items[i].string; if (!strcmp(p, "changes")) {
notes.c
+1-1
index 45fb7f22d1..eee806f626 100644--- a/notes.c+++ b/notes.c@@ -963,7 +963,7 @@ void string_list_add_refs_from_colon_sep(struct string_list *list, char *globs_copy = xstrdup(globs); int i;- string_list_split_in_place(&split, globs_copy, ':', -1);+ string_list_split_in_place(&split, globs_copy, ":", -1); string_list_remove_empty_items(&split, 0); for (i = 0; i < split.nr; i++)