memory arena, allocator struct, os alloc and abort, strings, broken build script

This commit is contained in:
Abdulmujeeb Raji 2025-01-17 09:36:09 +00:00
parent 2c9ce97a0e
commit cbb34b745c
Signed by: midnadimple
GPG key ID: EB02C582F8C3962B
9 changed files with 420 additions and 31 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
*.exe
*.ilk
*.obj
*.pdb

175
bastd.c
View file

@ -1,6 +1,160 @@
#ifndef BASTD_C #ifndef BASTD_C
#define BASTD_C #define BASTD_C
////////////////////////////////
// NOTE(allen): Context Cracking
// NOTE(allen): Untangle Compiler, OS & Architecture
#if defined(__clang__)
# define COMPILER_CLANG 1
# if defined(_WIN32)
# define OS_WINDOWS 1
# elif defined(__gnu_linux__)
# define OS_LINUX 1
# elif defined(__APPLE__) && defined(__MACH__)
# define OS_MAC 1
# else
# error missing OS detection
# endif
# if defined(__amd64__)
# define ARCH_X64 1
// TODO(allen): verify this works on clang
# elif defined(__i386__)
# define ARCH_X86 1
// TODO(allen): verify this works on clang
# elif defined(__arm__)
# define ARCH_ARM 1
// TODO(allen): verify this works on clang
# elif defined(__aarch64__)
# define ARCH_ARM64 1
# else
# error missing ARCH detection
# endif
#elif defined(_MSC_VER)
# define COMPILER_CL 1
# if defined(_WIN32)
# define OS_WINDOWS 1
# else
# error missing OS detection
# endif
# if defined(_M_AMD64)
# define ARCH_X64 1
# elif defined(_M_I86)
# define ARCH_X86 1
# elif defined(_M_ARM)
# define ARCH_ARM 1
// TODO(allen): ARM64?
# else
# error missing ARCH detection
# endif
#elif defined(__GNUC__)
# define COMPILER_GCC 1
# if defined(_WIN32)
# define OS_WINDOWS 1
# elif defined(__gnu_linux__)
# define OS_LINUX 1
# elif defined(__APPLE__) && defined(__MACH__)
# define OS_MAC 1
# else
# error missing OS detection
# endif
# if defined(__amd64__)
# define ARCH_X64 1
# elif defined(__i386__)
# define ARCH_X86 1
# elif defined(__arm__)
# define ARCH_ARM 1
# elif defined(__aarch64__)
# define ARCH_ARM64 1
# else
# error missing ARCH detection
# endif
#else
# error no context cracking for this compiler
#endif
#if !defined(COMPILER_CL)
# define COMPILER_CL 0
#endif
#if !defined(COMPILER_CLANG)
# define COMPILER_CLANG 0
#endif
#if !defined(COMPILER_GCC)
# define COMPILER_GCC 0
#endif
#if !defined(OS_WINDOWS)
# define OS_WINDOWS 0
#endif
#if !defined(OS_LINUX)
# define OS_LINUX 0
#endif
#if !defined(OS_MAC)
# define OS_MAC 0
#endif
#if !defined(ARCH_X64)
# define ARCH_X64 0
#endif
#if !defined(ARCH_X86)
# define ARCH_X86 0
#endif
#if !defined(ARCH_ARM)
# define ARCH_ARM 0
#endif
#if !defined(ARCH_ARM64)
# define ARCH_ARM64 0
#endif
// NOTE(allen): Language
#if defined(__cplusplus)
# define LANG_CXX 1
#else
# define LANG_C 1
#endif
#if !defined(LANG_CXX)
# define LANG_CXX 0
#endif
#if !defined(LANG_C)
# define LANG_C 0
#endif
// NOTE(allen): Profiler
#if !defined(PROFILER_SPALL)
# define PROFILER_SPALL 0
#endif
// NOTE(allen): Determine Intrinsics Mode
#if OS_WINDOWS
# if COMPILER_CL || COMPILER_CLANG
# define INTRINSICS_MICROSOFT 1
# endif
#endif
#if !defined(INTRINSICS_MICROSOFT)
# define INTRINSICS_MICROSOFT 0
#endif
// NOTE(allen): Setup Pointer Size Macro
#if ARCH_X64 || ARCH_ARM64
# define ARCH_ADDRSIZE 64
#else
# define ARCH_ADDRSIZE 32
#endif
// Types
#include <stdint.h> #include <stdint.h>
typedef int8_t I8; typedef int8_t I8;
@ -15,17 +169,34 @@ typedef uint64_t U64;
typedef float F32; typedef float F32;
typedef double F64; typedef double F64;
typedef uintptr_t UPtr;
typedef ptrdiff_t ISize;
typedef size_t USize;
typedef U32 B32; typedef U32 B32;
#define TRUE 1 #define TRUE 1
#define FALSE 0 #define FALSE 0
// Macros
#define FUNCTION static #define FUNCTION static
#define GLOBAL_VAR static #define GLOBAL_VAR static
#define LOCAL_PERSIST static #define LOCAL_PERSIST static
#define STMNT(s) do{ s }while(0) #define STMNT(s) do{ s }while(0)
// TODO: Add Linux Version #define ASSERT(x, msg) STMNT(if (!x) OS_DEBUGBREAK();)
#define ASSERT(x, msg) STMNT(if (!x) __debugbreak();)
#define CONCAT_(a,b) a##b
#define CONCAT(a,b) CONCAT_(a,b)
#define NIL 0
#define COUNT_OF(a) (ISize)(sizeof(a) / sizeof(*(a)))
#define LENGTH_OF(s) (COUNT_OF(s) - 1)
// Includes
#include "bastd/string.c"
#include "bastd/os_windows.c"
#include "bastd/mem.c"
#endif //BASTD_C #endif //BASTD_C

116
bastd/mem.c Normal file
View file

@ -0,0 +1,116 @@
#ifndef BASTD_MEM_C
#define BASTD_MEM_C
FUNCTION void *
M_memorySet(void *buffer, U8 value, ISize length)
{
U8* p = buffer;
while (length-- > 0) {
*p++ = value;
}
return buffer;
}
FUNCTION void *
M_memoryCopy(void *dst, void *src, size_t n)
{
U8 *s = (U8 *)src;
U8 *d = (U8 *)dst;
for (I32 i = 0; i < n; i++) {
d[i] = s[i];
}
return dst;
}
/* Alloc function taken from Lua.
If ptr == NIL, old_size irrelevant
If ptr != NIL, old_size = size of block of memory at ptr
If new_size > 0, function like realloc and either resize or create
If new_size <= 0, function like free and clear the memory
*/
typedef void *(*M_AllocFunc)(void *ctx, void *ptr, ISize old_size, ISize new_size);
typedef struct M_Allocator M_Allocator;
struct M_Allocator {
M_AllocFunc alloc;
void* ctx;
};
#define M_MAKE(T, n, a) ((T *)((a).alloc((a).ctx, NIL, 0, sizeof(T) * n)))
#define M_RESIZE(p, o, n, a) ((a).alloc((a).ctx, p, sizeof(*(p)) * o, sizeof(*(p)) * n))
#define M_RELEASE(p, s, a) ((a).alloc((a).ctx, p, sizeof(*(p)) * s, 0))
/* Linear Allocator (Arena)
Allocates variably-sized regions from a fixed-size block of memory.
A set of regions is freed by setting the allocator's offset to an earlier
value.
*/
typedef struct M_Arena M_Arena;
struct M_Arena {
U8* beg;
U8* end;
};
#define DEFAULT_ALIGNMENT (2 * sizeof(void *))
FUNCTION void *
M_Arena_alloc(void *ctx, void *ptr, ISize old_size, ISize new_size)
{
M_Arena *a = (M_Arena *)ctx;
if (new_size <= 0) {
/* Arena can only free the most recent block. This allows to follow stack pattern.
What we do is move the end pointer forward by the size of the block
Then, because there is now more available space, the data will be
ignored following the next allocation.
*/
ISize padding = -old_size & (DEFAULT_ALIGNMENT - 1);
a->end += old_size + padding;
return NIL;
} else {
// Allocate a block
ASSERT(new_size > old_size, "Can't reallocate to a smaller block");
ISize padding = -(UPtr)a->beg & (DEFAULT_ALIGNMENT - 1);
ISize available = a->end - a->beg - padding;
if (available < 0 || new_size > available) {
OS_Abort(S8("Out of Memory!"));
}
void *p = NIL;
if (a->beg == (U8 *)ptr + old_size) {
// This was the last allocated block, can extend allocation for realloc
p = ptr;
a->beg += padding + new_size;
} else {
// New allocation;
p = a->beg + padding;
a->beg += padding + new_size;
p = M_memorySet(p, 0, new_size);
if (ptr != NIL) {
// Arbitrary block, copy data from old pointer
p = M_memoryCopy(p, ptr, old_size);
}
}
return p;
}
}
FUNCTION M_Arena
M_Arena_create(void* buffer, ISize capacity)
{
M_Arena arena = {0};
arena.beg = (U8 *)buffer;
arena.end = arena.beg ? arena.beg + capacity : 0;
return arena;
}
#define M_ARENA_ALLOCATOR(a) (M_Allocator){M_Arena_alloc, (void *)&(a)}
#endif//BASTD_MEM_C

17
bastd/os.c Normal file
View file

@ -0,0 +1,17 @@
#ifndef BASTD_OS_C
#define BASTD_OS_C
FUNCTION void OS_Abort(S8 msg);
FUNCTION void *OS_Alloc(ISize cap);
#if defined(OS_WINDOWS)
#include "os_windows.c"
#elif defined(OS_LINUX)
#include "os_linux.c"
#endif//OS_WINDOWS
#endif//BASTD_OS_C

23
bastd/os_windows.c Normal file
View file

@ -0,0 +1,23 @@
#ifndef BASTD_OS_WINDOWS_C
#define BASTD_OS_WINDOWS_C
#include <windows.h>
#define OS_DEBUGBREAK() __debugbreak();
FUNCTION void
OS_Abort(S8 msg)
{
HANDLE stderr = GetStdHandle(-12);
U32 dummy;
WriteFile(stderr, msg.raw, msg.length, &dummy, 0);
ExitProcess(101);
}
FUNCTION void *
OS_Alloc(ISize cap)
{
return VirtualAlloc(NIL, cap, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
}
#endif//BASTD_OS_WINDOWS_C

12
bastd/string.c Normal file
View file

@ -0,0 +1,12 @@
#ifndef BASTD_STRING_C
#define BASTD_STRING_C
typedef struct S8 S8;
struct S8 {
ISize length;
U8 *raw;
};
#define S8(s) (S8){(U8 *)s, LENGTH_OF(s)}
#endif//BASTD_STRING_C

8
build.bat Normal file
View file

@ -0,0 +1,8 @@
@echo off
if not exist build\ mkdir build
pushd build
cl /Zi ..\main.c
popd

38
examples/memory.c Normal file
View file

@ -0,0 +1,38 @@
int
main(void)
{
ISize buffer_size = 100 * sizeof(U8);
M_Arena arena = M_Arena_create(OS_Alloc(buffer_size), buffer_size);
M_Allocator permanent_allocator = M_ARENA_ALLOCATOR(arena);
{
/* By cloning an arena at the start of the scope, all changes to the
arena are now local to the scope, so all allocations with be freed
at the end automatically.
*/
M_Arena scratch_arena = arena;
M_Allocator scratch_allocator = M_ARENA_ALLOCATOR(scratch_arena);
U8* array = M_MAKE(U8, 20, scratch_allocator);
for (int i = 0; i < 20; i++) {
array[i] = i;
}
} // Auto clears here
OS_DEBUGBREAK();
U8* array = M_MAKE(U8, 10, permanent_allocator);
for (int i = 10; i-- > 0;) {
array[10 - i] = i;
}
OS_DEBUGBREAK();
array = M_RESIZE(array, 10, 20, permanent_allocator);
for (int i = 20; i-- > 0;) {
array[20 - i] = i;
}
OS_DEBUGBREAK();
M_RELEASE(array, 20, permanent_allocator);
OS_DEBUGBREAK();
}

58
main.c
View file

@ -5,16 +5,16 @@
Line */ Line */
// "GLOBAL_VAR" macro expands to "static" // "GLOBAL_VAR" macro expands to "static"
GLOBAL_VAR F32 evil_number = 666 // variables names use snake_case GLOBAL_VAR F32 evil_number = 666; // variables names use snake_case
typedef struct RenderVertex RenderVertex; // typedefs come before structs typedef struct RenderVertex RenderVertex; // typedefs come before structs
struct RenderVertex { // struct names use PascalCase struct RenderVertex { // struct names use PascalCase
F32 x, y, z F32 x, y, z;
} };
// Generic pattern. the output type will be "Slice_RenderVertex" // Generic pattern. the output type will be "Slice_RenderVertex"
#define SLICE_TYPE RenderVertex // #define SLICE_TYPE RenderVertex
#include "slice.h" // #include "slice.h"
// all functions prefixed with "FUNCTION" macro, which expands to "static" // all functions prefixed with "FUNCTION" macro, which expands to "static"
FUNCTION I8 // primitive types follow rust style FUNCTION I8 // primitive types follow rust style
@ -29,32 +29,32 @@ addTwo(I8 a, I8 b) // Function names use camelCase
defined as "static" , incase you decide to move it to a different defined as "static" , incase you decide to move it to a different
compilation unit compilation unit
*/ */
CALLBACK Error // CALLBACK Error
start(Slice_S8 args, Buffer stdin, Buffer stdout) // start(Slice_S8 args, Buffer stdin, Buffer stdout)
{ // {
/* all functions that belong to a type or a module are styled // /* all functions that belong to a type or a module are styled
"m_TypeName_functionName". "m" is a lower-case shortened version of the // "m_TypeName_functionName". "m" is a lower-case shortened version of the
module's name (usually 1-3 letters). // module's name (usually 1-3 letters).
*/ // */
Buffer_appendS8(&stdout, args[0]); // Buffer_appendS8(&stdout, args[0]);
Buffer_flush(&stdout); // Buffer_flush(&stdout);
r_Window window = r_Window_create(1280, 720, "Bastd Example"); // r_Window window = r_Window_create(1280, 720, "Bastd Example");
I32 frame_number = 0; // I32 frame_number = 0;
for (!window.should_close) { // for (!window.should_close) {
frame_number += 1; // frame_number += 1;
// if there is no module, function is styled "TypeName_functionName" // // if there is no module, function is styled "TypeName_functionName"
Buffer_appendS8(&stdout, "Current frame: "); // Buffer_appendS8(&stdout, "Current frame: ");
Buffer_appendI32(&stdout, frame_number); // Buffer_appendI32(&stdout, frame_number);
Buffer_appendChar(&stdout, '\n'); // Buffer_appendChar(&stdout, '\n');
Buffer_flush(&stdout); // Buffer_flush(&stdout);
r_Window_tick(&window); // r_Window_tick(&window);
} // }
/* Enum names are styled with PascalCase. Enum values are styled // /* Enum names are styled with PascalCase. Enum values are styled
"TypeName_value_name"*/ // "TypeName_value_name"*/
return Error_success; // return Error_success;
} // }