Skip to content

Instantly share code, notes, and snippets.

@stillwwater
Created September 1, 2024 04:10
Show Gist options
  • Save stillwwater/5c341c7859c52a49a20fba8db8b1a7d1 to your computer and use it in GitHub Desktop.
Save stillwwater/5c341c7859c52a49a20fba8db8b1a7d1 to your computer and use it in GitHub Desktop.
struct arena {
usize commitsize;
char *name;
char *start; // start of reserved address space
char *end; // end of reserved address space
char *heap; // start of user block
char *allocated; // end of allocated block
char *commited; // end of commited block
};
// Allocates a memory arena. Capacity should be at least the allocation
// granularity on the system (64KB on Windows).
//
// capacity:
// Amount of the address space to be reserved for this arena.
struct arena *
make_arena(char *name, usize capacity)
{
struct arena *arena;
// we could deal with this edge case if we wanted
assert(sysinfo.dwPageSize >= sizeof(*arena));
assert(capacity >= sizeof(*arena));
assert(capacity >= sysinfo.dwAllocationGranularity);
// reserve address space
arena = VirtualAlloc(NULL, capacity, MEM_RESERVE, PAGE_NOACCESS);
if (!arena)
fatal_error("MEM_RESERVE failed for '%s' (0x%lX)", name, GetLastError());
// commit 1 page of memory to use for the arena header
arena = VirtualAlloc(arena, sysinfo.dwPageSize, MEM_COMMIT, PAGE_READWRITE);
if (!arena)
fatal_error("MEM_COMMIT failed for '%s' (0x%lX)", name, GetLastError());
arena->name = name;
arena->start = (char *)arena;
arena->end = arena->start + capacity;
arena->heap = arena->start + sysinfo.dwPageSize;
arena->allocated = arena->heap;
arena->commited = arena->heap;
if (capacity < ARENA_2MB)
arena->commitsize = ARENA_COMMIT_SMALL;
else if (capacity < ARENA_200MB)
arena->commitsize = ARENA_COMMIT_LARGE;
else
arena->commitsize = ARENA_COMMIT_GIGANTIC;
return arena;
}
// Deallocates memory reserved for an arena.
void
free_arena(struct arena *arena)
{
BOOL result = VirtualFree(arena->start, 0, MEM_RELEASE);
assert(result);
}
// Resets the arena, essentially freeing all allocated objects. Does not
// decommit the underlying memory used by the arena, use free_arena to actually
// free the memory.
void
arena_clear(struct arena *arena)
{
// should memory be decommited?
arena->allocated = arena->heap;
}
__attribute((noinline)) void *
arena_resize(struct arena *arena, void *ptr, usize count)
{
usize commit;
// round to next commit size
commit = arena->commitsize + (count & ~(arena->commitsize - 1));
if (arena->commited + commit > arena->end)
return NULL;
if (!VirtualAlloc(arena->commited, commit, MEM_COMMIT, PAGE_READWRITE))
return NULL;
arena->commited += commit;
#ifndef NDEBUG
memset(ptr, 0xCC, count);
#endif
return ptr;
}
// Allocates an object on the arena. Returns NULL if the arena is full.
//
// align:
// Alignment in bytes of the memory returned. Must be a power of two size.
void *
arena_alloc_align(struct arena *arena, usize align, usize count)
{
void *ptr;
assert(!(align & (align - 1)));
// align the end of allocated block
arena->allocated += (align - ((usize)arena->allocated & (align - 1)));
ptr = arena->allocated;
arena->allocated += count;
if (__builtin_expect(arena->allocated > arena->commited, 0)) {
return arena_resize(arena, ptr, count);
}
#ifndef NDEBUG
memset(ptr, 0xCC, count);
#endif
return ptr;
}
// Allocates an object on the arena. The result returned is 16-byte aligned.
// Returns NULL if the arena is full.
void *
arena_alloc(struct arena *arena, usize count)
{
return arena_alloc_align(arena, 16, count);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment