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 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 | 34840 | static inline StringView string_view(const char *str, size_t length) | |
34 | { | ||
35 | 34840 | return (StringView) { | |
36 | .data = str, | ||
37 | .length = length | ||
38 | }; | ||
39 | } | ||
40 | |||
41 | 21362 | static inline StringView strview(const char *str) | |
42 | { | ||
43 |
2/2✓ Branch 0 (2→3) taken 21073 times.
✓ Branch 1 (2→4) taken 289 times.
|
21362 | return string_view(str, str ? strlen(str) : 0); |
44 | } | ||
45 | |||
46 | 2427 | static inline bool strview_equal(StringView a, StringView b) | |
47 | { | ||
48 | 2427 | size_t n = a.length; | |
49 |
4/4✓ Branch 0 (2→3) taken 1395 times.
✓ Branch 1 (2→6) taken 1032 times.
✓ Branch 2 (4→5) taken 889 times.
✓ Branch 3 (4→6) taken 506 times.
|
2427 | return n == b.length && mem_equal(a.data, b.data, n); |
50 | } | ||
51 | |||
52 | 16 | static inline bool strview_equal_icase(StringView a, StringView b) | |
53 | { | ||
54 | 16 | size_t n = a.length; | |
55 |
3/4✓ Branch 0 (2→3) taken 13 times.
✓ Branch 1 (2→6) taken 3 times.
✗ Branch 2 (4→5) not taken.
✓ Branch 3 (4→6) taken 13 times.
|
16 | return n == b.length && mem_equal_icase(a.data, b.data, n); |
56 | } | ||
57 | |||
58 | 2401 | static inline bool strview_equal_cstring(StringView sv, const char *str) | |
59 | { | ||
60 | 2401 | return strview_equal(sv, strview(str)); | |
61 | } | ||
62 | |||
63 | 13177 | static inline bool strview_has_sv_prefix(StringView sv, StringView prefix) | |
64 | { | ||
65 | 13177 | const size_t plen = prefix.length; | |
66 |
4/4✓ Branch 0 (2→3) taken 11132 times.
✓ Branch 1 (2→6) taken 2045 times.
✓ Branch 2 (4→5) taken 10504 times.
✓ Branch 3 (4→6) taken 628 times.
|
13177 | return sv.length >= plen && mem_equal(sv.data, prefix.data, plen); |
67 | } | ||
68 | |||
69 | 666 | static inline bool strview_has_sv_suffix(StringView sv, StringView suffix) | |
70 | { | ||
71 | 666 | size_t len = sv.length; | |
72 | 666 | size_t suflen = suffix.length; | |
73 |
4/4✓ Branch 0 (2→3) taken 298 times.
✓ Branch 1 (2→6) taken 368 times.
✓ Branch 2 (4→5) taken 253 times.
✓ Branch 3 (4→6) taken 45 times.
|
666 | return len >= suflen && mem_equal(sv.data + len - suflen, suffix.data, suflen); |
74 | } | ||
75 | |||
76 | 11197 | static inline bool strview_has_prefix(StringView sv, const char *prefix) | |
77 | { | ||
78 | 11197 | return strview_has_sv_prefix(sv, strview(prefix)); | |
79 | } | ||
80 | |||
81 | 12 | static inline bool strview_has_prefix_icase(StringView sv, const char *prefix) | |
82 | { | ||
83 | 12 | size_t length = strlen(prefix); | |
84 |
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, prefix, length); |
85 | } | ||
86 | |||
87 | 25 | static inline bool strview_has_either_prefix ( | |
88 | StringView sv, | ||
89 | const char *pfx1, | ||
90 | const char *pfx2 | ||
91 | ) { | ||
92 | 25 | return strview_has_sv_prefix(sv, strview(pfx1)) | |
93 |
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_sv_prefix(sv, strview(pfx2)); |
94 | } | ||
95 | |||
96 | 24 | static inline bool strview_has_suffix(StringView sv, const char *suffix) | |
97 | { | ||
98 | 24 | return strview_has_sv_suffix(sv, strview(suffix)); | |
99 | } | ||
100 | |||
101 | 2 | static inline bool strview_has_prefix_and_suffix ( | |
102 | StringView sv, | ||
103 | const char *prefix, | ||
104 | const char *suffix | ||
105 | ) { | ||
106 |
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); |
107 | } | ||
108 | |||
109 | 103 | static inline bool strview_isblank(StringView sv) | |
110 | { | ||
111 | 103 | return ascii_blank_prefix_length(sv.data, sv.length) == sv.length; | |
112 | } | ||
113 | |||
114 | 3 | static inline bool strview_contains_char_type(StringView sv, AsciiCharType mask) | |
115 | { | ||
116 |
2/2✓ Branch 0 (5→3) taken 20 times.
✓ Branch 1 (5→6) taken 3 times.
|
23 | for (size_t i = 0, n = sv.length; i < n; i++) { |
117 |
1/2✓ Branch 0 (3→4) taken 20 times.
✗ Branch 1 (3→6) not taken.
|
20 | if (ascii_test(sv.data[i], mask)) { |
118 | return true; | ||
119 | } | ||
120 | } | ||
121 | return false; | ||
122 | } | ||
123 | |||
124 | 21 | static inline const char *strview_memchr(StringView sv, int c) | |
125 | { | ||
126 |
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; |
127 | } | ||
128 | |||
129 | 386 | static inline const char *strview_memrchr(StringView sv, int c) | |
130 | { | ||
131 |
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; |
132 | } | ||
133 | |||
134 | 382 | static inline ssize_t strview_memrchr_idx(StringView sv, int c) | |
135 | { | ||
136 | 382 | const char *ptr = strview_memrchr(sv, c); | |
137 |
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; |
138 | } | ||
139 | |||
140 | 11272 | static inline void strview_remove_prefix(StringView *sv, size_t len) | |
141 | { | ||
142 | 11272 | BUG_ON(len > sv->length); | |
143 |
2/2✓ Branch 0 (4→5) taken 657 times.
✓ Branch 1 (4→6) taken 10615 times.
|
11272 | if (len > 0) { |
144 | 657 | sv->data += len; | |
145 | 657 | sv->length -= len; | |
146 | } | ||
147 | 11272 | } | |
148 | |||
149 | 583 | static inline void strview_remove_suffix(StringView *sv, size_t len) | |
150 | { | ||
151 | 583 | BUG_ON(len > sv->length); | |
152 | 583 | sv->length -= len; | |
153 | 583 | } | |
154 | |||
155 | 325 | static inline bool strview_remove_matching_sv_prefix ( | |
156 | StringView *sv, | ||
157 | StringView prefix | ||
158 | ) { | ||
159 | 325 | bool match = strview_has_sv_prefix(*sv, prefix); | |
160 |
2/2✓ Branch 0 (3→4) taken 96 times.
✓ Branch 1 (3→5) taken 229 times.
|
325 | strview_remove_prefix(sv, match ? prefix.length : 0); |
161 | 325 | return match; | |
162 | } | ||
163 | |||
164 | 581 | static inline bool strview_remove_matching_sv_suffix ( | |
165 | StringView *sv, | ||
166 | StringView suffix | ||
167 | ) { | ||
168 | 581 | bool match = strview_has_sv_suffix(*sv, suffix); | |
169 |
2/2✓ Branch 0 (3→4) taken 12 times.
✓ Branch 1 (3→5) taken 569 times.
|
581 | strview_remove_suffix(sv, match ? suffix.length : 0); |
170 | 581 | return match; | |
171 | } | ||
172 | |||
173 | 323 | static inline bool strview_remove_matching_prefix(StringView *sv, const char *prefix) | |
174 | { | ||
175 | 323 | return strview_remove_matching_sv_prefix(sv, strview(prefix)); | |
176 | } | ||
177 | |||
178 | 581 | static inline bool strview_remove_matching_suffix(StringView *sv, const char *suffix) | |
179 | { | ||
180 | 581 | return strview_remove_matching_sv_suffix(sv, strview(suffix)); | |
181 | } | ||
182 | |||
183 | 56 | static inline bool strview_remove_matching_affixes ( | |
184 | StringView *sv, | ||
185 | StringView prefix, | ||
186 | StringView suffix | ||
187 | ) { | ||
188 | 56 | size_t total_affix_length = prefix.length + suffix.length; | |
189 | 56 | bool pmatch = strview_has_sv_prefix(*sv, prefix); | |
190 | 56 | bool smatch = strview_has_sv_suffix(*sv, suffix); | |
191 |
4/4✓ Branch 0 (4→5) taken 54 times.
✓ Branch 1 (4→7) taken 2 times.
✓ Branch 2 (5→6) taken 15 times.
✓ Branch 3 (5→7) taken 39 times.
|
56 | bool match = (total_affix_length <= sv->length) && pmatch && smatch; |
192 | |||
193 | 15 | if (match) { | |
194 | 15 | sv->data += prefix.length; | |
195 | 15 | sv->length -= total_affix_length; | |
196 | } | ||
197 | |||
198 | 56 | return match; | |
199 | } | ||
200 | |||
201 | NONNULL_ARGS | ||
202 | 10197 | static inline size_t strview_trim_left(StringView *sv) | |
203 | { | ||
204 | 10197 | size_t blank_len = ascii_blank_prefix_length(sv->data, sv->length); | |
205 | 10197 | strview_remove_prefix(sv, blank_len); | |
206 | 10197 | return blank_len; | |
207 | } | ||
208 | |||
209 | NONNULL_ARGS | ||
210 | 209 | static inline void strview_trim_right(StringView *sv) | |
211 | { | ||
212 | 209 | const char *data = sv->data; | |
213 | 209 | size_t n = sv->length; | |
214 |
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])) { |
215 | n--; | ||
216 | } | ||
217 | 209 | sv->length = n; | |
218 | 209 | } | |
219 | |||
220 | NONNULL_ARGS | ||
221 | 7 | static inline void strview_trim(StringView *sv) | |
222 | { | ||
223 | 7 | strview_trim_left(sv); | |
224 | 7 | strview_trim_right(sv); | |
225 | 7 | } | |
226 | |||
227 | #endif | ||
228 |