#include "cig.h" #include #include void *arena_allocator_alloc_func( arena_allocator_t *this, size_t bytes, const char *file, int line ) { buffer_allocator_t ba = (buffer_allocator_t) { .size=dyn_array_length(this->bytes), .capacity=dyn_array_capacity(this->bytes), .data=this->bytes }; void *allocated = buffer_allocator_alloc(&ba, bytes); if (allocated != NULL) { return allocated; } allocated = borrow_allocator_alloc_func(&this->borrow_allocator, bytes, file, line); if (allocated != NULL) { this->to_allocate += bytes; return allocated; } return NULL; } void arena_allocator_reset_func(arena_allocator_t *this, const char *file, int line) { borrow_allocator_free_all(&this->borrow_allocator); if (this->bytes == NULL) { this->bytes = dyn_array_create_non_crashing_func( (dyn_array_create_non_crashing_func_args_t) { .allocator=allocator_stdlib(), .itemsize=sizeof(uint8_t), .initial_capacity=this->to_allocate, .file=file, .line=line, } ); } if (this->bytes == NULL) { return; } if (dyn_array_capacity(this->bytes) < this->to_allocate) { size_t needed_bytes = this->to_allocate - dyn_array_length(this->bytes); uint8_t *new_bytes = dyn_array_grow_non_crashing_func( this->bytes, needed_bytes, file, line ); if (new_bytes == NULL) { dyn_array_shrink_func( this->bytes, dyn_array_length(this->bytes), file, line ); return; } else { this->bytes = new_bytes; } } if (this->bytes == NULL) { return; } dyn_array_shrink_func(this->bytes, dyn_array_length(this->bytes), file, line); return; } void arena_allocator_destroy(arena_allocator_t *this) { borrow_allocator_free_all(&this->borrow_allocator); dyn_array_destroy(this->bytes); *this = arena_allocator_create(); } static void *arena_allocator_alloc_impl(void *this, size_t bytes, const char *file, int line) { return arena_allocator_alloc_func( (arena_allocator_t*) this, bytes, file, line ); } // TODO move to concrete implementation static void *arena_allocator_resize_impl(void *this, void *old_ptr, size_t bytes, const char *file, int line) { (void)this; (void)file; (void)line; arena_allocator_t *t = (arena_allocator_t*) this; if ( (size_t)t->bytes <= (size_t)old_ptr && (size_t)old_ptr < (size_t)t->bytes + dyn_array_length(t->bytes) ) { uint8_t *old_buffer = old_ptr; uint8_t *new_buffer = arena_allocator_alloc_func(this, bytes, file, line); if (new_buffer == NULL) return NULL; // TODO: is this off by 1? size_t old_ptr_dist = (size_t)t->bytes + dyn_array_length(t->bytes) - (size_t)old_ptr; size_t bytes_to_copy = bytes < old_ptr_dist ? bytes : old_ptr_dist; for ( size_t i = 0; i < bytes_to_copy; i++ ) { new_buffer[i] = old_buffer[i]; } return new_buffer; } else { void *new_buffer = borrow_allocator_resize_func(&t->borrow_allocator, old_ptr, bytes, file, line); if (new_buffer == NULL) return NULL; // The bytes added to to_allocate is just an educated guess. If // calling realloc, you are often allocating twice what you got from the // last malloc/realloc call. t->to_allocate += bytes/2; return new_buffer; } return arena_allocator_alloc_func( (arena_allocator_t*) this, bytes, file, line ); } static void arena_allocator_free_impl(void *this, void *ptr, const char *file, int line) { (void)this; (void)file; (void)line; (void)ptr; } static const allocator_vtbl_t arena_allocator_vtbl = { .alloc = arena_allocator_alloc_impl, .resize = arena_allocator_resize_impl, .free = arena_allocator_free_impl, }; allocator_t arena_allocator_interface(arena_allocator_t *this) { return (allocator_t) { .this = this, .vtbl = &arena_allocator_vtbl, }; }