1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
#include "arena.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#ifdef ARENA_ASAN_INSTRUMENT
#include <sanitizer/asan_interface.h>
#define ASAN_POISON(...) __asan_poison_memory_region(__VA_ARGS__)
#define ASAN_UNPOISON(...) __asan_unpoison_memory_region(__VA_ARGS__)
#else
#define ASAN_POISON(...)
#define ASAN_UNPOISON(...)
#endif
struct vl__arena_tag {
size_t cap; /* number of bytes after the end of this struct we can use */
size_t cur;
};
#define ARENA_ALIGN (size_t)((1 << 6) - 1)
#define ROUND_UP(_am) (((_am) + ARENA_ALIGN) & ~ARENA_ALIGN)
static void *arena_addr(void *a, size_t len)
{
return (void *)((uintptr_t)a + ROUND_UP(sizeof(vl_arena)) + len);
}
/* returns NULL if an arena could not be allocated */
vl_arena *vl_arena_new(size_t cap)
{
cap = ROUND_UP(cap);
vl_arena *arena = calloc(1, ROUND_UP(sizeof(vl_arena)) + cap);
if (!arena) return NULL;
arena->cap = cap;
arena->cur = 0;
ASAN_POISON(arena + 1, cap + (ROUND_UP(sizeof(vl_arena)) - sizeof(vl_arena)));
return arena;
}
/* aborts if the arena is overflowing */
void *vl_arena_push(vl_arena *parena, size_t len)
{
size_t rlen = ROUND_UP(len);
if (parena->cur + rlen > parena->cap) abort();
void *ret = arena_addr(parena, parena->cur);
parena->cur += rlen;
ASAN_UNPOISON(ret, len);
return ret;
}
/* resets the arena (but does not free it) */
void vl_arena_reset(vl_arena *parena)
{
parena->cur = 0;
ASAN_POISON(arena_addr(parena, 0), parena->cap);
}
/* frees the arena */
void vl_arena_free(vl_arena *parena)
{
free(parena);
}
char *vl_arena_strdup(vl_arena *parena, const char *str)
{
size_t len = strlen(str);
void *out = vl_arena_push(parena, len + 1);
memcpy(out, str, len + 1);
return out;
}
char *vl_arena_sprintf(vl_arena *parena, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
char *ret = vl_arena_vsprintf(parena, fmt, ap);
va_end(ap);
return ret;
}
char *vl_arena_vsprintf(vl_arena *parena, const char *fmt, va_list ap)
{
va_list copy;
va_copy(copy, ap);
int desire = vsnprintf(NULL, 0, fmt, copy);
va_end(copy);
if (desire < 0) {
abort(); /* vsnprintf should never fail */
}
char *str = vl_arena_push(parena, (unsigned)desire + 1);
vsnprintf(str, (unsigned)desire + 1, fmt, ap);
return str;
}
|