Skip to content

Commit c1f922c

Browse files
committed
arena: memory allocator for fixed-size heaps.
1 parent 94cf471 commit c1f922c

File tree

4 files changed

+459
-0
lines changed

4 files changed

+459
-0
lines changed

Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ TESTS = \
4040
tests/slist$(TEST) \
4141
tests/cbuf$(TEST) \
4242
tests/strlcpy$(TEST) \
43+
tests/arena$(TEST) \
4344
tests/syserr$(TEST) \
4445
tests/clock$(TEST) \
4546
tests/thr$(TEST) \
@@ -117,6 +118,9 @@ tests/cbuf$(TEST): tests/test_cbuf.o src/cbuf.o
117118
tests/strlcpy$(TEST): tests/test_strlcpy.o src/strlcpy.o
118119
$(CC) -o $@ $^
119120

121+
tests/arena$(TEST): tests/test_arena.o src/arena.o
122+
$(CC) -o $@ $^
123+
120124
tests/syserr$(TEST): tests/test_syserr.o
121125
$(CC) -o $@ $^
122126

src/arena.c

+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/* libdlb - data structures and utilities library
2+
* Copyright (C) 2014 Daniel Beer <[email protected]>
3+
*
4+
* Permission to use, copy, modify, and/or distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
17+
#include <stdint.h>
18+
#include <string.h>
19+
#include "arena.h"
20+
21+
struct arena_block {
22+
struct arena_block *prev;
23+
struct arena_block *next;
24+
25+
uint8_t order;
26+
uint8_t is_free;
27+
};
28+
29+
/* Link/unlink items from block lists */
30+
static void block_link(struct arena_block **list, struct arena_block *blk)
31+
{
32+
blk->next = *list;
33+
blk->prev = NULL;
34+
35+
if (blk->next)
36+
blk->next->prev = blk;
37+
38+
*list = blk;
39+
}
40+
41+
static void block_unlink(struct arena_block **list, struct arena_block *blk)
42+
{
43+
if (blk->next)
44+
blk->next->prev = blk->prev;
45+
46+
if (blk->prev)
47+
blk->prev->next = blk->next;
48+
else
49+
*list = blk->next;
50+
}
51+
52+
/* Initialize a new arena */
53+
void arena_init(struct arena *a, void *base, size_t size)
54+
{
55+
int o = ARENA_ORDERS - 1;
56+
57+
memset(a, 0, sizeof(*a));
58+
a->base = base;
59+
a->size = size;
60+
61+
for (;;) {
62+
const size_t bs = 1 << o;
63+
struct arena_block *blk = (struct arena_block *)base;
64+
65+
if (bs < sizeof(struct arena_block))
66+
break;
67+
68+
if (bs > size) {
69+
o--;
70+
continue;
71+
}
72+
73+
blk->is_free = 1;
74+
blk->order = o;
75+
76+
block_link(&a->blocks[o], blk);
77+
78+
size -= bs;
79+
base = ((char *)base) + bs;
80+
}
81+
}
82+
83+
/* Allocate a new block */
84+
void *arena_alloc(struct arena *a, size_t size)
85+
{
86+
struct arena_block *b;
87+
int o = 1;
88+
int i;
89+
90+
/* Figure out the required block order */
91+
if (size >> (ARENA_ORDERS - 1))
92+
return NULL;
93+
while ((o < ARENA_ORDERS) &&
94+
(1 << o) < size + sizeof(struct arena_block))
95+
o++;
96+
if (o >= ARENA_ORDERS)
97+
return NULL;
98+
99+
/* Find the smallest free block at least as big */
100+
i = o;
101+
while ((i < ARENA_ORDERS) && !a->blocks[i])
102+
i++;
103+
if (i >= ARENA_ORDERS)
104+
return NULL;
105+
106+
/* Split blocks until we have one of the optimal order */
107+
while (i > o) {
108+
struct arena_block *c;
109+
110+
b = a->blocks[i];
111+
block_unlink(&a->blocks[i], b);
112+
113+
i--;
114+
c = (struct arena_block *)(((char *)b) + (1 << i));
115+
c->is_free = 1;
116+
c->order = i;
117+
b->order = i;
118+
119+
block_link(&a->blocks[i], b);
120+
block_link(&a->blocks[i], c);
121+
}
122+
123+
/* Return the free block */
124+
b = a->blocks[o];
125+
block_unlink(&a->blocks[o], b);
126+
127+
b->is_free = 0;
128+
return b + 1;
129+
}
130+
131+
static inline struct arena_block *buddy_of(void *base, struct arena_block *b,
132+
int order)
133+
{
134+
const size_t offset = ((char *)b) - ((char *)base);
135+
const size_t boff = offset ^ (1 << order);
136+
137+
return (struct arena_block *)(((char *)base) + boff);
138+
}
139+
140+
void arena_free(struct arena *a, void *ptr)
141+
{
142+
struct arena_block *b = ((struct arena_block *)ptr) - 1;
143+
int o;
144+
145+
if (!ptr)
146+
return;
147+
148+
o = b->order;
149+
b->is_free = 1;
150+
block_link(&a->blocks[o], b);
151+
152+
/* Join blocks */
153+
while (o + 1 < ARENA_ORDERS) {
154+
struct arena_block *c = buddy_of(a->base, b, o);
155+
156+
if ((char *)(c + 1) >
157+
((char *)a->base) + a->size)
158+
break;
159+
160+
if (!c->is_free || c->order != o)
161+
break;
162+
163+
if (b > c) {
164+
struct arena_block *t = c;
165+
166+
c = b;
167+
b = t;
168+
}
169+
170+
block_unlink(&a->blocks[o], b);
171+
block_unlink(&a->blocks[o], c);
172+
173+
b->order = ++o;
174+
block_link(&a->blocks[o], b);
175+
}
176+
}
177+
178+
void *arena_realloc(struct arena *a, void *ptr, size_t size)
179+
{
180+
struct arena_block *b = ((struct arena_block *)ptr) - 1;
181+
size_t copy_size;
182+
void *new_ptr;
183+
int o = 1;
184+
185+
/* No pointer? Revert to malloc */
186+
if (!ptr)
187+
return arena_alloc(a, size);
188+
189+
/* Figure out the required order */
190+
if (size >> (ARENA_ORDERS - 1))
191+
return NULL;
192+
193+
while ((o < ARENA_ORDERS) &&
194+
(1 << o) < size + sizeof(struct arena_block))
195+
o++;
196+
if (o >= ARENA_ORDERS)
197+
return NULL;
198+
199+
/* Is about right already? */
200+
if ((b->order == o) || (o + 1 == b->order))
201+
return ptr;
202+
203+
/* Allocate new and copy */
204+
new_ptr = arena_alloc(a, size);
205+
if (!new_ptr)
206+
return NULL;
207+
208+
copy_size = (1 << b->order) - sizeof(struct arena_block);
209+
if (copy_size > size)
210+
copy_size = size;
211+
212+
memcpy(new_ptr, ptr, copy_size);
213+
214+
arena_free(a, ptr);
215+
return new_ptr;
216+
}
217+
218+
size_t arena_count_free(const struct arena *a)
219+
{
220+
size_t count = 0;
221+
int i;
222+
223+
for (i = 0; i < ARENA_ORDERS; i++) {
224+
size_t c = 0;
225+
struct arena_block *b = a->blocks[i];
226+
227+
while (b) {
228+
b = b->next;
229+
c++;
230+
}
231+
232+
count += c * ((1 << i) - sizeof(struct arena_block));
233+
}
234+
235+
return count;
236+
}

