Line | Branch | Exec | Source |
---|---|---|---|
1 | #ifndef UTIL_STRING_VIEW_H | ||
2 | #define UTIL_STRING_VIEW_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include <stddef.h> | ||
6 | #include <string.h> | ||
7 | #include <sys/types.h> | ||
8 | #include "ascii.h" | ||
9 | #include "debug.h" | ||
10 | #include "macros.h" | ||
11 | #include "xmemrchr.h" | ||
12 | #include "xstring.h" | ||
13 | |||
14 | // A non-owning, length-bounded "view" into another string, similar to | ||
15 | // the C++17 string_view class or what many languages call a "slice". | ||
16 | // The .data member will usually *not* be null-terminated and the | ||
17 | // underlying string *must* outlive the view. | ||
18 | typedef struct { | ||
19 | const unsigned char NONSTRING *data; | ||
20 | size_t length; | ||
21 | } StringView; | ||
22 | |||
23 | #define STRING_VIEW_INIT { \ | ||
24 | .data = NULL, \ | ||
25 | .length = 0 \ | ||
26 | } | ||
27 | |||
28 | #define STRING_VIEW(s) { \ | ||
29 | .data = s, \ | ||
30 | .length = STRLEN(s) \ | ||
31 | } | ||
32 | |||
33 | 16410 | static inline StringView string_view(const char *str, size_t length) | |
34 | { | ||
35 | 16410 | return (StringView) { | |
36 | .data = str, | ||
37 | .length = length | ||
38 | }; | ||
39 | } | ||
40 | |||
41 | 4836 | static inline StringView strview_from_cstring(const char *str) | |
42 | { | ||
43 |
2/2✓ Branch 0 (2→3) taken 4584 times.
✓ Branch 1 (2→4) taken 252 times.
|
4836 | return string_view(str, str ? strlen(str) : 0); |
44 | } | ||
45 | |||
46 | NONNULL_ARGS | ||
47 | 2 | static inline bool strview_equal(const StringView *a, const StringView *b) | |
48 | { | ||
49 | 2 | size_t n = a->length; | |
50 |
2/4✓ Branch 0 (2→3) taken 2 times.
✗ Branch 1 (2→6) not taken.
✗ Branch 2 (4→5) not taken.
✓ Branch 3 (4→6) taken 2 times.
|
2 | return n == b->length && mem_equal(a->data, b->data, n); |
51 | } | ||
52 | |||
53 | NONNULL_ARGS | ||
54 | 2105 | static inline bool strview_equal_strn ( | |
55 | const StringView *sv, | ||
56 | const char *str, | ||
57 | size_t len | ||
58 | ) { | ||
59 |
4/4✓ Branch 0 (2→3) taken 1257 times.
✓ Branch 1 (2→6) taken 848 times.
✓ Branch 2 (4→5) taken 799 times.
✓ Branch 3 (4→6) taken 458 times.
|
2105 | return len == sv->length && mem_equal(sv->data, str, len); |
60 | } | ||
61 | |||
62 | NONNULL_ARGS | ||
63 | 15 | static inline bool strview_equal_strn_icase ( | |
64 | const StringView *sv, | ||
65 | const char *str, | ||
66 | size_t len | ||
67 | ) { | ||
68 |
3/4✓ Branch 0 (2→3) taken 12 times.
✓ Branch 1 (2→6) taken 3 times.
✗ Branch 2 (4→5) not taken.
✓ Branch 3 (4→6) taken 12 times.
|
15 | return len == sv->length && mem_equal_icase(sv->data, str, len); |
69 | } | ||
70 | |||
71 | NONNULL_ARGS | ||
72 | 2080 | static inline bool strview_equal_cstring(const StringView *sv, const char *str) | |
73 | { | ||
74 | 2080 | return strview_equal_strn(sv, str, strlen(str)); | |
75 | } | ||
76 | |||
77 | NONNULL_ARGS | ||
78 | 15 | static inline bool strview_equal_cstring_icase(const StringView *sv, const char *str) | |
79 | { | ||
80 | 15 | return strview_equal_strn_icase(sv, str, strlen(str)); | |
81 | } | ||
82 | |||
83 | NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3) | ||
84 | 10241 | static inline bool strview_has_strn_prefix(const StringView *sv, const char *p, size_t n) | |
85 | { | ||
86 |
4/4✓ Branch 0 (2→3) taken 8450 times.
✓ Branch 1 (2→6) taken 1791 times.
✓ Branch 2 (4→5) taken 8044 times.
✓ Branch 3 (4→6) taken 406 times.
|
10241 | return sv->length >= n && mem_equal(sv->data, p, n); |
87 | } | ||
88 | |||
89 | NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3) | ||
90 | 333 | static inline bool strview_has_strn_suffix(const StringView *sv, const char *suf, size_t suflen) | |
91 | { | ||
92 | // See also: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf | ||
93 | 333 | size_t len = sv->length; | |
94 |
6/6✓ Branch 0 (2→3) taken 324 times.
✓ Branch 1 (2→7) taken 9 times.
✓ Branch 2 (3→4) taken 140 times.
✓ Branch 3 (3→7) taken 184 times.
✓ Branch 4 (5→6) taken 28 times.
✓ Branch 5 (5→7) taken 112 times.
|
333 | return suflen == 0 || (len >= suflen && mem_equal(sv->data + len - suflen, suf, suflen)); |
95 | } | ||
96 | |||
97 | NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3) NONNULL_ARG_IF_NONZERO_LENGTH(4, 5) | ||
98 | 56 | static inline bool strview_has_strn_prefix_and_suffix ( | |
99 | const StringView *sv, | ||
100 | const char *prefix, size_t prefix_len, | ||
101 | const char *suffix, size_t suffix_len | ||
102 | ) { | ||
103 | 56 | return prefix_len + suffix_len <= sv->length | |
104 |
2/2✓ Branch 0 (4→5) taken 15 times.
✓ Branch 1 (4→8) taken 39 times.
|
54 | && strview_has_strn_prefix(sv, prefix, prefix_len) |
105 |
3/4✓ Branch 0 (2→3) taken 54 times.
✓ Branch 1 (2→8) taken 2 times.
✗ Branch 2 (6→7) not taken.
✓ Branch 3 (6→8) taken 15 times.
|
71 | && strview_has_strn_suffix(sv, suffix, suffix_len); |
106 | } | ||
107 | |||
108 | NONNULL_ARGS | ||
109 | 9833 | static inline bool strview_has_prefix(const StringView *sv, const char *p) | |
110 | { | ||
111 | 9833 | return strview_has_strn_prefix(sv, p, strlen(p)); | |
112 | } | ||
113 | |||
114 | NONNULL_ARGS | ||
115 | 12 | static inline bool strview_has_prefix_icase(const StringView *sv, const char *p) | |
116 | { | ||
117 | 12 | size_t length = strlen(p); | |
118 |
4/4✓ Branch 0 (2→3) taken 11 times.
✓ Branch 1 (2→6) taken 1 times.
✓ Branch 2 (4→5) taken 7 times.
✓ Branch 3 (4→6) taken 4 times.
|
12 | return sv->length >= length && mem_equal_icase(sv->data, p, length); |
119 | } | ||
120 | |||
121 | NONNULL_ARGS | ||
122 | 19 | static inline bool strview_has_suffix(const StringView *sv, const char *suffix) | |
123 | { | ||
124 | 19 | return strview_has_strn_suffix(sv, suffix, strlen(suffix)); | |
125 | } | ||
126 | |||
127 | NONNULL_ARGS | ||
128 | 2 | static inline bool strview_has_prefix_and_suffix ( | |
129 | const StringView *sv, | ||
130 | const char *prefix, | ||
131 | const char *suffix | ||
132 | ) { | ||
133 |
3/4✓ Branch 0 (3→4) taken 1 times.
✓ Branch 1 (3→7) taken 1 times.
✗ Branch 2 (5→6) not taken.
✓ Branch 3 (5→7) taken 1 times.
|
2 | return strview_has_prefix(sv, prefix) && strview_has_suffix(sv, suffix); |
134 | } | ||
135 | |||
136 | NONNULL_ARGS | ||
137 | 103 | static inline bool strview_isblank(const StringView *sv) | |
138 | { | ||
139 | 103 | size_t len = sv->length; | |
140 | 103 | return len == ascii_blank_prefix_length(sv->data, len); | |
141 | } | ||
142 | |||
143 | NONNULL_ARGS | ||
144 | 3 | static inline bool strview_contains_char_type(const StringView *sv, AsciiCharType mask) | |
145 | { | ||
146 | 3 | return strn_contains_ascii_char_type(sv->data, sv->length, mask); | |
147 | } | ||
148 | |||
149 | NONNULL_ARGS | ||
150 | 15 | static inline const unsigned char *strview_memchr(const StringView *sv, int c) | |
151 | { | ||
152 |
2/2✓ Branch 0 (2→3) taken 14 times.
✓ Branch 1 (2→4) taken 1 times.
|
15 | return sv->length ? memchr(sv->data, c, sv->length) : NULL; |
153 | } | ||
154 | |||
155 | NONNULL_ARGS | ||
156 | 380 | static inline const unsigned char *strview_memrchr(const StringView *sv, int c) | |
157 | { | ||
158 |
2/2✓ Branch 0 (2→3) taken 250 times.
✓ Branch 1 (2→4) taken 130 times.
|
380 | return sv->length ? xmemrchr(sv->data, c, sv->length) : NULL; |
159 | } | ||
160 | |||
161 | NONNULL_ARGS | ||
162 | 376 | static inline ssize_t strview_memrchr_idx(const StringView *sv, int c) | |
163 | { | ||
164 | 376 | const unsigned char *ptr = strview_memrchr(sv, c); | |
165 |
2/2✓ Branch 0 (2→3) taken 220 times.
✓ Branch 1 (2→4) taken 156 times.
|
376 | return ptr ? (ssize_t)(ptr - sv->data) : -1; |
166 | } | ||
167 | |||
168 | 9909 | static inline void strview_remove_prefix(StringView *sv, size_t len) | |
169 | { | ||
170 | 9909 | BUG_ON(len > sv->length); | |
171 |
2/2✓ Branch 0 (4→5) taken 564 times.
✓ Branch 1 (4→6) taken 9345 times.
|
9909 | if (len > 0) { |
172 | 564 | sv->data += len; | |
173 | 564 | sv->length -= len; | |
174 | } | ||
175 | 9909 | } | |
176 | |||
177 | 15 | static inline void strview_remove_suffix(StringView *sv, size_t len) | |
178 | { | ||
179 | 15 | BUG_ON(len > sv->length); | |
180 | 15 | sv->length -= len; | |
181 | 15 | } | |
182 | |||
183 | 317 | static inline bool strview_remove_matching_prefix(StringView *sv, const char *prefix) | |
184 | { | ||
185 | 317 | size_t prefix_len = strlen(prefix); | |
186 |
2/2✓ Branch 0 (3→4) taken 89 times.
✓ Branch 1 (3→6) taken 228 times.
|
317 | if (!strview_has_strn_prefix(sv, prefix, prefix_len)) { |
187 | return false; | ||
188 | } | ||
189 | 89 | strview_remove_prefix(sv, prefix_len); | |
190 | 89 | return true; | |
191 | } | ||
192 | |||
193 | 296 | static inline bool strview_remove_matching_suffix(StringView *sv, const char *suffix) | |
194 | { | ||
195 | 296 | size_t suffix_len = strlen(suffix); | |
196 |
2/2✓ Branch 0 (3→4) taken 9 times.
✓ Branch 1 (3→5) taken 287 times.
|
296 | if (!strview_has_strn_suffix(sv, suffix, suffix_len)) { |
197 | return false; | ||
198 | } | ||
199 | 9 | sv->length -= suffix_len; | |
200 | 9 | return true; | |
201 | } | ||
202 | |||
203 | 56 | static inline bool strview_remove_matching_prefix_and_suffix ( | |
204 | StringView *sv, | ||
205 | const char *prefix, | ||
206 | const char *suffix | ||
207 | ) { | ||
208 | 56 | size_t plen = strlen(prefix); | |
209 | 56 | size_t slen = strlen(suffix); | |
210 |
2/2✓ Branch 0 (3→4) taken 15 times.
✓ Branch 1 (3→7) taken 41 times.
|
56 | if (strview_has_strn_prefix_and_suffix(sv, prefix, plen, suffix, slen)) { |
211 | 15 | strview_remove_prefix(sv, plen); | |
212 | 15 | strview_remove_suffix(sv, slen); | |
213 | 15 | return true; | |
214 | } | ||
215 | return false; | ||
216 | } | ||
217 | |||
218 | NONNULL_ARGS | ||
219 | 9155 | static inline size_t strview_trim_left(StringView *sv) | |
220 | { | ||
221 | 9155 | size_t blank_len = ascii_blank_prefix_length(sv->data, sv->length); | |
222 | 9155 | strview_remove_prefix(sv, blank_len); | |
223 | 9155 | return blank_len; | |
224 | } | ||
225 | |||
226 | NONNULL_ARGS | ||
227 | 209 | static inline void strview_trim_right(StringView *sv) | |
228 | { | ||
229 | 209 | const unsigned char *data = sv->data; | |
230 | 209 | size_t n = sv->length; | |
231 |
4/4✓ Branch 0 (3→4) taken 233 times.
✓ Branch 1 (3→5) taken 44 times.
✓ Branch 2 (4→3) taken 68 times.
✓ Branch 3 (4→5) taken 165 times.
|
277 | while (n && ascii_isblank(data[n - 1])) { |
232 | n--; | ||
233 | } | ||
234 | 209 | sv->length = n; | |
235 | 209 | } | |
236 | |||
237 | NONNULL_ARGS | ||
238 | 7 | static inline void strview_trim(StringView *sv) | |
239 | { | ||
240 | 7 | strview_trim_left(sv); | |
241 | 7 | strview_trim_right(sv); | |
242 | 7 | } | |
243 | |||
244 | #endif | ||
245 |