aboutsummaryrefslogtreecommitdiffhomepage
path: root/mem.c
blob: 11c1c8c85b29e07bde59e38a2f2005c71c75b1ca (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
108
109
110
111
112
113
114
115
116
117
#include "common.h"
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>

#define ALLOCERR(f) do {                  \
   efmt("%s: %s\n", f, strerror(errno)); \
   ioflush(&bstdout);                    \
   ioflush(&bstderr);                    \
   abort();                              \
} while (0)

static void *
xcalloc(size_t n, const char *f)
{
   void *p = calloc(n, 1);
   if (!p) ALLOCERR(f);
   return p;
}

static void *
xrealloc(void *p, size_t n, const char *f)
{
   p = p ? realloc(p, n) : malloc(n);
   if (!p) ALLOCERR(f);
   return p;
}

/* vec: when _cap < 0, buf is dynamic allocated, otherwise an user provided buf */

void
vinit_(void **p, int *pcap, void *inlbuf, int cap, uint siz)
{
   assert(!*p);
   *pcap = cap;
   if (inlbuf) {
      *p = inlbuf; 
   } else if (cap) {
      *p = xrealloc(0, cap*siz, "vinit");
      *pcap = -cap;
   }
}

void
vpush_(void **p, int *pcap, uint *pn, uint siz)
{
   if (*pn == *pcap) { /* empty or inline buffer */
      int cap = *pcap ? *pcap * 2 : 8;
      *p = xrealloc(NULL, cap * siz, "vpush");
      *pcap = -cap;
   } else if (*pn == -*pcap) { /* dyn buf */
      *p = xrealloc(*p, -(*pcap *= 2) * siz, "vpush");
   }
   assert(-(volatile int)*pcap > *pn && "overflow");
}

void *
vpushn_(void **p, int *pcap, uint *pn, uint siz, const void *dat, uint ndat)
{
   void *beg;

   for (uint i = 0; i < ndat; ++i)
      vpush_(p, pcap, pn, siz);
   beg = (char *)*p + *pn * siz;
   memcpy(beg, dat, ndat * siz);
   *pn += ndat;
   return beg;
}

struct arena *
newarena(uint chunksiz)
{
   struct arena *ar = xcalloc(offsetof(struct arena, mem) + chunksiz, "newarena");
   assert(chunksiz < 1u<<31 && "toobig");
   ar->cap = chunksiz;
   ar->dyn = 1;
   return ar;
}

void *
alloc(struct arena **par, uint siz, uint align)
{
   uint idx;
   struct arena *new;
   
   if (siz > (*par)->cap) {
      new = newarena(siz);
      new->n = siz;
      new->prev = (*par)->prev;
      (*par)->prev = new;
      return new->mem;
   }
   align = align ? align : sizeof(void *);
   idx = (uchar *)alignup((uintptr_t)&(*par)->mem[(*par)->n], align) - (*par)->mem;
   if ((*par)->cap - idx >= siz) {
      (*par)->n = idx + siz;
      return (*par)->mem + idx;
   }
   new = newarena((*par)->cap);
   new->prev = *par;
   *par = new;
   new->n = siz;
   return new->mem;
}

void
freearena(struct arena *ar)
{
   struct arena *prev;
   for (; ar; ar = prev) {
      prev = ar->prev;
      if (ar->dyn)
         free(ar);
   }
}

/* vim:set ts=3 sw=3 expandtab: */