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 | 14376 | static inline StringView string_view(const char *str, size_t length) | |
34 | { | ||
35 | 14376 | return (StringView) { | |
36 | .data = str, | ||
37 | .length = length | ||
38 | }; | ||
39 | } | ||
40 | |||
41 | 4226 | static inline StringView strview_from_cstring(const char *str) | |
42 | { | ||
43 |
2/2✓ Branch 0 (2→3) taken 3974 times.
✓ Branch 1 (2→4) taken 252 times.
|
4226 | 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 | 1833 | 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 1104 times.
✓ Branch 1 (2→6) taken 729 times.
✓ Branch 2 (4→5) taken 668 times.
✓ Branch 3 (4→6) taken 436 times.
|
1833 | 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 | 1808 | static inline bool strview_equal_cstring(const StringView *sv, const char *str) | |
73 | { | ||
74 | 1808 | 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 | 9748 | 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 8004 times.
✓ Branch 1 (2→6) taken 1744 times.
✓ Branch 2 (4→5) taken 7641 times.
✓ Branch 3 (4→6) taken 363 times.
|
9748 | return sv->length >= n && mem_equal(sv->data, p, n); |
87 | } | ||
88 | |||
89 | NONNULL_ARG(1) NONNULL_ARG_IF_NONZERO_LENGTH(2, 3) | ||
90 | 332 | 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/n3261.pdf | ||
93 | 332 | size_t len = sv->length; | |
94 |
6/6✓ Branch 0 (2→3) taken 323 times.
✓ Branch 1 (2→7) taken 9 times.
✓ Branch 2 (3→4) taken 139 times.
✓ Branch 3 (3→7) taken 184 times.
✓ Branch 4 (5→6) taken 27 times.
✓ Branch 5 (5→7) taken 112 times.
|
332 | return suflen == 0 || (len >= suflen && mem_equal(sv->data + len - suflen, suf, suflen)); |
95 | } | ||
96 | |||
97 | NONNULL_ARGS | ||
98 | 9394 | static inline bool strview_has_prefix(const StringView *sv, const char *p) | |
99 | { | ||
100 | 9394 | return strview_has_strn_prefix(sv, p, strlen(p)); | |
101 | } | ||
102 | |||
103 | NONNULL_ARGS | ||
104 | 12 | static inline bool strview_has_prefix_icase(const StringView *sv, const char *p) | |
105 | { | ||
106 | 12 | size_t length = strlen(p); | |
107 |
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); |
108 | } | ||
109 | |||
110 | NONNULL_ARGS | ||
111 | 33 | static inline bool strview_has_suffix(const StringView *sv, const char *suffix) | |
112 | { | ||
113 | 33 | return strview_has_strn_suffix(sv, suffix, strlen(suffix)); | |
114 | } | ||
115 | |||
116 | NONNULL_ARGS | ||
117 | 2 | static inline bool strview_has_prefix_and_suffix ( | |
118 | const StringView *sv, | ||
119 | const char *prefix, | ||
120 | const char *suffix | ||
121 | ) { | ||
122 |
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); |
123 | } | ||
124 | |||
125 | NONNULL_ARGS | ||
126 | 103 | static inline bool strview_isblank(const StringView *sv) | |
127 | { | ||
128 | 103 | size_t len = sv->length; | |
129 | 103 | return len == ascii_blank_prefix_length(sv->data, len); | |
130 | } | ||
131 | |||
132 | NONNULL_ARGS | ||
133 | 3 | static inline bool strview_contains_char_type(const StringView *sv, AsciiCharType mask) | |
134 | { | ||
135 | 3 | return strn_contains_char_type(sv->data, sv->length, mask); | |
136 | } | ||
137 | |||
138 | NONNULL_ARGS | ||
139 | 15 | static inline const unsigned char *strview_memchr(const StringView *sv, int c) | |
140 | { | ||
141 |
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; |
142 | } | ||
143 | |||
144 | NONNULL_ARGS | ||
145 | 380 | static inline const unsigned char *strview_memrchr(const StringView *sv, int c) | |
146 | { | ||
147 |
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; |
148 | } | ||
149 | |||
150 | NONNULL_ARGS | ||
151 | 376 | static inline ssize_t strview_memrchr_idx(const StringView *sv, int c) | |
152 | { | ||
153 | 376 | const unsigned char *ptr = strview_memrchr(sv, c); | |
154 |
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; |
155 | } | ||
156 | |||
157 | 9449 | static inline void strview_remove_prefix(StringView *sv, size_t len) | |
158 | { | ||
159 | 9449 | BUG_ON(len > sv->length); | |
160 |
2/2✓ Branch 0 (4→5) taken 545 times.
✓ Branch 1 (4→6) taken 8904 times.
|
9449 | if (len > 0) { |
161 | 545 | sv->data += len; | |
162 | 545 | sv->length -= len; | |
163 | } | ||
164 | 9449 | } | |
165 | |||
166 | 317 | static inline bool strview_remove_matching_prefix(StringView *sv, const char *prefix) | |
167 | { | ||
168 | 317 | size_t prefix_len = strlen(prefix); | |
169 |
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)) { |
170 | return false; | ||
171 | } | ||
172 | 89 | strview_remove_prefix(sv, prefix_len); | |
173 | 89 | return true; | |
174 | } | ||
175 | |||
176 | 296 | static inline bool strview_remove_matching_suffix(StringView *sv, const char *suffix) | |
177 | { | ||
178 | 296 | size_t suffix_len = strlen(suffix); | |
179 |
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)) { |
180 | return false; | ||
181 | } | ||
182 | 9 | sv->length -= suffix_len; | |
183 | 9 | return true; | |
184 | } | ||
185 | |||
186 | NONNULL_ARGS | ||
187 | 8711 | static inline size_t strview_trim_left(StringView *sv) | |
188 | { | ||
189 | 8711 | size_t blank_len = ascii_blank_prefix_length(sv->data, sv->length); | |
190 | 8711 | strview_remove_prefix(sv, blank_len); | |
191 | 8711 | return blank_len; | |
192 | } | ||
193 | |||
194 | NONNULL_ARGS | ||
195 | 206 | static inline void strview_trim_right(StringView *sv) | |
196 | { | ||
197 | 206 | const unsigned char *data = sv->data; | |
198 | 206 | size_t n = sv->length; | |
199 |
4/4✓ Branch 0 (3→4) taken 230 times.
✓ Branch 1 (3→5) taken 44 times.
✓ Branch 2 (4→3) taken 68 times.
✓ Branch 3 (4→5) taken 162 times.
|
274 | while (n && ascii_isblank(data[n - 1])) { |
200 | n--; | ||
201 | } | ||
202 | 206 | sv->length = n; | |
203 | 206 | } | |
204 | |||
205 | NONNULL_ARGS | ||
206 | 7 | static inline void strview_trim(StringView *sv) | |
207 | { | ||
208 | 7 | strview_trim_left(sv); | |
209 | 7 | strview_trim_right(sv); | |
210 | 7 | } | |
211 | |||
212 | #endif | ||
213 |