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 | 18825 | static inline StringView string_view(const char *str, size_t length) | |
34 | { | ||
35 | 18825 | return (StringView) { | |
36 | .data = str, | ||
37 | .length = length | ||
38 | }; | ||
39 | } | ||
40 | |||
41 | 5382 | static inline StringView strview_from_cstring(const char *str) | |
42 | { | ||
43 |
2/2✓ Branch 0 (2→3) taken 5124 times.
✓ Branch 1 (2→4) taken 258 times.
|
5382 | 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 | 2324 | 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 1394 times.
✓ Branch 1 (2→6) taken 930 times.
✓ Branch 2 (4→5) taken 889 times.
✓ Branch 3 (4→6) taken 505 times.
|
2324 | 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 | 2299 | static inline bool strview_equal_cstring(const StringView *sv, const char *str) | |
73 | { | ||
74 | 2299 | 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 | 11692 | 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 9777 times.
✓ Branch 1 (2→6) taken 1915 times.
✓ Branch 2 (4→5) taken 9323 times.
✓ Branch 3 (4→6) taken 454 times.
|
11692 | return sv->length >= n && mem_equal(sv->data, p, n); |
87 | } | ||
88 | |||
89 | NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3) | ||
90 | 618 | 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 | 618 | size_t len = sv->length; | |
94 |
6/6✓ Branch 0 (2→3) taken 609 times.
✓ Branch 1 (2→7) taken 9 times.
✓ Branch 2 (3→4) taken 241 times.
✓ Branch 3 (3→7) taken 368 times.
✓ Branch 4 (5→6) taken 31 times.
✓ Branch 5 (5→7) taken 210 times.
|
618 | 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 | 11052 | static inline bool strview_has_prefix(const StringView *sv, const char *p) | |
110 | { | ||
111 | 11052 | 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 | 25 | static inline bool strview_has_either_prefix ( | |
123 | const StringView *sv, | ||
124 | const char *pfx1, | ||
125 | const char *pfx2 | ||
126 | ) { | ||
127 | 25 | return strview_has_strn_prefix(sv, pfx1, strlen(pfx1)) | |
128 |
4/4✓ Branch 0 (3→4) taken 21 times.
✓ Branch 1 (3→7) taken 4 times.
✓ Branch 2 (5→6) taken 2 times.
✓ Branch 3 (5→7) taken 19 times.
|
25 | || strview_has_strn_prefix(sv, pfx2, strlen(pfx2)); |
129 | } | ||
130 | |||
131 | NONNULL_ARGS | ||
132 | 19 | static inline bool strview_has_suffix(const StringView *sv, const char *suffix) | |
133 | { | ||
134 | 19 | return strview_has_strn_suffix(sv, suffix, strlen(suffix)); | |
135 | } | ||
136 | |||
137 | NONNULL_ARGS | ||
138 | 2 | static inline bool strview_has_prefix_and_suffix ( | |
139 | const StringView *sv, | ||
140 | const char *prefix, | ||
141 | const char *suffix | ||
142 | ) { | ||
143 |
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); |
144 | } | ||
145 | |||
146 | NONNULL_ARGS | ||
147 | 103 | static inline bool strview_isblank(const StringView *sv) | |
148 | { | ||
149 | 103 | size_t len = sv->length; | |
150 | 103 | return len == ascii_blank_prefix_length(sv->data, len); | |
151 | } | ||
152 | |||
153 | NONNULL_ARGS | ||
154 | 3 | static inline bool strview_contains_char_type(const StringView *sv, AsciiCharType mask) | |
155 | { | ||
156 | 3 | return strn_contains_ascii_char_type(sv->data, sv->length, mask); | |
157 | } | ||
158 | |||
159 | NONNULL_ARGS | ||
160 | 21 | static inline const unsigned char *strview_memchr(const StringView *sv, int c) | |
161 | { | ||
162 |
2/2✓ Branch 0 (2→3) taken 20 times.
✓ Branch 1 (2→4) taken 1 times.
|
21 | return sv->length ? memchr(sv->data, c, sv->length) : NULL; |
163 | } | ||
164 | |||
165 | NONNULL_ARGS | ||
166 | 386 | static inline const unsigned char *strview_memrchr(const StringView *sv, int c) | |
167 | { | ||
168 |
2/2✓ Branch 0 (2→3) taken 253 times.
✓ Branch 1 (2→4) taken 133 times.
|
386 | return sv->length ? xmemrchr(sv->data, c, sv->length) : NULL; |
169 | } | ||
170 | |||
171 | NONNULL_ARGS | ||
172 | 382 | static inline ssize_t strview_memrchr_idx(const StringView *sv, int c) | |
173 | { | ||
174 | 382 | const unsigned char *ptr = strview_memrchr(sv, c); | |
175 |
2/2✓ Branch 0 (2→3) taken 223 times.
✓ Branch 1 (2→4) taken 159 times.
|
382 | return ptr ? (ssize_t)(ptr - sv->data) : -1; |
176 | } | ||
177 | |||
178 | 10983 | static inline void strview_remove_prefix(StringView *sv, size_t len) | |
179 | { | ||
180 | 10983 | BUG_ON(len > sv->length); | |
181 |
2/2✓ Branch 0 (4→5) taken 669 times.
✓ Branch 1 (4→6) taken 10314 times.
|
10983 | if (len > 0) { |
182 | 669 | sv->data += len; | |
183 | 669 | sv->length -= len; | |
184 | } | ||
185 | 10983 | } | |
186 | |||
187 | 15 | static inline void strview_remove_suffix(StringView *sv, size_t len) | |
188 | { | ||
189 | 15 | BUG_ON(len > sv->length); | |
190 | 15 | sv->length -= len; | |
191 | 15 | } | |
192 | |||
193 | 325 | static inline bool strview_remove_matching_strn_prefix ( | |
194 | StringView *sv, | ||
195 | const char *prefix, | ||
196 | size_t prefix_len | ||
197 | ) { | ||
198 |
2/2✓ Branch 0 (3→4) taken 96 times.
✓ Branch 1 (3→6) taken 229 times.
|
325 | if (!strview_has_strn_prefix(sv, prefix, prefix_len)) { |
199 | return false; | ||
200 | } | ||
201 | 96 | strview_remove_prefix(sv, prefix_len); | |
202 | 96 | return true; | |
203 | } | ||
204 | |||
205 | 323 | static inline bool strview_remove_matching_prefix(StringView *sv, const char *prefix) | |
206 | { | ||
207 | 323 | return strview_remove_matching_strn_prefix(sv, prefix, strlen(prefix)); | |
208 | } | ||
209 | |||
210 | 581 | static inline bool strview_remove_matching_suffix(StringView *sv, const char *suffix) | |
211 | { | ||
212 | 581 | size_t suffix_len = strlen(suffix); | |
213 |
2/2✓ Branch 0 (3→4) taken 12 times.
✓ Branch 1 (3→5) taken 569 times.
|
581 | if (!strview_has_strn_suffix(sv, suffix, suffix_len)) { |
214 | return false; | ||
215 | } | ||
216 | 12 | sv->length -= suffix_len; | |
217 | 12 | return true; | |
218 | } | ||
219 | |||
220 | 56 | static inline bool strview_remove_matching_prefix_and_suffix ( | |
221 | StringView *sv, | ||
222 | const char *prefix, | ||
223 | const char *suffix | ||
224 | ) { | ||
225 | 56 | size_t plen = strlen(prefix); | |
226 | 56 | size_t slen = strlen(suffix); | |
227 |
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)) { |
228 | 15 | strview_remove_prefix(sv, plen); | |
229 | 15 | strview_remove_suffix(sv, slen); | |
230 | 15 | return true; | |
231 | } | ||
232 | return false; | ||
233 | } | ||
234 | |||
235 | NONNULL_ARGS | ||
236 | 10124 | static inline size_t strview_trim_left(StringView *sv) | |
237 | { | ||
238 | 10124 | size_t blank_len = ascii_blank_prefix_length(sv->data, sv->length); | |
239 | 10124 | strview_remove_prefix(sv, blank_len); | |
240 | 10124 | return blank_len; | |
241 | } | ||
242 | |||
243 | NONNULL_ARGS | ||
244 | 209 | static inline void strview_trim_right(StringView *sv) | |
245 | { | ||
246 | 209 | const unsigned char *data = sv->data; | |
247 | 209 | size_t n = sv->length; | |
248 |
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])) { |
249 | n--; | ||
250 | } | ||
251 | 209 | sv->length = n; | |
252 | 209 | } | |
253 | |||
254 | NONNULL_ARGS | ||
255 | 7 | static inline void strview_trim(StringView *sv) | |
256 | { | ||
257 | 7 | strview_trim_left(sv); | |
258 | 7 | strview_trim_right(sv); | |
259 | 7 | } | |
260 | |||
261 | #endif | ||
262 |