| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef UTIL_ARRAY_H | ||
| 2 | #define UTIL_ARRAY_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <sys/types.h> | ||
| 6 | #include "debug.h" | ||
| 7 | #include "macros.h" | ||
| 8 | #include "ptr-array.h" | ||
| 9 | #include "xstring.h" | ||
| 10 | |||
| 11 | #define static_assert_flat_string_array(a) \ | ||
| 12 | static_assert_incompatible_types(a[0], const char*); \ | ||
| 13 | static_assert_incompatible_types(a[0], char*); \ | ||
| 14 | static_assert_compatible_types(a[0][0], char) | ||
| 15 | |||
| 16 | #define static_assert_flat_struct_array(a, field) \ | ||
| 17 | static_assert_offsetof(a[0], field, 0); \ | ||
| 18 | static_assert_incompatible_types(a[0].field, const char*); \ | ||
| 19 | static_assert_incompatible_types(a[0].field, char*); \ | ||
| 20 | static_assert_compatible_types(a[0].field[0], char) | ||
| 21 | |||
| 22 | #define CHECK_STRING_ARRAY(a) \ | ||
| 23 | static_assert_flat_string_array(a); \ | ||
| 24 | check_array(a, #a, "", ARRAYLEN(a), sizeof(a[0]), sizeof(a[0])) | ||
| 25 | |||
| 26 | #define CHECK_STRUCT_ARRAY(a, field) \ | ||
| 27 | static_assert_flat_struct_array(a, field); \ | ||
| 28 | check_array(a, #a, "." #field, ARRAYLEN(a), sizeof(a[0]), sizeof(a[0].field)) | ||
| 29 | |||
| 30 | #define COLLECT_STRINGS(a, ptrs, prefix) \ | ||
| 31 | static_assert_flat_string_array(a); \ | ||
| 32 | collect_strings_from_flat_array(a[0], ARRAYLEN(a), sizeof(a[0]), ptrs, prefix) | ||
| 33 | |||
| 34 | #define COLLECT_STRING_FIELDS(a, field, ptrs, prefix) \ | ||
| 35 | static_assert_flat_struct_array(a, field); \ | ||
| 36 | collect_strings_from_flat_array(a[0].field, ARRAYLEN(a), sizeof(a[0]), ptrs, prefix) | ||
| 37 | |||
| 38 | #define STR_TO_ENUM_WITH_OFFSET(str, a, nfval, off) \ | ||
| 39 | str_to_enum(str, a[0], ARRAYLEN(a), sizeof(a[0]), off, nfval) | ||
| 40 | |||
| 41 | #define SSTR_TO_ENUM_WITH_OFFSET(str, a, field, nfval, off) \ | ||
| 42 | str_to_enum(str, a[0].field, ARRAYLEN(a), sizeof(a[0]), off, nfval) | ||
| 43 | |||
| 44 | #define STR_TO_ENUM(str, a, nfval) \ | ||
| 45 | STR_TO_ENUM_WITH_OFFSET(str, a, nfval, 0) | ||
| 46 | |||
| 47 | #define STR_TO_BITFLAGS(str, a, tolerate_errors) \ | ||
| 48 | str_to_bitflags(str, a[0], ARRAYLEN(a), sizeof(a[0]), tolerate_errors) | ||
| 49 | |||
| 50 | #define FIND_STR_IDX(str, a) \ | ||
| 51 | find_str_idx(str, (char*)a, ARRAYLEN(a), sizeof(a[0]), streq) | ||
| 52 | |||
| 53 | // This is somewhat similar to lfind(3), but returning an index | ||
| 54 | // instead of a pointer and specifically for arrays of type | ||
| 55 | // `const char[nmemb][size]` | ||
| 56 | 1223 | static inline ssize_t find_str_idx ( | |
| 57 | const char *str, | ||
| 58 | const char *base, | ||
| 59 | size_t nmemb, | ||
| 60 | size_t size, | ||
| 61 | bool (*equal)(const char *s1, const char *s2) | ||
| 62 | ) { | ||
| 63 | 1223 | const char *entry = base; | |
| 64 |
2/2✓ Branch 7 → 3 taken 12113 times.
✓ Branch 7 → 8 taken 437 times.
|
12550 | for (size_t i = 0; i < nmemb; i++, entry += size) { |
| 65 |
2/2✓ Branch 4 → 5 taken 786 times.
✓ Branch 4 → 6 taken 11327 times.
|
12113 | if (equal(str, entry)) { |
| 66 | 786 | return i; | |
| 67 | } | ||
| 68 | } | ||
| 69 | return -1; | ||
| 70 | } | ||
| 71 | |||
| 72 | // Attempt to find `str` in a flat array of strings starting at `base`. | ||
| 73 | // If found, return the index plus `return_offset`, otherwise return | ||
| 74 | // `not_found_val`. | ||
| 75 | 1203 | static inline int str_to_enum ( | |
| 76 | const char *str, | ||
| 77 | const char *base, | ||
| 78 | size_t nmemb, | ||
| 79 | size_t size, | ||
| 80 | int return_offset, | ||
| 81 | int not_found_val | ||
| 82 | ) { | ||
| 83 | 1203 | ssize_t idx = find_str_idx(str, base, nmemb, size, streq); | |
| 84 |
2/2✓ Branch 3 → 4 taken 776 times.
✓ Branch 3 → 5 taken 427 times.
|
1203 | return (idx >= 0) ? idx + return_offset : not_found_val; |
| 85 | } | ||
| 86 | |||
| 87 | void collect_strings_from_flat_array ( | ||
| 88 | const char *base, | ||
| 89 | size_t nr_elements, | ||
| 90 | size_t element_len, | ||
| 91 | PointerArray *a, | ||
| 92 | const char *prefix | ||
| 93 | ); | ||
| 94 | |||
| 95 | 696 | static inline void check_array ( | |
| 96 | const void *base, | ||
| 97 | const char *array_name, | ||
| 98 | const char *name_field_name, | ||
| 99 | size_t array_len, | ||
| 100 | size_t elem_size, | ||
| 101 | size_t name_size | ||
| 102 | ) { | ||
| 103 | 696 | if (!DEBUG_ASSERTIONS_ENABLED) { | |
| 104 | return; | ||
| 105 | } | ||
| 106 | |||
| 107 | 696 | BUG_ON(!base); | |
| 108 | 696 | BUG_ON(!array_name); | |
| 109 | 696 | BUG_ON(!name_field_name); | |
| 110 | 696 | BUG_ON(name_field_name[0] != '\0' && name_field_name[0] != '.'); | |
| 111 | 696 | BUG_ON(array_len == 0); | |
| 112 | 696 | BUG_ON(elem_size == 0); | |
| 113 | 696 | BUG_ON(name_size == 0); | |
| 114 | |||
| 115 | const char *first_name = base; | ||
| 116 |
2/2✓ Branch 21 → 16 taken 25272 times.
✓ Branch 21 → 22 taken 696 times.
|
25968 | for (size_t i = 0; i < array_len; i++) { |
| 117 | 25272 | const char *curr_name = first_name + (i * elem_size); | |
| 118 | // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) | ||
| 119 |
1/2✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 25272 times.
|
25272 | if (curr_name[0] == '\0') { |
| 120 | − | BUG("Empty string at %s[%zu]%s", array_name, i, name_field_name); | |
| 121 | } | ||
| 122 | // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) | ||
| 123 |
1/2✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 25272 times.
|
25272 | if (curr_name[name_size - 1] != '\0') { |
| 124 | − | BUG("String sentinel missing from %s[%zu]%s", array_name, i, name_field_name); | |
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | unsigned int str_to_bitflags(const char *str, const char *base, size_t nstrs, size_t size, bool tolerate_errors); | ||
| 130 | |||
| 131 | #endif | ||
| 132 |