Nick Hayashi
2 years ago
commit
34e4fb08c3
16 changed files with 3802 additions and 0 deletions
-
206alloc.cpp
-
77alloc.h
-
275array.hpp
-
149cpuid.cpp
-
8cpuid.h
-
209file.cpp
-
49file.h
-
280print.cpp
-
155print.h
-
316serialize.cpp
-
208serialize.h
-
50signal-handler.h
-
979string.h
-
720table.hpp
-
111types.h
-
10util.h
@ -0,0 +1,206 @@ |
|||
|
|||
#include "alloc.h"
|
|||
#include "string.h"
|
|||
#include "print.h"
|
|||
#include "types.h"
|
|||
|
|||
|
|||
#if false
|
|||
static void* leakcheckMalloc(size_t size, const char* file, s32 line) { |
|||
TYPES_H_FTAG; |
|||
return malloc(size); |
|||
} |
|||
|
|||
static void* leakcheckCalloc(size_t maxNumOfElements, size_t elementSize, const char* file, s32 line) { |
|||
TYPES_H_FTAG; |
|||
return calloc(maxNumOfElements, elementSize); |
|||
} |
|||
|
|||
static void* leakcheckRealloc(void* buffer, size_t newSize, const char* file, s32 line) { |
|||
TYPES_H_FTAG; |
|||
return realloc(buffer, newSize); |
|||
} |
|||
|
|||
static void leakcheckFree(void* ptr, const char* file, s32 line) { |
|||
TYPES_H_FTAG; |
|||
free(ptr); |
|||
} |
|||
|
|||
#define malloc(size) leakcheckMalloc(size, __FILE__, __LINE__)
|
|||
#define calloc(numElements, size) leakcheckCalloc(numElements, size, __FILE__, __LINE__)
|
|||
#define realloc(buffer, newSize) leakcheckRealloc(buffer, newSize, __FILE__, __LINE__)
|
|||
#define free(ptr) leakcheckFree(ptr, __FILE__, __LINE__)
|
|||
|
|||
static void dumpLeaks() { |
|||
void* address; |
|||
size_t size; |
|||
const char* file; |
|||
s32 line; |
|||
for (;;) { |
|||
println("%p - %lu, %s:%d", address, size, file, line); |
|||
} |
|||
} |
|||
|
|||
#endif
|
|||
|
|||
// system allocators
|
|||
void* pMalloc(size_t size) { |
|||
TYPES_H_FTAG; |
|||
void* p = malloc(size); |
|||
|
|||
if (!p) { |
|||
die("Out of memory!n\nfailed to malloc %p with size %u\n", p, size); |
|||
} |
|||
|
|||
return p; |
|||
} |
|||
void* pMalloc(size_t size, void* allocatorState) { |
|||
TYPES_H_FTAG; |
|||
return pMalloc(size); |
|||
} |
|||
|
|||
void* pCalloc(size_t maxNumOfElements, size_t elementSize) { |
|||
TYPES_H_FTAG; |
|||
void* p = calloc(maxNumOfElements, elementSize); |
|||
|
|||
if (!p) { |
|||
die("Out of memory!\nfailed to calloc %p with %u elements of size %u\n", p, maxNumOfElements, elementSize); |
|||
} |
|||
|
|||
return p; |
|||
} |
|||
void* pCalloc(size_t maxNumOfElements, size_t elementSize, void* allocatorState) { |
|||
TYPES_H_FTAG; |
|||
return pCalloc(maxNumOfElements, elementSize); |
|||
} |
|||
|
|||
void* pRealloc(void* buffer, size_t newSize) { |
|||
TYPES_H_FTAG; |
|||
void* p = realloc(buffer, newSize); |
|||
|
|||
if (!p) { |
|||
//pFree(buffer); // if we ever *don't* terminate the program at this point, we should free |buffer|
|
|||
die("Out of memory!\nfailed to realloc %p with size: %u\n", buffer, newSize); |
|||
} |
|||
|
|||
return p; |
|||
} |
|||
void* pRealloc(void* buffer, size_t newSize, void* allocatorState) { |
|||
TYPES_H_FTAG; |
|||
return pRealloc(buffer, newSize); |
|||
} |
|||
|
|||
void pFree(void* ptr) { |
|||
TYPES_H_FTAG; |
|||
free(ptr); |
|||
} |
|||
void pFree(void* ptr, void* allocatorState) { |
|||
TYPES_H_FTAG; |
|||
pFree(ptr); |
|||
} |
|||
|
|||
void pFree(const void* ptr) { |
|||
TYPES_H_FTAG; |
|||
pFree((void*) ptr); |
|||
} |
|||
void pFree(const void* ptr, void* allocatorState) { |
|||
TYPES_H_FTAG; |
|||
pFree((void*) ptr, allocatorState); |
|||
} |
|||
|
|||
#ifdef malloc
|
|||
#undef malloc
|
|||
#endif
|
|||
#ifdef calloc
|
|||
#undef calloc
|
|||
#endif
|
|||
#ifdef realloc
|
|||
#undef realloc
|
|||
#endif
|
|||
#ifdef free
|
|||
#undef free
|
|||
#endif
|
|||
|
|||
static bool DefaultAllocatorInited = false; |
|||
static Allocator DefaultAllocator; |
|||
static void defaultAllocatorInit() { |
|||
TYPES_H_FTAG; |
|||
DefaultAllocator.state = null; |
|||
DefaultAllocator.mallocate = pMalloc; |
|||
DefaultAllocator.callocate = pCalloc; |
|||
DefaultAllocator.reallocate = pRealloc; |
|||
DefaultAllocator.free = pFree; |
|||
DefaultAllocatorInited = true; |
|||
} |
|||
|
|||
Allocator* Allocator::GetDefault() { |
|||
TYPES_H_FTAG; |
|||
if (!DefaultAllocatorInited) defaultAllocatorInit(); |
|||
return &DefaultAllocator; |
|||
} |
|||
|
|||
//================================================================================
|
|||
// alignment should be a power of 2
|
|||
static u64 alignForward2(u64 ptr, size_t alignment) { |
|||
TYPES_H_FTAG; |
|||
u64 p, a, modulo; |
|||
|
|||
p = ptr; |
|||
a = alignment; |
|||
modulo = p & (a - 1); |
|||
|
|||
if (modulo != 0) { |
|||
p += a - modulo; |
|||
} |
|||
|
|||
return p; |
|||
} |
|||
static u64 alignForward(u64 ptr, size_t alignment) { |
|||
TYPES_H_FTAG; |
|||
return ((ptr + alignment - 1) / alignment) * alignment; |
|||
} |
|||
|
|||
//================================================================================
|
|||
// Scratch/Arena
|
|||
Arena* Arena::Init(u32 sizeInBytes) { |
|||
TYPES_H_FTAG; |
|||
Arena* arena = (Arena*) pMalloc(sizeof(Arena)); |
|||
arena->index = 0; |
|||
arena->buffer = (u8*) pMalloc(sizeof(u8) * sizeInBytes); |
|||
arena->bufferSizeInBytes = sizeInBytes; |
|||
return arena; |
|||
} |
|||
void* Arena::Alloc(u32 sizeInBytes) { |
|||
TYPES_H_FTAG; |
|||
u8* p = this->buffer + this->index; |
|||
u32 offset = (u32) alignForward2((u64) p, 64); |
|||
|
|||
if ((void*)(offset + sizeInBytes) <= (this->buffer + this->bufferSizeInBytes)) { |
|||
void* ptr = &this->buffer[offset]; |
|||
this->index += offset + sizeInBytes; |
|||
|
|||
String::memset(ptr, 0, sizeInBytes); |
|||
|
|||
return ptr; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
void Arena::Clear() { |
|||
TYPES_H_FTAG; |
|||
this->index = 0; |
|||
} |
|||
//================================================================================
|
|||
|
|||
struct StackAllocator { |
|||
u32 bufferSize; |
|||
u32 index; |
|||
u8* buffer; |
|||
}; |
|||
|
|||
struct PoolAllocator { |
|||
u8* buffer; |
|||
}; |
|||
|
|||
|
|||
|
@ -0,0 +1,77 @@ |
|||
|
|||
#ifndef ALLOC_H |
|||
#define ALLOC_H |
|||
|
|||
#include "types.h" |
|||
|
|||
|
|||
// define a consistent memory allocation interface |
|||
// trailing void* is a pointer to some allocator state, if relevant. |
|||
// will be unused for malloc/calloc/realloc/free, as the allocator state is internal to the OS. |
|||
// overloads should exist which do nothing with the trailing paramter. |
|||
|
|||
typedef void* (*mallocator) (size_t, void*); |
|||
typedef void* (*callocator) (size_t, size_t, void*); |
|||
typedef void* (*reallocator) (void*, size_t, void*); |
|||
typedef void (*freeer) (void*, void*); |
|||
typedef void (*clearer) ( void*); |
|||
typedef void (*destroyer) ( void*); |
|||
|
|||
// operating system allocator wrappers |
|||
extern void* pMalloc(size_t size); |
|||
extern void* pMalloc(size_t size, void* allocatorState); |
|||
extern void* pCalloc(size_t maxNumOfElements, size_t elementSize); |
|||
extern void* pCalloc(size_t maxNumOfElements, size_t elementSize, void* allocatorState); |
|||
extern void* pRealloc(void* buffer, size_t newSize); |
|||
extern void* pRealloc(void* buffer, size_t newSize, void* allocatorState); |
|||
extern void pFree(void* ptr); |
|||
extern void pFree(void* ptr, void* allocatorState); |
|||
extern void pFree(const void* ptr); |
|||
extern void pFree(const void* ptr, void* allocatorState); |
|||
|
|||
|
|||
struct Allocator { |
|||
void* state; |
|||
mallocator mallocate; |
|||
callocator callocate; |
|||
reallocator reallocate; |
|||
freeer free; // releases a specific piece of memory |
|||
clearer clear; // should release all the memory owned by this allocator at once. |
|||
destroyer destroy; // releases all the memory owned by this allocator, and also destroys the allocator. |
|||
|
|||
static Allocator* GetDefault(); |
|||
|
|||
Allocator() { |
|||
this->state = null; |
|||
this->mallocate = pMalloc; |
|||
this->callocate = pCalloc; |
|||
this->reallocate = pRealloc; |
|||
this->free = pFree; |
|||
this->clear = null; |
|||
this->destroy = null; |
|||
} |
|||
|
|||
Allocator(mallocator mallocate, freeer free) { |
|||
this->state = null; |
|||
this->mallocate = mallocate; |
|||
this->callocate = null; |
|||
this->reallocate = null; |
|||
this->free = free; |
|||
this->clear = null; |
|||
this->destroy = null; |
|||
} |
|||
}; |
|||
|
|||
struct Arena { |
|||
u32 bufferSizeInBytes; |
|||
u32 index; |
|||
u8* buffer; |
|||
|
|||
static Arena* Init(u32 sizeInBytes = 1024); |
|||
|
|||
void* Alloc(u32 sizeInBytes); |
|||
void Clear(); |
|||
}; |
|||
|
|||
#endif |
|||
|
@ -0,0 +1,275 @@ |
|||
|
|||
#ifndef ARRAY_H
|
|||
#define ARRAY_H
|
|||
|
|||
#include <new> // operator new, operator delete
|
|||
|
|||
#include "alloc.h" // allocators...
|
|||
#include "serialize.h" // serialization
|
|||
#include "string.h" // String::memcpy
|
|||
#include "types.h" // type definitions
|
|||
|
|||
|
|||
// this is a dynamic array (grows as needed)
|
|||
// should work with any data type for T including primitive types
|
|||
// some initial |capacity| is heap-allocated and a pointer is stored to it as |data|
|
|||
// the |length| of the array, or number of filled slots is also tracked.
|
|||
//
|
|||
// it implements a single constructor, and operator new. no destructor, or operator overloading besides that.
|
|||
// remember to use ->data or .data to actually access the underlying array.
|
|||
//
|
|||
// overhead:
|
|||
// size of the struct will be 128 bits on 64-bit platforms
|
|||
// note that if you heap allocate this structure and store it on another struct, you will have to chase two pointers to get at the data.
|
|||
// to avoid this, I often include this struct in other structs so there's only one pointer dereference,
|
|||
// just like including a raw pointer array + length in your struct.
|
|||
// because I like to do this, automatic destructors are not useful.
|
|||
//
|
|||
template <typename T> |
|||
struct Array { |
|||
u32 length; |
|||
u32 capacity; |
|||
T* data; |
|||
|
|||
Array<T>(u32 _capacity = 8) { |
|||
TYPES_H_FTAG; |
|||
this->length = 0; |
|||
this->capacity = _capacity; |
|||
this->data = (T*) pCalloc(sizeof (T), _capacity); |
|||
} |
|||
void* operator new(size_t size) { |
|||
TYPES_H_FTAG; |
|||
return pMalloc((u32) size); |
|||
} |
|||
|
|||
void checkIfShouldGrow() { |
|||
TYPES_H_FTAG; |
|||
if (this->isFull()) { |
|||
// optimal number as you approach infinite elements approaches PHI, but 1.5 sometimes works better for finite sizes
|
|||
// more testing is probably needed
|
|||
this->capacity = (u32) (this->capacity * 1.5); |
|||
this->data = (T*) pRealloc(data, sizeof(T) * this->capacity); |
|||
} |
|||
} |
|||
|
|||
// for when the order in the array doesn't matter, move the end of the array into the removed slot
|
|||
void removeSwapWithEnd(u32 index) { |
|||
TYPES_H_FTAG; |
|||
if (this->isEmpty()) return; // overhead, maybe assert instead?
|
|||
|
|||
u32 end = this->length - 1; |
|||
if (index != end) { |
|||
this->data[index] = this->data[end]; |
|||
} |
|||
this->pop(); |
|||
} |
|||
|
|||
void removeSwapWithEnd(T* addr) { |
|||
TYPES_H_FTAG; |
|||
for (u32 i = 0; i < this->length; i++) { |
|||
if ((this->data + i) == addr) { |
|||
removeSwapWithEnd(i); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void removeAndShrink(u32 index) { |
|||
TYPES_H_FTAG; |
|||
for (u32 i = index + 1; i < this->length; i++) { |
|||
String::memcpy(this->data[i - 1], this->data[i], sizeof(T)); |
|||
} |
|||
this->length--; |
|||
} |
|||
|
|||
void removeAndShrink(T* elementAddr) { |
|||
TYPES_H_FTAG; |
|||
s32 index = -1; |
|||
for (u32 i = 0; i < this->length; i++) { |
|||
if ((this->data + i) == elementAddr) { |
|||
index = i; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (index == -1) { |
|||
return; |
|||
} |
|||
|
|||
for (u32 i = index + 1; i < this->length; i++) { |
|||
String::memcpy((void*)(this->data + i - 1), (void*)(this->data + i), sizeof(T)); |
|||
} |
|||
this->length--; |
|||
} |
|||
|
|||
T pop() { |
|||
TYPES_H_FTAG; |
|||
if (this->isEmpty()) { |
|||
die("empty"); |
|||
} |
|||
|
|||
return this->data[--this->length]; |
|||
} |
|||
|
|||
// sometimes, you want to copy some POD data on the stack to the next position in the internal array
|
|||
// that's what this does
|
|||
u32 pushCopy(T* e) { |
|||
TYPES_H_FTAG; |
|||
this->checkIfShouldGrow(); |
|||
|
|||
String::memcpy((void*) &this->data[this->length++], e, sizeof(T)); |
|||
|
|||
return this->length - 1; |
|||
} |
|||
|
|||
// returns the next address into which you can store a T. makes sure there's enough room first.
|
|||
// it is irresponsible to call this and then not store a T in that address. this increments length,
|
|||
// reserving the next spot for you.
|
|||
T* pushNextAddrPromise() { |
|||
TYPES_H_FTAG; |
|||
this->checkIfShouldGrow(); |
|||
|
|||
return &this->data[this->length++]; |
|||
} |
|||
|
|||
u32 push(T e) { |
|||
TYPES_H_FTAG; |
|||
this->checkIfShouldGrow(); |
|||
|
|||
this->data[this->length++] = e; |
|||
|
|||
return this->length - 1; |
|||
} |
|||
|
|||
u32 pushMany(T* elements, u32 count) { |
|||
TYPES_H_FTAG; |
|||
// ensure we have capacity. if we have to realloc multiple times that can suck,
|
|||
// but should be avoidable in practice by having an appropriately large initial capacity
|
|||
while (this->capacity < (this->length + count)) { |
|||
this->capacity *= 1.5; |
|||
this->data = (T*) pRealloc(data, sizeof (T) * this->capacity); |
|||
} |
|||
|
|||
u32 start = this->length; |
|||
for (u32 i1 = start, i2 = 0; i1 < count; i1++, i2++) { |
|||
this->data[this->length++] = elements[i2]; |
|||
} |
|||
|
|||
return start; |
|||
} |
|||
|
|||
void reverse() { |
|||
TYPES_H_FTAG; |
|||
u32 count = this->length / 2; |
|||
|
|||
for (u32 i = 0; i < count; i++) { |
|||
u32 offset = this->length - 1 - i; |
|||
|
|||
T temp = this->data[i]; |
|||
this->data[i] = this->data[offset]; |
|||
this->data[offset] = temp; |
|||
} |
|||
} |
|||
|
|||
T shift() { |
|||
TYPES_H_FTAG; |
|||
if (this->length == 0) { |
|||
return null; |
|||
} |
|||
|
|||
T out = this->data[0]; |
|||
this->length -= 1; |
|||
|
|||
for (u32 i = 0; i < this->length; i++) { |
|||
*(this->data + i) = *(this->data + i + 1); |
|||
} |
|||
|
|||
return out; |
|||
} |
|||
|
|||
T unshift(T e) { |
|||
TYPES_H_FTAG; |
|||
this->checkIfShouldGrow(); |
|||
|
|||
for (u32 i = 0; i < this->length; i++) { |
|||
*(this->data + i + 1) = *(this->data + i); |
|||
} |
|||
|
|||
this->data[0] = e; |
|||
this->length += 1; |
|||
|
|||
return this->length; |
|||
} |
|||
|
|||
T peek() const { |
|||
TYPES_H_FTAG; |
|||
if (this->isEmpty()) { |
|||
return null; |
|||
} |
|||
|
|||
return this->data[this->length - 1]; |
|||
} |
|||
|
|||
bool isEmpty() const { |
|||
TYPES_H_FTAG; |
|||
return this->length == 0; |
|||
} |
|||
|
|||
bool isFull() const { |
|||
TYPES_H_FTAG; |
|||
return this->length == this->capacity; |
|||
} |
|||
|
|||
void clear() { |
|||
TYPES_H_FTAG; |
|||
this->length = 0; |
|||
} |
|||
}; |
|||
|
|||
template <typename T> |
|||
static void serialize(String* str, Array<T> array) { |
|||
TYPES_H_FTAG; |
|||
serialize(str, array.length); |
|||
serialize(str, array.capacity); |
|||
for (u32 i = 0; i < array.length; i++) { |
|||
serialize(str, array.data[i]); |
|||
} |
|||
} |
|||
|
|||
template <typename T> |
|||
static void serialize(String* str, Array<T>* array) { |
|||
TYPES_H_FTAG; |
|||
SERIALIZE_HANDLE_NULL(str, array); |
|||
serialize(str, array->length); |
|||
serialize(str, array->capacity); |
|||
for (u32 i = 0; i < array->length; i++) { |
|||
serialize(str, array->data[i]); |
|||
} |
|||
} |
|||
|
|||
template <typename T> |
|||
static void deserialize(char** buffer, Array<T>* array) { |
|||
TYPES_H_FTAG; |
|||
deserialize(buffer, &array->length); |
|||
deserialize(buffer, &array->capacity); |
|||
for (u32 i = 0; i < array->length; i++) { |
|||
deserialize(buffer, array->data + i); |
|||
} |
|||
} |
|||
|
|||
template <typename T> |
|||
static void deserialize(char** buffer, Array<T>** array) { |
|||
TYPES_H_FTAG; |
|||
DESERIALIZE_HANDLE_NULL(buffer, array); |
|||
u32 length, capacity; |
|||
deserialize(buffer, &length); |
|||
deserialize(buffer, &capacity); |
|||
Array<T>* _array = new Array<T>(capacity); |
|||
_array->length = length; |
|||
for (u32 i = 0; i < _array->length; i++) { |
|||
deserialize(buffer, _array->data + i); |
|||
} |
|||
*array = _array; |
|||
} |
|||
|
|||
#endif
|
@ -0,0 +1,149 @@ |
|||
|
|||
#include "types.h"
|
|||
#include "string.h"
|
|||
#include "print.h"
|
|||
|
|||
|
|||
static const char* szFeatures[] = { |
|||
"x87 FPU On Chip", |
|||
"Virtual-8086 Mode Enhancement", |
|||
"Debugging Extensions", |
|||
"Page Size Extensions", |
|||
"Time Stamp Counter", |
|||
"RDMR and WRMSR Support", |
|||
"Physical Address Extensions", |
|||
"Machine Check Exception", |
|||
"CMPXCHG8B Instruction", |
|||
"APIC On Chip", |
|||
"Unknown1", |
|||
"SYSENTER and SYSEXIT", |
|||
"Memory Type Range Registers", |
|||
"PTE Global Bit", |
|||
"Machine Check Architecture", |
|||
"Conditional Move/Compare Instruction", |
|||
"Page Attribute Table", |
|||
"36-bit Page Size Extension", |
|||
"Processor Serial Number", |
|||
"CFLUSH Extension", |
|||
"Unknown2", |
|||
"Debug Store", |
|||
"ThermalMonitor and Clock Ctrl", |
|||
"MMX Technology", |
|||
"FXSAVE/FXRSTOR", |
|||
"SSE Extensions", |
|||
"SSE2 Extensions", |
|||
"Self Snoop", |
|||
"Multithreading Technology", |
|||
"Thermal Monitor", |
|||
"Unknown4", |
|||
"Pending Break Enable" |
|||
|
|||
}; |
|||
|
|||
#if defined(_WIN32)
|
|||
// implementation originally from:
|
|||
// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/hskdteyh(v=vs.90)?redirectedfrom=MSDN
|
|||
#include <intrin.h>
|
|||
void cpuid() { |
|||
TYPES_H_FTAG; |
|||
int nSteppingID = 0; |
|||
int nModel = 0; |
|||
int nFamily = 0; |
|||
int nProcessorType = 0; |
|||
int nExtendedmodel = 0; |
|||
int nExtendedfamily = 0; |
|||
int nBrandIndex = 0; |
|||
int nCLFLUSHcachelinesize = 0; |
|||
int nLogicalProcessors = 0; |
|||
int nAPICPhysicalID = 0; |
|||
int nFeatureInfo = 0; |
|||
int nCacheLineSize = 0; |
|||
int nL2Associativity = 0; |
|||
int nCacheSizeK = 0; |
|||
int nPhysicalAddress = 0; |
|||
int nVirtualAddress = 0; |
|||
int nRet = 0; |
|||
|
|||
int nCores = 0; |
|||
int nCacheType = 0; |
|||
int nCacheLevel = 0; |
|||
int nMaxThread = 0; |
|||
int nSysLineSize = 0; |
|||
int nPhysicalLinePartitions = 0; |
|||
int nWaysAssociativity = 0; |
|||
int nNumberSets = 0; |
|||
|
|||
unsigned nIds, nExIds, i; |
|||
|
|||
bool bSSE3Instructions = false; |
|||
bool bMONITOR_MWAIT = false; |
|||
bool bCPLQualifiedDebugStore = false; |
|||
bool bVirtualMachineExtensions = false; |
|||
bool bEnhancedIntelSpeedStepTechnology = false; |
|||
bool bThermalMonitor2 = false; |
|||
bool bSupplementalSSE3 = false; |
|||
bool bL1ContextID = false; |
|||
bool bCMPXCHG16B = false; |
|||
bool bxTPRUpdateControl = false; |
|||
bool bPerfDebugCapabilityMSR = false; |
|||
bool bSSE41Extensions = false; |
|||
bool bSSE42Extensions = false; |
|||
bool bPOPCNT = false; |
|||
|
|||
bool bMultithreading = false; |
|||
|
|||
bool bLAHF_SAHFAvailable = false; |
|||
bool bCmpLegacy = false; |
|||
bool bSVM = false; |
|||
bool bExtApicSpace = false; |
|||
bool bAltMovCr8 = false; |
|||
bool bLZCNT = false; |
|||
bool bSSE4A = false; |
|||
bool bMisalignedSSE = false; |
|||
bool bPREFETCH = false; |
|||
bool bSKINITandDEV = false; |
|||
bool bSYSCALL_SYSRETAvailable = false; |
|||
bool bExecuteDisableBitAvailable = false; |
|||
bool bMMXExtensions = false; |
|||
bool bFFXSR = false; |
|||
bool b1GBSupport = false; |
|||
bool bRDTSCP = false; |
|||
bool b64Available = false; |
|||
bool b3DNowExt = false; |
|||
bool b3DNow = false; |
|||
bool bNestedPaging = false; |
|||
bool bLBRVisualization = false; |
|||
bool bFP128 = false; |
|||
bool bMOVOptimization = false; |
|||
|
|||
bool bSelfInit = false; |
|||
bool bFullyAssociative = false; |
|||
|
|||
int cpuinfo[4] = { -1 }; |
|||
__cpuid(cpuinfo, 0); |
|||
unsigned count = cpuinfo[0]; |
|||
|
|||
char cpustring[0x20]; |
|||
String::memset((void*) cpustring, '\0', sizeof(cpustring)); |
|||
*((int*) cpustring) = cpuinfo[1]; |
|||
*((int*) (cpustring + 4)) = cpuinfo[3]; |
|||
*((int*) (cpustring + 8)) = cpuinfo[2]; |
|||
println("CPU Identification String: %s", cpustring); |
|||
|
|||
//for (s32 i = 0; i < count; i++) {
|
|||
// __cpuid(cpuinfo, i);
|
|||
// println("info type: %d", i);
|
|||
// println("cpuinfo[0] = 0x%x", cpuinfo[0]);
|
|||
// println("cpuinfo[1] = 0x%x", cpuinfo[1]);
|
|||
// println("cpuinfo[2] = 0x%x", cpuinfo[2]);
|
|||
// println("cpuinfo[3] = 0x%x", cpuinfo[3]);
|
|||
//}
|
|||
} |
|||
|
|||
#else
|
|||
void cpuid() { |
|||
TYPES_H_FTAG; |
|||
} |
|||
|
|||
#endif
|
|||
|
@ -0,0 +1,8 @@ |
|||
|
|||
#ifndef CPUID_H |
|||
#define CPUID_H |
|||
|
|||
void cpuid(); |
|||
|
|||
#endif |
|||
|
@ -0,0 +1,209 @@ |
|||
|
|||
#define _CRT_SECURE_NO_WARNINGS
|
|||
#include <stdio.h> // fopen, fseek, ftell, fclose
|
|||
#undef _CRT_SECURE_NO_WARNINGS
|
|||
|
|||
#include "alloc.h"
|
|||
#include "array.hpp"
|
|||
#include "file.h"
|
|||
#include "print.h"
|
|||
#include "types.h"
|
|||
|
|||
|
|||
FILE* File::Open(const char* path, const char* mode) { |
|||
TYPES_H_FTAG; |
|||
return fopen(path, mode); |
|||
} |
|||
FILE* File::Open(const char* path, size_t* outSize, const char* mode) { |
|||
TYPES_H_FTAG; |
|||
FILE* fp = File::Open(path, mode); |
|||
|
|||
if (fp == null) { |
|||
return null; |
|||
} |
|||
|
|||
// get the file's size in bytes
|
|||
fseek(fp, 0, SEEK_END); |
|||
*outSize = ftell(fp); |
|||
fseek(fp, 0L, SEEK_SET); |
|||
|
|||
return fp; |
|||
} |
|||
void File::Close(FILE* file) { |
|||
fclose(file); |
|||
} |
|||
|
|||
size_t File::Size(const char* path) { |
|||
TYPES_H_FTAG; |
|||
FILE* fp = File::Open(path); |
|||
// get the file's size in bytes
|
|||
fseek(fp, 0, SEEK_END); |
|||
size_t size = ftell(fp); |
|||
fseek(fp, 0L, SEEK_SET); |
|||
fclose(fp); |
|||
return size; |
|||
} |
|||
size_t File::Size(FILE* fp) { |
|||
TYPES_H_FTAG; |
|||
fseek(fp, 0, SEEK_END); |
|||
size_t size = ftell(fp); |
|||
fseek(fp, 0L, SEEK_SET); |
|||
return size; |
|||
} |
|||
|
|||
u8* File::Read(const char* path) { |
|||
TYPES_H_FTAG; |
|||
FILE* fp = File::Open(path, "rb"); |
|||
|
|||
if (fp == null) { |
|||
return null; |
|||
} |
|||
|
|||
// get the file's size in bytes
|
|||
fseek(fp, 0, SEEK_END); |
|||
u32 size = ftell(fp); |
|||
fseek(fp, 0L, SEEK_SET); |
|||
|
|||
char* buffer = (char*) pMalloc(size + 1); |
|||
fread(buffer, sizeof (char), size + 1, fp); |
|||
buffer[size] = '\0'; |
|||
|
|||
fclose(fp); |
|||
|
|||
return (u8*) buffer; |
|||
} |
|||
u8* File::Read(const char* path, size_t* outSize) { |
|||
TYPES_H_FTAG; |
|||
FILE* fp = File::Open(path, "rb"); |
|||
|
|||
if (fp == null) { |
|||
return null; |
|||
} |
|||
|
|||
// get the file's size in bytes
|
|||
fseek(fp, 0, SEEK_END); |
|||
size_t size = ftell(fp); |
|||
// if we got a valid pointer to a size_t we should report the size back.
|
|||
if (outSize != null) { |
|||
*outSize = size; |
|||
} |
|||
fseek(fp, 0L, SEEK_SET); |
|||
|
|||
char* buffer = (char*) pMalloc(size + 1); |
|||
fread(buffer, sizeof (char), size + 1, fp); |
|||
buffer[size] = '\0'; |
|||
|
|||
fclose(fp); |
|||
|
|||
return (u8*) buffer; |
|||
} |
|||
size_t File::Read(FILE* fp, void* destination) { |
|||
TYPES_H_FTAG; |
|||
|
|||
fseek(fp, 0, SEEK_END); |
|||
size_t size = ftell(fp); |
|||
fseek(fp, 0L, SEEK_SET); |
|||
|
|||
fread(destination, sizeof (char), size + 1, fp); |
|||
return size; |
|||
} |
|||
size_t File::Read(FILE* fp, void* destination, size_t size) { |
|||
TYPES_H_FTAG; |
|||
return fread(destination, sizeof (char), size + 1, fp); |
|||
} |
|||
|
|||
s32 File::Write(const char* path, char* data, u32 count) { |
|||
TYPES_H_FTAG; |
|||
FILE* fp = File::Open(path, "wb"); |
|||
|
|||
if (fp == null) { |
|||
return -1; // failed to open the file
|
|||
} |
|||
|
|||
size_t writtenCount = fwrite(data, 1, count, fp); |
|||
fclose(fp); |
|||
|
|||
if (writtenCount != count) { |
|||
return -2; // wrote only partially
|
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
#if _WIN32
|
|||
#include <windows.h>
|
|||
// writes the filenames into the provided array |outFileNames|, must be allocated ahead of time.
|
|||
void File::GetFileNamesInFolder(const char* path, Array<char*>* outFileNames) { |
|||
TYPES_H_FTAG; |
|||
massert(path != null, "provided 'null' for path argument"); |
|||
massert(outFileNames != null, "provided 'null' for array argument"); |
|||
WIN32_FIND_DATAA findData; |
|||
HANDLE hFind = INVALID_HANDLE_VALUE; |
|||
|
|||
String str; |
|||
if (path[String::len(path) - 1] != '/') { |
|||
str = String64f("%s/*", path); |
|||
} else { |
|||
str = String64f("%s*", path); |
|||
} |
|||
hFind = FindFirstFileA(str.c_str(), &findData); |
|||
|
|||
if (hFind == INVALID_HANDLE_VALUE) die("failed to open folder: %s", path); |
|||
|
|||
while (FindNextFileA(hFind, &findData) != 0) { |
|||
if (findData.cFileName[0] != '.') { |
|||
outFileNames->push(String::cpy(findData.cFileName)); |
|||
} |
|||
} |
|||
FindClose(hFind); |
|||
} |
|||
#else
|
|||
#include <dirent.h>
|
|||
void File::GetFileNamesInFolder(const char* path, Array<char*>* outFileNames) { |
|||
TYPES_H_FTAG; |
|||
massert(path != null, "provided 'null' for path argument"); |
|||
massert(outFileNames != null, "provided 'null' for array argument"); |
|||
DIR* dir = opendir(path); |
|||
struct dirent* d; |
|||
|
|||
if (dir == null) die("failed to open folder: %s", path); |
|||
|
|||
do { |
|||
d = readdir(dir); |
|||
if (d == null) break; |
|||
if (d->d_name[0] != '.') outFileNames->push(String::cpy(d->d_name)); |
|||
} while (1); |
|||
|
|||
closedir(dir); |
|||
} |
|||
#endif
|
|||
|
|||
#include <sys/types.h>
|
|||
#include <sys/stat.h>
|
|||
#ifndef WIN32
|
|||
#include <unistd.h>
|
|||
#endif
|
|||
// msvc provides a 'stat' equivalent as _stat.
|
|||
#ifdef WIN32
|
|||
#define stat _stat
|
|||
#endif
|
|||
|
|||
time_t File::LastModified(const char* path) { |
|||
TYPES_H_FTAG; |
|||
struct stat result; |
|||
if (stat(path, &result) == 0) { |
|||
return result.st_mtime; |
|||
} |
|||
massert(false, "failed to get last modified timestamp."); |
|||
return -1; |
|||
} |
|||
#undef stat
|
|||
|
|||
s32 File::Rename(const char* oldFilename, const char* newFilename) { |
|||
return rename(oldFilename, newFilename); |
|||
} |
|||
|
|||
s32 File::Remove(const char* path) { |
|||
return remove(path); |
|||
} |
|||
|
@ -0,0 +1,49 @@ |
|||
|
|||
#ifndef FILE_H |
|||
#define FILE_H |
|||
|
|||
#include <stdio.h> // FILE |
|||
#include <sys/types.h> // time_t |
|||
|
|||
#include "array.hpp" |
|||
|
|||
|
|||
namespace File { |
|||
FILE* Open(const char* path, const char* mode = "rb"); |
|||
FILE* Open(const char* path, size_t* outSize, const char* mode = "rb"); |
|||
|
|||
void Close(FILE* file); |
|||
|
|||
size_t Size(const char* path); |
|||
size_t Size(FILE* fp); |
|||
|
|||
// most commonly this is what you want - provide a filesystem path, read the whole file, |
|||
// and return a buffer with the file's contents. |
|||
// the file is opened and closed internally. |
|||
// the buffer you get returned is allocated internally and must be freed by the caller. |
|||
u8* Read(const char* path); |
|||
// if you also want to know the size of the file. |
|||
u8* Read(const char* path, size_t* outSize); |
|||
|
|||
// for apis that want a more incremental approach, this function will read |file|'s contents into |destination|, |
|||
// which must be pre-allocated. the provided file must be closed by the caller. |
|||
// returns the number of bytes read. |
|||
size_t Read(FILE* fp, void* destination); // calls fseek to determine file size |
|||
size_t Read(FILE* fp, void* destination, size_t size); // you know and provide the file size |
|||
|
|||
s32 Write(const char* path, char* data, u32 count); |
|||
|
|||
// writes the filenames into the provided array |outFileNames|, must be allocated ahead of time. |
|||
void GetFileNamesInFolder(const char* path, Array<char*>* outFileNames); |
|||
|
|||
// get the last-modified timestamp on a file located at 'path'. |
|||
// there's no caching done, so it's the real, current value. |
|||
// specifically, it's the field 'st_mtime' on the 'stat' struct. |
|||
time_t LastModified(const char* path); |
|||
|
|||
s32 Rename(const char* oldFilename, const char* newFilename); |
|||
s32 Remove(const char* path); |
|||
} |
|||
|
|||
#endif |
|||
|
@ -0,0 +1,280 @@ |
|||
|
|||
#include <stdarg.h> // va_list, va_start, va_end
|
|||
#include <stdio.h> // FILE, stderr, stdout | vfprintf
|
|||
#include <stdlib.h> // exit
|
|||
|
|||
#include "alloc.h"
|
|||
#include "string.h"
|
|||
#include "print.h"
|
|||
#include "types.h"
|
|||
|
|||
|
|||
void vprint(const char* format, va_list args) { |
|||
TYPES_H_FTAG; |
|||
vfprintf(stdout, format, args); |
|||
} |
|||
|
|||
void vprintln(const char* format, va_list args) { |
|||
TYPES_H_FTAG; |
|||
vprint(format, args); |
|||
print("\n"); |
|||
} |
|||
|
|||
/**
|
|||
* The entire purpose of this is so we don't have to #import <stdio.h> everywhere |
|||
* +we intend to replace printf at some point with this |
|||
*/ |
|||
void print(const char* format, ...) { |
|||
TYPES_H_FTAG; |
|||
if (format == null) { print("null"); return; } |
|||
|
|||
va_list args; |
|||
va_start(args, format); |
|||
|
|||
vprint(format, args); |
|||
|
|||
va_end(args); |
|||
} |
|||
|
|||
void println(const char* format, ...) { |
|||
TYPES_H_FTAG; |
|||
if (format == null) { print("null\n"); return; } |
|||
|
|||
va_list args; |
|||
va_start(args, format); |
|||
|
|||
vprintln(format, args); |
|||
|
|||
va_end(args); |
|||
} |
|||
|
|||
/**
|
|||
* Prints a stack trace. |
|||
* Implementation varies for Win32 vs. *nix |
|||
*/ |
|||
#define BACKTRACE_MAX_FRAMES 63
|
|||
#ifdef _WIN32
|
|||
#include <windows.h>
|
|||
#include <dbghelp.h>
|
|||
// if |string| is non-null, then the stack trace will be concatenated to it instead of being printed to stdout.
|
|||
void trace(String* string) { |
|||
TYPES_H_FTAG; |
|||
|
|||
#define BACKTRACE_MAX_FUNCTION_NAME_LENGTH 1024
|
|||
HANDLE processHandle = GetCurrentProcess(); |
|||
SymInitialize(processHandle, null, true); |
|||
|
|||
void* stack[BACKTRACE_MAX_FRAMES]; |
|||
unsigned short numFrames = CaptureStackBackTrace(0, BACKTRACE_MAX_FRAMES, stack, null); |
|||
|
|||
char buffer[sizeof(SYMBOL_INFO) + (BACKTRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR)]; |
|||
SYMBOL_INFO* symbol = (SYMBOL_INFO*) buffer; |
|||
symbol->MaxNameLen = BACKTRACE_MAX_FUNCTION_NAME_LENGTH; |
|||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO); |
|||
|
|||
DWORD displacement; |
|||
IMAGEHLP_LINE64 line; |
|||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); |
|||
for (u32 i = 0; i < numFrames; i++) { |
|||
DWORD64 address = (DWORD64) stack[i]; |
|||
SymFromAddr(processHandle, address, null, symbol); |
|||
|
|||
if (SymGetLineFromAddr64(processHandle, address, &displacement, &line)) { |
|||
if (string == null) { |
|||
print("\tat %s in %s: line: %lu: address %0x%0X\n", symbol->Name, line.FileName, line.LineNumber, symbol->Address); |
|||
|
|||
} else { |
|||
string->appendf("\tat %s in %s: line: %lu: address %0x%0X\n", symbol->Name, line.FileName, line.LineNumber, symbol->Address); |
|||
} |
|||
} else { |
|||
if (string == null) { |
|||
print("\tSymGetLineFromAddr64 returned error code %lu.\n", GetLastError()); |
|||
print("\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address); |
|||
|
|||
} else { |
|||
string->appendf("\tSymGetLineFromAddr64 returned error code %lu.\n", GetLastError()); |
|||
string->appendf("\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address); |
|||
} |
|||
} |
|||
} |
|||
#undef BACKTRACE_MAX_FUNCTION_NAME_LENGTH
|
|||
} |
|||
#else
|
|||
// OSX and Linux stacktrace stuff.
|
|||
#include <execinfo.h> // backtrace, backtrace_symbols
|
|||
#include <cxxabi.h> // abi::__cxa_demangle
|
|||
// if |string| is non-null, then the stack trace will be concatenated to it instead of being printed to stdout.
|
|||
void trace(String* string) { |
|||
TYPES_H_FTAG; |
|||
|
|||
void* stack[BACKTRACE_MAX_FRAMES]; |
|||
u32 stackSize = backtrace(stack, BACKTRACE_MAX_FRAMES); |
|||
|
|||
// resolve addresses into strings containing "filename(function+address)"
|
|||
// this array must be free()-ed
|
|||
char** traces = backtrace_symbols(stack, stackSize); |
|||
|
|||
// iterate over the returned symbol lines. skip the first, it is the address of this function
|
|||
for (u32 i = 1; i < stackSize; i++) { |
|||
|
|||
// the names as provided by 'backtrace_symbols' are mangled for some reason.
|
|||
// we have to demangle them, using this weird api
|
|||
char buffer[1024]; |
|||
const char* mangledNameBegin = String::firstCharOccurence(traces[i], '_'); |
|||
const char* mangledNameEnd = String::lastCharOccurence(traces[i], '+'); |
|||
|
|||
if (mangledNameBegin == null || mangledNameEnd == null) { |
|||
// we can't demangle this name for some reason, just copy the mangled name to the buffer
|
|||
size_t length = String::len(traces[i]); |
|||
String::memcpy(buffer, (void*)traces[i], length); |
|||
buffer[length] = '\0'; |
|||
|
|||
} else { |
|||
size_t length = mangledNameEnd - 1 - mangledNameBegin; |
|||
String::memcpy(buffer, (void*)mangledNameBegin, length); |
|||
buffer[length] = '\0'; |
|||
} |
|||
|
|||
s32 status = -1; |
|||
char* trace = abi::__cxa_demangle(buffer, null, null, &status); |
|||
if (trace == null) { |
|||
println("warning: failed to demangle name: %s", traces[i]); |
|||
continue; |
|||
} |
|||
|
|||
if (string == null) { |
|||
print("%s\n", trace); |
|||
|
|||
} else { |
|||
string->appendf("%s\n", trace); |
|||
} |
|||
} |
|||
|
|||
pFree(traces); |
|||
} |
|||
#undef BACKTRACE_MAX_FRAMES
|
|||
#endif
|
|||
|
|||
void _debug(const char* format, ...) { |
|||
TYPES_H_FTAG; |
|||
if (format == null) { |
|||
print("%sdebug:%s null\n", ANSI_BLUE, ANSI_RESET); |
|||
return; |
|||
} |
|||
|
|||
va_list args; |
|||
va_start(args, format); |
|||
|
|||
print("%sdebug:%s ", ANSI_BLUE, ANSI_RESET); |
|||
vprintln(format, args); |
|||
|
|||
va_end(args); |
|||
} |
|||
|
|||
void _warn(const char* format, ...) { |
|||
TYPES_H_FTAG; |
|||
if (format == null) { |
|||
print("%swarning:%s null\n", ANSI_YELLOW, ANSI_RESET); |
|||
return; |
|||
} |
|||
|
|||
va_list args; |
|||
va_start(args, format); |
|||
|
|||
print("%swarning:%s ", ANSI_YELLOW, ANSI_RESET); |
|||
vprintln(format, args); |
|||
|
|||
va_end(args); |
|||
} |
|||
|
|||
static void (*customDie)(const char* string) = null; |
|||
// if you want to override what happens by default when your program calls 'die', you can do so here.
|
|||
// just keep in mind the intention is for 'die' to be for when your program has encountered a fatal, unrecoverable error.
|
|||
void setCustomDieBehavior(void (*dieBehavior)(const char* string)) { |
|||
customDie = dieBehavior; |
|||
} |
|||
|
|||
// for fatal errors which may occur at runtime, even on a release binary.
|
|||
// if a fatal error should not occur at runtime on a release binary, consider preferring 'massert'
|
|||
// it's unclear when you should use asserts vs. die actually. idk man
|
|||
void die(const char* format, ...) { |
|||
TYPES_H_FTAG; |
|||
if (format == null) { |
|||
if (customDie == null) { |
|||
print("%serror:%s (unspecified error)\n", ANSI_RED, ANSI_RESET); |
|||
trace(); |
|||
exit(1); |
|||
return; |
|||
|
|||
} else { |
|||
String string = String128f("error: (unspecified error)\n"); |
|||
trace(&string); |
|||
customDie(string.c_str()); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
va_list args; |
|||
va_start(args, format); |
|||
|
|||
if (customDie == null) { |
|||
println("%serror:%s", ANSI_RED, ANSI_RESET); |
|||
vprintln(format, args); |
|||
println(); |
|||
|
|||
va_end(args); |
|||
|
|||
trace(); |
|||
exit(1); |
|||
|
|||
} else { |
|||
String string = String128f(""); |
|||
string.setfv(format, args); |
|||
string.append("\n"); |
|||
trace(&string); |
|||
|
|||
va_end(args); |
|||
|
|||
customDie(string.c_str()); |
|||
} |
|||
} |
|||
|
|||
void print(bool b) { TYPES_H_FTAG; print("%s", b ? "true" : "false"); } |
|||
void print(char c) { TYPES_H_FTAG; print("%c", c); } |
|||
void print(signed int i) { TYPES_H_FTAG; print("%d", i); } |
|||
void print(unsigned int i) { TYPES_H_FTAG; print("%u", i); } |
|||
void print(float f) { TYPES_H_FTAG; print("%.14g", f); } |
|||
void print(double d) { TYPES_H_FTAG; print("%.14g", d); } |
|||
void print(void* p) { TYPES_H_FTAG; print("%p", p); } |
|||
void print(char* s) { TYPES_H_FTAG; print("%s", s); } |
|||
|
|||
#ifndef _WIN32
|
|||
void print(size_t i) { TYPES_H_FTAG; print("%u", i); } |
|||
void println(size_t i) { TYPES_H_FTAG; print(i); print("\n"); } |
|||
#endif
|
|||
|
|||
void println(bool b) { TYPES_H_FTAG; print(b); print("\n"); } |
|||
void println(char c) { TYPES_H_FTAG; print(c); print("\n"); } |
|||
void println(signed int i) { TYPES_H_FTAG; print(i); print("\n"); } |
|||
void println(unsigned int i) { TYPES_H_FTAG; print(i); print("\n"); } |
|||
void println(float f) { TYPES_H_FTAG; print(f); print("\n"); } |
|||
void println(double d) { TYPES_H_FTAG; print(d); print("\n"); } |
|||
void println(void* p) { TYPES_H_FTAG; print(p); print("\n"); } |
|||
void println(char* s) { TYPES_H_FTAG; print(s); print("\n"); } |
|||
void println() { TYPES_H_FTAG; print("\n"); } |
|||
|
|||
#ifdef _USING_GLM_TYPES__
|
|||
void print(glm::vec<2, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print("vec2: %.14g,%.14g", v.x, v.y); } |
|||
void print(glm::vec<3, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print("vec3: %.14g,%.14g,%.14g", v.x, v.y, v.z); } |
|||
void print(glm::vec<4, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print("vec4: %.14g,%.14g,%.14g,%.14g", v.x, v.y, v.z, v.w); } |
|||
void print(glm::mat<2, 2, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print("mat2: "); print(m[0]); print(m[1]); } |
|||
void print(glm::mat<3, 3, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print("mat3: "); print(m[0]); print(m[1]); print(m[2]); } |
|||
void print(glm::mat<4, 4, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print("mat4: "); print(m[0]); print(m[1]); print(m[2]); print(m[3]); } |
|||
|
|||
void println(glm::vec<2, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print(v); print("\n"); } |
|||
void println(glm::vec<3, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print(v); print("\n"); } |
|||
void println(glm::vec<4, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print(v); print("\n"); } |
|||
void println(glm::mat<2, 2, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print(m); print("\n"); } |
|||
void println(glm::mat<3, 3, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print(m); print("\n"); } |
|||
void println(glm::mat<4, 4, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print(m); print("\n"); } |
|||
#endif
|
@ -0,0 +1,155 @@ |
|||
|
|||
#ifndef PRINT_H |
|||
#define PRINT_H |
|||
|
|||
#include <stdarg.h> // va_list |
|||
|
|||
#include "string.h" |
|||
#include "types.h" |
|||
|
|||
|
|||
#ifndef _WIN32 |
|||
// comment this line out if you don't want terminal output to contain ANSI escape codes |
|||
// by default on windows executables it's disabled, since things like the command prompt still doesn't support ANSI color codes |
|||
#define PRINT_H_USE_ANSI_COLOR_ESCAPE_CODES |
|||
#endif |
|||
|
|||
#ifdef PRINT_H_USE_ANSI_COLOR_ESCAPE_CODES |
|||
#define ANSI_BLACK "\x001B[30m" |
|||
#define ANSI_RED "\x001B[31m" |
|||
#define ANSI_GREEN "\x001B[32m" |
|||
#define ANSI_YELLOW "\x001B[33m" |
|||
#define ANSI_BLUE "\x001B[34m" |
|||
#define ANSI_PURPLE "\x001B[35m" |
|||
#define ANSI_CYAN "\x001B[36m" |
|||
#define ANSI_WHITE "\x001B[37m" |
|||
#define ANSI_BRIGHT_BLACK "\x001B[90m" |
|||
#define ANSI_BRIGHT_RED "\x001B[91m" |
|||
#define ANSI_BRIGHT_GREEN "\x001B[92m" |
|||
#define ANSI_BRIGHT_YELLOW "\x001B[93m" |
|||
#define ANSI_BRIGHT_BLUE "\x001B[94m" |
|||
#define ANSI_BRIGHT_MAGENTA "\x001B[95m" |
|||
#define ANSI_BRIGHT_CYAN "\x001B[96m" |
|||
#define ANSI_BRIGHT_WHITE "\x001B[97m" |
|||
#define ANSI_BLACK_BACKGROUND "\x001B[40m" |
|||
#define ANSI_RED_BACKGROUND "\x001B[41m" |
|||
#define ANSI_GREEN_BACKGROUND "\x001B[42m" |
|||
#define ANSI_YELLOW_BACKGROUND "\x001B[43m" |
|||
#define ANSI_BLUE_BACKGROUND "\x001B[44m" |
|||
#define ANSI_PURPLE_BACKGROUND "\x001B[45m" |
|||
#define ANSI_CYAN_BACKGROUND "\x001B[46m" |
|||
#define ANSI_WHITE_BACKGROUND "\x001B[47m" |
|||
#define ANSI_WRAP "\x001B[7m" |
|||
#define ANSI_BLINK "\x001B[5m" |
|||
#define ANSI_CLEAR "\x001B[2J\x001B[;H" |
|||
#define ANSI_RESET "\x001B[0m" |
|||
#else |
|||
#define ANSI_BLACK "" |
|||
#define ANSI_RED "" |
|||
#define ANSI_GREEN "" |
|||
#define ANSI_YELLOW "" |
|||
#define ANSI_BLUE "" |
|||
#define ANSI_PURPLE "" |
|||
#define ANSI_CYAN "" |
|||
#define ANSI_WHITE "" |
|||
#define ANSI_BRIGHT_BLACK "" |
|||
#define ANSI_BRIGHT_RED "" |
|||
#define ANSI_BRIGHT_GREEN "" |
|||
#define ANSI_BRIGHT_YELLOW "" |
|||
#define ANSI_BRIGHT_BLUE "" |
|||
#define ANSI_BRIGHT_MAGENTA "" |
|||
#define ANSI_BRIGHT_CYAN "" |
|||
#define ANSI_BRIGHT_WHITE "" |
|||
#define ANSI_BLACK_BACKGROUND "" |
|||
#define ANSI_RED_BACKGROUND "" |
|||
#define ANSI_GREEN_BACKGROUND "" |
|||
#define ANSI_YELLOW_BACKGROUND "" |
|||
#define ANSI_BLUE_BACKGROUND "" |
|||
#define ANSI_PURPLE_BACKGROUND "" |
|||
#define ANSI_CYAN_BACKGROUND "" |
|||
#define ANSI_WHITE_BACKGROUND "" |
|||
#define ANSI_WRAP "" |
|||
#define ANSI_BLINK "" |
|||
#define ANSI_CLEAR "" |
|||
#define ANSI_RESET "" |
|||
#endif |
|||
|
|||
extern void vprint(const char* format, va_list args); |
|||
extern void vprintln(const char* format, va_list args); |
|||
extern void print(const char* format, ...); |
|||
extern void println(const char* format, ...); |
|||
|
|||
// Prints a stack trace, or concatenates the stack trace to |string| if it is not null. |
|||
extern void trace(String* string = null); |
|||
|
|||
// This ends the program and calls trace(). generally you should use 'massert' instead |
|||
extern void die(const char* format, ...); |
|||
|
|||
// when calling 'die', instead of the default behavior, |
|||
// (print a stack trace and then call 'exit(1)') |
|||
// do something else. |
|||
// |
|||
// The function will get passed a string which is the formatted string you passed to 'die' + a stack trace. |
|||
extern void setCustomDieBehavior(void (*dieBehavior)(const char* string)); |
|||
|
|||
#ifdef PRINT_DEBUG |
|||
// simple wrapper to allow custom messages... |
|||
//#define massert(test, message) (((void)(message), test)) |
|||
#define massert(test, message) if (!(test)) die("%s\n", message); |
|||
|
|||
extern void _debug(const char* format, ...); |
|||
#define debug(format, ...) _debug(format, ##__VA_ARGS__) |
|||
|
|||
// @NOTE there's a conflict on win32 with the name 'warning'... |
|||
extern void _warn(const char* format, ...); |
|||
#define warn(format, ...) _warn(format, ##__VA_ARGS__) |
|||
|
|||
#else |
|||
// define some empty macros |
|||
#define massert(test, message) ((void) 0) |
|||
#define debug(format, ...) |
|||
#define warn(format, ...) |
|||
#endif |
|||
|
|||
extern void print(bool b); |
|||
extern void print(char c); |
|||
extern void print(signed int i); |
|||
extern void print(unsigned int i); |
|||
extern void print(float f); |
|||
extern void print(double d); |
|||
extern void print(void* p); |
|||
extern void print(char* s); |
|||
|
|||
#ifndef _WIN32 |
|||
extern void print(size_t i); |
|||
extern void println(size_t i); |
|||
#endif |
|||
|
|||
extern void println(bool b); |
|||
extern void println(char c); |
|||
extern void println(signed int i); |
|||
extern void println(unsigned int i); |
|||
extern void println(float f); |
|||
extern void println(double d); |
|||
extern void println(void* p); |
|||
extern void println(char* s); |
|||
extern void println(); |
|||
|
|||
#ifdef _USING_GLM_TYPES__ |
|||
extern void print(glm::vec<2, float, (glm::qualifier) 3>); |
|||
extern void print(glm::vec<3, float, (glm::qualifier) 3>); |
|||
extern void print(glm::vec<4, float, (glm::qualifier) 3>); |
|||
extern void print(glm::mat<2, 2, float, (glm::qualifier) 3>); |
|||
extern void print(glm::mat<3, 3, float, (glm::qualifier) 3>); |
|||
extern void print(glm::mat<4, 4, float, (glm::qualifier) 3>); |
|||
|
|||
extern void println(glm::vec<2, float, (glm::qualifier) 3> v); |
|||
extern void println(glm::vec<3, float, (glm::qualifier) 3> v); |
|||
extern void println(glm::vec<3, float, (glm::qualifier) 3> v); |
|||
extern void println(glm::mat<2, 2, float, (glm::qualifier) 3> m); |
|||
extern void println(glm::mat<3, 3, float, (glm::qualifier) 3> m); |
|||
extern void println(glm::mat<4, 4, float, (glm::qualifier) 3> m); |
|||
#endif |
|||
|
|||
#endif |
|||
|
@ -0,0 +1,316 @@ |
|||
|
|||
#include <fast_float/fast_float.h>
|
|||
|
|||
#include "types.h"
|
|||
#include "serialize.h"
|
|||
#include "string.h"
|
|||
#include "print.h"
|
|||
|
|||
|
|||
static inline const char* getFormatStringOut(u8 v) { TYPES_H_FTAG; return "%hu\n"; } |
|||
static inline const char* getFormatStringOut(u16 v) { TYPES_H_FTAG; return "%hu\n"; } |
|||
static inline const char* getFormatStringOut(u32 v) { TYPES_H_FTAG; return "%u\n"; } |
|||
static inline const char* getFormatStringOut(u64 v) { TYPES_H_FTAG; return "%llu\n"; } |
|||
|
|||
static inline const char* getFormatStringOut(s8 v) { TYPES_H_FTAG; return "%hd\n"; } |
|||
static inline const char* getFormatStringOut(s16 v) { TYPES_H_FTAG; return "%hd\n"; } |
|||
static inline const char* getFormatStringOut(s32 v) { TYPES_H_FTAG; return "%d\n"; } |
|||
static inline const char* getFormatStringOut(s64 v) { TYPES_H_FTAG; return "%lld\n"; } |
|||
|
|||
static inline const char* getFormatStringOut(float v) { TYPES_H_FTAG; return "%f\n"; } |
|||
static inline const char* getFormatStringOut(double v) { TYPES_H_FTAG; return "%f\n"; } |
|||
|
|||
// important constraint - strings need to be wrapped in double-quotes.
|
|||
// the sentinel value 'null' without quotations is used to denote null values, which means
|
|||
// if strings were not wrapped in double quotes, you would not be able to distinguish null
|
|||
// values from the literal string "null".
|
|||
static inline const char* getFormatStringOut(char* v) { TYPES_H_FTAG; return "\"%s\"\n"; } |
|||
static inline const char* getFormatStringOut(const char* v) { TYPES_H_FTAG; return "\"%s\"\n"; } |
|||
|
|||
#ifdef _USING_GLM_TYPES__
|
|||
static inline const char* getFormatStringOut(glm::vec<2, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; return "%f %f\n"; } |
|||
static inline const char* getFormatStringOut(glm::vec<3, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; return "%f %f %f\n"; } |
|||
static inline const char* getFormatStringOut(glm::vec<4, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; return "%f %f %f %f\n"; } |
|||
|
|||
static inline const char* getFormatStringOut(glm::mat<2, 2, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; return "%f %f %f %f\n"; } |
|||
static inline const char* getFormatStringOut(glm::mat<3, 3, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; return "%f %f %f %f %f %f %f %f %f\n"; } |
|||
static inline const char* getFormatStringOut(glm::mat<4, 4, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; return "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n"; } |
|||
|
|||
#endif
|
|||
|
|||
#define SERIALIZE_H_FUNC_BODY str->appendf(getFormatStringOut(v), v);
|
|||
void serialize(String* str, u8 v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
void serialize(String* str, u16 v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
void serialize(String* str, u32 v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
void serialize(String* str, u64 v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
void serialize(String* str, s8 v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
void serialize(String* str, s16 v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
void serialize(String* str, s32 v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
void serialize(String* str, s64 v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
void serialize(String* str, float v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
void serialize(String* str, double v) { TYPES_H_FTAG; SERIALIZE_H_FUNC_BODY } |
|||
|
|||
template<typename T> // do I really need a template for this?
|
|||
static inline void deserializeInteger(char** buffer, T* v) { |
|||
TYPES_H_FTAG; |
|||
char* _buffer = *buffer; |
|||
T value = 0; |
|||
|
|||
// skip leading whitespace - all our functions do this, so gotta
|
|||
while (String::isAsciiWhitespace(*_buffer)) { |
|||
_buffer++; |
|||
} |
|||
|
|||
// check the first character for a negative sign
|
|||
bool negative = false; |
|||
if (_buffer[0] == '-') { |
|||
negative = true; |
|||
_buffer++; |
|||
} |
|||
|
|||
// parse the digits of the number. doesn't account for overflow
|
|||
while (String::isDigit(*_buffer)) { |
|||
value = 10 * value + (*_buffer++ - '0'); |
|||
} |
|||
|
|||
// make it negative if the first character was '-'
|
|||
if (negative) value *= -1; |
|||
|
|||
// store the value back into v
|
|||
*v = value; |
|||
|
|||
// if the original pointer is the same as the current, we didn't parse anything
|
|||
massert(_buffer != *buffer, "tried to parse an integer, and found nothing"); |
|||
|
|||
// report back how far we advanced the buffer
|
|||
*buffer = _buffer; |
|||
} |
|||
|
|||
// just for integers, signed/unsigned including size_t
|
|||
#define SERIALIZE_H_DESERIALIZE_FUNC_BODY deserializeInteger(buffer, v);
|
|||
void deserialize(char** buffer, u8* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY } |
|||
void deserialize(char** buffer, u16* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY } |
|||
void deserialize(char** buffer, u32* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY } |
|||
void deserialize(char** buffer, u64* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY } |
|||
void deserialize(char** buffer, s8* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY } |
|||
void deserialize(char** buffer, s16* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY } |
|||
void deserialize(char** buffer, s32* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY } |
|||
void deserialize(char** buffer, s64* v) { SERIALIZE_H_DESERIALIZE_FUNC_BODY } |
|||
|
|||
// base value = ceil(log10(2^64)) or ceil(log10(2^32))
|
|||
// is this right? lemire's paper mentions you only need 17 digits for 64 bit floats: https://lemire.me/en/publication/arxiv2101/
|
|||
// +1 for 'e'
|
|||
// +1 for '.'
|
|||
// +1 for '^'
|
|||
// +1 for '-'/'+'
|
|||
static const u32 BINARY32_MAX_CHARS = 14; |
|||
static const u32 BINARY64_MAX_CHARS = 24; |
|||
|
|||
void deserialize(char** buffer, float* v) { |
|||
TYPES_H_FTAG; |
|||
char* _buffer = *buffer; |
|||
while (String::isAsciiWhitespace(*_buffer)) _buffer++; |
|||
fast_float::from_chars_result result = fast_float::from_chars(_buffer, _buffer + BINARY32_MAX_CHARS, *v); |
|||
massert(result.ec == std::errc(), "failed to parse a float"); |
|||
*buffer = ( |