==> 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}