Skip to content

Commit 86219f4

Browse files
committed
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 parent b7d36ef commit 86219f4

File tree

3 files changed

+71
-16
lines changed

3 files changed

+71
-16
lines changed

src/util.c

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -252,35 +252,47 @@ void git__strtolower(char *str)
252252
git__strntolower(str, strlen(str));
253253
}
254254

255-
int git__prefixcmp(const char *str, const char *prefix)
255+
GIT_INLINE(int) prefixcmp(const char *str, size_t str_n, const char *prefix, bool icase)
256256
{
257-
for (;;) {
258-
unsigned char p = *(prefix++), s;
257+
int s, p;
258+
259+
while (str_n--) {
260+
s = (unsigned char)*str++;
261+
p = (unsigned char)*prefix++;
262+
263+
if (icase) {
264+
s = git__tolower(s);
265+
p = git__tolower(p);
266+
}
267+
259268
if (!p)
260269
return 0;
261-
if ((s = *(str++)) != p)
270+
271+
if (s != p)
262272
return s - p;
263273
}
274+
275+
return (0 - *prefix);
264276
}
265277

266-
int git__prefixcmp_icase(const char *str, const char *prefix)
278+
int git__prefixcmp(const char *str, const char *prefix)
267279
{
268-
return strncasecmp(str, prefix, strlen(prefix));
280+
return prefixcmp(str, SIZE_MAX, prefix, false);
269281
}
270282

271-
int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
283+
int git__prefixncmp(const char *str, size_t str_n, const char *prefix)
272284
{
273-
int s, p;
274-
275-
while(str_n--) {
276-
s = (unsigned char)git__tolower(*str++);
277-
p = (unsigned char)git__tolower(*prefix++);
285+
return prefixcmp(str, str_n, prefix, false);
286+
}
278287

279-
if (s != p)
280-
return s - p;
281-
}
288+
int git__prefixcmp_icase(const char *str, const char *prefix)
289+
{
290+
return prefixcmp(str, SIZE_MAX, prefix, true);
291+
}
282292

283-
return (0 - *prefix);
293+
int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
294+
{
295+
return prefixcmp(str, str_n, prefix, true);
284296
}
285297

286298
int git__suffixcmp(const char *str, const char *suffix)

src/util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ GIT_INLINE(void) git__free(void *ptr)
180180

181181
extern int git__prefixcmp(const char *str, const char *prefix);
182182
extern int git__prefixcmp_icase(const char *str, const char *prefix);
183+
extern int git__prefixncmp(const char *str, size_t str_n, const char *prefix);
183184
extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
184185
extern int git__suffixcmp(const char *str, const char *suffix);
185186

tests/core/string.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,48 @@ void test_core_string__2(void)
4040
cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0);
4141
}
4242

43+
/* compare prefixes with len */
44+
void test_core_string__prefixncmp(void)
45+
{
46+
cl_assert(git__prefixncmp("", 0, "") == 0);
47+
cl_assert(git__prefixncmp("a", 1, "") == 0);
48+
cl_assert(git__prefixncmp("", 0, "a") < 0);
49+
cl_assert(git__prefixncmp("a", 1, "b") < 0);
50+
cl_assert(git__prefixncmp("b", 1, "a") > 0);
51+
cl_assert(git__prefixncmp("ab", 2, "a") == 0);
52+
cl_assert(git__prefixncmp("ab", 1, "a") == 0);
53+
cl_assert(git__prefixncmp("ab", 2, "ac") < 0);
54+
cl_assert(git__prefixncmp("a", 1, "ac") < 0);
55+
cl_assert(git__prefixncmp("ab", 1, "ac") < 0);
56+
cl_assert(git__prefixncmp("ab", 2, "aa") > 0);
57+
cl_assert(git__prefixncmp("ab", 1, "aa") < 0);
58+
}
59+
60+
/* compare prefixes with len */
61+
void test_core_string__prefixncmp_icase(void)
62+
{
63+
cl_assert(git__prefixncmp_icase("", 0, "") == 0);
64+
cl_assert(git__prefixncmp_icase("a", 1, "") == 0);
65+
cl_assert(git__prefixncmp_icase("", 0, "a") < 0);
66+
cl_assert(git__prefixncmp_icase("a", 1, "b") < 0);
67+
cl_assert(git__prefixncmp_icase("A", 1, "b") < 0);
68+
cl_assert(git__prefixncmp_icase("a", 1, "B") < 0);
69+
cl_assert(git__prefixncmp_icase("b", 1, "a") > 0);
70+
cl_assert(git__prefixncmp_icase("B", 1, "a") > 0);
71+
cl_assert(git__prefixncmp_icase("b", 1, "A") > 0);
72+
cl_assert(git__prefixncmp_icase("ab", 2, "a") == 0);
73+
cl_assert(git__prefixncmp_icase("Ab", 2, "a") == 0);
74+
cl_assert(git__prefixncmp_icase("ab", 2, "A") == 0);
75+
cl_assert(git__prefixncmp_icase("ab", 1, "a") == 0);
76+
cl_assert(git__prefixncmp_icase("ab", 2, "ac") < 0);
77+
cl_assert(git__prefixncmp_icase("Ab", 2, "ac") < 0);
78+
cl_assert(git__prefixncmp_icase("ab", 2, "Ac") < 0);
79+
cl_assert(git__prefixncmp_icase("a", 1, "ac") < 0);
80+
cl_assert(git__prefixncmp_icase("ab", 1, "ac") < 0);
81+
cl_assert(git__prefixncmp_icase("ab", 2, "aa") > 0);
82+
cl_assert(git__prefixncmp_icase("ab", 1, "aa") < 0);
83+
}
84+
4385
void test_core_string__strcmp(void)
4486
{
4587
cl_assert(git__strcmp("", "") == 0);

0 commit comments

Comments
 (0)