Skip to content

Commit 1ce90c7

Browse files
authored
C: Implement realloc for hb_allocator_T (#1322)
This pull request adds `realloc` to the `hb_allocator_T` interface and implements all backend-specific `realloc` functions. Currently, our `hb_array_T` and `hb_buffer_T` implementations use `realloc` for growing beyond their current capacity. In order to also migrate `hb_array` and `hb_buffer` to the allocator infrastructure we need a way to also support `realloc` in the interface.
1 parent 997835c commit 1ce90c7

File tree

4 files changed

+190
-0
lines changed

4 files changed

+190
-0
lines changed

src/include/util/hb_allocator.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ typedef struct {
3737

3838
typedef struct hb_allocator {
3939
void* (*alloc)(struct hb_allocator* self, size_t size);
40+
void* (*realloc)(struct hb_allocator* self, void* pointer, size_t old_size, size_t new_size);
4041
void (*dealloc)(struct hb_allocator* self, void* pointer);
4142
char* (*strdup)(struct hb_allocator* self, const char* string);
4243
char* (*strndup)(struct hb_allocator* self, const char* string, size_t length);
@@ -58,6 +59,10 @@ static inline void* hb_allocator_alloc(hb_allocator_T* allocator, size_t size) {
5859
return allocator->alloc(allocator, size);
5960
}
6061

62+
static inline void* hb_allocator_realloc(hb_allocator_T* allocator, void* pointer, size_t old_size, size_t new_size) {
63+
return allocator->realloc(allocator, pointer, old_size, new_size);
64+
}
65+
6166
static inline void hb_allocator_dealloc(hb_allocator_T* allocator, void* pointer) {
6267
allocator->dealloc(allocator, pointer);
6368
}

src/util/hb_allocator.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,17 @@ static char* malloc_strndup(hb_allocator_T* _self, const char* string, size_t le
3939
return copy;
4040
}
4141

42+
static void* malloc_realloc(hb_allocator_T* _self, void* pointer, size_t _old_size, size_t new_size) {
43+
return realloc(pointer, new_size);
44+
}
45+
4246
static void malloc_destroy(hb_allocator_T* _self) {
4347
}
4448

4549
hb_allocator_T hb_allocator_with_malloc(void) {
4650
return (hb_allocator_T) {
4751
.alloc = malloc_alloc,
52+
.realloc = malloc_realloc,
4853
.dealloc = malloc_dealloc,
4954
.strdup = malloc_strdup,
5055
.strndup = malloc_strndup,
@@ -59,6 +64,18 @@ static void* arena_alloc(hb_allocator_T* self, size_t size) {
5964
return hb_arena_alloc((hb_arena_T*) self->context, size);
6065
}
6166

67+
static void* arena_realloc(hb_allocator_T* self, void* pointer, size_t old_size, size_t new_size) {
68+
if (!pointer) { return arena_alloc(self, new_size); }
69+
70+
void* new_pointer = hb_arena_alloc((hb_arena_T*) self->context, new_size);
71+
if (!new_pointer) { return NULL; }
72+
73+
size_t copy_size = old_size < new_size ? old_size : new_size;
74+
memcpy(new_pointer, pointer, copy_size);
75+
76+
return new_pointer;
77+
}
78+
6279
static void arena_dealloc(hb_allocator_T* _self, void* _pointer) {
6380
}
6481

@@ -96,6 +113,7 @@ static void arena_destroy(hb_allocator_T* self) {
96113
hb_allocator_T hb_allocator_with_arena(hb_arena_T* arena) {
97114
return (hb_allocator_T) {
98115
.alloc = arena_alloc,
116+
.realloc = arena_realloc,
99117
.dealloc = arena_dealloc,
100118
.strdup = arena_strdup,
101119
.strndup = arena_strndup,
@@ -207,6 +225,18 @@ static void* tracking_alloc(hb_allocator_T* self, size_t size) {
207225
return pointer;
208226
}
209227

228+
static void* tracking_realloc(hb_allocator_T* self, void* pointer, size_t _old_size, size_t new_size) {
229+
hb_allocator_tracking_stats_T* stats = (hb_allocator_tracking_stats_T*) self->context;
230+
231+
if (pointer) { tracking_unrecord(stats, pointer); }
232+
233+
void* new_pointer = realloc(pointer, new_size);
234+
235+
if (new_pointer) { tracking_record(stats, new_pointer, new_size); }
236+
237+
return new_pointer;
238+
}
239+
210240
static void tracking_dealloc(hb_allocator_T* self, void* pointer) {
211241
hb_allocator_tracking_stats_T* stats = (hb_allocator_tracking_stats_T*) self->context;
212242
tracking_unrecord(stats, pointer);
@@ -256,6 +286,7 @@ hb_allocator_T hb_allocator_with_tracking(void) {
256286

257287
return (hb_allocator_T) {
258288
.alloc = tracking_alloc,
289+
.realloc = tracking_realloc,
259290
.dealloc = tracking_dealloc,
260291
.strdup = tracking_strdup,
261292
.strndup = tracking_strndup,

test/c/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <check.h>
22
#include <stdlib.h>
33

4+
TCase *hb_allocator_realloc_tests(void);
45
TCase *hb_arena_tests(void);
56
TCase *hb_array_tests(void);
67
TCase *hb_narray_tests(void);
@@ -17,6 +18,7 @@ TCase *extract_tests(void);
1718
Suite *herb_suite(void) {
1819
Suite *suite = suite_create("Herb Suite");
1920

21+
suite_add_tcase(suite, hb_allocator_realloc_tests());
2022
suite_add_tcase(suite, hb_arena_tests());
2123
suite_add_tcase(suite, hb_array_tests());
2224
suite_add_tcase(suite, hb_narray_tests());

test/c/test_hb_allocator_realloc.c

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#include "include/test.h"
2+
#include "../../src/include/util/hb_allocator.h"
3+
4+
#include <string.h>
5+
6+
TEST(test_malloc_realloc_null_pointer)
7+
hb_allocator_T allocator = hb_allocator_with_malloc();
8+
9+
void* pointer = hb_allocator_realloc(&allocator, NULL, 0, 64);
10+
ck_assert_ptr_nonnull(pointer);
11+
12+
hb_allocator_dealloc(&allocator, pointer);
13+
END
14+
15+
TEST(test_malloc_realloc_grow)
16+
hb_allocator_T allocator = hb_allocator_with_malloc();
17+
18+
char* pointer = hb_allocator_alloc(&allocator, 16);
19+
ck_assert_ptr_nonnull(pointer);
20+
memcpy(pointer, "hello, world!!!", 16);
21+
22+
char* new_pointer = hb_allocator_realloc(&allocator, pointer, 16, 64);
23+
ck_assert_ptr_nonnull(new_pointer);
24+
ck_assert_int_eq(memcmp(new_pointer, "hello, world!!!", 16), 0);
25+
26+
hb_allocator_dealloc(&allocator, new_pointer);
27+
END
28+
29+
TEST(test_malloc_realloc_shrink)
30+
hb_allocator_T allocator = hb_allocator_with_malloc();
31+
32+
char* pointer = hb_allocator_alloc(&allocator, 64);
33+
ck_assert_ptr_nonnull(pointer);
34+
memcpy(pointer, "shrink me", 10);
35+
36+
char* new_pointer = hb_allocator_realloc(&allocator, pointer, 64, 16);
37+
ck_assert_ptr_nonnull(new_pointer);
38+
ck_assert_int_eq(memcmp(new_pointer, "shrink me", 10), 0);
39+
40+
hb_allocator_dealloc(&allocator, new_pointer);
41+
END
42+
43+
TEST(test_arena_realloc_null_pointer)
44+
hb_allocator_T allocator;
45+
hb_allocator_init(&allocator, HB_ALLOCATOR_ARENA);
46+
47+
void* pointer = hb_allocator_realloc(&allocator, NULL, 0, 64);
48+
ck_assert_ptr_nonnull(pointer);
49+
50+
hb_allocator_destroy(&allocator);
51+
END
52+
53+
TEST(test_arena_realloc_grow)
54+
hb_allocator_T allocator;
55+
hb_allocator_init(&allocator, HB_ALLOCATOR_ARENA);
56+
57+
char* pointer = hb_allocator_alloc(&allocator, 16);
58+
ck_assert_ptr_nonnull(pointer);
59+
memcpy(pointer, "hello, world!!!", 16);
60+
61+
char* new_pointer = hb_allocator_realloc(&allocator, pointer, 16, 64);
62+
ck_assert_ptr_nonnull(new_pointer);
63+
ck_assert_ptr_ne(new_pointer, pointer);
64+
ck_assert_int_eq(memcmp(new_pointer, "hello, world!!!", 16), 0);
65+
66+
hb_allocator_destroy(&allocator);
67+
END
68+
69+
TEST(test_arena_realloc_shrink)
70+
hb_allocator_T allocator;
71+
hb_allocator_init(&allocator, HB_ALLOCATOR_ARENA);
72+
73+
char* pointer = hb_allocator_alloc(&allocator, 64);
74+
ck_assert_ptr_nonnull(pointer);
75+
memcpy(pointer, "shrink me", 10);
76+
77+
char* new_pointer = hb_allocator_realloc(&allocator, pointer, 64, 16);
78+
ck_assert_ptr_nonnull(new_pointer);
79+
ck_assert_int_eq(memcmp(new_pointer, "shrink me", 10), 0);
80+
81+
hb_allocator_destroy(&allocator);
82+
END
83+
84+
TEST(test_arena_realloc_preserves_data_across_pages)
85+
hb_allocator_T allocator;
86+
hb_allocator_init_with_size(&allocator, HB_ALLOCATOR_ARENA, 64);
87+
88+
char* pointer = hb_allocator_alloc(&allocator, 32);
89+
ck_assert_ptr_nonnull(pointer);
90+
memset(pointer, 'A', 32);
91+
92+
char* new_pointer = hb_allocator_realloc(&allocator, pointer, 32, 128);
93+
ck_assert_ptr_nonnull(new_pointer);
94+
95+
for (size_t i = 0; i < 32; i++) {
96+
ck_assert_int_eq(new_pointer[i], 'A');
97+
}
98+
99+
hb_allocator_destroy(&allocator);
100+
END
101+
102+
TEST(test_tracking_realloc_null_pointer)
103+
hb_allocator_T allocator = hb_allocator_with_tracking();
104+
105+
void* pointer = hb_allocator_realloc(&allocator, NULL, 0, 64);
106+
ck_assert_ptr_nonnull(pointer);
107+
108+
hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
109+
ck_assert_int_eq(stats->allocation_count, 1);
110+
ck_assert_int_eq(stats->bytes_allocated, 64);
111+
112+
hb_allocator_dealloc(&allocator, pointer);
113+
hb_allocator_destroy(&allocator);
114+
END
115+
116+
TEST(test_tracking_realloc_grow)
117+
hb_allocator_T allocator = hb_allocator_with_tracking();
118+
119+
char* pointer = hb_allocator_alloc(&allocator, 16);
120+
memcpy(pointer, "hello, world!!!", 16);
121+
122+
char* new_pointer = hb_allocator_realloc(&allocator, pointer, 16, 64);
123+
ck_assert_ptr_nonnull(new_pointer);
124+
ck_assert_int_eq(memcmp(new_pointer, "hello, world!!!", 16), 0);
125+
126+
hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
127+
ck_assert_int_eq(stats->allocation_count, 2);
128+
ck_assert_int_eq(stats->deallocation_count, 1);
129+
ck_assert_int_eq(stats->bytes_allocated, 16 + 64);
130+
ck_assert_int_eq(stats->bytes_deallocated, 16);
131+
132+
hb_allocator_dealloc(&allocator, new_pointer);
133+
hb_allocator_destroy(&allocator);
134+
END
135+
136+
TCase *hb_allocator_realloc_tests(void) {
137+
TCase *allocator = tcase_create("Herb Allocator");
138+
139+
tcase_add_test(allocator, test_malloc_realloc_null_pointer);
140+
tcase_add_test(allocator, test_malloc_realloc_grow);
141+
tcase_add_test(allocator, test_malloc_realloc_shrink);
142+
143+
tcase_add_test(allocator, test_arena_realloc_null_pointer);
144+
tcase_add_test(allocator, test_arena_realloc_grow);
145+
tcase_add_test(allocator, test_arena_realloc_shrink);
146+
tcase_add_test(allocator, test_arena_realloc_preserves_data_across_pages);
147+
148+
tcase_add_test(allocator, test_tracking_realloc_null_pointer);
149+
tcase_add_test(allocator, test_tracking_realloc_grow);
150+
151+
return allocator;
152+
}

0 commit comments

Comments
 (0)