aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-07-14 09:30:08 -0700
committer3gg <3gg@shellblade.net>2025-07-14 09:30:08 -0700
commitff565e8d422c5e58558d1f7682ba0c9756e5be27 (patch)
tree46b417ee95896ca11e6638624d8ff8b638d123f9
parent09166d46d6a30c1d431cc8371325d4fc8ae76162 (diff)
Add function to allocate aligned blocks on the memstackHEADmain
-rw-r--r--memstack/include/memstack.h10
-rw-r--r--memstack/src/memstack.c23
-rw-r--r--memstack/test/memstack_test.c27
3 files changed, 59 insertions, 1 deletions
diff --git a/memstack/include/memstack.h b/memstack/include/memstack.h
index 9a8a7ee..97a9d12 100644
--- a/memstack/include/memstack.h
+++ b/memstack/include/memstack.h
@@ -33,13 +33,23 @@ void memstack_del(memstack*);
33void memstack_clear(memstack*); 33void memstack_clear(memstack*);
34 34
35/// Allocate a new block. 35/// Allocate a new block.
36///
36/// Return null if the block does not fit in the remaining memory. 37/// Return null if the block does not fit in the remaining memory.
38///
37/// When there is no space left in the stack, allocation can either trap 39/// When there is no space left in the stack, allocation can either trap
38/// (default) or gracefully return null. Call mem_enable_traps() to toggle this 40/// (default) or gracefully return null. Call mem_enable_traps() to toggle this
39/// behaviour. 41/// behaviour.
42///
40/// Newly allocated blocks are conveniently zeroed out. 43/// Newly allocated blocks are conveniently zeroed out.
41void* memstack_alloc(memstack*, size_t bytes); 44void* memstack_alloc(memstack*, size_t bytes);
42 45
46/// Allocate a new aligned block.
47///
48/// An alignment of 0 is allowed for convenience and has the same effect as 1.
49///
50/// Has the same properties as memstack_alloc().
51void* memstack_alloc_aligned(memstack*, size_t bytes, size_t alignment);
52
43/// Return the stack's used size in bytes. 53/// Return the stack's used size in bytes.
44size_t memstack_size(const memstack*); 54size_t memstack_size(const memstack*);
45 55
diff --git a/memstack/src/memstack.c b/memstack/src/memstack.c
index 10d1e30..0848afb 100644
--- a/memstack/src/memstack.c
+++ b/memstack/src/memstack.c
@@ -5,6 +5,16 @@
5#include <stdlib.h> 5#include <stdlib.h>
6#include <string.h> 6#include <string.h>
7 7
8static bool is_pow2_or_0(size_t x) { return (x & (x - 1)) == 0; }
9
10/// Align the given address to the next address that is a multiple of the
11/// alignment. If the given address is already aligned, return the address.
12static uint8_t* align(uint8_t* address, size_t alignment) {
13 assert(is_pow2_or_0(alignment));
14 const size_t mask = alignment - 1;
15 return (uint8_t*)(((uintptr_t)address + mask) & ~mask);
16}
17
8bool memstack_make(memstack* stack, size_t capacity, void* memory) { 18bool memstack_make(memstack* stack, size_t capacity, void* memory) {
9 assert(stack); 19 assert(stack);
10 assert(capacity >= 1); 20 assert(capacity >= 1);
@@ -59,13 +69,24 @@ void* memstack_alloc(memstack* stack, size_t bytes) {
59 } 69 }
60 70
61 // Allocate the block. 71 // Allocate the block.
62 uint8_t* block = stack->watermark; 72 uint8_t* const block = stack->watermark;
63 stack->watermark += bytes; 73 stack->watermark += bytes;
64 assert(memstack_size(stack) <= stack->capacity); 74 assert(memstack_size(stack) <= stack->capacity);
65 75
66 return block; 76 return block;
67} 77}
68 78
79void* memstack_alloc_aligned(memstack* stack, size_t bytes, size_t alignment) {
80 assert(stack);
81
82 uint8_t* const new_watermark = align(stack->watermark, alignment);
83 assert(new_watermark >= stack->watermark);
84 assert((size_t)(new_watermark - stack->base) <= stack->capacity);
85 stack->watermark = new_watermark;
86
87 return memstack_alloc(stack, bytes);
88}
89
69size_t memstack_size(const memstack* stack) { 90size_t memstack_size(const memstack* stack) {
70 assert(stack); 91 assert(stack);
71 return stack->watermark - stack->base; 92 return stack->watermark - stack->base;
diff --git a/memstack/test/memstack_test.c b/memstack/test/memstack_test.c
index 5e9b493..285cf46 100644
--- a/memstack/test/memstack_test.c
+++ b/memstack/test/memstack_test.c
@@ -32,6 +32,8 @@ TEST_CASE(memstack_allocate_until_full) {
32 } 32 }
33 33
34 TEST_TRUE(memstack_size(&stack) == CAPACITY); 34 TEST_TRUE(memstack_size(&stack) == CAPACITY);
35
36 memstack_del(&stack);
35} 37}
36 38
37// Allocate all N ints, then free them. 39// Allocate all N ints, then free them.
@@ -48,6 +50,8 @@ TEST_CASE(memstack_fill_then_free) {
48 memstack_clear(&stack); 50 memstack_clear(&stack);
49 51
50 TEST_EQUAL(memstack_size(&stack), 0); 52 TEST_EQUAL(memstack_size(&stack), 0);
53
54 memstack_del(&stack);
51} 55}
52 56
53// Attempt to allocate blocks past the maximum stack size. 57// Attempt to allocate blocks past the maximum stack size.
@@ -68,6 +72,8 @@ TEST_CASE(memstack_allocate_beyond_max_size) {
68 } 72 }
69 73
70 TEST_TRUE(memstack_size(&stack) == CAPACITY); 74 TEST_TRUE(memstack_size(&stack) == CAPACITY);
75
76 memstack_del(&stack);
71} 77}
72 78
73// Free blocks should always remain zeroed out. 79// Free blocks should always remain zeroed out.
@@ -81,6 +87,8 @@ TEST_CASE(memstack_zero_free_blocks_after_creation) {
81 TEST_TRUE(block != nullptr); 87 TEST_TRUE(block != nullptr);
82 TEST_EQUAL(*block, 0); 88 TEST_EQUAL(*block, 0);
83 } 89 }
90
91 memstack_del(&stack);
84} 92}
85 93
86// Free blocks should always remain zeroed out. 94// Free blocks should always remain zeroed out.
@@ -102,6 +110,25 @@ TEST_CASE(memstack_zero_free_block_after_free) {
102 TEST_TRUE(block != nullptr); 110 TEST_TRUE(block != nullptr);
103 TEST_EQUAL(*block, 0); 111 TEST_EQUAL(*block, 0);
104 } 112 }
113
114 memstack_del(&stack);
115}
116
117// Aligned allocations should be properly aligned.
118TEST_CASE(memstack_alloc_aligned) {
119 memstack stack = {0};
120 memstack_make(&stack, CAPACITY, nullptr);
121
122 // -1 because the base address of the memory storage might be unaligned.
123 for (int i = 0; i < NUM_INTS - 1; ++i) {
124 const int* block =
125 memstack_alloc_aligned(&stack, sizeof(int), alignof(int));
126 TEST_TRUE(block != nullptr);
127 TEST_EQUAL(*block, 0);
128 TEST_EQUAL((uintptr_t)block % alignof(int), 0);
129 }
130
131 memstack_del(&stack);
105} 132}
106 133
107int main() { return 0; } 134int main() { return 0; }