| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef UTIL_BSEARCH_H | ||
| 2 | #define UTIL_BSEARCH_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | #include <string.h> | ||
| 7 | #include <sys/types.h> | ||
| 8 | #include "array.h" | ||
| 9 | #include "debug.h" | ||
| 10 | #include "macros.h" | ||
| 11 | |||
| 12 | typedef int (*CompareFunction)(const void *key, const void *elem); | ||
| 13 | typedef int (*StringCompareFunction)(const char *key, const char *elem); | ||
| 14 | |||
| 15 | #define BSEARCH(key, a, cmp) bsearch(key, a, ARRAYLEN(a), sizeof(a[0]), cmp) | ||
| 16 | #define BSEARCH_IDX(key, a, cmp) bisearch_idx(key, a, ARRAYLEN(a), sizeof(a[0]), cmp) | ||
| 17 | |||
| 18 | #define DO_CHECK_BSEARCH_ARRAY(a, field, cmp) \ | ||
| 19 | static_assert_flat_struct_array(a, field); \ | ||
| 20 | check_bsearch_array(a, #a, "." #field, ARRAYLEN(a), sizeof(a[0]), sizeof(a[0].field), cmp) | ||
| 21 | |||
| 22 | #define CHECK_BSEARCH_STR_ARRAY(a) \ | ||
| 23 | static_assert_flat_string_array(a); \ | ||
| 24 | check_bsearch_array(a, #a, "", ARRAYLEN(a), sizeof(a[0]), sizeof(a[0]), strcmp) | ||
| 25 | |||
| 26 | #define CHECK_BSEARCH_ARRAY(a, field) DO_CHECK_BSEARCH_ARRAY(a, field, strcmp) | ||
| 27 | #define CHECK_BSEARCH_ARRAY_ICASE(a, field) DO_CHECK_BSEARCH_ARRAY(a, field, ascii_strcmp_icase) | ||
| 28 | |||
| 29 | // NOLINTNEXTLINE(readability-function-size): 7 param debug function is fine | ||
| 30 | 384 | static inline void check_bsearch_array ( | |
| 31 | const void *base, | ||
| 32 | const char *array_name, | ||
| 33 | const char *name_field_name, | ||
| 34 | size_t array_len, | ||
| 35 | size_t elem_size, | ||
| 36 | size_t name_size, | ||
| 37 | StringCompareFunction cmp | ||
| 38 | ) { | ||
| 39 | 384 | if (!DEBUG_ASSERTIONS_ENABLED) { | |
| 40 | return; | ||
| 41 | } | ||
| 42 | |||
| 43 | 384 | BUG_ON(!cmp); | |
| 44 | 384 | check_array(base, array_name, name_field_name, array_len, elem_size, name_size); | |
| 45 | |||
| 46 | 384 | const char *first_name = base; | |
| 47 |
2/2✓ Branch 10 → 6 taken 21624 times.
✓ Branch 10 → 11 taken 384 times.
|
22008 | for (size_t i = 1; i < array_len; i++) { |
| 48 | 21624 | const char *curr_name = first_name + (i * elem_size); | |
| 49 | 21624 | const char *prev_name = curr_name - elem_size; | |
| 50 |
1/2✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 21624 times.
|
21624 | if (cmp(curr_name, prev_name) <= 0) { |
| 51 | − | BUG ( | |
| 52 | "String at %s[%zu]%s not in sorted order: \"%s\" (prev: \"%s\")", | ||
| 53 | array_name, i, name_field_name, | ||
| 54 | curr_name, prev_name | ||
| 55 | ); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | // Like bsearch(3), but returning the index of the element instead of | ||
| 61 | // a pointer to it (or -1 if not found) | ||
| 62 | 9703 | static inline ssize_t bisearch_idx ( | |
| 63 | const void *key, | ||
| 64 | const void *base, | ||
| 65 | size_t nmemb, | ||
| 66 | size_t size, | ||
| 67 | CompareFunction compare | ||
| 68 | ) { | ||
| 69 | 9703 | const char *found = bsearch(key, base, nmemb, size, compare); | |
| 70 |
2/2✓ Branch 3 → 4 taken 323 times.
✓ Branch 3 → 5 taken 9380 times.
|
9703 | if (!found) { |
| 71 | return -1; | ||
| 72 | } | ||
| 73 | 323 | const char *char_base = base; | |
| 74 | 323 | return (size_t)(found - char_base) / size; | |
| 75 | } | ||
| 76 | |||
| 77 | // This is a wrapper around strcmp(3) with void* parameters, suitable | ||
| 78 | // for use in bsearch(3) without the need for casting | ||
| 79 | 40310 | static inline int vstrcmp(const void *a, const void *b) | |
| 80 | { | ||
| 81 | 40310 | return strcmp(a, b); | |
| 82 | } | ||
| 83 | |||
| 84 | #endif | ||
| 85 |