skip to content
==> src/arena.c <==
1/* The arena: a list of chunks, newest first; big requests get a chunk of their own. */
2#include "tui/arena.h"
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8struct tui_arena_chunk {
9    tui_arena_chunk *next;
10    size_t cap;
11    _Alignas(16) unsigned char data[];
12};
13
14enum { CHUNK = 64 * 1024, ALIGN = 16 };
15
16static _Noreturn void oom(void) {
17    fputs("tui-arena: out of memory\n", stderr);
18    abort();
19}
20
21static tui_arena_chunk *chunk_new(size_t cap, tui_arena_chunk *next) {
22    tui_arena_chunk *c = malloc(sizeof *c + cap);
23    if (!c) oom();
24    c->next = next;
25    c->cap = cap;
26    return c;
27}
28
29void *tui_arena_alloc(tui_arena *a, size_t n) {
30    size_t need = (n + ALIGN - 1) & ~(size_t)(ALIGN - 1);
31    if (need == 0) need = ALIGN;
32    if (!a->head || a->used + need > a->head->cap) {
33        size_t cap = need > CHUNK ? need : CHUNK;
34        a->head = chunk_new(cap, a->head);
35        a->used = 0;
36    }
37    void *p = a->head->data + a->used;
38    a->used += need;
39    memset(p, 0, need);
40    return p;
41}
42
43void *tui_arena_grow(tui_arena *a, void *old, size_t old_n, size_t new_n) {
44    if (new_n <= old_n) return old;
45    size_t old_need = (old_n + ALIGN - 1) & ~(size_t)(ALIGN - 1);
46    size_t new_need = (new_n + ALIGN - 1) & ~(size_t)(ALIGN - 1);
47    /* The last allocation of the head chunk grows in place when it fits. */
48    if (old && a->head && (unsigned char *)old + old_need == a->head->data + a->used &&
49        a->used - old_need + new_need <= a->head->cap) {
50        memset((unsigned char *)old + old_need, 0, new_need - old_need);
51        a->used += new_need - old_need;
52        return old;
53    }
54    void *p = tui_arena_alloc(a, new_n);
55    if (old && old_n) memcpy(p, old, old_n);
56    return p;
57}
58
59char *tui_arena_strndup(tui_arena *a, const char *s, size_t n) {
60    char *d = tui_arena_alloc(a, n + 1);
61    if (n) memcpy(d, s, n);
62    return d;
63}
64
65char *tui_arena_strdup(tui_arena *a, const char *s) { return tui_arena_strndup(a, s, strlen(s)); }
66
67char *tui_arena_vfmt(tui_arena *a, const char *fmt, va_list ap) {
68    va_list copy;
69    va_copy(copy, ap);
70    int n = vsnprintf(NULL, 0, fmt, copy);
71    va_end(copy);
72    if (n < 0) return tui_arena_alloc(a, 1);
73    char *s = tui_arena_alloc(a, (size_t)n + 1);
74    vsnprintf(s, (size_t)n + 1, fmt, ap);
75    return s;
76}
77
78char *tui_arena_fmt(tui_arena *a, const char *fmt, ...) {
79    va_list ap;
80    va_start(ap, fmt);
81    char *s = tui_arena_vfmt(a, fmt, ap);
82    va_end(ap);
83    return s;
84}
85
86void tui_arena_reset(tui_arena *a) {
87    if (!a->head) return;
88    tui_arena_chunk *keep = a->head;
89    while (keep->next) keep = keep->next; /* the oldest chunk is the first, small one */
90    tui_arena_chunk *c = a->head;
91    while (c != keep) {
92        tui_arena_chunk *next = c->next;
93        free(c);
94        c = next;
95    }
96    keep->next = NULL;
97    a->head = keep;
98    a->used = 0;
99}
100
101void tui_arena_free(tui_arena *a) {
102    tui_arena_chunk *c = a->head;
103    while (c) {
104        tui_arena_chunk *next = c->next;
105        free(c);
106        c = next;
107    }
108    a->head = NULL;
109    a->used = 0;
110}