diff options
| -rw-r--r-- | CMakeLists.txt | 1 | ||||
| -rw-r--r-- | cassert/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | cassert/include/cassert.h | 29 | ||||
| -rw-r--r-- | mem/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | mem/include/mem.h | 11 | ||||
| -rw-r--r-- | mem/src/mem.c | 12 | ||||
| -rw-r--r-- | mem/test/mem_test.c | 1 | ||||
| -rw-r--r-- | mempool/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | mempool/include/mempool.h | 9 | ||||
| -rw-r--r-- | mempool/src/mempool.c | 11 | ||||
| -rw-r--r-- | mempool/test/mempool_test.c | 1 |
11 files changed, 85 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 868268d..e2206ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.0) | |||
| 2 | 2 | ||
| 3 | project(clib) | 3 | project(clib) |
| 4 | 4 | ||
| 5 | add_subdirectory(cassert) | ||
| 5 | add_subdirectory(cstring) | 6 | add_subdirectory(cstring) |
| 6 | add_subdirectory(error) | 7 | add_subdirectory(error) |
| 7 | add_subdirectory(filesystem) | 8 | add_subdirectory(filesystem) |
diff --git a/cassert/CMakeLists.txt b/cassert/CMakeLists.txt new file mode 100644 index 0000000..855f261 --- /dev/null +++ b/cassert/CMakeLists.txt | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | cmake_minimum_required(VERSION 3.0) | ||
| 2 | |||
| 3 | project(cassert) | ||
| 4 | |||
| 5 | add_library(cassert INTERFACE) | ||
| 6 | |||
| 7 | target_include_directories(cassert INTERFACE | ||
| 8 | include) | ||
diff --git a/cassert/include/cassert.h b/cassert/include/cassert.h new file mode 100644 index 0000000..3349b55 --- /dev/null +++ b/cassert/include/cassert.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <assert.h> // For convenience, bring in soft assertions with assert(). | ||
| 4 | #include <signal.h> | ||
| 5 | |||
| 6 | // Allow the client to define their own LOGE() macro. | ||
| 7 | #ifndef LOGE | ||
| 8 | #include <stdio.h> | ||
| 9 | #define LOGE(...) \ | ||
| 10 | { \ | ||
| 11 | fprintf(stderr, "[ASSERT] %s:%d: ", __FILE__, __LINE__); \ | ||
| 12 | fprintf(stderr, __VA_ARGS__); \ | ||
| 13 | fprintf(stderr, "\n"); \ | ||
| 14 | } | ||
| 15 | #endif // LOGE | ||
| 16 | |||
| 17 | #define TRAP() raise(SIGTRAP) | ||
| 18 | |||
| 19 | /// Unconditional hard assert. | ||
| 20 | #define FAIL(message) \ | ||
| 21 | LOGE(message); \ | ||
| 22 | TRAP(); | ||
| 23 | |||
| 24 | /// Conditional hard assert. | ||
| 25 | #define ASSERT(condition) \ | ||
| 26 | if (!condition) { \ | ||
| 27 | LOGE("Assertion failed: " #condition) \ | ||
| 28 | TRAP(); \ | ||
| 29 | } | ||
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 | ||
| 13 | target_link_libraries(mem | 13 | target_link_libraries(mem |
| 14 | cassert | ||
| 14 | list) | 15 | list) |
| 15 | 16 | ||
| 16 | target_compile_options(mem PRIVATE -Wall -Wextra) | 17 | target_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); | |||
| 159 | void* mem_get_chunk_(const Memory*, size_t chunk_handle); | 165 | void* mem_get_chunk_(const Memory*, size_t chunk_handle); |
| 160 | size_t mem_get_chunk_handle_(const Memory*, const void* chunk); | 166 | size_t mem_get_chunk_handle_(const Memory*, const void* chunk); |
| 161 | size_t mem_capacity_(const Memory*); | 167 | size_t mem_capacity_(const Memory*); |
| 168 | void 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 | |||
| 197 | void 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) { | |||
| 67 | TEST_CASE(mem_allocate_beyond_max_size) { | 67 | TEST_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) { |
diff --git a/mempool/CMakeLists.txt b/mempool/CMakeLists.txt index fe3e2a5..8c9dd30 100644 --- a/mempool/CMakeLists.txt +++ b/mempool/CMakeLists.txt | |||
| @@ -10,6 +10,9 @@ add_library(mempool | |||
| 10 | target_include_directories(mempool PUBLIC | 10 | target_include_directories(mempool PUBLIC |
| 11 | include) | 11 | include) |
| 12 | 12 | ||
| 13 | target_link_libraries(mempool PRIVATE | ||
| 14 | cassert) | ||
| 15 | |||
| 13 | target_compile_options(mempool PRIVATE -Wall -Wextra) | 16 | target_compile_options(mempool PRIVATE -Wall -Wextra) |
| 14 | 17 | ||
| 15 | # Test | 18 | # Test |
diff --git a/mempool/include/mempool.h b/mempool/include/mempool.h index bd4d4dd..de9ea4f 100644 --- a/mempool/include/mempool.h +++ b/mempool/include/mempool.h | |||
| @@ -65,6 +65,9 @@ | |||
| 65 | 65 | ||
| 66 | /// Allocate a new block. | 66 | /// Allocate a new block. |
| 67 | /// Return 0 if there is no memory left. | 67 | /// Return 0 if there is no memory left. |
| 68 | /// When there is no space left in the pool, allocation can either trap | ||
| 69 | /// (default) or gracefully return 0. Call mem_enable_traps() to toggle this | ||
| 70 | /// behaviour. | ||
| 68 | /// New blocks are conveniently zeroed out. | 71 | /// New blocks are conveniently zeroed out. |
| 69 | #define mempool_alloc(POOL) mempool_alloc_(&(POOL)->pool) | 72 | #define mempool_alloc(POOL) mempool_alloc_(&(POOL)->pool) |
| 70 | 73 | ||
| @@ -86,6 +89,10 @@ | |||
| 86 | /// Return the total capacity of the mempool in bytes. | 89 | /// Return the total capacity of the mempool in bytes. |
| 87 | #define mempool_capacity(POOL) mempool_capacity_(&(POOL)->pool) | 90 | #define mempool_capacity(POOL) mempool_capacity_(&(POOL)->pool) |
| 88 | 91 | ||
| 92 | /// Set whether to trap when attempting to allocate beyond capacity. | ||
| 93 | #define mempool_enable_traps(POOL, enable) \ | ||
| 94 | mempool_enable_traps_(&(POOL)->pool, enable) | ||
| 95 | |||
| 89 | /// Iterate over the used blocks of the pool. | 96 | /// Iterate over the used blocks of the pool. |
| 90 | /// | 97 | /// |
| 91 | /// The caller can use 'i' as the index of the current block. | 98 | /// The caller can use 'i' as the index of the current block. |
| @@ -129,6 +136,7 @@ typedef struct mempool { | |||
| 129 | size_t head; /// Points to the first block in the free list. | 136 | size_t head; /// Points to the first block in the free list. |
| 130 | size_t used; /// Points to the first block in the used list. | 137 | size_t used; /// Points to the first block in the used list. |
| 131 | bool dynamic; /// True if blocks and info are dynamically-allocated. | 138 | bool dynamic; /// True if blocks and info are dynamically-allocated. |
| 139 | bool trap; /// Whether to trap when allocating beyond capacity. | ||
| 132 | BlockInfo* block_info; | 140 | BlockInfo* block_info; |
| 133 | uint8_t* blocks; | 141 | uint8_t* blocks; |
| 134 | } mempool; | 142 | } mempool; |
| @@ -154,3 +162,4 @@ void mempool_free_(mempool*, void** block_ptr); | |||
| 154 | void* mempool_get_block_(const mempool*, size_t block_index); | 162 | void* mempool_get_block_(const mempool*, size_t block_index); |
| 155 | size_t mempool_get_block_index_(const mempool*, const void* block); | 163 | size_t mempool_get_block_index_(const mempool*, const void* block); |
| 156 | size_t mempool_capacity_(const mempool*); | 164 | size_t mempool_capacity_(const mempool*); |
| 165 | void mempool_enable_traps_(mempool*, bool); | ||
diff --git a/mempool/src/mempool.c b/mempool/src/mempool.c index 1100dad..b09038b 100644 --- a/mempool/src/mempool.c +++ b/mempool/src/mempool.c | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | #include "mempool.h" | 1 | #include "mempool.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 | ||
| @@ -24,6 +26,7 @@ bool mempool_make_( | |||
| 24 | pool->num_blocks = num_blocks; | 26 | pool->num_blocks = num_blocks; |
| 25 | pool->head = 0; | 27 | pool->head = 0; |
| 26 | pool->used = 0; | 28 | pool->used = 0; |
| 29 | pool->trap = true; | ||
| 27 | 30 | ||
| 28 | // Initialize blocks and block info. | 31 | // Initialize blocks and block info. |
| 29 | if (!block_info) { | 32 | if (!block_info) { |
| @@ -74,6 +77,9 @@ void* mempool_alloc_(mempool* pool) { | |||
| 74 | 77 | ||
| 75 | BlockInfo* head = &pool->block_info[pool->head]; | 78 | BlockInfo* head = &pool->block_info[pool->head]; |
| 76 | if (head->used) { | 79 | if (head->used) { |
| 80 | if (pool->trap) { | ||
| 81 | FAIL("mempool allocation failed, increase the pool's capacity."); | ||
| 82 | } | ||
| 77 | return 0; // Pool is full. | 83 | return 0; // Pool is full. |
| 78 | } | 84 | } |
| 79 | 85 | ||
| @@ -134,3 +140,8 @@ size_t mempool_capacity_(const mempool* pool) { | |||
| 134 | assert(pool); | 140 | assert(pool); |
| 135 | return pool->num_blocks * pool->block_size_bytes; | 141 | return pool->num_blocks * pool->block_size_bytes; |
| 136 | } | 142 | } |
| 143 | |||
| 144 | void mempool_enable_traps_(mempool* pool, bool enable) { | ||
| 145 | assert(pool); | ||
| 146 | pool->trap = enable; | ||
| 147 | } | ||
diff --git a/mempool/test/mempool_test.c b/mempool/test/mempool_test.c index d5ed1ea..6c48a2a 100644 --- a/mempool/test/mempool_test.c +++ b/mempool/test/mempool_test.c | |||
| @@ -67,6 +67,7 @@ TEST_CASE(mempool_fill_then_free) { | |||
| 67 | TEST_CASE(mempool_allocate_beyond_max_size) { | 67 | TEST_CASE(mempool_allocate_beyond_max_size) { |
| 68 | test_pool pool; | 68 | test_pool pool; |
| 69 | mempool_make(&pool); | 69 | mempool_make(&pool); |
| 70 | mempool_enable_traps(&pool, false); | ||
| 70 | 71 | ||
| 71 | // Fully allocate the pool. | 72 | // Fully allocate the pool. |
| 72 | for (int i = 0; i < NUM_BLOCKS; ++i) { | 73 | for (int i = 0; i < NUM_BLOCKS; ++i) { |
