dlib.h (12091B)
1 #ifndef DLIB_H 2 #define DLIB_H 1 3 4 #include <assert.h> 5 #include <ctype.h> 6 #include <fcntl.h> 7 #include <limits.h> 8 #include <stdarg.h> 9 #include <stdio.h> 10 #include <stdbool.h> 11 #include <stdint.h> 12 #include <stdlib.h> 13 #include <errno.h> 14 #include <string.h> 15 #include <unistd.h> 16 17 #define todo_p(msg) \ 18 do { \ 19 fprintf(stderr, "%s:%i:1: Error: TODO: %s\n", __FILE__, __LINE__, (msg)); \ 20 abort(); \ 21 } while (0) 22 23 #define todo(msg) \ 24 do { \ 25 fprintf(stderr, "%s:%i:1: Error: TODO: %s\n", __FILE__, __LINE__, (msg)); \ 26 return false; \ 27 } while (0) 28 29 #define unimplemented() \ 30 do { \ 31 fprintf(stderr, "%s:%i:1: Error: not implemented %s\n", __FILE__, __LINE__, __func__); \ 32 abort(); \ 33 } while (0) 34 35 #define unreachable(msg) \ 36 do { \ 37 fprintf(stderr, "%s:%i:1: Error: Unreachable: %s\n", __FILE__, __LINE__, (msg)); \ 38 abort(); \ 39 } while (0) 40 41 #define pop_arg(argc, argv) ((argc)--, *(argv)++) 42 43 #define da_append(da, new)\ 44 do {\ 45 if ((da)->capacity <= (da)->count) {\ 46 (da)->capacity = (da)->capacity ? (da)->capacity * 2 : 16;\ 47 (da)->items = realloc((da)->items, sizeof(*(da)->items) * (da)->capacity);\ 48 }\ 49 (da)->items[(da)->count++] = (new);\ 50 } while(0) 51 52 #define da_prepend(da, new)\ 53 do {\ 54 if ((da)->capacity <= (da)->count) {\ 55 (da)->capacity = (da)->capacity ? (da)->capacity * 2 : 16;\ 56 (da)->items = realloc((da)->items, sizeof(*(da)->items) * (da)->capacity);\ 57 }\ 58 memmove((da)->items + 1, (da)->items, sizeof(*(da)->items) * ((da)->count++));\ 59 (da)->items[0] = (new);\ 60 } while(0) 61 62 #define da_reserve(da, len)\ 63 do {\ 64 if ((da)->capacity >= (len)) break;\ 65 (da)->capacity = (len);\ 66 (da)->items = realloc((da)->items, sizeof(*(da)->items) * (len));\ 67 } while(0) 68 69 #define da_expand(da, size)\ 70 do {\ 71 if ((da)->count >= (size)) break;\ 72 da_reserve(da, size);\ 73 (da)->count = (size);\ 74 } while(0) 75 76 #define da_delete(da, index)\ 77 do {\ 78 assert(index >= 0 && index < (da)->count);\ 79 (da)->items[index] = (da)->items[--(da)->count];\ 80 } while (0) 81 82 #define da_delete_ordered(da, index)\ 83 do {\ 84 assert(index < (da)->count);\ 85 memmove(&(da)->items[(index)], &(da)->items[(index) + 1], sizeof(*(da)->items) * ((da)->count-- - (index) - 1));\ 86 } while (0) 87 88 typedef struct { 89 union { 90 char *items; 91 const char *data; 92 }; 93 union { 94 ssize_t count; 95 ssize_t length; 96 }; 97 ssize_t capacity; 98 } Sb; 99 #define String_builder Sb 100 101 typedef struct { 102 union { 103 const char *items; 104 const char *data; 105 }; 106 union { 107 ssize_t count; 108 ssize_t length; 109 }; 110 } Sv; 111 #define String_view Sv 112 113 typedef struct { 114 void *base; 115 void *head; 116 void *end; 117 } Temp_allocator; 118 119 #define TA_DEFAULT_SIZE (1024*1024) 120 121 void ta_init(Temp_allocator *ta, size_t size); 122 void *ta_alloc(Temp_allocator *ta, size_t size); 123 124 #define sb_append_char da_append 125 int sb_printf(Sb *sb, const char *fmt, ...); 126 void sb_append_cstr(Sb *sb, const char *cstr); 127 void sb_append_sv(Sb *sb, Sv sv); 128 bool sb_append_and_unescape_sv(Sb *sb, Sv sv); 129 130 bool sb_read(Sb *sb, int fd); 131 bool sb_read_file(Sb *sb, const char *file_name); 132 void sb_write(int fd, Sb sb); 133 134 void sb_release(Sb *sb); 135 136 #define sv_literal(str) ((Sv){.items = (str), .count = (sizeof (str) - 1)}) 137 138 Sv sv_from_sb(Sb sb); 139 Sv sv_from_cstr(const char *cstr); 140 const char *sv_to_cstr(Sv sv); 141 long int sv_to_number(Sv sv, int base); 142 ssize_t sv_find_char(Sv sv, char c); 143 144 Sv sv_chop(Sv sv, ssize_t count, Sv *rest); 145 Sv sv_chop_delim(Sv sv, char c, Sv *rest); 146 147 Sv sv_getline(Sv *sv); 148 149 Sv sv_trim_left(Sv sv); 150 Sv sv_trim_right(Sv sv); 151 Sv sv_trim(Sv sv); 152 153 Sv sv_substring(Sv sv, ssize_t start, ssize_t req_count); 154 155 int sv_compare(Sv sv1, Sv sv2); 156 bool sv_equal(Sv sv1, Sv sv2); 157 bool sv_starts_with_cstr(Sv sv, const char *cstr); 158 bool sv_starts_with(Sv sv, Sv prefix); 159 160 void sv_write(int fd, Sv sv); 161 162 /* Adding missing libc functions */ 163 int ctoi(int c); 164 #define isddigit isdigit 165 int isbdigit(int c); 166 int isodigit(int c); 167 168 int is_digit_base(int c, int base); 169 170 #ifdef DLIB_IMPLEMENTATION 171 172 void 173 ta_init(Temp_allocator *ta, size_t size) 174 { 175 ta->base = malloc(size); 176 ta->head = ta->base; 177 ta->end = ta->base + size; 178 } 179 180 void * 181 ta_alloc(Temp_allocator *ta, size_t size) 182 { 183 if (!ta->base) { 184 ta->base = malloc(TA_DEFAULT_SIZE); 185 ta->head = ta->base; 186 ta->end = ta->base + TA_DEFAULT_SIZE; 187 } 188 void *result = ta->head; 189 ta->head += (size + 0xF) & ~0xFul; 190 if (ta->head >= ta->end) { 191 fprintf(stderr, "Error: Temp allocator out of memory\n"); 192 exit(1); 193 } 194 return result; 195 } 196 197 int 198 sb_printf(Sb *sb, const char *fmt, ...) { 199 va_list args, args_copy; 200 va_start(args, fmt); 201 va_copy(args_copy, args); 202 int len = vsnprintf(NULL, 0, fmt, args_copy) + 1; 203 ssize_t new_size = sb->count + len; 204 da_reserve(sb, new_size); 205 va_end(args_copy); 206 vsnprintf(sb->items + sb->count, len, fmt, args); 207 va_end(args); 208 sb->count = new_size - 1; 209 return len - 1; 210 } 211 212 void 213 sb_append_cstr(Sb *sb, const char *cstr) 214 { 215 ssize_t len = strlen(cstr); 216 da_reserve(sb, sb->count + len); 217 strcpy(sb->items + sb->count, cstr); 218 sb->count += len; 219 } 220 221 void 222 sb_append_sv(Sb *sb, Sv sv) 223 { 224 da_reserve(sb, sb->count + sv.count); 225 memcpy(sb->items + sb->count, sv.items, sv.count); 226 sb->count += sv.count; 227 } 228 229 /* Returns `false` if it encounters an invalid escape character */ 230 231 bool 232 sb_append_and_unescape_sv(Sb *sb, Sv sv) 233 { 234 bool success = true; 235 ssize_t i; 236 char c; 237 for (i = 0; i < sv.count; i++) { 238 c = sv.data[i]; 239 if (c == '\\') { 240 if (++i >= sv.count) { 241 success = false; 242 } else { 243 c = sv.data[i]; 244 if (c == 'n') { 245 c = '\n'; 246 } else if (c == 't') { 247 c = '\t'; 248 } else if (c == 'r') { 249 c = '\r'; 250 } else if (c == '0') { 251 c = '\0'; 252 } else { 253 if (c >= 32 && c < 127) { 254 fprintf(stderr, "Info: escape character '%c' unsupported\n", c); 255 } else { 256 fprintf(stderr, "Info: escape character 0x%x02 unsupported\n", c); 257 } 258 success = false; 259 } 260 } 261 } 262 sb_append_char(sb, c); 263 } 264 return success; 265 } 266 267 bool 268 sb_read(Sb *sb, int fd) 269 { 270 ssize_t read_count; 271 ssize_t buf_size = 4096; 272 char *buf = malloc(buf_size); 273 do { 274 read_count = read(fd, buf, buf_size); 275 if (read_count < 0) { 276 if (errno == EINTR) { 277 continue; 278 } 279 perror("sb_read()"); 280 return false; 281 } 282 if (read_count == 0) { 283 break; 284 } 285 sb_append_sv(sb, ((Sv) {.items = buf, .length = read_count})); 286 if (read_count < buf_size) { 287 break; 288 } 289 } while (1); 290 free(buf); 291 return true; 292 } 293 294 bool 295 sb_read_file(Sb *sb, const char *file_name) 296 { 297 int fd = open(file_name, O_RDONLY); 298 if (fd < 0) { 299 perror("sb_read()"); 300 return false; 301 } 302 ssize_t read_count; 303 ssize_t buf_size = 4096; 304 char *buf = malloc(buf_size); 305 do { 306 read_count = read(fd, buf, buf_size); 307 if (read_count < 0) { 308 if (errno == EINTR) { 309 continue; 310 } 311 perror("sb_read_file()"); 312 return false; 313 } 314 if (read_count == 0) { 315 break; 316 } 317 sb_append_sv(sb, ((Sv) {.items = buf, .length = read_count})); 318 } while (1); 319 free(buf); 320 return true; 321 } 322 323 void 324 sb_write(int fd, Sb sb) 325 { 326 write(fd, sb.items, sb.length); 327 } 328 329 void 330 sb_release(Sb *sb) 331 { 332 if (sb->capacity) { 333 free(sb->items); 334 sb->items = NULL; 335 sb->count = 0; 336 sb->capacity = 0; 337 } 338 } 339 340 /* String view functions */ 341 342 void 343 sv_write(int fd, Sv sv) 344 { 345 write(fd, sv.items, sv.count); 346 } 347 348 Sv 349 sv_from_sb(Sb sb) 350 { 351 return (Sv) {.items = sb.items, .count = sb.count}; 352 } 353 354 Sv 355 sv_from_cstr(const char *cstr) 356 { 357 return (Sv) {.items = cstr, .count = strlen(cstr)}; 358 } 359 360 const char * 361 sv_to_cstr(Sv sv) 362 { 363 char *cstr = malloc(sv.count + 1); 364 memcpy(cstr, sv.data, sv.count); 365 cstr[sv.count] = '\0'; 366 return cstr; 367 } 368 369 long int 370 sv_to_number(Sv sv, int base) 371 { 372 assert(sv.count > 0 && sv.items); 373 long int result = 0; 374 bool negative = false; 375 if (*sv.items == '-') { 376 negative = true; 377 sv.items++; 378 sv.count--; 379 } 380 if (sv_starts_with_cstr(sv, "0b") || 381 sv_starts_with_cstr(sv, "0o") || 382 sv_starts_with_cstr(sv, "0d") || 383 sv_starts_with_cstr(sv, "0x")) { 384 sv.items += 2; 385 sv.count -= 2; 386 } 387 do { 388 if (!is_digit_base(*sv.items, base)) { 389 fprintf(stderr, "Error: sv_to_number: Not a valid base%i digit '%c'\n", base, *sv.items); 390 } 391 result *= base; 392 result += ctoi(*sv.items); 393 sv.items++; 394 sv.count--; 395 } while (sv.count); 396 return negative ? -result : result; 397 } 398 399 ssize_t 400 sv_find_char(Sv sv, char c) 401 { 402 ssize_t pos = 0; 403 while (sv.count > pos && sv.items[pos] != c) 404 pos++; 405 for (pos = 0; pos < sv.count && sv.items[pos] != c; pos++); 406 return pos < sv.count ? pos : -1; 407 } 408 409 Sv 410 sv_chop_delim(Sv sv, char c, Sv *rest) 411 { 412 ssize_t pos = sv_find_char(sv, c); 413 Sv res = sv; 414 if (pos > 0) { 415 res.count = pos; 416 417 rest->count -= pos + 1; 418 rest->items += pos + 1; 419 } 420 return res; 421 } 422 423 Sv 424 sv_chop(Sv sv, ssize_t count, Sv *rest) 425 { 426 Sv left = {0}; 427 ssize_t lcount, rcount; 428 if (count > sv.count) { 429 lcount = sv.count; 430 rcount = 0; 431 } else if (-count > sv.count) { 432 lcount = 0; 433 rcount = sv.count; 434 } else if (count < 0) { 435 lcount = sv.count + count; 436 rcount = -count; 437 } else { 438 lcount = count; 439 rcount = sv.count - count; 440 } 441 left.items = sv.items; 442 left.count = lcount; 443 if (rest) { 444 rest->items = sv.items + lcount; 445 rest->count = rcount; 446 } 447 return left; 448 } 449 450 Sv 451 sv_trim_left(Sv sv) 452 { 453 while (sv.count && isspace(sv.items[0])) { 454 sv.count--; 455 sv.items++; 456 } 457 return sv; 458 } 459 460 Sv 461 sv_trim_right(Sv sv) 462 { 463 while (sv.count && isspace(sv.items[sv.count - 1])) { 464 sv.count--; 465 } 466 return sv; 467 } 468 469 Sv 470 sv_trim(Sv sv) 471 { 472 return sv_trim_right(sv_trim_left(sv)); 473 } 474 475 Sv 476 sv_substring(Sv sv, ssize_t start, ssize_t req_count) 477 { 478 Sv ss = {0}; 479 if (start >= sv.count) return ss; 480 ss.count = (req_count < sv.count - start) ? req_count : sv.count - start; 481 ss.items = sv.items + start; 482 return ss; 483 } 484 485 int 486 sv_compare(Sv sv1, Sv sv2) 487 { 488 if (sv1.items == sv2.items && sv1.count == sv2.count) return 0; 489 signed char diff; 490 ssize_t pos; 491 for (pos = 0; pos < sv1.count; pos++) { 492 if (pos == sv2.count) return 1; 493 if ((diff = sv1.items[pos] - sv2.items[pos])) return diff; 494 } 495 return pos < sv2.count ? -1 : 0; 496 } 497 498 bool 499 sv_equal(Sv sv1, Sv sv2) 500 { 501 if (sv1.count != sv2.count) return false; 502 if (sv1.items == sv2.items) return true; 503 ssize_t pos; 504 for (pos = 0; pos < sv1.count && sv1.items[pos] == sv2.items[pos]; pos++); 505 return pos == sv1.count; 506 } 507 508 bool 509 sv_starts_with_cstr(Sv sv, const char *cstr) 510 { 511 size_t cstr_len = strlen(cstr); 512 assert(cstr_len <= SSIZE_MAX); 513 if (sv.count < (ssize_t) cstr_len) return false; 514 ssize_t pos; 515 for (pos = 0; pos < (ssize_t) cstr_len && sv.items[pos] == cstr[pos]; pos++); 516 return pos == (ssize_t) cstr_len; 517 } 518 519 bool 520 sv_starts_with(Sv sv, Sv prefix) 521 { 522 if (sv.count < prefix.count) return false; 523 ssize_t pos; 524 for (pos = 0; pos < prefix.count && sv.items[pos] == prefix.items[pos]; pos++); 525 return pos == prefix.count; 526 } 527 528 Sv 529 sv_getline(Sv *sv) 530 { 531 Sv line = sv_chop_delim(*sv, '\n', sv); 532 if (line.count && line.items[line.count - 1] == '\r') { 533 line.count--; 534 } 535 return line; 536 } 537 538 int 539 isbdigit(int c) 540 { 541 if (c > 127) { 542 todo_p("unicode support"); 543 } 544 return c == '0' || c == '1'; 545 } 546 547 int 548 isodigit(int c) 549 { 550 if (c > 127) { 551 todo_p("unicode support"); 552 } 553 return c == '0' || c == '1' || c == '2' || c == '3' || 554 c == '4' || c == '5' || c == '6' || c == '7'; 555 } 556 557 int 558 is_digit_base(int c, int base) 559 { 560 if (base == 2) return isbdigit(c); 561 if (base == 8) return isodigit(c); 562 if (base == 10) return isddigit(c); 563 if (base == 16) return isxdigit(c); 564 fprintf(stderr, "Error: is_digit_base: invalid base %i\n", base); 565 return 0; 566 } 567 568 int 569 ctoi(int c) 570 { 571 if (c >= '0' && c <= '9') return c - '0'; 572 if (c >= 'A' && c <= 'Z') return c - 'A' + 10; 573 if (c >= 'a' && c <= 'z') return c - 'a' + 10; 574 fprintf(stderr, "Error: ctoi: invalid digit '%c'\n", c); 575 return 0; 576 } 577 578 #endif // DLIB_IMPLEMENTATION 579 580 #endif // DLIB_H