1 // Copyright 2021 Alexey Kutepov <reximkut@gmail.com> 2 3 // Permission is hereby granted, free of charge, to any person obtaining 4 // a copy of this software and associated documentation files (the 5 // "Software"), to deal in the Software without restriction, including 6 // without limitation the rights to use, copy, modify, merge, publish, 7 // distribute, sublicense, and/or sell copies of the Software, and to 8 // permit persons to whom the Software is furnished to do so, subject to 9 // the following conditions: 10 11 // The above copyright notice and this permission notice shall be 12 // included in all copies or substantial portions of the Software. 13 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 #ifndef SV_H_ 23 #define SV_H_ 24 25 #include <stdint.h> 26 #include <stdlib.h> 27 #include <stdbool.h> 28 #include <string.h> 29 #include <ctype.h> 30 31 #ifndef SVDEF 32 #define SVDEF 33 #endif // SVDEF 34 35 typedef struct { 36 size_t count; 37 const char *data; 38 } String_View; 39 40 #define SV(cstr_lit) sv_from_parts(cstr_lit, sizeof(cstr_lit) - 1) 41 #define SV_STATIC(cstr_lit) \ 42 { \ 43 sizeof(cstr_lit) - 1, \ 44 (cstr_lit) \ 45 } 46 47 #define SV_NULL sv_from_parts(NULL, 0) 48 49 // printf macros for String_View 50 #define SV_Fmt "%.*s" 51 #define SV_Arg(sv) (int) (sv).count, (sv).data 52 // USAGE: 53 // String_View name = ...; 54 // printf("Name: "SV_Fmt"\n", SV_Arg(name)); 55 56 SVDEF String_View sv_from_parts(const char *data, size_t count); 57 SVDEF String_View sv_from_cstr(const char *cstr); 58 SVDEF String_View sv_trim_left(String_View sv); 59 SVDEF String_View sv_trim_right(String_View sv); 60 SVDEF String_View sv_trim(String_View sv); 61 SVDEF String_View sv_take_left_while(String_View sv, bool (*predicate)(char x)); 62 SVDEF String_View sv_chop_by_delim(String_View *sv, char delim); 63 SVDEF String_View sv_chop_by_sv(String_View *sv, String_View thicc_delim); 64 SVDEF bool sv_try_chop_by_delim(String_View *sv, char delim, String_View *chunk); 65 SVDEF String_View sv_chop_left(String_View *sv, size_t n); 66 SVDEF String_View sv_chop_right(String_View *sv, size_t n); 67 SVDEF String_View sv_chop_left_while(String_View *sv, bool (*predicate)(char x)); 68 SVDEF bool sv_index_of(String_View sv, char c, size_t *index); 69 SVDEF bool sv_eq(String_View a, String_View b); 70 SVDEF bool sv_eq_ignorecase(String_View a, String_View b); 71 SVDEF bool sv_starts_with(String_View sv, String_View prefix); 72 SVDEF bool sv_ends_with(String_View sv, String_View suffix); 73 SVDEF uint64_t sv_to_u64(String_View sv); 74 uint64_t sv_chop_u64(String_View *sv); 75 76 #endif // SV_H_ 77 78 #ifdef SV_IMPLEMENTATION 79 80 SVDEF String_View sv_from_parts(const char *data, size_t count) 81 { 82 String_View sv; 83 sv.count = count; 84 sv.data = data; 85 return sv; 86 } 87 88 SVDEF String_View sv_from_cstr(const char *cstr) 89 { 90 return sv_from_parts(cstr, strlen(cstr)); 91 } 92 93 SVDEF String_View sv_trim_left(String_View sv) 94 { 95 size_t i = 0; 96 while (i < sv.count && isspace(sv.data[i])) { 97 i += 1; 98 } 99 100 return sv_from_parts(sv.data + i, sv.count - i); 101 } 102 103 SVDEF String_View sv_trim_right(String_View sv) 104 { 105 size_t i = 0; 106 while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) { 107 i += 1; 108 } 109 110 return sv_from_parts(sv.data, sv.count - i); 111 } 112 113 SVDEF String_View sv_trim(String_View sv) 114 { 115 return sv_trim_right(sv_trim_left(sv)); 116 } 117 118 SVDEF String_View sv_chop_left(String_View *sv, size_t n) 119 { 120 if (n > sv->count) { 121 n = sv->count; 122 } 123 124 String_View result = sv_from_parts(sv->data, n); 125 126 sv->data += n; 127 sv->count -= n; 128 129 return result; 130 } 131 132 SVDEF String_View sv_chop_right(String_View *sv, size_t n) 133 { 134 if (n > sv->count) { 135 n = sv->count; 136 } 137 138 String_View result = sv_from_parts(sv->data + sv->count - n, n); 139 140 sv->count -= n; 141 142 return result; 143 } 144 145 SVDEF bool sv_index_of(String_View sv, char c, size_t *index) 146 { 147 size_t i = 0; 148 while (i < sv.count && sv.data[i] != c) { 149 i += 1; 150 } 151 152 if (i < sv.count) { 153 if (index) { 154 *index = i; 155 } 156 return true; 157 } else { 158 return false; 159 } 160 } 161 162 SVDEF bool sv_try_chop_by_delim(String_View *sv, char delim, String_View *chunk) 163 { 164 size_t i = 0; 165 while (i < sv->count && sv->data[i] != delim) { 166 i += 1; 167 } 168 169 String_View result = sv_from_parts(sv->data, i); 170 171 if (i < sv->count) { 172 sv->count -= i + 1; 173 sv->data += i + 1; 174 if (chunk) { 175 *chunk = result; 176 } 177 return true; 178 } 179 180 return false; 181 } 182 183 SVDEF String_View sv_chop_by_delim(String_View *sv, char delim) 184 { 185 size_t i = 0; 186 while (i < sv->count && sv->data[i] != delim) { 187 i += 1; 188 } 189 190 String_View result = sv_from_parts(sv->data, i); 191 192 if (i < sv->count) { 193 sv->count -= i + 1; 194 sv->data += i + 1; 195 } else { 196 sv->count -= i; 197 sv->data += i; 198 } 199 200 return result; 201 } 202 203 SVDEF String_View sv_chop_by_sv(String_View *sv, String_View thicc_delim) 204 { 205 String_View window = sv_from_parts(sv->data, thicc_delim.count); 206 size_t i = 0; 207 while (i + thicc_delim.count < sv->count 208 && !(sv_eq(window, thicc_delim))) 209 { 210 i++; 211 window.data++; 212 } 213 214 String_View result = sv_from_parts(sv->data, i); 215 216 if (i + thicc_delim.count == sv->count) { 217 // include last <thicc_delim.count> characters if they aren't 218 // equal to thicc_delim 219 result.count += thicc_delim.count; 220 } 221 222 // Chop! 223 sv->data += i + thicc_delim.count; 224 sv->count -= i + thicc_delim.count; 225 226 return result; 227 } 228 229 SVDEF bool sv_starts_with(String_View sv, String_View expected_prefix) 230 { 231 if (expected_prefix.count <= sv.count) { 232 String_View actual_prefix = sv_from_parts(sv.data, expected_prefix.count); 233 return sv_eq(expected_prefix, actual_prefix); 234 } 235 236 return false; 237 } 238 239 SVDEF bool sv_ends_with(String_View sv, String_View expected_suffix) 240 { 241 if (expected_suffix.count <= sv.count) { 242 String_View actual_suffix = sv_from_parts(sv.data + sv.count - expected_suffix.count, expected_suffix.count); 243 return sv_eq(expected_suffix, actual_suffix); 244 } 245 246 return false; 247 } 248 249 SVDEF bool sv_eq(String_View a, String_View b) 250 { 251 if (a.count != b.count) { 252 return false; 253 } else { 254 return memcmp(a.data, b.data, a.count) == 0; 255 } 256 } 257 258 SVDEF bool sv_eq_ignorecase(String_View a, String_View b) 259 { 260 if (a.count != b.count) { 261 return false; 262 } 263 264 char x, y; 265 for (size_t i = 0; i < a.count; i++) { 266 x = 'A' <= a.data[i] && a.data[i] <= 'Z' 267 ? a.data[i] + 32 268 : a.data[i]; 269 270 y = 'A' <= b.data[i] && b.data[i] <= 'Z' 271 ? b.data[i] + 32 272 : b.data[i]; 273 274 if (x != y) return false; 275 } 276 return true; 277 } 278 279 SVDEF uint64_t sv_to_u64(String_View sv) 280 { 281 uint64_t result = 0; 282 283 for (size_t i = 0; i < sv.count && isdigit(sv.data[i]); ++i) { 284 result = result * 10 + (uint64_t) sv.data[i] - '0'; 285 } 286 287 return result; 288 } 289 290 uint64_t sv_chop_u64(String_View *sv) 291 { 292 uint64_t result = 0; 293 while (sv->count > 0 && isdigit(*sv->data)) { 294 result = result*10 + *sv->data - '0'; 295 sv->count -= 1; 296 sv->data += 1; 297 } 298 return result; 299 } 300 301 SVDEF String_View sv_chop_left_while(String_View *sv, bool (*predicate)(char x)) 302 { 303 size_t i = 0; 304 while (i < sv->count && predicate(sv->data[i])) { 305 i += 1; 306 } 307 return sv_chop_left(sv, i); 308 } 309 310 SVDEF String_View sv_take_left_while(String_View sv, bool (*predicate)(char x)) 311 { 312 size_t i = 0; 313 while (i < sv.count && predicate(sv.data[i])) { 314 i += 1; 315 } 316 return sv_from_parts(sv.data, i); 317 } 318 319 #endif // SV_IMPLEMENTATION
