#include "rbs/util/rbs_allocator.h"
#include "rbs/util/rbs_assert.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#endif
typedef struct rbs_allocator_page {
struct rbs_allocator_page *next;
size_t size;
size_t used;
} rbs_allocator_page_t;
static size_t get_system_page_size(void) {
#ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
long sz = sysconf(_SC_PAGESIZE);
if (sz == -1) return 4096; return (size_t) sz;
#endif
}
static inline uintptr_t rbs_align_up_uintptr(uintptr_t value, size_t alignment) {
RBS_ASSERT(alignment != 0 && (alignment & (alignment - 1)) == 0, "alignment must be a non-zero power of two. alignment: %zu", alignment);
return (value + (alignment - 1)) & ~(uintptr_t) (alignment - 1);
}
static rbs_allocator_page_t *rbs_allocator_page_new(size_t payload_size) {
const size_t page_header_size = sizeof(rbs_allocator_page_t);
rbs_allocator_page_t *page = (rbs_allocator_page_t *) malloc(page_header_size + payload_size);
page->size = payload_size;
page->used = 0;
return page;
}
rbs_allocator_t *rbs_allocator_init(void) {
rbs_allocator_t *allocator = (rbs_allocator_t *) malloc(sizeof(rbs_allocator_t));
const size_t system_page_size = get_system_page_size();
allocator->default_page_payload_size = system_page_size - sizeof(rbs_allocator_page_t);
allocator->page = rbs_allocator_page_new(allocator->default_page_payload_size);
allocator->page->next = NULL;
return allocator;
}
void rbs_allocator_free(rbs_allocator_t *allocator) {
rbs_allocator_page_t *page = allocator->page;
while (page) {
rbs_allocator_page_t *next = page->next;
free(page);
page = next;
}
free(allocator);
}
void *rbs_allocator_realloc_impl(rbs_allocator_t *allocator, void *ptr, size_t old_size, size_t new_size, size_t alignment) {
void *p = rbs_allocator_malloc_impl(allocator, new_size, alignment);
memcpy(p, ptr, old_size);
return p;
}
void *rbs_allocator_malloc_impl(rbs_allocator_t *allocator, size_t size, size_t alignment) {
if (allocator->default_page_payload_size < size) { rbs_allocator_page_t *new_page = rbs_allocator_page_new(size + (alignment - 1));
new_page->next = allocator->page->next;
allocator->page->next = new_page;
uintptr_t base = (uintptr_t) new_page + sizeof(rbs_allocator_page_t);
uintptr_t aligned_ptr = rbs_align_up_uintptr(base, alignment);
return (void *) aligned_ptr;
}
rbs_allocator_page_t *page = allocator->page;
uintptr_t base = (uintptr_t) page + sizeof(rbs_allocator_page_t);
size_t used_aligned = (size_t) (rbs_align_up_uintptr(base + page->used, alignment) - base);
if (used_aligned + size > page->size) {
rbs_allocator_page_t *new_page = rbs_allocator_page_new(allocator->default_page_payload_size);
new_page->next = allocator->page;
allocator->page = new_page;
page = new_page;
base = (uintptr_t) page + sizeof(rbs_allocator_page_t);
used_aligned = (size_t) (rbs_align_up_uintptr(base, alignment) - base); }
uintptr_t pointer = base + used_aligned;
page->used = used_aligned + size;
return (void *) pointer;
}
void *rbs_allocator_calloc_impl(rbs_allocator_t *allocator, size_t count, size_t size, size_t alignment) {
void *p = rbs_allocator_malloc_many_impl(allocator, count, size, alignment);
memset(p, 0, count * size);
return p;
}
void *rbs_allocator_malloc_many_impl(rbs_allocator_t *allocator, size_t count, size_t size, size_t alignment) {
return rbs_allocator_malloc_impl(allocator, count * size, alignment);
}