Files
ld58/code/core/impl/arena.c

211 lines
4.6 KiB
C
Raw Normal View History

void *M_ZeroSize(void *base, U64 size) {
U8 *bytes = (U8 *) base;
while (size--) {
*bytes++ = 0;
}
return base;
}
void *M_CopySize(void *dst, void *src, U64 size) {
U8 *se = (U8 *) src + (size - 1);
U8 *de = (U8 *) dst + (size - 1);
while (size--) {
*de-- = *se--;
}
return dst;
}
M_Arena *_M_ArenaAlloc(U64 limit, M_ArenaOpts *opts) {
M_Arena *result = 0;
local_persist M_ArenaOpts _opts;
if (!opts) {
opts = &_opts;
}
U64 page_size = VM_PageSize();
U64 granularity = VM_AllocationGranularity();
U64 reserve = Max(AlignUp(limit, granularity), granularity);
U64 initial = Clamp(page_size, AlignUp(opts->initial, page_size), reserve);
void *base = VM_Reserve(reserve);
if (base != 0) {
if (VM_Commit(base, initial)) {
result = cast(M_Arena *) base;
result->current = result;
result->prev = 0;
result->base = 0;
result->offset = sizeof(M_Arena);
result->limit = reserve;
result->committed = initial;
result->increment = Max(AlignUp(opts->increment, page_size), page_size);
result->flags = opts->flags;
}
else {
VM_Release(base, reserve);
}
}
Assert(result != 0);
return result;
}
void *_M_ArenaPush(M_Arena *arena, U64 esize, M_ArenaPushOpts *opts) {
void *result = 0;
local_persist M_ArenaPushOpts _opts = { .count = 1, .align = 8 };
if (!opts) {
opts = &_opts;
}
U64 alignment = Clamp(1, opts->align, 4096);
M_Arena *current = arena->current;
U64 total = esize * opts->count;
U64 offset = AlignUp(current->offset, alignment);
U64 end = offset + total;
if (end > current->limit) {
// Not enough space, chain a new arena if flags allow
if ((arena->flags & M_ARENA_FIXED_SIZE) == 0) {
U64 reserve = Min(total + sizeof(M_Arena), M_ARENA_CHAIN_RESERVE);
M_Arena *next = M_ArenaAlloc(reserve, .flags = arena->flags);
next->base = current->base + current->limit;
SLL_PushN(arena->current, next, prev);
current = next;
offset = AlignUp(current->offset, alignment);
end = offset + total;
}
}
if (end > current->committed) {
// Not enough committed, commit more memory
U64 commit_offset = AlignUp(end, current->increment);
U64 commit_limit = Min(commit_offset, current->limit);
U8 *commit_base = cast(U8 *) current + current->committed;
U64 commit_size = commit_limit - current->committed;
if (VM_Commit(commit_base, commit_size)) {
current->committed = commit_limit;
}
}
if (end <= current->committed) {
// Successfully got enough memory, push the allocation
result = cast(U8 *) current + offset;
current->offset = end;
if (((opts->flags | arena->flags) & M_ARENA_NO_ZERO) == 0){
M_ZeroSize(result, total);
}
}
Assert(result != 0);
Assert(((U64) result & (alignment - 1)) == 0);
return result;
}
void *_M_ArenaPushCopy(M_Arena *arena, void *from, U64 size, M_ArenaPushOpts *opts) {
void *result = M_CopySize(_M_ArenaPush(arena, size, opts), from, size);
return result;
}
void M_ArenaReset(M_Arena *arena) {
M_Arena *base = arena->current;
while (base->base != 0) {
M_Arena *prev = base->prev;
VM_Release(base, base->limit);
base = prev;
}
Assert(arena == base);
// @Todo: We could decommit some of the memory in the base arena, do we want to give a
// parameter to choose how much :decommit
base->offset = sizeof(M_Arena);
arena->current = base;
}
void M_ArenaRelease(M_Arena *arena) {
M_ArenaReset(arena);
VM_Release(arena, arena->limit);
}
U64 M_ArenaOffset(M_Arena *arena) {
U64 result = arena->current->base + arena->current->offset;
return result;
}
void M_ArenaPop(M_Arena *arena, U64 offset) {
M_Arena *base = arena->current;
while (base->base > offset) {
M_Arena *prev = base->prev;
VM_Release(base, base->limit);
base = prev;
}
// :decommit
arena->current = base;
base->offset = Max(offset - base->base, sizeof(M_Arena));
}
void M_ArenaPopSize(M_Arena *arena, U64 size) {
U64 offset = M_ArenaOffset(arena);
offset -= Min(offset, size);
M_ArenaPop(arena, offset);
}
#define M_TEMP_ARENA_LIMIT GB(4)
static thread_var M_Arena *__tls_temp[2];
M_Temp M_TempAcquire(U64 count, M_Arena **conflicts) {
M_Temp result = { 0 };
for (U32 it = 0; it < ArraySize(__tls_temp); ++it) {
if (!__tls_temp[it]) {
__tls_temp[it] = M_ArenaAlloc(M_TEMP_ARENA_LIMIT, .initial = MB(1), .increment = MB(1));
}
result.arena = __tls_temp[it];
for (U32 c = 0; c < count; ++c) {
if (__tls_temp[it] == conflicts[c]) {
result.arena = 0;
break;
}
}
if (result.arena) {
result.offset = M_ArenaOffset(result.arena);
break;
}
}
Assert(result.arena != 0);
return result;
}
void M_TempRelease(M_Temp temp) {
M_ArenaPop(temp.arena, temp.offset);
}