aboutsummaryrefslogtreecommitdiff
path: root/mem
diff options
context:
space:
mode:
Diffstat (limited to 'mem')
-rw-r--r--mem/include/mem.h4
-rw-r--r--mem/src/mem.c30
-rw-r--r--mem/test/mem_test.c36
3 files changed, 41 insertions, 29 deletions
diff --git a/mem/include/mem.h b/mem/include/mem.h
index 224b069..3050ba8 100644
--- a/mem/include/mem.h
+++ b/mem/include/mem.h
@@ -68,13 +68,13 @@
68/// Allocate a new chunk of N blocks. 68/// Allocate a new chunk of N blocks.
69/// Return a pointer to the first block of the chunk. 69/// Return a pointer to the first block of the chunk.
70/// When there is no space left in the allocator, allocation can either trap 70/// When there is no space left in the allocator, allocation can either trap
71/// (default) or gracefully return 0. Call mem_enable_traps() to toggle this 71/// (default) or gracefully return null. Call mem_enable_traps() to toggle this
72/// behaviour. 72/// behaviour.
73/// New chunks are conveniently zeroed out. 73/// New chunks are conveniently zeroed out.
74#define mem_alloc(MEM, num_blocks) mem_alloc_(&(MEM)->mem, num_blocks) 74#define mem_alloc(MEM, num_blocks) mem_alloc_(&(MEM)->mem, num_blocks)
75 75
76/// Free the chunk. 76/// Free the chunk.
77/// The chunk pointer is conveniently set to 0. 77/// The chunk pointer is conveniently set to null.
78#define mem_free(MEM, CHUNK) mem_free_(&(MEM)->mem, (void**)CHUNK) 78#define mem_free(MEM, CHUNK) mem_free_(&(MEM)->mem, (void**)CHUNK)
79 79
80/// Return a pointer to a chunk given the chunk's handle. 80/// Return a pointer to a chunk given the chunk's handle.
diff --git a/mem/src/mem.c b/mem/src/mem.c
index 4f5e5ef..70648c9 100644
--- a/mem/src/mem.c
+++ b/mem/src/mem.c
@@ -46,24 +46,27 @@ void mem_del_(Memory* mem) {
46 if (mem->dynamic) { 46 if (mem->dynamic) {
47 if (mem->chunks) { 47 if (mem->chunks) {
48 free(mem->chunks); 48 free(mem->chunks);
49 mem->chunks = 0; 49 mem->chunks = nullptr;
50 } 50 }
51 if (mem->blocks) { 51 if (mem->blocks) {
52 free(mem->blocks); 52 free(mem->blocks);
53 mem->blocks = 0; 53 mem->blocks = nullptr;
54 } 54 }
55 } 55 }
56} 56}
57 57
58void mem_clear_(Memory* mem) { 58void mem_clear_(Memory* mem) {
59 assert(mem); 59 assert(mem);
60 mem->next_free_chunk = 0; 60 if (mem->num_blocks > 0) {
61 memset(mem->blocks, 0, mem->num_blocks * mem->block_size_bytes); 61 mem->num_used_blocks = 0;
62 memset(mem->chunks, 0, mem->num_blocks * sizeof(Chunk)); 62 mem->next_free_chunk = 0;
63 63 memset(mem->blocks, 0, mem->num_blocks * mem->block_size_bytes);
64 // Initialize the head as one large free chunk. 64 memset(mem->chunks, 0, mem->num_blocks * sizeof(Chunk));
65 Chunk* head = &mem->chunks[0]; 65
66 head->num_blocks = mem->num_blocks; 66 // Initialize the head as one large free chunk.
67 Chunk* head = &mem->chunks[0];
68 head->num_blocks = mem->num_blocks;
69 }
67} 70}
68 71
69void* mem_alloc_(Memory* mem, size_t num_blocks) { 72void* mem_alloc_(Memory* mem, size_t num_blocks) {
@@ -113,10 +116,11 @@ void* mem_alloc_(Memory* mem, size_t num_blocks) {
113 return &mem->blocks[chunk_idx * mem->block_size_bytes]; 116 return &mem->blocks[chunk_idx * mem->block_size_bytes];
114 } else { 117 } else {
115 if (mem->trap) { 118 if (mem->trap) {
116 FAIL("Memory allocation failed, increase the allocator's capacity or " 119 FAIL(
117 "avoid fragmentation."); 120 "Memory allocation failed, increase the allocator's capacity or "
121 "avoid fragmentation.");
118 } 122 }
119 return 0; // Large-enough free chunk not found. 123 return nullptr; // Large-enough free chunk not found.
120 } 124 }
121} 125}
122 126
@@ -172,7 +176,7 @@ void mem_free_(Memory* mem, void** chunk_ptr) {
172 176
173 mem->num_used_blocks--; 177 mem->num_used_blocks--;
174 178
175 *chunk_ptr = 0; 179 *chunk_ptr = nullptr;
176} 180}
177 181
178// The handle is the chunk's index. We don't call it an index in the public API 182// The handle is the chunk's index. We don't call it an index in the public API
diff --git a/mem/test/mem_test.c b/mem/test/mem_test.c
index 14718a5..52ce5a9 100644
--- a/mem/test/mem_test.c
+++ b/mem/test/mem_test.c
@@ -32,6 +32,12 @@ TEST_CASE(mem_create_dyn) {
32 mem_make_dyn(&mem, NUM_BLOCKS, sizeof(int)); 32 mem_make_dyn(&mem, NUM_BLOCKS, sizeof(int));
33} 33}
34 34
35// Clear an uninitialized allocator.
36TEST_CASE(mem_clear_uninitialized) {
37 test_mem mem = {0};
38 mem_clear(&mem);
39}
40
35// Allocate N chunks of 1 block each. 41// Allocate N chunks of 1 block each.
36TEST_CASE(mem_fully_allocate) { 42TEST_CASE(mem_fully_allocate) {
37 test_mem mem; 43 test_mem mem;
@@ -39,7 +45,7 @@ TEST_CASE(mem_fully_allocate) {
39 45
40 for (int i = 0; i < NUM_BLOCKS; ++i) { 46 for (int i = 0; i < NUM_BLOCKS; ++i) {
41 const int* block = mem_alloc(&mem, 1); 47 const int* block = mem_alloc(&mem, 1);
42 TEST_TRUE(block != 0); 48 TEST_TRUE(block != nullptr);
43 } 49 }
44 50
45 TEST_TRUE(mem_size(&mem) == NUM_BLOCKS); 51 TEST_TRUE(mem_size(&mem) == NUM_BLOCKS);
@@ -50,15 +56,15 @@ TEST_CASE(mem_fill_then_free) {
50 test_mem mem; 56 test_mem mem;
51 mem_make(&mem); 57 mem_make(&mem);
52 58
53 int* blocks[NUM_BLOCKS] = {0}; 59 int* blocks[NUM_BLOCKS] = {nullptr};
54 for (int i = 0; i < NUM_BLOCKS; i++) { 60 for (int i = 0; i < NUM_BLOCKS; i++) {
55 blocks[i] = mem_alloc(&mem, 1); 61 blocks[i] = mem_alloc(&mem, 1);
56 TEST_TRUE(blocks[i] != 0); 62 TEST_TRUE(blocks[i] != nullptr);
57 } 63 }
58 64
59 for (int i = 0; i < NUM_BLOCKS; i++) { 65 for (int i = 0; i < NUM_BLOCKS; i++) {
60 mem_free(&mem, &blocks[i]); 66 mem_free(&mem, &blocks[i]);
61 TEST_EQUAL(blocks[i], 0); // Pointer should be set to 0 on free. 67 TEST_EQUAL(blocks[i], nullptr); // Pointer should be set to 0 on free.
62 } 68 }
63 69
64 TEST_EQUAL(count(&mem), 0); 70 TEST_EQUAL(count(&mem), 0);
@@ -74,12 +80,12 @@ TEST_CASE(mem_allocate_beyond_max_size) {
74 80
75 // Fully allocate the mem. 81 // Fully allocate the mem.
76 for (int i = 0; i < NUM_BLOCKS; ++i) { 82 for (int i = 0; i < NUM_BLOCKS; ++i) {
77 TEST_TRUE(mem_alloc(&mem, 1) != 0); 83 TEST_TRUE(mem_alloc(&mem, 1) != nullptr);
78 } 84 }
79 85
80 // Past the end. 86 // Past the end.
81 for (int i = 0; i < NUM_BLOCKS; ++i) { 87 for (int i = 0; i < NUM_BLOCKS; ++i) {
82 TEST_EQUAL(mem_alloc(&mem, 1), 0); 88 TEST_EQUAL(mem_alloc(&mem, 1), nullptr);
83 } 89 }
84 90
85 TEST_TRUE(mem_size(&mem) == NUM_BLOCKS); 91 TEST_TRUE(mem_size(&mem) == NUM_BLOCKS);
@@ -105,7 +111,7 @@ TEST_CASE(mem_zero_free_block_after_free) {
105 mem_make(&mem); 111 mem_make(&mem);
106 112
107 int* val = mem_alloc(&mem, 1); 113 int* val = mem_alloc(&mem, 1);
108 TEST_TRUE(val != 0); 114 TEST_TRUE(val != nullptr);
109 *val = 177; 115 *val = 177;
110 116
111 int* old_val = val; 117 int* old_val = val;
@@ -131,7 +137,7 @@ TEST_CASE(mem_traverse_partially_full) {
131 137
132 for (int i = 0; i < N; ++i) { 138 for (int i = 0; i < N; ++i) {
133 int* val = mem_alloc(&mem, 1); 139 int* val = mem_alloc(&mem, 1);
134 TEST_TRUE(val != 0); 140 TEST_TRUE(val != nullptr);
135 *val = i + 1; 141 *val = i + 1;
136 } 142 }
137 143
@@ -146,7 +152,7 @@ TEST_CASE(mem_traverse_full) {
146 152
147 for (int i = 0; i < NUM_BLOCKS; ++i) { 153 for (int i = 0; i < NUM_BLOCKS; ++i) {
148 int* val = mem_alloc(&mem, 1); 154 int* val = mem_alloc(&mem, 1);
149 TEST_TRUE(val != 0); 155 TEST_TRUE(val != nullptr);
150 *val = i + 1; 156 *val = i + 1;
151 } 157 }
152 158
@@ -161,7 +167,7 @@ TEST_CASE(mem_get_block) {
161 167
162 for (int i = 0; i < NUM_BLOCKS; ++i) { 168 for (int i = 0; i < NUM_BLOCKS; ++i) {
163 int* block = mem_alloc(&mem, 1); 169 int* block = mem_alloc(&mem, 1);
164 TEST_TRUE(block != 0); 170 TEST_TRUE(block != nullptr);
165 *block = i; 171 *block = i;
166 TEST_EQUAL(mem_get_chunk_handle(&mem, block), (size_t)i); 172 TEST_EQUAL(mem_get_chunk_handle(&mem, block), (size_t)i);
167 } 173 }
@@ -179,7 +185,7 @@ TEST_CASE(mem_fragmentation) {
179 test_mem mem; 185 test_mem mem;
180 mem_make(&mem); 186 mem_make(&mem);
181 187
182 int* blocks[NUM_BLOCKS] = {0}; 188 int* blocks[NUM_BLOCKS] = {nullptr};
183 int next_block = 0; 189 int next_block = 0;
184 190
185#define ALLOC(num_blocks) \ 191#define ALLOC(num_blocks) \
@@ -205,7 +211,7 @@ TEST_CASE(mem_fragmentation) {
205 211
206 // Should be able to allocate 1 chunk of N blocks. 212 // Should be able to allocate 1 chunk of N blocks.
207 const void* chunk = mem_alloc(&mem, NUM_BLOCKS); 213 const void* chunk = mem_alloc(&mem, NUM_BLOCKS);
208 TEST_TRUE(chunk != 0); 214 TEST_TRUE(chunk != nullptr);
209} 215}
210 216
211// Clear and re-use an allocator. 217// Clear and re-use an allocator.
@@ -216,15 +222,17 @@ TEST_CASE(mem_clear_then_reuse) {
216 // Allocate chunks, contents not important. 222 // Allocate chunks, contents not important.
217 for (int i = 0; i < NUM_BLOCKS; ++i) { 223 for (int i = 0; i < NUM_BLOCKS; ++i) {
218 int* chunk = mem_alloc(&mem, 1); 224 int* chunk = mem_alloc(&mem, 1);
219 TEST_TRUE(chunk != 0); 225 TEST_TRUE(chunk != nullptr);
220 } 226 }
221 227
222 mem_clear(&mem); 228 mem_clear(&mem);
229 TEST_EQUAL(mem_size(&mem), 0);
230 TEST_EQUAL(mem_capacity(&mem), NUM_BLOCKS);
223 231
224 // Allocate chunks and assign values 0..N. 232 // Allocate chunks and assign values 0..N.
225 for (int i = 0; i < NUM_BLOCKS; ++i) { 233 for (int i = 0; i < NUM_BLOCKS; ++i) {
226 int* chunk = mem_alloc(&mem, 1); 234 int* chunk = mem_alloc(&mem, 1);
227 TEST_TRUE(chunk != 0); 235 TEST_TRUE(chunk != nullptr);
228 *chunk = i + 1; 236 *chunk = i + 1;
229 } 237 }
230 238