mirror of https://github.com/bjornbytes/lovr.git
Add defer system; Rework exceptions; util cleanup;
Defer can be used to clean up resources when an error occurs.
This commit is contained in:
parent
67e228125b
commit
cdecc1bae7
|
@ -1059,7 +1059,7 @@ static int l_lovrGraphicsNewShader(lua_State* L) {
|
|||
lovrGraphicsCompileShader(source, compiled, info.stageCount, luax_readfile);
|
||||
|
||||
arr_t(ShaderFlag) flags;
|
||||
arr_init(&flags, realloc);
|
||||
arr_init(&flags);
|
||||
|
||||
if (lua_istable(L, index)) {
|
||||
lua_getfield(L, index, "flags");
|
||||
|
|
|
@ -9,7 +9,7 @@ static int l_lovrShaderClone(lua_State* L) {
|
|||
lua_pushnil(L);
|
||||
|
||||
arr_t(ShaderFlag) flags;
|
||||
arr_init(&flags, realloc);
|
||||
arr_init(&flags);
|
||||
|
||||
while (lua_next(L, 2) != 0) {
|
||||
ShaderFlag flag = { 0 };
|
||||
|
|
|
@ -8,26 +8,34 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void threadRun(void* L) {
|
||||
int top = lua_gettop(L);
|
||||
int status = lua_pcall(L, top - 2, 0, 1);
|
||||
lua_pushinteger(L, status);
|
||||
}
|
||||
|
||||
static char* threadRunner(Thread* thread, Blob* body, Variant* arguments, uint32_t argumentCount) {
|
||||
lua_State* L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
luax_preload(L);
|
||||
lovrSetErrorCallback((errorFn*) luax_vthrow, L);
|
||||
|
||||
lua_pushcfunction(L, luax_getstack);
|
||||
int errhandler = lua_gettop(L);
|
||||
|
||||
if (!luaL_loadbuffer(L, body->data, body->size, "thread")) {
|
||||
for (uint32_t i = 0; i < argumentCount; i++) {
|
||||
luax_pushvariant(L, &arguments[i]);
|
||||
}
|
||||
|
||||
if (!lua_pcall(L, argumentCount, 0, errhandler)) {
|
||||
lovrTry(threadRun, L, luax_vthrow, L);
|
||||
|
||||
if (lua_tointeger(L, -1) == 0) {
|
||||
lua_close(L);
|
||||
return NULL;
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Error handling
|
||||
size_t length;
|
||||
const char* message = lua_tolstring(L, -1, &length);
|
||||
|
||||
|
@ -92,15 +100,18 @@ int luaopen_lovr_thread(lua_State* L) {
|
|||
int32_t workers = -1;
|
||||
|
||||
luax_pushconf(L);
|
||||
lua_getfield(L, -1, "thread");
|
||||
if (lua_istable(L, -1)) {
|
||||
lua_getfield(L, -1, "workers");
|
||||
if (lua_type(L, -1) == LUA_TNUMBER) {
|
||||
workers = lua_tointeger(L, -1);
|
||||
lua_getfield(L, -1, "thread");
|
||||
if (lua_istable(L, -1)) {
|
||||
lua_getfield(L, -1, "workers");
|
||||
if (lua_type(L, -1) == LUA_TNUMBER) {
|
||||
workers = lua_tointeger(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 2);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lovrThreadModuleInit(workers);
|
||||
luax_atexit(L, lovrThreadModuleDestroy);
|
||||
|
|
12
src/main.c
12
src/main.c
|
@ -10,6 +10,12 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void run(void* T) {
|
||||
while (luax_resume(T, 0) == LUA_YIELD) {
|
||||
os_sleep(0.);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
os_init();
|
||||
|
||||
|
@ -37,12 +43,8 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
lua_State* T = lua_tothread(L, -1);
|
||||
lovrSetErrorCallback(luax_vthrow, T);
|
||||
lovrSetLogCallback(luax_vlog, T);
|
||||
|
||||
while (luax_resume(T, 0) == LUA_YIELD) {
|
||||
os_sleep(0.);
|
||||
}
|
||||
lovrTry(run, T, luax_vthrow, T);
|
||||
|
||||
if (lua_type(T, 1) == LUA_TSTRING && !strcmp(lua_tostring(T, 1), "restart")) {
|
||||
luax_checkvariant(T, 2, &cookie);
|
||||
|
|
|
@ -123,16 +123,16 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source, ModelDataIO* io)
|
|||
arr_t(float) normals;
|
||||
arr_t(float) uvs;
|
||||
|
||||
arr_init(&groups, arr_alloc);
|
||||
arr_init(&images, arr_alloc);
|
||||
arr_init(&materials, arr_alloc);
|
||||
arr_init(&groups);
|
||||
arr_init(&images);
|
||||
arr_init(&materials);
|
||||
map_init(&materialMap, 0);
|
||||
arr_init(&vertexBlob, arr_alloc);
|
||||
arr_init(&indexBlob, arr_alloc);
|
||||
arr_init(&vertexBlob);
|
||||
arr_init(&indexBlob);
|
||||
map_init(&vertexMap, 0);
|
||||
arr_init(&positions, arr_alloc);
|
||||
arr_init(&normals, arr_alloc);
|
||||
arr_init(&uvs, arr_alloc);
|
||||
arr_init(&positions);
|
||||
arr_init(&normals);
|
||||
arr_init(&uvs);
|
||||
|
||||
arr_push(&groups, ((objGroup) { .material = -1 }));
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ void lovrVariantDestroy(Variant* variant) {
|
|||
|
||||
bool lovrEventInit(void) {
|
||||
if (atomic_fetch_add(&state.ref, 1)) return false;
|
||||
arr_init(&state.events, arr_alloc);
|
||||
arr_init(&state.events);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -568,7 +568,7 @@ static bool zip_init(Archive* archive, const char* filename, const char* root) {
|
|||
|
||||
// Parse the number of file entries and reserve memory
|
||||
uint16_t nodeCount = readu16(p + 10);
|
||||
arr_init(&archive->nodes, realloc);
|
||||
arr_init(&archive->nodes);
|
||||
arr_reserve(&archive->nodes, nodeCount);
|
||||
map_init(&archive->lookup, nodeCount);
|
||||
|
||||
|
|
|
@ -689,9 +689,9 @@ bool lovrGraphicsInit(GraphicsConfig* config) {
|
|||
|
||||
map_init(&state.passLookup, 4);
|
||||
map_init(&state.pipelineLookup, 64);
|
||||
arr_init(&state.layouts, realloc);
|
||||
arr_init(&state.materialBlocks, realloc);
|
||||
arr_init(&state.scratchTextures, realloc);
|
||||
arr_init(&state.layouts);
|
||||
arr_init(&state.materialBlocks);
|
||||
arr_init(&state.scratchTextures);
|
||||
|
||||
gpu_slot builtinSlots[] = {
|
||||
{ 0, GPU_SLOT_UNIFORM_BUFFER, GPU_STAGE_GRAPHICS }, // Globals
|
||||
|
@ -3467,7 +3467,7 @@ Font* lovrFontCreate(const FontInfo* info) {
|
|||
font->ref = 1;
|
||||
font->info = *info;
|
||||
lovrRetain(info->rasterizer);
|
||||
arr_init(&font->glyphs, realloc);
|
||||
arr_init(&font->glyphs);
|
||||
map_init(&font->glyphLookup, 36);
|
||||
map_init(&font->kerning, 36);
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ static void evaluate(float* restrict P, size_t n, float t, vec4 p) {
|
|||
Curve* lovrCurveCreate(void) {
|
||||
Curve* curve = lovrCalloc(sizeof(Curve));
|
||||
curve->ref = 1;
|
||||
arr_init(&curve->points, arr_alloc);
|
||||
arr_init(&curve->points);
|
||||
arr_reserve(&curve->points, 16);
|
||||
return curve;
|
||||
}
|
||||
|
|
|
@ -398,8 +398,8 @@ Collider* lovrColliderCreate(World* world, float x, float y, float z) {
|
|||
lovrColliderSetAngularDamping(collider, world->defaultAngularDamping, 0.f);
|
||||
lovrColliderSetSleepingAllowed(collider, world->defaultIsSleepingAllowed);
|
||||
|
||||
arr_init(&collider->shapes, arr_alloc);
|
||||
arr_init(&collider->joints, arr_alloc);
|
||||
arr_init(&collider->shapes);
|
||||
arr_init(&collider->joints);
|
||||
|
||||
// Adjust the world's collider list
|
||||
if (!collider->world->head) {
|
||||
|
|
|
@ -164,7 +164,7 @@ World* lovrWorldCreate(float xg, float yg, float zg, bool allowSleep, const char
|
|||
world->space = dHashSpaceCreate(0);
|
||||
dHashSpaceSetLevels(world->space, -4, 8);
|
||||
world->contactGroup = dJointGroupCreate(0);
|
||||
arr_init(&world->overlaps, arr_alloc);
|
||||
arr_init(&world->overlaps);
|
||||
lovrWorldSetGravity(world, xg, yg, zg);
|
||||
lovrWorldSetSleepingAllowed(world, allowSleep);
|
||||
for (uint32_t i = 0; i < tagCount; i++) {
|
||||
|
@ -448,8 +448,8 @@ Collider* lovrColliderCreate(World* world, float x, float y, float z) {
|
|||
collider->restitution = 0;
|
||||
collider->tag = NO_TAG;
|
||||
dBodySetData(collider->body, collider);
|
||||
arr_init(&collider->shapes, arr_alloc);
|
||||
arr_init(&collider->joints, arr_alloc);
|
||||
arr_init(&collider->shapes);
|
||||
arr_init(&collider->joints);
|
||||
|
||||
lovrColliderSetPosition(collider, x, y, z);
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ const char* lovrThreadGetError(Thread* thread) {
|
|||
Channel* lovrChannelCreate(uint64_t hash) {
|
||||
Channel* channel = lovrCalloc(sizeof(Channel));
|
||||
channel->ref = 1;
|
||||
arr_init(&channel->messages, arr_alloc);
|
||||
arr_init(&channel->messages);
|
||||
mtx_init(&channel->lock, mtx_plain);
|
||||
cnd_init(&channel->cond);
|
||||
channel->hash = hash;
|
||||
|
|
160
src/util.c
160
src/util.c
|
@ -1,25 +1,28 @@
|
|||
#include "util.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdatomic.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Allocation
|
||||
|
||||
void* lovrMalloc(size_t size) {
|
||||
void* data = malloc(size);
|
||||
if (!data) fprintf(stderr, "Out of memory"), abort();
|
||||
lovrAssert(data, "Out of memory");
|
||||
return data;
|
||||
}
|
||||
|
||||
void* lovrCalloc(size_t size) {
|
||||
void* data = calloc(1, size);
|
||||
if (!data) fprintf(stderr, "Out of memory"), abort();
|
||||
lovrAssert(data, "Out of memory");
|
||||
return data;
|
||||
}
|
||||
|
||||
void* lovrRealloc(void* old, size_t size) {
|
||||
void* data = realloc(old, size);
|
||||
if (!data) fprintf(stderr, "Out of memory"), abort();
|
||||
lovrAssert(data, "Out of memory");
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -27,40 +30,8 @@ void lovrFree(void* data) {
|
|||
free(data);
|
||||
}
|
||||
|
||||
// Error handling
|
||||
static LOVR_THREAD_LOCAL errorFn* lovrErrorCallback;
|
||||
static LOVR_THREAD_LOCAL void* lovrErrorUserdata;
|
||||
// Refcounting
|
||||
|
||||
void lovrSetErrorCallback(errorFn* callback, void* userdata) {
|
||||
lovrErrorCallback = callback;
|
||||
lovrErrorUserdata = userdata;
|
||||
}
|
||||
|
||||
void lovrThrow(const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
lovrErrorCallback(lovrErrorUserdata, format, args);
|
||||
va_end(args);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Logging
|
||||
logFn* lovrLogCallback;
|
||||
void* lovrLogUserdata;
|
||||
|
||||
void lovrSetLogCallback(logFn* callback, void* userdata) {
|
||||
lovrLogCallback = callback;
|
||||
lovrLogUserdata = userdata;
|
||||
}
|
||||
|
||||
void lovrLog(int level, const char* tag, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
lovrLogCallback(lovrLogUserdata, level, tag, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Refcounting; to be reference-counted the object must have uint ref field as struct's first member
|
||||
#if ATOMIC_INT_LOCK_FREE != 2
|
||||
#error "Lock-free integer atomics are not supported on this platform, but are required for refcounting"
|
||||
#endif
|
||||
|
@ -77,18 +48,118 @@ void lovrRelease(void* object, void (*destructor)(void*)) {
|
|||
}
|
||||
}
|
||||
|
||||
// Dynamic Array
|
||||
// Default malloc-based allocator for arr_t (like realloc except well-defined when size is 0)
|
||||
void* arr_alloc(void* data, size_t size) {
|
||||
if (size > 0) {
|
||||
return realloc(data, size);
|
||||
} else {
|
||||
free(data);
|
||||
return NULL;
|
||||
// Defer
|
||||
|
||||
typedef struct {
|
||||
void (*fn)(void*);
|
||||
void* arg;
|
||||
} Closure;
|
||||
|
||||
static LOVR_THREAD_LOCAL struct {
|
||||
Closure stack[16];
|
||||
uint16_t releaseMask;
|
||||
uint16_t errMask;
|
||||
uint32_t top;
|
||||
} defer;
|
||||
|
||||
uint32_t lovrDeferPush(void) {
|
||||
return defer.top;
|
||||
}
|
||||
|
||||
static void deferPop(uint32_t base, bool err) {
|
||||
while (defer.top > base) {
|
||||
uint32_t index = --defer.top;
|
||||
Closure c = defer.stack[index];
|
||||
if (err || (defer.errMask & (1u << index)) == 0) {
|
||||
if (defer.releaseMask & (1u << index)) {
|
||||
lovrRelease(c.arg, c.fn);
|
||||
} else {
|
||||
c.fn(c.arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lovrDeferPop(uint32_t base) {
|
||||
deferPop(base, false);
|
||||
}
|
||||
|
||||
void lovrDefer(void (*fn)(void*), void* arg) {
|
||||
lovrAssert(defer.top < COUNTOF(defer.stack), "Defer stack overflow!");
|
||||
defer.releaseMask &= ~(1u << defer.top);
|
||||
defer.errMask &= ~(1u << defer.top);
|
||||
defer.stack[defer.top++] = (Closure) { fn, arg };
|
||||
}
|
||||
|
||||
void lovrErrDefer(void (*fn)(void*), void* arg) {
|
||||
lovrAssert(defer.top < COUNTOF(defer.stack), "Defer stack overflow!");
|
||||
defer.releaseMask &= ~(1u << defer.top);
|
||||
defer.errMask |= (1u << defer.top);
|
||||
defer.stack[defer.top++] = (Closure) { fn, arg };
|
||||
}
|
||||
|
||||
void lovrDeferRelease(void* object, void (*destructor)(void*)) {
|
||||
lovrAssert(defer.top < COUNTOF(defer.stack), "Defer stack overflow!");
|
||||
defer.releaseMask |= (1u << defer.top);
|
||||
defer.errMask &= ~(1u << defer.top);
|
||||
defer.stack[defer.top++] = (Closure) { destructor, object };
|
||||
}
|
||||
|
||||
// Exceptions
|
||||
|
||||
typedef struct Handler {
|
||||
struct Handler* prev;
|
||||
uint32_t baseDefer;
|
||||
void (*catch)(void* arg, const char* format, va_list args);
|
||||
void* arg;
|
||||
jmp_buf env;
|
||||
} Handler;
|
||||
|
||||
static LOVR_THREAD_LOCAL Handler* lovrHandler;
|
||||
|
||||
void lovrTry(void (*fn)(void*), void* arg, void(*catch)(void*, const char*, va_list), void* catchArg) {
|
||||
lovrHandler = &(Handler) {
|
||||
.prev = lovrHandler,
|
||||
.baseDefer = defer.top,
|
||||
.catch = catch,
|
||||
.arg = arg
|
||||
};
|
||||
|
||||
if (setjmp(lovrHandler->env) == 0) {
|
||||
fn(arg);
|
||||
}
|
||||
|
||||
lovrHandler = lovrHandler->prev;
|
||||
}
|
||||
|
||||
void lovrThrow(const char* format, ...) {
|
||||
deferPop(lovrHandler->baseDefer, true);
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
lovrHandler->catch(lovrHandler->arg, format, args);
|
||||
va_end(args);
|
||||
longjmp(lovrHandler->env, 1);
|
||||
}
|
||||
|
||||
// Logging
|
||||
|
||||
static fn_log* lovrLogCallback;
|
||||
static void* lovrLogUserdata;
|
||||
|
||||
void lovrSetLogCallback(fn_log* callback, void* userdata) {
|
||||
lovrLogCallback = callback;
|
||||
lovrLogUserdata = userdata;
|
||||
}
|
||||
|
||||
void lovrLog(int level, const char* tag, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
lovrLogCallback(lovrLogUserdata, level, tag, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Hashmap
|
||||
|
||||
static void map_rehash(map_t* map) {
|
||||
map_t old = *map;
|
||||
map->size <<= 1;
|
||||
|
@ -155,6 +226,7 @@ void map_set(map_t* map, uint64_t hash, uint64_t value) {
|
|||
|
||||
// UTF-8
|
||||
// https://github.com/starwing/luautf8
|
||||
|
||||
size_t utf8_decode(const char *s, const char *e, unsigned *pch) {
|
||||
unsigned ch;
|
||||
|
||||
|
|
67
src/util.h
67
src/util.h
|
@ -39,13 +39,22 @@ void* lovrCalloc(size_t size);
|
|||
void* lovrRealloc(void* data, size_t size);
|
||||
void lovrFree(void* data);
|
||||
|
||||
// Error handling
|
||||
typedef void errorFn(void*, const char*, va_list);
|
||||
void lovrSetErrorCallback(errorFn* callback, void* userdata);
|
||||
// Refcounting (to be refcounted, a struct must have a uint32_t refcount as its first field)
|
||||
void lovrRetain(void* ref);
|
||||
void lovrRelease(void* ref, void (*destructor)(void*));
|
||||
|
||||
// Defer
|
||||
uint32_t lovrDeferPush(void);
|
||||
void lovrDeferPop(uint32_t base);
|
||||
void lovrDefer(void (*fn)(void*), void* arg);
|
||||
void lovrErrDefer(void (*fn)(void*), void* arg);
|
||||
void lovrDeferRelease(void* ref, void (*destructor)(void*));
|
||||
|
||||
// Exceptions
|
||||
void lovrTry(void (*fn)(void*), void* arg, void (*catch)(void*, const char*, va_list), void* catchArg);
|
||||
LOVR_NORETURN void lovrThrow(const char* format, ...);
|
||||
#define lovrAssert(c, ...) do { if (!(c)) { lovrThrow(__VA_ARGS__); } } while(0)
|
||||
#define lovrUnreachable() lovrThrow("Unreachable")
|
||||
|
||||
#ifdef LOVR_UNCHECKED
|
||||
#define lovrCheck(c, ...) ((void) 0)
|
||||
#else
|
||||
|
@ -53,11 +62,30 @@ LOVR_NORETURN void lovrThrow(const char* format, ...);
|
|||
#endif
|
||||
|
||||
// Logging
|
||||
typedef void logFn(void*, int, const char*, const char*, va_list);
|
||||
typedef void fn_log(void*, int, const char*, const char*, va_list);
|
||||
enum { LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR };
|
||||
void lovrSetLogCallback(logFn* callback, void* userdata);
|
||||
void lovrSetLogCallback(fn_log* callback, void* userdata);
|
||||
void lovrLog(int level, const char* tag, const char* format, ...);
|
||||
|
||||
// Dynamic Array
|
||||
#define arr_t(T) struct { T* data; size_t length, capacity; }
|
||||
#define arr_init(a) (a)->data = NULL, (a)->length = 0, (a)->capacity = 0
|
||||
#define arr_free(a) if ((a)->data) lovrFree((a)->data)
|
||||
#define arr_reserve(a, n) _arr_reserve((void**) &((a)->data), n, &(a)->capacity, sizeof(*(a)->data))
|
||||
#define arr_expand(a, n) arr_reserve(a, (a)->length + n)
|
||||
#define arr_push(a, x) arr_reserve(a, (a)->length + 1), (a)->data[(a)->length] = x, (a)->length++
|
||||
#define arr_pop(a) (a)->data[--(a)->length]
|
||||
#define arr_append(a, p, n) arr_reserve(a, (a)->length + n), memcpy((a)->data + (a)->length, p, n * sizeof(*(p))), (a)->length += n
|
||||
#define arr_splice(a, i, n) memmove((a)->data + (i), (a)->data + ((i) + n), ((a)->length - (i) - (n)) * sizeof(*(a)->data)), (a)->length -= n
|
||||
#define arr_clear(a) (a)->length = 0
|
||||
|
||||
static inline void _arr_reserve(void** data, size_t n, size_t* capacity, size_t stride) {
|
||||
if (*capacity >= n) return;
|
||||
if (*capacity == 0) *capacity = 1;
|
||||
while (*capacity < n) *capacity *= 2;
|
||||
*data = lovrRealloc(*data, *capacity * stride);
|
||||
}
|
||||
|
||||
// Hash function (FNV1a)
|
||||
static inline uint64_t hash64(const void* data, size_t length) {
|
||||
const uint8_t* bytes = (const uint8_t*) data;
|
||||
|
@ -68,33 +96,6 @@ static inline uint64_t hash64(const void* data, size_t length) {
|
|||
return hash;
|
||||
}
|
||||
|
||||
// Refcounting
|
||||
void lovrRetain(void* ref);
|
||||
void lovrRelease(void* ref, void (*destructor)(void*));
|
||||
|
||||
// Dynamic Array
|
||||
typedef void* arr_allocator(void* data, size_t size);
|
||||
#define arr_t(T) struct { T* data; arr_allocator* alloc; size_t length, capacity; }
|
||||
#define arr_init(a, allocator) (a)->data = NULL, (a)->length = 0, (a)->capacity = 0, (a)->alloc = allocator
|
||||
#define arr_free(a) if ((a)->data) (a)->alloc((a)->data, 0)
|
||||
#define arr_reserve(a, n) _arr_reserve((void**) &((a)->data), n, &(a)->capacity, sizeof(*(a)->data), (a)->alloc)
|
||||
#define arr_expand(a, n) arr_reserve(a, (a)->length + n)
|
||||
#define arr_push(a, x) arr_reserve(a, (a)->length + 1), (a)->data[(a)->length] = x, (a)->length++
|
||||
#define arr_pop(a) (a)->data[--(a)->length]
|
||||
#define arr_append(a, p, n) arr_reserve(a, (a)->length + n), memcpy((a)->data + (a)->length, p, n * sizeof(*(p))), (a)->length += n
|
||||
#define arr_splice(a, i, n) memmove((a)->data + (i), (a)->data + ((i) + n), ((a)->length - (i) - (n)) * sizeof(*(a)->data)), (a)->length -= n
|
||||
#define arr_clear(a) (a)->length = 0
|
||||
|
||||
void* arr_alloc(void* data, size_t size);
|
||||
|
||||
static inline void _arr_reserve(void** data, size_t n, size_t* capacity, size_t stride, arr_allocator* allocator) {
|
||||
if (*capacity >= n) return;
|
||||
if (*capacity == 0) *capacity = 1;
|
||||
while (*capacity < n) *capacity *= 2;
|
||||
*data = allocator(*data, *capacity * stride);
|
||||
lovrAssert(*data, "Out of memory");
|
||||
}
|
||||
|
||||
// Hashmap (does not support removal)
|
||||
typedef struct {
|
||||
uint64_t* hashes;
|
||||
|
|
Loading…
Reference in New Issue