Skip to content

Commit 4e93896

Browse files
committed
vutils/allocator: Create an arena style allocator
An arena allocator is a powerful tool for easing the allocation process, you only need to free once. This will be useful for automatically freeing allocated object in VUnit test case.
1 parent e568ad8 commit 4e93896

4 files changed

Lines changed: 164 additions & 1 deletion

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stddef.h>
4+
#include <string.h>
5+
6+
#include "allocator.h"
7+
#include "arena_allocator.h"
8+
9+
struct vut_arena vut_arena_new(struct vut_allocator *base_allocator,
10+
size_t common_max_allocation_size) {
11+
struct vut_arena new_arena = {
12+
.allocations = VUT_VEC_INIT(base_allocator),
13+
.base_allocator = base_allocator,
14+
.common_max_allocation_size = common_max_allocation_size,
15+
};
16+
return new_arena;
17+
}
18+
19+
static void *stub_arena_alloc_malloc(void *arena, size_t bytes, size_t times) {
20+
return vut_arena_malloc(arena, bytes, times);
21+
}
22+
23+
static void *stub_arena_alloc_calloc(void *arena, size_t bytes, size_t times) {
24+
return vut_arena_calloc(arena, bytes, times);
25+
}
26+
27+
static void *stub_arena_alloc_realloc(void *arena, void *ptr, size_t bytes, size_t times) {
28+
return vut_arena_realloc(arena, ptr, bytes, times);
29+
}
30+
31+
static void stub_arena_alloc_free(void *arena, void *ptr) {
32+
(void)arena;
33+
(void)ptr;
34+
}
35+
36+
static struct vut_allocator_funcs arena_allocator_funcs = {
37+
.malloc = stub_arena_alloc_malloc,
38+
.calloc = stub_arena_alloc_calloc,
39+
.realloc = stub_arena_alloc_realloc,
40+
.free = stub_arena_alloc_free,
41+
};
42+
43+
struct vut_allocator vut_arena_to_vut_allocator(struct vut_arena *arena) {
44+
struct vut_allocator new_allocator = {
45+
.base_allocator = arena,
46+
.funcs = arena_allocator_funcs,
47+
};
48+
49+
return new_allocator;
50+
}
51+
52+
static void append_new_buffer(struct vut_arena *arena, size_t buffer_size) {
53+
struct arena_buffer new = { 0 };
54+
new.buffer = vut_allocator_malloc(arena->base_allocator, buffer_size, 1);
55+
VUT_VEC_PUT(&arena->allocations, new);
56+
}
57+
58+
static struct arena_buffer get_last_buffer(struct vut_arena *arena) {
59+
return VUT_VEC_AT(&arena->allocations, arena->allocations.len - 1);
60+
}
61+
62+
void *vut_arena_malloc(struct vut_arena *arena, size_t bytes, size_t times) {
63+
if (bytes * times == 0)
64+
return NULL;
65+
size_t allocation_size = bytes * times;
66+
67+
if (arena->allocations.len == 0)
68+
append_new_buffer(arena, arena->common_max_allocation_size);
69+
70+
struct arena_buffer last_buffer = get_last_buffer(arena);
71+
72+
if (allocation_size > arena->common_max_allocation_size) {
73+
append_new_buffer(arena, allocation_size);
74+
void *ptr = get_last_buffer(arena).buffer;
75+
append_new_buffer(arena, arena->common_max_allocation_size);
76+
return ptr;
77+
} else if (arena->common_max_allocation_size - last_buffer.used < allocation_size) {
78+
append_new_buffer(arena, arena->common_max_allocation_size);
79+
last_buffer = get_last_buffer(arena);
80+
}
81+
82+
void *ptr = &last_buffer.buffer[last_buffer.used];
83+
last_buffer.used += allocation_size;
84+
85+
return ptr;
86+
}
87+
88+
void *vut_arena_calloc(struct vut_arena *arena, size_t bytes, size_t times) {
89+
if (bytes * times == 0)
90+
return NULL;
91+
92+
void *ptr = vut_arena_malloc(arena, bytes, times);
93+
memset(ptr, 0, bytes * times);
94+
return ptr;
95+
}
96+
97+
void *vut_arena_realloc(struct vut_arena *arena, void *old_ptr, size_t bytes, size_t times) {
98+
if (bytes * times == 0)
99+
return NULL;
100+
101+
void *ptr = vut_arena_malloc(arena, bytes, times);
102+
memcpy(ptr, old_ptr, bytes * times);
103+
104+
return ptr;
105+
}
106+
107+
void vut_arena_free_all(struct vut_arena *arena) {
108+
VUT_VEC_FOREACH(&arena->allocations, _, struct arena_buffer * buffer) {
109+
vut_allocator_free(arena->base_allocator, buffer->buffer);
110+
}
111+
VUT_VEC_FREE(&arena->allocations);
112+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#ifndef __VUT_ARENA_ALLOCATOR_H__
2+
#define __VUT_ARENA_ALLOCATOR_H__
3+
4+
#include <stddef.h>
5+
#include <stdint.h>
6+
7+
#include "allocator.h"
8+
#include "vec.h"
9+
10+
struct arena_buffer {
11+
uint8_t *buffer;
12+
size_t used;
13+
};
14+
15+
struct vut_arena_allocations VUT_VEC_DEF(struct arena_buffer);
16+
17+
struct vut_arena {
18+
struct vut_arena_allocations allocations;
19+
struct vut_allocator *base_allocator;
20+
size_t common_max_allocation_size;
21+
};
22+
23+
struct vut_arena vut_arena_new(struct vut_allocator *base_allocator,
24+
size_t common_max_allocation_size);
25+
struct vut_allocator vut_arena_to_vut_allocator(struct vut_arena *arena);
26+
27+
void *vut_arena_malloc(struct vut_arena *arena, size_t bytes, size_t times);
28+
void *vut_arena_calloc(struct vut_arena *arena, size_t bytes, size_t times);
29+
void *vut_arena_realloc(struct vut_arena *arena, void *old_ptr, size_t bytes, size_t times);
30+
void vut_arena_free_all(struct vut_arena *arena);
31+
32+
#endif // __VUT_ARENA_ALLOCATOR_H__

subprojects/vutils/meson.build

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
project('vutils', 'c', version: '0.1.0')
1+
project('vutils', 'c', version: '0.2.0')
22

33
local_inc = include_directories('include/vutils')
44
external_inc = include_directories('include')
@@ -10,6 +10,7 @@ install_subdir(
1010

1111
srcs = [
1212
'allocator.c',
13+
'arena_allocator.c',
1314
'system_allocator.c',
1415
'str.c',
1516
]

subprojects/vutils_tests/allocator.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <vunit/vunit.h>
33

44
#include <vutils/allocator.h>
5+
#include <vutils/arena_allocator.h>
56
#include <vutils/system_allocator.h>
67

78
#define ARRRAY_LEN(arr) (sizeof((arr))) / (sizeof((arr)[0]))
@@ -21,10 +22,27 @@ static struct vut_allocator gen_sys_allocator() {
2122
return *vut_get_system_allocator();
2223
}
2324

25+
static struct vut_allocator gen_arena_allocator() {
26+
struct vut_arena *arena =
27+
vut_allocator_malloc(vut_get_system_allocator(), sizeof(*arena), 1);
28+
*arena = vut_arena_new(vut_get_system_allocator(), DEFAULT_ALLOC_SIZE * DEFAULT_ALLOC_SIZE);
29+
return vut_arena_to_vut_allocator(arena);
30+
}
31+
32+
static void destroy_arena_allocator(struct vut_allocator allocator) {
33+
struct vut_arena *arena = allocator.base_allocator;
34+
vut_arena_free_all(arena);
35+
vut_allocator_free(vut_get_system_allocator(), arena);
36+
}
37+
2438
static struct allocator_case allocator_cases[] = {
2539
{
2640
.gen_allocator = gen_sys_allocator,
2741
},
42+
{
43+
.gen_allocator = gen_arena_allocator,
44+
.destroy_allocator = destroy_arena_allocator,
45+
},
2846
};
2947

3048
static void test_malloc_simple(struct vunit_test_ctx *ctx) {

0 commit comments

Comments
 (0)