Commit 138e2db
Changed files (18)
include/memory.h
@@ -0,0 +1,8 @@
+#ifndef KC_HEADER_MEMORY_INCLUDED
+#define KC_HEADER_MEMORY_INCLUDED
+
+#include "../memory/units.h"
+#include "../memory/arenas.h"
+#include "../memory/ops.h"
+
+#endif
include/types.h
@@ -0,0 +1,6 @@
+#ifndef KC_HEADER_TYPES_INCLUDED
+#define KC_HEADER_TYPES_INCLUDED
+
+#include "../types/array.h"
+
+#endif
macros/macros.h
@@ -0,0 +1,34 @@
+
+#ifndef KC_HEADER_MACROS_MACROS_INCLUDED
+#define KC_HEADER_MACROS_MACROS_INCLUDED
+
+#define EMPTY()
+
+#define DEFER( macro ) macro EMPTY()
+#define OBSTRUCT( ... ) __VA_ARGS__ DEFER( EMPTY )()
+#define EXPAND( ... ) __VA_ARGS__
+
+#define STRING_JOIN( str1, str2 ) str1##str2
+
+#define UNIQUE_NAME( name ) EXPAND( DEFER( STRING_JOIN )( name, __LINE__ ) )
+
+#define ASSERT( expression ) \
+ typedef char UNIQUE_NAME( compile_time_assert )[( expression ) ? 1 : -1]
+
+#define STRINGIZE( str ) #str
+#define STRINGIZE_MACRO( macro ) STRINGIZE( macro )
+
+#include "../platform/info.h"
+
+#if defined(KC_COMPILER_GCC) || defined(KC_COMPILER_CLANG) || defined(KC_COMPILER_TCC)
+ #define PACK( definition ) definition __attribute__( ( __packed__ ) )
+#elif defined(KC_COMPILER_MSVC)
+ #define PACK( DECL ) \
+ __pragma( pack( push, 1 ) ) \
+ DECL \
+ __pragma( pack( pop ) )
+#else
+ #error Unsupported compiler.
+#endif
+
+#endif
macros/util.h
@@ -0,0 +1,13 @@
+#ifndef KC_HEADER_UTIL_INCLUDED
+#define KC_HEADER_UTIL_INCLUDED
+
+#define MIN( a, b ) ( ((a) <= (b))? (a) : (b) )
+#define MAX( a, b ) ( ((a) >= (b))? (a) : (b) )
+#define ROUND_UP( num, multiple ) ((1 + ((num) - 1) / (multiple)) * (multiple) )
+#define ROUND_DOWN( num, multiple ) ((num / multiple) * multiple)
+
+/* http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 */
+#define UNSIGNED_IS_POWER_OF_TWO(v) ((v) && !((v)&((v) - 1)))
+
+
+#endif
memory/arenas.h
@@ -0,0 +1,137 @@
+#ifndef KC_HEADER_MEMORY_ARENAS_INCLUDED
+#define KC_HEADER_MEMORY_ARENAS_INCLUDED
+
+
+#include "../types/types.h"
+#include "../platform/assert.h"
+#include "../platform/memory.h"
+#include "./ops.h"
+#include "../macros/util.h"
+
+#define MEM_ARENA_ALIGNMENT (sizeof(void*))
+
+typedef struct _MemArena {
+ u8* start;
+ u8* end;
+ u8* top;
+ u8* last_commited;
+} MemArena;
+
+
+status MemArena_create( MemArena *arena, u64 virtual_size );
+status MemArena_destroy( MemArena *arena );
+
+MemArena MemArena_from( MemArena *arena ); /* Get a sub arena, with a new arena "frame" */
+
+status MemArena_take_amount( MemArena *arena, u64 size, void** out );
+#define MemArena_take( arena, type, count, out ) MemArena_take_amount( arena, (sizeof(type))*(count), out )
+
+void MemArena_clear( MemArena *arena );
+status MemArena_uncommit_unused( MemArena *arena );
+
+
+#ifdef KC_IMPLEMENT
+
+ #define ASSERT_MEM_ARENA_IS_SANE(arena) \
+ assert(arena); \
+ assert(arena->start); \
+ assert(arena->start <= arena->top); \
+ assert(arena->top <= arena->end); \
+
+
+ status MemArena_create( MemArena *arena, u64 virtual_size ) {
+ status ret = STATUS_SUCCESS;
+
+ PROPAGATE_ERROR(kc_platform_mem_va_reserve( (void**)&arena->start, virtual_size ));
+
+ arena->end = arena->start + virtual_size;
+ arena->top = arena->start;
+ arena->last_commited = arena->start;
+
+ return ret;
+ }
+
+ status MemArena_destroy( MemArena *arena ) {
+ ASSERT_MEM_ARENA_IS_SANE(arena);
+
+ PROPAGATE_ERROR(kc_platform_mem_va_release( arena->start, arena->end - arena->start ));
+ #if defined(KC_ZERO_MEMORY_LOOSE) || defined(KC_ZERO_MEMORY_STRICT)
+ mem_set( arena, sizeof(*arena), 0 );
+ #endif
+ return STATUS_SUCCESS;
+ }
+
+ MemArena MemArena_from( MemArena *arena ) {
+ ASSERT_MEM_ARENA_IS_SANE(arena);
+ return (MemArena) {
+ .start = arena->top,
+ .top = arena->top,
+ .end = arena->end,
+ .last_commited = arena->last_commited,
+ };
+ }
+
+ status MemArena_take_amount( MemArena *arena, u64 size, void **outptr ) {
+ ASSERT_MEM_ARENA_IS_SANE(arena);
+ assert(size);
+
+ if( size >= (u64)arena->end || arena->end - size < arena->start ) {
+ return ERROR_WOULD_OVERRUN;
+ }
+
+ u64 allocation = ROUND_UP_TO_A_POW2( (u64)arena->top, MEM_ARENA_ALIGNMENT );
+
+ u64 new_committed = ROUND_UP_TO_A_POW2(
+ allocation + size,
+ KC_PLATFORM_MEM_PAGESIZE
+ );
+
+ if( (u8*)new_committed > arena->end ) {
+ return ERROR_WOULD_OVERRUN;
+ }
+
+
+ if( new_committed > (u64)arena->last_commited ) {
+ PROPAGATE_ERROR(kc_platform_mem_va_commit( arena->last_commited, (u8*)new_committed - arena->last_commited ));
+ }
+
+ arena->last_commited = (void*)new_committed;
+
+ #if defined(KC_ZERO_MEMORY_STRICT)
+ mem_set( arena->top, arena->end - arena->top, 0 );
+ #endif
+
+ *outptr = (void*)allocation;
+
+ return STATUS_SUCCESS;
+ }
+
+ void MemArena_clear( MemArena *arena ) {
+ ASSERT_MEM_ARENA_IS_SANE(arena);
+
+ #if defined(KC_ZERO_MEMORY_STRICT)
+ mem_set( arena->start, arena->end - arena->start, 0 );
+ #endif
+
+ arena->top = arena->start;
+ }
+
+ status MemArena_uncommit_unused( MemArena *arena ) {
+ ASSERT_MEM_ARENA_IS_SANE(arena);
+
+ #if defined(KC_ZERO_MEMORY_STRICT)
+ mem_set( arena->top, arena->end - arena->start, 0 );
+ #endif
+
+ u64 new_last_commit = ROUND_UP_TO_A_POW2( arena->top, KC_PLATFORM_MEM_PAGESIZE );
+
+ PROPAGATE_ERROR( kc_platform_mem_va_uncommit( (void*)new_last_commit, (u64)arena->last_commited - new_last_commit) );
+ arena->last_commited = (u8*)new_last_commit;
+
+ return STATUS_SUCCESS;
+ }
+
+
+#endif
+
+#endif
memory/ops.h
@@ -0,0 +1,11 @@
+#ifndef KC_HEADER_MEMORY_OPS_INCLUDED
+#define KC_HEADER_MEMORY_OPS_INCLUDED
+
+#include "../types/types.h"
+
+status mem_copy( void *src, void *dst, u64 size );
+status mem_move( void *src, void *dst, u64 size );
+status mem_set( void *buf, u64 size, u8 value );
+
+
+#endif
memory/units.h
@@ -0,0 +1,10 @@
+#ifndef KC_HEADER_MEMORY_UNITS_INCLUDED
+#define KC_HEADER_MEMORY_UNITS_INCLUDED
+
+#include "../platform/types.h"
+
+#define KiB(n) ((u64)(n) << 10)
+#define MiB(n) ((u64)(n) << 20)
+#define GiB(n) ((u64)(n) << 30)
+
+#endif
platform/assert.h
@@ -0,0 +1,16 @@
+#ifndef KC_HEADER_PLATFORM_ASSERT_INCLUDED
+#define KC_HEADER_PLATFORM_ASSERT_INCLUDED
+
+#include "../platform/types.h"
+
+#if defined(KC_COMPILER_CLANG) || defined(KC_COMPILER_GCC) || defined(KC_COMPILER_TCC)
+ #define assert(condition) do{ if(!(condition)) __builtin_trap(); }while(0)
+#elif defined(KC_COMPILER_MSVC)
+ #include <intrin.h>
+
+ #define assert(condition) do{ if(!(condition)) __debugbreak(); }while(0);
+#else
+ #error Unsupported compiler.
+#endif
+
+#endif
platform/info.h
@@ -0,0 +1,61 @@
+#ifndef KC_HEADER_PLATFORM_INFO_INCLUDED
+#define KC_HEADER_PLATFORM_INFO_INCLUDED
+
+/* Operating system */
+
+#if defined(_WIN32) || defined(_WIN64)
+ #define KC_PLATFORM_WINDOWS 1
+
+ #if _WIN64
+ #define KC_PLATFORM_BITNESS 64
+ #define KC_PLATFORM_WIN64 1
+ #else
+ #define KC_PLATFORM_BITNESS 32
+ #define KC_PLATFORM_WIN32 1
+ #endif
+
+#elif defined(__linux__)
+ #define KC_PLATFORM_LINUX 1
+
+ #if __x86_64__
+ #define KC_PLATFORM_BITNESS 64
+ #define KC_PLATFORM_LINUX64 1
+ #elif __i386__
+ #define KC_PLATFORM_BITNESS 32
+ #define KC_PLATFORM_LINUX32 1
+ #else
+ #error Unknown linux-supported architecture.
+ #endif
+
+#else
+ #error Unknown platform.
+#endif
+
+/* Compiler */
+
+#if defined(__clang__) || defined(__llvm__)
+ #define KC_COMPILER_CLANG
+ #define KC_COMPILER_NAME "clang"
+#elif defined(__INTEL_COMPILER)
+ #define Intel compilers currently unsuppored
+#elif defined(__GNUC__) && !defined(__clang__) && !defined(__llvm__) && !defined(__INTEL_COMPILER)
+ #define KC_COMPILER_GCC
+ #define KC_COMPILER_NAME "gcc"
+#elif defined(__TINYCC__)
+ #define KC_COMPILER_TCC
+ #define KC_COMPILER_NAME "tcc"
+#elif defined( _MSC_VER )
+ #define KC_COMPILER_MSVC
+ #define KC_COMPILER_NAME "msvc"
+#else
+ #error Unidentified compiler.
+#endif
+
+
+/* For future use in osdev. */
+#if defined(KC_USE_PLATFORM_LIBC) && defined(KC_TARGET_BAREMETAL)
+ #error Can not use libc on a bare metal target.
+#endif
+
+
+#endif
platform/memory.h
@@ -0,0 +1,83 @@
+#ifndef KC_HEADER_PLATFORM_MEMORY_INCLUDED
+#define KC_HEADER_PLATFORM_MEMORY_INCLUDED
+
+#include "./info.h"
+#include "../types/types.h"
+#include "../memory/units.h"
+
+#if KC_PLATFORM_LINUX
+ #define KC_PLATFORM_MEM_PAGESIZE KiB(4)
+#elif KC_PLATFORM_WINDOWS
+ #define KC_PLATFORM_MEM_PAGESIZE KiB(4)
+#else
+ #error Unknown platform. Page size can not be known.
+#endif
+
+/* This aligns a number to a power of 2. 'p' must be a power of 2. */
+#define ROUND_UP_TO_A_POW2(n, p) (((u64)(n) + ((u64)(p) - 1)) & (~((u64)(p) - 1)))
+
+status kc_platform_mem_va_reserve( void** outptr, u64 amount );
+status kc_platform_mem_va_release( void* start, u64 amount );
+
+status kc_platform_mem_va_commit( void* start, u64 amount );
+status kc_platform_mem_va_uncommit( void* start, u64 amount );
+
+
+#ifdef KC_IMPLEMENT
+
+ #if defined(KC_USE_PLATFORM_LIBC)
+ #if defined(KC_PLATFORM_LINUX)
+ #include <sys/mman.h>
+
+ status kc_platform_mem_va_reserve( void** outptr, u64 amount ) {
+ void* res = mmap( 0, amount, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
+ if( res == MAP_FAILED ) {
+ return ERROR_PLATFORM_MAPPING_FAILED;
+ }
+ *outptr = res;
+ return STATUS_SUCCESS;
+ }
+
+ status kc_platform_mem_va_release( void* start, u64 amount ) {
+ i32 res = munmap( start, amount );
+
+ if( res != 0 ) { return ERROR_PLATFORM_MAPPING_FAILED; }
+
+ return STATUS_SUCCESS;
+ }
+
+ status kc_platform_mem_va_commit( void* start, u64 amount ) {
+ i32 res = mprotect( start, amount, PROT_READ | PROT_WRITE );
+
+ if( res != 0 ) { return ERROR_PLATFORM_MEMORY_PERMISSION_CHANGE_FAILED; };
+
+ return STATUS_SUCCESS;
+ }
+
+ status kc_platform_mem_va_uncommit( void* start, u64 amount ) {
+ i32 res = mprotect( start, amount, PROT_NONE );
+ if( res != 0 ) { return ERROR_PLATFORM_MEMORY_PERMISSION_CHANGE_FAILED; };
+
+ res = madvise( start, amount, MADV_DONTNEED);
+
+ if( res != 0 ) { return ERROR_PLATFORM_MEMORY_HINT_CHANGE_FAILED; };
+
+ return STATUS_SUCCESS;
+ }
+
+
+ #elif KC_PLATFORM_WINDOWS
+ #error Windows is not yet supported for memory arenas.
+ #elif KC_USE_PLATFORM_LIBC
+ #error Using libc for memory arenas is currently unsupported.
+ #warning Make sure malloc does not commit all the allocated memory immediately or else code may use unexpected amounts of memory (or just crash).
+ #else
+ #error Current platform does not have a memory arena implementation.
+ #endif
+ #else
+ #error No platform currently implements memory management without libc.
+ #endif
+
+#endif
+
+#endif
platform/types.h
@@ -0,0 +1,62 @@
+#ifndef KC_HEADER_PLATFORM_TYPES_INCLUDED
+#define KC_HEADER_PLATFORM_TYPES_INCLUDED
+
+#include "./info.h"
+#include "../macros/macros.h"
+
+#if defined(KC_USE_PLATFORM_LIBC)
+ #include <stdint.h>
+
+ typedef uint8_t u8;
+ typedef uint16_t u16;
+ typedef uint32_t u32;
+ typedef uint64_t u64;
+
+ typedef int8_t i8;
+ typedef int16_t i16;
+ typedef int32_t i32;
+ typedef int64_t i64;
+
+ typedef _Float16 f16;
+ typedef float f32;
+ typedef double f64;
+
+#else
+
+ #if defined(KC_PLATFORM_LINUX) || defined(KC_PLATFORM_WINDOWS)
+
+ typedef unsigned char u8;
+ typedef unsigned short u16;
+ typedef unsigned int u32;
+ typedef unsigned long long u64;
+
+ typedef signed char i8;
+ typedef signed short i16;
+ typedef signed int i32;
+ typedef signed long long i64;
+
+ typedef _Float16 f16;
+ typedef float f32;
+ typedef double f64;
+
+ typedef void ( *funcptr )( void );
+
+ #endif
+
+#endif
+
+ASSERT( sizeof( u8 ) == 1 );
+ASSERT( sizeof( u16 ) == 2 );
+ASSERT( sizeof( u32 ) == 4 );
+ASSERT( sizeof( u64 ) == 8 );
+
+ASSERT( sizeof( i8 ) == 1 );
+ASSERT( sizeof( i16 ) == 2 );
+ASSERT( sizeof( i32 ) == 4 );
+ASSERT( sizeof( i64 ) == 8 );
+
+ASSERT( sizeof(f16) == 2 );
+ASSERT( sizeof( f32 ) == 4 );
+ASSERT( sizeof( f64 ) == 8 );
+
+#endif
tests/a.out
Binary file
tests/main.c
@@ -0,0 +1,28 @@
+#define _DEFAULT_SOURCE
+#include <stdio.h>
+#define KC_IMPLEMENT
+#define KC_USE_PLATFORM_LIBC
+//#define KC_ZERO_MEMORY_STRICT
+
+#include "../include/memory.h"
+#include "../include/types.h"
+#include "../platform/assert.h"
+
+
+int main(void) {
+MemArena a;
+
+ assert(OK( MemArena_create( &a, GiB(120) )) );
+
+ u8* data;
+ assert( OK(MemArena_take( &a, u8, GiB(1), (void**)&data ) ));
+
+ for( u64 i = 0; i < GiB(1); ++i ) {
+ data[i] = 'K';
+ }
+
+ MemArena_uncommit_unused( &a );
+
+
+ assert(OK(MemArena_destroy(&a)));
+}
tests/main.o
Binary file
types/array.h
@@ -0,0 +1,68 @@
+#ifndef KC_HEADER_ARRAY_INCLUDED
+#define KC_HEADER_ARRAY_INCLUDED
+
+#include "./types.h"
+
+#define DECLARE_DYNAMIC_ARRAY(T) \
+ typedef struct _##T##Arr { \
+ u64 len; /* length */ \
+ u64 cap; /* capacity*/ \
+ T data[]; \
+ } T##Arr; \
+ \
+status T##Arr_create( T##Arr *out, u64 capacity ); \
+void T##Arr_destroy( T##Arr *array ); \
+ \
+status T##Arr_push( T##Arr *array, T *in); \
+status T##Arr_pop( T##Arr *array, T *out ); \
+ \
+status T##Arr_add_at( T##Arr *array, u64 index, T *in); \
+status T##Arr_remove_at( T##Arr *array, u64 index, T *out); \
+ \
+
+#define DEFINE_DYNAMIC_ARRAY(T) \
+ \
+status T##Arr_create( T##Arr *out, u64 capacity ); \
+ \
+} \
+ \
+void T##Arr_destroy( T##Arr *array ); \
+ \
+} \
+ \
+status T##Arr_push( T##Arr *array, T *in); \
+ \
+} \
+ \
+status T##Arr_pop( T##Arr *array, T *out ); \
+ \
+} \
+ \
+status T##Arr_add_at( T##Arr *array, u64 index, T *in); \
+ \
+} \
+ \
+status T##Arr_remove_at( T##Arr *array, u64 index, T *out); \
+ \
+} \
+
+
+// Decided to drop this idea for now. Maybe when I actually find a use for it I'll add it
+// #define DECLARE_DYNAMIC_ARRAY_WITH_FREELIST(T) \
+// ASSERT(sizeof(T) >= sizeof(u64)) /* Make sure we can store a next index */ \
+// typedef struct _##T##ArrFl { \
+// u64 freelist; /* index + 1 so 0 means no freelist */ \
+// T##Arr array; \
+// }; \
+// \
+// status T##ArrFl_add( T##Arr *array, T *in); \
+// status T##ArrFl_remove( T##Arr *array, u64 index, T *out); \
+// \
+//
+//
+// DECLARE_DYNAMIC_ARRAY(int);
+// //DECLARE_DYNAMIC_ARRAY_WITH_FREELIST(u64);
+
+
+
+#endif
types/types.h
@@ -0,0 +1,32 @@
+#ifndef KC_HEADER_TYPES_TYPES_INCLUDED
+#define KC_HEADER_TYPES_TYPES_INCLUDED
+
+#include "../platform/types.h"
+
+/* Boolean */
+typedef u8 bool;
+
+
+/* Status type */
+typedef i32 status;
+
+#define OK(status) ((status) == STATUS_SUCCESS)
+
+#define PROPAGATE_ERROR(_status) do{ status ret = _status; if(ret != STATUS_SUCCESS) return ret; }while(0)
+
+enum _Status {
+ STATUS_SUCCESS = 0,
+ ERROR_GENERIC = 1,
+ ERROR_WOULD_OVERRUN = 2,
+
+ ERROR_PLATFORM_GENERIC = 10000,
+ ERROR_PLATFORM_MAPPING_FAILED = 10001,
+ ERROR_PLATFORM_MEMORY_PERMISSION_CHANGE_FAILED = 10002,
+ ERROR_PLATFORM_MEMORY_HINT_CHANGE_FAILED = 10003,
+
+};
+
+
+/* String types */
+
+#endif
.gitignore
@@ -1,1 +1,2 @@
/compile_flags.txt
+/Session.vim
README.md
@@ -2,7 +2,18 @@
A minimal, portable header-only C library meant to provide a bunch of useful functionality, ideally across multiple platforms. Consider linux the default platform.
+# Convetions
+
+All headers in subdirectories are not supposed to be consumed. Only headers in the repo root are meant to be used.
+
# Usage
-This repo is meant to be used as a git submodule in any project you want to use it it. The include paths are convieved so that they are reasonable if you add this repo's root to the include paths of your compiler.
+This repo is meant to be used as a git submodule in any project you want to use it it. The include paths are convieved so that they are reasonable if you add this repo's `include` directory to the include paths of your compiler.
+
+# Macros
+
+- KC_IMPLEMENT - actually generate the implementation from the headers and not just declare everything. This is great for unity builds or grouping different implementations into different `.c` files.
+- KC_USE_PLATFORM_LIBC - use the current platform's libc where this is an option.
+- KC_ZERO_MEMORY_STRICT, KC_ZERO_MEMORY_LOOSE, KC_ZERO_MEMORY_NONE - zero out buffers, released memory etc. even in places where it would not be strictly necessary from a functional standpoint. Strict zeroes out the most, loose zeroes out the most critical ones and none does not zero out any places that are not strictly necessary for functionality.
+