Files
c3c/resources/lib/std/mem.c3

190 lines
5.0 KiB
C

module std::mem;
extern func void* _malloc(usize bytes) @extname("malloc");
extern func void* _realloc(void* ptr, usize bytes) @extname("realloc");
extern func void* _calloc(usize bytes, usize elements) @extname("calloc");
extern func void _free(void* ptr) @extname("free");
enum AllocationKind
{
ALLOC,
REALLOC,
FREE,
}
errtype AllocationFailure
{
OUT_OF_MEMORY
}
define AllocatorFunction = func void!(void *data, void** pointer, usize bytes, usize alignment, AllocationKind kind);
struct Allocator
{
AllocatorFunction allocation_function;
void *data;
}
func void copy(char* dst, char* src, usize size)
{
for (usize i = 0; i < size; i++) dst[i] = src[i];
}
func void! system_malloc_function(void *unused, void** pointer, usize bytes, usize alignment, AllocationKind kind) @inline
{
switch (kind)
{
case ALLOC:
void* data = _malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
*pointer = data;
return;
case REALLOC:
void* data = _realloc(*pointer, bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
*pointer = data;
return;
case FREE:
_free(*pointer);
*pointer = null;
return;
}
$unreachable;
}
struct SlotAllocator
{
void* pages;
usize page_size;
usize page_count;
usize bitmask;
usize current_page;
}
func void*! SlotAllocator.alloc(SlotAllocator *allocator, usize size)
{
void* active_page = (char*)(allocator.pages) + allocator.current_page * allocator.page_size;
void** page_pointer = (void**)(active_page);
if (*page_pointer)
{
mem::free(*page_pointer);
*page_pointer = null;
}
if (size > allocator.page_size - $sizeof(page_pointer))
{
void* mem = mem::_malloc(size);
if (!mem) return AllocationFailure.OUT_OF_MEMORY!;
*page_pointer = mem;
allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask);
return mem;
}
allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask);
return &page_pointer[1];
}
struct RingAllocator
{
char *data;
usize size;
usize offset;
}
func void* RingAllocator.alloc(RingAllocator *allocator, usize size)
{
if (size > allocator.size) return null;
// Wraparound? If so, start at the beginning.
if (allocator.offset + size > allocator.size)
{
allocator.offset = size;
return allocator.data;
}
void* data = allocator.offset + allocator.data;
allocator.offset = (allocator.offset + size) & allocator.size;
return data;
}
func void* RingAllocator.realloc(RingAllocator *allocator, void* ptr, usize size)
{
if (size > allocator.size) return null;
assert(allocator.data >= ptr && ptr < allocator.data + size, "Realloc on other allocator.");
// 1. The pointer is before the allocator
if (allocator.data + allocator.offset > ptr)
{
if (allocator.data + allocator.size < ptr + size)
{
// 1a. There is not enough space, we need to copy to the start.
usize pointer_offset = ptr - allocator.data;
usize copy_len = pointer_offset + size > allocator.offset ? allocator.offset - pointer_offset : size;
//memcpy(allocator.data, ptr, copy_len);
allocator.offset = size;
return allocator.data;
}
// 1b. There is enough space, so we just change the offset:
allocator.offset = ptr - allocator.data + size;
return ptr;
}
// 2. The pointer is after the allocator
// 2a. Is there sufficient space?
if (ptr + size <= allocator.data + allocator.size)
{
// Good, if so we simply change the offset and return the pointer.
allocator.offset = ptr - allocator.data + size;
return ptr;
}
// 2b. Not sufficient space, we copy to the beginning.
usize pointer_offset = ptr - allocator.data;
usize copy_len = allocator.size - (ptr - allocator.data);
if (copy_len > size) copy_len = size;
//memcpy(allocator.data, ptr, copy_len);
allocator.offset = size;
return allocator.data;
}
Allocator main_allocator = { &system_malloc_function, null };
macro malloc($Type)
{
return ($Type*)(mem::alloc($sizeof($Type)));
}
func void* alloc(usize size, usize count = 1) @inline
{
return _malloc(size * count);
}
func void* calloc(usize size, usize elements = 1) @inline
{
return _calloc(size, elements);
}
func void* realloc(void *ptr, usize size) @inline
{
return _realloc(ptr, size);
}
func void free(void* ptr) @inline
{
_free(ptr);
}
const TEMP_BLOCK_SIZE = 1024;
const TEMP_PAGES = 64;
private char[TEMP_BLOCK_SIZE * TEMP_PAGES] allocator_static_storage;
private void*[TEMP_PAGES] allocator_static_page_storage;
SlotAllocator default_allocator = {
.pages = &allocator_static_storage,
.page_size = TEMP_BLOCK_SIZE,
.page_count = TEMP_PAGES,
.bitmask = TEMP_PAGES - 1,
.current_page = 0,
};
func void*! talloc(usize size)
{
return default_allocator.alloc(size);
}