| 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 | 34854 | static inline StringView string_view(const char *str, size_t length) | |
| 34 | { | ||
| 35 | 34854 | return (StringView) { | |
| 36 | .data = str, | ||
| 37 | .length = length | ||
| 38 | }; | ||
| 39 | } | ||
| 40 | |||
| 41 | 21370 | static inline StringView strview(const char *str) | |
| 42 | { | ||
| 43 | 
        2/2✓ Branch 0 (2→3) taken 21081 times. 
            ✓ Branch 1 (2→4) taken 289 times. 
           | 
      21370 | 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 | 13184 | static inline bool strview_has_sv_prefix(StringView sv, StringView prefix) | |
| 64 | { | ||
| 65 | 13184 | const size_t plen = prefix.length; | |
| 66 | 
        4/4✓ Branch 0 (2→3) taken 11139 times. 
            ✓ Branch 1 (2→6) taken 2045 times. 
            ✓ Branch 2 (4→5) taken 10511 times. 
            ✓ Branch 3 (4→6) taken 628 times. 
           | 
      13184 | 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 | 11200 | static inline bool strview_has_prefix(StringView sv, const char *prefix) | |
| 77 | { | ||
| 78 | 11200 | 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 |