#include #include #include #include #include "std.h" int cp(const char src[static restrict 1], const char dst[static restrict 1]) { char c[4096]; FILE *fsrc = NULL, *fdst = NULL; int errnum = 0; if (!(fsrc = fopen(src, "r"))) { errnum = errno; goto err; } if (!(fdst = fopen(dst, "w"))) { errnum = errno; goto err; } while (!feof(fsrc)) { size_t bytes = fread(c, 1, sizeof(c), fsrc); if (bytes) { fwrite(c, 1, bytes, fdst); } } fclose(fsrc); fclose(fdst); return errnum; err: if (fsrc) fclose(fsrc); if (fdst) fclose(fdst); return errno; } void * ecalloc(size_t nmemb, size_t size) { void *p; if (!(p = calloc(nmemb, size))) { fprintf(stderr, "%s\n", strerror(errno)); abort(); } return p; } void * emalloc(size_t size) { void *p; if (!(p = malloc(size))) { fprintf(stderr, "%s\n", strerror(errno)); abort(); } return p; } String string_alloc(Region *r, size_t cap) { return (String) { .len = 0, .cap = cap, .buf = r ? region_malloc(r, cap * sizeof(char)) : STD_MALLOC(cap * sizeof(char)), }; } String string_concat(Region *r, String a, String b) { String s = string_alloc(r, a.len + b.len + 1); memcpy(s.buf, a.buf, a.len * sizeof(char)); memcpy(&s.buf[a.len], b.buf, b.len * sizeof(char)); s.buf[a.len + b.len] = '\0'; s.len = a.len + b.len; return s; } String string_copy(Region *r, String src) { String dst = (String) { .len = src.len * sizeof(char), .cap = src.len, .buf = NULL, }; size_t bufsize = src.len * sizeof(char); dst.buf = r ? region_malloc(r, bufsize) : STD_MALLOC(bufsize); memcpy(dst.buf, src.buf, bufsize); return dst; } String string_from_cstr(Region *r, const char *str, size_t len) { String result = string_alloc(r, len + 1); result.len = len; result.cap = len; memcpy(result.buf, str, len); result.buf[len] = '\0'; return result; } String string_from_strview(Region *r, const StrView str) { String result = string_alloc(r, str.len + 1); result.cap = str.len + 1; result.len = str.len; memcpy(result.buf, str.str, str.len); result.buf[str.len] = '\0'; return result; } void string_downcase(String str) { for (size_t i = 0; i < str.len; i++) { if (str.buf[i] >= 'A' && str.buf[i] <= 'Z') { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" str.buf[i] += 'a' - 'A'; #pragma GCC diagnostic pop } } } StrView strview_from_cstr(char *cstr) { return (StrView) { .len = strlen(cstr), .str = cstr, }; } StrView strview_from_string(const String str) { return (StrView) { .len = str.len, .str = str.buf, }; } bool strview_is_null(StrView s) { return !s.str; } StrView make_strview(size_t len, const char *str) { return (StrView) { .len = len, .str = str, }; } int strview_cmp(StrView s1, StrView s2) { unsigned long less = s1.len < s2.len; unsigned long min_length = s1.len * less + s2.len * (1 - less); return memcmp(s1.str, s2.str, min_length); } StrView strview_next_tok(StrView str[static 1], char delim) { size_t idx = 0; StrView result = { 0 }; if (str->len == 0) return result; #if defined(__SSE__) || defined(__SSE2__) || defined(__SSE3__) __m128i _toks = _mm_set1_epi8(delim); if (str->len >= 16) { for (idx = 0; idx < str->len && idx + 16 < str->len; idx += 16) { __m128i _chars, _mask; _chars = _mm_loadu_si128((__m128i *)&str->str[idx]); _mask = _mm_cmpeq_epi8(_chars, _toks); #if defined(__SSE4_1__) int32_t found = _mm_testz_si128(_mask, _mask); if (found == 0) break; #else int32_t found; found = _mm_movemask_epi8(_mask); if (found) break; #endif /* defined(__SSE4_1__) */ } } #endif /* defined(__SSE__) || defined(__SSE2__) || defined(__SSE3__) */ for (; idx < str->len; idx++) { if (str->str[idx] == delim) { while (idx < str->len && str->str[idx] == delim) idx++; idx--; break; } } result.len = idx; result.str = str->str; str->len -= idx + 1; str->str += idx + 1; if (idx == str->len) { str->len = 0; str->str = NULL; } return result; } bool strview_contains(const StrView s, const char *charbag, const size_t len) { bool result = false; for (int i = 0; i < s.len; i++) { for (int j = 0; j < len; j++) { result |= s.str[i] == charbag[j]; } } return result; } bool strview_ends_with(StrView end, StrView str) { if (end.len > str.len) return false; return strncmp(&str.str[str.len - end.len], end.str, end.len) == 0; } StrView strview_next_line(StrView str[static 1]) { return strview_next_tok(str, '\n'); } StrView strview_next_word(StrView str[static 1]) { size_t idx, ws; StrView result = { 0 }; if (str->len == 0) { return result; } for (ws = 1, idx = 0; idx < str->len; idx++) { if (isspace(str->str[idx])) { while (idx + ws - 1 < str->len && isspace(str->str[idx + ws - 1])) { ws++; } ws--; break; } } /* Something is wrong */ result.len = idx; result.str = str->str; if (idx + ws - 1 == str->len) { str->len = 0; str->str = NULL; return result; } str->len -= idx + ws; str->str += idx + ws; return result; } void strview_trim(StrView str[static 1]) { int i, j; for (i = 0; i < str->len; i++) { if (!isspace(str->str[i])) break; } for (j = (int)str->len - 1; j >= 0; j--) { if (!isspace(str->str[j])) break; } j = (int)str->len - 1 - j; str->str += i; str->len -= (size_t)i + (size_t)j; } void strview_trim_chars( StrView str[static 1], const char *charbag, const size_t len) { int i, j; for (i = 0; i < str->len; i++) { if (!strview_contains( (StrView) { .str = charbag, .len = len }, &str->str[i], 1)) { break; } } for (j = (int)str->len - 1; j > i; j--) { if (!strview_contains( (StrView) { .str = charbag, .len = len }, &str->str[j], 1)) { break; } } str->str += i; str->len = (size_t)j + 1; if (str->len) { str->len -= (size_t)i; } } bool strview_starts_with(StrView start, StrView str) { if (start.len > str.len) return false; return strncmp(str.str, start.str, start.len) == 0; } StrView strview_strcat(Region *r, StrView x, StrView y) { char *cat; cat = region_malloc(r, (x.len + y.len) * sizeof(char)); memcpy(cat, x.str, x.len); memcpy(&cat[x.len + 1], y.str, y.len); return (StrView) { .len = x.len + y.len, .str = cat }; } bool strview_strtoi(StrView str, int i[static 1]) { char *endptr = NULL; int result; result = (int)strtol(str.str, &endptr, 10); if ((result == 0 && endptr == str.str) || errno == ERANGE) { return false; } else { *i = result; return true; } } bool strview_strtoul(StrView str, unsigned long ul[static 1]) { char *endptr = NULL; unsigned long result; result = strtoul(str.str, &endptr, 10); if ((result == 0 && endptr == str.str) || errno == ERANGE) { return false; } else { *ul = result; return true; } } bool strview_strtol(StrView str, long l[static 1]) { char *endptr = NULL; long result; result = strtol(str.str, &endptr, 10); if ((result == 0 && endptr == str.str) || errno == ERANGE) { return false; } else { *l = result; return true; } } bool strview_strtod(StrView str, double d[static 1]) { char *endptr = NULL; double result; result = strtod(str.str, &endptr); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" if ((result == 0 && endptr == str.str) || errno == ERANGE) { return false; } else { *d = result; return true; } #pragma GCC diagnostic pop } StrView strview_take(StrView str[static 1], size_t n) { StrView result = { 0 }; if (str->len >= n) { result.len = n; result.str = str->str; str->len -= n; str->str = &str->str[n]; } else { result.len = str->len; result.str = str->str; str->len = 0; str->str = &str->str[str->len - 1]; } return result; } bool string_read(Region *r, FILE stream[static 1], String contents[static 1]) { long end, start; /* This is not guaranteed to return the number of characters in * stream, it would be better to read the file in chunks or write a * simple buffered input interface */ start = ftell(stream); fseek(stream, 0, SEEK_END); end = ftell(stream); fseek(stream, 0, SEEK_SET); *contents = string_alloc(r, (size_t)(end - start)); if (fread((void *)contents->buf, sizeof(char), contents->len, stream) < contents->len) { return false; } return true; } bool strview_read(Region *r, FILE stream[static 1], StrView contents[static 1]) { long end, start; /* This is not guaranteed to return the number of characters in * stream, it would be better to read the file in chunks or write a * simple buffered input interface */ start = ftell(stream); fseek(stream, 0, SEEK_END); end = ftell(stream); fseek(stream, 0, SEEK_SET); contents->len = (size_t)(end - start); contents->str = region_malloc(r, contents->len); if (fread((void *)contents->str, sizeof(char), contents->len, stream) < contents->len) { return false; } return true; } bool strview_read_file( Region *r, const char filename[static 1], StrView contents[static 1]) { FILE *f; if (!(f = fopen(filename, "rb"))) return false; if (!strview_read(r, f, contents)) return false; fclose(f); return contents; } Region * region_alloc(size_t bcap) { Region *region = STD_MALLOC(sizeof(Region)); *region = (Region) { .bcap = bcap, .buf = STD_MALLOC(bcap), .next = 0, }; memset(region->buf, 0, bcap); return region; } internal inline bool is_power_of_two(uintptr_t x) { return (x & (x - 1)) == 0; } /* https://www.gingerbill.org/article/2019/02/08/memory-allocation-strategies-002/ */ internal uintptr_t align_forward(uintptr_t ptr, size_t align) { uintptr_t p, a, modulo; STD_ASSERT(is_power_of_two(align)); p = ptr; a = (uintptr_t)align; modulo = p & (a - 1); if (modulo != 0) { p += a - modulo; } return p; } internal void * region_malloc_align(Region *r, size_t bsize, size_t alignment) { void *ptr; if (r == NULL) { return STD_MALLOC(bsize); } uintptr_t boffset = align_forward( (uintptr_t)r->buf + (uintptr_t)r->bsize, alignment); boffset -= (uintptr_t)r->buf; if (bsize + boffset > r->bcap) { if (!r->next) { if (bsize > r->bcap) { r->next = region_alloc(bsize + alignment); } else { r->next = region_alloc(r->bcap + alignment); } } return region_malloc_align(r->next, bsize, alignment); } ptr = (void *)((uintptr_t)r->buf + (uintptr_t)boffset); r->bsize = bsize + boffset; return ptr; } void * region_malloc(void *r, size_t bsize) { return region_malloc_align(r, bsize, 16); } void region_reset(Region *r) { Region *tmp, *next; for (tmp = r; tmp; tmp = next) { next = tmp->next; tmp->bsize = 0; } } void region_free(Region *r) { Region *tmp, *next; for (tmp = r; tmp; tmp = next) { next = tmp->next; STD_FREE(tmp->buf); STD_FREE(tmp); } } MemPool pool_alloc(Region *r, size_t count, size_t chunk_size) { MemPool p = { .r = r, .capacity = count, .chunk_size = chunk_size, .buf = r ? region_malloc(r, count * chunk_size) : STD_MALLOC(count * chunk_size), }; memset(p.buf, 0, count * chunk_size); return p; } void * pool_malloc(MemPool *pool) { if (pool->len == pool->capacity) { abort(); } return &pool->buf + (pool->len++) * pool->chunk_size; } void pool_free_all(MemPool *pool) { free(pool->buf); } void pool_reset(MemPool *pool) { pool->len = 0; }