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) do { \ | ||
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 | } while(0) | ||
16 | |||
17 | #define static_assert_flat_struct_array(a, field) do { \ | ||
18 | static_assert_offsetof(a[0], field, 0); \ | ||
19 | static_assert_incompatible_types(a[0].field, const char*); \ | ||
20 | static_assert_incompatible_types(a[0].field, char*); \ | ||
21 | static_assert_compatible_types(a[0].field[0], char); \ | ||
22 | } while(0) | ||
23 | |||
24 | #define CHECK_STRING_ARRAY(a) do { \ | ||
25 | static_assert_flat_string_array(a); \ | ||
26 | check_array(a, #a, "", ARRAYLEN(a), sizeof(a[0]), sizeof(a[0])); \ | ||
27 | } while (0) | ||
28 | |||
29 | #define CHECK_STRUCT_ARRAY(a, field) do { \ | ||
30 | static_assert_flat_struct_array(a, field); \ | ||
31 | check_array(a, #a, "." #field, ARRAYLEN(a), sizeof(a[0]), sizeof(a[0].field)); \ | ||
32 | } while (0) | ||
33 | |||
34 | #define COLLECT_STRINGS(a, ptrs, prefix) do { \ | ||
35 | static_assert_flat_string_array(a); \ | ||
36 | collect_strings_from_flat_array(a[0], ARRAYLEN(a), sizeof(a[0]), ptrs, prefix); \ | ||
37 | } while (0) | ||
38 | |||
39 | #define COLLECT_STRING_FIELDS(a, field, ptrs, prefix) do { \ | ||
40 | static_assert_flat_struct_array(a, field); \ | ||
41 | collect_strings_from_flat_array(a[0].field, ARRAYLEN(a), sizeof(a[0]), ptrs, prefix); \ | ||
42 | } while (0) | ||
43 | |||
44 | #define STR_TO_ENUM_WITH_OFFSET(str, a, nfval, off) \ | ||
45 | str_to_enum(str, a[0], ARRAYLEN(a), sizeof(a[0]), off, nfval) | ||
46 | |||
47 | #define SSTR_TO_ENUM_WITH_OFFSET(str, a, field, nfval, off) \ | ||
48 | str_to_enum(str, a[0].field, ARRAYLEN(a), sizeof(a[0]), off, nfval) | ||
49 | |||
50 | #define STR_TO_ENUM(str, a, nfval) \ | ||
51 | STR_TO_ENUM_WITH_OFFSET(str, a, nfval, 0) | ||
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 | 834 | 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 | 834 | const char *entry = base; | |
64 |
2/2✓ Branch 0 (7→3) taken 8311 times.
✓ Branch 1 (7→8) taken 298 times.
|
8609 | for (size_t i = 0; i < nmemb; i++, entry += size) { |
65 |
2/2✓ Branch 0 (4→5) taken 536 times.
✓ Branch 1 (4→6) taken 7775 times.
|
8311 | if (equal(str, entry)) { |
66 | 536 | 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 | 834 | 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 | 834 | ssize_t idx = find_str_idx(str, base, nmemb, size, streq); | |
84 |
2/2✓ Branch 0 (3→4) taken 536 times.
✓ Branch 1 (3→5) taken 298 times.
|
834 | 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 | 504 | 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 | 504 | if (DEBUG < 1) { | |
104 | return; | ||
105 | } | ||
106 | |||
107 | 504 | BUG_ON(!base); | |
108 | 504 | BUG_ON(!array_name); | |
109 | 504 | BUG_ON(!name_field_name); | |
110 | 504 | BUG_ON(name_field_name[0] != '\0' && name_field_name[0] != '.'); | |
111 | 504 | BUG_ON(array_len == 0); | |
112 | 504 | BUG_ON(elem_size == 0); | |
113 | 504 | BUG_ON(name_size == 0); | |
114 | |||
115 | const char *first_name = base; | ||
116 |
2/2✓ Branch 0 (21→16) taken 18252 times.
✓ Branch 1 (21→22) taken 504 times.
|
18756 | for (size_t i = 0; i < array_len; i++) { |
117 | 18252 | const char *curr_name = first_name + (i * elem_size); | |
118 | // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) | ||
119 |
1/2✗ Branch 0 (16→17) not taken.
✓ Branch 1 (16→18) taken 18252 times.
|
18252 | 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 0 (18→19) not taken.
✓ Branch 1 (18→20) taken 18252 times.
|
18252 | 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 | #endif | ||
130 |