-
Notifications
You must be signed in to change notification settings - Fork 24
/
nalloc.c
139 lines (121 loc) · 3.1 KB
/
nalloc.c
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* nalloc.c: a simple single-arena allocator for command-line-lifetime allocation */
#include "rc.h"
static struct Block {
size_t used, size;
char *mem;
Block *n;
} *fl, *ul;
/* alignto() works only with power of 2 blocks and assumes 2's complement arithmetic */
#define alignto(m, n) ((m + n - 1) & ~(n - 1))
#define BLOCKSIZE ((size_t) 4096)
/* Allocate a block from the free list or malloc one if none in the fl fit */
static void getblock(size_t n) {
Block *r, *p;
for (r = fl, p = NULL; r != NULL; p = r, r = r->n)
if (n <= r->size)
break; /* look for a block which fits the request */
if (r != NULL) { /* if one is found, take it off the free list */
if (p != NULL)
p->n = r->n;
else
fl = r->n;
} else { /* else allocate a new block */
r = enew(Block);
r->mem = ealloc(r->size = alignto(n, BLOCKSIZE));
}
r->used = 0;
r->n = ul;
ul = r;
}
/*
A fast single-arena allocator. Looks at the current block, and if
there is not enough room, it goes to getblock() for more. "ul"
stands for "used list", and the head of the list is the current
block. "ulp" is a register cache for "ul"; this routine is hacked
for speed. (sigh, optimizing RISC compilers still can't cache the
address of a global in a register)
*/
extern void *nalloc(size_t n) {
size_t base;
Block *ulp;
n = alignto(n, sizeof(align_t));
ulp = ul;
if (ulp != NULL && n + (base = ulp->used) < ulp->size) {
ulp->used = base + n;
return &ulp->mem[base];
} else {
getblock(n);
assert(ul->used == 0);
(ulp = ul)->used = n;
return &ulp->mem[0];
}
}
/*
Frees memory from nalloc space by putting it on the free list.
Returns free blocks to the system, retaining at least MAXMEM bytes
worth of blocks for nalloc.
*/
#define MAXMEM 500000
extern void nfree() {
size_t count;
Block *r;
if (ul == NULL)
return;
for (r = ul; r->n != NULL; r = r->n)
; /* get to end of used list */
r->n = fl; /* tack free list onto it */
fl = ul; /* and make it the free list */
ul = NULL; /* finally, zero out the used list */
for (r = fl, count = r->size; r->n != NULL; r = r->n, count += r->size) {
if (count >= MAXMEM) {
Block *tmp = r;
r = r->n;
tmp->n = NULL; /* terminate the free list */
while (r != NULL) { /* free memory off the tail of the free list */
tmp = r->n;
efree(r->mem);
efree(r);
r = tmp;
}
return;
}
}
}
/*
"Allocates" a new arena by zeroing out the old one. Up to the
calling routine to keep the old value of the block around.
*/
extern Block *newblock() {
Block *old = ul;
ul = NULL;
return old;
}
/* "Restores" an arena to its saved value. */
extern void restoreblock(Block *old) {
nfree();
ul = old;
}
/* generic memory allocation functions */
extern void *ealloc(size_t n) {
void *p;
assert(n);
p = malloc(n);
if (p == NULL) {
uerror("malloc");
rc_exit(1);
}
return p;
}
extern void *erealloc(void *p, size_t n) {
if (p == NULL) /* erealloc() has POSIX realloc() semantics */
return ealloc(n);
if ((p = realloc(p, n)) == NULL) {
uerror("realloc");
rc_exit(1);
}
return p;
}
extern void efree(void *p) {
if (p != NULL)
free(p);
}