119 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <stddef.h>
 | |
| #include <stdint.h>
 | |
| #include <stdbool.h>
 | |
| #include <stdio.h>
 | |
| #include <assert.h>
 | |
| #include <string.h>
 | |
| 
 | |
| bool is_power_of_two(uintptr_t x) {
 | |
| 	return (x & (x-1)) == 0;
 | |
| }
 | |
| 
 | |
| uintptr_t align_forward(uintptr_t ptr, size_t align) {
 | |
| 	uintptr_t p, a, modulo;
 | |
| 
 | |
| 	assert(is_power_of_two(align));
 | |
| 
 | |
| 	p = ptr;
 | |
| 	a = (uintptr_t)align;
 | |
| 	// Same as (p % a) but faster as 'a' is a power of two
 | |
| 	modulo = p & (a-1);
 | |
| 
 | |
| 	if (modulo != 0) {
 | |
| 		// If 'p' address is not aligned, push the address to the
 | |
| 		// next value which is aligned
 | |
| 		p += a - modulo;
 | |
| 	}
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| #ifndef DEFAULT_ALIGNMENT
 | |
| #define DEFAULT_ALIGNMENT (2*sizeof(void *))
 | |
| #endif
 | |
| 
 | |
| typedef struct Arena Arena;
 | |
| struct Arena {
 | |
| 	unsigned char *buf;
 | |
| 	size_t         buf_len;
 | |
| 	size_t         prev_offset; // This will be useful for later on
 | |
| 	size_t         curr_offset;
 | |
| };
 | |
| 
 | |
| void arena_init(Arena *a, void *backing_buffer, size_t backing_buffer_length) {
 | |
| 	a->buf = (unsigned char *)backing_buffer;
 | |
| 	a->buf_len = backing_buffer_length;
 | |
| 	a->curr_offset = 0;
 | |
| 	a->prev_offset = 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void *arena_alloc_align(Arena *a, size_t size, size_t align) {
 | |
| 	// Align 'curr_offset' forward to the specified alignment
 | |
| 	uintptr_t curr_ptr = (uintptr_t)a->buf + (uintptr_t)a->curr_offset;
 | |
| 	uintptr_t offset = align_forward(curr_ptr, align);
 | |
| 	offset -= (uintptr_t)a->buf; // Change to relative offset
 | |
| 
 | |
| 	// Check to see if the backing memory has space left
 | |
| 	if (offset+size <= a->buf_len) {
 | |
| 		void *ptr = &a->buf[offset];
 | |
| 		a->prev_offset = offset;
 | |
| 		a->curr_offset = offset+size;
 | |
| 
 | |
| 		// Zero new memory by default
 | |
| 		memset(ptr, 0, size);
 | |
| 		return ptr;
 | |
| 	}
 | |
| 	// Return NULL if the arena is out of memory (or handle differently)
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| // Because C doesn't have default parameters
 | |
| void *arena_alloc(Arena *a, size_t size) {
 | |
| 	return arena_alloc_align(a, size, DEFAULT_ALIGNMENT);
 | |
| }
 | |
| 
 | |
| // void arena_free(Arena *a, void *ptr) {
 | |
| // 	// Do nothing
 | |
| // }
 | |
| 
 | |
| void *arena_resize_align(Arena *a, void *old_memory, size_t old_size, size_t new_size, size_t align) {
 | |
| 	unsigned char *old_mem = (unsigned char *)old_memory;
 | |
| 
 | |
| 	assert(is_power_of_two(align));
 | |
| 
 | |
| 	if (old_mem == NULL || old_size == 0) {
 | |
| 		return arena_alloc_align(a, new_size, align);
 | |
| 	} else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) {
 | |
| 		if (a->buf+a->prev_offset == old_mem) {
 | |
| 			a->curr_offset = a->prev_offset + new_size;
 | |
| 			if (new_size > old_size) {
 | |
| 				// Zero the new memory by default
 | |
| 				memset(&a->buf[a->curr_offset], 0, new_size-old_size);
 | |
| 			}
 | |
| 			return old_memory;
 | |
| 		} else {
 | |
| 			void *new_memory = arena_alloc_align(a, new_size, align);
 | |
| 			size_t copy_size = old_size < new_size ? old_size : new_size;
 | |
| 			// Copy across old memory to the new memory
 | |
| 			memmove(new_memory, old_memory, copy_size);
 | |
| 			return new_memory;
 | |
| 		}
 | |
| 
 | |
| 	} else {
 | |
| 		assert(0 && "Memory is out of bounds of the buffer in this arena");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| // Because C doesn't have default parameters
 | |
| void *arena_resize(Arena *a, void *old_memory, size_t old_size, size_t new_size) {
 | |
| 	return arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT);
 | |
| }
 | |
| 
 | |
| void arena_free_all(Arena *a) {
 | |
| 	a->curr_offset = 0;
 | |
| 	a->prev_offset = 0;
 | |
| }
 | |
| 
 |