summaryrefslogtreecommitdiffstats
path: root/lib/arena.c
blob: bea98ef7a6357cb32606018fc9b75c937077adc8 (plain) (blame)
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;
}