/* The following software is licensed under the GPL-3.0 license and created by Goren Barak. */ #include #include #include #include #ifndef NO_FETCH_MACRO #define fetch(T, gc, x) *((T*)Gc_fetch(gc, x)) #endif typedef enum { NONE, NOT_ENOUGH_MEMORY_ALLOC, NOT_ENOUGH_MEMORY_HEADER, } Error; /* NOTE: you can use if (error) { ... } to check if there is an error. */ static Error error = NONE; typedef struct Gc { void **ptrs; /* The pointers to the actual data or NULL if the data is a reference to an index on here. */ uint32_t *rcs; /* The reference counter for the data on this index in ptrs. */ size_t len; /* The length of ptrs, references, and rcs (they have the same length). */ size_t cap; /* The maximum capacity, will be doubled if reached. */ } Gc; void Gc_new(Gc *self) { self->cap = 256; void *ptr = malloc((sizeof(void*) * 256) + (sizeof(uint32_t) * 256)); /* This allocates a grand total of 3072 bytes. */ if (ptr == NULL) { error == NOT_ENOUGH_MEMORY_HEADER; } self->ptrs = ptr; self->rcs = ptr + (sizeof(void*) * 256); self->len = 0; } uint32_t Gc_allocate(Gc *self, size_t size) { if (self->len+1 > self->cap) { self->cap *= 2; if (!realloc(self->ptrs, (sizeof(void*) * self->cap) + ((sizeof(uint32_t) * self->cap)))) { error = NOT_ENOUGH_MEMORY_HEADER; } } void *ptr = malloc(size); if (ptr == NULL) { error = NOT_ENOUGH_MEMORY_ALLOC; } self->ptrs[self->len] = ptr; self->rcs[self->len] = 1; return self->len++; } uint32_t Gc_ref(Gc *self, uint32_t id) { self->rcs[id] += 1; return id; } void* Gc_fetch(Gc *self, uint32_t id) { void* ptr = self->ptrs[id]; if (ptr != NULL) { return ptr; } else { printf("ERROR: tried to access deleted variable.\n"); exit(-1); } } void Gc_drop(Gc *self, uint32_t id) { self->rcs[id] -= 1; if (self->rcs[id] == 0) { free(self->ptrs[id]); self->ptrs[id] = NULL; } }