src/arena.h

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* libdlb - data structures and utilities library
2+
* Copyright (C) 2014 Daniel Beer <[email protected]>
3+
*
4+
* Permission to use, copy, modify, and/or distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
17+
#ifndef ARENA_H_
18+
#define ARENA_H_
19+
20+
#include <stddef.h>
21+
22+
/* This object is a memory allocator for fixed-size heaps. Initialize it
23+
* with a large chunk of memory and it will provide implementations of
24+
* malloc, free and realloc.
25+
*
26+
* The algorithm used is buddy allocation. All operations are O(log N)
27+
* in the size of the heap.
28+
*/
29+
#define ARENA_ORDERS 32
30+
31+
struct arena_block;
32+
33+
struct arena {
34+
void *base;
35+
size_t size;
36+
37+
struct arena_block *blocks[ARENA_ORDERS];
38+
};
39+
40+
/* Initialize a new arena */
41+
void arena_init(struct arena *a, void *base, size_t size);
42+
43+
/* Allocate and free blocks */
44+
void *arena_alloc(struct arena *a, size_t size);
45+
void arena_free(struct arena *a, void *ptr);
46+
47+
/* Reallocate a block (if necessary) */
48+
void *arena_realloc(struct arena *a, void *ptr, size_t new_size);
49+
50+
/* Count up free space available in blocks */
51+
size_t arena_count_free(const struct arena *a);
52+
53+
#endif

0 commit comments

Comments
 (0)