util: introduce `git__prefixncmp` and consolidate implementations Introduce `git_prefixncmp` that will search up to the first `n` characters of a string to see if it is prefixed by another string. This is useful for examining if a non-null terminated character array is prefixed by a particular substring. Consolidate the various implementations of `git__prefixcmp` around a single core implementation and add some test cases to validate its behavior.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
diff --git a/src/util.c b/src/util.c
index 6ae5cda..1760a31 100644
--- a/src/util.c
+++ b/src/util.c
@@ -252,35 +252,47 @@ void git__strtolower(char *str)
git__strntolower(str, strlen(str));
}
-int git__prefixcmp(const char *str, const char *prefix)
+GIT_INLINE(int) prefixcmp(const char *str, size_t str_n, const char *prefix, bool icase)
{
- for (;;) {
- unsigned char p = *(prefix++), s;
+ int s, p;
+
+ while (str_n--) {
+ s = (unsigned char)*str++;
+ p = (unsigned char)*prefix++;
+
+ if (icase) {
+ s = git__tolower(s);
+ p = git__tolower(p);
+ }
+
if (!p)
return 0;
- if ((s = *(str++)) != p)
+
+ if (s != p)
return s - p;
}
+
+ return (0 - *prefix);
}
-int git__prefixcmp_icase(const char *str, const char *prefix)
+int git__prefixcmp(const char *str, const char *prefix)
{
- return strncasecmp(str, prefix, strlen(prefix));
+ return prefixcmp(str, SIZE_MAX, prefix, false);
}
-int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
+int git__prefixncmp(const char *str, size_t str_n, const char *prefix)
{
- int s, p;
-
- while(str_n--) {
- s = (unsigned char)git__tolower(*str++);
- p = (unsigned char)git__tolower(*prefix++);
+ return prefixcmp(str, str_n, prefix, false);
+}
- if (s != p)
- return s - p;
- }
+int git__prefixcmp_icase(const char *str, const char *prefix)
+{
+ return prefixcmp(str, SIZE_MAX, prefix, true);
+}
- return (0 - *prefix);
+int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
+{
+ return prefixcmp(str, str_n, prefix, true);
}
int git__suffixcmp(const char *str, const char *suffix)
diff --git a/src/util.h b/src/util.h
index 7c9a54f..80ee8e6 100644
--- a/src/util.h
+++ b/src/util.h
@@ -180,6 +180,7 @@ GIT_INLINE(void) git__free(void *ptr)
extern int git__prefixcmp(const char *str, const char *prefix);
extern int git__prefixcmp_icase(const char *str, const char *prefix);
+extern int git__prefixncmp(const char *str, size_t str_n, const char *prefix);
extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix);
diff --git a/tests/core/string.c b/tests/core/string.c
index 90e8fa0..85db0c6 100644
--- a/tests/core/string.c
+++ b/tests/core/string.c
@@ -40,6 +40,48 @@ void test_core_string__2(void)
cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0);
}
+/* compare prefixes with len */
+void test_core_string__prefixncmp(void)
+{
+ cl_assert(git__prefixncmp("", 0, "") == 0);
+ cl_assert(git__prefixncmp("a", 1, "") == 0);
+ cl_assert(git__prefixncmp("", 0, "a") < 0);
+ cl_assert(git__prefixncmp("a", 1, "b") < 0);
+ cl_assert(git__prefixncmp("b", 1, "a") > 0);
+ cl_assert(git__prefixncmp("ab", 2, "a") == 0);
+ cl_assert(git__prefixncmp("ab", 1, "a") == 0);
+ cl_assert(git__prefixncmp("ab", 2, "ac") < 0);
+ cl_assert(git__prefixncmp("a", 1, "ac") < 0);
+ cl_assert(git__prefixncmp("ab", 1, "ac") < 0);
+ cl_assert(git__prefixncmp("ab", 2, "aa") > 0);
+ cl_assert(git__prefixncmp("ab", 1, "aa") < 0);
+}
+
+/* compare prefixes with len */
+void test_core_string__prefixncmp_icase(void)
+{
+ cl_assert(git__prefixncmp_icase("", 0, "") == 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "") == 0);
+ cl_assert(git__prefixncmp_icase("", 0, "a") < 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "b") < 0);
+ cl_assert(git__prefixncmp_icase("A", 1, "b") < 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "B") < 0);
+ cl_assert(git__prefixncmp_icase("b", 1, "a") > 0);
+ cl_assert(git__prefixncmp_icase("B", 1, "a") > 0);
+ cl_assert(git__prefixncmp_icase("b", 1, "A") > 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "a") == 0);
+ cl_assert(git__prefixncmp_icase("Ab", 2, "a") == 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "A") == 0);
+ cl_assert(git__prefixncmp_icase("ab", 1, "a") == 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("Ab", 2, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "Ac") < 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("ab", 1, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "aa") > 0);
+ cl_assert(git__prefixncmp_icase("ab", 1, "aa") < 0);
+}
+
void test_core_string__strcmp(void)
{
cl_assert(git__strcmp("", "") == 0);