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