diff options
| author | iamcheeseman <[email protected]> | 2026-04-06 17:04:05 -0400 |
|---|---|---|
| committer | iamcheeseman <[email protected]> | 2026-04-06 17:06:53 -0400 |
| commit | 957c64c7b8b5e98d8a03dd84c7e27e7991fb9dbc (patch) | |
| tree | f5fc230703791cee8d8e7851fb87eaef07ae63a2 /common | |
Initial commit
Diffstat (limited to 'common')
| -rw-r--r-- | common/common.c | 30 | ||||
| -rw-r--r-- | common/common.h | 25 | ||||
| -rw-r--r-- | common/dyn_arr.c | 44 | ||||
| -rw-r--r-- | common/dyn_arr.h | 33 | ||||
| -rw-r--r-- | common/log.c | 34 | ||||
| -rw-r--r-- | common/log.h | 44 | ||||
| -rw-r--r-- | common/mem.c | 200 | ||||
| -rw-r--r-- | common/mem.h | 24 |
8 files changed, 434 insertions, 0 deletions
diff --git a/common/common.c b/common/common.c new file mode 100644 index 0000000..d38f7ac --- /dev/null +++ b/common/common.c @@ -0,0 +1,30 @@ +#include "common.h" + +#include <stdarg.h> +#include <stdio.h> + +char *read_file(const char *path, size_t *size_out) +{ + FILE *file = fopen(path, "r"); + if (!file) + log_fatal(ERR_IO, "could not open file '%s'", path); + + fseek(file, 0L, SEEK_END); + size_t size = ftell(file); + rewind(file); + + char *dat = mem_alloc(sizeof(char) * (size + 1)); + + size_t bytes_read = fread(dat, sizeof(char), size, file); + + if (bytes_read < size) { + log_fatal(ERR_IO, "could not read file '%s'", path); + } + + fclose(file); + + if (size_out) + *size_out = size; + + return dat; +} diff --git a/common/common.h b/common/common.h new file mode 100644 index 0000000..7396f98 --- /dev/null +++ b/common/common.h @@ -0,0 +1,25 @@ +#ifndef __MICRO_COMMON_H__ +#define __MICRO_COMMON_H__ + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#include "log.h" +#include "mem.h" + +#define PI 3.14 +#define DEG2RAD (180 / PI) + +typedef int8_t i8; +typedef uint8_t u8; +typedef int16_t i16; +typedef uint16_t u16; +typedef int32_t i32; +typedef uint32_t u32; +typedef int64_t i64; +typedef uint64_t u64; + +char *read_file(const char *path, size_t *size_out); + +#endif // __MICRO_COMMON_H__ diff --git a/common/dyn_arr.c b/common/dyn_arr.c new file mode 100644 index 0000000..f378702 --- /dev/null +++ b/common/dyn_arr.c @@ -0,0 +1,44 @@ +#include "dyn_arr.h" + +#include <stdio.h> +#include <assert.h> + +void *_da_create(size_t type_size, int init_cap) +{ + init_cap = init_cap < DA_MIN_CAP + ? DA_MIN_CAP + : init_cap; + + int *arr = (int*)mem_alloc(type_size * init_cap + sizeof(int) * 2); + + arr[0] = 0; + arr[1] = init_cap; + return (void*)(arr + 2); +} + +void *_da_append_slot(size_t type_size, void **arr) +{ + int *cap = da_cap_ptr(*arr); + int *len = da_len_ptr(*arr); + + (*len)++; + + if (*len > *cap) { + *cap *= DA_GROW_RATE; + + void* base = da_base(*arr); + base = mem_realloc(base, (type_size * *cap) + sizeof(int) * 2); + assert(base != NULL); // Just to handle the case + *arr = (void*)((int*)base + 2); + + len = da_len_ptr(*arr); + } + + return (char*)*arr + ((*len - 1) * type_size); +} + +void da_free(void *arr) +{ + void *base = da_base(arr); + mem_free(base); +} diff --git a/common/dyn_arr.h b/common/dyn_arr.h new file mode 100644 index 0000000..85e6c15 --- /dev/null +++ b/common/dyn_arr.h @@ -0,0 +1,33 @@ +#ifndef __MICRO_VEC_H__ +#define __MICRO_VEC_H__ + +#include "common.h" + +#define DA_MIN_CAP 8 +#define DA_GROW_RATE 2 + +#define da_create(T, init_capacity) \ + ((T*)_da_create(sizeof(T), init_capacity)) + +#define da_len_ptr(arr) ((int*)arr - 2) +#define da_len(arr) (*da_len_ptr(arr)) + +#define da_cap_ptr(arr) ((int*)arr - 1) +#define da_cap(arr) (*da_cap_ptr(arr)) + +#define da_base(arr) ((void*)da_len_ptr(arr)) + +#define da_append_slot(T, arr) \ + ((T*)_da_append_slot(sizeof(T), (void**)arr)) + +#define da_append(T, arr, elem) \ + (*da_append_slot(T, arr) = elem) + +#define da_clear(arr) \ + (*da_len_ptr(arr) = 0) + +void *_da_create(size_t type_size, int init_cap); +void *_da_append_slot(size_t type_size, void **arr); +void da_free(void *arr); + +#endif // __MICRO_VEC_H__ diff --git a/common/log.c b/common/log.c new file mode 100644 index 0000000..fb82de1 --- /dev/null +++ b/common/log.c @@ -0,0 +1,34 @@ +#include "log.h" + +#include <stdarg.h> + +#include "common.h" + +void log_msg( + FILE *file, + const char *src_name, + int src_line, + const char *type, + const char *fmt, + ... +) +{ + fprintf(file, "[%s] (%s:%d) ", type, src_name, src_line); + + va_list args; + va_start(args, fmt); + vfprintf(file, fmt, args); + va_end(args); + + putc('\n', file); +} + +void _log_plain(FILE *file, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(file, fmt, args); + va_end(args); + + putc('\n', file); +} diff --git a/common/log.h b/common/log.h new file mode 100644 index 0000000..e04b71f --- /dev/null +++ b/common/log.h @@ -0,0 +1,44 @@ +#ifndef __MICRO_LOG_H__ +#define __MICRO_LOG_H__ + +#include <stdio.h> +#include <stdlib.h> + +// Don't include common.h, because this should be included there + +#define log_info(fmt, ...) \ + log_msg(stdout, __FILE__, __LINE__, "info", fmt, ## __VA_ARGS__) +#define log_warn(fmt, ...) \ + log_msg(stderr, __FILE__, __LINE__, "warn", fmt, ## __VA_ARGS__) +#define log_err(fmt, ...) \ + log_msg(stderr, __FILE__, __LINE__, "error", fmt, ## __VA_ARGS__) +#define log_fatal(ec, fmt, ...) \ + (log_msg(stderr, __FILE__, __LINE__, "fatal", fmt, ## __VA_ARGS__), \ + exit(ec)) +#define olog(fmt, ...) \ + _log_plain(stdout, fmt, ## __VA_ARGS__) +#define elog(fmt, ...) \ + _log_plain(stderr, fmt, ## __VA_ARGS__) + +// To be passed as the exit code to log_fatal() +#define ERR_MEM 0x01 +#define ERR_IO 0x02 +#define ERR_MEM_LEAK 0x03 + +#define ERR_GLFW_INIT 0x21 + +#define ERR_OPENGL_INIT 0x31 +#define ERR_OPENGL_SHADER 0x32 + +void log_msg( + FILE *file, + const char *src_name, + int src_line, + const char *type, + const char *fmt, + ... +); + +void _log_plain(FILE *file, const char *fmt, ...); + +#endif // __MICRO_LOG_H__ diff --git a/common/mem.c b/common/mem.c new file mode 100644 index 0000000..4196812 --- /dev/null +++ b/common/mem.c @@ -0,0 +1,200 @@ +#include "mem.h" + +#include <GLFW/glfw3.h> + +#include "common.h" +#include "dyn_arr.h" + +// Array to track temp allocations. +void **temp_allocations; + +#ifdef UE_DEBUG + +#include <execinfo.h> + +#define BACKTRACE_SIZE 16 + +struct alloc { + void *ptr; + size_t size; + int backtrace_len; + char **backtrace; +}; + +int allocations_cap; +int allocations_len; +struct alloc *allocations; + +#endif // UE_DEBUG + +static +void *glfw_alloc(size_t size, void *user) +{ + (void)user; + return mem_alloc(size); +} + +static +void *glfw_realloc(void *block, size_t size, void *user) +{ + (void)user; + return mem_realloc(block, size); +} + +static +void glfw_dealloc(void *block, void *user) +{ + (void)user; + mem_free(block); +} + +void init_mem(void) +{ +#ifdef UE_DEBUG + allocations_len = 0; + allocations_cap = 8; + allocations = malloc(sizeof(struct alloc) * 8); + if (!allocations) + log_fatal(ERR_MEM, "(%s) (track) ran out of memory", __func__); +#endif // UE_DEBUG + + GLFWallocator glfw_allocator; + glfw_allocator.allocate = glfw_alloc; + glfw_allocator.reallocate = glfw_realloc; + glfw_allocator.deallocate = glfw_dealloc; + glfwInitAllocator(&glfw_allocator); + + temp_allocations = da_create(void*, 0); +} + +void deinit_mem(void) +{ + da_free(temp_allocations); +#ifdef UE_DEBUG + fprintf(stderr, "%d allocations leaked\n", allocations_len); + for (int i = 0; i < allocations_len; i++) { + struct alloc *alloc = &allocations[i]; + fprintf( + stderr, + "leaked %zu bytes at %p\n", + alloc->size, + alloc->ptr + ); + for (int j = 0; j < alloc->backtrace_len; j++) { + fprintf(stderr, "\t%s\n", alloc->backtrace[j]); + } + putc('\n', stderr); + + free(alloc->backtrace); + } + free(allocations); + + if (allocations_len > 0) + exit(ERR_MEM_LEAK); +#endif // UE_DEBUG +} + +void free_temp_allocs(void) +{ + for (int i = 0; i < da_len(temp_allocations); i++) { + void *temp = temp_allocations[i]; + mem_free(temp); + } + da_clear(temp_allocations); +} + +void *mem_alloc(size_t size) +{ + void *ptr = malloc(size); + if (!ptr) + log_fatal(ERR_MEM, "(%s) ran out of memory", __func__); + +#ifdef UE_DEBUG + // Log allocation + struct alloc alloc; + void *bt[BACKTRACE_SIZE]; + int bt_len = backtrace(bt, BACKTRACE_SIZE); + char **symbols = backtrace_symbols(bt, bt_len); + + alloc.ptr = ptr; + alloc.size = size; + alloc.backtrace_len = bt_len; + alloc.backtrace = symbols; + + if (allocations_len + 1 > allocations_cap) { + allocations_cap *= 2; + allocations = realloc( + allocations, + sizeof(struct alloc) * allocations_cap + ); + if (!allocations) + log_fatal( + ERR_MEM, + "(%s) (track) ran out of memory", + __func__ + ); + } + + allocations[allocations_len++] = alloc; +#endif // UE_DEBUG + + return ptr; +} + +void *mem_realloc(void *ptr, size_t new_size) +{ + if (new_size == 0) { + mem_free(ptr); + return NULL; + } + + void *new_ptr = realloc(ptr, new_size); + if (!new_ptr) + log_fatal(ERR_MEM, "(%s) ran out of memory", __func__); + +#ifdef UE_DEBUG + for (int i = 0; i < allocations_len; i++) { + struct alloc *alloc = &allocations[i]; + if (alloc->ptr == ptr) { + alloc->ptr = new_ptr; + alloc->size = new_size; + break; + } + } +#endif // UE_DEBUG + + return new_ptr; +} + +void *mem_talloc(size_t size) +{ + void *ptr = mem_alloc(size); + da_append(void*, &temp_allocations, ptr); + return ptr; +} + +void mem_free(void *ptr) +{ + free(ptr); + +#ifdef UE_DEBUG + for (int i = 0; i < allocations_len; i++) { + struct alloc *alloc = &allocations[i]; + if (alloc->ptr == ptr) { + free(alloc->backtrace); + *alloc = allocations[allocations_len - 1]; + allocations_len--; + break; + } + } +#endif // UE_DEBUG +} + +int alloc_count(void) +{ +#ifdef UE_DEBUG + return allocations_len; +#else + return -1; +#endif +} diff --git a/common/mem.h b/common/mem.h new file mode 100644 index 0000000..101f328 --- /dev/null +++ b/common/mem.h @@ -0,0 +1,24 @@ +#ifndef __MICRO_MEM_H__ +#define __MICRO_MEM_H__ + +#include <stdlib.h> + +// NOTE: Overrides GLFW allocator +void init_mem(void); +void deinit_mem(void); +void free_temp_allocs(void); +// Temp allocation. Freed at the end of every frame. Do NOT realloc. +void *mem_talloc(size_t size); + +// These mem_* functions handle the case of a bad allocation. No need to check +// for NULL after allocating with these. In debug builds, they will also track +// allocations and report any memory still in use when the program exits. + +void *mem_alloc(size_t size); +void *mem_realloc(void *ptr, size_t new_size); +void mem_free(void *ptr); + +// Returns -1 in release builds. +int alloc_count(void); + +#endif // __MICRO_MEM_H__ |
