0
Fork 0
data-structures-lesson1/base.h

635 lines
18 KiB
C

#ifndef BASE_H
#define BASE_H
#if !defined(MR4TH_SYMBOL)
# define MR4TH_SYMBOL static
#endif
// 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
#if COMPILER_CL
# define MR4TH_THREADVAR __declspec(thread)
#elif COMPILER_CLANG || COMPILER_GCC
# define MR4TH_THREADVAR __thread
#else
# error MR4TH_THREADVAR not defined for this compiler
#endif
// setup pointer size macro
#if ARCH_X64 || ARCH_ARM64
# define ARCH_ADDRSIZE 64
#else
# define ARCH_ADDRSIZE 32
#endif
////////////////////////////////
// Macros: Assert
#if !defined(AssertBreak)
# define AssertBreak() (*(volatile int*)0 = 0)
#endif
#define Assert(c) do{ if (!(c)){ AssertBreak(); } }while(0)
#define StaticAssert(c,l) typedef U8 Glue(l,__LINE__) [(c)?1:-1]
////////////////////////////////
// Macros: Memory
#include <string.h>
#define MemoryZero(p,z) memset((p), 0, (z))
#define MemoryZeroStruct(p) MemoryZero((p), sizeof(*(p)))
#define MemoryZeroArray(p) MemoryZero((p), sizeof(p))
#define MemoryZeroTyped(p,c) MemoryZero((p), sizeof(*(p))*(c))
#define MemoryMatch(a,b,z) (memcmp((a),(b),(z)) == 0)
#define MemoryCopy(d,s,z) memmove((d), (s), (z))
#define MemoryCopyStruct(d,s) MemoryCopy((d),(s),Min(sizeof(*(d)),sizeof(*(s))))
#define MemoryCopyArray(d,s) MemoryCopy((d),(s),Min(sizeof(s),sizeof(d)))
#define MemoryCopyTyped(d,s,c) MemoryCopy((d),(s),Min(sizeof(*(d)),sizeof(*(s)))*(c))
////////////////////////////////
// Macros: Linked Lists
#define DLLPushBack_NPZ(f,l,n,next,prev,nil)\
(((f) == (nil))?\
((f)=(l)=(n),(n)->next=(n)->prev=(nil)):\
((n)->prev=(l),(l)->next=(n),(l)=(n),(n)->next=(nil)))
#define DLLPushBack(f,l,n) DLLPushBack_NPZ(f,l,n,next,prev,0)
#define DLLPushFront(f,l,n) DLLPushBack_NPZ(l,f,n,prev,next,0)
#define DLLInsert_NPZ(f,l,p,n,next,prev,nil) \
(((p) != (l))?\
((n)->next = (p)->next,\
(n)->prev = (p),\
(p)->next->prev = (n),\
(p)->next = (n))\
:((n)->next = (nil),\
(n)->prev = (l),\
(l)->next = (n),\
(l) = (n)))
#define DLLInsert(f,l,p,n) DLLInsert_NPZ(f,l,p,n,next,prev,0)
#define DLLRemove_NPZ(f,l,n,next,prev,nil)\
((f)==(n)?\
((f)==(l)?\
((f)=(l)=(nil)):\
((f)=(f)->next,(f)->prev=(nil))):\
(l)==(n)?\
((l)=(l)->prev,(l)->next=(nil)):\
((n)->next->prev=(n)->prev,\
(n)->prev->next=(n)->next))
#define DLLRemove(f,l,n) DLLRemove_NPZ(f,l,n,next,prev,0)
#define SLLQueuePush_NZ(f,l,n,next,nil) (((f)==(nil)?\
(f)=(l)=(n):\
((l)->next=(n),(l)=(n))),\
(n)->next=(nil))
#define SLLQueuePush(f,l,n) SLLQueuePush_NZ(f,l,n,next,0)
#define SLLQueuePushFront_NZ(f,l,n,next,nil) ((f)==(nil)?\
((f)=(l)=(n),(n)->next=(nil)):\
((n)->next=(f),(f)=(n)))
#define SLLQueuePushFront(f,l,n) SLLQueuePushFront_NZ(f,l,n,next,0)
#define SLLQueuePop_NZ(f,l,next,nil) ((f)==(l)?\
(f)=(l)=(nil):\
((f)=(f)->next))
#define SLLQueuePop(f,l) SLLQueuePop_NZ(f,l,next,0)
#define SLLStackPush_N(f,n,next) ((n)->next=(f),(f)=(n))
#define SLLStackPush(f,n) SLLStackPush_N(f,n,next)
#define SLLStackPop_NZ(f,next,nil) ((f)==(nil)?(nil):\
((f)=(f)->next))
#define SLLStackPop(f) SLLStackPop_NZ(f,next,0)
////////////////////////////////
// Macros: Common Expressions
#define ArrayCount(a) (sizeof(a)/sizeof(*(a)))
#define IntFromPtr(p) (UAddr)(p)
#define PtrFromInt(n) (void*)((UAddr)(n))
#define PtrDif(a,b) ((U8*)(a) - (U8*)(b))
#define AlignUpPow2(x,p) (((x) + (p) - 1)&~((p) - 1))
#define AlignDownPow2(x,p) ((x)&~((p) - 1))
#define IsPow2OrZero(x) (((x)&((x)-1)) == 0)
#define Min(a,b) (((a)<(b))?(a):(b))
#define Max(a,b) (((a)>(b))?(a):(b))
#define Clamp(a,x,b) (((x)<(a))?(a):\
((b)<(x))?(b):(x))
#define ClampTop(a,b) Min(a,b)
#define ClampBot(a,b) Max(a,b)
#define KB(x) ((U64)(x) << 10)
#define MB(x) ((U64)(x) << 20)
#define GB(x) ((U64)(x) << 30)
#define TB(x) ((U64)(x) << 40llu)
////////////////////////////////
// Types: Basic
#include <stdint.h>
#include <stdarg.h>
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;
#if ARCH_ADDRSIZE == 32
typedef U32 UAddr;
typedef S32 SAddr;
#elif ARCH_ADDRSIZE == 64
typedef U64 UAddr;
typedef S64 SAddr;
#else
# error UAddr and SAddr not defined for this architecture
#endif
typedef void VoidFunc(void);
////////////////////////////////
// Types: Arena
typedef struct Arena{
struct Arena *current;
struct Arena *prev;
U64 alignment;
B8 growing;
U8 filler[7];
U64 base_pos;
U64 chunk_cap;
U64 chunk_pos;
U64 chunk_commit_pos;
} Arena;
typedef struct ArenaTemp{
Arena *arena;
U64 pos;
} ArenaTemp;
////////////////////////////////
// Types: String
typedef struct String8{
U8 *str;
U64 size;
} String8;
////////////////////////////////
// Types: Stream
#include <stdio.h>
typedef void STREAM_Handle;
typedef struct STREAM_Node{
struct STREAM_Node *next;
U64 size;
U8 data[0];
} STREAM_Node;
#define STREAM_NODE_FROM_DATA(d) (STREAM_Node*)((U8*)(d) - sizeof(STREAM_Node))
typedef struct STREAM{
Arena *arena;
U64 clear_pos;
STREAM_Node *first_node;
STREAM_Node *last_node;
U64 buffer_cap;
U64 total_size;
STREAM_Node *unused_node;
} STREAM;
#define STREAM_ALLOC_SIZE MB(16)
////////////////////////////////
// Functions: Arenas
// arena core
MR4TH_SYMBOL Arena* arena_alloc_reserve(U64 reserve_size, B32 growing);
MR4TH_SYMBOL Arena* arena_alloc(void);
MR4TH_SYMBOL void arena_release(Arena *arena);
MR4TH_SYMBOL void* arena_push_no_zero(Arena *arena, U64 size);
MR4TH_SYMBOL void arena_pop_to(Arena *arena, U64 pos);
MR4TH_SYMBOL U64 arena_current_pos(Arena *arena);
MR4TH_SYMBOL void* arena_push(Arena *arena, U64 size);
MR4TH_SYMBOL void arena_align(Arena *arena, U64 pow2_align);
MR4TH_SYMBOL void arena_pop_amount(Arena *arena, U64 amount);
#define push_array(a,T,c) (T*)arena_push((a), sizeof(T)*(c))
#define push_array_no_zero(a,T,c) (T*)arena_push_no_zero((a), sizeof(T)*(c))
#define push_array_copy(a,T,s,c) \
(T*)memory_move(push_array_no_zero(a,T,c), (s), sizeof(T)*(c))
// arena temp
MR4TH_SYMBOL ArenaTemp arena_begin_temp(Arena *arena);
MR4TH_SYMBOL void arena_end_temp(ArenaTemp *temp);
// scratch
MR4TH_SYMBOL ArenaTemp arena_get_scratch(Arena **conflict_array, U32 count);
#define arena_release_scratch(temp) arena_end_temp(temp)
////////////////////////////////
// Functions: Strings
MR4TH_SYMBOL String8 str8(U8 *str, U64 size);
MR4TH_SYMBOL String8 str8_range(U8 *first, U8 *opl);
MR4TH_SYMBOL String8 str8_cstring(U8 *cstr);
MR4TH_SYMBOL String8 str8_cstring_capped(U8 *cstr, U8 *opl);
#define str8_lit(s) str8((U8*)(s), sizeof(s) - 1)
#define str8_struct(s) str8((U8*)(s), sizeof(*(s)))
#define str8_array(s) str8((U8*)(s), sizeof(s))
#define str8_lit_const(s) { (U8*)(s), sizeof(s) - 1 }
MR4TH_SYMBOL U32 u32_from_str8_base10(String8 string);
////////////////////////////////
// Functions: Stream
// stream core
MR4TH_SYMBOL STREAM* stream_new(void);
MR4TH_SYMBOL void stream_release(STREAM *stream);
MR4TH_SYMBOL void stream_clear(STREAM *stream);
MR4TH_SYMBOL void stream_write(STREAM *stream, String8 data);
MR4TH_SYMBOL U8* stream_allocate(STREAM *stream, U64 size);
MR4TH_SYMBOL U64 stream_total_size(STREAM *stream);
MR4TH_SYMBOL STREAM_Handle* stream_next_chunk(STREAM *stream, STREAM_Handle *handle, String8 *chunk_out);
// stream helpers
MR4TH_SYMBOL String8 stream_read(Arena *arena, STREAM *stream);
MR4TH_SYMBOL void stream_printfv(STREAM *stream, char *fmt, va_list args);
MR4TH_SYMBOL void stream_printf(STREAM *stream, char *fmt, ...);
MR4TH_SYMBOL void stream_fprint(FILE *file, STREAM *stream);
////////////////////////////////
// Implementable Functions: Memory Functions
MR4TH_SYMBOL void* os_memory_reserve(U64 size);
MR4TH_SYMBOL B32 os_memory_commit(void *ptr, U64 size);
MR4TH_SYMBOL void os_memory_decommit(void *ptr, U64 size);
MR4TH_SYMBOL void os_memory_release(void *ptr, U64 size);
#if OS_WINDOWS
////////////////////////////////
// Win32: Includes
#include <Windows.h>
#include <userenv.h>
#include <psapi.h>
#elif OS_LINUX
////////////////////////////////
// Linux: Includes
#include <sys/mman.h>
// OS Missing
#else
# error not implemented on this OS
#endif
////////////////////////////////////////////////
////////////////////////////////////////////////
/// stb_sprintf.h STB_SPRINTF_H_INCLUDE ////
////////////////////////////////////////////////
////////////////////////////////////////////////
#define STB_SPRINTF_DECORATE(name) m4_##name
/*
Single file sprintf replacement.
Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
Hereby placed in public domain.
This is a full sprintf replacement that supports everything that
the C runtime sprintfs support, including float/double, 64-bit integers,
hex floats, field parameters (%*.*d stuff), length reads backs, etc.
Why would you need this if sprintf already exists? Well, first off,
it's *much* faster (see below). It's also much smaller than the CRT
versions code-space-wise. We've also added some simple improvements
that are super handy (commas in thousands, callbacks at buffer full,
for example). Finally, the format strings for MSVC and GCC differ
for 64-bit integers (among other small things), so this lets you use
the same format strings in cross platform code.
It uses the standard single file trick of being both the header file
and the source itself. If you just include it normally, you just get
the header file function definitions. To get the code, you include
it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
It only uses va_args macros from the C runtime to do it's work. It
does cast doubles to S64s and shifts and divides U64s, which does
drag in CRT code on most platforms.
It compiles to roughly 8K with float support, and 4K without.
As a comparison, when using MSVC static libs, calling sprintf drags
in 16K.
API:
====
int stbsp_sprintf( char * buf, char const * fmt, ... )
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
Convert an arg list into a buffer. stbsp_snprintf always returns
a zero-terminated string (unlike regular snprintf).
int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns
a zero-terminated string (unlike regular snprintf).
int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
Your callback can then copy the chars out, print them or whatever.
This function is actually the workhorse for everything else.
The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
// you return the next buffer to use or 0 to stop converting
void stbsp_set_separators( char comma, char period )
Set the comma and period characters to use.
FLOATS/DOUBLES:
===============
This code uses a internal float->ascii conversion method that uses
doubles with error correction (double-doubles, for ~105 bits of
precision). This conversion is round-trip perfect - that is, an atof
of the values output here will give you the bit-exact double back.
One difference is that our insignificant digits will be different than
with MSVC or GCC (but they don't match each other either). We also
don't attempt to find the minimum length matching float (pre-MSVC15
doesn't either).
If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
and you'll save 4K of code space.
NOTE(allen): MODIFICATION - I've hard coded that this copy *does* use floats
64-BIT INTS:
============
This library also supports 64-bit integers and you can use MSVC style or
GCC style indicators (%I64d or %lld). It supports the C99 specifiers
for size_t and ptr_diff_t (%jd %zd) as well.
EXTRAS:
=======
Like some GCCs, for integers and floats, you can use a ' (single quote)
specifier and commas will be inserted on the thousands: "%'d" on 12345
would print 12,345.
For integers and floats, you can use a "$" specifier and the number
will be converted to float and then divided to get kilo, mega, giga or
tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
suffix, add "_" specifier: "%_$d" -> "2.53M".
In addition to octal and hexadecimal conversions, you can print
integers in binary: "%b" for 256 would print 100.
PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
===================================================================
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
"%24d" across all 32-bit ints (4.5x/4.2x faster)
"%x" across all 32-bit ints (4.5x/3.8x faster)
"%08x" across all 32-bit ints (4.3x/3.8x faster)
"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
"%f" for values near e-300 (7.9x/6.5x faster)
"%f" for values near e+300 (10.0x/9.1x faster)
"%e" for values near e-300 (10.1x/7.0x faster)
"%e" for values near e+300 (9.2x/6.0x faster)
"%.320f" for values near e-300 (12.6x/11.2x faster)
"%a" for random values (8.6x/4.3x faster)
"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
"%s%s%s" for 64 char strings (7.1x/7.3x faster)
"...512 char string..." ( 35.0x/32.5x faster!)
*/
#if defined(__clang__)
#if defined(__has_feature) && defined(__has_attribute)
#if __has_feature(address_sanitizer)
#if __has_attribute(__no_sanitize__)
#define STBSP__ASAN __attribute__((__no_sanitize__("address")))
#elif __has_attribute(__no_sanitize_address__)
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
#elif __has_attribute(__no_address_safety_analysis__)
#define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
#endif
#endif
#endif
#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
#endif
#endif
#ifndef STBSP__ASAN
#define STBSP__ASAN
#endif
#ifdef STB_SPRINTF_STATIC
#define STBSP__PUBLICDEC static
#define STBSP__PUBLICDEF static STBSP__ASAN
#else
#ifdef __cplusplus
#define STBSP__PUBLICDEC extern "C"
#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
#else
#define STBSP__PUBLICDEC extern
#define STBSP__PUBLICDEF STBSP__ASAN
#endif
#endif
#if defined(__has_attribute)
#if __has_attribute(format)
#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va)))
#endif
#endif
#ifndef STBSP__ATTRIBUTE_FORMAT
#define STBSP__ATTRIBUTE_FORMAT(fmt,va)
#endif
#ifdef _MSC_VER
#define STBSP__NOTUSED(v) (void)(v)
#else
#define STBSP__NOTUSED(v) (void)sizeof(v)
#endif
#include <stdarg.h> // for va_arg(), va_list()
#include <stddef.h> // size_t, ptrdiff_t
#ifndef STB_SPRINTF_MIN
#define STB_SPRINTF_MIN 512 // how many characters per callback
#endif
typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);
#ifndef STB_SPRINTF_DECORATE
#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
#endif
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
#endif //BASE_H