/* * TODO: * - remove assertions from here * nothing in here should crash the program. * jrk_die macros should always die, just don't call it here * remove JRK_NO_EXIT_ON_DIE, i dont think its elegant. it is * just a bandaid fix for the above issues. * * - look at at the jrk_sv_chop_delim_loop stuff. there must * must be a better way of handling things. * * NOTES: * To avoid the application from crashing when calling the die macros * you can define JRK_NO_EXIT_ON_DIE. This will convert all die macros * to log calls. It is now up to the caller to handle the errors properly. * Most functions that can fail will return a bool and will log themselves, * which should make error handling easy. Be aware that some will return a * negative int to indicate failure. These are normally functions that call * into POSIX and return sizes as to be conformant to those POSIX syscalls. */ #include #include #ifdef JRK_IMPLEMENTATION_WITH_SHORTNAMES #define JRK_IMPLEMENTATION #define JRK_SHORTNAMES #endif // JRK_IMPLEMENTATION_WITH_SHORTNAMES 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_UNUSED(x) (void)x #define JRK_ARRLENGTH(arr) (sizeof(arr) / sizeof(arr[0])) #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 { char *items; u64 count; u64 capacity; } jrk_StringBuilder; typedef struct { char *data; u64 count; } jrk_StringView; void *jrk_ecalloc(u64, u64); void *jrk_erealloc(void*, u64); jrk_Arena jrk_arena_create(u8*, u64); void *jrk_arena_push(jrk_Arena*, u64); char *jrk_arena_push_strf(jrk_Arena*, char*, ...); void jrk_arena_reset(jrk_Arena*); void *jrk_arena_resize(jrk_Arena*, void*, u64, u64); bool jrk_arena_sb_appendf(jrk_Arena*, jrk_StringBuilder*, const char*, ...); bool jrk_arena_sb_append_buf_at(jrk_Arena*, jrk_StringBuilder*, char*, u64, u64); i32 jrk_rand_num(i32); i32 jrk_rand_num_range(i32, i32); i32 jrk_fd_open_read(char*); i32 jrk_fd_open_write(char*); i32 jrk_fd_open_write_append(char*); u64 jrk_fd_size(i32); void jrk_fd_close(i32); i32 jrk_sb_appendf(jrk_StringBuilder*, const char*, ...); bool jrk_sb_fd_read_all(jrk_StringBuilder*, i32); i64 jrk_sb_fd_write_all(jrk_StringBuilder*, i32); bool jrk_sb_write_file(jrk_StringBuilder*, char*); bool jrk_sb_read_entire_file(jrk_StringBuilder*, char*); void jrk_sb_append_buf_at(jrk_StringBuilder*, char*, u64, u64); jrk_StringView jrk_sv_from_parts(char*, u64); jrk_StringView jrk_sv_trim_right(jrk_StringView); jrk_StringView jrk_sv_trim_left(jrk_StringView); jrk_StringView jrk_sv_trim(jrk_StringView); jrk_StringView jrk_sv_from_cstr(char*); jrk_StringView jrk_sv_chop_delim(jrk_StringView*, char delim); char *jrk_tmpstrings_push(char*); char *jrk_tmpstrings_pushf(char*, ...); /* XXX: im not thrilled with this api, the memory can get messy real fast */ #define jrk_sv_chop_delim_loop(sv, it, delim) \ for (jrk_StringView it = jrk_sv_chop_delim(sv, delim); it.count != 0; it = jrk_sv_chop_delim(sv, delim)) #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 /* NOTE: no shortname as 'assert' is just too generic */ #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_sb_to_sv(sb) jrk_sv_from_parts((sb).items, (sb).count) #define JRK_DA_DEFAULT_INIT_CAPACITY 16 #define jrk_da_reserve(da, expected_capacity) \ do { \ if ((expected_capacity) > (da)->capacity) { \ if ((da)->capacity == 0) { \ (da)->capacity = (expected_capacity) ? (expected_capacity) : JRK_DA_DEFAULT_INIT_CAPACITY; \ } \ while ((expected_capacity) > (da)->capacity) { \ (da)->capacity *= 2; \ } \ (da)->items = jrk_erealloc((da)->items, (da)->capacity * sizeof(*(da)->items)); \ } \ } while (0) // NOTE: caller should verify (da)->items is valid #define jrk_arena_da_reserve(arena, da, expected_capacity) \ do { \ if ((da)->capacity == 0) { \ (da)->capacity = (expected_capacity) ? (expected_capacity) : JRK_DA_DEFAULT_INIT_CAPACITY; \ (da)->items = jrk_arena_push(arena, (da)->capacity * sizeof(*(da)->items)); \ break; \ } \ if ((expected_capacity) > (da)->capacity) { \ u64 old_cap = (da)->capacity; \ while ((expected_capacity) > (da)->capacity) { \ (da)->capacity *= 2; \ } \ u64 old_size = old_cap * sizeof(*(da)->items); \ u64 new_size = (da)->capacity * sizeof(*(da)->items); \ (da)->items = jrk_arena_resize((arena), (da)->items, old_size, new_size); \ } \ } while (0) // NOTE: caller should verify (da)->items is valid #define jrk_arena_da_append(arena, da, item) \ do { \ jrk_arena_da_reserve((arena), (da), (da)->count + 1); \ if ((da)->items) { \ (da)->items[(da)->count++] = (item); \ } \ } while (0) // NOTE: caller should verify (da)->items is valid #define jrk_arena_da_append_many(arena, da, new_items, new_items_count) \ do { \ jrk_arena_da_reserve((arena), (da), (da)->count + (new_items_count)); \ if ((da)->items) { \ memcpy((da)->items + (da)->count, (new_items), (new_items_count)*sizeof(*(da)->items)); \ (da)->count += (new_items_count); \ } \ } while (0) #define jrk_da_append(da, item) \ do { \ jrk_da_reserve((da), (da)->count + 1); \ (da)->items[(da)->count++] = (item); \ } while (0); #define jrk_da_append_many(da, new_items, new_items_count) \ do { \ jrk_da_reserve((da), (da)->count + (new_items_count)); \ memcpy((da)->items + (da)->count, (new_items), (new_items_count)*sizeof(*(da)->items)); \ (da)->count += (new_items_count); \ } while (0) #define jrk_da_create(name, type) \ struct name { \ type *items; \ u64 count; \ u64 capacity; \ }; \ typedef struct name name #define jrk_da_free(da) \ do { \ if ((da).items) \ free((da).items); \ } while (0) #define jrk_da_foreach(type,it,da) for (type *it = (da)->items; it < (da)->items + (da)->count; ++it) #define jrk_sb_append_null(sb) jrk_da_append((sb), '\0') #define jrk_sb_free(sb) jrk_da_free((sb)) #define jrk_arena_sb_append_null(arena, sb) jrk_arena_da_append((arena), (sb), '\0') #define jrk_arena_sb_append_cstr(arena, sb, cstr) \ do { \ const char *s = (cstr); \ i64 n = strlen((cstr)); \ jrk_arena_da_append_many((arena), (sb), s, n); \ } while (0) #define jrk_sb_append_cstr(sb, cstr) \ do { \ const char *s = (cstr); \ i64 n = strlen((cstr)); \ jrk_da_append_many((sb), s, n); \ } while (0) #define jrk_arena_sb_append_buf(arena, sb, buf, size) jrk_arena_da_append_many((arena), (sb), (buf), (size)) #define jrk_sb_append_buf(sb, buf, size) jrk_da_append_many((sb), (buf), (size)) #ifdef JRK_SHORTNAMES #define UNUSED JRK_UNUSED #define ARRLENGTH JRK_ARRLENGTH #define KILOBYTES JRK_KILOBYTES #define MEGABYTES JRK_MEGABYTES #define GIGABYTES JRK_GIGABYTES #define TERABYTES JRK_TERABYTES #define return_defer jrk_return_defer #define die jrk_die #define diev jrk_diev #define edie jrk_edie #define ediev jrk_ediev #define log jrk_log #define logv jrk_logv #define error jrk_error #define errorv jrk_errorv #define Arena jrk_Arena #define StringBuilder jrk_StringBuilder #define StringView jrk_StringView #define tmpstrings_push jrk_tmpstrings_push #define tmpstrings_pushf jrk_tmpstrings_pushf #define sb_appendf jrk_sb_appendf #define sb_append_null jrk_sb_append_null #define sb_append_buf jrk_sb_append_buf #define sb_append_buf_at jrk_sb_append_buf_at #define sb_append_cstr jrk_sb_append_cstr #define sb_free jrk_sb_free #define sb_fd_read_all jrk_sb_fd_read_all #define sb_fd_write_all jrk_sb_fd_write_all #define sb_to_sv jrk_sb_to_sv #define sb_write_file jrk_sb_write_file #define sb_read_entire_file jrk_sb_read_entire_file #define sv_trim_left jrk_sv_trim_left #define sv_trim_right jrk_sv_trim_right #define sv_trim jrk_sv_trim #define sv_from_cstr jrk_sv_from_cstr #define sv_from_parts jrk_sv_from_parts #define sv_chop_delim jrk_sv_chop_delim #define ecalloc jrk_ecalloc #define erealloc jrk_erealloc #define emalloc jrk_emalloc #define da_reserve jrk_da_reserve #define da_append jrk_da_append #define da_append_many jrk_da_append_many #define da_foreach jrk_da_foreach #define da_create jrk_da_create #define da_free jrk_da_free #define arena_create jrk_arena_create #define arena_push jrk_arena_push #define arena_push_strf jrk_arena_push_strf #define arena_resize jrk_arena_resize #define arena_reset jrk_arena_reset #define arena_da_reserve jrk_arena_da_reserve #define arena_da_append jrk_arena_da_append #define arena_da_append_many jrk_arena_da_append_many #define arena_sb_appendf jrk_arena_sb_appendf #define arena_sb_append_buf_at jrk_arena_sb_append_buf_at #define arena_sb_append_buf jrk_arena_sb_append_buf #define arena_sb_append_cstr jrk_arena_sb_append_cstr #define arena_sb_append_null jrk_arena_sb_append_null #define rand_num jrk_rand_num #define rand_num_range jrk_rand_num_range #define fd_close jrk_fd_close #define fd_open_read jrk_fd_open_read #define fd_open_write jrk_fd_open_write #define fd_open_write_append jrk_fd_open_write_append #define shift jrk_shift #define shift_loop jrk_shift_loop #endif // JRK_SHORTNAMES #ifdef JRK_IMPLEMENTATION #include #include #include #include #include #include #include #include #include void * jrk_ecalloc(u64 nmemb, u64 size) { void *p; if (!(p = calloc(nmemb, size))) jrk_ediev("jrk_ecalloc(%ld, %ld)", nmemb, size); return p; } void * jrk_erealloc(void *ptr, u64 size) { void *p; if (!(p = realloc(ptr, size))) jrk_ediev("jrk_erealloc(%p, %ld)", 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(char *str) { char *result; result = jrk_tmpstrings_pushf(str); return result; } char * jrk_tmpstrings_pushf(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) { if (arena->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, arena->offset + n, arena->capacity); return NULL; } void *result = &arena->data[arena->offset]; arena->prev_offset = arena->offset; arena->offset += n; memset(result, 0, n); return result; } char * jrk_arena_push_strf(jrk_Arena *arena, 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->offset = new_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_arena_sb_appendf(jrk_Arena *arena, jrk_StringBuilder *sb, const char *fmt, ...) { va_list args; va_start(args, fmt); i32 n = vsnprintf(NULL, 0, fmt, args); va_end(args); jrk_arena_da_reserve(arena, sb, sb->count + n + 1); if (!sb->items) return false; va_start(args, fmt); vsnprintf(sb->items + sb->count, n + 1, fmt, args); va_end(args); sb->count += n; return true; } i32 jrk_sb_appendf(jrk_StringBuilder *sb, const char *fmt, ...) { i32 result = -1; va_list args; va_start(args, fmt); result = vsnprintf(NULL, 0, fmt, args); va_end(args); jrk_da_reserve(sb, sb->count + result + 1); va_start(args, fmt); vsnprintf(sb->items + sb->count, result + 1, fmt, args); va_end(args); sb->count += result; return result; } bool jrk_arena_sb_append_buf_at(jrk_Arena *arena, jrk_StringBuilder *sb, char *buf, u64 size, u64 idx) { if (idx >= sb->count) { jrk_errorv("jrk_arena_sb_append_buf_at(%p, %p, %p, %ld, %ld): idx is greater than sb->count", (void *) arena, (void *) sb, buf, size, idx); return false; } u64 new_size = sb->count + size; char *old_items = sb->items; jrk_arena_da_reserve(arena, sb, new_size); if (!sb->items) { jrk_errorv("jrk_arena_sb_append_buf_at(%p, %p, %p, %ld, %ld): failed to reserve space for the jrk_StringBuilder", (void *) arena, (void *) sb, buf, size, idx); return false; } if (old_items == sb->items) { // didn't move jrk_StringBuilder temp_builder = {0}; temp_builder.items = jrk_emalloc(sb->capacity); temp_builder.count = sb->count; temp_builder.capacity = sb->capacity; memcpy(temp_builder.items, sb->items, sb->count); memcpy(sb->items, temp_builder.items, idx); memcpy(sb->items + idx, buf, size); memcpy(sb->items + idx + size, temp_builder.items + idx, temp_builder.count - idx); jrk_sb_free(temp_builder); } else { // did move memcpy(sb->items, old_items, idx); memcpy(sb->items + idx, buf, size); memcpy(sb->items + idx + size, old_items + idx, sb->count - idx); } sb->count = new_size; return true; } void jrk_sb_append_buf_at(jrk_StringBuilder *sb, char *buf, u64 size, u64 idx) { jrk_assertv(idx < sb->count, "jrk_sb_append_buf_at(%p, %p, %ld, %ld): idx is greater than sb->count (%ld > %ld)", (void *) sb, buf, size, idx, idx, sb->count); u64 new_size = sb->count + size; char *temp = jrk_emalloc(new_size); memcpy(temp, sb->items, idx); memcpy(temp + idx, buf, size); memcpy(temp + idx + size, sb->items + idx, sb->count - idx); free(sb->items); sb->items = temp; sb->count = new_size; // NOTE: maybe keep it a proper ^2? sb->capacity = new_size; } u64 jrk_fd_size(i32 fd) { struct stat statbuf = {0}; if (fstat(fd, &statbuf) < 0) jrk_ediev("jrk_fd_size(%d)", fd); return statbuf.st_size; } bool jrk_sb_fd_read_all(jrk_StringBuilder *sb, i32 fd) { u64 sz = jrk_fd_size(fd); jrk_da_reserve(sb, sz); if (read(fd, sb->items, sz) < 0) { jrk_ediev("jrk_sb_fd_read_all(%p, %d)", (void *)sb, fd); return false; } sb->count = sz; return true; } i64 jrk_sb_fd_write_all(jrk_StringBuilder *sb, i32 fd) { i64 result; result = write(fd, (void *) sb->items, sb->count); if (result < 0) { jrk_ediev("jrk_sb_fd_write_all(%p, %d)", (void *)sb, fd); return -1; } return result; } void jrk_fd_close(i32 fd) { close(fd); } bool jrk_sb_write_file(jrk_StringBuilder *sb, char *path) { i32 fd = jrk_fd_open_write(path); if (fd < 0) return false; if (jrk_sb_fd_write_all(sb, fd) < 0) return false; jrk_fd_close(fd); return true; } bool jrk_sb_read_entire_file(jrk_StringBuilder *sb, char *path) { i32 fd = jrk_fd_open_read(path); if (fd < 0) return false; if (!jrk_sb_fd_read_all(sb, fd)) return false; jrk_fd_close(fd); return true; } i32 jrk_fd_open_read(char *path) { i32 result = open(path, O_RDONLY); if (result < 0) { jrk_ediev("jrk_fd_open_read(%s)", path); return -1; } return result; } i32 jrk_fd_open_write(char *path) { i32 result = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (result < 0) { jrk_ediev("jrk_fd_open_write(%s)", path); return -1; } return result; } i32 jrk_fd_open_write_append(char *path) { i32 result = open(path, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (result < 0) { jrk_ediev("jrk_fd_open_write_append(%s)", path); return -1; } return result; } jrk_StringView jrk_sv_from_parts(char *data, u64 count) { jrk_StringView result = {0}; result.data = data; result.count = count; return result; } jrk_StringView jrk_sv_from_cstr(char *data) { jrk_StringView result = {0}; result.data = data; result.count = strlen(data); return result; } jrk_StringView jrk_sv_trim_right(jrk_StringView sv) { u64 i = 0; for (; i < sv.count && isspace(sv.data[sv.count - 1 - i]); ++i); return jrk_sv_from_parts(sv.data, sv.count - i); } jrk_StringView jrk_sv_trim_left(jrk_StringView sv) { u64 i = 0; for (; i < sv.count && isspace(sv.data[i]); ++i); return jrk_sv_from_parts(sv.data + i, sv.count - i); } jrk_StringView jrk_sv_trim(jrk_StringView sv) { return jrk_sv_trim_right(jrk_sv_trim_left(sv)); } jrk_StringView jrk_sv_chop_delim(jrk_StringView *sv, char delim) { u64 i = 0; while (i < sv->count && sv->data[i] != delim) ++i; jrk_StringView result = jrk_sv_from_parts(sv->data, i); if (i < sv->count) { sv->data += i + 1; sv->count -= i + 1; } else { sv->data += i; sv->count -= i; } 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