lib: darr: add string search macros

Add macros to support searching for and finding (perhaps closest) matches
for a key in an array of strings.

Signed-off-by: Christian Hopps <chopps@labn.net>
This commit is contained in:
Christian Hopps 2025-04-18 12:32:34 +00:00
parent fca333e342
commit 7d2a0ffc69
3 changed files with 250 additions and 0 deletions

View file

@ -158,3 +158,68 @@ void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero,
return a;
#undef _a_at
}
int _darr_search_floor(const void *a, size_t esize, const void *key, bool *equal,
darr_search_cmpf cmpf)
{
struct darr_metadata *dm;
if (equal)
*equal = false;
if (!a)
return -1;
dm = (struct darr_metadata *)a - 1;
int len = dm->len;
int low = 0, high = len - 1;
int floor = -1;
#define _a_at(i) ((void *)((char *)a + ((i)*esize)))
while (low <= high) {
int mid = low + (high - low) / 2;
int cmp = cmpf(_a_at(mid), key);
if (!cmp) {
if (equal)
*equal = true;
return mid;
} else if (cmp < 0) {
floor = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
return floor;
#undef _a_at
}
int _darr_search(const void *a, size_t esize, const void *key, darr_search_cmpf cmpf)
{
bool equal;
int i;
i = _darr_search_floor(a, esize, key, &equal, cmpf);
if (!equal)
return -1;
return i;
}
uint _darr_search_ceil(const void *a, size_t esize, const void *key, bool *equal,
darr_search_cmpf cmpf)
{
uint i;
i = _darr_search_floor(a, esize, key, equal, cmpf);
if (*equal)
return i;
return i + 1;
}
int darr_strings_cmp(const char **a, const char *key)
{
return strcmp(*a, key);
}

View file

@ -64,6 +64,9 @@
* - darr_strlen
* - darr_strlen_fixup
* - darr_strnul
* - darr_str_search
* - darr_str_search_ceil
* - darr_str_search_floor
* - darr_sprintf, darr_vsprintf
*/
/*
@ -788,6 +791,63 @@ void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mt);
d; \
})
/*
* darr_search_{floor,ceil}() functions - search for key in sorted arrays
*/
typedef int (*darr_search_cmpf)(const void *ep, const void *key);
extern int darr_strings_cmp(const char **a, const char *key);
extern int _darr_search(const void *a, size_t esize, const void *key, darr_search_cmpf cmpf);
extern uint _darr_search_ceil(const void *a, size_t esize, const void *key, bool *equal,
darr_search_cmpf cmpf);
extern int _darr_search_floor(const void *a, size_t esize, const void *key, bool *equal,
darr_search_cmpf cmpf);
/**
* darr_str_search() - Find exact key in array of strings.
*
* Args:
* A: array of string pointers
* K: key string
*
* Return:
* The index of the string which matches the key or -1 for no match.
*/
#define darr_str_search(A, K) \
_darr_search((A), _darr_esize(A), (K), (darr_search_cmpf)darr_strings_cmp)
/**
* darr_str_search_ceil() - Find least elm greater than or equal to the key
*
* Args:
* A: array of string pointers
* K: key string
* E: Ptr to bool, set to true if element matching key is found
*
* Return:
* The index of the least element that is greater than or equal to the @K
* string. @E is set to true if equal otherwise false. The return value can
* be passed directly to darr_insert().
*/
#define darr_str_search_ceil(A, K, E) \
_darr_search_ceil((A), _darr_esize(A), (K), (E), (darr_search_cmpf)darr_strings_cmp)
/**
* darr_str_search_floor() - Find greatest elm less than or equal to the key
*
* Args:
* A: array of string pointers
* K: key string
* E: Ptr to bool, set to true if element matching key is found
*
* Return:
* The index of the greatest element that is less than or equal to the @K
* string. @E is set to true if equal otherwise false. If used with
* darr_insert() then the index should be passed +1 because darr_insert()
* inserts *before* the given index.
*/
#define darr_str_search_floor(A, K, E) \
_darr_search_floor((A), _darr_esize(A), (K), (E), (darr_search_cmpf)darr_strings_cmp)
/**
* Iterate over array `A` using a pointer to each element in `P`.
*

View file

@ -50,6 +50,9 @@
* [x] - darr_strlen
* [x] - darr_strlen_fixup
* [x] - darr_strnul
* [x] - darr_str_search
* [x] - darr_str_search_ceil
* [x] - darr_str_search_floor
* [ ] - darr_vsprintf
*/
@ -329,6 +332,8 @@ static void test_string(void)
const char **strings = NULL;
uint sum = 0;
uint i;
bool b;
int idx;
i = darr_strlen(da1);
assert(i == 0);
@ -462,6 +467,126 @@ static void test_string(void)
darr_free_free(strings);
assert(strings == NULL);
add = darr_strdup("5");
i = darr_str_search_ceil(strings, add, &b);
assert(!b);
*darr_insert(strings, i) = add;
add = darr_strdup("2");
i = darr_str_search_ceil(strings, add, &b);
assert(!b);
*darr_insert(strings, i) = add;
add = darr_strdup("9");
i = darr_str_search_ceil(strings, add, &b);
assert(!b);
*darr_insert(strings, i) = add;
add = darr_strdup("3");
i = darr_str_search_ceil(strings, add, &b);
assert(!b);
*darr_insert(strings, i) = add;
i = darr_str_search_ceil(strings, "0", &b);
assert(!b);
assert(i == 0);
i = darr_str_search_ceil(strings, "1", &b);
assert(!b);
assert(i == 0);
i = darr_str_search_ceil(strings, "2", &b);
assert(b);
assert(i == 0);
i = darr_str_search_ceil(strings, "3", &b);
assert(b);
assert(i == 1);
i = darr_str_search_ceil(strings, "4", &b);
assert(!b);
assert(i == 2);
i = darr_str_search_ceil(strings, "5", &b);
assert(b);
assert(i == 2);
i = darr_str_search_ceil(strings, "6", &b);
assert(!b);
assert(i == 3);
i = darr_str_search_ceil(strings, "7", &b);
assert(!b);
assert(i == 3);
i = darr_str_search_ceil(strings, "8", &b);
assert(!b);
assert(i == 3);
i = darr_str_search_ceil(strings, "9", &b);
assert(b);
assert(i == 3);
i = darr_str_search_ceil(strings, "X", &b);
assert(!b);
assert(i == 4);
assert(!strcmp(strings[0], "2"));
assert(!strcmp(strings[1], "3"));
assert(!strcmp(strings[2], "5"));
assert(!strcmp(strings[3], "9"));
darr_free_free(strings);
assert(strings == NULL);
/* -------------------- */
/* Test sorted prefixes */
/* -------------------- */
add = darr_strdup("/foo");
i = darr_str_search_ceil(strings, add, &b);
assert(!b);
*darr_insert(strings, i) = add;
add = darr_strdup("/bar");
i = darr_str_search_ceil(strings, add, &b);
assert(!b);
*darr_insert(strings, i) = add;
add = darr_strdup("/abc");
i = darr_str_search_ceil(strings, add, &b);
assert(!b);
*darr_insert(strings, i) = add;
add = darr_strdup("/xyz/abc");
i = darr_str_search_ceil(strings, add, &b);
assert(!b);
*darr_insert(strings, i) = add;
add = darr_strdup("/foo/bar");
i = darr_str_search_ceil(strings, add, &b);
assert(!b);
*darr_insert(strings, i) = add;
i = darr_str_search_ceil(strings, "/abc", &b);
assert(i == 0 && b);
i = darr_str_search_ceil(strings, "/bar", &b);
assert(i == 1 && b);
i = darr_str_search_ceil(strings, "/foo", &b);
assert(i == 2 && b);
i = darr_str_search_ceil(strings, "/foo/bar", &b);
assert(i == 3 && b);
i = darr_str_search_ceil(strings, "/xyz/abc", &b);
assert(i == 4 && b);
idx = darr_str_search(strings, "/abc");
assert(idx == 0);
idx = darr_str_search(strings, "/abc/123");
assert(idx == -1);
idx = darr_str_search(strings, "/xyz");
assert(idx == -1);
idx = darr_str_search(strings, "/xyz/abc");
assert(idx == 4);
assert(!strcmp(strings[0], "/abc"));
assert(!strcmp(strings[1], "/bar"));
assert(!strcmp(strings[2], "/foo"));
assert(!strcmp(strings[3], "/foo/bar"));
assert(!strcmp(strings[4], "/xyz/abc"));
darr_free_free(strings);
assert(strings == NULL);
}
int main(int argc, char **argv)