/* * requires POSIX.1-2008 * #define _POSIX_C_SOURCE 200809L */ /* * TODO: * - add a log interface to give user control of logging. * disable completely, log to file, log format, etc. * * - add an arg parsing api. * * - add real random api. and remove the rand() wrappers. */ #include #include typedef int8_t i8; typedef int16_t i16; typedef int32_t i32; typedef int64_t i64; typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef float f32; typedef double f64; #define _JRK_MAX_STRNLEN 1024 #define _JRK_DEFAULT_ALIGNMENT (2*sizeof(uintptr_t)) #define JRK_UNUSED(x) (void)x #define JRK_ARRLENGTH(arr) (sizeof(arr) / sizeof(arr[0])) #define JRK_MIN(a, b) ((a) < (b) ? (a) : (b)) #define JRK_MAX(a, b) ((a) > (b) ? (a) : (b)) #define jrk_return_defer(value) \ do { result = value; goto defer; } while(0) #define JRK_KILOBYTES(x) ((u64)x*1024) #define JRK_MEGABYTES(x) ((u64)JRK_KILOBYTES((u64)x*1024)) #define JRK_GIGABYTES(x) ((u64)JRK_MEGABYTES((u64)x*1024)) #define JRK_TERABYTES(x) ((u64)JRK_GIGABYTES((u64)x*1024)) #define JRK_TMPSTRINGS_ARR_CAPACITY 4 #define JRK_TMPSTRINGS_STR_CAPACITY JRK_KILOBYTES(1) typedef struct { u8 *data; u64 offset; u64 prev_offset; u64 capacity; } jrk_Arena; typedef struct { const char *data; u64 len; } jrk_String; #define JRK_STRFMT(s) (int)(s).len, (s).data bool jrk_is_power_of_two(uintptr_t); uintptr_t jrk_align_forward(uintptr_t, u64); void *jrk_ecalloc(u64, u64); void *jrk_erealloc(void*, u64); void *jrk_array_alloc_function_arena(u64, u64, void*); void *jrk_array_realloc_function_arena(void*, u64, u64, void*); jrk_Arena jrk_arena_create(u8*, u64); void *jrk_arena_push(jrk_Arena*, u64); char *jrk_arena_push_strf(jrk_Arena*, const char*, ...); void jrk_arena_reset(jrk_Arena*); void *jrk_arena_resize(jrk_Arena*, void*, u64, u64); i32 jrk_rand_num(i32); i32 jrk_rand_num_range(i32, i32); i32 jrk_fd_open_read(const char*); i32 jrk_fd_open_write(const char*); i32 jrk_fd_open_write_append(const char*); bool jrk_fd_size(i32, u64*); void jrk_fd_close(i32); jrk_String jrk_string_from_parts(const char*, u64); jrk_String jrk_string_trim_right(jrk_String); jrk_String jrk_string_trim_left(jrk_String); jrk_String jrk_string_trim(jrk_String); jrk_String jrk_string_from_cstr(const char*); #define jrk_string_from_lit(str_lit) jrk_string_from_parts((str_lit), sizeof((str_lit)) - 1) jrk_String jrk_string_chop_delim(jrk_String*, char delim); bool jrk_string_equals(jrk_String, jrk_String); /* takes the lowest len and matches up to that size */ bool jrk_string_equals_exact(jrk_String, jrk_String); /* must be same len to be equal */ char *jrk_tmpstrings_push(const char*); char *jrk_tmpstrings_pushf(const char*, ...); #ifndef jrk_die #define jrk_die(x) \ do { \ fprintf(stderr, "%s:%d: error: "x" \n", __FILE__, __LINE__); \ exit(69); \ } while (0) #endif #ifndef jrk_diev #define jrk_diev(x, ...) \ do { \ fprintf(stderr, "%s:%d: error: "x"\n", __FILE__, __LINE__, __VA_ARGS__); \ exit(69); \ } while (0) #endif #define jrk_edie(x) jrk_diev(x": %s", strerror(errno)) #define jrk_ediev(x, ...) jrk_diev(x": %s", __VA_ARGS__, strerror(errno)) #ifndef jrk_log #define jrk_log(x) \ do { \ fprintf(stderr, "info: "x"\n"); \ } while (0) #endif #ifndef jrk_logv #define jrk_logv(x, ...) \ do { \ fprintf(stderr, "info: "x"\n", __VA_ARGS__); \ } while (0) #endif #ifndef jrk_elog #define jrk_elog(x) jrk_log(x": %s", strerror(errno)) #endif #ifndef jrk_elogv #define jrk_elogv(x, ...) jrk_logv(x": %s", __VA_ARGS__, strerror(errno)) #endif #ifndef jrk_error #define jrk_error(x) \ do { \ fprintf(stderr, "error: "x"\n"); \ } while (0) #endif #ifndef jrk_errorv #define jrk_errorv(x, ...) \ do { \ fprintf(stderr, "error: "x"\n", __VA_ARGS__); \ } while (0) #endif #ifndef jrk_eerror #define jrk_eerror(x) jrk_errorv(x": %s", strerror(errno)) #endif #ifndef jrk_eerrorv #define jrk_eerrorv(x, ...) jrk_errorv(x": %s", __VA_ARGS__, strerror(errno)) #endif #define jrk_assert(c, msg) do { if (!(c)) jrk_die("jrk_assert: "msg); } while(0) #define jrk_assertv(c, msg, ...) do { if (!(c)) jrk_diev("jrk_assert: "msg, __VA_ARGS__); } while(0) #define jrk_shift(x, n) ((n)--, *(x)++) #define jrk_shift_loop(x, n, it) for (char *it = jrk_shift(x, n); n >= 0; it = jrk_shift(x, n)) #define jrk_emalloc(n) jrk_ecalloc(1, n) #define JRK_ARRAY_DEFAULT_INIT_CAPACITY 16 #ifndef JRK_ARRAY_DEFAULT_ALLOC_FN #define JRK_ARRAY_DEFAULT_ALLOC_FN jrk_ecalloc #endif #ifndef JRK_ARRAY_DEFAULT_REALLOC_FN #define JRK_ARRAY_DEFAULT_REALLOC_FN jrk_erealloc #endif #ifndef JRK_ARRAY_DEFAULT_FREE_FN #define JRK_ARRAY_DEFAULT_FREE_FN free #endif typedef void *(*jrk_array_alloc_function_t) (u64, u64, void*); typedef void *(*jrk_array_realloc_function_t) (void*, u64, u64, void*); #define __jrk_array_alloc(arr, count, len) (arr)->allocfn ? (arr)->allocfn((count), (len), (arr)->allocfn_user) : JRK_ARRAY_DEFAULT_ALLOC_FN((count), (len)) #define __jrk_array_realloc(arr, _len) (arr)->reallocfn ? (arr)->reallocfn((arr)->items, (arr)->len * sizeof((arr)->items[0]), (_len), (arr)->allocfn_user) : JRK_ARRAY_DEFAULT_REALLOC_FN((arr)->items, (_len)) #define jrk_array_prototype(type, fnname, typename) \ typedef struct { \ type *items; \ u64 len; \ u64 capacity; \ jrk_array_alloc_function_t allocfn; \ jrk_array_realloc_function_t reallocfn; \ void *allocfn_user; \ } jrk_Array_##typename; \ \ bool jrk_array_##fnname##_init_ex(jrk_Array_##typename*, u64, jrk_array_alloc_function_t, jrk_array_realloc_function_t, void*); \ bool jrk_array_##fnname##_init(jrk_Array_##typename*, u64); \ void jrk_array_##fnname##_destroy(jrk_Array_##typename*); \ bool jrk_array_##fnname##_setcap(jrk_Array_##typename*, u64); \ bool jrk_array_##fnname##_push(jrk_Array_##typename*, type); \ bool jrk_array_##fnname##_pushn(jrk_Array_##typename*, const type*, u64); #define jrk_array_impl(type, fnname, typename) \ bool jrk_array_##fnname##_init_ex(jrk_Array_##typename *arr, u64 capacity, jrk_array_alloc_function_t allocfn, jrk_array_realloc_function_t reallocfn, void *allocfn_user) \ { \ (arr)->allocfn = (allocfn) ? (allocfn) : NULL; \ (arr)->reallocfn = (reallocfn) ? (reallocfn) : NULL; \ (arr)->allocfn_user = (allocfn_user) ? (allocfn_user) : NULL; \ (arr)->len = 0; \ (arr)->capacity = (capacity); \ (arr)->items = __jrk_array_alloc((arr), (arr)->capacity, sizeof(type)); \ if (!(arr)->items) return false; \ memset((arr)->items, 0, (arr)->capacity * sizeof(type)); \ return true; \ } \ \ bool jrk_array_##fnname##_init(jrk_Array_##typename *arr, u64 capacity) \ { \ return jrk_array_##fnname##_init_ex(arr, capacity, NULL, NULL, NULL); \ } \ \ void jrk_array_##fnname##_destroy(jrk_Array_##typename *arr) \ { \ if ((arr)->items && !(arr)->allocfn) \ JRK_ARRAY_DEFAULT_FREE_FN((arr)->items); \ } \ \ bool jrk_array_##fnname##_setcap(jrk_Array_##typename *arr, u64 new_capacity) \ { \ if (new_capacity < (arr)->capacity) return false; \ (arr)->items = __jrk_array_realloc((arr), new_capacity * sizeof(type)); \ if (!(arr)->items) return false; \ memset((arr)->items + (arr)->len, 0, (new_capacity * sizeof(type)) - ((arr)->capacity * sizeof(type))); \ (arr)->capacity = new_capacity; \ return true; \ } \ \ bool jrk_array_##fnname##_push(jrk_Array_##typename *arr, type val) \ { \ if ((arr)->len+ 1 > (arr)->capacity) { \ while ((arr)->len + 1 > ((arr)->capacity)) \ (arr)->capacity = (arr->capacity) ? (arr)->capacity * 2 : JRK_ARRAY_DEFAULT_INIT_CAPACITY; \ (arr)->items = __jrk_array_realloc((arr), (arr)->capacity * sizeof(type)); \ if (!(arr)->items) return false; \ } \ (arr)->items[(arr)->len++] = (val); \ return true; \ } \ \ bool jrk_array_##fnname##_pushn(jrk_Array_##typename *arr, const type *vals, u64 count) \ { \ if ((arr)->len+ count > (arr)->capacity) { \ while ((arr)->len+ count > ((arr)->capacity)) \ (arr)->capacity = (arr->capacity) ? (arr)->capacity * 2 : JRK_ARRAY_DEFAULT_INIT_CAPACITY; \ (arr)->items = __jrk_array_realloc((arr), (arr)->capacity * sizeof(type)); \ if (!(arr)->items) return false; \ memset((arr)->items + (arr)->len, 0, count * sizeof(type)); \ } \ memcpy(&(arr)->items[(arr)->len], (vals), sizeof(type) * (count)); \ (arr)->len+= count; \ return true; \ } #define jrk_array_prototype_and_impl(type, fnname, typename) \ jrk_array_prototype(type, fnname, typename); \ jrk_array_impl(type, fnname, typename) #define jrk_array_foreach(type, it, arr) for (type *it = (arr)->items; it < (arr)->items + (arr)->len; ++it) jrk_array_prototype(char, string_builder, String_Builder) typedef jrk_Array_String_Builder jrk_String_Builder; #define jrk_sb_append_null(sb) jrk_array_string_builder_push((sb), 0) #define jrk_sb_append_cstr(sb, str) jrk_sb_appendf((sb), (str)) #define jrk_sb_append_buf(sb, buf, bufsz) jrk_array_string_builder_pushn((sb), (buf), (bufsz)) #define jrk_sb_append_string(sb, string) jrk_sb_append_buf((sb), (string).data, (string).len) #define jrk_sb_append_lit(sb, str_lit) jrk_sb_append_buf((sb), (str_lit), sizeof((str_lit)) - 1) #define jrk_sb_init_ex(sb, size, allocfn, reallocfn, userarg) jrk_array_string_builder_init_ex((sb), (size), (allocfn), (reallocfn), (userarg)) #define jrk_sb_init_arena(sb, size, arena) jrk_sb_init_ex((sb), (size), jrk_array_alloc_function_arena, jrk_array_realloc_function_arena, (void *)(arena)) #define jrk_sb_destroy(sb) jrk_array_string_builder_destroy((sb)) #define jrk_sb_to_string(sb) jrk_string_from_parts((sb).items, (sb).len) bool jrk_sb_appendf(jrk_String_Builder*, const char*, ...); bool jrk_sb_append_buf_at(jrk_String_Builder*, const char*, u64, u64); bool jrk_sb_fd_write(jrk_String_Builder*, i32); bool jrk_sb_write_entire_file(jrk_String_Builder *, const char *); bool jrk_sb_fd_read_all(jrk_String_Builder*, i32); bool jrk_sb_read_entire_file(jrk_String_Builder*, const char*); /* * http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param * for 64 bit hash */ #define JRK_FNV1A_64_OFFSET_BASIS 0xCBF29CE484222325 #define JRK_FNV1A_64_PRIME 0x100000001B3 u64 jrk_fnv1a_64(const u8*, u64); #ifndef JRK_HM_DEFAULT_ALLOC_FN #define JRK_HM_DEFAULT_ALLOC_FN jrk_ecalloc #endif #ifndef JRK_HM_DEFAULT_FREE_FN #define JRK_HM_DEFAULT_FREE_FN free #endif typedef jrk_array_alloc_function_t jrk_hm_alloc_function_t; typedef jrk_array_realloc_function_t jrk_hm_realloc_function_t; #define __jrk_hm_alloc(hm, count, len) (hm)->allocfn ? (hm)->allocfn((count), (len), (hm)->allocfn_user) : JRK_HM_DEFAULT_ALLOC_FN((count), (len)) #define jrk_hm_prototype(type, fnname, typename) \ typedef struct { \ jrk_String key; \ type val; \ } jrk_Hash_Map_Item_##typename; \ \ typedef struct { \ jrk_Hash_Map_Item_##typename *items; \ u64 len; \ u64 capacity; \ jrk_hm_alloc_function_t allocfn; \ void *allocfn_user; \ } jrk_Hash_Map_##typename; \ \ bool jrk_hm_##fnname##_grow(jrk_Hash_Map_##typename*); \ jrk_Hash_Map_Item_##typename *jrk_hm_##fnname##_get_spot(jrk_Hash_Map_##typename*,jrk_String); \ bool jrk_hm_##fnname##_push(jrk_Hash_Map_##typename*,jrk_String,type); \ bool jrk_hm_##fnname##_push_no_alloc(jrk_Hash_Map_##typename*,jrk_String,type); \ bool jrk_hm_##fnname##_get(const jrk_Hash_Map_##typename*,jrk_String,type*); \ bool jrk_hm_##fnname##_init(jrk_Hash_Map_##typename*,u64); \ bool jrk_hm_##fnname##_init_ex(jrk_Hash_Map_##typename*,u64,jrk_hm_alloc_function_t,void*); #define jrk_hm_impl(type, fnname, typename) \ jrk_Hash_Map_Item_##typename * \ jrk_hm_##fnname##_get_spot(jrk_Hash_Map_##typename *hm, jrk_String key) \ { \ jrk_Hash_Map_Item_##typename *result = NULL; \ u64 idx = jrk_fnv1a_64((u8 *)key.data, key.len) % hm->capacity; \ result = &hm->items[idx]; \ for (; result < &hm->items[hm->capacity] && result->key.len > 0 && !jrk_string_equals_exact(key, result->key); ++result); \ if (result == &hm->items[hm->capacity]) { \ for (result = &hm->items[0]; result < &hm->items[idx] && result->key.len > 0; ++result); \ } \ return result; \ } \ \ bool \ jrk_hm_##fnname##_push_no_alloc(jrk_Hash_Map_##typename *hm, jrk_String key, type val) \ { \ if (hm->len + 1 > hm->capacity) { \ if (!jrk_hm_##fnname##_grow(hm)) return false; \ } \ jrk_Hash_Map_Item_##typename *item = jrk_hm_##fnname##_get_spot(hm, key); \ jrk_Hash_Map_Item_##typename new_item = (jrk_Hash_Map_Item_##typename) { \ .key = key, \ .val = val \ }; \ *item = new_item; \ hm->len++; \ return true; \ } \ \ bool \ jrk_hm_##fnname##_push(jrk_Hash_Map_##typename *hm, jrk_String key, type val) \ { \ if (hm->len + 1 > hm->capacity) { \ if (!jrk_hm_##fnname##_grow(hm)) return false; \ } \ jrk_Hash_Map_Item_##typename *item = jrk_hm_##fnname##_get_spot(hm, key); \ if (jrk_string_equals_exact(key, item->key)) { \ item->val = val; \ return true; \ } \ char *allocd_key = __jrk_hm_alloc(hm, key.len, 1); \ if (!allocd_key) return false; \ memcpy(allocd_key, key.data, key.len); \ jrk_String new_key = (jrk_String) { \ .data = allocd_key, \ .len = key.len, \ }; \ jrk_Hash_Map_Item_##typename new_item = (jrk_Hash_Map_Item_##typename) { \ .key = new_key, \ .val = val \ }; \ *item = new_item; \ hm->len++; \ return true; \ } \ \ bool \ jrk_hm_##fnname##_get(const jrk_Hash_Map_##typename *hm, jrk_String key, type *outval) \ { \ u64 idx = jrk_fnv1a_64((u8 *)key.data, key.len) % hm->capacity; \ jrk_Hash_Map_Item_##typename *item = &hm->items[idx]; \ if (jrk_string_equals_exact(key, item->key)) { \ *outval = item->val; \ return true; \ } \ for (; item < &hm->items[hm->capacity] && !jrk_string_equals_exact(key, item->key); ++item) { \ if (item->key.len == 0) \ return false; \ } \ if (item == &hm->items[hm->capacity]) { \ for (item = &hm->items[0]; item < &hm->items[idx] && !jrk_string_equals_exact(key, item->key); ++item); \ if (item == &hm->items[idx]) \ return false; \ } \ *outval = item->val; \ return true; \ } \ \ bool \ jrk_hm_##fnname##_init_ex(jrk_Hash_Map_##typename *hm, u64 capacity, jrk_hm_alloc_function_t allocfn, void *allocfn_user) \ { \ hm->allocfn = allocfn ? allocfn : NULL; \ hm->allocfn_user = allocfn_user ? allocfn_user : NULL; \ hm->len = 0; \ hm->capacity = capacity; \ hm->items = __jrk_hm_alloc(hm, hm->capacity, sizeof(jrk_Hash_Map_Item_##typename)); \ if (!hm->items) return false; \ return true; \ } \ \ bool \ jrk_hm_##fnname##_init(jrk_Hash_Map_##typename *hm, u64 capacity) \ { \ return jrk_hm_##fnname##_init_ex(hm, capacity, NULL, NULL); \ } \ \ void \ jrk_hm_##fnname##_destroy(jrk_Hash_Map_##typename *hm) \ { \ if (hm->items && !hm->allocfn) { \ for (u64 i = 0; i < hm->capacity; ++i) { \ if (hm->items[i].key.data) \ JRK_HM_DEFAULT_FREE_FN((void *)hm->items[i].key.data); \ } \ JRK_HM_DEFAULT_FREE_FN(hm->items); \ } \ } \ \ bool \ jrk_hm_##fnname##_grow(jrk_Hash_Map_##typename *hm) \ { \ jrk_Hash_Map_##typename tmp_map = {0}; \ tmp_map.capacity = hm->capacity * 2; \ tmp_map.items = __jrk_hm_alloc(hm, tmp_map.capacity, sizeof(jrk_Hash_Map_Item_##typename)); \ if (!tmp_map.items) return false; \ tmp_map.len = hm->len; \ for (u64 i = 0; i < hm->capacity; ++i) { \ jrk_Hash_Map_Item_##typename *item = &hm->items[i]; \ if (item->key.len > 0) { \ if (!jrk_hm_##fnname##_push_no_alloc(&tmp_map, item->key, item->val)) { \ free(tmp_map.items); \ return false; \ } \ } \ } \ if (hm->items && !hm->allocfn) \ JRK_HM_DEFAULT_FREE_FN(hm->items); \ hm->items = tmp_map.items; \ hm->capacity *= 2; \ hm->len = tmp_map.len; \ return true; \ } #define jrk_hm_prototype_and_impl(type, fnname, typename) \ jrk_hm_prototype(type, fnname, typename); \ jrk_hm_impl(type, fnname, typename) #ifdef JRK_IMPLEMENTATION #include #include #include #include #include #include #include #include #include bool jrk_is_power_of_two(uintptr_t x) { return (x & (x-1)) == 0; } uintptr_t jrk_align_forward(uintptr_t ptr, u64 alignment) { uintptr_t result = ptr; jrk_assertv(jrk_is_power_of_two(alignment), "jrk_align_forward(%p, %lu): alignment %lu is not a power of 2!", (void *)ptr, alignment, alignment); /* Same as (p % a) but faster as 'a' is a power of two */ uintptr_t modulo = ptr & (alignment - 1); if (modulo != 0) { result += alignment - modulo; } return result; } jrk_array_impl(char, string_builder, String_Builder) void * jrk_array_alloc_function_arena(u64 size, u64 count, void *user) { void *result = NULL; jrk_Arena *arena = (jrk_Arena *) user; result = jrk_arena_push(arena, size * count); return result; } void * jrk_array_realloc_function_arena(void *ptr, u64 old_size, u64 new_size, void *user) { void *result = NULL; jrk_Arena *arena = (jrk_Arena *) user; result = jrk_arena_resize(arena, ptr, old_size, new_size); return result; } bool jrk_sb_appendf(jrk_String_Builder *sb, const char *fmt, ...) { char buf[JRK_KILOBYTES(1)]; va_list args; va_start(args, fmt); i32 buf_sz = vsnprintf(buf, sizeof(buf), fmt, args); if (buf_sz < 0) { jrk_eerrorv("jrk_sb_appendf(%p, %s): failed to create full string from '%s'", (void *) sb, fmt, fmt); return false; } va_end(args); i32 actual_sz = 0; if ((u32) buf_sz > JRK_ARRLENGTH(buf)) { actual_sz = JRK_ARRLENGTH(buf); jrk_errorv("jrk_sb_appendf(%p, %s): pushing '%s' into sb caused truncation of %ld bytes", (void *) sb, fmt, fmt, buf_sz - JRK_ARRLENGTH(buf)); return false; } else { actual_sz = buf_sz; } if (!jrk_array_string_builder_pushn(sb, buf, actual_sz)) { jrk_errorv("jrk_sb_appendf(%p, %s): failed to push '%s' into sb", (void *) sb, fmt, fmt); return false; } return true; } bool jrk_sb_append_buf_at(jrk_String_Builder *sb, const char *buf, u64 buf_sz, u64 idx) { if (idx > sb->len) { jrk_errorv("jrk_sb_append_buf_at(%p, %s, %ld, %ld): idx is greater than sb.size", (void *)sb, buf, buf_sz, idx); return false; } if (sb->len + (buf_sz) > sb->capacity) { while (sb->len + (buf_sz) > sb->capacity) { sb->capacity *= 2; } sb->items = __jrk_array_realloc(sb, sb->capacity); if (!sb->items) return false; memset(sb->items + sb->len, 0, sb->capacity - sb->len); } memmove(sb->items + idx + buf_sz, sb->items + idx, sb->len - idx); memcpy(sb->items + idx, buf, buf_sz); sb->len += buf_sz; return true; } bool jrk_sb_fd_read_all(jrk_String_Builder *sb, i32 fd) { bool result = true; u64 filesize = 0; if (!jrk_fd_size(fd, &filesize)) return false; char *buf = jrk_emalloc(filesize); if (read(fd, buf, filesize) < 0) { jrk_eerrorv("jrk_sb_fd_read_all(%p, %d)", (void *)sb, fd); jrk_return_defer(false); } if (!jrk_sb_append_buf(sb, buf, filesize)) jrk_return_defer(false); defer: free(buf); return result; } bool jrk_sb_fd_write(jrk_String_Builder *sb, i32 fd) { i64 result; result = write(fd, (void *) sb->items, sb->len); if (result < 0) { jrk_eerrorv("jrk_sb_fd_write_all(%p, %d)", (void *)sb, fd); return -1; } return result; } bool jrk_sb_read_entire_file(jrk_String_Builder *sb, const char *filename) { bool result = true; i32 fd = jrk_fd_open_read(filename); if (fd < 0) return false; if (!jrk_sb_fd_read_all(sb, fd)) jrk_return_defer(false); defer: jrk_fd_close(fd); return result; } bool jrk_sb_write_entire_file(jrk_String_Builder *sb, const char *filename) { bool result = true; i32 fd = jrk_fd_open_write(filename); if (fd < 0) return false; if (!jrk_sb_fd_write(sb, fd)) jrk_return_defer(false); defer: jrk_fd_close(fd); return result; } void * jrk_ecalloc(u64 nmemb, u64 size) { void *p; if (!(p = calloc(nmemb, size))) jrk_ediev("jrk_ecalloc(%ld, %ld): buy more ram lol", nmemb, size); return p; } void * jrk_erealloc(void *ptr, u64 size) { void *p; if (!(p = realloc(ptr, size))) jrk_ediev("jrk_erealloc(%p, %ld): buy more ram lol", ptr, size); return p; } /* NOTE: only allocate space for translation units with JRK_IMPLEMENTATION */ static char jrk__tmpstrings[JRK_TMPSTRINGS_ARR_CAPACITY][JRK_TMPSTRINGS_STR_CAPACITY]; static u32 jrk__tmpstrings_idx = 0; char * jrk_tmpstrings_push(const char *str) { char *result; result = jrk_tmpstrings_pushf(str); return result; } char * jrk_tmpstrings_pushf(const char *fmt, ...) { char *result = jrk__tmpstrings[jrk__tmpstrings_idx]; va_list args; va_start(args, fmt); vsnprintf(result, JRK_TMPSTRINGS_STR_CAPACITY, fmt, args); va_end(args); if (++jrk__tmpstrings_idx >= JRK_TMPSTRINGS_ARR_CAPACITY) jrk__tmpstrings_idx = 0; return result; } jrk_Arena jrk_arena_create(u8 *buffer, u64 buffer_count) { jrk_Arena result = {0}; result.data = buffer; result.capacity = buffer_count; result.offset = 0; result.prev_offset = 0; return result; } void * jrk_arena_push(jrk_Arena *arena, u64 n) { u8 *aligned = (u8 *) jrk_align_forward((uintptr_t)(arena->data + arena->offset), _JRK_DEFAULT_ALIGNMENT); u64 new_offset = aligned - arena->data; if (new_offset + n > arena->capacity) { jrk_errorv("jrk_arena_push(%p, %ld): arena push requires %ld bytes but has a capacity of %ld bytes", (void *) arena, n, new_offset + n, arena->capacity); return NULL; } void *result = &arena->data[new_offset]; arena->prev_offset = arena->offset; arena->offset = new_offset + n; memset(result, 0, n); return result; } char * jrk_arena_push_strf(jrk_Arena *arena, const char *fmt, ...) { char *result = NULL; va_list args; va_start(args, fmt); i32 n = vsnprintf(NULL, 0, fmt, args); va_end(args); result = jrk_arena_push(arena, (u64) n + 1); if (!result) return NULL; va_start(args, fmt); vsnprintf(result, n + 1, fmt, args); va_end(args); return result; } void * jrk_arena_resize(jrk_Arena *arena, void *old, u64 old_size, u64 new_size) { u8 *result = NULL; if (new_size < old_size) { jrk_errorv("jrk_arena_resize(%p, %p, %ld, %ld): new_size is smaller than the old_size", (void *) arena, old, old_size, new_size); return result; } if (arena->offset + (new_size - old_size) > arena->capacity) { jrk_errorv("jrk_arena_resize(%p, %p, %ld, %ld): arena resize requires %ld bytes but has a capacity of %ld bytes", (void *) arena, old, old_size, new_size, arena->offset + (new_size - old_size), arena->capacity); return result; } if (old == &arena->data[arena->prev_offset]) { result = old; arena->prev_offset = arena->offset; arena->offset += new_size; memset(&arena->data[arena->prev_offset], 0, new_size - old_size); } else { result = &arena->data[arena->offset]; arena->prev_offset = arena->offset; arena->offset += new_size; if (arena->offset > arena->capacity) { jrk_errorv("jrk_arena_resize(%p, %p, %ld, %ld): arena resize requires %ld bytes but has a capacity of %ld bytes", (void *) arena, old, old_size, new_size, arena->offset, arena->capacity); return NULL; } memmove(result, old, old_size); memset(result + old_size, 0, new_size - old_size); } return (void *) result; } void jrk_arena_reset(jrk_Arena *arena) { arena->offset = 0; arena->prev_offset = 0; memset(arena->data, 0, arena->capacity); } bool jrk_fd_size(i32 fd, u64 *size) { if (!size) { jrk_errorv("jrk_fd_size(%d, %p): size ptr is NULL", fd, (void *) size); return false; } struct stat statbuf = {0}; if (fstat(fd, &statbuf) < 0) { jrk_eerrorv("jrk_fd_size(%d, %p)", fd, (void *) size); return false; } *size = statbuf.st_size; return true; } void jrk_fd_close(i32 fd) { close(fd); } i32 jrk_fd_open_read(const char *path) { i32 result = open(path, O_RDONLY); if (result < 0) { jrk_eerrorv("jrk_fd_open_read(%s)", path); return -1; } return result; } i32 jrk_fd_open_write(const char *path) { i32 result = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (result < 0) { jrk_eerrorv("jrk_fd_open_write(%s)", path); return -1; } return result; } i32 jrk_fd_open_write_append(const char *path) { i32 result = open(path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (result < 0) { jrk_eerrorv("jrk_fd_open_write_append(%s)", path); return -1; } return result; } jrk_String jrk_string_from_parts(const char *data, u64 len) { jrk_String result = {0}; result.data = data; result.len = len; return result; } jrk_String jrk_string_from_cstr(const char *data) { jrk_String result = {0}; result.data = data; result.len = strnlen(data, _JRK_MAX_STRNLEN); return result; } jrk_String jrk_string_trim_right(jrk_String str) { u64 i = 0; for (; i < str.len && isspace(str.data[str.len - 1 - i]); ++i); return jrk_string_from_parts(str.data, str.len - i); } jrk_String jrk_string_trim_left(jrk_String str) { u64 i = 0; for (; i < str.len && isspace(str.data[i]); ++i); return jrk_string_from_parts(str.data + i, str.len - i); } jrk_String jrk_string_trim(jrk_String str) { return jrk_string_trim_right(jrk_string_trim_left(str)); } jrk_String jrk_string_chop_delim(jrk_String *str, char delim) { u64 i = 0; while (i < str->len && str->data[i] != delim) ++i; jrk_String result = jrk_string_from_parts(str->data, i); if (i < str->len) { str->data += i + 1; str->len -= i + 1; } else { str->data += i; str->len -= i; } return result; } bool jrk_string_equals(jrk_String a, jrk_String b) { for (u64 i = 0; i < JRK_MIN(a.len, b.len); ++i) if (a.data[i] != b.data[i]) return false; return true; } bool jrk_string_equals_exact(jrk_String a, jrk_String b) { if (a.len != b.len) return false; for (u64 i = 0; i < a.len; ++i) if (a.data[i] != b.data[i]) return false; return true; } u64 jrk_fnv1a_64(const u8 *data, u64 size) { u64 result = JRK_FNV1A_64_OFFSET_BASIS; for (u64 i = 0; i < size; ++i) { result ^= data[i]; result *= JRK_FNV1A_64_PRIME; } return result; } i32 jrk_rand_num(i32 upbound) { return rand() % upbound + 1; } i32 jrk_rand_num_range(i32 min, i32 max) { return rand() % (max - min + 1) + min; } #endif /* JRK_IMPLEMENTATION */