app_codebase/src/base/base_core.h
Anton Ljungdahl bbc1b91772 some dsa
2025-01-16 23:12:28 +01:00

295 lines
9.1 KiB
C

#ifndef BASE_TYPES_H
#define BASE_TYPES_H
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
/////////////////////////
//~ Macros
/////////////////////////
//- Linking keywords
// TODO(anton): Understand this, yoinked from rjf's layer.
#if LANG_CPP
# define no_name_mangle extern "C"
#else
# define no_name_mangle
#endif
// TODO(anton): OS_WINDOWS dll import/export macros
/////////////////////////
//- Keywords
// Static is stupid and means different things depending on context in C and C++.
// These defines increases readability.
#define function static // Function internal to compilation unit.
#define local_persist static
#define global static
#define fallthrough // for use in switch statements, for clarity..
// TODO(anton): Understand and add good comment on this.
#if LANG_CPP
# define root_global no_name_mangle
# define root_function function
#else
# define root_global extern
# define root_function function
#endif
#define inline_function inline static
#if OS_WINDOWS
# pragma section(".roglob", read)
# define read_only __declspec(allocate(".roglob"))
#else
# define read_only
#endif
#if COMPILER_MSVC
# define per_thread __declspec(thread)
#else
# error Thread keyword not abstracted on compiler.
#endif
/////////////////////////
//- Memory operations
// It's nice to put these in macros, so we can swap out the functionality from standard library, eventually.
#define MemoryCopy memcpy
#define MemoryMove memmove
#define MemorySet memset
// NOTE(anton): This gives a 4127 compiler warning for the sizeof conditional. This should be ignored
#define MemoryCopyStruct(dst, src) do { Assert(sizeof(*(dst)) == sizeof(*(src))); MemoryCopy((dst), (src), sizeof(*(dst))); } while(0)
#define MemoryZero(ptr, size) MemorySet((ptr), 0, (size))
#define MemoryZeroStruct(ptr) MemoryZero((ptr), sizeof(*(ptr)))
#define MemoryZeroArray(arr) MemoryZero((arr), sizeof(arr))
/////////////////////////
//- Integer/pointer/array/type manipulations
#define ArrayCount(a) (sizeof(a) / sizeof((a)[0]))
#define IntFromPtr(p) (U64)(((U8*)p) - 0)
#define PtrFromInt(i) (void*)(((U8*)0) + i)
#define Member(type, member_name) ((type *)0)->member_name
// TODO(anton): Understand why this becomes offset actually
#define OffsetOf(type, member_name) IntFromPtr(&Member(type, member_name))
// TODO(anton): Understand this
#define BaseFromMember(type, member_name, ptr) (type *)((U8 *)(ptr) - OffsetOf(type, member_name))
#define Bytes(n) (n)
#define Kilobytes(n) (n << 10) // 2^10 == 1024 etc
#define Megabytes(n) (n << 20)
#define Gigabytes(n) (((U64)n) << 30)
#define Terabytes(n) (((U64)n) << 40)
#define Thousand(n) ((n)*1000)
#define Million(n) ((n)*1000000)
#define Billion(n) ((n)*1000000000LL)
#define AbsoluteValueU64(x) (U64)llabs((U64)(x))
/////////////////////////
//- Linked list helpers
#define CheckNull(p) ((p)==0)
#define SetNull(p) ((p)=0)
// Link list helper macros that are a bit involved
// Suffixes N,P,Z means that we have (N)ext, (P)rev arguments and/or a (Z)ero check and/or set argument
// f, l, n are "first", "last", "node" I think?
// DLL
// Doubly Linked List: Each node has a prev and next pointer. Operations: Push back, Push front, remove
#define DLLInsert_NPZ(f,l,p,n,next,prev,zchk,SetNull) \
(zchk(f) ? (((f) = (l) = (n)), SetNull((n)->next), SetNull((n)->prev)) :\
zchk(p) ? (SetNull((n)->prev), (n)->next = (f), (zchk(f) ? (0) : ((f)->prev = (n))), (f) = (n)) :\
((zchk((p)->next) ? (0) : (((p)->next->prev) = (n))), (n)->next = (p)->next, (n)->prev = (p), (p)->next = (n),\
((p) == (l) ? (l) = (n) : (0))))
#define DLLPushBack_NPZ(f,l,n,next,prev,zchk,SetNull) DLLInsert_NPZ(f,l,l,n,next,prev,zchk,SetNull)
#define DLLPushBack_NP(f, l, n, next, prev, zchk) \
(zchk(f) ? ((f)=(l)=(n),(n)->next=(n)->prev=0) : ((n)->prev=(l),(l)->next=(n),(l)=(n),(n)->next=0))
// If f == n we put f to f->next, and f->prev = 0.
// Else if l == n, we put l=l->prev, l->next = 0.
// If l != n and f != n we set n->next->prev to n->prev, and n->prev->next to n->next
#define DLLRemove_NP(f, l, n, next, prev) (((f) == (n) ? \
((f)=(f)->next, (f)->prev=0) : \
(l) == (n) ? \
((l)=(l)->prev, (l)->next=0) : \
((n)->next->prev=(n)->prev, \
(n)->prev->next=(n)->next) ))
#define DLLRemove_NPZ(f,l,n,next,prev,zchk,SetNull) (((f)==(n))?\
((f)=(f)->next, (zchk(f) ? (SetNull(l)) : SetNull((f)->prev))):\
((l)==(n))?\
((l)=(l)->prev, (zchk(l) ? (SetNull(f)) : SetNull((l)->next))):\
((zchk((n)->next) ? (0) : ((n)->next->prev=(n)->prev)),\
(zchk((n)->prev) ? (0) : ((n)->prev->next=(n)->next))))
#define DLLPushBack(f, l, n) DLLPushBack_NPZ(f, l, n, next, prev, CheckNull, SetNull)
// For front push I can just switch prev/next!
#define DLLPushFront(f, l, n) DLLPushBack_NPZ(l, f, n, prev, next, CheckNull, SetNull)
#define DLLRemove(f, l, n) DLLRemove_NPZ(f, l, n, next, prev, CheckNull, SetNull)
// SLL, queue or stack.
// These are from rjf's layer.
////////////////
// Queue
// Queue has only a next pointer. But we can push from front also.
// zchk = zero check, SetNull = zero set
#define QueuePush_NZ(f, l, n, next, zchk, SetNull) (zchk(f)?\
(((f)=(l)=(n)), SetNull((n)->next)):\
((l)->next=(n),(l)=(n),SetNull((n)->next)))
#define QueuePushFront_NZ(f, l, n, next, zchk, SetNull) ( zchk(f) ? \
((f)=(l)=(n)), SetNull((n)->next) : \
((n)->next = (f)), ((f) = (n)) )
#define QueuePop_NZ(f, l, next, zchk, SetNull) ( (f)==(l) ? \
(SetNull(f), SetNull(l)) : ((f)=(f)->next))
#define QueuePush(f, l, n) QueuePush_NZ(f, l, n, next, CheckNull, SetNull)
#define QueuePushFront(f, l, n) QueuePushFront_NZ(f, l, n, next, CheckNull, SetNull)
#define QueuePop(f, l) QueuePop_NZ(f, l, next, CheckNull, SetNull)
////////////////
// Stack
#define StackPush_N(f, n, next) ((n)->next=(f), (f)=(n)) // Take the first element and set it to n->next, and set the first element to the node n.
#define StackPop_NZ(f, next, zchk) (zchk(f) ? 0 : ((f)=(f)->next)) // If first element is not zero we say that the first element is f->next, ie we pop f and put f->next on top.
#define StackPush(f, n) StackPush_N(f, n, next)
#define StackPop(f) StackPop_NZ(f, next, CheckNull)
/////////////////////////
//- Clamp/min/max
#define Min(a, b) (((a)<(b)) ? (a) : (b))
#define Max(a, b) (((a)>(b)) ? (a) : (b))
#define ClampTop(x, a) Min(x,a) // "Top" since we are cutting off anything above Min(x,a)
#define ClampBot(a, x) Max(a,x) // "Bot" since we're cutting off anything below Max(a,x)
// If a > x we get a, else we see if b < x and then get b if true, else x.
// TODO(anton): Is this actually what we want from a Clamp?
#define Clamp(a, x, b) (((a)>(x))?(a):((b)<(x))?(b):(x))
//- loop
#define DeferLoop(start, end) for(int _i_ = ((start), 0); _i_ == 0; _i_ += 1, (end))
#define DeferLoopChecked(begin, end) for(int _i_ = 2 * !(begin); (_i_ == 2 ? ((end), 0) : !_i_); _i_ += 1, (end))
#define EachEnumVal(type, it) type it = (type)0; it < type##_COUNT; it = (type)(it+1)
#define EachNonZeroEnumVal(type, it) type it = (type)1; it < type##_COUNT; it = (type)(it+1)
/////////////////////////
//~ Base types
typedef int8_t S8;
typedef int16_t S16;
typedef int32_t S32;
typedef int64_t S64;
typedef uint8_t U8;
typedef uint16_t U16;
typedef uint32_t U32;
typedef uint64_t U64;
typedef S8 B8;
typedef S16 B16;
typedef S32 B32;
typedef S64 B64;
typedef float F32;
typedef double F64;
typedef void VoidFunction(void);
/////////////////////////
//~ Numerical limits
read_only global U8 U8Max = 0xFF;
read_only global U8 U8Min = 0;
read_only global U32 U32Max = 0xFFFFFFFF;
read_only global U32 U32Min = 0;
read_only global U64 U64Max = 0xFFFFFFFFFFFFFFFF;
// TODO(anton): Rest of the limits, unsigned and signed integer values
read_only global U32 SignF32 = 0x80000000;
//- compiler, shut up! helpers
#define unused_variable(name) (void)name
/////////////////////////
//~ Base enums
// Describing a 2-coordinate system
typedef enum Axis2
{
Axis2_Invalid = -1,
Axis2_X,
Axis2_Y,
Axis2_COUNT
}
Axis2;
#define Axis2_flip(a) ((Axis2)(!(a)))
// Corners of a rectangle.
// 00 ----- 10
// | |
// 01 ----- 11
typedef enum Corner
{
Corner_Invalid = -1,
Corner_00,
Corner_01,
Corner_10,
Corner_11,
Corner_COUNT
}
Corner;
////////////////////////////////
//~ Member Offset Helper
typedef struct MemberOffset MemberOffset;
struct MemberOffset
{
U64 v;
};
#define MemberOff(S, member) (MemberOffset){OffsetOf(S, member)}
#define MemberOffLit(S, member) {OffsetOf(S, member)}
#define MemberFromOff(ptr, type, memoff) (*(type *)((U8 *)ptr + memoff.v))
/////////////////////////
//~ Assertions
#if OS_WINDOWS
# define break_debugger() __debugbreak()
#else
# error not implemented
#endif
#undef Assert
#define Assert(b) do { if(!(b)) { break_debugger(); } } while(0)
#if !defined(LOG_NOT_IMPLEMENTED)
# define LOG_NOT_IMPLEMENTED printf("\nFATAL ERROR: Not implemented yet.\n"); Assert(false); exit(1);
#endif
/////////////////////////
//~ Bit patterns
#define AlignUpToPow2(bytes_to_align, alignment_bytes) (((bytes_to_align) + (alignment_bytes - 1)) & ~(alignment_bytes - 1))
inline_function F32
absolute_value_F32(F32 f)
{
union { U32 u; F32 f; } x;
x.f = f;
x.u = x.u & ~SignF32;
return x.f;
}
// TODO(anton): Understand rjf's bit patterns
#endif //BASE_TYPES_H