128 lines
No EOL
4 KiB
Markdown
128 lines
No EOL
4 KiB
Markdown
# Arena
|
|
-----------
|
|
|
|
An arena is a bump allocator, under the hood it can use one of 3 strategies to allocate its data:
|
|
|
|
* `Virtual`: allocates with virtual memory, meaning that it reserves the data upfront, but only allocates one page at a time (usually 64 Kb). This is the preferred way to use the arena as it can freely allocate gigabytes of memory for free
|
|
* `Malloc`: allocates the memory upfront using malloc
|
|
* `Static`: uses a buffer passed by the user instead of allocating
|
|
|
|
To help with allocating big chunks of data, `arena.h` defines the macros `KB`, `MB`, and `GB`. If you don't need them you can define `ARENA_NO_SIZE_HELPERS`
|
|
|
|
To create an arena use the macro `arenaMake`:
|
|
```c
|
|
arena_t varena = arenaMake(ARENA_VIRTUAL, GB(1));
|
|
uint8 buffer[1024];
|
|
arena_t sarena = arenaMake(ARENA_STATIC, sizeof(buffer), buffer);
|
|
```
|
|
|
|
To allocate use the `alloc` macro. The parameters to allocate are:
|
|
|
|
* A pointer to the arena
|
|
* The type to allocate
|
|
* (optional) the number of items to allocate
|
|
* (optional) flags:
|
|
* `ALLOC_NOZERO`: by default the arena sets the memory to zero before returning, use this if you want to skip it
|
|
* `ALLOC_SOFT_FAIL`: by default the arena panics when an allocation fails, meaning that it never returns `NULL`.
|
|
* (automatic) the align of the type
|
|
* (automatic) the size of the type
|
|
|
|
Example usage:
|
|
|
|
```c
|
|
// allocate 15 strings
|
|
str_t *string_list = alloc(arena, str_t, 15);
|
|
// allocate a 1024 bytes buffer
|
|
uint8 *buffer = alloc(arena, uint8, 1024);
|
|
// allocate a structure
|
|
game_t *game = alloc(arena, game_t);
|
|
```
|
|
|
|
The strength of the arena is that it makes it much easier to reason about lifetimes, for instance:
|
|
|
|
```c
|
|
// pass a pointer to the arena for the data that we need to return
|
|
WCHAR *win32_get_full_path(arena_t *arena, const char *rel_path) {
|
|
// we need a small scratch arena for allocations that
|
|
// will be thrown away at the end of this function
|
|
uint8 scratch_buf[1024];
|
|
arena_t scratch = arenaMake(ARENA_STATIC, sizeof(scratch_buf, scratch_buf));
|
|
|
|
WCHAR *win32_rel_path = str_to_wchar(&scratch, rel_path);
|
|
|
|
DWORD pathlen = GetFullPathName(win32_rel_path, 0, NULL, NULL);
|
|
WCHAR *fullpath = alloc(arena, WCHAR, pathlen + 1);
|
|
GetFullPath(win32_rel_path, pathlen + 1, fullpath, NULL);
|
|
|
|
// notice how we don't need to free anything at the end
|
|
return fullpath;
|
|
}
|
|
```
|
|
|
|
There are a few helper functions:
|
|
|
|
* `arenaScratch`: sub allocate an arena from another arena
|
|
* `arenaTell`: returns the number of bytes allocated
|
|
* `arenaRemaining`: returns the number of bytes that can still be allocated
|
|
* `arenaRewind`: rewinds the arena to N bytes from the start (effectively "freeing" that memory)
|
|
* `arenaPop`: pops N bytes from the arena (effectively "freeing" that memory)
|
|
|
|
Here is an example usage of a full program:
|
|
|
|
```c
|
|
typedef struct {
|
|
char *buf;
|
|
usize len;
|
|
} str_t;
|
|
|
|
str_t read_file(arena_t *arena, const char *filename) {
|
|
str_t out = {0};
|
|
|
|
FILE *fp = fopen(filename, "rb");
|
|
if (!fp) goto error;
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
out.len = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
out.buf = alloc(arena, char, out.len + 1);
|
|
|
|
fread(out.buf, 1, out.len, fp);
|
|
|
|
error:
|
|
return out;
|
|
}
|
|
|
|
void write_file(const char *filename, str_t data) {
|
|
FILE *fp = fopen(filename, "wb");
|
|
if (!fp) return;
|
|
fwrite(data.buf, 1, data.len, fp);
|
|
}
|
|
|
|
int main() {
|
|
arena_t arena = arenaMake(ARENA_VIRTUAL, GB(1));
|
|
str_t title = {0};
|
|
|
|
{
|
|
uint8 tmpbuf[KB(5)];
|
|
arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tmpbuf), tmpbuf);
|
|
str_t file = read_file(&scratch, "file.json");
|
|
json_t json = json_parse(&scratch, file);
|
|
title = str_dup(arena, json_get(json, "title"));
|
|
}
|
|
|
|
{
|
|
// copying an arena by value effectively makes it a scratch arena,
|
|
// as long as you don't use the original inside the same scope!
|
|
arena_t scratch = arena;
|
|
str_t to_write = str_fmt(
|
|
&scratch,
|
|
"{ \"title\": \"%s\" }", title.buf
|
|
);
|
|
write_file("out.json", to_write);
|
|
}
|
|
|
|
// cleanup all allocations at once
|
|
arenaCleanup(&arena);
|
|
}
|
|
``` |