Array tests, allocator design changes

This commit is contained in:
Krzosa Karol
2023-12-31 16:07:58 +01:00
parent f14d5462b0
commit b3f5ce3772
11 changed files with 485 additions and 365 deletions

87
arena.c
View File

@@ -22,6 +22,7 @@ MA_API void MA_MemoryCopy(void *dst, void *src, size_t size) {
#include <stdlib.h> #include <stdlib.h>
#define MA_CMalloc(x) malloc(x) #define MA_CMalloc(x) malloc(x)
#define MA_CFree(x) free(x) #define MA_CFree(x) free(x)
#define MA_CRealloc(p, size) realloc(p, size)
#endif #endif
#ifndef MA_StaticFunc #ifndef MA_StaticFunc
@@ -94,24 +95,6 @@ MA_StaticFunc size_t MA__AlignLen(MA_Arena *a) {
return aligned; return aligned;
} }
MA_API void *MA__BeginPackedArray(MA_Arena *arena, size_t element_size) {
MA_ASSERT(arena->memory.data);
arena->len = MA__AlignLen(arena);
arena->saved_alignment = arena->alignment;
arena->alignment = 0;
arena->packed_array_begin = arena->len;
arena->packed_array_element_size = element_size;
void *result = arena->memory.data + arena->len;
return result;
}
MA_API int MA_EndPackedArray(MA_Arena *arena) {
arena->alignment = arena->saved_alignment;
size_t different = (arena->len - arena->packed_array_begin);
int result = (int)((arena->len - arena->packed_array_begin) / arena->packed_array_element_size);
return result;
}
MA_API void MA_SetAlignment(MA_Arena *arena, int alignment) { MA_API void MA_SetAlignment(MA_Arena *arena, int alignment) {
arena->alignment = alignment; arena->alignment = alignment;
} }
@@ -154,7 +137,6 @@ MA_API void *MA_PushSize(MA_Arena *arena, size_t size) {
MA_API void MA_InitEx(MA_Arena *a, size_t reserve) { MA_API void MA_InitEx(MA_Arena *a, size_t reserve) {
a->memory = MV_Reserve(reserve); a->memory = MV_Reserve(reserve);
a->alignment = MA_DEFAULT_ALIGNMENT; a->alignment = MA_DEFAULT_ALIGNMENT;
MA_INIT_HOOK(a);
} }
MA_API void MA_Init(MA_Arena *a) { MA_API void MA_Init(MA_Arena *a) {
@@ -171,7 +153,6 @@ MA_API MA_Arena *MA_Bootstrap(void) {
MA_Arena bootstrap_arena = {0}; MA_Arena bootstrap_arena = {0};
MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena); MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena);
*arena = bootstrap_arena; *arena = bootstrap_arena;
arena->allocator.obj = arena;
return arena; return arena;
} }
@@ -180,7 +161,6 @@ MA_API void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size) {
arena->memory.commit = size; arena->memory.commit = size;
arena->memory.reserve = size; arena->memory.reserve = size;
arena->alignment = MA_DEFAULT_ALIGNMENT; arena->alignment = MA_DEFAULT_ALIGNMENT;
MA_INIT_HOOK(arena);
} }
MA_API MA_Arena MA_MakeFromBuffer(void *buffer, size_t size) { MA_API MA_Arena MA_MakeFromBuffer(void *buffer, size_t size) {
@@ -190,6 +170,12 @@ MA_API MA_Arena MA_MakeFromBuffer(void *buffer, size_t size) {
return arena; return arena;
} }
MA_API MA_Arena MA_Create() {
MA_Arena arena = {0};
MA_Init(&arena);
return arena;
}
MA_API char *MA_PushStringCopy(MA_Arena *arena, char *p, size_t size) { MA_API char *MA_PushStringCopy(MA_Arena *arena, char *p, size_t size) {
char *copy_buffer = (char *)MA_PushSizeNonZeroed(arena, size + 1); char *copy_buffer = (char *)MA_PushSizeNonZeroed(arena, size + 1);
MA_MemoryCopy(copy_buffer, p, size); MA_MemoryCopy(copy_buffer, p, size);
@@ -233,12 +219,12 @@ MA_API void MA_Load(MA_Checkpoint checkpoint) {
} }
MA_API void *M_AllocNonZeroed(M_Allocator allocator, size_t size) { MA_API void *M_AllocNonZeroed(M_Allocator allocator, size_t size) {
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size); void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0);
return p; return p;
} }
MA_API void *M_Alloc(M_Allocator allocator, size_t size) { MA_API void *M_Alloc(M_Allocator allocator, size_t size) {
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size); void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0);
MA_MemoryZero(p, size); MA_MemoryZero(p, size);
return p; return p;
} }
@@ -250,10 +236,16 @@ MA_API void *M_AllocCopy(M_Allocator allocator, void *p, size_t size) {
} }
MA_API void M_Dealloc(M_Allocator allocator, void *p) { MA_API void M_Dealloc(M_Allocator allocator, void *p) {
allocator.p(allocator.obj, M_AllocatorOp_Deallocate, p, 0); allocator.p(allocator.obj, M_AllocatorOp_Deallocate, p, 0, 0);
} }
MA_StaticFunc void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size) { MA_API void *M_ReallocNonZeroed(M_Allocator allocator, void *p, size_t size, size_t old_size) {
void *result = allocator.p(allocator.obj, M_AllocatorOp_Reallocate, p, size, old_size);
// @todo: add old_size? because we can't zero
return result;
}
MA_StaticFunc void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) {
if (kind == M_AllocatorOp_Allocate) { if (kind == M_AllocatorOp_Allocate) {
return MA_CMalloc(size); return MA_CMalloc(size);
} }
@@ -263,16 +255,26 @@ MA_StaticFunc void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, voi
return NULL; return NULL;
} }
if (kind == M_AllocatorOp_Reallocate) {
return MA_CRealloc(p, size);
}
MA_ASSERT("MA_Arena invalid codepath"); MA_ASSERT("MA_Arena invalid codepath");
return NULL; return NULL;
} }
MA_API void *MA_AllocatorProc(M_Allocator allocator, M_AllocatorOp kind, void *p, size_t size) { MA_API void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) {
if (kind == M_AllocatorOp_Allocate) { if (kind == M_AllocatorOp_Allocate) {
return MA_PushSizeNonZeroed((MA_Arena *)allocator.obj, size); return MA_PushSizeNonZeroed((MA_Arena *)allocator, size);
} }
if (kind == M_AllocatorOp_Deallocate) { else if (kind == M_AllocatorOp_Reallocate) {
void *new_p = MA_PushSizeNonZeroed((MA_Arena *)allocator, size);
MA_MemoryCopy(new_p, p, old_size);
return new_p;
}
else if (kind == M_AllocatorOp_Deallocate) {
return NULL; return NULL;
} }
@@ -280,6 +282,35 @@ MA_API void *MA_AllocatorProc(M_Allocator allocator, M_AllocatorOp kind, void *p
return NULL; return NULL;
} }
MA_API void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) {
MA_Arena *arena = (MA_Arena *)allocator;
if (kind == M_AllocatorOp_Reallocate) {
if (size > arena->len) {
size_t size_to_push = size - arena->len;
MA_PushSizeNonZeroed(arena, size_to_push);
return arena->memory.data;
}
}
if (kind == M_AllocatorOp_Deallocate) {
MA_DeallocateArena(arena);
return NULL;
}
MA_ASSERT("MA_Arena invalid codepath");
return NULL;
}
MA_API M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena) {
M_Allocator allocator = {arena, MA_ExclusiveAllocatorProc};
return allocator;
}
MA_API M_Allocator MA_GetAllocator(MA_Arena *arena) {
M_Allocator allocator = {arena, MA_AllocatorProc};
return allocator;
}
MA_API M_Allocator M_GetSystemAllocator(void) { MA_API M_Allocator M_GetSystemAllocator(void) {
M_Allocator allocator; M_Allocator allocator;
allocator.obj = 0; allocator.obj = 0;

47
arena.h
View File

@@ -26,24 +26,10 @@ typedef struct M_Allocator M_Allocator;
#define MA_COMMIT_ADD_SIZE MA_MIB(4) #define MA_COMMIT_ADD_SIZE MA_MIB(4)
#endif #endif
#ifndef MA_INHERIT_HOOK
#define MA_INHERIT_HOOK
#endif
#ifndef MA_C_INHERIT_HOOK
#define MA_C_INHERIT_HOOK M_Allocator allocator;
#endif
#ifndef MA_ZERO_IS_INITIALIZATION #ifndef MA_ZERO_IS_INITIALIZATION
#define MA_ZERO_IS_INITIALIZATION 1 #define MA_ZERO_IS_INITIALIZATION 1
#endif #endif
#ifndef MA_INIT_HOOK
#define MA_INIT_HOOK(arena) \
arena->allocator.obj = (void *)arena; \
arena->allocator.p = (M_AllocatorProc *)MA_AllocatorProc;
#endif
#ifndef MA_API #ifndef MA_API
#ifdef __cplusplus #ifdef __cplusplus
#define MA_API extern "C" #define MA_API extern "C"
@@ -72,12 +58,16 @@ typedef enum M_AllocatorOp {
M_AllocatorOp_Invalid, M_AllocatorOp_Invalid,
M_AllocatorOp_Allocate, M_AllocatorOp_Allocate,
M_AllocatorOp_Deallocate, M_AllocatorOp_Deallocate,
M_AllocatorOp_Reallocate,
} M_AllocatorOp; } M_AllocatorOp;
typedef void *M_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size); typedef void *M_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size);
MA_API void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size);
MA_API void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size);
struct M_Allocator { struct M_Allocator {
void *obj; void *obj;
void *(*p)(void *allocator, M_AllocatorOp kind, void *p, size_t size); M_AllocatorProc *p;
}; };
struct MV_Memory { struct MV_Memory {
@@ -86,15 +76,14 @@ struct MV_Memory {
uint8_t *data; uint8_t *data;
}; };
struct MA_Arena MA_INHERIT_HOOK { struct MA_Arena {
MA_C_INHERIT_HOOK
MV_Memory memory; MV_Memory memory;
int alignment; int alignment;
int saved_alignment;
size_t len; size_t len;
size_t packed_array_element_size; #ifdef __cplusplus
size_t packed_array_begin; operator M_Allocator() { return {this, MA_AllocatorProc}; }
#endif
}; };
struct MA_Checkpoint { struct MA_Checkpoint {
@@ -107,7 +96,6 @@ struct MA_Checkpoint {
#define MA_PushStruct(a, T) (T *)MA_PushSize(a, sizeof(T)) #define MA_PushStruct(a, T) (T *)MA_PushSize(a, sizeof(T))
#define MA_PushArray(a, T, c) (T *)MA_PushSize(a, sizeof(T) * (c)) #define MA_PushArray(a, T, c) (T *)MA_PushSize(a, sizeof(T) * (c))
#define MA_PushStructCopy(a, T, p) (T *)MA_PushCopy(a, (p), sizeof(T)) #define MA_PushStructCopy(a, T, p) (T *)MA_PushCopy(a, (p), sizeof(T))
#define MA_BeginPackedArray(arena, T) (T *)MA__BeginPackedArray(arena, sizeof(T))
#define MA_CheckpointScope(name, InArena) for (MA_Checkpoint name = MA_Save(InArena); name.arena; (MA_Load(name), name.arena = 0)) #define MA_CheckpointScope(name, InArena) for (MA_Checkpoint name = MA_Save(InArena); name.arena; (MA_Load(name), name.arena = 0))
#define M_AllocStruct(a, T) (T *)M_Alloc((a), sizeof(T)) #define M_AllocStruct(a, T) (T *)M_Alloc((a), sizeof(T))
@@ -130,18 +118,20 @@ MA_API size_t MA_GetAlignOffset(size_t size, size_t align);
MA_API size_t MA_AlignUp(size_t size, size_t align); MA_API size_t MA_AlignUp(size_t size, size_t align);
MA_API size_t MA_AlignDown(size_t size, size_t align); MA_API size_t MA_AlignDown(size_t size, size_t align);
MA_API void MA_DeallocateStub(MA_Arena *arena, void *p); MA_API void MA_DeallocateStub(MA_Arena *arena, void *p);
MA_API void MA_InitEx(MA_Arena *a, size_t reserve);
MA_API void MA_Init(MA_Arena *a);
MA_API MA_Arena MA_Create();
MA_API void MA_PopToPos(MA_Arena *arena, size_t pos); MA_API void MA_PopToPos(MA_Arena *arena, size_t pos);
MA_API void * MA_PopSize(MA_Arena *arena, size_t size); MA_API void * MA_PopSize(MA_Arena *arena, size_t size);
MA_API void MA_DeallocateArena(MA_Arena *arena); MA_API void MA_DeallocateArena(MA_Arena *arena);
MA_API void MA_Reset(MA_Arena *arena); MA_API void MA_Reset(MA_Arena *arena);
MA_API void * MA__BeginPackedArray(MA_Arena *arena, size_t element_size);
MA_API int MA_EndPackedArray(MA_Arena *arena);
MA_API void MA_SetAlignment(MA_Arena *arena, int alignment); MA_API void MA_SetAlignment(MA_Arena *arena, int alignment);
MA_API uint8_t * MA_GetTop(MA_Arena *a); MA_API uint8_t * MA_GetTop(MA_Arena *a);
MA_API void * MA_PushSizeNonZeroed(MA_Arena *a, size_t size); MA_API void * MA_PushSizeNonZeroed(MA_Arena *a, size_t size);
MA_API void * MA_PushSize(MA_Arena *arena, size_t size); MA_API void * MA_PushSize(MA_Arena *arena, size_t size);
MA_API void MA_InitEx(MA_Arena *a, size_t reserve);
MA_API void MA_Init(MA_Arena *a);
MA_API void MA_MakeSureInitialized(MA_Arena *a); MA_API void MA_MakeSureInitialized(MA_Arena *a);
MA_API MA_Arena * MA_Bootstrap(void); MA_API MA_Arena * MA_Bootstrap(void);
MA_API void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size); MA_API void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size);
@@ -155,7 +145,6 @@ MA_API void MA_Load(MA_Checkpoint checkpoint);
MA_API MA_Checkpoint MA_GetScratchEx(MA_Arena **conflicts, int conflict_count); MA_API MA_Checkpoint MA_GetScratchEx(MA_Arena **conflicts, int conflict_count);
MA_API MA_Checkpoint MA_GetScratch(void); MA_API MA_Checkpoint MA_GetScratch(void);
MA_API MA_Checkpoint MA_GetScratch1(MA_Arena *conflict); MA_API MA_Checkpoint MA_GetScratch1(MA_Arena *conflict);
MA_API void * MA_AllocatorProc(M_Allocator allocator, M_AllocatorOp kind, void *p, size_t size);
MA_API MV_Memory MV_Reserve(size_t size); MA_API MV_Memory MV_Reserve(size_t size);
MA_API bool MV_Commit(MV_Memory *m, size_t commit); MA_API bool MV_Commit(MV_Memory *m, size_t commit);
@@ -165,8 +154,11 @@ MA_API bool MV_DecommitPos(MV_Memory *m, size_t pos);
MA_API void * M_AllocNonZeroed(M_Allocator allocator, size_t size); MA_API void * M_AllocNonZeroed(M_Allocator allocator, size_t size);
MA_API void * M_Alloc(M_Allocator allocator, size_t size); MA_API void * M_Alloc(M_Allocator allocator, size_t size);
MA_API void * M_AllocCopy(M_Allocator allocator, void *p, size_t size); MA_API void * M_AllocCopy(M_Allocator allocator, void *p, size_t size);
MA_API void * M_ReallocNonZeroed(M_Allocator allocator, void *p, size_t size, size_t old_size);
MA_API void M_Dealloc(M_Allocator allocator, void *p); MA_API void M_Dealloc(M_Allocator allocator, void *p);
MA_API M_Allocator M_GetSystemAllocator(void); MA_API M_Allocator M_GetSystemAllocator(void);
MA_API M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena);
MA_API M_Allocator MA_GetAllocator(MA_Arena *arena);
// clang-format on // clang-format on
#ifndef MA_DISABLE_SCRATCH #ifndef MA_DISABLE_SCRATCH
@@ -180,6 +172,7 @@ struct MA_Scratch {
MA_Scratch() { this->checkpoint = MA_GetScratch(); } MA_Scratch() { this->checkpoint = MA_GetScratch(); }
~MA_Scratch() { MA_Load(checkpoint); } ~MA_Scratch() { MA_Load(checkpoint); }
operator MA_Arena *() { return checkpoint.arena; } operator MA_Arena *() { return checkpoint.arena; }
operator M_Allocator() { return MA_GetAllocator(checkpoint.arena); }
private: // @Note: Disable copy constructors, cause its error prone private: // @Note: Disable copy constructors, cause its error prone
MA_Scratch(MA_Scratch &arena); MA_Scratch(MA_Scratch &arena);

102
array.hpp
View File

@@ -1,20 +1,8 @@
#pragma once #pragma once
#ifndef ARRAY_PRIVATE_FUNCTION #ifndef ARRAY_REALLOCATE
#if defined(__GNUC__) || defined(__clang__)
#define ARRAY_PRIVATE_FUNCTION __attribute__((unused)) static
#else
#define ARRAY_PRIVATE_FUNCTION static
#endif
#endif
#ifndef ARRAY_ALLOCATE
#include <stdlib.h>
#define ARRAY_ALLOCATE(allocator, size) malloc(size)
#endif
#ifndef ARRAY_DEALLOCATE
#include <stdlib.h> #include <stdlib.h>
#define ARRAY_REALLOCATE(allocator, p, size, old_size) realloc(p, size)
#define ARRAY_DEALLOCATE(allocator, p) free(p) #define ARRAY_DEALLOCATE(allocator, p) free(p)
#endif #endif
@@ -28,44 +16,45 @@
#define ARRAY_MemoryMove(dst, src, size) memmove(dst, src, size) #define ARRAY_MemoryMove(dst, src, size) memmove(dst, src, size)
#endif #endif
#ifndef ARRAY_SET_DEFAULT_ALLOCATOR #ifndef ARRAY_Allocator
#define ARRAY_SET_DEFAULT_ALLOCATOR #define ARRAY_Allocator void *
#endif
// Example: // Example:
// #define ARRAY_SET_DEFAULT_ALLOCATOR if (!allocator) allocator = global_heap; // #define ARRAY_SET_DEFAULT_ALLOCATOR if (!allocator) allocator = global_heap;
#ifndef ARRAY_SET_DEFAULT_ALLOCATOR
#define ARRAY_SET_DEFAULT_ALLOCATOR
#endif #endif
// Iterating and removing elements
//
// ForArrayRemovable(array) {
// ForArrayRemovablePrepare(array);
// if (it == 4) ForArrayRemovableDeclare();
// }
//
#ifdef DEFER_HEADER #ifdef DEFER_HEADER
#define ForArrayRemovable(a) for (int __i = 0; __i < (a).len; __i += 1) #define ForArrayRemovable(a) for (int __i = 0; __i < (a).len; __i += 1)
#define ForArrayRemovablePrepare(a) \ #define ForArrayRemovablePrepare(a) \
auto &it = (a)[__i]; \ auto &it = (a)[__i]; \
bool remove_it = false; \ bool remove_it = false; \
defer { \ defer { \
if (remove_it) { \ if (remove_it) { \
(a).ordered_remove(it); \ (a).ordered_remove(it); \
__i -= 1; \ __i -= 1; \
} \ } \
} }
#define ForArrayRemovableDeclare() (remove_it = true) #define ForArrayRemovableDeclare() (remove_it = true)
#endif #endif
#if !defined(ARRAY_ALLOCATOR_CODE) #ifndef For
#if defined(ARRAY_ALLOCATOR_TYPE) #define For2(it, array) for(auto &it : (array))
#define ARRAY_ALLOCATOR_CODE(x) x #define For(array) For2(it, array)
#define ARRAY_ALLOCATOR_PARAM ARRAY_ALLOCATOR_TYPE allocator,
#else
#define ARRAY_ALLOCATOR_CODE(x)
#define ARRAY_ALLOCATOR_PARAM
#endif
#endif
#if !defined(For)
#define For2(array,it) for(auto &it : (array))
#define For(array) For2(array,it)
#endif #endif
template <class T> template <class T>
struct Array { struct Array {
ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE allocator;) ARRAY_Allocator allocator;
T *data; T *data;
int cap, len; int cap, len;
@@ -81,8 +70,8 @@ struct Array {
bool is_first(T &item) { return &item == first(); } bool is_first(T &item) { return &item == first(); }
bool is_last(T &item) { return &item == last(); } bool is_last(T &item) { return &item == last(); }
bool contains(T *item) { bool contains(T &item) {
bool result = item >= data && item < data + len; bool result = &item >= data && &item < data + len;
return result; return result;
} }
@@ -154,24 +143,19 @@ struct Array {
if (size > cap) { if (size > cap) {
ARRAY_SET_DEFAULT_ALLOCATOR; ARRAY_SET_DEFAULT_ALLOCATOR;
void *p = ARRAY_ALLOCATE(allocator, size * sizeof(T)); void *p = ARRAY_REALLOCATE(allocator, data, size * sizeof(T), cap * sizeof(T));
ARRAY_ASSERT(p); ARRAY_ASSERT(p);
if (data) {
ARRAY_MemoryMove(p, data, len * sizeof(T));
ARRAY_DEALLOCATE(allocator, data);
}
data = (T *)p; data = (T *)p;
cap = size; cap = size;
} }
} }
void init(ARRAY_ALLOCATOR_PARAM int size) { void init(ARRAY_Allocator allocator, int size) {
len = 0; len = 0;
cap = 0; cap = 0;
data = 0; data = 0;
ARRAY_ALLOCATOR_CODE(this->allocator = allocator;) this->allocator = allocator;
reserve(size); reserve(size);
} }
@@ -190,6 +174,11 @@ struct Array {
item = data[--len]; item = data[--len];
} }
void unordered_remove_index(int index) {
ARRAY_ASSERT(index >= 0 && index < len);
data[index] = data[--len];
}
int get_index(const T &item) { int get_index(const T &item) {
ptrdiff_t index = (ptrdiff_t)(&item - data); ptrdiff_t index = (ptrdiff_t)(&item - data);
ARRAY_ASSERT(index >= 0 && index < len); ARRAY_ASSERT(index >= 0 && index < len);
@@ -201,8 +190,11 @@ struct Array {
ARRAY_ASSERT(len > 0); ARRAY_ASSERT(len > 0);
ARRAY_ASSERT(&item >= begin() && &item < end()); ARRAY_ASSERT(&item >= begin() && &item < end());
int index = get_index(item); int index = get_index(item);
ARRAY_ASSERT(index >= 0 && index < len); ordered_remove_index(index);
}
void ordered_remove_index(int index) {
ARRAY_ASSERT(index >= 0 && index < len);
int right_len = len - index - 1; int right_len = len - index - 1;
ARRAY_MemoryMove(data + index, data + index + 1, right_len * sizeof(T)); ARRAY_MemoryMove(data + index, data + index + 1, right_len * sizeof(T));
len -= 1; len -= 1;
@@ -230,9 +222,9 @@ struct Array {
len = cap = 0; len = cap = 0;
} }
Array<T> exact_copy(ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator)) { Array<T> exact_copy(ARRAY_Allocator allocator) {
Array result = {}; Array result = {};
ARRAY_ALLOCATOR_CODE(result.allocator = allocator;) result.allocator = allocator;
result.reserve(cap); result.reserve(cap);
ARRAY_MemoryMove(result.data, data, sizeof(T) * len); ARRAY_MemoryMove(result.data, data, sizeof(T) * len);
@@ -240,7 +232,7 @@ struct Array {
return result; return result;
} }
Array<T> tight_copy(ARRAY_ALLOCATOR_CODE(ARRAY_ALLOCATOR_TYPE *allocator)) { Array<T> tight_copy(ARRAY_Allocator allocator) {
Array result = {}; Array result = {};
ARRAY_ALLOCATOR_CODE(result.allocator = allocator;) ARRAY_ALLOCATOR_CODE(result.allocator = allocator;)
result.reserve(len); result.reserve(len);

View File

@@ -14,13 +14,14 @@ set RELEASE_LINE=%RELEASE% %WRN% %COMMON% -link -incremental:no %LINK_RELEASE%
mkdir build mkdir build
cd build cd build
cl.exe -Fe:test_arena.exe ../test/test_arena.cpp %DEBUG_LINE% cl.exe -Fe:test_array.exe ../test/test_array.cpp %DEBUG_LINE%
if %errorlevel% neq 0 exit /b %errorlevel% if %errorlevel% neq 0 exit /b %errorlevel%
test_arena.exe test_array.exe
if %errorlevel% neq 0 exit /b %errorlevel% if %errorlevel% neq 0 exit /b %errorlevel%
cl.exe -Fe:cpp_debug.exe ../test/main.cpp %DEBUG_LINE% cl.exe -Fe:cpp_debug.exe ../test/main.cpp %DEBUG_LINE%
if %errorlevel% neq 0 exit /b %errorlevel% if %errorlevel% neq 0 exit /b %errorlevel%
cl.exe -Fe:cpp_release.exe ../test/main.cpp %RELEASE_LINE% cl.exe -Fe:cpp_release.exe ../test/main.cpp %RELEASE_LINE%
if %errorlevel% neq 0 exit /b %errorlevel% if %errorlevel% neq 0 exit /b %errorlevel%
cl.exe -Fe:c_debug.exe ../test/main.c %DEBUG_LINE% cl.exe -Fe:c_debug.exe ../test/main.c %DEBUG_LINE%

View File

@@ -3,6 +3,6 @@ set -e
mkdir build mkdir build
cd build cd build
clang -o test_arena ../test/test_arena.cpp -fno-exceptions -fno-rtti -Wno-writable-strings clang -o test_array ../test/test_array.cpp -fno-exceptions -fno-rtti -Wno-writable-strings
./test_arena ./test_array
cd .. cd ..

8
core.c
View File

@@ -2,29 +2,21 @@
#define STB_SPRINTF_IMPLEMENTATION #define STB_SPRINTF_IMPLEMENTATION
#include "stb_sprintf.h" #include "stb_sprintf.h"
#define IO_VSNPRINTF stbsp_vsnprintf #define IO_VSNPRINTF stbsp_vsnprintf
#define IO_SNPRINTF stbsp_snprintf #define IO_SNPRINTF stbsp_snprintf
#include "io.c" #include "io.c"
#define MA_ASSERT(x) IO_Assert(x) #define MA_ASSERT(x) IO_Assert(x)
#include "arena.c" #include "arena.c"
#define RE_ASSERT(x) IO_Assert(x) #define RE_ASSERT(x) IO_Assert(x)
#include "regex.c" #include "regex.c"
#include "unicode.c" #include "unicode.c"
#define S8_VSNPRINTF stbsp_vsnprintf #define S8_VSNPRINTF stbsp_vsnprintf
#define S8_ALLOCATE(allocator, size) MA_PushSize(allocator, size) #define S8_ALLOCATE(allocator, size) MA_PushSize(allocator, size)
#define S8_ASSERT(x) IO_Assert(x) #define S8_ASSERT(x) IO_Assert(x)
#define S8_MemoryCopy MA_MemoryCopy #define S8_MemoryCopy MA_MemoryCopy
#include "string.c" #include "string.c"
#define MU_ASSERT IO_Assert #define MU_ASSERT IO_Assert
#include "multimedia.h" #include "multimedia.h"
#include "hash.c" #include "hash.c"
#include "load_library.c" #include "load_library.c"
#include "filesystem.c" #include "filesystem.c"

2
core.h
View File

@@ -21,6 +21,8 @@
#include "table.hpp" #include "table.hpp"
#define ARRAY_ASSERT IO_Assert #define ARRAY_ASSERT IO_Assert
#define ARRAY_ALLOCATOR_TYPE M_Allocator #define ARRAY_ALLOCATOR_TYPE M_Allocator
#define ARRAY_REALLOCATE(allocator, p, size, old_size) M_ReallocNonZeroed(allocator, p, size, old_size)
#define ARRAY_DEALLOCATE(allocator, p) M_Dealloc(allocator, p)
#define ARRAY_SET_DEFAULT_ALLOCATOR \ #define ARRAY_SET_DEFAULT_ALLOCATOR \
if (!allocator.p) allocator = M_GetSystemAllocator(); if (!allocator.p) allocator = M_GetSystemAllocator();
#include "array.hpp" #include "array.hpp"

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
#define DEFER_HEADER
template <typename T> template <typename T>
struct DEFER_ExitScope { struct DEFER_ExitScope {
@@ -19,4 +20,4 @@ class DEFER_ExitScopeHelp {
#define DEFER_CONCAT_INTERNAL(x, y) x##y #define DEFER_CONCAT_INTERNAL(x, y) x##y
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y) #define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]() #define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()

456
table.hpp
View File

@@ -1,229 +1,229 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
/* /*
Hash table implementation: Hash table implementation:
Pointers to values Pointers to values
Open adressing Open adressing
Linear Probing Linear Probing
Power of 2 Power of 2
Robin Hood hashing Robin Hood hashing
Resizes on high probe count (min max load factor) Resizes on high probe count (min max load factor)
Hash 0 is reserved for empty hash table entry Hash 0 is reserved for empty hash table entry
*/ */
// #define TABLE_ALLOCATOR_TYPE Allocator // #define TABLE_ALLOCATOR_TYPE Allocator
#ifndef TABLE_PRIVATE_FUNCTION #ifndef TABLE_PRIVATE_FUNCTION
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define TABLE_PRIVATE_FUNCTION __attribute__((unused)) static #define TABLE_PRIVATE_FUNCTION __attribute__((unused)) static
#else #else
#define TABLE_PRIVATE_FUNCTION static #define TABLE_PRIVATE_FUNCTION static
#endif #endif
#endif #endif
#ifndef TABLE_ALLOCATE #ifndef TABLE_ALLOCATE
#include <stdlib.h> #include <stdlib.h>
#define TABLE_ALLOCATE(allocator, size) malloc(size) #define TABLE_ALLOCATE(allocator, size) malloc(size)
#endif #endif
#ifndef TABLE_DEALLOCATE #ifndef TABLE_DEALLOCATE
#include <stdlib.h> #include <stdlib.h>
#define TABLE_DEALLOCATE(allocator, p) free(p) #define TABLE_DEALLOCATE(allocator, p) free(p)
#endif #endif
#ifndef TABLE_ASSERT #ifndef TABLE_ASSERT
#include <assert.h> #include <assert.h>
#define TABLE_ASSERT(x) assert(x) #define TABLE_ASSERT(x) assert(x)
#endif #endif
#ifndef TABLE_SET_DEFAULT_ALLOCATOR #ifndef TABLE_SET_DEFAULT_ALLOCATOR
#define TABLE_SET_DEFAULT_ALLOCATOR #define TABLE_SET_DEFAULT_ALLOCATOR
// Example: // Example:
// #define TABLE_SET_DEFAULT_ALLOCATOR if (!allocator) allocator = global_heap; // #define TABLE_SET_DEFAULT_ALLOCATOR if (!allocator) allocator = global_heap;
#endif #endif
#ifndef TABLE_HASH_BYTES #ifndef TABLE_HASH_BYTES
#define TABLE_HASH_BYTES TABLE__HashBytes #define TABLE_HASH_BYTES TABLE__HashBytes
TABLE_PRIVATE_FUNCTION uint64_t TABLE__HashBytes(void *data, unsigned size) { TABLE_PRIVATE_FUNCTION uint64_t TABLE__HashBytes(void *data, unsigned size) {
uint8_t *data8 = (uint8_t *)data; uint8_t *data8 = (uint8_t *)data;
uint64_t hash = (uint64_t)14695981039346656037ULL; uint64_t hash = (uint64_t)14695981039346656037ULL;
for (unsigned i = 0; i < size; i++) { for (unsigned i = 0; i < size; i++) {
hash = hash ^ (uint64_t)(data8[i]); hash = hash ^ (uint64_t)(data8[i]);
hash = hash * (uint64_t)1099511628211ULL; hash = hash * (uint64_t)1099511628211ULL;
} }
return hash; return hash;
} }
#endif #endif
#define TABLE__WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu))) #define TABLE__WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu)))
TABLE_PRIVATE_FUNCTION int TABLE_CStringLen(char *str) { TABLE_PRIVATE_FUNCTION int TABLE_CStringLen(char *str) {
int i = 0; int i = 0;
while(str[i]) i += 1; while(str[i]) i += 1;
return i; return i;
} }
template <class Value> template <class Value>
struct Table { struct Table {
struct Entry { struct Entry {
uint64_t hash; uint64_t hash;
uint64_t key; uint64_t key;
size_t distance; size_t distance;
Value value; Value value;
}; };
size_t len, cap; #ifdef TABLE_ALLOCATOR_TYPE
Entry *values; TABLE_ALLOCATOR_TYPE allocator;
#ifdef TABLE_ALLOCATOR_TYPE #endif
TABLE_ALLOCATOR_TYPE allocator; size_t len, cap;
#endif Entry *values;
inline bool is_empty(Entry *entry) { return entry->hash == 0; } inline bool is_empty(Entry *entry) { return entry->hash == 0; }
inline bool is_occupied(Entry *entry) { return entry->hash != 0; } inline bool is_occupied(Entry *entry) { return entry->hash != 0; }
void reserve(size_t size) { void reserve(size_t size) {
TABLE_ASSERT(size > cap && "New size is smaller then original size"); TABLE_ASSERT(size > cap && "New size is smaller then original size");
TABLE_SET_DEFAULT_ALLOCATOR; TABLE_SET_DEFAULT_ALLOCATOR;
Entry *old_values = values; Entry *old_values = values;
size_t old_cap = cap; size_t old_cap = cap;
values = (Entry *)TABLE_ALLOCATE(allocator, sizeof(Entry) * size); values = (Entry *)TABLE_ALLOCATE(allocator, sizeof(Entry) * size);
for (int i = 0; i < size; i += 1) values[i] = {}; for (int i = 0; i < size; i += 1) values[i] = {};
cap = size; cap = size;
TABLE_ASSERT(!(old_values == 0 && len != 0)); TABLE_ASSERT(!(old_values == 0 && len != 0));
if (len == 0) { if (len == 0) {
if (old_values) TABLE_DEALLOCATE(allocator, old_values); if (old_values) TABLE_DEALLOCATE(allocator, old_values);
return; return;
} }
len = 0; len = 0;
for (size_t i = 0; i < old_cap; i += 1) { for (size_t i = 0; i < old_cap; i += 1) {
Entry *it = old_values + i; Entry *it = old_values + i;
if (is_occupied(it)) { if (is_occupied(it)) {
insert(it->key, it->value); insert(it->key, it->value);
} }
} }
TABLE_DEALLOCATE(allocator, old_values); TABLE_DEALLOCATE(allocator, old_values);
} }
Entry *get_table_entry(uint64_t key) { Entry *get_table_entry(uint64_t key) {
uint64_t hash = TABLE_HASH_BYTES(&key, sizeof(key)); uint64_t hash = TABLE_HASH_BYTES(&key, sizeof(key));
if (hash == 0) hash += 1; if (hash == 0) hash += 1;
uint64_t index = TABLE__WRAP_AROUND_POWER_OF_2(hash, cap); uint64_t index = TABLE__WRAP_AROUND_POWER_OF_2(hash, cap);
uint64_t i = index; uint64_t i = index;
uint64_t distance = 0; uint64_t distance = 0;
for (;;) { for (;;) {
Entry *it = values + i; Entry *it = values + i;
if (distance > it->distance) { if (distance > it->distance) {
return 0; return 0;
} }
if (it->hash == hash && it->key == key) { if (it->hash == hash && it->key == key) {
return it; return it;
} }
distance += 1; distance += 1;
i = TABLE__WRAP_AROUND_POWER_OF_2(i + 1, cap); i = TABLE__WRAP_AROUND_POWER_OF_2(i + 1, cap);
if (i == index) return 0; if (i == index) return 0;
} }
TABLE_ASSERT(!"Invalid codepath"); TABLE_ASSERT(!"Invalid codepath");
} }
// load factor calculation was rearranged // load factor calculation was rearranged
// to get rid of division: // to get rid of division:
//> 100 * len / cap = load_factor //> 100 * len / cap = load_factor
//> len * 100 = load_factor * cap //> len * 100 = load_factor * cap
inline bool reached_load_factor(size_t lfactor) { inline bool reached_load_factor(size_t lfactor) {
return (len + 1) * 100 >= lfactor * cap; return (len + 1) * 100 >= lfactor * cap;
} }
void insert(uint64_t key, const Value &value) { void insert(uint64_t key, const Value &value) {
if (reached_load_factor(max_load_factor)) { if (reached_load_factor(max_load_factor)) {
if (cap == 0) cap = 16; // 32 cause cap*2 if (cap == 0) cap = 16; // 32 cause cap*2
reserve(cap * 2); reserve(cap * 2);
} }
uint64_t hash = TABLE_HASH_BYTES(&key, sizeof(key)); uint64_t hash = TABLE_HASH_BYTES(&key, sizeof(key));
if (hash == 0) hash += 1; if (hash == 0) hash += 1;
uint64_t index = TABLE__WRAP_AROUND_POWER_OF_2(hash, cap); uint64_t index = TABLE__WRAP_AROUND_POWER_OF_2(hash, cap);
uint64_t i = index; uint64_t i = index;
Entry to_insert = {hash, key, 0, value}; Entry to_insert = {hash, key, 0, value};
for (;;) { for (;;) {
Entry *it = values + i; Entry *it = values + i;
if (is_empty(it)) { if (is_empty(it)) {
*it = to_insert; *it = to_insert;
len += 1; len += 1;
// If we have more then 8 consecutive items we try to resize // If we have more then 8 consecutive items we try to resize
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) { if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
reserve(cap * 2); reserve(cap * 2);
} }
return; return;
} }
if (it->hash == hash && it->key == key) { if (it->hash == hash && it->key == key) {
*it = to_insert; *it = to_insert;
// If we have more then 8 consecutive items we try to resize // If we have more then 8 consecutive items we try to resize
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) { if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
reserve(cap * 2); reserve(cap * 2);
} }
return; return;
} }
// Robin hood hashing // Robin hood hashing
if (to_insert.distance > it->distance) { if (to_insert.distance > it->distance) {
Entry temp = to_insert; Entry temp = to_insert;
to_insert = *it; to_insert = *it;
*it = temp; *it = temp;
} }
to_insert.distance += 1; to_insert.distance += 1;
i = TABLE__WRAP_AROUND_POWER_OF_2(i + 1, cap); i = TABLE__WRAP_AROUND_POWER_OF_2(i + 1, cap);
TABLE_ASSERT(i != index && "Did a full 360 through a hash table, no good :( that shouldnt be possible"); TABLE_ASSERT(i != index && "Did a full 360 through a hash table, no good :( that shouldnt be possible");
} }
TABLE_ASSERT(!"Invalid codepath"); TABLE_ASSERT(!"Invalid codepath");
} }
void remove(uint64_t key) { void remove(uint64_t key) {
Entry *entry = get_table_entry(key); Entry *entry = get_table_entry(key);
entry->hash = 0; entry->hash = 0;
entry->distance = 0; entry->distance = 0;
} }
Value *get(uint64_t key) { Value *get(uint64_t key) {
Entry *v = get_table_entry(key); Entry *v = get_table_entry(key);
if (!v) return 0; if (!v) return 0;
return &v->value; return &v->value;
} }
Value *gets(char *str) { Value *gets(char *str) {
int len = TABLE_CStringLen(str); int len = TABLE_CStringLen(str);
uint64_t hash = TABLE_HASH_BYTES(str, len); uint64_t hash = TABLE_HASH_BYTES(str, len);
return get(hash); return get(hash);
} }
void puts(char *str, const Value &value) { void puts(char *str, const Value &value) {
int len = TABLE_CStringLen(str); int len = TABLE_CStringLen(str);
uint64_t hash = TABLE_HASH_BYTES(str, len); uint64_t hash = TABLE_HASH_BYTES(str, len);
insert(hash, value); insert(hash, value);
} }
void reset() { void reset() {
len = 0; len = 0;
for (size_t i = 0; i < cap; i += 1) { for (size_t i = 0; i < cap; i += 1) {
Entry *it = values + i; Entry *it = values + i;
it->hash = 0; it->hash = 0;
} }
} }
void dealloc() { void dealloc() {
COR_DEALLOCATE(allocator, values); COR_DEALLOCATE(allocator, values);
len = 0; len = 0;
cap = 0; cap = 0;
values = 0; values = 0;
} }
static const size_t max_load_factor = 80; static const size_t max_load_factor = 80;
static const size_t min_load_factor = 50; static const size_t min_load_factor = 50;
static const size_t significant_distance = 8; static const size_t significant_distance = 8;
}; };

View File

@@ -1,14 +0,0 @@
#include "../io.c"
#define MA_ASSERT(x) IO_Assert(x)
#include "../arena.c"
int main() {
MA_Scratch scratch;
int *thing = MA_PushStruct(scratch, int);
*thing = 10;
MA_Arena *arena = MA_Bootstrap();
float *thingf = MA_PushStruct(arena, float);
*thingf = 10.0f;
}

122
test/test_array.cpp Normal file
View File

@@ -0,0 +1,122 @@
#include "../io.c"
#define MA_ASSERT(x) IO_Assert(x)
#include "../arena.c"
#include "../defer.hpp"
#define ARRAY_REALLOCATE(allocator, p, size, old_size) M_ReallocNonZeroed(allocator, p, size, old_size)
#define ARRAY_DEALLOCATE(allocator, p) M_Dealloc(allocator, p)
#define ARRAY_Allocator M_Allocator
#define ARRAY_SET_DEFAULT_ALLOCATOR \
if (!allocator.p) allocator = M_GetSystemAllocator();
#include "../array.hpp"
void TestExclusiveArenaBackedArray() {
MA_Scratch scratch;
MA_Arena ex = MA_Create();
Array<int> array = {MA_GetExclusiveAllocator(&ex)};
Array<int *> ptrs = {scratch};
array.reserve(16);
ptrs.reserve(16);
void *initial_p0 = array.data;
void *initial_p1 = ptrs.data;
for (int i = 0; i < 1000; i += 1) {
array.add(i);
ptrs.add(&array[i]);
}
for (int i = 0; i < 1000; i += 1) {
IO_Assert(array[i] == i);
IO_Assert(&array[i] == ptrs[i]);
}
int i = 0;
For(array) {
IO_Assert(it == i++);
}
IO_Assert(initial_p0 == array.data);
IO_Assert(initial_p1 != ptrs.data);
array.dealloc();
}
Array<int> GenArray() {
Array<int> result = {M_GetSystemAllocator()};
for (int i = 0; i < 100; i += 1) result.add(i);
return result;
}
void TestRemoveForLoop() {
Array<int> array = GenArray();
IO_Assert(array.len == 100);
IO_Assert(array[4] == 4);
ForArrayRemovable(array) {
ForArrayRemovablePrepare(array);
if (it == 4) ForArrayRemovableDeclare();
}
IO_Assert(array[4] != 4);
IO_Assert(array[4] == 5);
IO_Assert(array[5] == 6);
IO_Assert(array[3] == 3);
IO_Assert(array.len == 99);
}
void TestBasic() {
Array<int> array = GenArray();
array.unordered_remove_index(40);
IO_Assert(array.len == 99);
IO_Assert(array[40] != 40);
IO_Assert(array[40] == 99);
array.ordered_remove_index(35);
IO_Assert(array.len == 98);
IO_Assert(array[35] == 36);
array.ordered_remove_index(array[34]);
IO_Assert(array.len == 97);
IO_Assert(array[34] == 36);
array.unordered_remove(array[30]);
IO_Assert(array.len == 96);
IO_Assert(array[30] != 30);
IO_Assert(array[30] == 98);
array.insert(101, 20);
IO_Assert(array[20] == 101);
IO_Assert(array[21] == 20);
IO_Assert(array.len == 97);
IO_Assert(array.contains(array[20]));
IO_Assert(array.contains(array[96]));
IO_Assert(array.contains(array[0]));
IO_Assert(!array.contains(array.data[97]));
IO_Assert(array.is_first(array[0]));
IO_Assert(!array.is_first(array[1]));
IO_Assert(!array.is_last(array[0]));
IO_Assert(array.is_last(array[array.len - 1]));
array.reset();
IO_Assert(array.len == 0);
array.dealloc();
}
void TestReverseLoop() {
Array<int> array = GenArray();
int i = 99;
For(array.reverse()) {
assert(it == i--);
}
array.dealloc();
}
int main() {
TestExclusiveArenaBackedArray();
TestRemoveForLoop();
TestBasic();
TestReverseLoop();
return 0;
}