aboutsummaryrefslogtreecommitdiff
path: root/mem
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-02-13 17:51:51 -0800
committer3gg <3gg@shellblade.net>2024-02-13 17:51:51 -0800
commite153be0be2fb8df6656292daab3fa59963c76737 (patch)
tree7cca3fc134553017cb3c259db1dca33c98556109 /mem
parent84bdfa4a23f5b8daa7921541b007518bc634be0f (diff)
Let memory allocators trap by default when attempting to allocate beyond capacity.
Diffstat (limited to 'mem')
-rw-r--r--mem/CMakeLists.txt1
-rw-r--r--mem/include/mem.h11
-rw-r--r--mem/src/mem.c12
-rw-r--r--mem/test/mem_test.c1
4 files changed, 23 insertions, 2 deletions
diff --git a/mem/CMakeLists.txt b/mem/CMakeLists.txt
index 233d2be..e4b28c3 100644
--- a/mem/CMakeLists.txt
+++ b/mem/CMakeLists.txt
@@ -11,6 +11,7 @@ target_include_directories(mem PUBLIC
11 include) 11 include)
12 12
13target_link_libraries(mem 13target_link_libraries(mem
14 cassert
14 list) 15 list)
15 16
16target_compile_options(mem PRIVATE -Wall -Wextra) 17target_compile_options(mem PRIVATE -Wall -Wextra)
diff --git a/mem/include/mem.h b/mem/include/mem.h
index bcff39f..892ea4f 100644
--- a/mem/include/mem.h
+++ b/mem/include/mem.h
@@ -66,8 +66,10 @@
66#define mem_clear(MEM) mem_clear_(&(MEM)->mem) 66#define mem_clear(MEM) mem_clear_(&(MEM)->mem)
67 67
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, or 0 if there is no memory 69/// Return a pointer to the first block of the chunk.
70/// left. 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
72/// behaviour.
71/// New chunks are conveniently zeroed out. 73/// New chunks are conveniently zeroed out.
72#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)
73 75
@@ -87,6 +89,9 @@
87/// Return the total capacity of the allocator in bytes. 89/// Return the total capacity of the allocator in bytes.
88#define mem_capacity(MEM) mem_capacity_(&(MEM)->mem) 90#define mem_capacity(MEM) mem_capacity_(&(MEM)->mem)
89 91
92/// Set whether to trap when attempting to allocate beyond capacity.
93#define mem_enable_traps(MEM, enable) mem_enable_traps_(&(MEM)->mem, enable)
94
90/// Iterate over the used chunks of the allocator. 95/// Iterate over the used chunks of the allocator.
91/// 96///
92/// The caller can use 'i' as the index of the current chunk. 97/// The caller can use 'i' as the index of the current chunk.
@@ -134,6 +139,7 @@ typedef struct Memory {
134 size_t num_blocks; 139 size_t num_blocks;
135 size_t next_free_chunk; 140 size_t next_free_chunk;
136 bool dynamic; /// True if blocks and chunks are dynamically-allocated. 141 bool dynamic; /// True if blocks and chunks are dynamically-allocated.
142 bool trap; /// Whether to trap when allocating beyond capacity.
137 Chunk* chunks; /// Array of chunk information. 143 Chunk* chunks; /// Array of chunk information.
138 uint8_t* blocks; /// Array of blocks; 144 uint8_t* blocks; /// Array of blocks;
139} Memory; 145} Memory;
@@ -159,3 +165,4 @@ void mem_free_(Memory*, void** chunk_ptr);
159void* mem_get_chunk_(const Memory*, size_t chunk_handle); 165void* mem_get_chunk_(const Memory*, size_t chunk_handle);
160size_t mem_get_chunk_handle_(const Memory*, const void* chunk); 166size_t mem_get_chunk_handle_(const Memory*, const void* chunk);
161size_t mem_capacity_(const Memory*); 167size_t mem_capacity_(const Memory*);
168void mem_enable_traps_(Memory*, bool);
diff --git a/mem/src/mem.c b/mem/src/mem.c
index 056d947..2904035 100644
--- a/mem/src/mem.c
+++ b/mem/src/mem.c
@@ -1,5 +1,7 @@
1#include "mem.h" 1#include "mem.h"
2 2
3#include <cassert.h>
4
3#include <stdlib.h> 5#include <stdlib.h>
4#include <string.h> 6#include <string.h>
5 7
@@ -13,6 +15,7 @@ bool mem_make_(
13 mem->block_size_bytes = block_size_bytes; 15 mem->block_size_bytes = block_size_bytes;
14 mem->num_blocks = num_blocks; 16 mem->num_blocks = num_blocks;
15 mem->next_free_chunk = 0; 17 mem->next_free_chunk = 0;
18 mem->trap = true;
16 19
17 // Allocate chunks and blocks if necessary and zero them out. 20 // Allocate chunks and blocks if necessary and zero them out.
18 if (!chunks) { 21 if (!chunks) {
@@ -107,6 +110,10 @@ void* mem_alloc_(Memory* mem, size_t num_blocks) {
107 mem->next_free_chunk = mem->chunks[chunk_idx].next; 110 mem->next_free_chunk = mem->chunks[chunk_idx].next;
108 return &mem->blocks[chunk_idx * mem->block_size_bytes]; 111 return &mem->blocks[chunk_idx * mem->block_size_bytes];
109 } else { 112 } else {
113 if (mem->trap) {
114 FAIL("Memory allocation failed, increase the allocator's capacity or "
115 "avoid fragmentation.");
116 }
110 return 0; // Large-enough free chunk not found. 117 return 0; // Large-enough free chunk not found.
111 } 118 }
112} 119}
@@ -186,3 +193,8 @@ size_t mem_capacity_(const Memory* mem) {
186 assert(mem); 193 assert(mem);
187 return mem->num_blocks * mem->block_size_bytes; 194 return mem->num_blocks * mem->block_size_bytes;
188} 195}
196
197void mem_enable_traps_(Memory* mem, bool enable) {
198 assert(mem);
199 mem->trap = enable;
200}
diff --git a/mem/test/mem_test.c b/mem/test/mem_test.c
index 2f242c3..d3c43b9 100644
--- a/mem/test/mem_test.c
+++ b/mem/test/mem_test.c
@@ -67,6 +67,7 @@ TEST_CASE(mem_fill_then_free) {
67TEST_CASE(mem_allocate_beyond_max_size) { 67TEST_CASE(mem_allocate_beyond_max_size) {
68 test_mem mem; 68 test_mem mem;
69 mem_make(&mem); 69 mem_make(&mem);
70 mem_enable_traps(&mem, false);
70 71
71 // Fully allocate the mem. 72 // Fully allocate the mem.
72 for (int i = 0; i < NUM_BLOCKS; ++i) { 73 for (int i = 0; i < NUM_BLOCKS; ++i) {