summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/common.c30
-rw-r--r--common/common.h25
-rw-r--r--common/dyn_arr.c44
-rw-r--r--common/dyn_arr.h33
-rw-r--r--common/log.c34
-rw-r--r--common/log.h44
-rw-r--r--common/mem.c200
-rw-r--r--common/mem.h24
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__