diff options
| author | Junio C Hamano <gitster@pobox.com> | 2025-07-14 11:19:28 -0700 |
|---|---|---|
| committer | Junio C Hamano <gitster@pobox.com> | 2025-07-14 11:19:28 -0700 |
| commit | 2b5bf70039b027ce37ec2a5442499bb9061c2da0 (patch) | |
| tree | c45e271ad25be4d69f80df01f68bef2937ecb389 | |
| parent | e78bca2eb79cb209151be4b8530fdea0bf0d5865 (diff) | |
| parent | 6e5b26c3ff639211147ccb2b1ca681c768b8db11 (diff) | |
| download | git-2b5bf70039b027ce37ec2a5442499bb9061c2da0.tar.gz | |
Merge branch 'sj/string-list'
Code and test clean-up around string-list API.
* sj/string-list:
u-string-list: move "remove duplicates" test to "u-string-list.c"
u-string-list: move "filter string" test to "u-string-list.c"
u-string-list: move "test_split_in_place" to "u-string-list.c"
u-string-list: move "test_split" into "u-string-list.c"
string-list: enable sign compare warnings check
string-list: return index directly when inserting an existing element
string-list: remove unused "insert_at" parameter from add_entry
string-list: fix sign compare warnings for loop iterator
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | string-list.c | 48 | ||||
| -rw-r--r-- | t/helper/test-string-list.c | 96 | ||||
| -rw-r--r-- | t/meson.build | 2 | ||||
| -rwxr-xr-x | t/t0063-string-list.sh | 142 | ||||
| -rw-r--r-- | t/unit-tests/u-string-list.c | 227 |
6 files changed, 249 insertions, 267 deletions
@@ -1365,6 +1365,7 @@ CLAR_TEST_SUITES += u-prio-queue CLAR_TEST_SUITES += u-reftable-tree CLAR_TEST_SUITES += u-strbuf CLAR_TEST_SUITES += u-strcmp-offset +CLAR_TEST_SUITES += u-string-list CLAR_TEST_SUITES += u-strvec CLAR_TEST_SUITES += u-trailer CLAR_TEST_SUITES += u-urlmatch-normalization diff --git a/string-list.c b/string-list.c index bf061fec56..53faaa8420 100644 --- a/string-list.c +++ b/string-list.c @@ -1,5 +1,3 @@ -#define DISABLE_SIGN_COMPARE_WARNINGS - #include "git-compat-util.h" #include "string-list.h" @@ -17,19 +15,19 @@ void string_list_init_dup(struct string_list *list) /* if there is no exact match, point to the index where the entry could be * inserted */ -static int get_entry_index(const struct string_list *list, const char *string, - int *exact_match) +static size_t get_entry_index(const struct string_list *list, const char *string, + int *exact_match) { - int left = -1, right = list->nr; + size_t left = 0, right = list->nr; compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; - while (left + 1 < right) { - int middle = left + (right - left) / 2; + while (left < right) { + size_t middle = left + (right - left) / 2; int compare = cmp(string, list->items[middle].string); if (compare < 0) right = middle; else if (compare > 0) - left = middle; + left = middle + 1; else { *exact_match = 1; return middle; @@ -40,14 +38,13 @@ static int get_entry_index(const struct string_list *list, const char *string, return right; } -/* returns -1-index if already exists */ -static int add_entry(int insert_at, struct string_list *list, const char *string) +static size_t add_entry(struct string_list *list, const char *string) { int exact_match = 0; - int index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match); + size_t index = get_entry_index(list, string, &exact_match); if (exact_match) - return -1 - index; + return index; ALLOC_GROW(list->items, list->nr+1, list->alloc); if (index < list->nr) @@ -63,10 +60,7 @@ static int add_entry(int insert_at, struct string_list *list, const char *string struct string_list_item *string_list_insert(struct string_list *list, const char *string) { - int index = add_entry(-1, list, string); - - if (index < 0) - index = -1 - index; + size_t index = add_entry(list, string); return list->items + index; } @@ -116,9 +110,9 @@ struct string_list_item *string_list_lookup(struct string_list *list, const char void string_list_remove_duplicates(struct string_list *list, int free_util) { if (list->nr > 1) { - int src, dst; + size_t dst = 1; compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; - for (src = dst = 1; src < list->nr; src++) { + for (size_t src = 1; src < list->nr; src++) { if (!cmp(list->items[dst - 1].string, list->items[src].string)) { if (list->strdup_strings) free(list->items[src].string); @@ -134,8 +128,8 @@ void string_list_remove_duplicates(struct string_list *list, int free_util) int for_each_string_list(struct string_list *list, string_list_each_func_t fn, void *cb_data) { - int i, ret = 0; - for (i = 0; i < list->nr; i++) + int ret = 0; + for (size_t i = 0; i < list->nr; i++) if ((ret = fn(&list->items[i], cb_data))) break; return ret; @@ -144,8 +138,8 @@ int for_each_string_list(struct string_list *list, void filter_string_list(struct string_list *list, int free_util, string_list_each_func_t want, void *cb_data) { - int src, dst = 0; - for (src = 0; src < list->nr; src++) { + size_t dst = 0; + for (size_t src = 0; src < list->nr; src++) { if (want(&list->items[src], cb_data)) { list->items[dst++] = list->items[src]; } else { @@ -171,13 +165,12 @@ void string_list_remove_empty_items(struct string_list *list, int free_util) void string_list_clear(struct string_list *list, int free_util) { if (list->items) { - int i; if (list->strdup_strings) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) free(list->items[i].string); } if (free_util) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) free(list->items[i].util); } free(list->items); @@ -189,13 +182,12 @@ void string_list_clear(struct string_list *list, int free_util) void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc) { if (list->items) { - int i; if (clearfunc) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) clearfunc(list->items[i].util, list->items[i].string); } if (list->strdup_strings) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) free(list->items[i].string); } free(list->items); diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c index 6f10c5a435..6be0cdb8e2 100644 --- a/t/helper/test-string-list.c +++ b/t/helper/test-string-list.c @@ -1,105 +1,9 @@ -#define DISABLE_SIGN_COMPARE_WARNINGS - #include "test-tool.h" #include "strbuf.h" #include "string-list.h" -/* - * Parse an argument into a string list. arg should either be a - * ':'-separated list of strings, or "-" to indicate an empty string - * list (as opposed to "", which indicates a string list containing a - * single empty string). list->strdup_strings must be set. - */ -static void parse_string_list(struct string_list *list, const char *arg) -{ - if (!strcmp(arg, "-")) - return; - - (void)string_list_split(list, arg, ':', -1); -} - -static void write_list(const struct string_list *list) -{ - int i; - for (i = 0; i < list->nr; i++) - printf("[%d]: \"%s\"\n", i, list->items[i].string); -} - -static void write_list_compact(const struct string_list *list) -{ - int i; - if (!list->nr) - printf("-\n"); - else { - printf("%s", list->items[0].string); - for (i = 1; i < list->nr; i++) - printf(":%s", list->items[i].string); - printf("\n"); - } -} - -static int prefix_cb(struct string_list_item *item, void *cb_data) -{ - const char *prefix = (const char *)cb_data; - return starts_with(item->string, prefix); -} - int cmd__string_list(int argc, const char **argv) { - if (argc == 5 && !strcmp(argv[1], "split")) { - struct string_list list = STRING_LIST_INIT_DUP; - int i; - const char *s = argv[2]; - int delim = *argv[3]; - int maxsplit = atoi(argv[4]); - - i = string_list_split(&list, s, delim, maxsplit); - printf("%d\n", i); - write_list(&list); - string_list_clear(&list, 0); - return 0; - } - - if (argc == 5 && !strcmp(argv[1], "split_in_place")) { - struct string_list list = STRING_LIST_INIT_NODUP; - int i; - char *s = xstrdup(argv[2]); - const char *delim = argv[3]; - int maxsplit = atoi(argv[4]); - - i = string_list_split_in_place(&list, s, delim, maxsplit); - printf("%d\n", i); - write_list(&list); - string_list_clear(&list, 0); - free(s); - return 0; - } - - if (argc == 4 && !strcmp(argv[1], "filter")) { - /* - * Retain only the items that have the specified prefix. - * Arguments: list|- prefix - */ - struct string_list list = STRING_LIST_INIT_DUP; - const char *prefix = argv[3]; - - parse_string_list(&list, argv[2]); - filter_string_list(&list, 0, prefix_cb, (void *)prefix); - write_list_compact(&list); - string_list_clear(&list, 0); - return 0; - } - - if (argc == 3 && !strcmp(argv[1], "remove_duplicates")) { - struct string_list list = STRING_LIST_INIT_DUP; - - parse_string_list(&list, argv[2]); - string_list_remove_duplicates(&list, 0); - write_list_compact(&list); - string_list_clear(&list, 0); - return 0; - } - if (argc == 2 && !strcmp(argv[1], "sort")) { struct string_list list = STRING_LIST_INIT_NODUP; struct strbuf sb = STRBUF_INIT; diff --git a/t/meson.build b/t/meson.build index 6d7fe6b117..1af289425d 100644 --- a/t/meson.build +++ b/t/meson.build @@ -11,6 +11,7 @@ clar_test_suites = [ 'unit-tests/u-reftable-tree.c', 'unit-tests/u-strbuf.c', 'unit-tests/u-strcmp-offset.c', + 'unit-tests/u-string-list.c', 'unit-tests/u-strvec.c', 'unit-tests/u-trailer.c', 'unit-tests/u-urlmatch-normalization.c', @@ -123,7 +124,6 @@ integration_tests = [ 't0060-path-utils.sh', 't0061-run-command.sh', 't0062-revision-walking.sh', - 't0063-string-list.sh', 't0066-dir-iterator.sh', 't0067-parse_pathspec_file.sh', 't0068-for-each-repo.sh', diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh deleted file mode 100755 index aac63ba506..0000000000 --- a/t/t0063-string-list.sh +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2012 Michael Haggerty -# - -test_description='Test string list functionality' - -. ./test-lib.sh - -test_split () { - cat >expected && - test_expect_success "split $1 at $2, max $3" " - test-tool string-list split '$1' '$2' '$3' >actual && - test_cmp expected actual && - test-tool string-list split_in_place '$1' '$2' '$3' >actual && - test_cmp expected actual - " -} - -test_split_in_place() { - cat >expected && - test_expect_success "split (in place) $1 at $2, max $3" " - test-tool string-list split_in_place '$1' '$2' '$3' >actual && - test_cmp expected actual - " -} - -test_split "foo:bar:baz" ":" "-1" <<EOF -3 -[0]: "foo" -[1]: "bar" -[2]: "baz" -EOF - -test_split "foo:bar:baz" ":" "0" <<EOF -1 -[0]: "foo:bar:baz" -EOF - -test_split "foo:bar:baz" ":" "1" <<EOF -2 -[0]: "foo" -[1]: "bar:baz" -EOF - -test_split "foo:bar:baz" ":" "2" <<EOF -3 -[0]: "foo" -[1]: "bar" -[2]: "baz" -EOF - -test_split "foo:bar:" ":" "-1" <<EOF -3 -[0]: "foo" -[1]: "bar" -[2]: "" -EOF - -test_split "" ":" "-1" <<EOF -1 -[0]: "" -EOF - -test_split ":" ":" "-1" <<EOF -2 -[0]: "" -[1]: "" -EOF - -test_split_in_place "foo:;:bar:;:baz:;:" ":;" "-1" <<EOF -10 -[0]: "foo" -[1]: "" -[2]: "" -[3]: "bar" -[4]: "" -[5]: "" -[6]: "baz" -[7]: "" -[8]: "" -[9]: "" -EOF - -test_split_in_place "foo:;:bar:;:baz" ":;" "0" <<EOF -1 -[0]: "foo:;:bar:;:baz" -EOF - -test_split_in_place "foo:;:bar:;:baz" ":;" "1" <<EOF -2 -[0]: "foo" -[1]: ";:bar:;:baz" -EOF - -test_split_in_place "foo:;:bar:;:baz" ":;" "2" <<EOF -3 -[0]: "foo" -[1]: "" -[2]: ":bar:;:baz" -EOF - -test_split_in_place "foo:;:bar:;:" ":;" "-1" <<EOF -7 -[0]: "foo" -[1]: "" -[2]: "" -[3]: "bar" -[4]: "" -[5]: "" -[6]: "" -EOF - -test_expect_success "test filter_string_list" ' - test "x-" = "x$(test-tool string-list filter - y)" && - test "x-" = "x$(test-tool string-list filter no y)" && - test yes = "$(test-tool string-list filter yes y)" && - test yes = "$(test-tool string-list filter no:yes y)" && - test yes = "$(test-tool string-list filter yes:no y)" && - test y1:y2 = "$(test-tool string-list filter y1:y2 y)" && - test y2:y1 = "$(test-tool string-list filter y2:y1 y)" && - test "x-" = "x$(test-tool string-list filter x1:x2 y)" -' - -test_expect_success "test remove_duplicates" ' - test "x-" = "x$(test-tool string-list remove_duplicates -)" && - test "x" = "x$(test-tool string-list remove_duplicates "")" && - test a = "$(test-tool string-list remove_duplicates a)" && - test a = "$(test-tool string-list remove_duplicates a:a)" && - test a = "$(test-tool string-list remove_duplicates a:a:a:a:a)" && - test a:b = "$(test-tool string-list remove_duplicates a:b)" && - test a:b = "$(test-tool string-list remove_duplicates a:a:b)" && - test a:b = "$(test-tool string-list remove_duplicates a:b:b)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:b:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:a:b:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:b:b:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:b:c:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:a:b:b:c:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:a:a:b:b:b:c:c:c)" -' - -test_done diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c new file mode 100644 index 0000000000..d4ba5f9fa5 --- /dev/null +++ b/t/unit-tests/u-string-list.c @@ -0,0 +1,227 @@ +#include "unit-test.h" +#include "string-list.h" + +static void t_vcreate_string_list_dup(struct string_list *list, + int free_util, va_list ap) +{ + const char *arg; + + cl_assert(list->strdup_strings); + + string_list_clear(list, free_util); + while ((arg = va_arg(ap, const char *))) + string_list_append(list, arg); +} + +static void t_create_string_list_dup(struct string_list *list, int free_util, ...) +{ + va_list ap; + + cl_assert(list->strdup_strings); + + string_list_clear(list, free_util); + va_start(ap, free_util); + t_vcreate_string_list_dup(list, free_util, ap); + va_end(ap); +} + +static void t_string_list_clear(struct string_list *list, int free_util) +{ + string_list_clear(list, free_util); + cl_assert_equal_p(list->items, NULL); + cl_assert_equal_i(list->nr, 0); + cl_assert_equal_i(list->alloc, 0); +} + +static void t_string_list_equal(struct string_list *list, + struct string_list *expected_strings) +{ + cl_assert_equal_i(list->nr, expected_strings->nr); + cl_assert(list->nr <= list->alloc); + for (size_t i = 0; i < expected_strings->nr; i++) + cl_assert_equal_s(list->items[i].string, + expected_strings->items[i].string); +} + +static void t_string_list_split(const char *data, int delim, int maxsplit, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_DUP; + va_list ap; + int len; + + va_start(ap, maxsplit); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split(&list, data, delim, maxsplit); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +void test_string_list__split(void) +{ + t_string_list_split("foo:bar:baz", ':', -1, "foo", "bar", "baz", NULL); + t_string_list_split("foo:bar:baz", ':', 0, "foo:bar:baz", NULL); + t_string_list_split("foo:bar:baz", ':', 1, "foo", "bar:baz", NULL); + t_string_list_split("foo:bar:baz", ':', 2, "foo", "bar", "baz", NULL); + t_string_list_split("foo:bar:", ':', -1, "foo", "bar", "", NULL); + t_string_list_split("", ':', -1, "", NULL); + t_string_list_split(":", ':', -1, "", "", NULL); +} + +static void t_string_list_split_in_place(const char *data, const char *delim, + int maxsplit, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_NODUP; + char *string = xstrdup(data); + va_list ap; + int len; + + va_start(ap, maxsplit); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split_in_place(&list, string, delim, maxsplit); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + free(string); + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +void test_string_list__split_in_place(void) +{ + t_string_list_split_in_place("foo:;:bar:;:baz:;:", ":;", -1, + "foo", "", "", "bar", "", "", "baz", "", "", "", NULL); + t_string_list_split_in_place("foo:;:bar:;:baz", ":;", 0, + "foo:;:bar:;:baz", NULL); + t_string_list_split_in_place("foo:;:bar:;:baz", ":;", 1, + "foo", ";:bar:;:baz", NULL); + t_string_list_split_in_place("foo:;:bar:;:baz", ":;", 2, + "foo", "", ":bar:;:baz", NULL); + t_string_list_split_in_place("foo:;:bar:;:", ":;", -1, + "foo", "", "", "bar", "", "", "", NULL); +} + +static int prefix_cb(struct string_list_item *item, void *cb_data) +{ + const char *prefix = (const char *)cb_data; + return starts_with(item->string, prefix); +} + +static void t_string_list_filter(struct string_list *list, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + const char *prefix = "y"; + va_list ap; + + va_start(ap, list); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + filter_string_list(list, 0, prefix_cb, (void *)prefix); + t_string_list_equal(list, &expected_strings); + + string_list_clear(&expected_strings, 0); +} + +void test_string_list__filter(void) +{ + struct string_list list = STRING_LIST_INIT_DUP; + + t_create_string_list_dup(&list, 0, NULL); + t_string_list_filter(&list, NULL); + + t_create_string_list_dup(&list, 0, "no", NULL); + t_string_list_filter(&list, NULL); + + t_create_string_list_dup(&list, 0, "yes", NULL); + t_string_list_filter(&list, "yes", NULL); + + t_create_string_list_dup(&list, 0, "no", "yes", NULL); + t_string_list_filter(&list, "yes", NULL); + + t_create_string_list_dup(&list, 0, "yes", "no", NULL); + t_string_list_filter(&list, "yes", NULL); + + t_create_string_list_dup(&list, 0, "y1", "y2", NULL); + t_string_list_filter(&list, "y1", "y2", NULL); + + t_create_string_list_dup(&list, 0, "y2", "y1", NULL); + t_string_list_filter(&list, "y2", "y1", NULL); + + t_create_string_list_dup(&list, 0, "x1", "x2", NULL); + t_string_list_filter(&list, NULL); + + t_string_list_clear(&list, 0); +} + +static void t_string_list_remove_duplicates(struct string_list *list, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + va_list ap; + + va_start(ap, list); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_remove_duplicates(list, 0); + t_string_list_equal(list, &expected_strings); + + string_list_clear(&expected_strings, 0); +} + +void test_string_list__remove_duplicates(void) +{ + struct string_list list = STRING_LIST_INIT_DUP; + + t_create_string_list_dup(&list, 0, NULL); + t_string_list_remove_duplicates(&list, NULL); + + t_create_string_list_dup(&list, 0, "", NULL); + t_string_list_remove_duplicates(&list, "", NULL); + + t_create_string_list_dup(&list, 0, "a", NULL); + t_string_list_remove_duplicates(&list, "a", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", NULL); + t_string_list_remove_duplicates(&list, "a", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "a", NULL); + t_string_list_remove_duplicates(&list, "a", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "b", NULL); + t_string_list_remove_duplicates(&list, "a", "b", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "b", NULL); + t_string_list_remove_duplicates(&list, "a", "b", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "b", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "b", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "c", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "b", "b", "c", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "a", "b", "b", "b", + "c", "c", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_string_list_clear(&list, 0); +} |
