main
1#ifndef KC_HEADER_MEMORY_ARENAS_INCLUDED
2#define KC_HEADER_MEMORY_ARENAS_INCLUDED
3
4
5#include "../types/types.h"
6#include "../platform/assert.h"
7#include "../platform/memory.h"
8#include "./ops.h"
9#include "../macros/util.h"
10
11#define MEM_ARENA_ALIGNMENT (sizeof(void*))
12
13typedef struct _Arena {
14 u8* start;
15 u8* end;
16 u8* top;
17 u8* last_commited;
18} Arena;
19
20
21status Arena_create( Arena *arena, u64 virtual_size );
22status Arena_destroy( Arena *arena );
23
24Arena Arena_from( Arena *arena ); /* Get a sub arena, with a new arena "frame" */
25
26status Arena_take_amount( Arena *arena, u64 size, u8** out );
27#define Arena_take( arena, type, count, out ) Arena_take_amount( arena, (sizeof(type))*(count), out )
28status Arena_give_amount( Arena *arena, u64 size );
29#define Arena_give( arena, type, count ) Arena_give_amount( arena, (sizeof(type))*(count) )
30
31void Arena_clear( Arena *arena );
32status Arena_uncommit_unused( Arena *arena );
33
34
35#ifdef KC_IMPLEMENT
36
37 #define ASSERT_MEM_ARENA_IS_SANE(arena) \
38 assert(arena); \
39 assert(arena->start); \
40 assert(arena->start <= arena->top); \
41 assert(arena->top <= arena->end); \
42
43
44 status Arena_create( Arena *arena, u64 virtual_size ) {
45 status ret = STATUS_SUCCESS;
46
47 PROPAGATE_ERROR(kc_platform_mem_va_reserve( (void**)&arena->start, virtual_size ));
48
49 arena->end = arena->start + virtual_size;
50 arena->top = arena->start;
51 arena->last_commited = arena->start;
52
53 return ret;
54 }
55
56 status Arena_destroy( Arena *arena ) {
57 ASSERT_MEM_ARENA_IS_SANE(arena);
58
59 PROPAGATE_ERROR(kc_platform_mem_va_release( arena->start, arena->end - arena->start ));
60 #if defined(KC_ZERO_MEMORY_LOOSE) || defined(KC_ZERO_MEMORY_STRICT)
61 mem_set( arena, sizeof(*arena), 0 );
62 #endif
63 return STATUS_SUCCESS;
64 }
65
66 Arena Arena_from( Arena *arena ) {
67 ASSERT_MEM_ARENA_IS_SANE(arena);
68 return (Arena) {
69 .start = arena->top,
70 .top = arena->top,
71 .end = arena->end,
72 .last_commited = arena->last_commited,
73 };
74 }
75
76 status Arena_take_amount( Arena *arena, u64 size, u8 **outptr ) {
77 ASSERT_MEM_ARENA_IS_SANE(arena);
78 assert(size);
79
80 if( size >= (u64)arena->end || arena->end - size < arena->start ) {
81 return ERROR_WOULD_OVERRUN;
82 }
83
84 u64 allocation = ROUND_UP_TO_A_POW2( (u64)arena->top, MEM_ARENA_ALIGNMENT );
85
86 u64 new_committed = ROUND_UP_TO_A_POW2(
87 allocation + size,
88 KC_PLATFORM_MEM_PAGESIZE
89 );
90
91 if( (u8*)new_committed > arena->end ) {
92 return ERROR_WOULD_OVERRUN;
93 }
94
95
96 if( new_committed > (u64)arena->last_commited ) {
97 PROPAGATE_ERROR(kc_platform_mem_va_commit( arena->last_commited, (u8*)new_committed - arena->last_commited ));
98 }
99
100 arena->last_commited = (void*)new_committed;
101 arena->top += (allocation - (u64)arena->top + size);
102
103 #if defined(KC_ZERO_MEMORY_STRICT)
104 mem_set_range( arena->top, arena->end, 0 );
105 #endif
106
107 *outptr = (void*)allocation;
108
109 return STATUS_SUCCESS;
110 }
111
112 status Arena_give_amount( Arena *arena, u64 size ) {
113 ASSERT_MEM_ARENA_IS_SANE(arena);
114 assert(size);
115
116 if( size > (u64)(arena->top - arena->start) ) {
117 return ERROR_WOULD_UNDERRUN;
118 }
119
120 u8* new_top = arena->top - size;
121 arena->top = new_top;
122
123 #if defined(KC_ZERO_MEMORY_STRICT)
124 mem_set_range( new_top, arena->top, 0 );
125 #endif
126
127 return STATUS_SUCCESS;
128 }
129
130 void Arena_clear( Arena *arena ) {
131 ASSERT_MEM_ARENA_IS_SANE(arena);
132
133 #if defined(KC_ZERO_MEMORY_STRICT)
134 mem_set_range( arena->start, arena->end, 0 );
135 #endif
136
137 arena->top = arena->start;
138 }
139
140 status Arena_uncommit_unused( Arena *arena ) {
141 ASSERT_MEM_ARENA_IS_SANE(arena);
142
143
144 u8* new_commited = (u8*)ROUND_UP_TO_A_POW2( (u64)arena->top, KC_PLATFORM_MEM_PAGESIZE );
145
146 #if defined(KC_ZERO_MEMORY_STRICT)
147 mem_set_range( new_commited, arena->last_commited, 0 );
148 #endif
149 PROPAGATE_ERROR( kc_platform_mem_va_uncommit( new_commited, arena->end - new_commited ) ); /* We have to get rid of it until the end, in case a subarena has also allocated more. */
150
151 arena->last_commited = new_commited;
152
153 return STATUS_SUCCESS;
154 }
155
156
157#endif
158
159#endif