2017-06-20 12:23:34 +00:00
// Dqn.h Usage
2018-01-18 09:25:44 +00:00
// =================================================================================================
2017-04-09 05:08:31 +00:00
/*
2018-05-17 11:19:58 +00:00
// Define this wherever you want access to DQN code that uses the platform.
2018-07-04 14:47:11 +00:00
# define DQN_PLATFORM_HEADER // Enable function prototypes for xplatform/platform code
# define DQN_IMPLEMENTATION // Enable the implementation
# define DQN_PLATFORM_IMPLEMENTATION // Enable platform specific implementation on Win32 you must link against user32.lib and kernel32.lib
# define DQN_MAKE_STATIC // Make all functions be static
2018-05-17 11:19:58 +00:00
# include "dqn.h"
2017-04-09 05:08:31 +00:00
*/
2017-06-22 06:16:28 +00:00
2017-06-23 13:12:20 +00:00
// Conventions
// All data structures fields are exposed by default, with exceptions here and there. The rationale
// is I rarely go into libraries and start changing values around in fields unless I know the
// implementation OR we're required to fill out a struct for some function.
// Just treat all struct fields to be internal and read-only unless explicitly stated otherwise.
2017-06-22 04:43:53 +00:00
// Table Of Contents #TOC #TableOfContents
2018-01-18 09:25:44 +00:00
// =================================================================================================
2017-06-22 04:43:53 +00:00
// You can search by #<entry> to jump straight to the section.
2017-06-22 06:16:28 +00:00
// The first match is the public API, the next matche(s) are the implementation
2017-06-22 04:43:53 +00:00
2018-09-10 12:49:17 +00:00
// #Preprocessor Checks
2017-06-22 08:10:44 +00:00
// #Portable Code
2018-09-10 12:49:17 +00:00
// #Library Settings
// #Win32 Prototypes
2018-06-11 06:26:57 +00:00
// #DqnSprintf Cross-platform Sprintf Implementation (Public Domain lib stb_sprintf)
2018-09-10 12:49:17 +00:00
// #DqnDefer Macro helper/template to implement defer in C++
2018-06-11 06:26:57 +00:00
// #DqnAssert Assertions and Logging
2018-09-10 12:49:17 +00:00
// #DqnAllocator Generic allocation API for Dqn Data Structures
2018-08-19 01:59:04 +00:00
// #DqnSlice A ptr and length into memory but doesn't imply ownership of memory
// #DqnBuffer Typedef to slices, but indicates ownership of the memory
2018-09-10 12:49:17 +00:00
// #DqnFixedString Fixed sized strings at compile time.
// #DqnMem Memory Allocation
// #DqnMemStack Memory Allocator, Push, Pop Style
// #DqnLogger
// #DqnArray Dynamic Array using Templates
2018-06-11 06:26:57 +00:00
// #DqnChar Char Operations (IsDigit(), IsAlpha() etc)
// #DqnStr Str Operations (Str_Len(), Str_Copy() etc)
// #DqnWChar WChar Operations (IsDigit(), IsAlpha() etc)
2018-09-10 12:49:17 +00:00
// #DqnWStr WStr Operations (Str_Len(), Str_Copy() etc)
// #DqnString String library
// #DqnRndPCG 32 bit Random Number Generator using PCG (ints and floats)
// #Dqn_* Random utility functions
// #DqnFixedPool Pool objects
2018-07-04 15:04:49 +00:00
// #DqnPool Pool objects
2018-06-11 06:26:57 +00:00
// #DqnHash Hashing using Murmur
// #DqnMath Simple Math Helpers (Lerp etc.)
// #DqnV2 2D Math Vectors
// #DqnV3 3D Math Vectors
// #DqnV4 4D Math Vectors
// #DqnMat4 4x4 Math Matrix
// #DqnRect Rectangles
2018-08-18 06:14:29 +00:00
// #DqnJson Zero Allocation Json Parser
2017-06-22 04:43:53 +00:00
2017-06-22 08:10:44 +00:00
// #XPlatform (Win32 & Unix)
2018-07-14 13:00:25 +00:00
// #DqnVArray Array backed by virtual memory
// #DqnVHashTable Hash Table using templates backed by virtual memory
// #DqnFile File I/O (Read, Write, Delete)
// #DqnTimer High Resolution Timer
// #DqnLock Mutex Synchronisation
// #DqnJobQueue Multithreaded Job Queue
// #DqnAtomic Interlocks/Atomic Operations
// #DqnOS Common Platform API helpers
2017-06-22 04:43:53 +00:00
2017-06-22 08:10:44 +00:00
// #Platform
// - #DqnWin32 Common Win32 API Helpers
2017-06-22 04:43:53 +00:00
2017-06-26 11:46:28 +00:00
// TODO
// - Win32
// - Get rid of reliance on MAX_PATH
2017-06-28 12:47:27 +00:00
//
2018-03-08 15:03:05 +00:00
// - Mbuildake lib compile and run on Linux with GCC using -03
2017-11-10 12:14:50 +00:00
// - Make DqnV* operations be static to class for consistency?
2017-06-26 11:46:28 +00:00
2018-09-10 12:49:17 +00:00
// #Preprocessor Checks
2018-01-18 09:25:44 +00:00
// =================================================================================================
// This needs to be above the portable layer so that, if the user requests a platform
// implementation, platform specific implementations in the portable layer will get activated.
2017-07-03 05:17:09 +00:00
# if (defined(_WIN32) || defined(_WIN64))
2018-07-09 14:53:54 +00:00
# define DQN_IS_WIN32 1
2018-07-04 14:47:11 +00:00
# else
2018-07-09 14:53:54 +00:00
# define DQN_IS_UNIX 1
2017-07-03 05:17:09 +00:00
# endif
2018-07-04 14:47:11 +00:00
# if defined(DQN_PLATFORM_IMPLEMENTATION)
2018-06-13 13:16:05 +00:00
# define DQN__XPLATFORM_LAYER 1
2017-06-20 12:23:34 +00:00
# endif
2017-06-22 08:10:44 +00:00
// #Portable Code
2018-01-18 09:25:44 +00:00
// =================================================================================================
2017-06-20 12:23:34 +00:00
# ifndef DQN_H
# define DQN_H
2017-06-25 15:57:51 +00:00
2017-05-01 06:40:52 +00:00
# ifdef DQN_MAKE_STATIC
2018-05-17 11:19:58 +00:00
# define DQN_FILE_SCOPE static
2017-05-01 06:40:52 +00:00
# else
2018-05-17 11:19:58 +00:00
# define DQN_FILE_SCOPE
2017-05-01 06:40:52 +00:00
# endif
2017-04-11 11:05:40 +00:00
2017-05-01 06:40:52 +00:00
# include <stdint.h> // For standard types
2017-06-21 04:10:53 +00:00
# include <stddef.h> // For standard types
# include <string.h> // memmove
2018-03-08 15:03:05 +00:00
# include <stdarg.h> // va_list
2018-05-17 11:19:58 +00:00
# include <float.h> // FLT_MAX
2017-04-09 05:08:31 +00:00
# define LOCAL_PERSIST static
# define FILE_SCOPE static
2018-01-24 03:58:03 +00:00
using usize = size_t ;
using isize = ptrdiff_t ;
2017-04-09 05:08:31 +00:00
2018-12-15 14:58:11 +00:00
using uint = unsigned int ;
using u64 = uint64_t ;
using u32 = uint32_t ;
using u16 = uint16_t ;
using u8 = uint8_t ;
2017-04-09 05:08:31 +00:00
2018-01-24 03:58:03 +00:00
using i64 = int64_t ;
using i32 = int32_t ;
using i16 = int16_t ;
using i8 = int8_t ;
2018-05-18 13:19:50 +00:00
using b32 = i32 ;
2018-01-24 03:58:03 +00:00
using f64 = double ;
using f32 = float ;
2017-04-09 05:08:31 +00:00
2018-07-14 16:17:34 +00:00
# define DQN_F32_MIN -FLT_MAX
# define DQN_I64_MAX INT64_MAX
# define DQN_U64_MAX UINT64_MAX
2017-05-25 15:10:32 +00:00
2018-02-19 13:21:00 +00:00
# define DQN_TERABYTE(val) (DQN_GIGABYTE(val) * 1024LL)
2017-04-17 12:48:20 +00:00
# define DQN_TERABYTE(val) (DQN_GIGABYTE(val) * 1024LL)
# define DQN_GIGABYTE(val) (DQN_MEGABYTE(val) * 1024LL)
# define DQN_MEGABYTE(val) (DQN_KILOBYTE(val) * 1024LL)
# define DQN_KILOBYTE(val) ((val) * 1024LL)
2018-03-10 14:23:04 +00:00
# define DQN_DAY_TO_S(val) ((DQN_HOUR_TO_S(val)) * 24)
# define DQN_HOUR_TO_S(val) ((DQN_MINUTE_TO_S(val)) * 60)
# define DQN_MINUTE_TO_S(val) ((val) * 60)
2018-02-07 05:39:40 +00:00
2018-02-02 08:34:44 +00:00
# define DQN_ALIGN_POW_N(val, align) ((((usize)val) + ((usize)align-1)) & (~(usize)(align-1)))
2017-05-01 11:03:27 +00:00
# define DQN_ALIGN_POW_4(val) DQN_ALIGN_POW_N(val, 4)
2017-04-10 08:40:15 +00:00
# define DQN_INVALID_CODE_PATH 0
2018-01-04 05:07:18 +00:00
# define DQN_CHAR_COUNT(charArray) DQN_ARRAY_COUNT(charArray) - 1
2017-04-10 08:40:15 +00:00
# define DQN_ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
2017-04-09 05:08:31 +00:00
2017-04-10 08:40:15 +00:00
# define DQN_PI 3.14159265359f
2017-05-15 07:45:53 +00:00
# define DQN_SQUARED(x) ((x) * (x))
2017-04-10 08:40:15 +00:00
# define DQN_ABS(x) (((x) < 0) ? (-(x)) : (x))
# define DQN_DEGREES_TO_RADIANS(x) ((x * (DQN_PI / 180.0f)))
# define DQN_RADIANS_TO_DEGREES(x) ((x * (180.0f / DQN_PI)))
2017-05-15 07:45:53 +00:00
2018-07-14 16:17:34 +00:00
# define DQN_CLAMP(value, min, max) DQN_MIN(DQN_MAX(value, min), max)
2017-04-10 08:40:15 +00:00
# define DQN_MAX(a, b) ((a) < (b) ? (b) : (a))
# define DQN_MIN(a, b) ((a) < (b) ? (a) : (b))
2017-05-15 07:45:53 +00:00
# define DQN_SWAP(type, a, b) do { type tmp = a; a = b; b = tmp; } while(0)
2017-06-20 09:19:52 +00:00
2018-01-19 10:52:34 +00:00
// NOTE: Directives don't get replaced if there's a stringify or paste (# or ##) so TOKEN_COMBINE2 is needed
// to let directives get expanded (i.e. __COUNTER__), then we can combine.
# define DQN_TOKEN_COMBINE(x, y) x ## y
# define DQN_TOKEN_COMBINE2(x, y) DQN_TOKEN_COMBINE(x, y)
2018-01-20 08:03:29 +00:00
// Produce a unique name with prefix and counter. i.e. where prefix is "data" then it gives "data1"
2018-01-19 10:52:34 +00:00
# define DQN_UNIQUE_NAME(prefix) DQN_TOKEN_COMBINE2(prefix, __COUNTER__)
2018-07-04 14:47:11 +00:00
# define DQN_FOR_EACH(i, lim) for (isize (i) = 0; (i) < (isize)(lim); ++(i))
2018-01-19 10:52:34 +00:00
2018-09-10 12:49:17 +00:00
// #Library Settings
// =================================================================================================
2018-02-19 13:21:00 +00:00
namespace Dqn
{
2018-09-02 11:17:14 +00:00
enum struct ZeroMem { No = 0 , Yes = 1 } ;
enum struct IgnoreCase { No = 0 , Yes = 1 } ;
2018-09-10 12:49:17 +00:00
FILE_SCOPE const bool is_debug = true ;
FILE_SCOPE const bool allow_allocation_tagging = true ;
2018-02-19 13:21:00 +00:00
} ; // namespace Dqn
2018-09-10 12:49:17 +00:00
// #Win32 Prototypes
2018-06-11 06:26:57 +00:00
// =================================================================================================
2018-07-09 14:53:54 +00:00
# if defined(DQN_PLATFORM_HEADER) && defined(DQN_IS_WIN32) && !defined(_WINDOWS_)
using WORD = unsigned short ;
using DWORD = unsigned long ;
using BOOL = int ;
using LONG = long ;
using LONGLONG = long long ;
using HANDLE = void * ;
using HMODULE = HANDLE ;
using HWND = HANDLE ;
using UINT = unsigned int ;
using ULONG = unsigned long ;
using ULONGLONG = unsigned long long ;
using DWORD64 = unsigned long long ;
using BYTE = unsigned char ;
u32 const MB_OK = 0x00000000L ;
HANDLE const INVALID_HANDLE_VALUE = ( ( HANDLE ) ( LONG * ) - 1 ) ;
u32 const MAX_PATH = 260 ;
u32 const INFINITE = 0xFFFFFFFF ;
u32 const CP_UTF8 = 65001 ;
u32 const FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200 ;
u32 const FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 ;
u32 const MEM_COMMIT = 0x00001000 ;
u32 const MEM_RESERVE = 0x00002000 ;
u32 const PAGE_READWRITE = 0x04 ;
u32 const MEM_DECOMMIT = 0x4000 ;
u32 const MEM_RELEASE = 0x8000 ;
2018-07-10 13:01:18 +00:00
u32 const GENERIC_READ = 0x80000000L ;
u32 const GENERIC_WRITE = 0x40000000L ;
u32 const GENERIC_EXECUTE = 0x20000000L ;
u32 const GENERIC_ALL = 0x10000000L ;
u32 const CREATE_NEW = 1 ;
u32 const CREATE_ALWAYS = 2 ;
u32 const OPEN_EXISTING = 3 ;
u32 const OPEN_ALWAYS = 4 ;
u32 const TRUNCATE_EXISTING = 5 ;
u32 const FILE_ATTRIBUTE_NORMAL = 0x00000080 ;
2018-07-09 14:53:54 +00:00
struct RECT
{
LONG left ;
LONG top ;
LONG right ;
LONG bottom ;
} ;
union LARGE_INTEGER
{
struct { DWORD LowPart ; LONG HighPart ; } ;
struct { DWORD LowPart ; LONG HighPart ; } u ;
LONGLONG QuadPart ;
} ;
union ULARGE_INTEGER
{
struct { DWORD LowPart ; DWORD HighPart ; } ;
struct { DWORD LowPart ; DWORD HighPart ; } u ;
ULONGLONG QuadPart ;
} ;
struct SECURITY_ATTRIBUTES
{
DWORD length ;
void * securityDescriptor ;
BOOL inheritHandle ;
} ;
struct PROCESS_INFORMATION
{
void * hProcess ;
void * hThread ;
DWORD dwProcessId ;
DWORD dwThreadId ;
} ;
struct FILETIME
{
DWORD dwLowDateTime ;
DWORD dwHighDateTime ;
} ;
struct WIN32_FILE_ATTRIBUTE_DATA
{
DWORD dwFileAttributes ;
FILETIME ftCreationTime ;
FILETIME ftLastAccessTime ;
FILETIME ftLastWriteTime ;
DWORD nFileSizeHigh ;
DWORD nFileSizeLow ;
} ;
enum GET_FILEEX_INFO_LEVELS
{
GetFileExInfoStandard ,
GetFileExMaxInfoLevel
} ;
struct WIN32_FIND_DATAW
{
DWORD dwFileAttributes ;
FILETIME ftCreationTime ;
FILETIME ftLastAccessTime ;
FILETIME ftLastWriteTime ;
DWORD nFileSizeHigh ;
DWORD nFileSizeLow ;
DWORD dwReserved0 ;
DWORD dwReserved1 ;
wchar_t cFileName [ MAX_PATH ] ;
wchar_t cAlternateFileName [ 14 ] ;
} ;
struct LIST_ENTRY {
struct LIST_ENTRY * Flink ;
struct LIST_ENTRY * Blink ;
} ;
struct RTL_CRITICAL_SECTION_DEBUG
{
WORD Type ;
WORD CreatorBackTraceIndex ;
struct CRITICAL_SECTION * CriticalSection ;
LIST_ENTRY ProcessLocksList ;
DWORD EntryCount ;
DWORD ContentionCount ;
DWORD Flags ;
WORD CreatorBackTraceIndexHigh ;
WORD SpareWORD ;
} ;
struct CRITICAL_SECTION
{
RTL_CRITICAL_SECTION_DEBUG * DebugInfo ;
LONG LockCount ;
LONG RecursionCount ;
HANDLE OwningThread ;
HANDLE LockSemaphore ;
ULONG * SpinCount ;
} ;
struct OVERLAPPED {
ULONG * Internal ;
ULONG * InternalHigh ;
union {
struct {
DWORD Offset ;
DWORD OffsetHigh ;
} ;
void * Pointer ;
} ;
HANDLE hEvent ;
} ;
struct SYSTEM_INFO {
union
{
DWORD dwOemId ;
struct
{
WORD wProcessorArchitecture ;
WORD wReserved ;
} ;
} ;
DWORD dwPageSize ;
void * lpMinimumApplicationAddress ;
void * lpMaximumApplicationAddress ;
DWORD * dwActiveProcessorMask ;
DWORD dwNumberOfProcessors ;
DWORD dwProcessorType ;
DWORD dwAllocationGranularity ;
WORD wProcessorLevel ;
WORD wProcessorRevision ;
} ;
enum LOGICAL_PROCESSOR_RELATIONSHIP
{
RelationProcessorCore ,
RelationNumaNode ,
RelationCache ,
RelationProcessorPackage ,
RelationGroup ,
RelationAll = 0xffff
} ;
typedef unsigned long * KAFFINITY ;
struct GROUP_AFFINITY {
KAFFINITY Mask ;
WORD Group ;
WORD Reserved [ 3 ] ;
} ;
struct PROCESSOR_RELATIONSHIP
{
BYTE Flags ;
BYTE EfficiencyClass ;
BYTE Reserved [ 20 ] ;
WORD GroupCount ;
GROUP_AFFINITY GroupMask [ 1 ] ;
} ;
struct NUMA_NODE_RELATIONSHIP {
DWORD NodeNumber ;
BYTE Reserved [ 20 ] ;
GROUP_AFFINITY GroupMask ;
} ;
enum PROCESSOR_CACHE_TYPE
{
CacheUnified ,
CacheInstruction ,
CacheData ,
CacheTrace
} ;
struct CACHE_RELATIONSHIP
{
BYTE Level ;
BYTE Associativity ;
WORD LineSize ;
DWORD CacheSize ;
PROCESSOR_CACHE_TYPE Type ;
BYTE Reserved [ 20 ] ;
GROUP_AFFINITY GroupMask ;
} ;
struct PROCESSOR_GROUP_INFO
{
BYTE MaximumProcessorCount ;
BYTE ActiveProcessorCount ;
BYTE Reserved [ 38 ] ;
KAFFINITY ActiveProcessorMask ;
} ;
struct GROUP_RELATIONSHIP
{
WORD MaximumGroupCount ;
WORD ActiveGroupCount ;
BYTE Reserved [ 20 ] ;
PROCESSOR_GROUP_INFO GroupInfo [ 1 ] ;
} ;
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX
{
LOGICAL_PROCESSOR_RELATIONSHIP Relationship ;
DWORD Size ;
union
{
PROCESSOR_RELATIONSHIP Processor ;
NUMA_NODE_RELATIONSHIP NumaNode ;
CACHE_RELATIONSHIP Cache ;
GROUP_RELATIONSHIP Group ;
} ;
} ;
2018-09-10 12:49:17 +00:00
struct SYSTEMTIME
{
WORD wYear ;
WORD wMonth ;
WORD wDayOfWeek ;
WORD wDay ;
WORD wHour ;
WORD wMinute ;
WORD wSecond ;
WORD wMilliseconds ;
} ;
struct FILETIME
{
DWORD dwLowDateTime ;
DWORD dwHighDateTime ;
} ;
2018-07-09 14:53:54 +00:00
typedef DWORD ( * LPTHREAD_START_ROUTINE ) ( void * lpThreadParameter ) ;
DWORD64 __rdtsc ( ) ;
void DeleteCriticalSection ( CRITICAL_SECTION * lpCriticalSection ) ;
BOOL DeleteFileA ( char const * lpFileName ) ; // TODO(doyle): Wide versions only
BOOL DeleteFileW ( wchar_t const * lpFileName ) ;
BOOL CloseHandle ( HANDLE hObject ) ;
BOOL CopyFileA ( char const * lpExistingFileName , char const * lpNewFileName , BOOL bFailIfExists ) ;
BOOL CopyFileW ( wchar_t const * lpExistingFileName , wchar_t const * lpNewFileName , BOOL bFailIfExists ) ;
BOOL CloseHandle ( HANDLE * hObject ) ;
2018-09-10 12:49:17 +00:00
HANDLE CreateFileW ( wchar_t const * lpFileName , DWORD dwDesiredAccess , DWORD dwShareMode , SECURITY_ATTRIBUTES * lpSecurityAttributes ,
DWORD dwCreationDisposition , DWORD dwFlagsAndAttributes , HANDLE hTemplateFile ) ;
HANDLE CreateSemaphoreA ( SECURITY_ATTRIBUTES * lpSemaphoreAttributes , long lInitialCount , long lMaximumCount , char const * lpName ) ;
HANDLE CreateThread ( SECURITY_ATTRIBUTES * lpThreadAttributes , size_t dwStackSize , LPTHREAD_START_ROUTINE lpStartAddress ,
void * lpParameter , DWORD dwCreationFlags , DWORD * lpThreadId ) ;
2018-07-09 14:53:54 +00:00
void EnterCriticalSection ( CRITICAL_SECTION * lpCriticalSection ) ;
BOOL FindClose ( HANDLE hFindFile ) ;
HANDLE FindFirstFileW ( wchar_t const * lpFileName , WIN32_FIND_DATAW * lpFindFileData ) ;
BOOL FindNextFileW ( HANDLE hFindFile , WIN32_FIND_DATAW * lpFindFileData ) ;
2018-09-10 12:49:17 +00:00
DWORD FormatMessageA ( DWORD dwFlags , void const * lpSource , DWORD dwMessageId , DWORD dwLanguageId , char * lpBuffer , DWORD nSize , va_list * Arguments ) ;
2018-07-09 14:53:54 +00:00
BOOL GetClientRect ( HWND hWnd , RECT * lpRect ) ;
BOOL GetExitCodeProcess ( HANDLE * hProcess , DWORD * lpExitCode ) ;
BOOL GetFileSizeEx ( HANDLE hFile , LARGE_INTEGER * lpFileSize ) ;
2018-09-10 12:49:17 +00:00
BOOL GetFileAttributesExW ( wchar_t const * lpFileName , GET_FILEEX_INFO_LEVELS fInfoLevelId , void * lpFileInformation ) ;
2018-07-09 14:53:54 +00:00
DWORD GetLastError ( void ) ;
2018-09-10 12:49:17 +00:00
void GetLocalTime ( SYSTEMTIME * lpSystemTime ) ;
2018-07-09 14:53:54 +00:00
DWORD GetModuleFileNameA ( HMODULE hModule , char * lpFilename , DWORD nSize ) ;
void GetNativeSystemInfo ( SYSTEM_INFO * lpSystemInfo ) ;
2018-09-10 12:49:17 +00:00
BOOL GetLogicalProcessorInformationEx ( LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType , SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * Buffer , DWORD * ReturnedLength ) ;
BOOL InitializeCriticalSectionEx ( CRITICAL_SECTION * lpCriticalSection , DWORD dwSpinCount , DWORD Flags ) ;
2018-07-09 14:53:54 +00:00
long InterlockedAdd ( long volatile * Addend , long Value ) ;
long InterlockedCompareExchange ( long volatile * Destination , long Exchange , long Comparand ) ;
void LeaveCriticalSection ( CRITICAL_SECTION * lpCriticalSection ) ;
int MessageBoxA ( HWND hWnd , char const * lpText , char const * lpCaption , UINT uType ) ;
2018-09-10 12:49:17 +00:00
int MultiByteToWideChar ( unsigned int CodePage , DWORD dwFlags , char const * lpMultiByteStr , int cbMultiByte , wchar_t * lpWideCharStr , int cchWideChar ) ;
2018-07-09 14:53:54 +00:00
void OutputDebugStringA ( char const * lpOutputString ) ;
2018-09-10 12:49:17 +00:00
BOOL ReadFile ( HANDLE hFile , void * lpBuffer , DWORD nNumberOfBytesToRead , DWORD * lpNumberOfBytesRead , OVERLAPPED * lpOverlapped ) ;
2018-07-09 14:53:54 +00:00
BOOL ReleaseSemaphore ( HANDLE hSemaphore , long lReleaseCount , long * lpPreviousCount ) ;
BOOL QueryPerformanceFrequency ( LARGE_INTEGER * lpFrequency ) ;
BOOL QueryPerformanceCounter ( LARGE_INTEGER * lpPerformanceCount ) ;
DWORD WaitForSingleObject ( HANDLE * hHandle , DWORD dwMilliseconds ) ;
DWORD WaitForSingleObjectEx ( HANDLE hHandle , DWORD dwMilliseconds , BOOL bAlertable ) ;
2018-09-10 12:49:17 +00:00
int WideCharToMultiByte ( unsigned int CodePage , DWORD dwFlags , wchar_t const * lpWideCharStr , int cchWideChar ,
char * lpMultiByteStr , int cbMultiByte , char const * lpDefaultChar , BOOL * lpUsedDefaultChar ) ;
2018-07-09 14:53:54 +00:00
void Sleep ( DWORD dwMilliseconds ) ;
2018-09-10 12:49:17 +00:00
BOOL WriteFile ( HANDLE hFile , void * const lpBuffer , DWORD nNumberOfBytesToWrite , DWORD * lpNumberOfBytesWritten , OVERLAPPED * lpOverlapped ) ;
void * VirtualAlloc ( void * lpAddress , size_t dwSize , DWORD flAllocationType , DWORD flProtect ) ;
BOOL VirtualFree ( void * lpAddress , size_t dwSize , DWORD dwFreeType ) ;
2018-07-09 14:53:54 +00:00
# endif // defined(DQN_PLATFORM_HEADER) && defined(DQN_IS_WIN32) && !defined(_WINDOWS_)
2018-06-11 06:26:57 +00:00
# ifndef STB_SPRINTF_H_INCLUDE
# define STB_SPRINTF_H_INCLUDE
////////////////////////////////////////////////////////////////////////////////
// #DqnSprintf Public API - Cross-platform Sprintf Implementation
////////////////////////////////////////////////////////////////////////////////
2018-07-04 14:47:11 +00:00
// stb_sprintf - v1.05 - public domain snprintf() implementation
// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
// http://github.com/nothings/stb
//
// allowed types: sc uidBboXx p AaGgEef n
// lengths : h ll j z t I64 I32 I
//
// Contributors:
// Fabian "ryg" Giesen (reformatting)
//
// Contributors (bugfixes):
// github:d26435
// github:trex78
// Jari Komppa (SI suffixes)
// Rohit Nirmal
// Marcin Wojdyr
// Leonard Ritter
//
// LICENSE:
//
// See end of file for license information.
2018-06-11 06:26:57 +00:00
2018-07-04 14:47:11 +00:00
/*
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 8 K with float support , and 4 K without .
As a comparison , when using MSVC static libs , calling sprintf drags
in 16 K .
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 .
2018-06-11 06:26:57 +00:00
FLOATS / DOUBLES :
= = = = = = = = = = = = = = =
2018-07-04 14:47:11 +00:00
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 4 K of code space .
2018-06-11 06:26:57 +00:00
64 - BIT INTS :
= = = = = = = = = = = =
2018-07-04 14:47:11 +00:00
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 .
2018-06-11 06:26:57 +00:00
EXTRAS :
= = = = = = =
Like some GCCs , for integers and floats , you can use a ' ( single quote )
2018-07-04 14:47:11 +00:00
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.8 x / 4.0 x faster than 32 - / 64 - bit MSVC )
" %24d " across all 32 - bit ints ( 4.5 x / 4.2 x faster )
" %x " across all 32 - bit ints ( 4.5 x / 3.8 x faster )
" %08x " across all 32 - bit ints ( 4.3 x / 3.8 x faster )
" %f " across e - 10 to e + 10 floats ( 7.3 x / 6.0 x faster )
" %e " across e - 10 to e + 10 floats ( 8.1 x / 6.0 x faster )
" %g " across e - 10 to e + 10 floats ( 10.0 x / 7.1 x faster )
" %f " for values near e - 300 ( 7.9 x / 6.5 x faster )
" %f " for values near e + 300 ( 10.0 x / 9.1 x faster )
" %e " for values near e - 300 ( 10.1 x / 7.0 x faster )
" %e " for values near e + 300 ( 9.2 x / 6.0 x faster )
" %.320f " for values near e - 300 ( 12.6 x / 11.2 x faster )
" %a " for random values ( 8.6 x / 4.3 x faster )
" %I64d " for 64 - bits with 32 - bit values ( 4.8 x / 3.4 x faster )
" %I64d " for 64 - bits > 32 - bit values ( 4.9 x / 5.5 x faster )
" %s%s%s " for 64 char strings ( 7.1 x / 7.3 x faster )
" ...512 char string... " ( 35.0 x / 32.5 x faster ! )
2018-06-11 06:26:57 +00:00
*/
# if defined(__has_feature)
2018-07-04 14:47:11 +00:00
# if __has_feature(address_sanitizer)
# define STBI__ASAN __attribute__((no_sanitize("address")))
# endif
2018-06-11 06:26:57 +00:00
# endif
# ifndef STBI__ASAN
2018-07-04 14:47:11 +00:00
# define STBI__ASAN
2018-06-11 06:26:57 +00:00
# endif
# ifdef STB_SPRINTF_STATIC
2018-07-04 14:47:11 +00:00
# define STBSP__PUBLICDEC static
# define STBSP__PUBLICDEF static STBI__ASAN
2018-06-11 06:26:57 +00:00
# else
2018-07-04 14:47:11 +00:00
# ifdef __cplusplus
# define STBSP__PUBLICDEC extern "C"
# define STBSP__PUBLICDEF extern "C" STBI__ASAN
# else
# define STBSP__PUBLICDEC extern
# define STBSP__PUBLICDEF STBI__ASAN
# endif
2018-06-11 06:26:57 +00:00
# endif
2018-07-04 14:47:11 +00:00
# include <stdarg.h> // for va_list()
2018-06-11 06:26:57 +00:00
# ifndef STB_SPRINTF_MIN
2018-07-04 14:47:11 +00:00
# define STB_SPRINTF_MIN 512 // how many characters per callback
2018-06-11 06:26:57 +00:00
# endif
2018-07-04 14:47:11 +00:00
typedef char * STBSP_SPRINTFCB ( char * buf , void * user , int len ) ;
2018-06-11 06:26:57 +00:00
# ifndef STB_SPRINTF_DECORATE
2018-07-04 14:47:11 +00:00
# define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
2018-06-11 06:26:57 +00:00
# endif
// Convert a va_list arg list into a buffer. vsnprintf always returns a zero-terminated string (unlike regular snprintf).
// return: The number of characters copied into the buffer
2018-07-04 14:47:11 +00:00
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( vsprintf ) ( char * buf , char const * fmt , va_list va ) ;
2018-06-11 06:26:57 +00:00
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( vsnprintf ) ( char * buf , int count , char const * fmt , va_list va ) ;
// Write format string to buffer. It always writes the whole string and appends a null.
// return: The number of characters copied into the buffer not including the null terminator.
2018-07-04 14:47:11 +00:00
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( sprintf ) ( char * buf , char const * fmt , . . . ) ;
2018-06-11 06:26:57 +00:00
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( snprintf ) ( char * buf , int count , char const * fmt , . . . ) ;
2018-07-04 14:47:11 +00:00
// snprintf() always returns a zero-terminated string (unlike regular snprintf).
2018-06-11 06:26:57 +00:00
// 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
2018-07-04 14:47:11 +00:00
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( vsprintfcb ) ( STBSP_SPRINTFCB * callback , void * user , char * buf , char const * fmt , va_list va ) ;
2018-06-11 06:26:57 +00:00
// Set the comma and period characters to use.
STBSP__PUBLICDEF void STB_SPRINTF_DECORATE ( set_separators ) ( char comma , char period ) ;
# endif // STB_SPRINTF_H_INCLUDE
2018-05-18 13:19:50 +00:00
2018-09-10 12:49:17 +00:00
// #DqnDefer
// =================================================================================================
template < typename Proc >
struct DqnDefer_
{
DqnDefer_ ( Proc p ) : proc ( p ) { }
~ DqnDefer_ ( ) { proc ( ) ; }
Proc proc ;
} ;
struct DqnDeferHelper_
{
template < typename Proc >
DqnDefer_ < Proc > operator + ( Proc proc ) { return DqnDefer_ < Proc > ( proc ) ; } ;
} ;
# define DQN_DEFER const auto DQN_UNIQUE_NAME(dqn_defer_lambda_) = DqnDeferHelper_() + [&]()
2018-01-18 09:25:44 +00:00
// #DqnAssert API
// =================================================================================================
2017-06-20 09:19:52 +00:00
// NOTE: "## __VA_ARGS__" is a GCC hack. Zero variadic arguments won't compile
// because there will be a trailing ',' at the end. Pasting it swallows it. MSVC
// implicitly swallows the trailing comma.
2018-07-14 13:21:57 +00:00
// Always assert are enabled in release mode.
2018-12-15 14:58:11 +00:00
# define DQN_ALWAYS_ASSERT(expr) DQN_ASSERT_MSG(expr, "")
# define DQN_ALWAYS_ASSERTM(expr, msg, ...) DQN_ASSERT_MSG(expr, msg, ## __VA_ARGS__)
2018-07-14 13:21:57 +00:00
2018-09-13 13:58:48 +00:00
// Generate a DqnLogger::Context structure
# define DQN_LOGGER_MAKE_CONTEXT_ \
{ \
( char * ) __FILE__ , DQN_CHAR_COUNT ( __FILE__ ) , ( char * ) __func__ , DQN_CHAR_COUNT ( __func__ ) , __LINE__ \
}
2018-12-15 14:58:11 +00:00
# define DQN_ASSERT(expr) DQN_ASSERT_MSG(expr, "asserted.")
# define DQN_ASSERT_MSG(expr, msg, ...) \
2018-05-17 11:19:58 +00:00
do \
{ \
if ( ! ( expr ) ) \
{ \
2018-09-13 13:58:48 +00:00
dqn_lib_context_ . logger - > Log ( DqnLogger : : Type : : Error , DQN_LOGGER_MAKE_CONTEXT_ , " [ " # expr " ] " msg , # # __VA_ARGS__ ) ; \
2018-05-17 11:19:58 +00:00
( * ( ( int * ) 0 ) ) = 0 ; \
} \
} while ( 0 )
2017-06-20 09:19:52 +00:00
2017-06-22 06:16:28 +00:00
// Assert at compile time by making a type that is invalid depending on the expression result
2018-01-20 08:03:29 +00:00
# define DQN_COMPILE_ASSERT(expr) DQN_COMPILE_ASSERT_INTERNAL(expr, DQN_UNIQUE_NAME(DqnCompileAssertInternal_))
# define DQN_COMPILE_ASSERT_INTERNAL(expr, name) typedef char name[((int)(expr)) * 2 - 1];
2017-06-22 06:16:28 +00:00
2018-09-13 13:58:48 +00:00
struct DqnLibContext
{
struct DqnAllocator * xallocator ;
struct DqnAllocator * allocator ;
struct DqnLogger * logger ;
} ;
extern DqnLibContext dqn_lib_context_ ;
2018-09-10 12:49:17 +00:00
2018-01-24 03:58:03 +00:00
DQN_COMPILE_ASSERT ( sizeof ( isize ) = = sizeof ( usize ) ) ;
2018-09-10 12:49:17 +00:00
// #DqnAllocator
// =================================================================================================
// A generic structure for managing memory allocations for different Dqn data structures.
struct DqnAllocator
{
enum struct Type
{
2018-09-13 13:58:48 +00:00
Default , // Malloc, realloc, free
2018-09-10 12:49:17 +00:00
XAllocator , // Malloc, realloc, free, crash on failure
VirtualMemory , // VirtualAlloc, VirtualFree, mmap, munmap
DqnMemStack ,
} ;
Type type ;
void * user_context ;
DqnAllocator ( ) = default ;
DqnAllocator ( Type type_ ) { * this = { } ; type = type_ ; }
DqnAllocator ( struct DqnMemStack * mem_stack ) { * this = { } ; type = Type : : DqnMemStack ; user_context = mem_stack ; }
void * Malloc ( size_t size , Dqn : : ZeroMem zero = Dqn : : ZeroMem : : No ) ;
void * Realloc ( void * ptr , size_t new_size ) ;
void Free ( void * ptr , size_t old_size ) ;
} ;
2018-08-19 01:59:04 +00:00
// #DqnSlice/#DqnBuffer
2018-01-18 09:25:44 +00:00
// =================================================================================================
2018-08-19 01:59:04 +00:00
// NOTE: A slice and buffer is the same thing but, slices have the pre-existing concepts of being a
// a ptr and length into pre-existing allocated memory. So instead, I use buffer to indicate the
// data structure it self owns the memory it's holding.
2018-03-08 15:03:05 +00:00
template < typename T >
struct DqnSlice
{
2018-08-19 01:59:04 +00:00
union { T * data ; T * str ; } ;
int len ;
2017-05-04 16:37:38 +00:00
2018-12-15 14:58:11 +00:00
inline int SizeInBytes ( ) const { return len * sizeof ( T ) ; }
operator bool ( ) const { bool result = ( str ! = nullptr ) ; return result ; }
2018-05-17 11:19:58 +00:00
DqnSlice ( ) = default ;
2018-08-19 01:59:04 +00:00
DqnSlice ( T * data_ , int len_ ) : data ( data_ ) , len ( len_ ) { }
2018-03-08 15:03:05 +00:00
} ;
2018-09-02 03:28:19 +00:00
template < typename T >
2018-08-19 01:59:04 +00:00
using DqnBuffer = DqnSlice < T > ;
# define DQN_BUFFER_STR_LIT(literal) DqnBuffer<char const>(literal, DQN_CHAR_COUNT(literal))
# define DQN_BUFFER_STRCMP(a, b, ignore_case) ((a).len == (b).len && (DqnStr_Cmp((char *)((a).str), (char *)((b).str), (a).len, ignore_case) == 0))
# define DQN_BUFFER_MEMCMP(a, b) ((a).len == (b).len && (DqnMem_Cmp((void *)((a).str), (void *)((b).str), (a).len) == 0))
2018-09-02 03:28:19 +00:00
# define DQN_SLICE_STRCMP(a, b, ignore_case) ((a).len == (b).len && (DqnStr_Cmp((char *)((a).str), (char *)((b).str), (a).len, ignore_case) == 0))
# define DQN_SLICE_MEMCMP(a, b) ((a).len == (b).len && (DqnMem_Cmp((void *)((a).str), (void *)((b).str), (a).len) == 0))
2017-10-29 01:45:00 +00:00
2018-12-15 14:58:11 +00:00
template < typename T >
DQN_FILE_SCOPE DqnBuffer < T > DqnBuffer_Make ( DqnAllocator * allocator , int num , Dqn : : ZeroMem clear = Dqn : : ZeroMem : : Yes )
{
DqnBuffer < T > result = { } ;
result . len = num ;
result . data = static_cast < T * > ( allocator - > Malloc ( num * sizeof ( T ) , clear ) ) ;
return result ;
}
2018-09-10 12:49:17 +00:00
template < typename T >
DQN_FILE_SCOPE DqnBuffer < T > DqnBuffer_Copy ( DqnAllocator * allocator , T const * data , int len )
{
DqnBuffer < T > result ;
result . len = len ;
result . data = static_cast < T * > ( allocator - > Malloc ( len * sizeof ( T ) , Dqn : : ZeroMem : : No ) ) ;
2018-09-13 13:58:48 +00:00
DqnMem_Copy ( result . data , data , len * sizeof ( T ) ) ;
2018-09-10 12:49:17 +00:00
return result ;
}
2017-10-29 01:45:00 +00:00
2018-09-10 12:49:17 +00:00
template < typename T >
DQN_FILE_SCOPE DqnBuffer < T > DqnBuffer_CopyAndNullTerminate ( DqnAllocator * allocator , T const * data , int len )
{
DqnBuffer < T > result ;
result . len = len ;
result . data = static_cast < T * > ( allocator - > Malloc ( ( len + 1 ) * sizeof ( T ) , Dqn : : ZeroMem : : No ) ) ;
2018-09-13 13:58:48 +00:00
DqnMem_Copy ( result . data , data , len * sizeof ( T ) ) ;
2018-09-10 12:49:17 +00:00
result . data [ len ] = 0 ;
return result ;
}
2017-10-29 01:45:00 +00:00
2018-09-10 12:49:17 +00:00
// #DqnFixedString Public API - Fixed sized strings at compile time
// =================================================================================================
template < int MAX >
struct DqnFixedString
{
int len ;
char str [ MAX ] ;
2018-01-20 08:03:29 +00:00
2018-09-10 12:49:17 +00:00
DqnFixedString ( ) : len ( 0 ) { this - > str [ 0 ] = 0 ; }
2018-12-12 13:23:02 +00:00
DqnFixedString ( char const * fmt , . . . ) { va_list va ; va_start ( va , fmt ) ; len = stbsp_vsnprintf ( str , MAX , fmt , va ) ; va_end ( va ) ; DQN_ASSERT ( len < MAX ) ; }
DqnFixedString ( char const * fmt , va_list va ) { len = stbsp_vsnprintf ( str , MAX , fmt , va ) ; DQN_ASSERT ( len < MAX ) ; }
DqnFixedString ( DqnSlice < char const > const & other ) { DqnMem_Copy ( str , other . str , other . len ) ; len = other . len ; str [ len ] = 0 ; }
DqnFixedString ( DqnSlice < char > const & other ) { DqnMem_Copy ( str , other . str , other . len ) ; len = other . len ; str [ len ] = 0 ; }
DqnFixedString ( DqnFixedString const & other ) { if ( this ! = & other ) { DqnMem_Copy ( str , other . str , other . len ) ; len = other . len ; str [ len ] = 0 ; } }
DqnFixedString & operator + = ( char const * other ) { int other_len = Dqn_StrLen ( other ) ; DqnMem_Copy ( str + len , other , other_len ) ; len + = other_len ; str [ len ] = 0 ; DQN_ASSERT ( len < MAX ) ; return * this ; }
DqnFixedString & operator + = ( DqnSlice < char const > const & other ) { DqnMem_Copy ( str + len , other . str , other . len ) ; len + = other . len ; str [ len ] = 0 ; DQN_ASSERT ( len < MAX ) ; return * this ; }
DqnFixedString & operator + = ( DqnSlice < char > const & other ) { DqnMem_Copy ( str + len , other . str , other . len ) ; len + = other . len ; str [ len ] = 0 ; DQN_ASSERT ( len < MAX ) ; return * this ; }
DqnFixedString & operator + = ( DqnFixedString const & other ) { DqnMem_Copy ( str + len , other . str , other . len ) ; len + = other . len ; str [ len ] = 0 ; DQN_ASSERT ( len < MAX ) ; return * this ; }
DqnFixedString operator + ( char const * other ) { auto result = * this ; int other_len = Dqn_StrLen ( other ) ; DqnMem_Copy ( str + len , other , other_len ) ; len + = other_len ; str [ len ] = 0 ; DQN_ASSERT ( len < MAX ) ; return result ; }
DqnFixedString operator + ( DqnSlice < char const > const & other ) { auto result = * this ; DqnMem_Copy ( str + len , other . str , other . len ) ; len + = other . len ; str [ len ] = 0 ; DQN_ASSERT ( len < MAX ) ; return result ; }
DqnFixedString operator + ( DqnSlice < char > const & other ) { auto result = * this ; DqnMem_Copy ( str + len , other . str , other . len ) ; len + = other . len ; str [ len ] = 0 ; DQN_ASSERT ( len < MAX ) ; return result ; }
DqnFixedString operator + ( DqnFixedString const & other ) { auto result = * this ; DqnMem_Copy ( str + len , other . str , other . len ) ; len + = other . len ; str [ len ] = 0 ; DQN_ASSERT ( len < MAX ) ; return result ; }
2018-01-20 08:03:29 +00:00
2018-09-10 12:49:17 +00:00
// Xprintf functions always modifies buffer and null-terminates even with insufficient buffer size.
// Asserts on failure if DQN_ASSERT is defined.
// return: The number of characters copied to the buffer
2018-12-12 13:23:02 +00:00
int SprintfAppend ( char const * fmt , . . . ) { va_list va ; va_start ( va , fmt ) ; int extra = stbsp_sprintf ( str + len , MAX , fmt , va ) ; len + = extra ; DQN_ASSERT ( len < MAX ) ; va_end ( va ) ; return extra ; }
int VSprintfAppend ( char const * fmt , va_list va ) { int extra = stbsp_vsnprintf ( str + len , MAX , fmt , va ) ; len + = extra ; DQN_ASSERT ( len < MAX ) ; return extra ; }
void NullTerminate ( ) { str [ len ] = 0 ; } // NOTE: If you modify the storage directly, this can be handy.
void Clear ( Dqn : : ZeroMem clear = Dqn : : ZeroMem : : No ) { if ( clear = = Dqn : : ZeroMem : : Yes ) DqnMem_Set ( str , 0 , MAX ) ; * this = { } ; }
2018-09-10 12:49:17 +00:00
} ;
2018-06-25 10:51:18 +00:00
2018-09-10 12:49:17 +00:00
using DqnFixedString16 = DqnFixedString < 16 > ;
using DqnFixedString32 = DqnFixedString < 32 > ;
using DqnFixedString64 = DqnFixedString < 64 > ;
using DqnFixedString128 = DqnFixedString < 128 > ;
using DqnFixedString256 = DqnFixedString < 256 > ;
using DqnFixedString512 = DqnFixedString < 512 > ;
using DqnFixedString1024 = DqnFixedString < 1024 > ;
using DqnFixedString2048 = DqnFixedString < 2048 > ;
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
// #DqnMem
// =================================================================================================
// TODO(doyle): Use platform allocation, fallback to malloc if platform not defined
DQN_FILE_SCOPE void * DqnMem_Alloc ( usize size ) ;
DQN_FILE_SCOPE void * DqnMem_XAlloc ( usize size ) ;
DQN_FILE_SCOPE void * DqnMem_XCalloc ( usize size ) ;
DQN_FILE_SCOPE void DqnMem_Clear ( void * memory , u8 clear_val , usize size ) ;
DQN_FILE_SCOPE void * DqnMem_Realloc ( void * memory , usize new_size ) ;
DQN_FILE_SCOPE void * DqnMem_XRealloc ( void * memory , usize new_size ) ;
DQN_FILE_SCOPE void DqnMem_Free ( void * memory ) ;
DQN_FILE_SCOPE void DqnMem_Copy ( void * dest , void const * src , usize num_bytes_to_copy ) ;
DQN_FILE_SCOPE void * DqnMem_Set ( void * dest , u8 value , usize num_bytes_to_set ) ;
DQN_FILE_SCOPE int DqnMem_Cmp ( void const * src , void const * dest , usize num_bytes ) ;
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
// #DqnMemTracker
2018-03-08 15:03:05 +00:00
// =================================================================================================
2018-09-10 12:49:17 +00:00
// Allocation Layout
// +--------------------+-------------------------------------------------------------------------+------------------------+-----------------+
// | Ptr From Allocator | Offset To Src | Alignment | Alloc Type | Alloc Amount | B. Guard (Opt.) | Aligned Ptr For Client | B. Guard (Opt.) |
// +--------------------+-------------------------------------------------------------------------+------------------------+-----------------+
// Ptr From Allocator: The pointer returned by the allocator, not aligned
// Offset To Src: Number of bytes to subtract from the "Aligned Ptr For Client" to return to the "Ptr From Allocator"
// Alignment: The pointer given to the client is aligned to this power of two boundary
// Alloc Type: If the allocation was allocated from the head or tail of the memory block (mainly for memstacks).
// Alloc Amount: The requested allocation amount by the client (so does not include metadata)
// B. Guard: Bounds Guard value.
// Aligned Ptr For Client: The allocated memory for the client.
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
# pragma pack(push, 1)
struct DqnPtrHeader
{
u8 offset_to_src_ptr ; // Offset to subtract from the client ptr to receive the allocation ptr
u8 alignment ;
u8 alloc_type ; // TODO(doyle): alloc_type is a memstack specific thing
usize alloc_amount ;
} ;
# pragma pack(pop)
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
struct DqnMemTracker
{
enum Flag
{
None = 0 ,
TrackPtr = ( 1 < < 0 ) ,
BoundsGuard = ( 1 < < 1 ) ,
TagAllocation = ( 1 < < 2 ) ,
Simple = ( TrackPtr | BoundsGuard ) ,
All = ( TrackPtr | BoundsGuard | TagAllocation ) ,
} ;
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
struct TaggedAllocation
{
DqnBuffer < char > filename ;
DqnBuffer < char > function ;
size_t line_num ;
isize bytes_allocated ;
TaggedAllocation * next ;
} ;
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
static u32 const HEAD_GUARD_VALUE = 0xCAFEBABE ;
static u32 const TAIL_GUARD_VALUE = 0xDEADBEEF ;
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
void * * ptrs ; // If track_ptr was set, ptrs is an array to all the pointers that get passed through SetupPtr, otherwise null
int ptrs_len ;
int ptrs_max ;
u32 bounds_guard_size ; // If bounds_guard was set, sizeof(GUARD_VALUE) otherwise 0
u16 tagged_allocs_max ;
TaggedAllocation * * tagged_allocs ;
u16 * tagged_allocs_used_list ;
u16 tagged_allocs_used_index ;
void Init ( Flag flag ) ;
void Free ( ) ;
bool IsTrackingPtrs ( ) const { return ( ptrs ! = nullptr ) ; }
bool IsGuardingBounds ( ) const { return ( bounds_guard_size > 0 ) ; }
bool IsTaggingAllocations ( ) const { return ( tagged_allocs_used_index > 0 ) ; } // TODO(doyle): Just store the flags instead of these unreasonable checks
# define DQN_STRINGIFY(val) DQN_STRINGIFY2(val)
# define DQN_STRINGIFY2(val) #val
# define DQN_MEMTRACKER_TAG_ALLOC(tracker, size) (tracker)->Tag_(DQN_BUFFER_STR_LIT(__FILE__), DQN_BUFFER_STR_LIT(__func__), __LINE__, DQN_BUFFER_STR_LIT(__FILE__ DQN_STRINGIFY(__LINE__)), (isize)size)
void Tag_ ( DqnBuffer < const char > filename , DqnBuffer < const char > function , int line_num , DqnBuffer < const char > filename_line_num_data , isize bytes ) ;
// MemTracker setups up the ptr by adding a DqnPtrHeader to each given ptr. When the client
// requests an allocation, use GetAllocationSize() to get the adjusted size necessary for adding
// the tracking metadata, then pass in the allocated ptr into SetupPtr.
// If MemTracker was init with
// - track_ptr: it also tracks each ptr passed in
// - bounds_guard: it also adds a 4 byte magic value on both sides of the ptr
// size: The original size requested, i.e. before GetAllocationSize adjusted it
void * SetupPtr ( void * ptr , isize size , u8 alignment ) ;
void RemovePtr ( void * ptr ) ;
void RemovePtrRange ( void * begin , void * end ) ;
void CheckPtrs ( ) const ;
usize GetAllocationSize ( usize size , u8 alignment = 1 ) const { return sizeof ( DqnPtrHeader ) + bounds_guard_size + ( alignment - 1 ) + size + bounds_guard_size ; }
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
// ptr: The ptr given to the client when allocating.
u32 * PtrToHeadGuard ( char * ptr ) const { return reinterpret_cast < u32 * > ( ptr - bounds_guard_size ) ; }
u32 * PtrToTailGuard ( char * ptr ) const { return reinterpret_cast < u32 * > ( ptr + PtrToHeader ( ptr ) - > alloc_amount ) ; }
DqnPtrHeader * PtrToHeader ( char * ptr ) const { return reinterpret_cast < DqnPtrHeader * > ( ptr - bounds_guard_size - sizeof ( DqnPtrHeader ) ) ; }
2018-02-02 08:34:44 +00:00
} ;
2018-09-10 12:49:17 +00:00
// #DqnMemStack
2018-02-02 08:34:44 +00:00
// =================================================================================================
2018-09-10 12:49:17 +00:00
// DqnMemStack is a memory allocator in a stack like, push-pop style. It
// pre-allocates a block of memory at init and sub-allocates from this block to
// take advantage of memory locality. You can allocate and grow the stack from
// the bottom and/or allocate from the top and grow downwards until space is
// insufficient in the memory block.
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
// When an allocation requires a larger amount of memory than available in the
// block then the MemStack will allocate a new block of sufficient size for you
// in Push(..). This DOES mean that occasionally there will be wasted space at
// the end of each block and is a tradeoff for memory locality against optimal
// space usage.
2018-08-27 14:25:08 +00:00
2018-09-10 12:49:17 +00:00
// My convention with using a memory stack is, any function that takes a memory
// stack must revert all allocations from the the end of the stack in the scope
// it was used in. If the function takes an allocator, then it must always do
// some operation that allocates persistent data into the head of the allocator.
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
// If a function requires an allocator purely for temporary purposes, then there
// always exists some non-user facing global allocator that is accessible which
// it can use.
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
# define DQN_MEMSTACK_PUSH(stack, size) DQN_MEMSTACK_PUSH_ARRAY(stack, char, size)
# define DQN_MEMSTACK_PUSH_STRUCT(stack, Type) DQN_MEMSTACK_PUSH_ARRAY(stack, Type, 1)
# define DQN_MEMSTACK_PUSH_ARRAY(stack, Type, num) (Type *)(stack)->Push_(sizeof(Type) * (num)); DQN_MEMTRACKER_TAG_ALLOC(&(stack)->tracker, sizeof(Type) * num)
2018-09-13 13:58:48 +00:00
# define DQN_MEMSTACK_PUSH_BACK(stack, size) DQN_MEMSTACK_PUSH_BACK_ARRAY(stack, char, size)
# define DQN_MEMSTACK_PUSH_BACK_STRUCT(stack, Type) DQN_MEMSTACK_PUSH_BACK_ARRAY(stack, Type, 1)
# define DQN_MEMSTACK_PUSH_BACK_ARRAY(stack, Type, num) (Type *)(stack)->Push_(sizeof(Type) * (num), DqnMemStack::PushType::Opposite); DQN_MEMTRACKER_TAG_ALLOC(&(stack)->tracker, sizeof(Type) * num)
2018-09-10 12:49:17 +00:00
struct DqnMemStack
2018-03-08 15:03:05 +00:00
{
2018-09-10 12:49:17 +00:00
static const i32 MINIMUM_BLOCK_SIZE = DQN_KILOBYTE ( 64 ) ;
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
enum Flag
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
NonExpandable = ( 1 < < 0 ) , // Disallow additional memory blocks when full.
NonExpandableAssert = ( 1 < < 1 ) , // Assert when non-expandable is set and we run out of space
DefaultAllocateTail = ( 1 < < 2 ) , // Allocate to tail when push_type is unspecified, otherwise allocate to head
} ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
struct Info // Statistics of the memory stack.
{
isize total_used ;
isize total_size ;
isize wasted_size ;
i32 num_blocks ;
} ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
struct Block
{
char * memory ;
isize size ;
char * head ;
char * tail ;
Block * prev_block ;
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
Block ( ) = default ;
Block ( void * memory_ , isize size_ ) { * this = { } ; memory = ( char * ) memory_ ; size = size_ ; head = memory ; tail = memory + size ; }
isize Usage ( ) const { return size - ( tail - head ) ; }
} ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
DqnMemTracker tracker ; // Metadata for managing ptr allocation
DqnAllocator * block_allocator ; // Memory block allocator
Block * block ; // Backing memory storage
u32 flags ;
i32 mem_region_count ; // The number of memory regions in use
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
// Initialisation
// =============================================================================================
DqnMemStack ( ) = default ; // Zero Is Initialisation, on first allocation LazyInit() is called.
DqnMemStack ( void * mem , isize size , Dqn : : ZeroMem clear , u32 flags_ = 0 , DqnMemTracker : : Flag flags = DqnMemTracker : : Flag : : Simple ) ; // Use fixed memory from the given buffer. Assert after buffer is full.
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
// Init and alloc additional memory blocks when full, but only if NonExpandable flag is not set.
2018-09-13 13:58:48 +00:00
DqnMemStack ( isize size , Dqn : : ZeroMem clear , u32 flags_ = 0 , DqnMemTracker : : Flag tracker_flags = DqnMemTracker : : Simple , DqnAllocator * block_allocator_ = dqn_lib_context_ . allocator ) { LazyInit ( size , clear , flags_ , tracker_flags , block_allocator_ ) ; }
void LazyInit ( isize size , Dqn : : ZeroMem clear , u32 flags_ = 0 , DqnMemTracker : : Flag tracker_flags = DqnMemTracker : : Simple , DqnAllocator * block_allocator_ = dqn_lib_context_ . allocator ) ;
2018-09-10 12:49:17 +00:00
// Allocation
// =============================================================================================
enum struct AllocMode
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
Head , // Set the default to allocating to the start of the memory block
Tail , // Set the default to allocating from the end of the memory block
} ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
enum struct PushType
{
Default , // Allocation defaults to the AllocMode set in SetAllocMode(), by default this is initialised to be from the start of the memory stack.
Opposite , // Opposite is always the opposite side of the current default allocation mode.
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
// TODO(doyle): Head and Tail are possibly redundant modes now. When the
// client changes the default allocation mode outside of functions that
// take a memory stack as an allocator, it means if the function wants
// to use the "temporary" side of the memory stack, they need to be
// aware of what the opposite side of the memory stack is as to not
// trample over the memory that the client wants allocated memory from.
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
// This is what the 2 new PushType options above facilitate. I can't
// immediately see a situation now where you want to actually specify
// the side and override the allocation mode which could make for
// unintuitive behaviour, but I'm going to sit on this for awhile and
// see how the actual real world use cases pan out.
2018-07-15 10:18:27 +00:00
2018-09-10 12:49:17 +00:00
Head , // Ignores the AllocMode, always allocate from the start of the memory block
Tail , // Ignores the AllocMode, always allocate from the end of the memory block
} ;
// NOTE: Prefer the macro to enable allocation tagging if defined.
// Allocate memory from the MemStack.
// alignment: Must be a power of 2, ptr returned from allocator is address aligned to this value.
// return: Ptr to memory, nullptr if out of space and is disallowed to expand OR the stack is full and malloc fails
void * Push_ ( usize size , PushType push_type = PushType : : Default , u8 alignment = 4 ) ;
// On Push, if push_type is PushType::Default, it will behave according to the AllocMode last set.
void SetAllocMode ( AllocMode mode ) { if ( mode = = AllocMode : : Head ) flags & = ~ Flag : : DefaultAllocateTail ; else flags | = Flag : : DefaultAllocateTail ; }
// TODO(doyle): Edge case where Pop fails. If you Push to the head and it
// fits in the existing block. Then push to the tail a size larger than the
// remaining space and generate a new block, then, if you try to Pop the ptr
// from the head, it will not find the right block to pop from and assert.
void Pop ( void * ptr , Dqn : : ZeroMem zero = Dqn : : ZeroMem : : No ) ; // Free the ptr. MUST be the last allocated ptr on the block head or tail, assert otherwise.
void PopBlock ( ) { FreeBlock ( block ) ; }
void Free ( ) { tracker . Free ( ) ; while ( block_allocator & & block ) PopBlock ( ) ; }
bool FreeBlock ( DqnMemStack : : Block * mem_block ) ;
void Reset ( Dqn : : ZeroMem zero ) { while ( block & & block - > prev_block ) PopBlock ( ) ; ClearCurrBlock ( zero ) ; }
void ResetTail ( ) ; // Reset just the tail
void ClearCurrBlock ( Dqn : : ZeroMem zero = Dqn : : ZeroMem : : No ) ; // Reset the current memory block usage to 0.
Info GetInfo ( ) const ;
// Temporary Memory Regions
// =============================================================================================
// Revert all allocations between the Begin() and End() regions. Scope version is RAII'ed.
struct MemRegion
2018-07-15 10:18:27 +00:00
{
2018-09-10 12:49:17 +00:00
DqnMemStack * stack ; // Stack associated with this MemRegion
Block * starting_block ; // The block to revert back to
char * starting_block_head ;
char * starting_block_tail ;
} ;
2018-07-15 10:18:27 +00:00
2018-09-10 12:49:17 +00:00
struct MemRegionScoped
{
MemRegionScoped ( DqnMemStack * stack ) { region = stack - > MemRegionBegin ( ) ; }
~ MemRegionScoped ( ) { if ( region . stack ) region . stack - > MemRegionEnd ( region ) ; }
MemRegion region ;
} ;
2018-07-15 10:18:27 +00:00
2018-09-10 12:49:17 +00:00
MemRegion MemRegionBegin ( ) ;
void MemRegionEnd ( MemRegion region ) ;
MemRegionScoped MemRegionScope ( ) { return MemRegionScoped ( this ) ; } ;
// Keep allocations that have occurred since Begin(). End() does not need to be called anymore.
void MemRegionSave ( MemRegion * region ) ;
void MemRegionSave ( MemRegionScoped * scope ) { MemRegionSave ( & scope - > region ) ; }
} ;
// #DqnLogger Public
// =================================================================================================
struct DqnLogger
{
# define LOG_TYPES \
2018-12-15 15:23:04 +00:00
X ( Warning , " WRN " ) \
X ( Error , " ERR " ) \
X ( Debug , " DBG " ) \
X ( Memory , " MEM " ) \
X ( Message , " MSG " )
2018-09-10 12:49:17 +00:00
# define X(type, prefix) type,
enum struct Type { LOG_TYPES } ;
# undef X
# define X(type, prefix) prefix,
static char const * TypePrefix ( Type type )
{
LOCAL_PERSIST char const * type_string [ ] = { LOG_TYPES } ;
return type_string [ static_cast < i32 > ( type ) ] ;
2018-07-15 10:18:27 +00:00
}
2018-09-10 12:49:17 +00:00
# undef X
# undef LOG_TYPES
2018-07-15 10:18:27 +00:00
2018-09-10 12:49:17 +00:00
struct Context
{
char * filename ;
int filename_len ;
char * function ;
int function_len ;
int line_num ;
} ;
2018-07-15 10:18:27 +00:00
2018-09-13 13:58:48 +00:00
# define DQN_LOGGER(logger, type, fmt, ...) (logger)->Log(type, DQN_LOGGER_MAKE_CONTEXT_, fmt, ## __VA_ARGS__)
# define DQN_LOGGER_E(logger, fmt, ...) (logger)->Log(DqnLogger::Type::Error, DQN_LOGGER_MAKE_CONTEXT_, fmt, ## __VA_ARGS__)
# define DQN_LOGGER_W(logger, fmt, ...) (logger)->Log(DqnLogger::Type::Warning, DQN_LOGGER_MAKE_CONTEXT_, fmt, ## __VA_ARGS__)
# define DQN_LOGGER_D(logger, fmt, ...) (logger)->Log(DqnLogger::Type::Debug, DQN_LOGGER_MAKE_CONTEXT_, fmt, ## __VA_ARGS__)
# define DQN_LOGGER_M(logger, fmt, ...) (logger)->Log(DqnLogger::Type::Message, DQN_LOGGER_MAKE_CONTEXT_, fmt, ## __VA_ARGS__)
2018-09-10 12:49:17 +00:00
DqnMemStack allocator ;
DqnFixedString1024 log_builder ;
DqnBuffer < char > log_buf ;
int log_buf_index ;
// TODO(doyle): Switch to bit flags
b32 no_console ; // Log to console if false.
b32 no_print_error ;
b32 no_print_debug ;
b32 no_print_warning ;
// Build up a log line that gets prepended to the next log. When Log() is called and is then reset.
// <file context> <prepend to log> <log message>
void PrependToLog ( char const * fmt , . . . ) { va_list va ; va_start ( va , fmt ) ; log_builder . VSprintfAppend ( fmt , va ) ; va_end ( va ) ; }
// return: A static string whose lifetime persists until the next log call.
char const * LogNoContext ( Type type , char const * fmt , . . . ) ;
2018-09-13 13:58:48 +00:00
char const * Log ( Type type , Context log_context , char const * fmt , . . . ) ;
char const * LogVA ( Type type , Context log_context , char const * fmt , va_list va ) ;
2018-09-10 12:49:17 +00:00
} ;
// #DqnArray
// =================================================================================================
template < typename T >
struct DqnArray
{
2018-09-13 13:58:48 +00:00
DqnAllocator * allocator = dqn_lib_context_ . allocator ;
2018-09-10 12:49:17 +00:00
isize len ;
isize max ;
T * data ;
DqnArray ( ) = default ;
DqnArray ( DqnAllocator * allocator ) { * this = { } ; this - > allocator = allocator ; }
DqnArray ( T * data_ , isize max_ , isize len_ = 0 ) { * this = { } ; this - > allocator = nullptr ; this - > data = data_ ; this - > max = max_ ; this - > len = len_ ; }
void Clear ( Dqn : : ZeroMem clear = Dqn : : ZeroMem : : No ) { if ( ! data ) return ; len = 0 ; if ( clear = = Dqn : : ZeroMem : : Yes ) DqnMem_Clear ( data , 0 , sizeof ( T ) * max ) ; }
void Free ( ) { if ( data ) { allocator - > Free ( data , sizeof ( * data ) * max ) ; } * this = { } ; }
2018-12-15 14:58:11 +00:00
T * Front ( ) { DQN_ASSERT_MSG ( len > 0 , " len: %zu " , len ) ; return data + 0 ; }
T * Back ( ) { DQN_ASSERT_MSG ( len > 0 , " len: %zu " , len ) ; return data + ( len - 1 ) ; }
2018-09-10 12:49:17 +00:00
void Resize ( isize new_len ) { if ( new_len > max ) Reserve ( GrowCapacity_ ( new_len ) ) ; len = new_len ; }
void Resize ( isize new_len , T const * v ) { if ( new_len > max ) Reserve ( GrowCapacity_ ( new_len ) ) ; if ( new_len > len ) for ( isize n = len ; n < new_len ; n + + ) data [ n ] = * v ; len = new_len ; }
void Reserve ( isize new_max ) ;
T * Make ( isize len_ = 1 ) { len + = len_ ; if ( len > max ) Reserve ( GrowCapacity_ ( len ) ) ; return & data [ len - len_ ] ; }
2018-12-15 15:23:04 +00:00
T * Add ( T const & v ) { if ( len + 1 > max ) Reserve ( GrowCapacity_ ( len + 1 ) ) ; data [ len + + ] = v ; return data + ( len - 1 ) ; }
T * Add ( T const * v , isize v_len ) { isize new_len = len + v_len ; if ( new_len > max ) Reserve ( GrowCapacity_ ( new_len ) ) ; T * result = data + len ; for ( isize i = 0 ; i < v_len ; + + i ) data [ len + + ] = v [ i ] ; return result ; }
2018-09-10 12:49:17 +00:00
void Pop ( ) { if ( len > 0 ) len - - ; }
2018-12-15 14:58:11 +00:00
void Erase ( isize index ) { DQN_ASSERT_MSG ( index > = 0 & & index < len , " index: %zu, len: %zu " , index , len ) ; data [ index ] = data [ - - len ] ; }
2018-09-10 12:49:17 +00:00
void EraseStable ( isize index ) ;
T * Insert ( isize index , T const * v ) { return Insert ( index , v , 1 ) ; }
T * Insert ( isize index , T const & v ) { return Insert ( index , & v , 1 ) ; }
T * Insert ( isize index , T const * v , isize len_items ) ;
bool Contains ( T const * v ) const { T const * ptr = data ; T const * end = data + len ; while ( ptr < end ) if ( * ptr + + = = * v ) return true ; return false ; }
2018-07-15 10:18:27 +00:00
2018-12-15 14:58:11 +00:00
T & operator [ ] ( isize i ) const { DQN_ASSERT_MSG ( i > = 0 & & i < len , " i: %zu, len: %zu " , i , len ) ; return this - > data [ i ] ; }
2018-09-10 12:49:17 +00:00
T * begin ( ) { return data ; }
T * begin ( ) const { return data ; }
T * end ( ) { return data + len ; }
T * end ( ) const { return data + len ; }
2018-07-15 10:18:27 +00:00
2018-09-10 12:49:17 +00:00
private :
isize GrowCapacity_ ( isize size ) const { isize new_max = max ? ( max * 2 ) : 8 ; return new_max > size ? new_max : size ; }
} ;
2018-07-15 10:18:27 +00:00
2018-09-10 12:49:17 +00:00
template < typename T > T * DqnArray < T > : : Insert ( isize index , T const * v , isize len_items )
{
index = DQN_MIN ( DQN_MAX ( index , 0 ) , len ) ;
isize const new_len = len + len_items ;
2018-07-15 10:18:27 +00:00
2018-09-10 12:49:17 +00:00
if ( new_len > = max )
Reserve ( GrowCapacity_ ( new_len ) ) ;
2018-07-15 10:18:27 +00:00
2018-09-10 12:49:17 +00:00
T * src = data + index ;
T * dest = src + len_items ;
2018-07-15 10:18:27 +00:00
2018-09-10 12:49:17 +00:00
if ( src < dest )
memmove ( dest , src , ( ( data + len ) - src ) * sizeof ( T ) ) ;
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
len = new_len ;
for ( isize i = 0 ; i < len_items ; i + + )
src [ i ] = v [ i ] ;
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
return src ;
}
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
template < typename T > void DqnArray < T > : : EraseStable ( isize index )
2018-03-08 15:03:05 +00:00
{
2018-09-10 12:49:17 +00:00
DQN_ASSERT ( index > = 0 & & index < len ) ;
isize const off = ( data + index ) - data ;
memmove ( data + off , data + off + 1 , ( ( usize ) len - ( usize ) off - 1 ) * sizeof ( T ) ) ;
len - - ;
}
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
template < typename T > void DqnArray < T > : : Reserve ( isize new_max )
{
if ( new_max < = max ) return ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
if ( data )
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
T * newData = ( T * ) allocator - > Realloc ( data , new_max * sizeof ( T ) ) ;
data = newData ;
max = new_max ;
2018-05-17 11:19:58 +00:00
}
else
{
2018-09-10 12:49:17 +00:00
data = ( T * ) allocator - > Malloc ( new_max * sizeof ( T ) ) ;
max = new_max ;
2018-05-17 11:19:58 +00:00
}
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
DQN_ASSERT ( data ) ;
}
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
// #DqnChar
2018-03-08 15:03:05 +00:00
// =================================================================================================
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE char DqnChar_ToLower ( char c ) ;
DQN_FILE_SCOPE char DqnChar_ToUpper ( char c ) ;
DQN_FILE_SCOPE bool DqnChar_IsDigit ( char c ) ;
DQN_FILE_SCOPE bool DqnChar_IsAlpha ( char c ) ;
DQN_FILE_SCOPE bool DqnChar_IsAlphaNum ( char c ) ;
DQN_FILE_SCOPE bool DqnChar_IsEndOfLine ( char c ) ;
DQN_FILE_SCOPE bool DqnChar_IsWhitespace ( char c ) ;
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE char * DqnChar_TrimWhitespaceAround ( char const * src , i32 src_len , i32 * new_len ) ;
DQN_FILE_SCOPE char * DqnChar_SkipWhitespace ( char const * ptr ) ;
// TODO(doyle): this is NOT UTF8 safe
// ch: Char to find
// len: The length of the string stored in ptr, (doesn't care if it includes null terminator)
// len_to_char: The length to the char from end of the ptr, i.e. (ptr + len)
// return: The ptr to the last char, null if it could not find.
DQN_FILE_SCOPE char * DqnChar_FindLastChar ( char * ptr , char ch , i32 len , u32 * len_to_char ) ;
2018-08-29 15:04:56 +00:00
2018-09-10 12:49:17 +00:00
// #DqnStr
2018-07-04 15:04:49 +00:00
// =================================================================================================
2018-09-10 12:49:17 +00:00
// num_bytes_to_cmp: If -1, cmp runs until \0 is encountered.
// return: 0 if equal. 0 < if a is before b, > 0 if a is after b
DQN_FILE_SCOPE i32 DqnStr_Cmp ( char const * a , char const * b , i32 num_bytes_to_cmp = - 1 , Dqn : : IgnoreCase ignore = Dqn : : IgnoreCase : : No ) ;
2018-07-04 15:04:49 +00:00
2018-09-10 12:49:17 +00:00
// str_len: Len of string, if -1, StrLen is used.
// return: Pointer in str to the last slash, if none then the original string.
DQN_FILE_SCOPE char * DqnStr_GetPtrToLastSlash ( char const * str , i32 str_len = - 1 ) ;
2018-07-04 15:04:49 +00:00
2018-09-10 12:49:17 +00:00
// return: String length not including the nullptr terminator. 0 if invalid args.
DQN_FILE_SCOPE i32 DqnStr_Len ( char const * a ) ;
DQN_FILE_SCOPE i32 DqnStr_LenUTF8 ( u32 const * a , i32 * len_in_bytes = nullptr ) ;
2018-07-04 15:04:49 +00:00
2018-09-10 12:49:17 +00:00
// return: String length starting from a, up to and not including the first delimiter character.
DQN_FILE_SCOPE i32 DqnStr_LenDelimitWith ( char const * a , char delimiter ) ;
2018-07-04 15:38:35 +00:00
2018-09-10 12:49:17 +00:00
// return: The dest argument, nullptr if args invalid (i.e. nullptr pointers or numChars < 0)
DQN_FILE_SCOPE void DqnStr_Reverse ( char * buf , isize buf_size ) ;
2018-07-04 15:38:35 +00:00
2018-09-10 12:49:17 +00:00
// return: Number of bytes in codepoint, 0 if *a becomes invalid or end of stream.
DQN_FILE_SCOPE i32 DqnStr_ReadUTF8Codepoint ( u32 const * a , u32 * out_codepoint ) ;
// return: The offset into the src to first char of the found string. Returns -1 if not found
DQN_FILE_SCOPE i32 DqnStr_FindFirstOccurence ( char const * src , i32 src_len , char const * find , i32 find_len , Dqn : : IgnoreCase ignore = Dqn : : IgnoreCase : : No ) ;
DQN_FILE_SCOPE bool DqnStr_EndsWith ( char const * src , i32 src_len , char const * find , i32 find_len , Dqn : : IgnoreCase ignore = Dqn : : IgnoreCase : : No ) ;
// return: Helper function that returns the pointer to the first occurence, nullptr if not found.
DQN_FILE_SCOPE char * DqnStr_GetFirstOccurence ( char const * src , i32 src_len , char const * find , i32 find_len , Dqn : : IgnoreCase ignore = Dqn : : IgnoreCase : : No ) ;
DQN_FILE_SCOPE bool DqnStr_HasSubstring ( char const * src , i32 src_len , char const * find , i32 find_len , Dqn : : IgnoreCase ignore = Dqn : : IgnoreCase : : No ) ;
DQN_FILE_SCOPE DqnSlice < char > DqnStr_RemoveLeadTrailChar ( char const * str , i32 str_len , char lead_char , char trail_char ) ;
DQN_FILE_SCOPE inline DqnSlice < char > DqnStr_RemoveLeadTrailQuotes ( DqnSlice < char > slice ) ;
DQN_FILE_SCOPE inline DqnSlice < char > DqnStr_RemoveLeadTrailBraces ( DqnSlice < char > slice ) ;
DQN_FILE_SCOPE inline DqnSlice < char > DqnStr_RemoveLeadTrailQuotes ( char const * str , i32 str_len ) ;
DQN_FILE_SCOPE inline DqnSlice < char > DqnStr_RemoveLeadTrailBraces ( char const * str , i32 str_len ) ;
2018-07-04 15:04:49 +00:00
2018-09-10 12:49:17 +00:00
// #DqnWChar
2018-07-09 14:53:54 +00:00
// =================================================================================================
2018-09-10 12:49:17 +00:00
// NOTE: See above for documentation
DQN_FILE_SCOPE bool DqnWChar_IsDigit ( wchar_t c ) ;
DQN_FILE_SCOPE wchar_t DqnWChar_ToLower ( wchar_t c ) ;
DQN_FILE_SCOPE wchar_t * DqnWChar_SkipWhitespace ( wchar_t * ptr ) ;
DQN_FILE_SCOPE wchar_t * DqnWChar_FindLastChar ( wchar_t * ptr , wchar_t ch , i32 len , u32 * len_to_char ) ;
DQN_FILE_SCOPE i32 DqnWChar_GetNextLine ( wchar_t * ptr , i32 * line_len ) ;
// #DqnWStr
// =================================================================================================
DQN_FILE_SCOPE i32 DqnWStr_Cmp ( wchar_t const * a , wchar_t const * b ) ;
DQN_FILE_SCOPE i32 DqnWStr_FindFirstOccurence ( wchar_t const * src , i32 src_len , wchar_t const * find , i32 find_len ) ;
DQN_FILE_SCOPE bool DqnWStr_HasSubstring ( wchar_t const * src , i32 src_len , wchar_t const * find , i32 find_len ) ;
DQN_FILE_SCOPE i32 DqnWStr_Len ( wchar_t const * a ) ;
DQN_FILE_SCOPE i32 DqnWStr_LenDelimitWith ( wchar_t const * a , wchar_t delimiter ) ;
DQN_FILE_SCOPE void DqnWStr_Reverse ( wchar_t * buf , i32 buf_size ) ;
// #DqnString
// =================================================================================================
struct DqnString
2018-07-09 14:53:54 +00:00
{
2018-09-13 13:58:48 +00:00
DqnAllocator * allocator = dqn_lib_context_ . allocator ;
2018-09-10 12:49:17 +00:00
int len = 0 ;
int max = 0 ;
char * str = nullptr ;
2018-07-09 14:53:54 +00:00
2018-09-10 12:49:17 +00:00
DqnString ( ) = default ;
DqnString ( char * buf , int max_ ) : len ( 0 ) , str ( buf ) { max = max_ ; NullTerminate ( ) ; }
DqnString ( char const * str_ ) { Append ( str_ ) ; }
DqnString ( char const * str_ , int len_ ) { Append ( str_ , len_ ) ; }
DqnString ( DqnSlice < char const > const & other ) { Append ( other . data , other . len ) ; }
DqnString ( DqnSlice < char > const & other ) { Append ( other . data , other . len ) ; }
DqnString ( DqnString const & other ) { if ( this = = & other ) return ; * this = other ; } // TODO(doyle): I can't decide on copy semantics
2018-07-09 14:53:54 +00:00
2018-09-10 12:49:17 +00:00
DqnString & operator + = ( char const * other ) { Append ( other ) ; return * this ; }
DqnString & operator + = ( DqnSlice < char const > const & other ) { Append ( other . data , other . len ) ; return * this ; }
DqnString & operator + = ( DqnSlice < char > const & other ) { Append ( other . data , other . len ) ; return * this ; }
DqnString & operator + = ( DqnString const & other ) { Append ( other . str , other . len ) ; return * this ; }
2018-07-09 14:53:54 +00:00
2018-09-10 12:49:17 +00:00
DqnString operator + ( char const * other ) { auto result = * this ; result . Append ( other ) ; return result ; }
DqnString operator + ( DqnSlice < char const > const & other ) { auto result = * this ; result . Append ( other . data , other . len ) ; return result ; }
DqnString operator + ( DqnSlice < char > const & other ) { auto result = * this ; result . Append ( other . data , other . len ) ; return result ; }
DqnString operator + ( DqnString const & other ) { auto result = * this ; result . Append ( other . str , other . len ) ; return result ; }
2018-07-09 14:53:54 +00:00
2018-09-10 12:49:17 +00:00
// Xprintf functions always modifies buffer and null-terminates even with insufficient buffer size.
// return: The number of characters copied to the buffer
int Sprintf ( char const * fmt , . . . ) { va_list va ; va_start ( va , fmt ) ; int result = VSprintf ( fmt , va ) ; va_end ( va ) ; return result ; }
int SprintfAppend ( char const * fmt , . . . ) { va_list va ; va_start ( va , fmt ) ; int result = VSprintfAppend ( fmt , va ) ; va_end ( va ) ; return result ; }
2018-07-09 14:53:54 +00:00
2018-09-10 12:49:17 +00:00
int VSprintf ( char const * fmt , va_list va ) { return VSprintfAtOffset ( fmt , va , 0 /*offset*/ ) ; }
int VSprintfAppend ( char const * fmt , va_list va ) { return VSprintfAtOffset ( fmt , va , len /*offset*/ ) ; }
2018-07-09 14:53:54 +00:00
2018-09-10 12:49:17 +00:00
void NullTerminate ( ) { str [ len ] = 0 ; } // NOTE: If you modify the storage directly, this can be handy.
void Clear ( Dqn : : ZeroMem clear = Dqn : : ZeroMem : : No ) { if ( clear = = Dqn : : ZeroMem : : Yes ) DqnMem_Set ( str , 0 , max ) ; len = max = 0 ; NullTerminate ( ) ; }
void Free ( ) { if ( str ) allocator - > Free ( str , sizeof ( * str ) * len ) ; str = nullptr ; }
void Resize ( int new_max ) { if ( new_max > max ) Reserve ( new_max ) ; len = DQN_MIN ( new_max , len ) ; NullTerminate ( ) ; }
void Reserve ( int new_max ) ;
2018-06-28 13:25:25 +00:00
2018-09-10 12:49:17 +00:00
void Append ( char const * src , int len_ = - 1 ) ;
2018-12-15 15:00:36 +00:00
int VSprintfAtOffset ( char const * fmt , va_list va , int offset ) { Reserve ( len + stbsp_vsnprintf ( nullptr , 0 , fmt , va ) + 1 ) ; int result = stbsp_vsnprintf ( str + offset , max - len , fmt , va ) ; len = ( offset + result ) ; return result ; }
2018-09-02 11:17:14 +00:00
2018-09-10 12:49:17 +00:00
static bool Cmp ( DqnString const * a , DqnString const * b , Dqn : : IgnoreCase ignore = Dqn : : IgnoreCase : : No ) { return ( a - > len = = b - > len ) & & ( DqnStr_Cmp ( a - > str , b - > str , a - > len , ignore ) = = 0 ) ; }
static bool Cmp ( DqnString const * a , DqnSlice < char const > const b , Dqn : : IgnoreCase ignore = Dqn : : IgnoreCase : : No ) { return ( a - > len = = b . len ) & & ( DqnStr_Cmp ( a - > str , b . data , b . len , ignore ) = = 0 ) ; }
static bool Cmp ( DqnString const * a , DqnSlice < char > const b , Dqn : : IgnoreCase ignore = Dqn : : IgnoreCase : : No ) { return ( a - > len = = b . len ) & & ( DqnStr_Cmp ( a - > str , b . data , b . len , ignore ) = = 0 ) ; }
2018-06-28 13:25:25 +00:00
2018-09-10 12:49:17 +00:00
// return: -1 if invalid, or if buf_size is 0 the required buffer length in wchar_t characters
i32 ToWChar ( wchar_t * const buf , i32 const buf_size ) const ;
// return: String allocated using api.
2018-09-13 13:58:48 +00:00
wchar_t * ToWChar ( DqnAllocator * allocator = dqn_lib_context_ . allocator ) const ;
2018-06-28 13:25:25 +00:00
} ;
2018-09-10 12:49:17 +00:00
// #DqnRnd
// =================================================================================================
struct DqnRndPCG // PCG (Permuted Congruential Generator)
2018-06-28 13:25:25 +00:00
{
2018-09-10 12:49:17 +00:00
u64 state [ 2 ] ;
2018-06-28 13:25:25 +00:00
2018-09-10 12:49:17 +00:00
DqnRndPCG ( ) ;
DqnRndPCG ( u32 seed ) ;
2018-07-04 14:47:11 +00:00
2018-09-10 12:49:17 +00:00
u32 Next ( ) ; // return: A random number N between [0, 0xFFFFFFFF]
f32 Nextf ( ) ; // return: A random float N between [0.0, 1.0f]
i32 Range ( i32 min , i32 max ) ; // return: A random integer N between [min, max]
} ;
2018-06-28 13:25:25 +00:00
2018-09-10 12:49:17 +00:00
// #Dqn_*
// =================================================================================================
// return: The number of splits in the array. If array is null this returns the required size of the array.
i32 Dqn_SplitString ( char const * src , i32 src_len , char split_char , DqnSlice < char > * array = nullptr , i32 size = 0 ) ;
2018-03-08 15:03:05 +00:00
2018-09-10 12:49:17 +00:00
// Util function that uses Dqn_SplitString
// return: The number of splits, splitting by "split_char" would generate.
i32 Dqn_GetNumSplits ( char const * src , i32 src_len , char split_char ) ;
2018-06-28 13:25:25 +00:00
2018-09-10 12:49:17 +00:00
// Skips whitespace then reads UTF8 rune upto first \r or \n and null terminates at that point. Advances input to the start of the next line.
// line_len: (Optional) Returns the length of the null terminated line returned
// return : The immediate null terminated line, nullptr if no more lines are to be read.
DQN_FILE_SCOPE char * Dqn_EatLine ( char * * input , int * line_len ) ;
DQN_FILE_SCOPE wchar_t * Dqn_EatLine ( wchar_t * * input , int * line_len ) ;
2018-06-28 13:25:25 +00:00
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE inline bool Dqn_BitIsSet ( u32 bits , u32 flag ) ;
DQN_FILE_SCOPE inline u32 Dqn_BitSet ( u32 bits , u32 flag ) ;
DQN_FILE_SCOPE inline u32 Dqn_BitUnset ( u32 bits , u32 flag ) ;
DQN_FILE_SCOPE inline u32 Dqn_BitToggle ( u32 bits , u32 flag ) ;
template < typename T > using DqnQuickSort_LessThanProc = bool ( * ) ( T const & a , T const & b , void * user_context ) ;
# define DQN_QUICK_SORT_LESS_THAN_PROC(name) template <typename T> inline bool name(T const &a, T const &b, void *user_context)
DQN_QUICK_SORT_LESS_THAN_PROC ( DqnQuickSort_DefaultLessThan )
2018-06-28 13:25:25 +00:00
{
2018-09-10 12:49:17 +00:00
( void ) user_context ;
bool result = a < b ;
return result ;
2018-06-28 13:25:25 +00:00
}
2018-09-10 12:49:17 +00:00
template < typename T , DqnQuickSort_LessThanProc < T > IsLessThan = DqnQuickSort_DefaultLessThan < T > >
DQN_FILE_SCOPE void DqnQuickSort ( T * array , isize size , void * user_context )
2018-06-28 13:25:25 +00:00
{
2018-09-10 12:49:17 +00:00
if ( ! array | | size < = 1 ) return ;
2018-07-04 14:47:11 +00:00
2018-09-10 12:49:17 +00:00
# if 1
// Insertion Sort, under 24->32 is an optimal amount
const i32 QUICK_SORT_THRESHOLD = 24 ;
if ( size < QUICK_SORT_THRESHOLD )
2018-06-28 13:25:25 +00:00
{
2018-09-10 12:49:17 +00:00
i32 item_to_insert_index = 1 ;
while ( item_to_insert_index < size )
{
for ( i32 check_index = 0 ; check_index < item_to_insert_index ; check_index + + )
{
if ( ! IsLessThan ( array [ check_index ] , array [ item_to_insert_index ] , user_context ) )
{
T item_to_insert = array [ item_to_insert_index ] ;
for ( i32 i = item_to_insert_index ; i > check_index ; i - - )
array [ i ] = array [ i - 1 ] ;
array [ check_index ] = item_to_insert ;
break ;
}
}
item_to_insert_index + + ;
}
return ;
2018-07-05 01:26:04 +00:00
}
2018-09-10 12:49:17 +00:00
# endif
auto state = DqnRndPCG ( ) ;
auto last_index = size - 1 ;
auto pivot_index = ( i64 ) state . Range ( 0 , ( i32 ) last_index ) ;
auto partition_index = 0 ;
auto start_index = 0 ;
// Swap pivot with last index, so pivot is always at the end of the array.
// This makes logic much simpler.
DQN_SWAP ( T , array [ last_index ] , array [ pivot_index ] ) ;
pivot_index = last_index ;
// 4^, 8, 7, 5, 2, 3, 6
if ( IsLessThan ( array [ start_index ] , array [ pivot_index ] , user_context ) ) partition_index + + ;
start_index + + ;
// 4, |8, 7, 5^, 2, 3, 6*
// 4, 5, |7, 8, 2^, 3, 6*
// 4, 5, 2, |8, 7, ^3, 6*
// 4, 5, 2, 3, |7, 8, ^6*
for ( auto check_index = start_index ; check_index < last_index ; check_index + + )
2018-07-05 01:26:04 +00:00
{
2018-09-10 12:49:17 +00:00
if ( IsLessThan ( array [ check_index ] , array [ pivot_index ] , user_context ) )
{
DQN_SWAP ( T , array [ partition_index ] , array [ check_index ] ) ;
partition_index + + ;
}
2018-06-28 13:25:25 +00:00
}
2018-09-10 12:49:17 +00:00
// Move pivot to right of partition
// 4, 5, 2, 3, |6, 8, ^7*
DQN_SWAP ( T , array [ partition_index ] , array [ pivot_index ] ) ;
DqnQuickSort < T , IsLessThan > ( array , partition_index , user_context ) ;
2018-06-28 13:25:25 +00:00
2018-09-10 12:49:17 +00:00
// Skip the value at partion index since that is guaranteed to be sorted.
// 4, 5, 2, 3, (x), 8, 7
i32 one_after_partition_index = partition_index + 1 ;
DqnQuickSort < T , IsLessThan > ( array + one_after_partition_index , ( size - one_after_partition_index ) , user_context ) ;
}
2018-07-22 13:53:08 +00:00
2018-09-10 12:49:17 +00:00
template < typename T >
DQN_FILE_SCOPE void DqnQuickSort ( T * array , isize size )
2018-07-22 13:53:08 +00:00
{
2018-09-10 12:49:17 +00:00
if ( ! array | | size < = 1 ) return ;
# if 1
// Insertion Sort, under 24->32 is an optimal amount
const i32 QUICK_SORT_THRESHOLD = 24 ;
if ( size < QUICK_SORT_THRESHOLD )
{
i32 item_to_insert_index = 1 ;
while ( item_to_insert_index < size )
{
for ( i32 check_index = 0 ; check_index < item_to_insert_index ; check_index + + )
{
if ( ! ( array [ check_index ] < array [ item_to_insert_index ] ) )
{
T item_to_insert = array [ item_to_insert_index ] ;
for ( i32 i = item_to_insert_index ; i > check_index ; i - - )
array [ i ] = array [ i - 1 ] ;
2018-07-22 13:53:08 +00:00
2018-09-10 12:49:17 +00:00
array [ check_index ] = item_to_insert ;
break ;
}
}
item_to_insert_index + + ;
}
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
return ;
}
# endif
2018-02-02 12:26:05 +00:00
2018-09-10 12:49:17 +00:00
auto state = DqnRndPCG ( ) ;
auto last_index = size - 1 ;
auto pivot_index = ( i64 ) state . Range ( 0 , ( i32 ) last_index ) ;
auto partition_index = 0 ;
auto start_index = 0 ;
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
// Swap pivot with last index, so pivot is always at the end of the array.
// This makes logic much simpler.
DQN_SWAP ( T , array [ last_index ] , array [ pivot_index ] ) ;
pivot_index = last_index ;
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
// 4^, 8, 7, 5, 2, 3, 6
if ( array [ start_index ] < array [ pivot_index ] ) partition_index + + ;
start_index + + ;
2018-02-02 08:34:44 +00:00
2018-09-10 12:49:17 +00:00
// 4, |8, 7, 5^, 2, 3, 6*
// 4, 5, |7, 8, 2^, 3, 6*
// 4, 5, 2, |8, 7, ^3, 6*
// 4, 5, 2, 3, |7, 8, ^6*
for ( auto check_index = start_index ; check_index < last_index ; check_index + + )
{
if ( array [ check_index ] < array [ pivot_index ] )
{
DQN_SWAP ( T , array [ partition_index ] , array [ check_index ] ) ;
partition_index + + ;
}
}
2018-09-02 14:02:32 +00:00
2018-09-10 12:49:17 +00:00
// Move pivot to right of partition
// 4, 5, 2, 3, |6, 8, ^7*
DQN_SWAP ( T , array [ partition_index ] , array [ pivot_index ] ) ;
DqnQuickSort ( array , partition_index ) ;
2018-09-02 14:02:32 +00:00
2018-09-10 12:49:17 +00:00
// Skip the value at partion index since that is guaranteed to be sorted.
// 4, 5, 2, 3, (x), 8, 7
i32 one_after_partition_index = partition_index + 1 ;
DqnQuickSort ( array + one_after_partition_index , ( size - one_after_partition_index ) ) ;
}
2018-09-02 14:02:32 +00:00
2017-05-05 16:44:17 +00:00
2018-09-10 12:49:17 +00:00
template < typename T > using DqnBSearch_LessThanProc = bool ( * ) ( const T & , const T & ) ;
template < typename T > using DqnBSearch_EqualsProc = bool ( * ) ( const T & , const T & ) ;
# define DQN_BSEARCH_LESS_THAN_PROC(name) template <typename T> inline bool name(T const &a, T const &b)
# define DQN_BSEARCH_EQUALS_PROC(name) template <typename T> inline bool name(T const &a, T const &b)
DQN_BSEARCH_LESS_THAN_PROC ( DqnBSearch_DefaultLessThan ) { return a < b ; }
DQN_BSEARCH_EQUALS_PROC ( DqnBSearch_DefaultEquals ) { return a = = b ; }
enum struct DqnBSearchType
2017-05-04 16:37:38 +00:00
{
2018-09-10 12:49:17 +00:00
Match , // Return the index of the first item that matches the find value
MinusOne , // Return the index of the first item lower than the find value
PlusOne , // Return the index of the first item higher than the find value
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
MatchOrMinusOne , // Return the index of the matching item if not found the first item lower
MatchOrPlusOne , // Return the index of the matching item if not found the first item higher
} ;
// type: The matching behaviour of the binary search,
// return: -1 if element not found, otherwise index of the element.
// For higher and lower bounds return -1 if there is no element higher/lower than the
// find value (i.e. -1 if the 0th element is the find val for lower bound).
template < typename T ,
DqnBSearch_LessThanProc < T > IsLessThan = DqnBSearch_DefaultLessThan < T > ,
DqnBSearch_EqualsProc < T > Equals = DqnBSearch_DefaultEquals < T > >
DQN_FILE_SCOPE i64
DqnBSearch ( T const * array , isize size , T const & find , DqnBSearchType type = DqnBSearchType : : Match )
{
if ( size = = 0 | | ! array )
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
return - 1 ;
}
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
isize start = 0 ;
isize end = size - 1 ;
isize mid = static_cast < isize > ( ( start + end ) * 0.5f ) ;
while ( start < = end )
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
if ( Equals ( array [ mid ] , find ) )
{
if ( type = = DqnBSearchType : : Match | |
type = = DqnBSearchType : : MatchOrMinusOne | |
type = = DqnBSearchType : : MatchOrPlusOne )
{
return mid ;
}
else if ( type = = DqnBSearchType : : MinusOne )
{
// NOTE: We can always -1 because at worst case, 0 index will go to -1 which is
// correct behaviour.
return mid - 1 ;
}
else
{
return ( ( mid + 1 ) > = size ) ? - 1 : mid + 1 ;
}
}
else if ( IsLessThan ( array [ mid ] , find ) ) start = mid + 1 ;
else end = mid - 1 ;
mid = static_cast < isize > ( ( start + end ) * 0.5f ) ;
}
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
if ( type = = DqnBSearchType : : Match )
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
return - 1 ;
}
if ( type = = DqnBSearchType : : MinusOne | | type = = DqnBSearchType : : MatchOrMinusOne )
{
return ( IsLessThan ( find , array [ mid ] ) ) ? - 1 : mid ;
}
else
{
return ( IsLessThan ( find , array [ mid ] ) ) ? mid : - 1 ;
}
}
2018-07-17 14:47:13 +00:00
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE inline i64 DqnBSearch ( i64 const * array , i64 size , i64 find , DqnBSearchType type = DqnBSearchType : : Match ) { return DqnBSearch < i64 > ( array , size , find , type ) ; }
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
const size_t DQN_I32_MAX_STR_SIZE = 11 ;
const size_t DQN_I64_MAX_STR_SIZE = 21 ;
// Return the len of the derived string. If buf is nullptr and or buf_size is 0 the function returns the
// required string length for the integer
// TODO NOTE(doyle): Parsing stops when a non-digit is encountered, so numbers with ',' don't work atm.
DQN_FILE_SCOPE i32 Dqn_I64ToStr ( i64 const value , char * buf , i32 buf_size ) ;
DQN_FILE_SCOPE i64 Dqn_StrToI64 ( char const * buf , i64 buf_size ) ;
DQN_FILE_SCOPE inline i64 Dqn_StrToI64 ( DqnSlice < char const > buf ) { return Dqn_StrToI64 ( buf . data , buf . len ) ; }
DQN_FILE_SCOPE inline i64 Dqn_StrToI64 ( DqnSlice < char > buf ) { return Dqn_StrToI64 ( buf . data , buf . len ) ; }
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
// WARNING: Not robust, precision errors and whatnot but good enough!
DQN_FILE_SCOPE f32 Dqn_StrToF32 ( char const * buf , i64 buf_size ) ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
// Both return the number of bytes read, return 0 if invalid codepoint or UTF8
DQN_FILE_SCOPE u32 Dqn_UCSToUTF8 ( u32 * dest , u32 character ) ;
DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS ( u32 * dest , u32 character ) ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE i32 Dqn_WStrToI32 ( wchar_t const * buf , i32 buf_size ) ;
DQN_FILE_SCOPE i32 Dqn_I32ToWStr ( i32 value , wchar_t * buf , i32 buf_size ) ;
2018-09-02 14:02:32 +00:00
2018-09-10 12:49:17 +00:00
// #DqnPool
// =================================================================================================
template < typename T , i16 SIZE >
struct DqnFixedPool
{
struct Entry : public T
2018-09-02 14:02:32 +00:00
{
2018-09-10 12:49:17 +00:00
u16 nextIndex ;
2018-09-02 14:02:32 +00:00
} ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
const static isize SENTINEL_INDEX = SIZE ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
Entry pool [ SIZE ] ;
i16 freeIndex ;
i16 numFree ;
2018-09-02 14:02:32 +00:00
2018-09-10 12:49:17 +00:00
DqnFixedPool ( ) : freeIndex ( 0 ) , numFree ( SIZE )
{
DQN_FOR_EACH ( i , SIZE - 1 )
{
Entry * entry = pool + i ;
entry - > nextIndex = i + 1 ;
}
Entry * last = pool + ( SIZE - 1 ) ;
last - > nextIndex = SENTINEL_INDEX ;
}
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
T * GetNext ( )
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
if ( freeIndex = = SENTINEL_INDEX ) return nullptr ;
Entry * result = pool + freeIndex ;
freeIndex = result - > nextIndex ;
numFree - - ;
return result ;
}
void Return ( T * item )
{
auto * entry = reinterpret_cast < Entry * > ( item ) ;
entry - > nextIndex = freeIndex ;
freeIndex = entry - pool ;
numFree + + ;
}
} ;
// #DqnPool
// =================================================================================================
template < typename T >
struct DqnPool
{
struct Entry : public T
{
u16 nextIndex ;
2018-05-17 11:19:58 +00:00
} ;
2018-09-10 12:49:17 +00:00
Entry * pool ;
i16 freeIndex ;
i16 numFree ;
i32 size ;
void UseMemory ( Entry * pool_ , isize size )
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
pool = pool_ ;
freeIndex = 0 ;
numFree = size ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
DQN_FOR_EACH ( i , size - 1 )
{
Entry * entry = pool + i ;
entry - > nextIndex = i + 1 ;
}
Entry * last = pool + ( size - 1 ) ;
last - > nextIndex = size ;
}
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
T * GetNext ( )
{
if ( freeIndex = = size ) return nullptr ;
Entry * result = pool + freeIndex ;
freeIndex = result - > nextIndex ;
numFree - - ;
return result ;
}
void Return ( T * item )
{
auto * entry = reinterpret_cast < Entry * > ( item ) ;
entry - > nextIndex = freeIndex ;
freeIndex = entry - pool ;
numFree + + ;
}
2018-01-26 03:54:03 +00:00
} ;
2018-01-04 05:07:18 +00:00
2018-09-10 12:49:17 +00:00
// #DqnHash
2018-01-18 09:25:44 +00:00
// =================================================================================================
2018-02-02 08:34:44 +00:00
DQN_FILE_SCOPE u32 DqnHash_Murmur32Seed ( void const * data , usize len , u32 seed ) ;
DQN_FILE_SCOPE u64 DqnHash_Murmur64Seed ( void const * data_ , usize len , u64 seed ) ;
2017-10-29 09:30:07 +00:00
2018-02-02 08:34:44 +00:00
DQN_FILE_SCOPE inline u32 DqnHash_Murmur32 ( void const * data , usize len )
2017-10-29 09:30:07 +00:00
{
2018-05-17 11:19:58 +00:00
return DqnHash_Murmur32Seed ( data , len , 0x9747b28c ) ;
2017-10-29 09:30:07 +00:00
}
2018-02-02 08:34:44 +00:00
DQN_FILE_SCOPE inline u64 DqnHash_Murmur64 ( void const * data , usize len )
2017-10-29 09:30:07 +00:00
{
2018-05-17 11:19:58 +00:00
return DqnHash_Murmur64Seed ( data , len , 0x9747b28c ) ;
2017-10-29 09:30:07 +00:00
}
2018-09-10 12:49:17 +00:00
// #DqnMath
2018-01-18 09:25:44 +00:00
// =================================================================================================
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE f32 DqnMath_Lerp ( f32 a , f32 t , f32 b ) ;
DQN_FILE_SCOPE f32 DqnMath_Sqrtf ( f32 a ) ;
DQN_FILE_SCOPE f32 DqnMath_Clampf ( f32 val , f32 min , f32 max ) ;
2017-10-29 09:30:07 +00:00
2018-09-10 12:49:17 +00:00
// #DqnV2
2018-01-31 13:08:42 +00:00
// =================================================================================================
union DqnV2i
{
2018-05-17 11:19:58 +00:00
struct { i32 x , y ; } ;
struct { i32 w , h ; } ;
struct { i32 min , max ; } ;
i32 e [ 2 ] ;
2018-06-09 04:29:19 +00:00
DqnV2i ( ) = default ;
DqnV2i ( i32 x_ , i32 y_ ) : x ( x_ ) , y ( y_ ) { }
DqnV2i ( f32 x_ , f32 y_ ) : x ( ( i32 ) x_ ) , y ( ( i32 ) y_ ) { }
2018-05-17 11:19:58 +00:00
bool operator = = ( DqnV2i const & b ) const { return ( this - > x = = b . x ) & & ( this - > y = = b . y ) ; }
bool operator ! = ( DqnV2i const & b ) const { return ! ( * this = = b ) ; }
bool operator > = ( DqnV2i const & b ) const { return ( this - > x > = b . x ) & & ( this - > y > = b . y ) ; }
bool operator < = ( DqnV2i const & b ) const { return ( this - > x < = b . x ) & & ( this - > y < = b . y ) ; }
bool operator < ( DqnV2i const & b ) const { return ( this - > x < b . x ) & & ( this - > y < b . y ) ; }
bool operator > ( DqnV2i const & b ) const { return ( this - > x > b . x ) & & ( this - > y > b . y ) ; }
2018-01-31 13:08:42 +00:00
} ;
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
union DqnV2
{
2018-05-17 11:19:58 +00:00
struct { f32 x , y ; } ;
struct { f32 w , h ; } ;
struct { f32 min , max ; } ;
f32 e [ 2 ] ;
2018-06-09 04:29:19 +00:00
DqnV2 ( ) = default ;
DqnV2 ( f32 xy ) : x ( xy ) , y ( xy ) { }
DqnV2 ( f32 x_ , f32 y_ ) : x ( x_ ) , y ( y_ ) { }
DqnV2 ( i32 x_ , i32 y_ ) : x ( ( f32 ) x_ ) , y ( ( f32 ) y_ ) { }
DqnV2 ( DqnV2i a ) : x ( ( f32 ) a . x ) , y ( ( f32 ) a . y ) { }
2018-05-17 11:19:58 +00:00
bool operator = = ( DqnV2 const & b ) const { return ( this - > x = = b . x ) & & ( this - > y = = b . y ) ; }
bool operator ! = ( DqnV2 const & b ) const { return ! ( * this = = b ) ; }
bool operator > = ( DqnV2 const & b ) const { return ( this - > x > = b . x ) & & ( this - > y > = b . y ) ; }
bool operator < = ( DqnV2 const & b ) const { return ( this - > x < = b . x ) & & ( this - > y < = b . y ) ; }
bool operator < ( DqnV2 const & b ) const { return ( this - > x < b . x ) & & ( this - > y < b . y ) ; }
bool operator > ( DqnV2 const & b ) const { return ( this - > x > b . x ) & & ( this - > y > b . y ) ; }
2018-01-31 13:08:42 +00:00
} ;
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_Add ( DqnV2 a , DqnV2 b ) ;
DQN_FILE_SCOPE DqnV2 DqnV2_Sub ( DqnV2 a , DqnV2 b ) ;
DQN_FILE_SCOPE DqnV2 DqnV2_Scalei ( DqnV2 a , i32 b ) ;
DQN_FILE_SCOPE DqnV2 DqnV2_Scalef ( DqnV2 a , f32 b ) ;
DQN_FILE_SCOPE DqnV2 DqnV2_Hadamard ( DqnV2 a , DqnV2 b ) ;
DQN_FILE_SCOPE f32 DqnV2_Dot ( DqnV2 a , DqnV2 b ) ;
DQN_FILE_SCOPE bool DqnV2_Equals ( DqnV2 a , DqnV2 b ) ;
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE f32 DqnV2_LengthSquared ( const DqnV2 a , const DqnV2 b ) ;
DQN_FILE_SCOPE f32 DqnV2_Length ( const DqnV2 a , const DqnV2 b ) ;
DQN_FILE_SCOPE DqnV2 DqnV2_Normalise ( const DqnV2 a ) ;
DQN_FILE_SCOPE bool DqnV2_Overlaps ( DqnV2 a , DqnV2 b ) ;
DQN_FILE_SCOPE DqnV2 DqnV2_Perpendicular ( const DqnV2 a ) ;
2017-10-29 09:30:07 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_ResizeKeepAspectRatio ( DqnV2 src_size , DqnV2 target_size ) ;
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_ConstrainToRatio ( DqnV2 dim , DqnV2 ratio ) ; // Resize the dimension to fit the aspect ratio provided. Downscale only.
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE inline DqnV2 operator - ( DqnV2 a , DqnV2 b ) { return DqnV2_Sub ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2 operator + ( DqnV2 a , DqnV2 b ) { return DqnV2_Add ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2 operator * ( DqnV2 a , DqnV2 b ) { return DqnV2_Hadamard ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2 operator * ( DqnV2 a , f32 b ) { return DqnV2_Scalef ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2 operator * ( DqnV2 a , i32 b ) { return DqnV2_Scalei ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2 & operator * = ( DqnV2 & a , DqnV2 b ) { return ( a = DqnV2_Hadamard ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV2 & operator * = ( DqnV2 & a , f32 b ) { return ( a = DqnV2_Scalef ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV2 & operator * = ( DqnV2 & a , i32 b ) { return ( a = DqnV2_Scalei ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV2 & operator - = ( DqnV2 & a , DqnV2 b ) { return ( a = DqnV2_Sub ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV2 & operator + = ( DqnV2 & a , DqnV2 b ) { return ( a = DqnV2_Add ( a , b ) ) ; }
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
// DqnV2i
DQN_FILE_SCOPE DqnV2i DqnV2i_Add ( DqnV2i a , DqnV2i b ) ;
DQN_FILE_SCOPE DqnV2i DqnV2i_Sub ( DqnV2i a , DqnV2i b ) ;
DQN_FILE_SCOPE DqnV2i DqnV2i_Scalei ( DqnV2i a , i32 b ) ;
DQN_FILE_SCOPE DqnV2i DqnV2i_Scalef ( DqnV2i a , f32 b ) ;
DQN_FILE_SCOPE DqnV2i DqnV2i_Hadamard ( DqnV2i a , DqnV2i b ) ;
DQN_FILE_SCOPE f32 DqnV2i_Dot ( DqnV2i a , DqnV2i b ) ;
DQN_FILE_SCOPE bool DqnV2i_Equals ( DqnV2i a , DqnV2i b ) ;
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE inline DqnV2i operator - ( DqnV2i a , DqnV2i b ) { return DqnV2i_Sub ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2i operator + ( DqnV2i a , DqnV2i b ) { return DqnV2i_Add ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2i operator * ( DqnV2i a , DqnV2i b ) { return DqnV2i_Hadamard ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2i operator * ( DqnV2i a , f32 b ) { return DqnV2i_Scalef ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2i operator * ( DqnV2i a , i32 b ) { return DqnV2i_Scalei ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV2i & operator * = ( DqnV2i & a , DqnV2i b ) { return ( a = DqnV2i_Hadamard ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV2i & operator - = ( DqnV2i & a , DqnV2i b ) { return ( a = DqnV2i_Sub ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV2i & operator + = ( DqnV2i & a , DqnV2i b ) { return ( a = DqnV2i_Add ( a , b ) ) ; }
2017-10-29 09:30:07 +00:00
2018-09-10 12:49:17 +00:00
// #DqnV3
2018-01-31 13:08:42 +00:00
// =================================================================================================
union DqnV3
2018-01-18 05:01:37 +00:00
{
2018-05-17 11:19:58 +00:00
struct { f32 x , y , z ; } ;
struct { f32 r , g , b ; } ;
2018-06-09 04:29:19 +00:00
DqnV2 xy ;
2018-05-17 11:19:58 +00:00
f32 e [ 3 ] ;
2018-06-09 04:29:19 +00:00
DqnV3 ( ) = default ;
DqnV3 ( f32 xyz ) : x ( xyz ) , y ( xyz ) , z ( xyz ) { }
DqnV3 ( f32 x_ , f32 y_ , f32 z_ ) : x ( x_ ) , y ( y_ ) , z ( z_ ) { }
DqnV3 ( i32 x_ , i32 y_ , i32 z_ ) : x ( ( f32 ) x_ ) , y ( ( f32 ) y_ ) , z ( ( f32 ) z_ ) { }
2018-01-31 13:08:42 +00:00
} ;
2018-01-18 05:01:37 +00:00
2018-01-31 13:08:42 +00:00
union DqnV3i
2017-10-29 09:30:07 +00:00
{
2018-05-17 11:19:58 +00:00
struct { i32 x , y , z ; } ;
struct { i32 r , g , b ; } ;
i32 e [ 3 ] ;
2018-06-09 04:29:19 +00:00
DqnV3i ( ) = default ;
DqnV3i ( i32 x_ , i32 y_ , i32 z_ ) : x ( x_ ) , y ( y_ ) , z ( z_ ) { }
DqnV3i ( f32 x_ , f32 y_ , f32 z_ ) : x ( ( i32 ) x_ ) , y ( ( i32 ) y_ ) , z ( ( i32 ) z_ ) { }
2018-01-31 13:08:42 +00:00
} ;
2018-01-19 02:48:01 +00:00
2018-01-31 13:08:42 +00:00
// DqnV3
DQN_FILE_SCOPE DqnV3 DqnV3_Add ( DqnV3 a , DqnV3 b ) ;
DQN_FILE_SCOPE DqnV3 DqnV3_Sub ( DqnV3 a , DqnV3 b ) ;
DQN_FILE_SCOPE DqnV3 DqnV3_Scalei ( DqnV3 a , i32 b ) ;
DQN_FILE_SCOPE DqnV3 DqnV3_Scalef ( DqnV3 a , f32 b ) ;
DQN_FILE_SCOPE DqnV3 DqnV3_Hadamard ( DqnV3 a , DqnV3 b ) ;
DQN_FILE_SCOPE f32 DqnV3_Dot ( DqnV3 a , DqnV3 b ) ;
DQN_FILE_SCOPE bool DqnV3_Equals ( DqnV3 a , DqnV3 b ) ;
DQN_FILE_SCOPE DqnV3 DqnV3_Cross ( DqnV3 a , DqnV3 b ) ;
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE DqnV3 DqnV3_Normalise ( DqnV3 a ) ;
DQN_FILE_SCOPE f32 DqnV3_Length ( DqnV3 a , DqnV3 b ) ;
DQN_FILE_SCOPE f32 DqnV3_LengthSquared ( DqnV3 a , DqnV3 b ) ;
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE inline DqnV3 operator - ( DqnV3 a , DqnV3 b ) { return DqnV3_Sub ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV3 operator + ( DqnV3 a , DqnV3 b ) { return DqnV3_Add ( a , b ) ; }
2018-06-09 04:29:19 +00:00
DQN_FILE_SCOPE inline DqnV3 operator + ( DqnV3 a , f32 b ) { return DqnV3_Add ( a , DqnV3 ( b ) ) ; }
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE inline DqnV3 operator * ( DqnV3 a , DqnV3 b ) { return DqnV3_Hadamard ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV3 operator * ( DqnV3 a , f32 b ) { return DqnV3_Scalef ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV3 operator * ( DqnV3 a , i32 b ) { return DqnV3_Scalei ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV3 operator / ( DqnV3 a , f32 b ) { return DqnV3_Scalef ( a , ( 1.0f / b ) ) ; }
DQN_FILE_SCOPE inline DqnV3 & operator * = ( DqnV3 & a , DqnV3 b ) { return ( a = DqnV3_Hadamard ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV3 & operator * = ( DqnV3 & a , f32 b ) { return ( a = DqnV3_Scalef ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV3 & operator * = ( DqnV3 & a , i32 b ) { return ( a = DqnV3_Scalei ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV3 & operator - = ( DqnV3 & a , DqnV3 b ) { return ( a = DqnV3_Sub ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV3 & operator + = ( DqnV3 & a , DqnV3 b ) { return ( a = DqnV3_Add ( a , b ) ) ; }
DQN_FILE_SCOPE inline bool operator = = ( DqnV3 a , DqnV3 b ) { return DqnV3_Equals ( a , b ) ; }
2017-10-29 09:30:07 +00:00
2018-09-10 12:49:17 +00:00
// #DqnV4
2018-01-31 13:08:42 +00:00
// =================================================================================================
union DqnV4
2017-10-29 09:30:07 +00:00
{
2018-05-17 11:19:58 +00:00
struct { f32 x , y , z , w ; } ;
DqnV3 xyz ;
DqnV2 xy ;
2017-10-29 09:30:07 +00:00
2018-05-17 11:19:58 +00:00
struct { f32 r , g , b , a ; } ;
DqnV3 rgb ;
2017-10-29 09:30:07 +00:00
2018-05-17 11:19:58 +00:00
f32 e [ 4 ] ;
DqnV2 v2 [ 2 ] ;
2017-10-29 09:30:07 +00:00
2018-06-09 04:29:19 +00:00
DqnV4 ( ) = default ;
DqnV4 ( f32 xyzw ) : x ( xyzw ) , y ( xyzw ) , z ( xyzw ) , w ( xyzw ) { }
DqnV4 ( f32 x_ , f32 y_ , f32 z_ , f32 w_ ) : x ( x_ ) , y ( y_ ) , z ( z_ ) , w ( w_ ) { }
DqnV4 ( i32 x_ , i32 y_ , i32 z_ , i32 w_ ) : x ( ( f32 ) x_ ) , y ( ( f32 ) y_ ) , z ( ( f32 ) z_ ) , w ( ( f32 ) w_ ) { }
DqnV4 ( DqnV3 a , f32 w_ ) : x ( a . x ) , y ( a . y ) , z ( a . z ) , w ( w_ ) { }
} ;
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE DqnV4 DqnV4_Add ( DqnV4 a , DqnV4 b ) ;
DQN_FILE_SCOPE DqnV4 DqnV4_Sub ( DqnV4 a , DqnV4 b ) ;
DQN_FILE_SCOPE DqnV4 DqnV4_Scalef ( DqnV4 a , f32 b ) ;
DQN_FILE_SCOPE DqnV4 DqnV4_Scalei ( DqnV4 a , i32 b ) ;
DQN_FILE_SCOPE DqnV4 DqnV4_Hadamard ( DqnV4 a , DqnV4 b ) ;
DQN_FILE_SCOPE f32 DqnV4_Dot ( DqnV4 a , DqnV4 b ) ;
DQN_FILE_SCOPE bool DqnV4_Equals ( DqnV4 a , DqnV4 b ) ;
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE inline DqnV4 operator - ( DqnV4 a , DqnV4 b ) { return DqnV4_Sub ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV4 operator + ( DqnV4 a , DqnV4 b ) { return DqnV4_Add ( a , b ) ; }
2018-06-09 04:29:19 +00:00
DQN_FILE_SCOPE inline DqnV4 operator + ( DqnV4 a , f32 b ) { return DqnV4_Add ( a , DqnV4 ( b ) ) ; }
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE inline DqnV4 operator * ( DqnV4 a , DqnV4 b ) { return DqnV4_Hadamard ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV4 operator * ( DqnV4 a , f32 b ) { return DqnV4_Scalef ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV4 operator * ( DqnV4 a , i32 b ) { return DqnV4_Scalei ( a , b ) ; }
DQN_FILE_SCOPE inline DqnV4 & operator * = ( DqnV4 & a , DqnV4 b ) { return ( a = DqnV4_Hadamard ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV4 & operator * = ( DqnV4 & a , f32 b ) { return ( a = DqnV4_Scalef ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV4 & operator * = ( DqnV4 & a , i32 b ) { return ( a = DqnV4_Scalei ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV4 & operator - = ( DqnV4 & a , DqnV4 b ) { return ( a = DqnV4_Sub ( a , b ) ) ; }
DQN_FILE_SCOPE inline DqnV4 & operator + = ( DqnV4 & a , DqnV4 b ) { return ( a = DqnV4_Add ( a , b ) ) ; }
DQN_FILE_SCOPE inline bool operator = = ( DqnV4 & a , DqnV4 b ) { return DqnV4_Equals ( a , b ) ; }
2017-10-29 09:30:07 +00:00
2018-09-10 12:49:17 +00:00
// #DqnMat4
2018-01-31 13:08:42 +00:00
// =================================================================================================
typedef union DqnMat4
2017-10-29 09:30:07 +00:00
{
2018-05-17 11:19:58 +00:00
// TODO(doyle): Row/column instead? More cache friendly since multiplication
// prefers rows.
DqnV4 col [ 4 ] ;
f32 e [ 4 ] [ 4 ] ; // Column/row
2018-01-31 13:08:42 +00:00
} DqnMat4 ;
2017-10-29 09:30:07 +00:00
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity ( ) ;
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_Orthographic ( f32 left , f32 right , f32 bottom , f32 top , f32 znear , f32 zfar ) ;
DQN_FILE_SCOPE DqnMat4 DqnMat4_Perspective ( f32 fov_y_degrees , f32 aspect_ratio , f32 znear , f32 zfar ) ;
2018-01-31 13:08:42 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_LookAt ( DqnV3 eye , DqnV3 center , DqnV3 up ) ;
DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate3f ( f32 x , f32 y , f32 z ) ;
DQN_FILE_SCOPE DqnMat4 DqnMat4_TranslateV3 ( DqnV3 vec ) ;
DQN_FILE_SCOPE DqnMat4 DqnMat4_Rotate ( f32 radians , f32 x , f32 y , f32 z ) ;
DQN_FILE_SCOPE DqnMat4 DqnMat4_Scale ( f32 x , f32 y , f32 z ) ;
DQN_FILE_SCOPE DqnMat4 DqnMat4_ScaleV3 ( DqnV3 scale ) ;
DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul ( DqnMat4 a , DqnMat4 b ) ;
DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4 ( DqnMat4 a , DqnV4 b ) ;
2018-09-10 12:49:17 +00:00
// #DqnRect
2018-01-31 13:08:42 +00:00
// =================================================================================================
2018-03-08 15:03:05 +00:00
struct DqnRect
2017-10-29 09:30:07 +00:00
{
2018-05-17 11:19:58 +00:00
DqnV2 min ;
DqnV2 max ;
2017-10-29 09:30:07 +00:00
2018-06-09 04:29:19 +00:00
DqnRect ( ) = default ;
DqnRect ( DqnV2 origin , DqnV2 size ) { this - > min = origin ; this - > max = origin + size ; }
DqnRect ( f32 x , f32 y , f32 w , f32 h ) { this - > min = DqnV2 ( x , y ) ; this - > max = DqnV2 ( x + w , y + h ) ; }
DqnRect ( i32 x , i32 y , i32 w , i32 h ) { this - > min = DqnV2 ( x , y ) ; this - > max = DqnV2 ( x + w , y + h ) ; }
2018-05-17 11:19:58 +00:00
f32 GetWidth ( ) const { return max . w - min . w ; }
f32 GetHeight ( ) const { return max . h - min . h ; }
DqnV2 GetSize ( ) const { return max - min ; }
void GetSize ( f32 * const width , f32 * const height ) const ;
DqnV2 GetCenter ( ) const ;
2018-01-31 13:08:42 +00:00
2018-06-09 04:29:19 +00:00
DqnRect ClipRect ( DqnRect const clip ) const ;
DqnRect Move ( DqnV2 const shift ) const ;
bool ContainsP ( DqnV2 const p ) const ;
2018-03-08 15:03:05 +00:00
} ;
2018-05-17 11:19:58 +00:00
struct DqnJson
{
enum struct Type
{
Object ,
2018-07-12 14:46:08 +00:00
ArrayOfObjects ,
ArrayOfPrimitives ,
2018-05-17 11:19:58 +00:00
} ;
Type type ;
DqnSlice < char > value ;
2018-08-05 07:30:54 +00:00
i32 num_entries ;
2018-05-17 11:19:58 +00:00
2018-07-12 14:46:08 +00:00
operator bool ( ) const { return ( value . data ! = nullptr ) ; }
bool IsArray ( ) const { return ( type = = Type : : ArrayOfObjects | | type = = Type : : ArrayOfPrimitives ) ; }
i64 ToI64 ( ) const { return Dqn_StrToI64 ( value . data , value . len ) ; }
2018-05-17 11:19:58 +00:00
} ;
// Zero allocation json finder. Returns the data of the value.
// If array, it returns a slice from [..] not-inclusive, if object, it returns a slice from {..} not-inclusive
// If just name value pair, it returns the literal with quotes or just the value if it is a primitive with quotes.
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnJson DqnJson_Get ( char const * buf , i32 buf_len , char const * find_property , i32 find_property_len ) ;
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnSlice < char > const buf , DqnSlice < char > const find_property ) ;
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnSlice < char > const buf , DqnSlice < char const > const find_property ) ;
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnSlice < char const > const buf , DqnSlice < char const > const find_property ) ;
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnJson const input , DqnSlice < char const > const find_property ) ;
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnJson const input , DqnSlice < char > const find_property ) ;
2018-03-08 15:03:05 +00:00
// return: The array item.
2018-06-25 10:51:18 +00:00
DQN_FILE_SCOPE DqnJson DqnJson_GetNextArrayItem ( DqnJson * iterator ) ;
2018-03-08 15:03:05 +00:00
2017-06-20 12:23:34 +00:00
# endif /* DQN_H */
2018-09-10 12:49:17 +00:00
// #XPlatform (Win32 & Unix)
2018-01-18 09:25:44 +00:00
// =================================================================================================
2017-06-22 04:00:11 +00:00
// Functions in the Cross Platform are guaranteed to be supported in both Unix
// and Win32
2017-06-25 15:57:51 +00:00
2017-07-03 05:17:09 +00:00
// NOTE(doyle): DQN_PLATFORM_HEADER is enabled by the user to have the function prototypes be
// visible. DQN_PLATFORM_H is like a normal header guard that ensures singular declaration of
// functions.
# ifdef DQN_PLATFORM_HEADER
# ifndef DQN_PLATFORM_H
# define DQN_PLATFORM_H
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_UNIX)
2018-05-17 11:19:58 +00:00
# include <pthread.h>
# include <semaphore.h>
2017-06-25 15:57:51 +00:00
# endif
2018-05-18 13:19:50 +00:00
2018-09-10 12:49:17 +00:00
// XPlatform > #DqnOS
2018-07-09 14:53:54 +00:00
// =================================================================================================
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void * DqnOS_VAlloc ( isize size , void * base_addr = nullptr ) ;
2018-07-09 14:53:54 +00:00
DQN_FILE_SCOPE void DqnOS_VFree ( void * address , isize size ) ;
// Uses a single call to DqnMem_Calloc() and DqnMem_Free(). Not completely platform "independent" for Unix.
2018-08-05 07:30:54 +00:00
// num_cores: num_threads_per_core: Can be nullptr, the function will just skip it.
DQN_FILE_SCOPE void DqnOS_GetThreadsAndCores ( u32 * const num_cores , u32 * const num_threads_per_core ) ;
2018-07-09 14:53:54 +00:00
// #XPlatform > #DqnVArray Array backed by virtual memory
// =================================================================================================
template < typename T >
struct DqnVArray
{
2018-08-05 07:30:54 +00:00
isize len ; // Read
isize max ; // Read
T * data ; // Read
2018-07-09 14:53:54 +00:00
2018-07-17 14:47:13 +00:00
DqnVArray ( ) = default ; // Zero is initialisation
2018-07-14 16:17:34 +00:00
DqnVArray ( isize size ) { LazyInit ( size ) ; }
2018-08-29 15:04:56 +00:00
void LazyInit ( isize size ) { * this = { } ; if ( data ) return ; len = 0 ; max = size ; data = ( T * ) DqnOS_VAlloc ( max * sizeof ( T ) ) ; DQN_ALWAYS_ASSERT ( data ) ; }
// ~DqnVArray () { if (data) DqnOS_VFree(data, sizeof(T) * max); }
2018-07-14 16:17:34 +00:00
2018-09-02 11:17:14 +00:00
void Clear ( Dqn : : ZeroMem clear = Dqn : : ZeroMem : : No ) { if ( data ) { len = 0 ; if ( clear = = Dqn : : ZeroMem : : Yes ) DqnMem_Clear ( data , 0 , sizeof ( T ) * max ) ; } }
2018-07-09 14:53:54 +00:00
void Free ( ) { if ( data ) { DqnOS_VFree ( data , sizeof ( T ) * max ) ; } * this = { } ; }
2018-08-05 07:30:54 +00:00
T * Front ( ) { return ( len > 0 ) ? ( data + 0 ) : nullptr ; }
T * Back ( ) { return ( len > 0 ) ? ( data + ( len - 1 ) ) : nullptr ; }
2018-08-29 15:04:56 +00:00
T * Make ( isize num = 1 ) { if ( ! data ) LazyInit ( 1024 ) ; len + = num ; DQN_ASSERT ( len < = max ) ; return & data [ len - num ] ; }
2018-12-15 15:23:04 +00:00
T * Add ( T const & v ) { data [ len + + ] = v ; return data + ( len - 1 ) ; }
T * Add ( T const * v , isize v_len = 1 ) { T * result = data + len ; for ( isize i = 0 ; i < v_len ; + + i ) data [ len + + ] = v [ i ] ; return result ; }
2018-08-05 07:30:54 +00:00
void Pop ( ) { if ( len > 0 ) len - - ; }
void Erase ( isize index ) { if ( ! data ) return ; DQN_ASSERT ( index > = 0 & & index < len ) ; data [ index ] = data [ - - len ] ; }
2018-07-09 14:53:54 +00:00
void EraseStable ( isize index ) ;
T * Insert ( isize index , T const * v ) { return Insert ( index , v , 1 ) ; }
T * Insert ( isize index , T const & v ) { return Insert ( index , & v , 1 ) ; }
2018-08-05 07:30:54 +00:00
T * Insert ( isize index , T const * v , isize num_items ) ;
bool Contains ( T const * v ) const { T const * ptr = data ; T const * end = data + len ; while ( ptr < end ) { if ( * ptr + + = = * v ) return true ; } return false ; }
2018-07-09 14:53:54 +00:00
2018-08-05 07:30:54 +00:00
T & operator [ ] ( isize i ) const { DQN_ASSERT ( i < len & & i > 0 ) ; return this - > data [ i ] ; }
2018-07-09 14:53:54 +00:00
T * begin ( ) { return data ; }
2018-08-05 07:30:54 +00:00
T * end ( ) { return data + len ; }
2018-07-09 14:53:54 +00:00
} ;
2018-08-05 07:30:54 +00:00
template < typename T > T * DqnVArray < T > : : Insert ( isize index , T const * v , isize num_items )
2018-07-09 14:53:54 +00:00
{
2018-08-29 15:04:56 +00:00
if ( ! data ) LazyInit ( 1024 ) ;
2018-07-14 16:17:34 +00:00
2018-08-05 07:30:54 +00:00
index = DQN_CLAMP ( index , 0 , len ) ;
isize const new_len = len + num_items ;
DQN_ASSERT ( new_len < = max ) ;
2018-07-09 14:53:54 +00:00
T * src = data + index ;
2018-08-05 07:30:54 +00:00
T * dest = src + num_items ;
2018-07-09 14:53:54 +00:00
if ( src < dest )
2018-08-05 07:30:54 +00:00
memmove ( dest , src , ( ( data + len ) - src ) * sizeof ( T ) ) ;
2018-07-09 14:53:54 +00:00
2018-08-05 07:30:54 +00:00
len = new_len ;
for ( isize i = 0 ; i < num_items ; i + + )
2018-07-09 14:53:54 +00:00
src [ i ] = v [ i ] ;
return src ;
}
template < typename T > void DqnVArray < T > : : EraseStable ( isize index )
{
2018-07-14 16:17:34 +00:00
if ( ! data ) return ;
2018-08-05 07:30:54 +00:00
DQN_ASSERT ( index > = 0 & & index < len ) ;
2018-07-09 14:53:54 +00:00
isize const off = ( data + index ) - data ;
2018-08-05 07:30:54 +00:00
memmove ( data + off , data + off + 1 , ( ( usize ) len - ( usize ) off - 1 ) * sizeof ( T ) ) ;
len - - ;
2018-07-09 14:53:54 +00:00
}
2018-09-10 12:49:17 +00:00
// #XPlatform > #DqnVHashTable
2018-07-14 13:00:25 +00:00
// =================================================================================================
2018-07-14 16:17:34 +00:00
template < typename Key > using DqnVHashTableHashingProc = isize ( * ) ( isize count , Key const & data ) ;
template < typename Key > using DqnVHashTableEqualsProc = bool ( * ) ( Key const & a , Key const & b ) ;
2018-07-28 07:40:57 +00:00
const u64 DQN_VHASH_TABLE_DEFAULT_SEED = 0x9747B28CAB3F8A7B ;
# define DQN_VHASH_TABLE_HASHING_PROC(name, Type) inline isize name(isize count, Type const &key)
# define DQN_VHASH_TABLE_EQUALS_PROC(name, Type) inline bool name(Type const &a, Type const &b)
template < typename T > DQN_VHASH_TABLE_HASHING_PROC ( DqnVHashTableDefaultHash , T ) { return DqnHash_Murmur64Seed ( & key , sizeof ( key ) , DQN_VHASH_TABLE_DEFAULT_SEED ) % count ; }
template < typename T > DQN_VHASH_TABLE_EQUALS_PROC ( DqnVHashTableDefaultEquals , T ) { return ( DqnMem_Cmp ( & a , & b , sizeof ( a ) ) = = 0 ) ; }
2018-08-27 14:25:08 +00:00
template < > DQN_VHASH_TABLE_HASHING_PROC ( DqnVHashTableDefaultHash < DqnString > , DqnString ) { return DqnHash_Murmur64Seed ( & key . str , key . len , DQN_VHASH_TABLE_DEFAULT_SEED ) % count ; }
template < > DQN_VHASH_TABLE_EQUALS_PROC ( DqnVHashTableDefaultEquals < DqnString > , DqnString ) { return ( a . len = = b . len ) & & ( DqnStr_Cmp ( a . str , b . str , a . len ) = = 0 ) ; }
template < > DQN_VHASH_TABLE_HASHING_PROC ( DqnVHashTableDefaultHash < DqnBuffer < char > > , DqnBuffer < char > ) { return DqnHash_Murmur64Seed ( & key . str , key . len , DQN_VHASH_TABLE_DEFAULT_SEED ) % count ; }
template < > DQN_VHASH_TABLE_EQUALS_PROC ( DqnVHashTableDefaultEquals < DqnBuffer < char > > , DqnBuffer < char > ) { return DQN_BUFFER_STRCMP ( a , b , Dqn : : IgnoreCase : : No ) ; }
2018-07-28 07:40:57 +00:00
// TODO(doyle): Fix this so we don't have to manually declare the fixed string sizes for hashing and equals
# define DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS(StringCapacity) \
template < > \
DQN_VHASH_TABLE_HASHING_PROC ( DqnVHashTableDefaultHash < DqnFixedString < StringCapacity > > , \
DqnFixedString < StringCapacity > ) \
{ \
return DqnHash_Murmur64Seed ( key . str , key . len , DQN_VHASH_TABLE_DEFAULT_SEED ) % count ; \
} \
template < > \
DQN_VHASH_TABLE_EQUALS_PROC ( DqnVHashTableDefaultEquals < DqnFixedString < StringCapacity > > , \
DqnFixedString < StringCapacity > ) \
{ \
return ( a . len = = b . len ) & & ( DqnStr_Cmp ( a . str , b . str , a . len , Dqn : : IgnoreCase : : No ) = = 0 ) ; \
}
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS ( 1024 )
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS ( 512 )
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS ( 256 )
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS ( 128 )
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS ( 64 )
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS ( 32 )
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS ( 16 )
DQN_VHASH_TABLE_DEFAULT_FIXED_STRING_PROCS ( 8 )
2018-07-14 16:17:34 +00:00
2018-07-14 13:00:25 +00:00
# define DQN_VHASH_TABLE_TEMPLATE \
template < typename Key , \
typename Item , \
2018-07-27 04:43:39 +00:00
DqnVHashTableHashingProc < Key > Hash , \
DqnVHashTableEqualsProc < Key > Equals >
2018-07-14 13:00:25 +00:00
# define DQN_VHASH_TABLE_DECL DqnVHashTable<Key, Item, Hash, Equals>
2018-07-27 04:43:39 +00:00
template < typename Key ,
typename Item ,
DqnVHashTableHashingProc < Key > Hash = DqnVHashTableDefaultHash < Key > ,
DqnVHashTableEqualsProc < Key > Equals = DqnVHashTableDefaultEquals < Key > >
struct DqnVHashTable
2018-07-14 13:00:25 +00:00
{
struct Entry
{
2018-07-24 15:33:31 +00:00
union { Key key ; Key first ; } ;
union { Item item ; Item second ; } ;
2018-07-14 13:00:25 +00:00
} ;
struct Bucket
{
Entry entries [ 4 ] ;
2018-08-27 14:25:08 +00:00
isize entry_index ;
2018-07-14 13:00:25 +00:00
} ;
Bucket * buckets ;
2018-08-27 14:25:08 +00:00
isize num_buckets ;
isize * indexes_of_used_buckets ;
2018-09-02 14:02:32 +00:00
isize num_used_entries ;
2018-08-27 14:25:08 +00:00
isize num_used_buckets ;
2018-07-14 13:00:25 +00:00
2018-07-24 13:05:08 +00:00
DqnVHashTable ( ) = default ;
2018-08-27 14:25:08 +00:00
DqnVHashTable ( isize size ) { LazyInit ( size ) ; }
2018-07-24 13:05:08 +00:00
void LazyInit ( isize size = DQN_MAX ( DQN_MEGABYTE ( 1 ) / sizeof ( Bucket ) , 1024 ) ) ;
2018-08-27 14:25:08 +00:00
void Free ( ) { if ( buckets ) DqnOS_VFree ( buckets , sizeof ( buckets ) * num_buckets ) ; * this = { } ; }
2018-07-14 13:00:25 +00:00
2018-07-24 15:33:31 +00:00
void Erase ( Key const & key ) ; // Delete the element matching key, does nothing if key not found.
Entry * GetEntry ( Key const & key ) ; // return: The (key, item) entry associated with the key, nullptr if key not in table yet.
Item * GetOrMake ( Key const & key , bool * existed = nullptr ) ; // return: Item if found, otherwise make an entry (key, item) and return the ptr to the uninitialised item
2018-07-24 13:05:08 +00:00
Item * Get ( Key const & key ) { Entry * entry = GetEntry ( key ) ; return ( entry ) ? & entry - > item : nullptr ; }
Item * Set ( Key const & key , Item const & item ) { Item * result = GetOrMake ( key ) ; * result = item ; return result ; }
2018-07-14 13:00:25 +00:00
2018-07-24 13:05:08 +00:00
Item * operator [ ] ( Key const & key ) { return Get ( key ) ; }
2018-07-14 13:00:25 +00:00
2018-07-23 14:05:43 +00:00
struct Iterator
{
2018-07-24 15:33:31 +00:00
Entry * entry ;
2018-08-27 14:25:08 +00:00
Iterator ( DqnVHashTable * table_ , isize num_used_buckets_ = 0 , isize index_in_bucket_ = 0 ) ;
Bucket * GetCurrBucket ( ) const { return ( table - > buckets + table - > indexes_of_used_buckets [ num_used_buckets ] ) ; }
Entry * GetCurrEntry ( ) const { return GetCurrBucket ( ) - > entries + index_in_bucket ; }
2018-07-23 14:05:43 +00:00
Item * GetCurrItem ( ) const { return & ( GetCurrEntry ( ) - > item ) ; }
2018-08-27 14:25:08 +00:00
bool operator ! = ( Iterator const & other ) const { return ! ( num_used_buckets = = other . num_used_buckets & & index_in_bucket = = other . index_in_bucket ) ; }
2018-07-24 15:33:31 +00:00
Entry & operator * ( ) const { return * GetCurrEntry ( ) ; }
2018-07-28 04:41:38 +00:00
Iterator & operator + + ( ) ;
2018-08-27 14:25:08 +00:00
Iterator & operator - - ( ) { if ( - - index_in_bucket < 0 ) { index_in_bucket = 0 ; num_used_buckets = DQN_MAX ( - - num_used_buckets , 0 ) ; } entry = GetCurrEntry ( ) ; return * this ; }
2018-09-02 11:17:14 +00:00
Iterator operator + + ( int ) { Iterator result = * this ; + + ( * this ) ; return result ; } // postfix
Iterator operator - - ( int ) { Iterator result = * this ; - - ( * this ) ; return result ; } // postfix
2018-07-24 15:33:31 +00:00
Iterator operator + ( int offset ) const { Iterator result = * this ; DQN_FOR_EACH ( i , DQN_ABS ( offset ) ) { ( offset > 0 ) ? + + result : - - result ; } return result ; } // TODO(doyle): Improve
Iterator operator - ( int offset ) const { Iterator result = * this ; DQN_FOR_EACH ( i , DQN_ABS ( offset ) ) { ( offset > 0 ) ? - - result : + + result ; } return result ; } // TODO(doyle): Improve
private :
DqnVHashTable * table ;
2018-08-27 14:25:08 +00:00
isize num_used_buckets ;
isize index_in_bucket ;
2018-07-23 14:05:43 +00:00
} ;
2018-09-02 11:17:14 +00:00
Iterator begin ( ) { return Iterator ( this ) ; }
Iterator end ( ) { return Iterator ( this , num_buckets , DQN_ARRAY_COUNT ( this - > buckets [ 0 ] . entries ) ) ; }
2018-07-14 13:00:25 +00:00
} ;
2018-07-28 04:41:38 +00:00
DQN_VHASH_TABLE_TEMPLATE DQN_VHASH_TABLE_DECL : : Iterator : : Iterator ( DqnVHashTable * table_ ,
2018-08-27 14:25:08 +00:00
isize num_used_buckets_ ,
isize index_in_bucket_ )
2018-07-28 04:41:38 +00:00
: table ( table_ )
2018-08-27 14:25:08 +00:00
, num_used_buckets ( num_used_buckets_ )
, index_in_bucket ( index_in_bucket_ )
2018-07-28 04:41:38 +00:00
, entry ( nullptr )
{
2018-08-27 14:25:08 +00:00
bool sentinel_index = ( num_used_buckets = = table - > num_buckets & &
index_in_bucket = = DQN_ARRAY_COUNT ( table - > buckets [ 0 ] . entries ) ) ;
bool empty_table = ( table - > num_used_buckets = = 0 ) ;
if ( empty_table | | sentinel_index )
2018-07-28 04:41:38 +00:00
{
2018-08-27 14:25:08 +00:00
if ( empty_table )
2018-07-28 04:41:38 +00:00
{
2018-08-27 14:25:08 +00:00
this - > num_used_buckets = table - > num_buckets ;
this - > index_in_bucket = DQN_ARRAY_COUNT ( table - > buckets [ 0 ] . entries ) ;
2018-07-28 04:41:38 +00:00
}
}
else
{
entry = GetCurrEntry ( ) ;
}
}
DQN_VHASH_TABLE_TEMPLATE typename DQN_VHASH_TABLE_DECL : : Iterator & DQN_VHASH_TABLE_DECL : : Iterator : : operator + + ( )
{
2018-08-27 14:25:08 +00:00
if ( + + index_in_bucket > = GetCurrBucket ( ) - > entry_index )
2018-07-28 04:41:38 +00:00
{
2018-08-27 14:25:08 +00:00
index_in_bucket = 0 ;
num_used_buckets + + ;
2018-07-28 04:41:38 +00:00
}
2018-08-27 14:25:08 +00:00
if ( num_used_buckets < table - > num_used_buckets )
2018-07-28 04:41:38 +00:00
entry = GetCurrEntry ( ) ;
else
* this = table - > end ( ) ;
return * this ;
}
2018-07-23 14:05:43 +00:00
DQN_VHASH_TABLE_TEMPLATE void DQN_VHASH_TABLE_DECL : : LazyInit ( isize size )
{
2018-09-02 14:02:32 +00:00
* this = { } ;
this - > num_buckets = size ;
this - > buckets = static_cast < Bucket * > ( DqnOS_VAlloc ( size * sizeof ( * this - > buckets ) ) ) ;
2018-08-27 14:25:08 +00:00
this - > indexes_of_used_buckets = static_cast < isize * > ( DqnOS_VAlloc ( size * sizeof ( * this - > indexes_of_used_buckets ) ) ) ;
DQN_ASSERT ( this - > buckets & & this - > indexes_of_used_buckets ) ;
2018-07-23 14:05:43 +00:00
}
2018-07-24 13:05:08 +00:00
DQN_VHASH_TABLE_TEMPLATE typename DQN_VHASH_TABLE_DECL : : Entry *
DQN_VHASH_TABLE_DECL : : GetEntry ( Key const & key )
2018-07-14 13:00:25 +00:00
{
2018-07-24 13:05:08 +00:00
if ( ! buckets ) return nullptr ;
2018-07-14 13:00:25 +00:00
2018-08-27 14:25:08 +00:00
isize index = Hash ( this - > num_buckets , key ) ;
2018-07-24 13:05:08 +00:00
Bucket * bucket = this - > buckets + index ;
Entry * result = nullptr ;
2018-08-27 14:25:08 +00:00
for ( isize i = 0 ; i < bucket - > entry_index & & ! result ; i + + )
2018-07-24 13:05:08 +00:00
{
Entry * entry = bucket - > entries + i ;
result = Equals ( entry - > key , key ) ? entry : nullptr ;
}
2018-07-14 13:00:25 +00:00
2018-07-24 13:05:08 +00:00
return result ;
}
2018-07-24 15:33:31 +00:00
DQN_VHASH_TABLE_TEMPLATE Item * DQN_VHASH_TABLE_DECL : : GetOrMake ( Key const & key , bool * existed )
2018-07-24 13:05:08 +00:00
{
if ( ! this - > buckets ) LazyInit ( ) ;
2018-08-27 14:25:08 +00:00
isize index = Hash ( this - > num_buckets , key ) ;
2018-07-24 13:05:08 +00:00
Bucket * bucket = this - > buckets + index ;
Entry * entry = nullptr ;
2018-08-27 14:25:08 +00:00
for ( isize i = 0 ; i < bucket - > entry_index & & ! entry ; i + + )
2018-07-14 13:00:25 +00:00
{
Entry * check = bucket - > entries + i ;
entry = Equals ( check - > key , key ) ? check : nullptr ;
}
2018-07-24 15:33:31 +00:00
if ( existed )
* existed = ( entry ! = nullptr ) ;
2018-07-14 13:00:25 +00:00
if ( ! entry )
{
2018-08-27 14:25:08 +00:00
DQN_ALWAYS_ASSERTM ( bucket - > entry_index < DQN_ARRAY_COUNT ( bucket - > entries ) ,
2018-07-24 13:05:08 +00:00
" More than %zu collisions in hash table, increase the size of the table or buckets " ,
DQN_ARRAY_COUNT ( bucket - > entries ) ) ;
2018-08-27 14:25:08 +00:00
if ( bucket - > entry_index = = 0 )
this - > indexes_of_used_buckets [ this - > num_used_buckets + + ] = index ;
2018-07-23 14:05:43 +00:00
2018-08-27 14:25:08 +00:00
entry = bucket - > entries + bucket - > entry_index + + ;
2018-07-24 13:05:08 +00:00
entry - > key = key ;
2018-09-02 14:02:32 +00:00
+ + this - > num_used_entries ;
2018-07-14 13:21:57 +00:00
2018-07-24 13:05:08 +00:00
// TODO(doyle): A maybe case. We're using virtual memory, so you should
// just initialise a larger size. It's essentially free ... maybe one
// day we care about resizing the table but at the cost of a lot more code
// complexity.
2018-08-27 14:25:08 +00:00
isize const threshold = static_cast < isize > ( 0.75f * this - > num_buckets ) ;
DQN_ALWAYS_ASSERTM ( this - > num_used_buckets < threshold , " %zu >= %zu " , this - > num_used_buckets , threshold ) ;
2018-07-14 13:00:25 +00:00
}
2018-07-24 13:05:08 +00:00
Item * result = & entry - > item ;
2018-07-14 13:00:25 +00:00
return result ;
}
DQN_VHASH_TABLE_TEMPLATE void DQN_VHASH_TABLE_DECL : : Erase ( Key const & key )
{
if ( ! buckets )
return ;
2018-08-27 14:25:08 +00:00
isize index = Hash ( this - > num_buckets , key ) ;
2018-07-14 13:00:25 +00:00
Bucket * bucket = this - > buckets + index ;
2018-08-27 14:25:08 +00:00
DQN_FOR_EACH ( i , bucket - > entry_index )
2018-07-14 13:00:25 +00:00
{
Entry * check = bucket - > entries + i ;
2018-09-02 14:02:32 +00:00
if ( ! Equals ( check - > key , key ) )
2018-07-14 13:00:25 +00:00
{
2018-09-02 14:02:32 +00:00
continue ;
}
for ( isize j = i ; j < ( bucket - > entry_index - 1 ) ; + + j )
bucket - > entries [ j ] = bucket - > entries [ j + 1 ] ;
2018-07-14 13:00:25 +00:00
2018-09-02 14:02:32 +00:00
- - this - > num_used_entries ;
if ( - - bucket - > entry_index = = 0 )
{
DQN_FOR_EACH ( bucketIndex , this - > num_used_buckets )
2018-07-23 14:05:43 +00:00
{
2018-09-02 14:02:32 +00:00
if ( this - > indexes_of_used_buckets [ bucketIndex ] = = index )
2018-07-23 14:05:43 +00:00
{
2018-09-02 14:02:32 +00:00
indexes_of_used_buckets [ bucketIndex ] =
indexes_of_used_buckets [ - - this - > num_used_buckets ] ;
2018-07-23 14:05:43 +00:00
}
}
2018-07-14 13:00:25 +00:00
}
2018-09-02 14:02:32 +00:00
DQN_ASSERT ( this - > num_used_entries > = 0 ) ;
DQN_ASSERT ( this - > num_used_buckets > = 0 ) ;
DQN_ASSERT ( bucket - > entry_index > = 0 ) ;
return ;
2018-07-14 13:00:25 +00:00
}
}
2018-09-10 12:49:17 +00:00
// XPlatform > #DqnFile
2018-07-27 04:43:39 +00:00
// =================================================================================================
struct DqnFile
{
2018-07-28 05:16:01 +00:00
enum Flag
2018-07-27 04:43:39 +00:00
{
2018-07-28 05:16:01 +00:00
FileRead = ( 1 < < 0 ) ,
FileWrite = ( 1 < < 1 ) ,
Execute = ( 1 < < 2 ) ,
All = ( 1 < < 3 ) ,
FileReadWrite = FileRead | FileWrite
2018-07-27 04:43:39 +00:00
} ;
enum struct Action
{
OpenOnly , // Only open file if it exists. Fails and returns false if file did not exist or could not open.
CreateIfNotExist , // Try and create file. Return true if it was able to create. If it already exists, this fails.
ClearIfExist , // Clear the file contents to zero if it exists. Fails and returns false if file does not exist.
ForceCreate , // Always create, even if it exists
} ;
u32 flags ;
void * handle ;
usize size ;
2018-09-02 11:17:14 +00:00
int curr_write_offset ;
2018-07-27 04:43:39 +00:00
// API
// ==============================================================================================
// NOTE: W(ide) versions of functions only work on Win32, since Unix is already UTF-8 compatible.
// Open a handle for file read and writing. Deleting files does not need a handle. Handles should be
// closed before deleting files otherwise the OS may not be able to delete the file.
2018-07-28 05:16:01 +00:00
// return: FALSE if invalid args or failed to get handle (i.e. insufficient permission)
2018-07-27 04:43:39 +00:00
bool Open ( char const * path , u32 const flags_ , Action const action ) ;
bool Open ( wchar_t const * path , u32 const flags_ , Action const action ) ;
2018-09-02 11:17:14 +00:00
// file_offset: The byte offset to starting writing from.
2018-07-27 04:43:39 +00:00
// return: The number of bytes written. 0 if invalid args or it failed to write.
2018-09-02 11:17:14 +00:00
usize Write ( u8 const * buf , usize const num_bytes_to_write ) ;
2018-07-27 04:43:39 +00:00
// IMPORTANT: You may want to allocate size+1 for null-terminating the file contents when reading into a buffer.
// return: The number of bytes read. 0 if invalid args or it failed to read.
2018-08-05 07:30:54 +00:00
usize Read ( u8 * buf , usize const num_bytes_to_read ) ;
2018-07-27 04:43:39 +00:00
// File close invalidates the handle after it is called.
void Close ( ) ;
} ;
struct DqnFileInfo
{
usize size ;
2018-08-05 07:30:54 +00:00
u64 create_time_in_s ;
u64 last_write_time_in_s ;
u64 last_access_time_in_s ;
2018-07-27 04:43:39 +00:00
} ;
2018-08-05 07:30:54 +00:00
// Read entire file into the given buffer. To determine required buf_size size, use GetFileSize.
2018-07-27 04:43:39 +00:00
// NOTE: You want size + 1 and add the null-terminator yourself if you want a null terminated buffer.
2018-08-05 07:30:54 +00:00
// bytes_read: Pass in to get how many bytes of the buf was used. Basically the return value of Read
// return: False if insufficient buf_size OR file access failure OR nullptr arguments.
DQN_FILE_SCOPE bool DqnFile_ReadAll ( char const * path , u8 * buf , usize buf_size ) ;
DQN_FILE_SCOPE bool DqnFile_ReadAll ( wchar_t const * path , u8 * buf , usize buf_size ) ;
2018-07-27 04:43:39 +00:00
// Buffer is null-terminated and should be freed when done with.
// return: False if file access failure OR nullptr arguments.
2018-09-13 13:58:48 +00:00
DQN_FILE_SCOPE u8 * DqnFile_ReadAll ( char const * path , usize * buf_size , DqnAllocator * allocator = dqn_lib_context_ . allocator ) ;
DQN_FILE_SCOPE u8 * DqnFile_ReadAll ( wchar_t const * path , usize * buf_size , DqnAllocator * allocator = dqn_lib_context_ . allocator ) ;
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE u8 * DqnFile_ReadAll ( wchar_t const * path , usize * buf_size , DqnMemStack * stack ) ;
DQN_FILE_SCOPE u8 * DqnFile_ReadAll ( char const * path , usize * buf_size , DqnMemStack * stack ) ;
2018-07-27 04:43:39 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE bool DqnFile_WriteAll ( char const * path , u8 const * buf , usize const buf_size ) ;
DQN_FILE_SCOPE bool DqnFile_WriteAll ( wchar_t const * path , u8 const * buf , usize const buf_size ) ;
2018-07-28 05:16:01 +00:00
2018-07-27 04:43:39 +00:00
// return: False if file access failure
DQN_FILE_SCOPE bool DqnFile_Size ( char const * path , usize * size ) ;
DQN_FILE_SCOPE bool DqnFile_Size ( wchar_t const * path , usize * size ) ;
2018-07-29 05:09:35 +00:00
DQN_FILE_SCOPE bool DqnFile_MakeDir ( char const * path ) ;
2018-08-27 14:25:08 +00:00
// info: (Optional) Pass in to fill with file attributes
2018-07-27 04:43:39 +00:00
// return: False if file access failure
DQN_FILE_SCOPE bool DqnFile_GetInfo ( char const * path , DqnFileInfo * info ) ;
DQN_FILE_SCOPE bool DqnFile_GetInfo ( wchar_t const * path , DqnFileInfo * info ) ;
// NOTE: You can't delete a file unless the handle has been closed to it on Win32.
// return: False if file access failure
DQN_FILE_SCOPE bool DqnFile_Delete ( char const * path ) ;
DQN_FILE_SCOPE bool DqnFile_Delete ( wchar_t const * path ) ;
DQN_FILE_SCOPE bool DqnFile_Copy ( char const * src , char const * dest ) ;
DQN_FILE_SCOPE bool DqnFile_Copy ( wchar_t const * src , wchar_t const * dest ) ;
// NOTE: Win32: Current directory is "*", Unix: "."
2018-08-05 07:30:54 +00:00
// num_files: Pass in a ref to a i32. The function fills it out with the number of entries.
2018-07-27 04:43:39 +00:00
// return: An array of strings of the files in the directory in UTF-8. The directory lisiting is
// allocated with malloc and must be freed using free() or the helper function ListDirFree()
2018-09-13 13:58:48 +00:00
DQN_FILE_SCOPE char * * DqnFile_ListDir ( char const * dir , i32 * num_files , DqnAllocator * allocator = dqn_lib_context_ . allocator ) ;
DQN_FILE_SCOPE void DqnFile_ListDirFree ( char * * file_list , i32 num_files , DqnAllocator * allocator = dqn_lib_context_ . allocator ) ;
2018-07-27 04:43:39 +00:00
2018-09-10 12:49:17 +00:00
// XPlatform > #DqnCatalog
2018-07-23 14:05:43 +00:00
// =================================================================================================
2018-07-28 07:40:57 +00:00
using DqnCatalogPath = DqnFixedString1024 ;
template < typename T > using DqnCatalogLoadProc = bool ( * ) ( DqnCatalogPath const & file , T * data ) ;
# define DQN_CATALOG_LOAD_PROC(name, type) bool name(DqnCatalogPath const &file, type *data)
2018-07-24 15:33:31 +00:00
# define DQN_CATALOG_TEMPLATE template <typename T, DqnCatalogLoadProc<T> LoadAsset>
# define DQN_CATALOG_DECL DqnCatalog<T, LoadAsset>
2018-07-28 07:40:57 +00:00
#if 0
struct RawBuf { char * buffer ; int len ; } ;
DQN_CATALOG_LOAD_PROC ( CatalogRawLoad , RawBuf )
{
2018-08-05 07:30:54 +00:00
size_t buf_size ;
uint8_t * buf = DqnFile_ReadAll ( file . str , & buf_size ) ;
2018-07-28 07:40:57 +00:00
if ( ! buf ) return false ;
data - > buffer = reinterpret_cast < char * > ( buf ) ;
2018-08-05 07:30:54 +00:00
data - > len = static_cast < int > ( buf_size ) ;
2018-07-28 07:40:57 +00:00
return true ;
}
int main ( int , char )
{
DqnCatalog < RawBuf , CatalogRawLoad > catalog = { } ;
RawBuf * file = catalog . GetIfUpdated ( " path/to/file/ " ) ;
if ( file ) { ( void ) file ; // do work on file }
else { // file not updated since last query }
while ( true ) // Or event loop, poll the assets in the catalog
{
catalog . PollAssets ( ) ;
}
catalog . Free ( ) ;
}
# endif
2018-07-24 15:33:31 +00:00
DQN_CATALOG_TEMPLATE struct DqnCatalog
2018-07-23 14:05:43 +00:00
{
struct Entry
{
2018-07-24 13:05:08 +00:00
T data ;
2018-08-05 07:30:54 +00:00
u64 last_write_time_in_s ;
2018-07-24 15:33:31 +00:00
bool updated ;
2018-07-23 14:05:43 +00:00
} ;
2018-08-05 07:30:54 +00:00
DqnVHashTable < DqnCatalogPath , Entry > asset_table ;
2018-07-24 15:33:31 +00:00
2018-07-28 07:40:57 +00:00
// Adds the file to the catalog if it has not been added yet.
// return: Asset if an update has been detected and not consumed yet otherwise nullptr. Update is consumed after called.
T * GetIfUpdated ( DqnCatalogPath const & file ) ;
2018-08-05 07:30:54 +00:00
Entry * GetEntry ( DqnCatalogPath const & file ) { Entry * entry = asset_table . Get ( file ) ; return entry ; }
T * Get ( DqnCatalogPath const & file ) { Entry * entry = asset_table . Get ( file ) ; return ( entry ) ? & entry - > data : nullptr ; }
void Erase ( DqnCatalogPath const & file ) { asset_table . Erase ( file ) ; } ;
2018-07-24 15:33:31 +00:00
2018-07-28 07:40:57 +00:00
// return: Iterate all loaded assets for updates, true if atleast 1 asset was updated.
bool PollAssets ( ) ;
2018-08-05 07:30:54 +00:00
void Free ( ) { asset_table . Free ( ) ; }
2018-07-28 07:40:57 +00:00
// NOTE: Unlikely you will need to use. Prefer GetIfUpdated.
// Manually invoke an update on the entry by querying its last write time on disk and updating accordingly.
bool QueryAndUpdateAsset ( DqnCatalogPath const & file , Entry * entry ) ;
2018-07-23 14:05:43 +00:00
} ;
2018-07-28 07:40:57 +00:00
DQN_CATALOG_TEMPLATE bool DQN_CATALOG_DECL : : QueryAndUpdateAsset ( DqnCatalogPath const & file , Entry * entry )
2018-07-24 15:33:31 +00:00
{
2018-07-28 07:40:57 +00:00
DqnFileInfo info = { } ;
if ( ! DqnFile_GetInfo ( file . str , & info ) )
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_W ( dqn_lib_context_ . logger , " Catalog could not get file info for: %s \n " , file . str ) ;
2018-07-28 07:40:57 +00:00
return false ;
}
2018-07-24 15:33:31 +00:00
2018-08-05 07:30:54 +00:00
if ( entry - > last_write_time_in_s = = info . last_write_time_in_s )
2018-07-28 07:40:57 +00:00
return true ;
T newData = { } ;
if ( LoadAsset ( file , & newData ) )
2018-07-24 15:33:31 +00:00
{
2018-08-05 07:30:54 +00:00
entry - > last_write_time_in_s = info . last_write_time_in_s ;
2018-07-28 07:40:57 +00:00
entry - > data = newData ;
entry - > updated = true ;
}
else
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_W ( dqn_lib_context_ . logger , " Catalog could not load file: %s \n " , file . str ) ;
2018-07-28 07:40:57 +00:00
return false ;
2018-07-24 15:33:31 +00:00
}
2018-07-28 07:40:57 +00:00
return true ;
2018-07-24 15:33:31 +00:00
}
2018-07-28 07:40:57 +00:00
DQN_CATALOG_TEMPLATE T * DQN_CATALOG_DECL : : GetIfUpdated ( DqnCatalogPath const & file )
2018-07-24 15:33:31 +00:00
{
2018-08-05 07:30:54 +00:00
Entry * entry = this - > asset_table . GetOrMake ( file ) ;
2018-07-28 07:40:57 +00:00
if ( QueryAndUpdateAsset ( file , entry ) )
2018-07-24 15:33:31 +00:00
{
if ( entry - > updated ) entry - > updated = false ;
else entry = nullptr ;
}
2018-07-28 07:40:57 +00:00
else
{
entry = nullptr ;
}
2018-07-24 15:33:31 +00:00
2018-07-28 07:40:57 +00:00
return & entry - > data ;
2018-07-24 15:33:31 +00:00
}
2018-07-28 07:40:57 +00:00
DQN_CATALOG_TEMPLATE bool DQN_CATALOG_DECL : : PollAssets ( )
2018-07-24 15:33:31 +00:00
{
2018-07-28 07:40:57 +00:00
bool result = false ;
2018-09-02 11:17:14 +00:00
for ( auto it = this - > asset_table . begin ( ) ; it ! = this - > asset_table . end ( ) ; + + it )
2018-07-24 15:33:31 +00:00
{
2018-07-28 07:40:57 +00:00
DqnCatalogPath const * file = & it . entry - > key ;
Entry * entry = & it . entry - > item ;
result | = QueryAndUpdateAsset ( * file , entry ) ;
2018-07-24 15:33:31 +00:00
}
2018-07-28 07:40:57 +00:00
return result ;
2018-07-24 15:33:31 +00:00
}
2017-04-10 08:18:23 +00:00
2018-09-10 12:49:17 +00:00
// XPlatform > #DqnTimer
2018-01-18 09:25:44 +00:00
// =================================================================================================
2017-06-22 08:10:44 +00:00
DQN_FILE_SCOPE f64 DqnTimer_NowInMs ( ) ;
DQN_FILE_SCOPE f64 DqnTimer_NowInS ( ) ;
2017-06-22 04:00:11 +00:00
2018-09-10 12:49:17 +00:00
// XPlatform > #DqnLock
2018-01-18 09:25:44 +00:00
// =================================================================================================
2018-07-15 10:09:55 +00:00
struct DqnLock
2017-06-20 09:19:52 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
CRITICAL_SECTION win32_handle ;
2017-07-03 05:17:09 +00:00
# else
2018-08-05 07:30:54 +00:00
pthread_mutex_t unix_handle ;
2017-06-25 15:57:51 +00:00
# endif
2018-05-17 11:19:58 +00:00
// Win32 only, when trying to acquire a locked lock, it is the number of spins permitted
// spinlocking on the lock before being blocked. Set before init if you want a different value.
2018-08-05 07:30:54 +00:00
u32 win32_spin_count = 16000 ;
2017-06-25 15:57:51 +00:00
2018-05-17 11:19:58 +00:00
bool Init ( ) ;
void Acquire ( ) ;
void Release ( ) ;
void Delete ( ) ;
2017-06-25 15:57:51 +00:00
2018-07-15 10:09:55 +00:00
struct Guard_
{
Guard_ ( DqnLock * lock_ ) : lock ( lock_ ) { lock - > Acquire ( ) ; }
~ Guard_ ( ) { lock - > Release ( ) ; }
2017-06-20 09:19:52 +00:00
2018-07-15 10:09:55 +00:00
private :
DqnLock * lock ;
} ;
2017-06-24 04:59:48 +00:00
2018-07-15 10:09:55 +00:00
// Create a lock guard on the lock this is invoked on.
Guard_ Guard ( ) { return Guard_ ( this ) ; }
2017-06-24 04:59:48 +00:00
} ;
2018-09-10 12:49:17 +00:00
// XPlatform > #DqnJobQueue
2018-01-18 09:25:44 +00:00
// =================================================================================================
2017-06-22 04:00:11 +00:00
// DqnJobQueue is a platform abstracted "lockless" multithreaded work queue. It will create threads
2018-08-05 07:30:54 +00:00
// and assign threads to complete the job via the job "callback" using the "user_data" supplied.
2017-06-20 09:19:52 +00:00
// Usage
2017-06-22 04:00:11 +00:00
// 1. Prepare your callback function for threads to execute following the 'DqnJob_Callback' function
// signature.
// 2. Create a job queue with DqnJobQueue_InitWithMem()
// 3. Add jobs with DqnJobQueue_AddJob() and threads will be dispatched automatically.
// When all jobs are sent you can also utilise the main executing thread to complete jobs whilst you
// wait for all jobs to complete using DqnJobQueue_TryExecuteNextJob() or spinlock on
// DqnJobQueue_AllJobsComplete(). Alternatively you can combine both for the main thread to help
// complete work and not move on until all tasks are complete.
2017-06-23 13:12:20 +00:00
2018-08-05 07:30:54 +00:00
typedef void DqnJob_Callback ( struct DqnJobQueue * const queue , void * const user_data ) ;
2018-01-18 09:25:44 +00:00
struct DqnJob
2017-06-20 09:19:52 +00:00
{
2018-05-17 11:19:58 +00:00
DqnJob_Callback * callback ;
2018-08-05 07:30:54 +00:00
void * user_data ;
2018-01-18 09:25:44 +00:00
} ;
2017-06-20 09:19:52 +00:00
2018-01-18 09:25:44 +00:00
struct DqnJobQueue
2017-06-23 13:12:20 +00:00
{
2018-05-17 11:19:58 +00:00
// JobList Circular Array, is setup in Init()
2018-08-05 07:30:54 +00:00
DqnJob * job_list ;
2018-05-17 11:19:58 +00:00
u32 size ;
2017-06-23 13:12:20 +00:00
2018-05-17 11:19:58 +00:00
// NOTE(doyle): Modified by main+worker threads
i32 volatile jobToExecuteIndex ;
2018-08-05 07:30:54 +00:00
i32 volatile num_jobs_queued ;
2017-06-25 15:57:51 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
void * semaphore ;
2017-06-25 15:57:51 +00:00
# else
2018-07-04 14:47:11 +00:00
sem_t semaphore ;
2017-06-25 15:57:51 +00:00
# endif
2018-05-17 11:19:58 +00:00
// NOTE: Modified by main thread ONLY
i32 volatile jobInsertIndex ;
2017-06-23 13:12:20 +00:00
2018-08-05 07:30:54 +00:00
bool Init ( DqnJob * const job_list_ , const u32 job_list_size , const u32 num_threads ) ;
2018-05-17 11:19:58 +00:00
bool AddJob ( const DqnJob job ) ;
2017-06-24 04:59:48 +00:00
void BlockAndCompleteAllJobs ( ) ;
2018-05-17 11:19:58 +00:00
bool TryExecuteNextJob ( ) ;
bool AllJobsComplete ( ) ;
2018-01-18 09:25:44 +00:00
} ;
2017-06-23 13:12:20 +00:00
2017-06-24 04:59:48 +00:00
// TODO(doyle): Queue delete, thread delete
2017-06-23 13:12:20 +00:00
// queue: Pass a pointer to a zero cleared DqnJobQueue struct
2018-08-05 07:30:54 +00:00
// job_list: Pass in a pointer to an array of DqnJob's
// job_list_size: The number of elements in the job_list array
// num_threads: The number of threads the queue should request from the OS for working on the queue
// return: FALSE if invalid args i.e. nullptr ptrs or job_list_size & num_threads == 0
DQN_FILE_SCOPE bool DqnJobQueue_Init ( DqnJobQueue * const queue , const DqnJob * const job_list ,
const u32 job_list_size , const u32 num_threads ) ;
2017-06-20 09:19:52 +00:00
2017-06-22 04:00:11 +00:00
// return: FALSE if the job is not able to be added, this occurs if the queue is full.
2017-06-20 09:19:52 +00:00
DQN_FILE_SCOPE bool DqnJobQueue_AddJob ( DqnJobQueue * const queue , const DqnJob job ) ;
2017-06-24 04:59:48 +00:00
// Helper function that combines TryExecuteNextJob() and AllJobsComplete(), i.e.
2017-10-29 01:45:00 +00:00
// complete all work before moving on. Does nothing if queue is nullptr.
2017-06-24 04:59:48 +00:00
DQN_FILE_SCOPE void DqnJobQueue_BlockAndCompleteAllJobs ( DqnJobQueue * const queue ) ;
2017-06-22 04:00:11 +00:00
// return: TRUE if there was a job to execute (the calling thread executes it). FALSE if it could
// not get a job. It may return FALSE whilst there are still jobs, this means that another thread
// has taken the job before the calling thread could and should NOT be used to determine if there
// are any remaining jobs left. That can only be definitively known using
// DqnJobQueue_AllJobsComplete(). This is typically combined like so ..
2017-06-24 04:59:48 +00:00
// while (DqnJobQueue_TryExecuteNextJob(queue) || !DqnJobQueue_AllJobsComplete(queue));
2017-10-29 01:45:00 +00:00
// Return FALSE also if queue is a nullptr pointer.
2017-06-20 09:19:52 +00:00
DQN_FILE_SCOPE bool DqnJobQueue_TryExecuteNextJob ( DqnJobQueue * const queue ) ;
DQN_FILE_SCOPE bool DqnJobQueue_AllJobsComplete ( DqnJobQueue * const queue ) ;
2017-06-22 04:43:53 +00:00
2018-09-10 12:49:17 +00:00
// XPlatform > #DqnAtomic
2018-01-18 09:25:44 +00:00
// =================================================================================================
2017-06-25 15:57:51 +00:00
// All atomic operations generate a full read/write barrier. This is implicitly enforced by the
// OS calls, not explicitly in my code.
2018-08-05 07:30:54 +00:00
// swap_val: The value to put into "dest", IF at point of read, "dest" has the value of "compare_val"
// compare_val: The value to check in "dest"
2017-06-25 15:57:51 +00:00
// return: Return the original value that was in "dest"
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnAtomic_CompareSwap32 ( i32 volatile * const dest , const i32 swap_val , const i32 compare_val ) ;
2017-06-25 15:57:51 +00:00
2017-06-26 02:37:52 +00:00
// Add "value" to src
// return: The new value at src
DQN_FILE_SCOPE i32 DqnAtomic_Add32 ( i32 volatile * const src , const i32 value ) ;
2017-06-25 15:57:51 +00:00
2018-09-10 12:49:17 +00:00
// #Platform Specific
2018-01-18 09:25:44 +00:00
// =================================================================================================
2017-06-25 15:57:51 +00:00
// Functions here are only available for the #defined sections (i.e. all functions in
2018-07-09 14:53:54 +00:00
// DQN_IS_WIN32 only have a valid implementation in Win32.
2017-06-25 15:57:51 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-09-10 12:49:17 +00:00
// Platform > #DqnWin32
2018-01-18 09:25:44 +00:00
// =================================================================================================
2018-06-13 13:16:05 +00:00
# define DQN__WIN32_ERROR_BOX(text, title) MessageBoxA(nullptr, text, title, MB_OK);
2017-04-18 13:38:52 +00:00
2017-10-28 08:58:09 +00:00
// The function automatically null-terminates the output string.
2017-06-22 04:00:11 +00:00
// out: A pointer to the buffer to receive the characters.
2018-08-05 07:30:54 +00:00
// out_len: The length/capacity of the buffer "out". If 0, the function returns the required length including null terminator.
// return: -1 if invalid, or if out_len is 0 the required buffer length.
DQN_FILE_SCOPE i32 DqnWin32_UTF8ToWChar ( const char * const in , wchar_t * const out , const i32 out_len ) ;
DQN_FILE_SCOPE i32 DqnWin32_WCharToUTF8 ( const wchar_t * const in , char * const out , const i32 out_len ) ;
2017-06-22 04:00:11 +00:00
// "width" and "height" are optional and won't be used if not given by user.
// width & height: Pass in a pointer for function to fill out.
2018-05-18 13:19:50 +00:00
DQN_FILE_SCOPE void DqnWin32_GetClientDim ( HWND const window , LONG * width , LONG * height ) ;
DQN_FILE_SCOPE void DqnWin32_GetRectDim ( RECT const rect , LONG * width , LONG * height ) ;
2017-06-22 04:00:11 +00:00
2018-08-18 06:08:29 +00:00
DQN_FILE_SCOPE char const * DqnWin32_GetLastError ( ) ;
// TODO(doyle): #deprecate #delete Display Last Error is mostly useless.
2018-08-05 07:30:54 +00:00
// Displays error in the format <err_prefix>: <Win32 Error Message> in a Win32 Dialog Box.
// err_prefix: The message before the Win32 error, can be nullptr
DQN_FILE_SCOPE void DqnWin32_DisplayLastError ( const char * const err_prefix ) ;
2017-06-22 04:00:11 +00:00
// Asimilar to DqnWin32_DisplayLastError() a particular error can be specified in a Win32 Dialog Box.
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void DqnWin32_DisplayErrorCode ( const DWORD error , const char * const err_prefix ) ;
2017-06-22 04:00:11 +00:00
// Output text to the debug console. For visual studio this is the output window and not the console.
// ...: Variable args alike printf, powered by stb_sprintf
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void DqnWin32_OutputDebugString ( const char * const fmt_str , . . . ) ;
2017-04-17 12:48:20 +00:00
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE void DqnWin32_GetExeNameAndDirectory ( DqnMemStack * allocator , DqnBuffer < wchar_t > * exe_name , DqnBuffer < wchar_t > * exe_directory ) ;
DQN_FILE_SCOPE u64 DqnWin32_EpochTimeUTC ( ) ;
DQN_FILE_SCOPE u64 DqnWin32_EpochTimeLocal ( ) ;
2018-07-09 14:53:54 +00:00
# endif // DQN_IS_WIN32
2017-07-03 05:17:09 +00:00
# endif // DQN_PLATFORM_H
# endif // DQN_PLATFORM_HEADER
2017-06-20 09:19:52 +00:00
2017-06-22 04:00:11 +00:00
2018-06-11 06:26:57 +00:00
# ifdef DQN_IMPLEMENTATION
2018-09-10 12:49:17 +00:00
// DQN_IMPLEMENTATION
// =================================================================================================
# include <math.h> // TODO(doyle): For trigonometry functions (for now)
# include <stdlib.h> // For calloc, malloc, free
# include <stdio.h> // For printf
2017-04-14 15:27:11 +00:00
2018-09-13 13:58:48 +00:00
DqnAllocator xallocator_ ;
DqnAllocator allocator_ ;
DqnLogger logger_ ;
DqnLibContext dqn_lib_context_ = { & xallocator_ , & allocator_ , & logger_ } ;
2017-04-14 15:27:11 +00:00
2018-09-10 12:49:17 +00:00
// NOTE: STB_SPRINTF is included when DQN_IMPLEMENTATION defined
// #define STB_SPRINTF_IMPLEMENTATION
2017-04-14 15:27:11 +00:00
2018-09-10 12:49:17 +00:00
// NOTE: DQN_INI_IMPLEMENTATION modified to be included when DQN_IMPLEMENTATION defined
// #define DQN_INI_IMPLEMENTATION
# define DQN_INI_STRLEN(s) DqnStr_Len(s)
2017-04-14 15:27:11 +00:00
2018-06-11 06:26:57 +00:00
// #DqnMemory
// =================================================================================================
// NOTE: All memory allocations in dqn.h go through these functions. So they can
// be rerouted fairly easily especially for platform specific mallocs.
DQN_FILE_SCOPE void * DqnMem_Alloc ( usize size )
{
void * result = malloc ( size ) ;
return result ;
}
2017-04-14 15:27:11 +00:00
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE void * DqnMem_XAlloc ( usize size )
{
void * result = malloc ( size ) ;
DQN_ALWAYS_ASSERT ( result ) ;
return result ;
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE void * DqnMem_Calloc ( usize size )
{
void * result = calloc ( 1 , size ) ;
return result ;
}
2017-04-14 15:27:11 +00:00
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE void * DqnMem_XCalloc ( usize size )
{
void * result = calloc ( 1 , size ) ;
DQN_ALWAYS_ASSERT ( result ) ;
return result ;
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void DqnMem_Clear ( void * memory , u8 clear_val , usize size )
2018-06-11 06:26:57 +00:00
{
if ( memory )
{
2018-08-05 07:30:54 +00:00
DqnMem_Set ( memory , clear_val , size ) ;
2018-06-11 06:26:57 +00:00
}
}
2017-04-14 15:27:11 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void * DqnMem_Realloc ( void * memory , usize new_size )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
void * result = realloc ( memory , new_size ) ;
2018-06-11 06:26:57 +00:00
return result ;
}
2017-04-14 15:27:11 +00:00
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE void * DqnMem_XRealloc ( void * memory , usize new_size )
{
void * result = realloc ( memory , new_size ) ;
DQN_ALWAYS_ASSERT ( result ) ;
return result ;
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE void DqnMem_Free ( void * memory )
{
if ( memory ) free ( memory ) ;
}
2017-04-14 15:27:11 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void DqnMem_Copy ( void * dest , void const * src , usize num_bytes_to_copy )
2018-06-11 06:26:57 +00:00
{
auto * to = ( u8 * ) dest ;
auto * from = ( u8 * ) src ;
2018-08-05 07:30:54 +00:00
for ( usize i = 0 ; i < num_bytes_to_copy ; i + + )
2018-06-11 06:26:57 +00:00
to [ i ] = from [ i ] ;
}
2017-04-14 15:27:11 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void * DqnMem_Set ( void * dest , u8 value , usize num_bytes_to_set )
2018-06-11 06:26:57 +00:00
{
auto volatile * ptr = ( u8 * ) dest ; // NOTE: Volatile so memset is not optimised out.
2018-08-05 07:30:54 +00:00
for ( usize i = 0 ; i < num_bytes_to_set ; i + + )
2018-06-11 06:26:57 +00:00
ptr [ i ] = value ;
2017-04-14 15:27:11 +00:00
2018-06-11 06:26:57 +00:00
return dest ;
}
2017-04-14 15:27:11 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE int DqnMem_Cmp ( void const * src , void const * dest , usize num_bytes )
2018-07-14 13:00:25 +00:00
{
2018-08-05 07:30:54 +00:00
auto const * src_ptr = static_cast < char const * > ( src ) ;
auto const * dest_ptr = static_cast < char const * > ( dest ) ;
2018-07-14 13:00:25 +00:00
usize i ;
2018-08-05 07:30:54 +00:00
for ( i = 0 ; i < num_bytes ; + + i )
2018-07-14 13:00:25 +00:00
{
2018-08-05 07:30:54 +00:00
if ( src_ptr [ i ] ! = dest_ptr [ i ] )
2018-07-14 13:00:25 +00:00
break ;
}
2018-08-05 07:30:54 +00:00
i = DQN_MIN ( i , ( num_bytes - 1 ) ) ;
return ( src_ptr [ i ] - dest_ptr [ i ] ) ;
2018-07-14 13:00:25 +00:00
}
2018-07-17 14:47:13 +00:00
// #DqnMemTracker
2018-06-11 06:26:57 +00:00
// =================================================================================================
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE void * DqnAllocator : : Malloc ( size_t size , Dqn : : ZeroMem zero )
2018-06-11 06:26:57 +00:00
{
2018-09-10 12:49:17 +00:00
void * result = nullptr ;
switch ( this - > type )
2018-06-11 06:26:57 +00:00
{
2018-09-13 13:58:48 +00:00
case Type : : Default : ( zero = = Dqn : : ZeroMem : : Yes ) ? result = DqnMem_Alloc ( size ) : result = DqnMem_Calloc ( size ) ; break ;
2018-09-10 12:49:17 +00:00
case Type : : XAllocator :
{
( zero = = Dqn : : ZeroMem : : Yes ) ? result = DqnMem_Alloc ( size ) : result = DqnMem_Calloc ( size ) ;
DQN_ASSERT ( result ) ;
}
break ;
case Type : : VirtualMemory :
{
# if defined(DQN_PLATFORM_HEADER)
result = DqnOS_VAlloc ( size ) ;
# else
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( DQN_INVALID_CODE_PATH ,
2018-09-10 12:49:17 +00:00
" Dqn library hasn't been built with the platform header. I don't know how "
" to allocate virtual memory! " ) ;
# endif
}
break ;
case Type : : DqnMemStack :
{
auto * mem_stack = reinterpret_cast < DqnMemStack * > ( user_context ) ;
result = DQN_MEMSTACK_PUSH ( mem_stack , size ) ;
if ( zero = = Dqn : : ZeroMem : : Yes ) DqnMem_Clear ( result , 0 , size ) ;
}
break ;
2018-12-15 14:58:11 +00:00
default : DQN_ASSERT_MSG ( DQN_INVALID_CODE_PATH , " New context type not handled. " ) ; break ;
2018-06-11 06:26:57 +00:00
}
2018-09-10 12:49:17 +00:00
return result ;
}
DQN_FILE_SCOPE void * DqnAllocator : : Realloc ( void * ptr , size_t new_size )
{
void * result = nullptr ;
switch ( this - > type )
{
2018-09-13 13:58:48 +00:00
case Type : : Default : result = DqnMem_Realloc ( ptr , new_size ) ; break ;
2018-09-10 12:49:17 +00:00
case Type : : XAllocator :
{
result = DqnMem_Realloc ( ptr , new_size ) ;
DQN_ASSERT ( result ) ;
}
break ;
2018-12-15 14:58:11 +00:00
case Type : : VirtualMemory : DQN_ASSERT_MSG ( DQN_INVALID_CODE_PATH , " Realloc is disallowed on virtual memory! Reserve a bigger range! " ) ; break ;
2018-09-10 12:49:17 +00:00
case Type : : DqnMemStack :
{
auto * mem_stack = reinterpret_cast < DqnMemStack * > ( user_context ) ;
DqnPtrHeader * ptr_header = mem_stack - > tracker . PtrToHeader ( static_cast < char * > ( ptr ) ) ;
result = DQN_MEMSTACK_PUSH ( mem_stack , new_size ) ;
DqnMem_Copy ( result , ptr , ptr_header - > alloc_amount ) ;
2018-09-13 13:58:48 +00:00
DQN_LOGGER_W ( dqn_lib_context_ . logger , " Memory stack used realloc and ptr: %p with: %zu bytes has been lost " , ptr , ptr_header - > alloc_amount ) ;
2018-09-10 12:49:17 +00:00
}
break ;
2018-12-15 14:58:11 +00:00
default : DQN_ASSERT_MSG ( DQN_INVALID_CODE_PATH , " New context type not handled. " ) ; break ;
2018-09-10 12:49:17 +00:00
}
return result ;
}
DQN_FILE_SCOPE void DqnAllocator : : Free ( void * ptr , size_t old_size )
{
( void ) old_size ;
switch ( this - > type )
{
case Type : : XAllocator :
2018-09-13 13:58:48 +00:00
case Type : : Default : DqnMem_Free ( ptr ) ; break ;
2018-09-10 12:49:17 +00:00
case Type : : VirtualMemory :
{
# if defined(DQN_PLATFORM_HEADER)
DqnOS_VFree ( ptr , old_size ) ;
# else
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( DQN_INVALID_CODE_PATH ,
2018-09-10 12:49:17 +00:00
" Dqn library hasn't been built with the platform header. I don't know how to free virtual memory! " ) ;
# endif
}
break ;
case Type : : DqnMemStack : /*do nothing*/ break ;
2018-12-15 14:58:11 +00:00
default : DQN_ASSERT_MSG ( DQN_INVALID_CODE_PATH , " New context type not handled. " ) ; break ;
2018-09-10 12:49:17 +00:00
}
}
// #DqnMemTracker
// =================================================================================================
// TODO(doyle): We shouldn't be using the library context for allocations since this is per
// allocator or actually maybe yes? I think we want more granularity then that
void DqnMemTracker : : Init ( DqnMemTracker : : Flag flag )
{
* this = { } ;
this - > bounds_guard_size = ( flag & DqnMemTracker : : BoundsGuard ) ? sizeof ( HEAD_GUARD_VALUE ) : 0 ;
if ( flag & DqnMemTracker : : TrackPtr )
{
this - > ptrs_max = 8192 ;
2018-09-13 13:58:48 +00:00
this - > ptrs = ( void * * ) dqn_lib_context_ . allocator - > Malloc ( this - > ptrs_max * sizeof ( this - > ptrs ) ) ;
2018-09-10 12:49:17 +00:00
}
if ( flag & DqnMemTracker : : TagAllocation )
{
this - > tagged_allocs_max = 4096 ;
2018-09-13 13:58:48 +00:00
this - > tagged_allocs = ( decltype ( this - > tagged_allocs ) ) dqn_lib_context_ . allocator - > Malloc ( this - > tagged_allocs_max * sizeof ( * this - > tagged_allocs ) ) ;
this - > tagged_allocs_used_list = ( decltype ( this - > tagged_allocs_used_list ) ) dqn_lib_context_ . allocator - > Malloc ( this - > tagged_allocs_max * sizeof ( * this - > tagged_allocs_used_list ) ) ;
2018-09-10 12:49:17 +00:00
}
}
void DqnMemTracker : : Free ( )
{
if ( this - > IsTaggingAllocations ( ) )
{
DQN_FOR_EACH ( i , this - > tagged_allocs_used_index )
{
u16 used_index = this - > tagged_allocs_used_list [ i ] ;
for ( TaggedAllocation * tagged_alloc = this - > tagged_allocs [ used_index ] ; tagged_alloc ; )
{
TaggedAllocation * tag_to_free = tagged_alloc ;
tagged_alloc = tagged_alloc - > next ;
2018-09-13 13:58:48 +00:00
dqn_lib_context_ . allocator - > Free ( tag_to_free - > filename . str , tag_to_free - > filename . len ) ;
dqn_lib_context_ . allocator - > Free ( tag_to_free - > function . str , tag_to_free - > function . len ) ;
dqn_lib_context_ . allocator - > Free ( tag_to_free , sizeof ( tag_to_free ) ) ;
2018-09-10 12:49:17 +00:00
}
}
}
if ( this - > IsTrackingPtrs ( ) )
2018-09-13 13:58:48 +00:00
dqn_lib_context_ . allocator - > Free ( this - > ptrs , sizeof ( * this - > ptrs ) * ptrs_max ) ;
2018-09-10 12:49:17 +00:00
}
void DqnMemTracker : : Tag_ ( DqnBuffer < const char > filename , DqnBuffer < const char > function , int line_num , DqnBuffer < const char > filename_line_num_data , isize bytes )
{
if ( ! Dqn : : allow_allocation_tagging | | ! this - > IsTaggingAllocations ( ) )
return ;
u16 index = DqnHash_Murmur32 ( filename_line_num_data . data , filename_line_num_data . len ) % this - > tagged_allocs_max ;
TaggedAllocation * * entry = this - > tagged_allocs + index ;
for ( ; * entry & & ( * entry ) - > filename . len > 0 ; entry = & ( ( * entry ) - > next ) )
{
if ( line_num = = ( * entry ) - > line_num & & DQN_BUFFER_STRCMP ( filename , ( * entry ) - > filename , Dqn : : IgnoreCase : : Yes ) )
break ;
}
if ( ! ( * entry ) )
2018-09-13 13:58:48 +00:00
* entry = ( TaggedAllocation * ) dqn_lib_context_ . allocator - > Malloc ( sizeof ( * * entry ) , Dqn : : ZeroMem : : Yes ) ;
2018-09-10 12:49:17 +00:00
if ( ( * entry ) - > filename . len = = 0 )
{
this - > tagged_allocs_used_list [ this - > tagged_allocs_used_index + + ] = index ;
2018-09-13 13:58:48 +00:00
( * entry ) - > filename = DqnBuffer_CopyAndNullTerminate ( dqn_lib_context_ . allocator , filename . str , filename . len ) ;
( * entry ) - > function = DqnBuffer_CopyAndNullTerminate ( dqn_lib_context_ . allocator , function . str , function . len ) ;
2018-09-10 12:49:17 +00:00
( * entry ) - > line_num = line_num ;
}
( * entry ) - > bytes_allocated + = bytes ;
DQN_ALWAYS_ASSERT ( ( * entry ) - > bytes_allocated > = 0 ) ;
DqnLogger : : Context context = { } ;
context . filename = ( * entry ) - > filename . str ;
context . filename_len = ( * entry ) - > filename . len ;
context . function = ( * entry ) - > function . str ;
context . function_len = ( * entry ) - > function . len ;
context . line_num = line_num ;
2018-09-13 13:58:48 +00:00
dqn_lib_context_ . logger - > Log ( DqnLogger : : Type : : Memory , context , " Currently allocated: %zu bytes " , ( * entry ) - > bytes_allocated ) ;
2018-09-10 12:49:17 +00:00
}
void * DqnMemTracker : : SetupPtr ( void * ptr , isize size , u8 alignment )
{
// Calculate the aligned ptr
auto * byte_ptr = static_cast < char * > ( ptr ) ;
auto * unaligned_result = static_cast < char * > ( ptr ) + sizeof ( DqnPtrHeader ) + this - > bounds_guard_size ;
auto * aligned_result = reinterpret_cast < char * > ( DQN_ALIGN_POW_N ( unaligned_result , alignment ) ) ;
isize const offset_to_ptr_header = aligned_result - unaligned_result ;
DQN_ASSERT ( offset_to_ptr_header > = 0 & & offset_to_ptr_header < = ( alignment - 1 ) ) ;
char * check_allignment = reinterpret_cast < char * > ( DQN_ALIGN_POW_N ( aligned_result , alignment ) ) ;
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( check_allignment = = aligned_result , " Adding bounds guard should not destroy alignment! %p != %p " , aligned_result , check_allignment ) ;
2018-09-10 12:49:17 +00:00
// Instrument allocation with guards and tracker
{
auto * ptr_header = reinterpret_cast < DqnPtrHeader * > ( byte_ptr + offset_to_ptr_header ) ;
ptr_header - > offset_to_src_ptr = static_cast < u8 > ( aligned_result - byte_ptr ) ;
ptr_header - > alignment = alignment ;
ptr_header - > alloc_amount = size ;
}
if ( this - > IsGuardingBounds ( ) )
{
u32 * head_guard = reinterpret_cast < u32 * > ( aligned_result - sizeof ( DqnMemTracker : : HEAD_GUARD_VALUE ) ) ;
u32 * tail_guard = reinterpret_cast < u32 * > ( aligned_result + size ) ;
* head_guard = DqnMemTracker : : HEAD_GUARD_VALUE ;
* tail_guard = DqnMemTracker : : TAIL_GUARD_VALUE ;
}
if ( this - > IsTrackingPtrs ( ) )
2018-06-11 06:26:57 +00:00
{
2018-09-10 12:49:17 +00:00
if ( this - > ptrs_len + 1 > this - > ptrs_max )
{
this - > ptrs_max * = 2 ;
this - > ptrs = ( void * * ) DqnMem_XRealloc ( this - > ptrs , sizeof ( this - > ptrs ) * this - > ptrs_max ) ;
}
this - > ptrs [ this - > ptrs_len + + ] = aligned_result ;
this - > CheckPtrs ( ) ;
2018-06-11 06:26:57 +00:00
}
2018-09-10 12:49:17 +00:00
return aligned_result ;
2018-06-11 06:26:57 +00:00
}
2017-04-14 15:27:11 +00:00
2018-09-10 12:49:17 +00:00
void DqnMemTracker : : RemovePtr ( void * ptr )
2018-06-11 06:26:57 +00:00
{
2018-09-10 12:49:17 +00:00
if ( ! this - > IsTrackingPtrs ( ) )
return ;
DQN_ASSERT ( this - > ptrs_len > 0 ) ;
b32 found = false ;
DQN_FOR_EACH ( i , this - > ptrs_len )
2018-06-11 06:26:57 +00:00
{
2018-09-10 12:49:17 +00:00
if ( this - > ptrs [ i ] = = ptr )
2018-06-11 06:26:57 +00:00
{
2018-09-10 12:49:17 +00:00
this - > ptrs [ i ] = this - > ptrs [ - - this - > ptrs_len ] ;
found = true ;
2018-06-11 06:26:57 +00:00
break ;
}
}
2017-04-14 15:27:11 +00:00
2018-09-10 12:49:17 +00:00
DQN_ALWAYS_ASSERTM ( found , " Ptr %p was not in the tracked pointers list " , ptr ) ;
}
void DqnMemTracker : : RemovePtrRange ( void * start , void * end )
{
if ( ! this - > IsTrackingPtrs ( ) )
return ;
if ( start > = end ) return ;
DQN_FOR_EACH ( i , this - > ptrs_len )
{
void * ptr = this - > ptrs [ i ] ;
if ( ptr > = start & & ptr < end )
this - > ptrs [ i - - ] = this - > ptrs [ - - this - > ptrs_len ] ;
}
2018-06-11 06:26:57 +00:00
}
2017-04-14 15:27:11 +00:00
2018-09-10 12:49:17 +00:00
void DqnMemTracker : : CheckPtrs ( ) const
2018-06-11 06:26:57 +00:00
{
2018-09-10 12:49:17 +00:00
if ( ! this - > IsGuardingBounds ( ) )
return ;
DQN_FOR_EACH ( i , this - > ptrs_len )
2018-06-11 06:26:57 +00:00
{
2018-09-10 12:49:17 +00:00
char * ptr = static_cast < char * > ( this - > ptrs [ i ] ) ;
2018-08-05 07:30:54 +00:00
u32 const * head_guard = this - > PtrToHeadGuard ( ptr ) ;
u32 const * tail_guard = this - > PtrToTailGuard ( ptr ) ;
2017-04-14 15:27:11 +00:00
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( * head_guard = = HEAD_GUARD_VALUE ,
2018-06-11 06:26:57 +00:00
" Bounds guard has been destroyed at the head end of the allocation! Expected: "
" %x, received: %x " ,
2018-08-05 07:30:54 +00:00
HEAD_GUARD_VALUE , * head_guard ) ;
2017-04-14 15:27:11 +00:00
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( * tail_guard = = TAIL_GUARD_VALUE ,
2018-06-11 06:26:57 +00:00
" Bounds guard has been destroyed at the tail end of the allocation! Expected: "
" %x, received: %x " ,
2018-08-05 07:30:54 +00:00
TAIL_GUARD_VALUE , * tail_guard ) ;
2018-06-11 06:26:57 +00:00
}
}
2017-04-14 15:27:11 +00:00
2018-06-11 06:26:57 +00:00
// #DqnMemStack
// =================================================================================================
DQN_FILE_SCOPE DqnMemStack : : Block *
2018-09-10 12:49:17 +00:00
DqnMemStack__AllocateBlock ( isize size , Dqn : : ZeroMem zero , DqnAllocator * allocator )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
isize total_size = sizeof ( DqnMemStack : : Block ) + size ;
2018-09-10 12:49:17 +00:00
auto * result = static_cast < DqnMemStack : : Block * > ( allocator - > Malloc ( total_size , zero ) ) ;
2018-07-17 14:47:13 +00:00
DQN_ALWAYS_ASSERTM ( result , " Allocated memory block was null " ) ;
2017-04-14 15:27:11 +00:00
2018-08-05 07:30:54 +00:00
char * block_offset = reinterpret_cast < char * > ( result ) + sizeof ( * result ) ;
2018-08-27 14:25:08 +00:00
* result = DqnMemStack : : Block ( block_offset , size ) ;
2018-06-11 06:26:57 +00:00
return result ;
}
2017-10-28 08:58:09 +00:00
2018-09-10 12:49:17 +00:00
DqnMemStack : : DqnMemStack ( void * mem , isize size , Dqn : : ZeroMem clear , u32 flags_ , DqnMemTracker : : Flag flags )
2018-06-11 06:26:57 +00:00
{
2018-07-17 14:47:13 +00:00
DQN_ALWAYS_ASSERTM ( mem , " Supplied fixed memory buffer is nullptr, initialise with fixed memory failed " ) ;
2018-09-10 12:49:17 +00:00
DQN_ALWAYS_ASSERTM ( size > sizeof ( DqnMemStack : : Block ) , " [%zu < %zu] Buffer too small for block metadata " , size , sizeof ( DqnMemStack : : Block ) ) ;
2018-07-17 14:47:13 +00:00
* this = { } ;
2017-06-22 04:43:53 +00:00
2018-09-02 11:17:14 +00:00
if ( clear = = Dqn : : ZeroMem : : Yes )
2018-06-11 06:26:57 +00:00
DqnMem_Set ( mem , 0 , size ) ;
2017-05-04 16:37:38 +00:00
2018-08-05 07:30:54 +00:00
char * block_offset = static_cast < char * > ( mem ) + sizeof ( * this - > block ) ;
isize const block_size = size - sizeof ( * this - > block ) ;
2017-05-04 16:37:38 +00:00
2018-08-29 15:04:56 +00:00
this - > block = static_cast < DqnMemStack : : Block * > ( mem ) ;
* this - > block = Block ( block_offset , block_size ) ;
this - > flags = ( flags_ | Flag : : NonExpandable ) ;
2018-09-10 12:49:17 +00:00
this - > tracker . Init ( flags ) ;
2018-06-11 06:26:57 +00:00
}
2017-06-20 09:19:52 +00:00
2018-09-10 12:49:17 +00:00
void DqnMemStack : : LazyInit ( isize size , Dqn : : ZeroMem clear , u32 flags_ , DqnMemTracker : : Flag tracker_flags , DqnAllocator * block_allocator_ )
2017-06-20 09:19:52 +00:00
{
2018-07-17 14:47:13 +00:00
DQN_ALWAYS_ASSERTM ( size > 0 , " %zu <= 0 " , size ) ;
2018-08-29 15:04:56 +00:00
* this = { } ;
this - > block = DqnMemStack__AllocateBlock ( size , clear , block_allocator_ ) ;
this - > flags = flags_ ;
this - > block_allocator = block_allocator_ ;
2018-09-10 12:49:17 +00:00
this - > tracker . Init ( tracker_flags ) ;
2018-01-24 03:58:03 +00:00
}
2018-09-10 12:49:17 +00:00
void * DqnMemStack : : Push_ ( usize size , PushType push_type , u8 alignment )
2018-01-24 03:58:03 +00:00
{
2018-06-11 06:26:57 +00:00
DQN_ASSERT ( size > = 0 & & ( alignment % 2 = = 0 ) ) ;
2018-09-02 03:28:19 +00:00
DQN_ALWAYS_ASSERTM ( alignment < = 128 , " Alignment _not_ supported. Update metadata to use u16 for storing the offset! " ) ;
2018-06-11 06:26:57 +00:00
if ( size = = 0 )
return nullptr ;
2018-08-29 15:04:56 +00:00
if ( ! this - > block )
2018-09-10 12:49:17 +00:00
this - > LazyInit ( MINIMUM_BLOCK_SIZE , Dqn : : ZeroMem : : Yes ) ;
2018-09-02 03:28:19 +00:00
2018-09-10 12:49:17 +00:00
usize size_to_alloc = this - > tracker . GetAllocationSize ( size , alignment ) ;
2018-09-02 03:28:19 +00:00
bool push_to_head = true ;
2018-09-02 14:02:32 +00:00
if ( push_type = = PushType : : Default | | push_type = = PushType : : Opposite )
2018-09-02 03:28:19 +00:00
{
2018-09-02 14:02:32 +00:00
push_to_head = ! ( this - > flags & Flag : : DefaultAllocateTail ) ;
if ( push_type = = PushType : : Opposite ) push_to_head = ! push_to_head ;
2018-09-02 03:28:19 +00:00
}
else
{
push_to_head = ( push_type = = PushType : : Head ) ;
}
2018-08-29 15:04:56 +00:00
2018-07-17 15:53:58 +00:00
2018-06-11 06:26:57 +00:00
// Allocate New Block If Full
// =============================================================================================
2018-08-05 07:30:54 +00:00
bool need_new_block = true ;
2018-08-29 15:04:56 +00:00
if ( push_to_head ) need_new_block = ( ( this - > block - > head + size_to_alloc ) > this - > block - > tail ) ;
else need_new_block = ( ( this - > block - > tail - size_to_alloc ) < this - > block - > head ) ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
if ( need_new_block )
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
if ( ( this - > flags & Flag : : NonExpandable ) & & this - > block )
2018-05-17 11:19:58 +00:00
{
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( ! ( this - > flags & Flag : : NonExpandableAssert ) , " Allocator is non-expandable and has run out of memory " ) ;
2018-06-11 06:26:57 +00:00
return nullptr ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
isize new_block_size = DQN_MAX ( size_to_alloc , MINIMUM_BLOCK_SIZE ) ;
2018-09-02 11:17:14 +00:00
Block * new_block = DqnMemStack__AllocateBlock ( new_block_size , Dqn : : ZeroMem : : No , this - > block_allocator ) ;
2018-08-05 07:30:54 +00:00
new_block - > prev_block = this - > block ;
this - > block = new_block ;
2018-06-11 06:26:57 +00:00
}
// Calculate Ptr To Give Client
// =============================================================================================
2018-09-10 12:49:17 +00:00
char * src_ptr = ( push_to_head ) ? ( this - > block - > head ) : ( this - > block - > tail - size_to_alloc ) ;
void * aligned_result = this - > tracker . SetupPtr ( src_ptr , size , alignment ) ;
this - > tracker . PtrToHeader ( ( char * ) aligned_result ) - > alloc_type = ( push_to_head ) ? 0 : 1 ;
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
if ( push_to_head )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
this - > block - > head + = size_to_alloc ;
2018-07-17 15:53:58 +00:00
DQN_ASSERT ( this - > block - > head < = this - > block - > tail ) ;
2018-06-11 06:26:57 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
this - > block - > tail - = size_to_alloc ;
2018-07-17 15:53:58 +00:00
DQN_ASSERT ( this - > block - > tail > = this - > block - > head ) ;
2018-06-11 06:26:57 +00:00
}
2018-08-05 07:30:54 +00:00
return aligned_result ;
2017-06-20 09:19:52 +00:00
}
2018-09-02 11:17:14 +00:00
void DqnMemStack : : Pop ( void * ptr , Dqn : : ZeroMem clear )
2017-05-04 16:37:38 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ! ptr ) return ;
2017-05-04 16:37:38 +00:00
2018-08-05 07:30:54 +00:00
char * byte_ptr = static_cast < char * > ( ptr ) ;
2018-09-10 12:49:17 +00:00
DqnPtrHeader * ptr_header = reinterpret_cast < DqnPtrHeader * > ( byte_ptr - sizeof ( * ptr_header ) - this - > tracker . bounds_guard_size ) ;
2017-10-28 12:25:17 +00:00
2018-06-11 06:26:57 +00:00
// Check instrumented data
2018-09-10 12:49:17 +00:00
this - > tracker . CheckPtrs ( ) ;
this - > tracker . RemovePtr ( byte_ptr ) ;
2017-12-06 08:08:49 +00:00
2018-09-10 12:49:17 +00:00
isize full_alloc_size = this - > tracker . GetAllocationSize ( ptr_header - > alloc_amount , ptr_header - > alignment ) ;
char * start = byte_ptr - ptr_header - > offset_to_src_ptr ;
char * end = start + full_alloc_size ;
2018-08-05 07:30:54 +00:00
char const * block_end = this - > block - > memory + this - > block - > size ;
2017-10-29 01:45:00 +00:00
2018-08-05 07:30:54 +00:00
if ( ptr_header - > alloc_type = = 0 )
2018-05-17 11:19:58 +00:00
{
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( end = = this - > block - > head , " Pointer to pop was not the last allocation! %p != %p " , end , this - > block - > head ) ;
2018-09-10 12:49:17 +00:00
this - > block - > head - = full_alloc_size ;
2018-07-17 15:53:58 +00:00
DQN_ASSERT ( this - > block - > head > = this - > block - > memory ) ;
2018-06-11 06:26:57 +00:00
}
else
{
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( start = = this - > block - > tail , " Pointer to pop was not the last allocation! %p != %p " , start , this - > block - > tail ) ;
2018-09-10 12:49:17 +00:00
this - > block - > tail + = full_alloc_size ;
2018-08-05 07:30:54 +00:00
DQN_ASSERT ( this - > block - > tail < = block_end ) ;
2018-05-17 11:19:58 +00:00
}
2017-10-29 01:45:00 +00:00
2018-09-02 11:17:14 +00:00
if ( clear = = Dqn : : ZeroMem : : Yes )
2018-06-11 06:26:57 +00:00
DqnMem_Set ( start , 0 , end - start ) ;
2018-08-05 07:30:54 +00:00
if ( this - > block - > tail = = block_end & & this - > block - > head = = this - > block - > memory )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
if ( this - > block - > prev_block )
2018-09-02 11:17:14 +00:00
this - > PopBlock ( ) ;
2018-05-17 11:19:58 +00:00
}
2018-01-20 08:03:29 +00:00
}
2017-10-29 01:45:00 +00:00
2018-09-02 11:17:14 +00:00
bool DqnMemStack : : FreeBlock ( DqnMemStack : : Block * mem_block )
2017-10-29 01:45:00 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! mem_block | | ! this - > block )
2018-06-11 06:26:57 +00:00
return false ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
DqnMemStack : : Block * * block_ptr = & this - > block ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
while ( * block_ptr & & ( * block_ptr ) ! = mem_block )
block_ptr = & ( ( * block_ptr ) - > prev_block ) ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
if ( * block_ptr )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
DqnMemStack : : Block * block_to_free = * block_ptr ;
( * block_ptr ) = block_to_free - > prev_block ;
2018-06-11 06:26:57 +00:00
2018-09-10 12:49:17 +00:00
this - > tracker . RemovePtrRange ( block_to_free - > memory , block_to_free - > memory + block_to_free - > size ) ;
2018-08-05 07:30:54 +00:00
isize real_size = block_to_free - > size + sizeof ( DqnMemStack : : Block ) ;
2018-09-10 12:49:17 +00:00
this - > block_allocator - > Free ( block_to_free , real_size ) ;
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
// No more blocks, then last block has been freed
2018-09-02 11:17:14 +00:00
if ( ! this - > block ) DQN_ASSERT ( this - > mem_region_count = = 0 ) ;
2018-06-11 06:26:57 +00:00
return true ;
}
2017-10-29 01:45:00 +00:00
2018-06-11 06:26:57 +00:00
return false ;
}
void DqnMemStack : : ResetTail ( )
2018-02-04 14:50:36 +00:00
{
2018-07-17 14:47:13 +00:00
char * start = this - > block - > tail ;
char * end = this - > block - > memory + this - > block - > size ;
2018-09-10 12:49:17 +00:00
this - > tracker . RemovePtrRange ( start , end ) ;
2018-06-11 06:26:57 +00:00
this - > block - > tail = end ;
}
2018-02-04 14:50:36 +00:00
2018-09-02 11:17:14 +00:00
void DqnMemStack : : ClearCurrBlock ( Dqn : : ZeroMem zero )
2018-06-11 06:26:57 +00:00
{
if ( this - > block )
{
2018-09-10 12:49:17 +00:00
this - > tracker . RemovePtrRange ( this - > block - > memory , this - > block - > memory + this - > block - > size ) ;
2018-06-11 06:26:57 +00:00
this - > block - > head = this - > block - > memory ;
this - > block - > tail = this - > block - > memory + this - > block - > size ;
2018-09-02 11:17:14 +00:00
if ( zero = = Dqn : : ZeroMem : : Yes )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
DqnMem_Clear ( this - > block - > memory , 0 , this - > block - > size ) ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
}
}
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
DqnMemStack : : Info DqnMemStack : : GetInfo ( ) const
{
Info result = { } ;
2018-08-05 07:30:54 +00:00
for ( Block * block_ = this - > block ; block_ ; block_ = block_ - > prev_block )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
char const * block_end = block_ - > memory + block_ - > size ;
isize usage_from_head = block_ - > head - block_ - > memory ;
isize usage_from_tail = block_end - block_ - > tail ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
result . total_used + = usage_from_head + usage_from_tail ;
result . total_size + = block_ - > size ;
result . wasted_size + = ( block_ - > size - usage_from_head - usage_from_tail ) ;
result . num_blocks + + ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
char const * block_end = this - > block - > memory + this - > block - > size ;
isize usage_from_head = this - > block - > head - this - > block - > memory ;
isize usage_from_tail = block_end - this - > block - > tail ;
result . wasted_size - = ( this - > block - > size - usage_from_head - usage_from_tail ) ; // Don't include the curr block
2018-06-11 06:26:57 +00:00
return result ;
}
2018-09-02 11:17:14 +00:00
DqnMemStack : : MemRegion DqnMemStack : : MemRegionBegin ( )
2018-06-11 06:26:57 +00:00
{
2018-09-02 11:17:14 +00:00
MemRegion result = { } ;
result . stack = this ;
if ( this - > block )
{
result . starting_block = this - > block ;
result . starting_block_head = this - > block - > head ;
result . starting_block_tail = this - > block - > tail ;
}
2018-06-11 06:26:57 +00:00
2018-09-02 11:17:14 +00:00
this - > mem_region_count + + ;
2018-06-11 06:26:57 +00:00
return result ;
}
2018-09-02 11:17:14 +00:00
void DqnMemStack : : MemRegionEnd ( MemRegion region )
2018-06-11 06:26:57 +00:00
{
DQN_ASSERT ( region . stack = = this ) ;
2018-09-02 11:17:14 +00:00
this - > mem_region_count - - ;
DQN_ASSERT ( this - > mem_region_count > = 0 ) ;
2018-05-17 11:19:58 +00:00
2018-09-02 11:17:14 +00:00
while ( this - > block ! = region . starting_block )
this - > PopBlock ( ) ;
2018-05-17 11:19:58 +00:00
2018-09-02 11:17:14 +00:00
if ( this - > block )
2018-06-11 06:26:57 +00:00
{
2018-09-02 11:17:14 +00:00
this - > block - > head = region . starting_block_head ;
this - > block - > tail = region . starting_block_tail ;
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
char * start = this - > block - > head ;
char * end = this - > block - > tail ;
this - > tracker . RemovePtrRange ( start , end ) ;
2018-06-11 06:26:57 +00:00
}
}
2018-05-17 11:19:58 +00:00
2018-09-10 12:49:17 +00:00
void DqnMemStack : : MemRegionSave ( MemRegion * region )
{
DQN_ASSERT ( region - > stack = = this ) ;
region - > stack = nullptr ;
this - > mem_region_count - - ;
DQN_ASSERT ( this - > mem_region_count > = 0 ) ;
}
2018-06-11 06:26:57 +00:00
// #DqnHash
// =================================================================================================
// Taken from GingerBill single file library @ github.com/gingerbill/gb
u32 DqnHash_Murmur32Seed ( void const * data , usize len , u32 seed )
{
u32 const c1 = 0xcc9e2d51 ; u32 const c2 = 0x1b873593 ; u32 const r1 = 15 ;
u32 const r2 = 13 ; u32 const m = 5 ; u32 const n = 0xe6546b64 ;
usize i , nblocks = len / 4 ;
u32 hash = seed , k1 = 0 ;
u32 const * blocks = ( u32 const * ) data ;
u8 const * tail = ( u8 const * ) ( data ) + nblocks * 4 ;
for ( i = 0 ; i < nblocks ; i + + ) {
u32 k = blocks [ i ] ;
k * = c1 ;
k = ( k < < r1 ) | ( k > > ( 32 - r1 ) ) ;
k * = c2 ;
hash ^ = k ;
hash = ( ( hash < < r2 ) | ( hash > > ( 32 - r2 ) ) ) * m + n ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
switch ( len & 3 ) {
case 3 : k1 ^ = tail [ 2 ] < < 16 ;
case 2 : k1 ^ = tail [ 1 ] < < 8 ;
case 1 : k1 ^ = tail [ 0 ] ;
k1 * = c1 ;
k1 = ( k1 < < r1 ) | ( k1 > > ( 32 - r1 ) ) ;
k1 * = c2 ;
hash ^ = k1 ;
2018-05-17 11:19:58 +00:00
}
2017-10-29 01:45:00 +00:00
2018-06-11 06:26:57 +00:00
hash ^ = len ; hash ^ = ( hash > > 16 ) ;
hash * = 0x85ebca6b ; hash ^ = ( hash > > 13 ) ;
hash * = 0xc2b2ae35 ; hash ^ = ( hash > > 16 ) ;
2017-10-29 01:45:00 +00:00
2018-06-11 06:26:57 +00:00
return hash ;
2018-02-04 14:50:36 +00:00
}
2018-06-11 06:26:57 +00:00
u64 DqnHash_Murmur64Seed ( void const * data_ , usize len , u64 seed )
2018-02-04 14:50:36 +00:00
{
2018-06-11 06:26:57 +00:00
u64 const m = 0xc6a4a7935bd1e995ULL ;
i32 const r = 47 ;
u64 h = seed ^ ( len * m ) ;
u64 const * data = ( u64 const * ) data_ ;
u8 const * data2 = ( u8 const * ) data_ ;
u64 const * end = data + ( len / 8 ) ;
while ( data ! = end ) {
u64 k = * data + + ;
k * = m ; k ^ = k > > r ; k * = m ;
h ^ = k ; h * = m ;
}
switch ( len & 7 ) {
case 7 : h ^ = ( u64 ) ( data2 [ 6 ] ) < < 48 ;
case 6 : h ^ = ( u64 ) ( data2 [ 5 ] ) < < 40 ;
case 5 : h ^ = ( u64 ) ( data2 [ 4 ] ) < < 32 ;
case 4 : h ^ = ( u64 ) ( data2 [ 3 ] ) < < 24 ;
case 3 : h ^ = ( u64 ) ( data2 [ 2 ] ) < < 16 ;
case 2 : h ^ = ( u64 ) ( data2 [ 1 ] ) < < 8 ;
case 1 : h ^ = ( u64 ) ( data2 [ 0 ] ) ;
h * = m ;
} ;
h ^ = h > > r ; h * = m ; h ^ = h > > r ;
return h ;
2018-02-04 14:50:36 +00:00
}
2018-06-11 06:26:57 +00:00
// #DqnMath
// =================================================================================================
DQN_FILE_SCOPE f32 DqnMath_Lerp ( f32 a , f32 t , f32 b )
2017-10-29 09:30:07 +00:00
{
2018-06-11 06:26:57 +00:00
/*
Linear blend between two values . We having a starting point " a " , and
the distance to " b " is defined as ( b - a ) . Then we can say
2017-10-29 09:30:07 +00:00
2018-06-11 06:26:57 +00:00
a + t ( b - a )
As our linear blend fn . We start from " a " and choosing a t from 0 - > 1
will vary the value of ( b - a ) towards b . If we expand this , this
becomes
a + ( t * b ) - ( a * t ) = = ( 1 - t ) a + t * b
*/
f32 result = a + ( b - a ) * t ;
2018-05-17 11:19:58 +00:00
return result ;
2017-10-29 09:30:07 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnMath_Sqrtf ( f32 a )
2017-10-29 09:30:07 +00:00
{
2018-06-11 06:26:57 +00:00
f32 result = sqrtf ( a ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-10-29 09:30:07 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnMath_Clampf ( f32 val , f32 min , f32 max )
2017-10-29 09:30:07 +00:00
{
2018-06-11 06:26:57 +00:00
if ( val < min ) return min ;
if ( val > max ) return max ;
return val ;
2017-10-29 09:30:07 +00:00
}
2018-06-11 06:26:57 +00:00
// #DqnV2
// =================================================================================================
DQN_FILE_SCOPE DqnV2 DqnV2_Add ( DqnV2 a , DqnV2 b )
2017-10-29 01:45:00 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV2 result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] + b . e [ i ] ;
2018-05-17 11:19:58 +00:00
return result ;
2017-10-29 01:45:00 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_Sub ( DqnV2 a , DqnV2 b )
2017-10-29 01:45:00 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV2 result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] - b . e [ i ] ;
2018-05-17 11:19:58 +00:00
return result ;
2017-10-29 01:45:00 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_Scalei ( DqnV2 a , i32 b )
2018-02-02 08:34:44 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV2 result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b ;
2018-02-02 08:34:44 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2018-02-02 08:34:44 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_Scalef ( DqnV2 a , f32 b )
2018-02-02 08:34:44 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV2 result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b ;
return result ;
2018-02-02 08:34:44 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_Hadamard ( DqnV2 a , DqnV2 b )
2018-02-02 08:34:44 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV2 result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b . e [ i ] ;
2018-02-02 08:34:44 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2018-02-02 08:34:44 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnV2_Dot ( DqnV2 a , DqnV2 b )
2018-02-02 08:34:44 +00:00
{
2018-06-11 06:26:57 +00:00
/*
DOT PRODUCT
Two vectors with dot product equals | a | | b | cos ( theta )
| a | | d |
| b | . | e | = ( ad + be + cf )
| c | | f |
*/
f32 result = 0 ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result + = ( a . e [ i ] * b . e [ i ] ) ;
2018-02-02 08:34:44 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2018-02-02 08:34:44 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnV2_Equals ( DqnV2 a , DqnV2 b )
2018-02-02 08:34:44 +00:00
{
2018-06-11 06:26:57 +00:00
bool result = true ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
if ( a . e [ i ] ! = b . e [ i ] ) result = false ;
return result ;
2018-02-02 08:34:44 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnV2_LengthSquared ( DqnV2 a , DqnV2 b )
2018-02-02 08:34:44 +00:00
{
2018-06-11 06:26:57 +00:00
f32 x_ = b . x - a . x ;
f32 y_ = b . y - a . y ;
f32 result = ( DQN_SQUARED ( x_ ) + DQN_SQUARED ( y_ ) ) ;
return result ;
2018-02-02 08:34:44 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnV2_Length ( DqnV2 a , DqnV2 b )
2018-02-02 08:34:44 +00:00
{
2018-08-05 07:30:54 +00:00
f32 len_sq = DqnV2_LengthSquared ( a , b ) ;
if ( len_sq = = 0 ) return 0 ;
2018-02-02 08:34:44 +00:00
2018-08-05 07:30:54 +00:00
f32 result = DqnMath_Sqrtf ( len_sq ) ;
2018-06-11 06:26:57 +00:00
return result ;
2018-02-02 08:34:44 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_Normalise ( DqnV2 a )
2018-02-02 08:34:44 +00:00
{
2018-06-11 06:26:57 +00:00
f32 magnitude = DqnV2_Length ( DqnV2 ( 0 , 0 ) , a ) ;
if ( magnitude = = 0 ) return DqnV2 ( 0.0f ) ;
DqnV2 result = a * ( 1.0f / magnitude ) ;
return result ;
2018-02-02 08:34:44 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnV2_Overlaps ( DqnV2 a , DqnV2 b )
2017-05-04 16:37:38 +00:00
{
2018-06-11 06:26:57 +00:00
bool result = false ;
f32 lenOfA = a . max - a . min ;
f32 lenOfB = b . max - b . min ;
if ( lenOfA > lenOfB )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV2 tmp = a ;
a = b ;
b = tmp ;
2018-05-17 11:19:58 +00:00
}
2017-10-29 01:45:00 +00:00
2018-06-11 06:26:57 +00:00
if ( ( a . min > = b . min & & a . min < = b . max ) | | ( a . max > = b . min & & a . max < = b . max ) )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
result = true ;
2018-05-17 11:19:58 +00:00
}
2017-05-04 16:37:38 +00:00
2018-05-17 11:19:58 +00:00
return result ;
2017-05-04 16:37:38 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_Perpendicular ( DqnV2 a )
2017-06-08 06:41:24 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV2 result = DqnV2 ( a . y , - a . x ) ;
return result ;
}
2017-06-08 06:41:24 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_ResizeKeepAspectRatio ( DqnV2 src_size , DqnV2 target_size )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
f32 ratio_a = src_size . w / target_size . w ;
f32 ratio_b = src_size . h / target_size . h ;
f32 ratio = DQN_MIN ( ratio_a , ratio_b ) ;
DqnV2 result = DqnV2_Scalef ( target_size , ratio ) ;
2018-06-11 06:26:57 +00:00
return result ;
}
2017-06-08 06:41:24 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2 DqnV2_ConstrainToRatio ( DqnV2 dim , DqnV2 ratio )
{
DqnV2 result = { 0 } ;
2018-08-05 07:30:54 +00:00
f32 num_ratio_increments_to_width = ( f32 ) ( dim . w / ratio . w ) ;
f32 num_ratio_increments_to_height = ( f32 ) ( dim . h / ratio . h ) ;
2018-02-03 04:51:47 +00:00
2018-08-05 07:30:54 +00:00
f32 least_increments_to_side =
DQN_MIN ( num_ratio_increments_to_height , num_ratio_increments_to_width ) ;
2017-05-04 16:37:38 +00:00
2018-08-05 07:30:54 +00:00
result . w = ( f32 ) ( ratio . w * least_increments_to_side ) ;
result . h = ( f32 ) ( ratio . h * least_increments_to_side ) ;
2018-06-11 06:26:57 +00:00
return result ;
}
2017-05-04 16:37:38 +00:00
2018-06-11 06:26:57 +00:00
// #DqnV2i
// =================================================================================================
DQN_FILE_SCOPE DqnV2i DqnV2i_Add ( DqnV2i a , DqnV2i b )
{
DqnV2i result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] + b . e [ i ] ;
2017-06-20 09:19:52 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2017-06-20 09:19:52 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2i DqnV2i_Sub ( DqnV2i a , DqnV2i b )
2017-05-04 16:37:38 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV2i result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] - b . e [ i ] ;
2018-01-19 10:52:34 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2017-05-04 16:37:38 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2i DqnV2i_Scalef ( DqnV2i a , f32 b )
{
DqnV2i result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = ( i32 ) ( a . e [ i ] * b ) ;
2017-05-04 16:37:38 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-02-02 08:34:44 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2i DqnV2i_Scalei ( DqnV2i a , i32 b )
{
DqnV2i result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b ;
2018-02-02 08:34:44 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2017-05-04 16:37:38 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV2i DqnV2i_Hadamard ( DqnV2i a , DqnV2i b )
2017-05-04 16:37:38 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV2i result = { } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b . e [ i ] ;
2018-02-03 09:58:15 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-02-03 09:58:15 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnV2i_Dot ( DqnV2i a , DqnV2i b )
{
/*
DOT PRODUCT
Two vectors with dot product equals | a | | b | cos ( theta )
| a | | d |
| b | . | e | = ( ad + be + cf )
| c | | f |
*/
f32 result = 0 ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result + = ( a . e [ i ] * b . e [ i ] ) ;
2018-02-04 14:50:36 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-02-03 04:51:47 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnV2i_Equals ( DqnV2i a , DqnV2i b )
{
bool result = true ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
if ( a . e [ i ] ! = b . e [ i ] ) result = false ;
return result ;
}
2018-02-03 10:39:15 +00:00
2018-06-11 06:26:57 +00:00
// #DqnV3
// =================================================================================================
DQN_FILE_SCOPE DqnV3 DqnV3_Add ( DqnV3 a , DqnV3 b )
{
DqnV3 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] + b . e [ i ] ;
2018-02-03 10:39:15 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-02-03 10:39:15 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV3 DqnV3_Sub ( DqnV3 a , DqnV3 b )
{
DqnV3 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] - b . e [ i ] ;
2018-01-19 10:52:34 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV3 DqnV3_Scalei ( DqnV3 a , i32 b )
{
DqnV3 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b ;
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV3 DqnV3_Scalef ( DqnV3 a , f32 b )
{
DqnV3 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b ;
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV3 DqnV3_Hadamard ( DqnV3 a , DqnV3 b )
{
DqnV3 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b . e [ i ] ;
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnV3_Dot ( DqnV3 a , DqnV3 b )
{
/*
DOT PRODUCT
Two vectors with dot product equals | a | | b | cos ( theta )
| a | | d |
| b | . | e | = ( ad + be + cf )
| c | | f |
*/
f32 result = 0 ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result + = ( a . e [ i ] * b . e [ i ] ) ;
2018-05-17 11:19:58 +00:00
return result ;
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnV3_Equals ( DqnV3 a , DqnV3 b )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
bool result = true ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
if ( a . e [ i ] ! = b . e [ i ] ) result = false ;
2018-05-17 11:19:58 +00:00
return result ;
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV3 DqnV3_Cross ( DqnV3 a , DqnV3 b )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
/*
CROSS PRODUCT
Generate a perpendicular vector to the 2 vectors
| a | | d | | bf - ce |
| b | x | e | = | cd - af |
| c | | f | | ae - be |
*/
DqnV3 result = { 0 } ;
result . e [ 0 ] = ( a . e [ 1 ] * b . e [ 2 ] ) - ( a . e [ 2 ] * b . e [ 1 ] ) ;
result . e [ 1 ] = ( a . e [ 2 ] * b . e [ 0 ] ) - ( a . e [ 0 ] * b . e [ 2 ] ) ;
result . e [ 2 ] = ( a . e [ 0 ] * b . e [ 1 ] ) - ( a . e [ 1 ] * b . e [ 0 ] ) ;
2018-05-17 11:19:58 +00:00
return result ;
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV3 DqnV3_Normalise ( DqnV3 a )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
f32 length = DqnMath_Sqrtf ( DQN_SQUARED ( a . x ) + DQN_SQUARED ( a . y ) + DQN_SQUARED ( a . z ) ) ;
2018-08-05 07:30:54 +00:00
f32 inv_len = 1 / length ;
DqnV3 result = a * inv_len ;
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnV3_LengthSquared ( DqnV3 a , DqnV3 b )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
f32 x = b . x - a . x ;
f32 y = b . y - a . y ;
f32 z = b . z - a . z ;
f32 result = ( DQN_SQUARED ( x ) + DQN_SQUARED ( y ) + DQN_SQUARED ( z ) ) ;
return result ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnV3_Length ( DqnV3 a , DqnV3 b )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
f32 len_sq = DqnV3_LengthSquared ( a , b ) ;
if ( len_sq = = 0 ) return 0 ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
f32 result = DqnMath_Sqrtf ( len_sq ) ;
2018-06-11 06:26:57 +00:00
return result ;
}
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
// #DqnV4
// =================================================================================================
DQN_FILE_SCOPE DqnV4 DqnV4_Add ( DqnV4 a , DqnV4 b )
{
DqnV4 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] + b . e [ i ] ;
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV4 DqnV4_Sub ( DqnV4 a , DqnV4 b )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV4 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] - b . e [ i ] ;
return result ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV4 DqnV4_Scalei ( DqnV4 a , i32 b )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV4 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b ;
return result ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV4 DqnV4_Scalef ( DqnV4 a , f32 b )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV4 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b ;
2018-02-02 15:49:54 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2017-05-22 08:50:21 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV4 DqnV4_Hadamard ( DqnV4 a , DqnV4 b )
2017-05-04 16:37:38 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV4 result = { 0 } ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result . e [ i ] = a . e [ i ] * b . e [ i ] ;
2018-02-02 08:34:44 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-02-02 08:34:44 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE f32 DqnV4_Dot ( DqnV4 a , DqnV4 b )
{
/*
DOT PRODUCT
Two vectors with dot product equals | a | | b | cos ( theta )
| a | | d |
| b | . | e | = ( ad + be + cf )
| c | | f |
*/
f32 result = 0 ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
result + = ( a . e [ i ] * b . e [ i ] ) ;
2017-05-04 16:37:38 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2017-05-05 16:44:17 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnV4_Equals ( DqnV4 a , DqnV4 b )
{
bool result = true ;
for ( u32 i = 0 ; i < DQN_ARRAY_COUNT ( a . e ) ; i + + )
if ( a . e [ i ] ! = b . e [ i ] ) result = false ;
return result ;
}
2018-01-20 10:08:04 +00:00
2018-06-11 06:26:57 +00:00
// #DqnMat4 Implementation
// =================================================================================================
DQN_FILE_SCOPE DqnMat4 DqnMat4_Identity ( )
{
DqnMat4 result = { 0 , 0 , 0 , 0 } ;
result . e [ 0 ] [ 0 ] = 1 ;
result . e [ 1 ] [ 1 ] = 1 ;
result . e [ 2 ] [ 2 ] = 1 ;
result . e [ 3 ] [ 3 ] = 1 ;
return result ;
}
2018-02-02 12:26:05 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_Orthographic ( f32 left , f32 right , f32 bottom , f32 top , f32 znear ,
f32 zfar )
2018-06-11 06:26:57 +00:00
{
DqnMat4 result = DqnMat4_Identity ( ) ;
result . e [ 0 ] [ 0 ] = + 2.0f / ( right - left ) ;
result . e [ 1 ] [ 1 ] = + 2.0f / ( top - bottom ) ;
2018-08-05 07:30:54 +00:00
result . e [ 2 ] [ 2 ] = - 2.0f / ( zfar - znear ) ;
2017-05-05 16:44:17 +00:00
2018-06-11 06:26:57 +00:00
result . e [ 3 ] [ 0 ] = - ( right + left ) / ( right - left ) ;
result . e [ 3 ] [ 1 ] = - ( top + bottom ) / ( top - bottom ) ;
2018-08-05 07:30:54 +00:00
result . e [ 3 ] [ 2 ] = - ( zfar + znear ) / ( zfar - znear ) ;
2017-05-05 16:44:17 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2017-05-05 16:44:17 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_Perspective ( f32 fov_y_degrees , f32 aspect_ratio , f32 znear , f32 zfar )
2018-02-05 06:42:27 +00:00
{
2018-08-05 07:30:54 +00:00
f32 fov_y_radians = DQN_DEGREES_TO_RADIANS ( fov_y_degrees ) ;
f32 fov_y_radians_over_2 = fov_y_radians * 0.5f ;
f32 tan_fov_y_radians_over_2 = tanf ( fov_y_radians_over_2 ) ;
f32 znear_sub_zfar = znear - zfar ;
2018-02-05 06:42:27 +00:00
2018-06-11 06:26:57 +00:00
DqnMat4 result = DqnMat4_Identity ( ) ;
2018-08-05 07:30:54 +00:00
result . e [ 0 ] [ 0 ] = 1.0f / ( aspect_ratio * tan_fov_y_radians_over_2 ) ;
result . e [ 1 ] [ 1 ] = 1.0f / tan_fov_y_radians_over_2 ;
result . e [ 2 ] [ 2 ] = ( znear + zfar ) / znear_sub_zfar ;
2018-06-11 06:26:57 +00:00
result . e [ 2 ] [ 3 ] = - 1.0f ;
2018-08-05 07:30:54 +00:00
result . e [ 3 ] [ 2 ] = ( 2.0f * znear * zfar ) / znear_sub_zfar ;
2018-06-11 06:26:57 +00:00
result . e [ 3 ] [ 3 ] = 0.0f ;
2018-02-05 06:42:27 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2018-02-05 06:42:27 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_LookAt ( DqnV3 eye , DqnV3 center , DqnV3 up )
2018-02-04 14:50:36 +00:00
{
2018-06-11 06:26:57 +00:00
DqnMat4 result = { 0 , 0 , 0 , 0 } ;
DqnV3 f = DqnV3_Normalise ( DqnV3_Sub ( eye , center ) ) ;
DqnV3 s = DqnV3_Normalise ( DqnV3_Cross ( up , f ) ) ;
DqnV3 u = DqnV3_Cross ( f , s ) ;
result . e [ 0 ] [ 0 ] = s . x ;
result . e [ 0 ] [ 1 ] = u . x ;
result . e [ 0 ] [ 2 ] = - f . x ;
result . e [ 1 ] [ 0 ] = s . y ;
result . e [ 1 ] [ 1 ] = u . y ;
result . e [ 1 ] [ 2 ] = - f . y ;
result . e [ 2 ] [ 0 ] = s . z ;
result . e [ 2 ] [ 1 ] = u . z ;
result . e [ 2 ] [ 2 ] = - f . z ;
result . e [ 3 ] [ 0 ] = - DqnV3_Dot ( s , eye ) ;
result . e [ 3 ] [ 1 ] = - DqnV3_Dot ( u , eye ) ;
result . e [ 3 ] [ 2 ] = DqnV3_Dot ( f , eye ) ;
result . e [ 3 ] [ 3 ] = 1.0f ;
return result ;
2018-02-04 14:50:36 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_Translate3f ( f32 x , f32 y , f32 z )
{
DqnMat4 result = DqnMat4_Identity ( ) ;
result . e [ 3 ] [ 0 ] = x ;
result . e [ 3 ] [ 1 ] = y ;
result . e [ 3 ] [ 2 ] = z ;
return result ;
}
2018-02-04 14:50:36 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_TranslateV3 ( DqnV3 vec )
2017-05-05 16:44:17 +00:00
{
2018-06-11 06:26:57 +00:00
DqnMat4 result = DqnMat4_Identity ( ) ;
result . e [ 3 ] [ 0 ] = vec . x ;
result . e [ 3 ] [ 1 ] = vec . y ;
result . e [ 3 ] [ 2 ] = vec . z ;
2018-05-17 11:19:58 +00:00
return result ;
2017-05-04 16:37:38 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_Rotate ( f32 radians , f32 x , f32 y , f32 z )
2017-05-04 16:37:38 +00:00
{
2018-06-11 06:26:57 +00:00
DqnMat4 result = DqnMat4_Identity ( ) ;
2018-08-05 07:30:54 +00:00
f32 sin_val = sinf ( radians ) ;
f32 cos_val = cosf ( radians ) ;
f32 one_minux_cos_val = 1 - cos_val ;
2018-02-03 04:51:47 +00:00
2018-06-11 06:26:57 +00:00
DqnV3 axis = DqnV3_Normalise ( DqnV3 ( x , y , z ) ) ;
2018-02-03 04:51:47 +00:00
2018-08-05 07:30:54 +00:00
result . e [ 0 ] [ 0 ] = ( axis . x * axis . x * one_minux_cos_val ) + cos_val ;
result . e [ 0 ] [ 1 ] = ( axis . x * axis . y * one_minux_cos_val ) + ( axis . z * sin_val ) ;
result . e [ 0 ] [ 2 ] = ( axis . x * axis . z * one_minux_cos_val ) - ( axis . y * sin_val ) ;
2017-05-04 16:37:38 +00:00
2018-08-05 07:30:54 +00:00
result . e [ 1 ] [ 0 ] = ( axis . y * axis . x * one_minux_cos_val ) - ( axis . z * sin_val ) ;
result . e [ 1 ] [ 1 ] = ( axis . y * axis . y * one_minux_cos_val ) + cos_val ;
result . e [ 1 ] [ 2 ] = ( axis . y * axis . z * one_minux_cos_val ) + ( axis . x * sin_val ) ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
result . e [ 2 ] [ 0 ] = ( axis . z * axis . x * one_minux_cos_val ) + ( axis . y * sin_val ) ;
result . e [ 2 ] [ 1 ] = ( axis . z * axis . y * one_minux_cos_val ) - ( axis . x * sin_val ) ;
result . e [ 2 ] [ 2 ] = ( axis . z * axis . z * one_minux_cos_val ) + cos_val ;
2018-02-03 09:58:15 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-02-03 09:58:15 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_Scale ( f32 x , f32 y , f32 z )
{
DqnMat4 result = { 0 , 0 , 0 , 0 } ;
result . e [ 0 ] [ 0 ] = x ;
result . e [ 1 ] [ 1 ] = y ;
result . e [ 2 ] [ 2 ] = z ;
result . e [ 3 ] [ 3 ] = 1 ;
2018-05-17 11:19:58 +00:00
return result ;
2018-01-18 09:25:44 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnMat4 DqnMat4_ScaleV3 ( DqnV3 scale )
2017-05-04 16:37:38 +00:00
{
2018-06-11 06:26:57 +00:00
DqnMat4 result = { 0 , 0 , 0 , 0 } ;
result . e [ 0 ] [ 0 ] = scale . x ;
result . e [ 1 ] [ 1 ] = scale . y ;
result . e [ 2 ] [ 2 ] = scale . z ;
result . e [ 3 ] [ 3 ] = 1 ;
return result ;
}
DQN_FILE_SCOPE DqnMat4 DqnMat4_Mul ( DqnMat4 a , DqnMat4 b )
{
DqnMat4 result = { 0 } ;
for ( u32 j = 0 ; j < 4 ; j + + ) {
for ( u32 i = 0 ; i < 4 ; i + + )
{
result . e [ j ] [ i ] = a . e [ 0 ] [ i ] * b . e [ j ] [ 0 ]
+ a . e [ 1 ] [ i ] * b . e [ j ] [ 1 ]
+ a . e [ 2 ] [ i ] * b . e [ j ] [ 2 ]
+ a . e [ 3 ] [ i ] * b . e [ j ] [ 3 ] ;
}
}
2018-02-02 08:34:44 +00:00
2018-05-17 11:19:58 +00:00
return result ;
2017-05-04 16:37:38 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE DqnV4 DqnMat4_MulV4 ( DqnMat4 a , DqnV4 b )
2017-05-04 16:37:38 +00:00
{
2018-06-11 06:26:57 +00:00
DqnV4 result = { 0 } ;
result . x = ( a . e [ 0 ] [ 0 ] * b . x ) + ( a . e [ 1 ] [ 0 ] * b . y ) + ( a . e [ 2 ] [ 0 ] * b . z ) + ( a . e [ 3 ] [ 0 ] * b . w ) ;
result . y = ( a . e [ 0 ] [ 1 ] * b . x ) + ( a . e [ 1 ] [ 1 ] * b . y ) + ( a . e [ 2 ] [ 1 ] * b . z ) + ( a . e [ 3 ] [ 1 ] * b . w ) ;
result . z = ( a . e [ 0 ] [ 2 ] * b . x ) + ( a . e [ 1 ] [ 2 ] * b . y ) + ( a . e [ 2 ] [ 2 ] * b . z ) + ( a . e [ 3 ] [ 2 ] * b . w ) ;
result . w = ( a . e [ 0 ] [ 3 ] * b . x ) + ( a . e [ 1 ] [ 3 ] * b . y ) + ( a . e [ 2 ] [ 3 ] * b . z ) + ( a . e [ 3 ] [ 3 ] * b . w ) ;
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
return result ;
}
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
// #DqnRect Implementation
// =================================================================================================
void DqnRect : : GetSize ( f32 * width , f32 * height ) const
{
DQN_ASSERT ( this - > min < = this - > max ) ;
if ( width ) * width = this - > max . x - this - > min . x ;
if ( height ) * height = this - > max . y - this - > min . y ;
}
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
DqnV2 DqnRect : : GetCenter ( ) const
{
DQN_ASSERT ( this - > min < = this - > max ) ;
f32 sumX = this - > min . x + this - > max . x ;
f32 sumY = this - > min . y + this - > max . y ;
DqnV2 result = DqnV2 ( sumX , sumY ) * 0.5f ;
return result ;
}
DqnRect DqnRect : : ClipRect ( DqnRect clip ) const
{
DQN_ASSERT ( this - > min < = this - > max ) ;
DQN_ASSERT ( clip . min < = clip . max ) ;
DqnRect result = * this ;
if ( clip . min . x > this - > min . x & & clip . min . x < this - > max . x )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
if ( clip . min . y > this - > min . y & & clip . min . y < this - > max . y )
{
result . min = clip . min ;
}
else if ( clip . max . y > this - > min . y )
{
result . min . x = clip . min . x ;
}
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
if ( clip . max . x > this - > min . x & & clip . max . x < this - > max . x )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
if ( clip . max . y > this - > min . y & & clip . max . y < this - > max . y )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
result . max = clip . max ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
else if ( clip . min . y < this - > max . y )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
result . max . x = clip . max . x ;
2018-05-17 11:19:58 +00:00
}
}
2018-01-18 05:01:37 +00:00
2018-06-11 06:26:57 +00:00
return result ;
2017-10-28 08:58:09 +00:00
}
2018-06-11 06:26:57 +00:00
DqnRect DqnRect : : Move ( DqnV2 shift ) const
2017-10-28 08:58:09 +00:00
{
2018-06-11 06:26:57 +00:00
DQN_ASSERT ( this - > min < = this - > max ) ;
2017-04-09 05:08:31 +00:00
2018-06-11 06:26:57 +00:00
DqnRect result ;
result . min = this - > min + shift ;
result . max = this - > max + shift ;
return result ;
2017-05-04 16:37:38 +00:00
}
2017-04-11 11:05:40 +00:00
2018-06-11 06:26:57 +00:00
bool DqnRect : : ContainsP ( DqnV2 p ) const
2017-10-29 09:30:07 +00:00
{
2018-06-11 06:26:57 +00:00
DQN_ASSERT ( this - > min < = this - > max ) ;
2017-10-29 09:30:07 +00:00
2018-08-05 07:30:54 +00:00
bool outside_of_rect_x = false ;
2018-06-11 06:26:57 +00:00
if ( p . x < this - > min . x | | p . x > this - > max . w )
2018-08-05 07:30:54 +00:00
outside_of_rect_x = true ;
2017-10-29 09:30:07 +00:00
2018-08-05 07:30:54 +00:00
bool outside_of_rect_y = false ;
2018-06-11 06:26:57 +00:00
if ( p . y < this - > min . y | | p . y > this - > max . h )
2018-08-05 07:30:54 +00:00
outside_of_rect_y = true ;
2017-10-29 09:30:07 +00:00
2018-08-05 07:30:54 +00:00
if ( outside_of_rect_x | | outside_of_rect_y ) return false ;
2017-10-29 09:30:07 +00:00
2018-06-11 06:26:57 +00:00
return true ;
}
2017-10-29 09:30:07 +00:00
2018-06-11 06:26:57 +00:00
// #DqnChar Implementation
// =================================================================================================
DQN_FILE_SCOPE char DqnChar_ToLower ( char c )
{
if ( c > = ' A ' & & c < = ' Z ' )
{
2018-08-05 07:30:54 +00:00
i32 shift_offset = ' a ' - ' A ' ;
return ( c + ( char ) shift_offset ) ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
return c ;
}
2017-10-29 09:30:07 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE char DqnChar_ToUpper ( char c )
{
if ( c > = ' a ' & & c < = ' z ' )
{
2018-08-05 07:30:54 +00:00
i32 shift_offset = ' a ' - ' A ' ;
return ( c - ( char ) shift_offset ) ;
2018-06-11 06:26:57 +00:00
}
return c ;
}
2017-10-29 09:30:07 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnChar_IsEndOfLine ( char c )
{
bool result = ( c = = ' \n ' ) ;
return result ;
2017-10-29 09:30:07 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnChar_IsWhitespace ( char c )
2017-10-29 09:30:07 +00:00
{
2018-06-11 06:26:57 +00:00
bool result = ( c = = ' ' | | c = = ' \r ' | | c = = ' \n ' | | c = = ' \t ' ) ;
return result ;
}
2017-10-29 09:30:07 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE char * DqnChar_TrimWhitespaceAround ( char const * src , i32 src_len , i32 * new_len )
2018-06-11 06:26:57 +00:00
{
if ( ! src ) return nullptr ;
2018-08-05 07:30:54 +00:00
if ( src_len < 0 ) return ( char * ) src ;
2017-10-29 09:30:07 +00:00
2018-06-11 06:26:57 +00:00
char const * start = src ;
2018-08-05 07:30:54 +00:00
char const * end = start + ( src_len - 1 ) ;
2018-06-11 06:26:57 +00:00
while ( start [ 0 ] & & DqnChar_IsWhitespace ( start [ 0 ] ) )
{
start + + ;
}
2017-10-29 09:30:07 +00:00
2018-08-05 07:30:54 +00:00
i32 chars_skipped = ( i32 ) ( start - src ) ;
i32 updated_len = src_len - chars_skipped ;
if ( updated_len < = 0 )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
if ( new_len ) * new_len = 0 ;
2018-06-11 06:26:57 +00:00
return nullptr ;
2018-05-17 11:19:58 +00:00
}
2017-10-29 09:30:07 +00:00
2018-06-11 06:26:57 +00:00
while ( end [ 0 ] & & DqnChar_IsWhitespace ( end [ 0 ] ) )
{
end - - ;
}
2017-10-29 09:30:07 +00:00
2018-08-05 07:30:54 +00:00
chars_skipped = ( i32 ) ( ( src + src_len - 1 ) - end ) ;
updated_len = updated_len - chars_skipped ;
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
if ( new_len ) * new_len = updated_len ;
2018-06-11 06:26:57 +00:00
return ( char * ) start ;
2017-10-29 09:30:07 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnChar_IsDigit ( char c )
2017-04-09 06:35:54 +00:00
{
2018-06-11 06:26:57 +00:00
if ( c > = ' 0 ' & & c < = ' 9 ' ) return true ;
return false ;
}
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnChar_IsAlpha ( char c )
{
if ( ( c > = ' a ' & & c < = ' z ' ) | | ( c > = ' A ' & & c < = ' Z ' ) ) return true ;
return false ;
2017-04-09 06:35:54 +00:00
}
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE bool DqnChar_IsAlphaNum ( char c )
2017-04-09 06:53:24 +00:00
{
2018-06-11 06:26:57 +00:00
if ( DqnChar_IsAlpha ( c ) | | DqnChar_IsDigit ( c ) ) return true ;
return false ;
2017-04-09 06:53:24 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE char * DqnChar_SkipWhitespace ( char const * ptr )
2017-05-20 09:11:28 +00:00
{
2018-06-11 06:26:57 +00:00
while ( ptr & & ( * ptr = = ' ' | | * ptr = = ' \r ' | | * ptr = = ' \n ' | | * ptr = = ' \t ' ) ) ptr + + ;
return ( char * ) ptr ;
2017-05-20 09:11:28 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE char * DqnChar_FindLastChar ( char * ptr , char ch , i32 len , u32 * len_to_char )
2017-04-09 05:08:31 +00:00
{
2018-06-11 06:26:57 +00:00
for ( i32 i = len - 1 ; i > = 0 ; i - - )
{
if ( ptr [ i ] = = ch )
{
2018-08-05 07:30:54 +00:00
if ( len_to_char ) * len_to_char = ( u32 ) len - i ;
2018-06-11 06:26:57 +00:00
return & ptr [ i ] ;
}
}
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
return nullptr ;
2017-04-09 06:26:08 +00:00
}
2018-08-27 14:25:08 +00:00
DQN_FILE_SCOPE char * Dqn_EatLine ( char * * input , int * line_len )
2017-04-09 06:26:08 +00:00
{
2018-08-27 14:25:08 +00:00
* input = DqnChar_SkipWhitespace ( * input ) ;
char * result = * input ;
2017-04-09 06:26:08 +00:00
2018-08-27 14:25:08 +00:00
for ( int rune_len = - 1 ; ; rune_len = - 1 )
2018-06-11 06:26:57 +00:00
{
2018-08-27 14:25:08 +00:00
u32 rune = 0 ;
for ( ; rune_len ! = 0 & & rune_len ! = 1 ; * input + = rune_len )
rune_len = DqnStr_ReadUTF8Codepoint ( reinterpret_cast < u32 * > ( * input ) , & rune ) ;
2017-05-01 06:40:52 +00:00
2018-08-27 14:25:08 +00:00
if ( rune_len = = 1 )
{
if ( rune = = ' \r ' | | rune = = ' \n ' )
{
char * ptr_to_rune = ( * input - rune_len ) ;
* ptr_to_rune = 0 ;
2018-09-02 03:28:19 +00:00
if ( line_len ) * line_len = static_cast < int > ( ptr_to_rune - result ) ;
2018-08-27 14:25:08 +00:00
return result ;
}
}
else if ( rune_len = = 0 )
{
2018-09-02 03:28:19 +00:00
if ( line_len ) * line_len = static_cast < int > ( * input - result ) ;
2018-08-27 14:25:08 +00:00
* input = nullptr ;
return result ;
}
2018-06-11 06:26:57 +00:00
}
2017-05-01 06:40:52 +00:00
}
2018-08-27 14:25:08 +00:00
DQN_FILE_SCOPE wchar_t * Dqn_EatLine ( wchar_t * * input , int * line_len )
2017-04-09 06:26:08 +00:00
{
2018-08-27 14:25:08 +00:00
* input = DqnWChar_SkipWhitespace ( * input ) ;
wchar_t * result = * input ;
2018-06-11 06:26:57 +00:00
2018-08-27 14:25:08 +00:00
for ( ; ; ( * input ) + + )
2018-06-11 06:26:57 +00:00
{
2018-08-27 14:25:08 +00:00
if ( ! ( * input ) )
{
2018-09-02 03:28:19 +00:00
if ( line_len ) * line_len = static_cast < int > ( * input - result ) ;
2018-08-27 14:25:08 +00:00
return result ;
}
2017-04-09 06:26:08 +00:00
2018-08-27 14:25:08 +00:00
wchar_t ch = ( * input ) [ 0 ] ;
if ( ch = = ' \r ' | | ch = = ' \n ' )
{
( * input ) [ 0 ] = 0 ;
2018-09-02 03:28:19 +00:00
if ( line_len ) * line_len = static_cast < int > ( * input - result ) ;
2018-08-27 14:25:08 +00:00
( * input ) + + ;
return result ;
}
}
2017-04-09 06:26:08 +00:00
}
2018-06-11 06:26:57 +00:00
// #DqnStr Implementation
// =================================================================================================
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnStr_Cmp ( char const * a , char const * b , i32 num_bytes_to_cmp , Dqn : : IgnoreCase ignore )
2017-04-09 06:26:08 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ! a | | ! b ) return - 1 ;
if ( ! a ) return - b [ 0 ] ;
if ( ! b ) return - a [ 0 ] ;
2018-08-05 07:30:54 +00:00
if ( num_bytes_to_cmp = = 0 ) return 0 ;
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
i32 bytes_cmped = 0 ;
2018-06-11 06:26:57 +00:00
i32 result = 0 ;
2018-06-25 10:54:38 +00:00
if ( ignore = = Dqn : : IgnoreCase : : Yes )
2018-06-11 06:26:57 +00:00
{
while ( a [ 0 ] & & ( DqnChar_ToLower ( a [ 0 ] ) = = DqnChar_ToLower ( b [ 0 ] ) ) )
{
a + + ; b + + ;
2018-08-05 07:30:54 +00:00
if ( + + bytes_cmped = = num_bytes_to_cmp ) return 0 ;
2018-06-11 06:26:57 +00:00
}
result = DqnChar_ToLower ( a [ 0 ] ) - DqnChar_ToLower ( b [ 0 ] ) ;
}
else
{
while ( a [ 0 ] & & ( a [ 0 ] = = b [ 0 ] ) )
{
a + + ; b + + ;
2018-08-05 07:30:54 +00:00
if ( + + bytes_cmped = = num_bytes_to_cmp ) return 0 ;
2018-06-11 06:26:57 +00:00
}
result = a [ 0 ] - b [ 0 ] ;
}
2017-04-09 06:26:08 +00:00
2018-05-17 11:19:58 +00:00
return result ;
2017-04-09 06:26:08 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE char * DqnStr_GetPtrToLastSlash ( char const * str , i32 str_len )
2017-04-09 06:26:08 +00:00
{
2018-06-11 06:26:57 +00:00
char const * result = str ;
2018-08-05 07:30:54 +00:00
if ( str_len = = - 1 ) str_len = DqnStr_Len ( str ) ;
2017-04-09 06:26:08 +00:00
2018-08-05 07:30:54 +00:00
for ( auto i = str_len - 1 ; i > = 0 ; i - - )
2018-06-11 06:26:57 +00:00
{
if ( result [ i ] = = ' \\ ' | | result [ i ] = = ' / ' )
{
result = result + i + 1 ;
break ;
}
}
return ( char * ) result ;
2017-04-09 06:26:08 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE i32 DqnStr_Len ( char const * a )
2017-04-09 06:26:08 +00:00
{
2018-06-11 06:26:57 +00:00
i32 result = 0 ;
while ( a & & a [ result ] ) result + + ;
2018-05-17 11:19:58 +00:00
return result ;
2017-04-09 06:26:08 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnStr_LenUTF8 ( u32 const * a , i32 * len_in_bytes )
2017-04-09 06:53:24 +00:00
{
2018-08-05 07:30:54 +00:00
i32 utf8_len = 0 ;
i32 utf8_len_in_bytes = 0 ;
u8 * byte_ptr = ( u8 * ) a ;
2018-06-11 06:26:57 +00:00
while ( true )
{
u32 codepoint = 0 ;
2018-08-05 07:30:54 +00:00
i32 num_bytes_in_codepoint = DqnStr_ReadUTF8Codepoint ( ( u32 * ) byte_ptr , & codepoint ) ;
2017-04-09 06:53:24 +00:00
2018-08-05 07:30:54 +00:00
if ( num_bytes_in_codepoint = = 0 ) break ;
utf8_len + + ;
byte_ptr + = num_bytes_in_codepoint ;
utf8_len_in_bytes + = num_bytes_in_codepoint ;
2018-06-11 06:26:57 +00:00
}
2017-05-20 09:11:28 +00:00
2018-08-05 07:30:54 +00:00
if ( len_in_bytes ) * len_in_bytes = utf8_len_in_bytes ;
return utf8_len ;
2017-04-09 06:53:24 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE i32 DqnStr_LenDelimitWith ( char const * a , char delimiter )
2017-04-09 06:53:24 +00:00
{
2018-06-11 06:26:57 +00:00
i32 result = 0 ;
while ( a & & a [ result ] & & a [ result ] ! = delimiter ) result + + ;
2018-05-17 11:19:58 +00:00
return result ;
2017-04-09 06:53:24 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnStr_ReadUTF8Codepoint ( u32 const * a , u32 * out_codepoint )
2017-04-09 06:53:24 +00:00
{
2018-06-11 06:26:57 +00:00
u8 * byte = ( u8 * ) a ;
if ( a & & byte [ 0 ] )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
i32 num_bytes_in_char = 0 ;
u32 actual_char = 0 ;
2017-04-09 06:53:24 +00:00
2018-06-11 06:26:57 +00:00
if ( byte [ 0 ] < = 128 )
{
2018-08-05 07:30:54 +00:00
actual_char = byte [ 0 ] ;
num_bytes_in_char = 1 ;
2018-06-11 06:26:57 +00:00
}
else if ( ( byte [ 0 ] & 0xE0 ) = = 0xC0 )
{
// Header 110xxxxx 10xxxxxx
2018-08-05 07:30:54 +00:00
actual_char = ( ( u32 ) ( byte [ 0 ] & 0x3F ) < < 6 )
2018-06-11 06:26:57 +00:00
| ( ( u32 ) ( byte [ 1 ] & 0x1F ) < < 0 ) ;
2018-08-05 07:30:54 +00:00
num_bytes_in_char = 2 ;
2018-06-11 06:26:57 +00:00
}
else if ( ( byte [ 0 ] & 0xF0 ) = = 0xE0 )
{
// Header 1110xxxx 10xxxxxx 10xxxxxx
2018-08-05 07:30:54 +00:00
actual_char = ( ( u32 ) ( byte [ 0 ] & 0x0F ) < < 12 )
2018-06-11 06:26:57 +00:00
| ( ( u32 ) ( byte [ 1 ] & 0x3F ) < < 6 )
| ( ( u32 ) ( byte [ 2 ] & 0x3F ) < < 0 ) ;
2018-08-05 07:30:54 +00:00
num_bytes_in_char = 3 ;
2018-06-11 06:26:57 +00:00
}
else if ( ( byte [ 0 ] & 0xF8 ) = = 0xF0 )
{
// Header 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
2018-08-05 07:30:54 +00:00
actual_char = ( ( u32 ) ( byte [ 0 ] & 0x07 ) < < 18 )
2018-06-11 06:26:57 +00:00
| ( ( u32 ) ( byte [ 1 ] & 0x3F ) < < 12 )
| ( ( u32 ) ( byte [ 2 ] & 0x3F ) < < 6 )
| ( ( u32 ) ( byte [ 3 ] & 0x3F ) < < 0 ) ;
2018-08-05 07:30:54 +00:00
num_bytes_in_char = 4 ;
2018-06-11 06:26:57 +00:00
}
else
{
// NOTE: Malformed utf8 stream
}
2018-08-05 07:30:54 +00:00
if ( out_codepoint ) * out_codepoint = actual_char ;
return num_bytes_in_char ;
2018-05-17 11:19:58 +00:00
}
2017-04-09 06:53:24 +00:00
2018-06-11 06:26:57 +00:00
return 0 ;
2017-04-09 06:53:24 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void DqnStr_Reverse ( char * buf , isize buf_size )
2017-04-09 06:53:24 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ! buf ) return ;
2018-08-05 07:30:54 +00:00
isize mid = buf_size / 2 ;
2018-06-11 06:26:57 +00:00
for ( isize i = 0 ; i < mid ; i + + )
{
char tmp = buf [ i ] ;
2018-08-05 07:30:54 +00:00
buf [ i ] = buf [ ( buf_size - 1 ) - i ] ;
buf [ ( buf_size - 1 ) - i ] = tmp ;
2018-06-11 06:26:57 +00:00
}
2017-04-09 06:53:24 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE bool DqnStr_EndsWith ( char const * src , i32 src_len , char const * find , i32 find_len ,
2018-06-11 06:26:57 +00:00
Dqn : : IgnoreCase ignore )
2017-07-13 02:50:01 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! src | | ! find | | find_len < 0 | | src_len < 0 ) return false ;
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
if ( src_len < find_len )
2018-06-11 06:26:57 +00:00
return false ;
2018-08-05 07:30:54 +00:00
char const * src_end = src + ( src_len ) ;
char const * check_src_from = src_end - find_len ;
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
bool result = ( DqnStr_Cmp ( check_src_from , find , find_len , ignore ) = = 0 ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-07-13 02:50:01 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnStr_FindFirstOccurence ( char const * src , i32 src_len ,
char const * find , i32 find_len , Dqn : : IgnoreCase ignore )
2017-04-09 06:26:08 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ! src | | ! find ) return - 1 ;
2018-08-05 07:30:54 +00:00
if ( src_len = = 0 | | find_len = = 0 ) return - 1 ;
if ( src_len < find_len ) return - 1 ;
2017-04-09 05:08:31 +00:00
2018-08-05 07:30:54 +00:00
for ( i32 index_in_src = 0 ; index_in_src < src_len ; index_in_src + + )
2018-06-11 06:26:57 +00:00
{
// NOTE: As we scan through, if the src string we index into becomes
// shorter than the substring we're checking then the substring is not
// contained in the src string.
2018-08-05 07:30:54 +00:00
i32 remaining_len_in_src = src_len - index_in_src ;
if ( remaining_len_in_src < find_len ) break ;
2017-04-09 05:08:31 +00:00
2018-08-05 07:30:54 +00:00
const char * src_substr = src + index_in_src ;
if ( DqnStr_Cmp ( src_substr , find , find_len , ignore ) = = 0 )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
return index_in_src ;
2018-06-11 06:26:57 +00:00
}
}
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
// NOTE(doyle): We have early exit, if we reach here, then the substring was
// not found.
return - 1 ;
2017-05-01 06:40:52 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE char * DqnStr_GetFirstOccurence ( char const * src , i32 src_len , char const * find ,
i32 find_len , Dqn : : IgnoreCase ignore )
2017-05-01 06:40:52 +00:00
{
2018-08-05 07:30:54 +00:00
i32 offset = DqnStr_FindFirstOccurence ( src , src_len , find , find_len , ignore ) ;
2018-06-11 06:26:57 +00:00
if ( offset = = - 1 ) return nullptr ;
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
char * result = ( char * ) ( src + offset ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-05-01 06:40:52 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE bool DqnStr_HasSubstring ( char const * src , i32 src_len ,
char const * find , i32 find_len , Dqn : : IgnoreCase ignore )
2017-05-01 06:40:52 +00:00
{
2018-08-05 07:30:54 +00:00
if ( DqnStr_FindFirstOccurence ( src , src_len , find , find_len , ignore ) = = - 1 )
2018-06-11 06:26:57 +00:00
return false ;
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
return true ;
2017-05-01 06:40:52 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 Dqn_I64ToStr ( i64 value , char * buf , i32 buf_size )
2017-05-01 06:40:52 +00:00
{
2018-08-05 07:30:54 +00:00
bool valid_buf = true ;
if ( ! buf | | buf_size = = 0 ) valid_buf = false ;
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
if ( value = = 0 )
{
2018-08-05 07:30:54 +00:00
if ( valid_buf )
2018-06-11 06:26:57 +00:00
{
buf [ 0 ] = ' 0 ' ;
buf [ 1 ] = 0 ;
}
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
return 1 ;
}
2018-08-05 07:30:54 +00:00
i32 char_index = 0 ;
2018-06-11 06:26:57 +00:00
bool negative = false ;
if ( value < 0 ) negative = true ;
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
if ( negative )
{
2018-08-05 07:30:54 +00:00
if ( valid_buf ) buf [ char_index ] = ' - ' ;
char_index + + ;
2018-06-11 06:26:57 +00:00
}
2017-05-01 06:40:52 +00:00
2018-08-05 07:30:54 +00:00
bool last_digit_decremented = false ;
2018-06-11 06:26:57 +00:00
i64 val = DQN_ABS ( value ) ;
if ( val < 0 )
{
// TODO(doyle): This will occur if we are checking the smallest number
// possible in i64 since the range of negative numbers is one more than
// it is for positives, so ABS will fail.
2018-08-05 07:30:54 +00:00
last_digit_decremented = true ;
2018-06-11 06:26:57 +00:00
val = DQN_ABS ( val - 1 ) ;
DQN_ASSERT ( val > = 0 ) ;
}
2017-05-01 06:40:52 +00:00
2018-08-05 07:30:54 +00:00
if ( valid_buf )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
if ( last_digit_decremented )
2018-06-11 06:26:57 +00:00
{
i64 rem = ( val % 10 ) + 1 ;
2018-08-05 07:30:54 +00:00
buf [ char_index + + ] = ( u8 ) rem + ' 0 ' ;
2018-06-11 06:26:57 +00:00
val / = 10 ;
}
2017-05-01 06:40:52 +00:00
2018-08-05 07:30:54 +00:00
while ( val ! = 0 & & char_index < buf_size )
2018-06-11 06:26:57 +00:00
{
i64 rem = val % 10 ;
2018-08-05 07:30:54 +00:00
buf [ char_index + + ] = ( u8 ) rem + ' 0 ' ;
2018-06-11 06:26:57 +00:00
val / = 10 ;
}
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
// NOTE(doyle): If string is negative, we only want to reverse starting
// from the second character, so we don't put the negative sign at the
// end
if ( negative )
{
2018-08-05 07:30:54 +00:00
DqnStr_Reverse ( buf + 1 , char_index - 1 ) ;
2018-06-11 06:26:57 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
DqnStr_Reverse ( buf , char_index ) ;
2018-06-11 06:26:57 +00:00
}
}
else
{
while ( val ! = 0 )
{
val / = 10 ;
2018-08-05 07:30:54 +00:00
char_index + + ;
2018-06-11 06:26:57 +00:00
}
}
2017-04-09 06:26:08 +00:00
2018-08-05 07:30:54 +00:00
buf [ char_index ] = 0 ;
return char_index ;
2017-04-09 06:26:08 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i64 Dqn_StrToI64 ( char const * buf , i64 buf_size )
2017-04-09 06:26:08 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! buf | | buf_size = = 0 ) return 0 ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
i64 index = 0 ;
while ( buf [ index ] = = ' ' )
{
index + + ;
}
2017-04-09 06:26:08 +00:00
2018-08-05 07:30:54 +00:00
bool is_minus = false ;
2018-06-11 06:26:57 +00:00
if ( buf [ index ] = = ' - ' | | buf [ index ] = = ' + ' )
{
2018-08-05 07:30:54 +00:00
if ( buf [ index ] = = ' - ' ) is_minus = true ;
2018-06-11 06:26:57 +00:00
index + + ;
}
else if ( ! DqnChar_IsDigit ( buf [ index ] ) )
{
return 0 ;
}
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
i64 result = 0 ;
2018-08-05 07:30:54 +00:00
for ( auto i = index ; i < buf_size ; i + + )
2018-06-11 06:26:57 +00:00
{
if ( DqnChar_IsDigit ( buf [ i ] ) )
{
result * = 10 ;
result + = ( buf [ i ] - ' 0 ' ) ;
}
else
{
break ;
}
}
2017-05-01 06:40:52 +00:00
2018-08-05 07:30:54 +00:00
if ( is_minus ) result * = - 1 ;
2017-04-09 06:26:08 +00:00
2018-05-17 11:19:58 +00:00
return result ;
2017-04-09 06:26:08 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE f32 Dqn_StrToF32 ( char const * buf , i64 buf_size )
2017-04-09 06:26:08 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! buf | | buf_size = = 0 ) return 0 ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
i64 index = 0 ;
2018-08-05 07:30:54 +00:00
bool is_minus = false ;
2018-06-11 06:26:57 +00:00
if ( buf [ index ] = = ' - ' )
{
index + + ;
2018-08-05 07:30:54 +00:00
is_minus = true ;
2018-06-11 06:26:57 +00:00
}
2017-04-09 06:26:08 +00:00
2018-08-05 07:30:54 +00:00
bool is_past_decimal = false ;
i64 num_digits_after_decimal = 0 ;
i64 raw_num = 0 ;
2017-04-09 06:26:08 +00:00
2018-08-05 07:30:54 +00:00
f32 digit_shift_val = 1.0f ;
f32 digit_shift_multiplier = 0.1f ;
for ( auto i = index ; i < buf_size ; i + + )
2018-06-11 06:26:57 +00:00
{
char ch = buf [ i ] ;
if ( ch = = ' . ' )
{
2018-08-05 07:30:54 +00:00
is_past_decimal = true ;
2018-06-11 06:26:57 +00:00
continue ;
}
// Handle scientific notation
else if ( ch = = ' e ' )
{
2018-08-05 07:30:54 +00:00
bool digit_shift_is_positive = true ;
if ( i < buf_size )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
if ( buf [ i + 1 ] = = ' - ' ) digit_shift_is_positive = false ;
2018-06-11 06:26:57 +00:00
DQN_ASSERT ( buf [ i + 1 ] = = ' - ' | | buf [ i + 1 ] = = ' + ' ) ;
i + = 2 ;
}
2017-04-09 05:08:31 +00:00
2018-08-05 07:30:54 +00:00
i32 exponent_pow = 0 ;
bool scientific_notation = false ;
while ( i < buf_size )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
scientific_notation = true ;
char exponent_char = buf [ i ] ;
if ( DqnChar_IsDigit ( exponent_char ) )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
exponent_pow * = 10 ;
exponent_pow + = ( buf [ i ] - ' 0 ' ) ;
2018-06-11 06:26:57 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
i = buf_size ;
2018-06-11 06:26:57 +00:00
}
2017-04-09 06:53:24 +00:00
2018-06-11 06:26:57 +00:00
i + + ;
}
2017-05-25 15:10:32 +00:00
2018-06-11 06:26:57 +00:00
// NOTE(doyle): If exponent not specified but this branch occurred,
// the float string has a malformed scientific notation in the
// string, i.e. "e" followed by no number.
2018-08-05 07:30:54 +00:00
DQN_ASSERT ( scientific_notation ) ;
2017-05-25 15:10:32 +00:00
2018-08-05 07:30:54 +00:00
if ( digit_shift_is_positive )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
num_digits_after_decimal - = exponent_pow ;
2018-06-11 06:26:57 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
num_digits_after_decimal + = exponent_pow ;
2018-06-11 06:26:57 +00:00
}
}
else if ( DqnChar_IsDigit ( ch ) )
{
2018-08-05 07:30:54 +00:00
num_digits_after_decimal + = ( i32 ) is_past_decimal ;
raw_num * = 10 ;
raw_num + = ( ch - ' 0 ' ) ;
2018-06-11 06:26:57 +00:00
}
else
{
break ;
}
}
2017-06-08 06:41:24 +00:00
2018-08-05 07:30:54 +00:00
for ( isize i = 0 ; i < num_digits_after_decimal ; i + + )
digit_shift_val * = digit_shift_multiplier ;
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
f32 result = ( f32 ) raw_num ;
if ( num_digits_after_decimal > 0 ) result * = digit_shift_val ;
if ( is_minus ) result * = - 1 ;
2017-06-08 06:41:24 +00:00
2018-05-17 11:19:58 +00:00
return result ;
2017-06-08 06:41:24 +00:00
}
2018-06-11 06:26:57 +00:00
/*
Encoding
The following byte sequences are used to represent a character . The sequence
to be used depends on the UCS code number of the character :
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
The extra 1 ' s are the headers used to identify the string as a UTF - 8 string .
UCS [ 0x00000000 , 0x0000007F ] - > UTF - 8 0 xxxxxxx
UCS [ 0x00000080 , 0x000007FF ] - > UTF - 8 110 xxxxx 10 xxxxxx
UCS [ 0x00000800 , 0x0000FFFF ] - > UTF - 8 1110 xxxx 10 xxxxxx 10 xxxxxx
UCS [ 0x00010000 , 0x001FFFFF ] - > UTF - 8 11110 xxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
UCS [ 0x00200000 , 0x03FFFFFF ] - > N / A 111110 xx 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
UCS [ 0x04000000 , 0x7FFFFFFF ] - > N / A 1111110 x 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx 10 xxxxxx
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
The xxx bit positions are filled with the bits of the character code number
in binary representation . Only the shortest possible multibyte sequence
which can represent the code number of the character can be used .
The UCS code values 0xd800 – 0xdfff ( UTF - 16 surrogates ) as well as 0xfffe and
0xffff ( UCS noncharacters ) should not appear in conforming UTF - 8 streams .
*/
DQN_FILE_SCOPE u32 Dqn_UCSToUTF8 ( u32 * dest , u32 character )
2017-04-09 06:26:08 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ! dest ) return 0 ;
2017-04-09 06:26:08 +00:00
2018-08-05 07:30:54 +00:00
u8 * byte_ptr = ( u8 * ) dest ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// Character is within ASCII range, so it's an ascii character
// UTF Bit Arrangement: 0xxxxxxx
// Character : 0xxxxxxx
if ( character > = 0 & & character < 0x80 )
{
2018-08-05 07:30:54 +00:00
byte_ptr [ 0 ] = ( u8 ) character ;
2018-06-11 06:26:57 +00:00
return 1 ;
}
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
// UTF Header Bits : 11000000 00xxxxxx
// UTF Bit Arrangement: 000xxxxx 00xxxxxx
// Character : 00000xxx xxxxxxxx
if ( character < 0x800 )
{
// Add the 2nd byte, 6 bits, OR the 0xC0 (11000000) header bits
2018-08-05 07:30:54 +00:00
byte_ptr [ 1 ] = ( u8 ) ( ( character > > 6 ) | 0xC0 ) ;
2017-05-01 06:40:52 +00:00
2018-06-11 06:26:57 +00:00
// Add the 1st byte, 6 bits, plus the 0x80 (10000000) header bits
2018-08-05 07:30:54 +00:00
byte_ptr [ 0 ] = ( u8 ) ( ( character & 0x3F ) | 0x80 ) ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
return 2 ;
}
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// UTF Header Bits : 11100000 10000000 10000000
// UTF Bit Arrangement : 0000xxxx 00xxxxxx 00xxxxxx
// Character : 00000000 xxxxxxxx xxxxxxxx
if ( character < 0x10000 )
{
// Add the 3rd byte, 4 bits, OR the 0xE0 (11100000) header bits
2018-08-05 07:30:54 +00:00
byte_ptr [ 2 ] = ( u8 ) ( ( character > > 12 ) | 0xE0 ) ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// Add the 2nd byte, 6 bits, OR the 0x80 (10000000) header bits
2018-08-05 07:30:54 +00:00
byte_ptr [ 1 ] = ( u8 ) ( ( character > > 6 ) | 0x80 ) ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// Add the 1st byte, 6 bits, plus the 0x80 (10000000) header bits
2018-08-05 07:30:54 +00:00
byte_ptr [ 0 ] = ( u8 ) ( ( character & 0x3F ) | 0x80 ) ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
return 3 ;
}
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// UTF Header Bits : 11110000 10000000 10000000 10000000
// UTF Bit Arrangement : 00000xxx 00xxxxxx 00xxxxxx 00xxxxxx
// Character : 00000000 00000xxx xxxxxxxx xxxxxxxx
if ( character < 0x110000 )
{
// Add the 4th byte, 3 bits, OR the 0xF0 (11110000) header bits
2018-08-05 07:30:54 +00:00
byte_ptr [ 3 ] = ( u8 ) ( ( character > > 18 ) | 0xF0 ) ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// Add the 3rd byte, 6 bits, OR the 0x80 (10000000) header bits
2018-08-05 07:30:54 +00:00
byte_ptr [ 2 ] = ( u8 ) ( ( ( character > > 12 ) & 0x3F ) | 0x80 ) ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// Add the 2nd byte, 6 bits, plus the 0x80 (10000000) header bits
2018-08-05 07:30:54 +00:00
byte_ptr [ 1 ] = ( u8 ) ( ( ( character > > 6 ) & 0x3F ) | 0x80 ) ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// Add the 2nd byte, 6 bits, plus the 0x80 (10000000) header bits
2018-08-05 07:30:54 +00:00
byte_ptr [ 0 ] = ( u8 ) ( ( character & 0x3F ) | 0x80 ) ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
return 4 ;
}
return 0 ;
2017-04-09 06:26:08 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE u32 Dqn_UTF8ToUCS ( u32 * dest , u32 character )
2017-06-08 06:41:24 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ! dest ) return 0 ;
2017-06-08 06:41:24 +00:00
2018-06-11 06:26:57 +00:00
const u32 HEADER_BITS_4_BYTES = 0xF0808080u ;
const u32 HEADER_BITS_3_BYTES = 0xE08080u ;
const u32 HEADER_BITS_2_BYTES = 0xC000u ;
const u32 HEADER_BITS_1_BYTE = 0x80u ;
2017-06-08 06:41:24 +00:00
2018-06-11 06:26:57 +00:00
// UTF Header Bits : 11110000 10000000 10000000 10000000
// UTF Bit Arrangement : 00000xxx 00xxxxxx 00xxxxxx 00xxxxxx
// UCS : 00000000 00000xxx xxxxxxxx xxxxxxxx
if ( ( character & HEADER_BITS_4_BYTES ) = = HEADER_BITS_4_BYTES )
{
2018-08-05 07:30:54 +00:00
u32 utf_without_header = HEADER_BITS_4_BYTES ^ character ;
2017-06-08 06:41:24 +00:00
2018-08-05 07:30:54 +00:00
u32 first_byte = utf_without_header & 0x3F ;
u32 second_byte = ( utf_without_header > > 8 ) & 0x3F ;
u32 third_byte = ( utf_without_header > > 16 ) & 0x3F ;
u32 fourth_byte = utf_without_header > > 24 ;
2017-06-08 06:41:24 +00:00
2018-06-11 06:26:57 +00:00
u32 result =
2018-08-05 07:30:54 +00:00
( fourth_byte < < 18 | third_byte < < 12 | second_byte < < 6 | first_byte ) ;
2018-06-11 06:26:57 +00:00
* dest = result ;
2017-06-08 06:41:24 +00:00
2018-06-11 06:26:57 +00:00
return 4 ;
}
2017-06-08 06:41:24 +00:00
2018-06-11 06:26:57 +00:00
// UTF Header Bits : 11100000 10000000 10000000
// UTF Bit Arrangement : 0000xxxx 00xxxxxx 00xxxxxx
// UCS : 00000000 xxxxxxxx xxxxxxxx
if ( ( character & HEADER_BITS_3_BYTES ) = = HEADER_BITS_3_BYTES )
{
2018-08-05 07:30:54 +00:00
u32 utf_without_header = HEADER_BITS_3_BYTES ^ character ;
2017-06-08 06:41:24 +00:00
2018-08-05 07:30:54 +00:00
u32 first_byte = utf_without_header & 0x3F ;
u32 second_byte = ( utf_without_header > > 8 ) & 0x3F ;
u32 third_byte = utf_without_header > > 16 ;
2017-06-08 06:41:24 +00:00
2018-08-05 07:30:54 +00:00
u32 result = ( third_byte < < 12 | second_byte < < 6 | first_byte ) ;
2018-06-11 06:26:57 +00:00
* dest = result ;
2017-06-08 06:41:24 +00:00
2018-06-11 06:26:57 +00:00
return 3 ;
}
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// UTF Header Bits : 11000000 00xxxxxx
// UTF Bit Arrangement: 000xxxxx 00xxxxxx
// UCS : 00000xxx xxxxxxxx
if ( ( character & HEADER_BITS_2_BYTES ) = = HEADER_BITS_2_BYTES )
{
2018-08-05 07:30:54 +00:00
u32 utf_without_header = HEADER_BITS_2_BYTES ^ character ;
2017-07-16 10:47:49 +00:00
2018-08-05 07:30:54 +00:00
u32 first_byte = utf_without_header & 0x3F ;
u32 second_byte = utf_without_header > > 8 ;
2017-04-09 06:26:08 +00:00
2018-08-05 07:30:54 +00:00
u32 result = ( second_byte < < 6 | first_byte ) ;
2018-06-11 06:26:57 +00:00
* dest = result ;
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
return 2 ;
}
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
// Character is within ASCII range, so it's an ascii character
// UTF Bit Arrangement: 0xxxxxxx
// UCS : 0xxxxxxx
if ( ( character & HEADER_BITS_1_BYTE ) = = 0 )
{
2018-08-05 07:30:54 +00:00
u32 first_byte = ( character & 0x3F ) ;
* dest = first_byte ;
2017-07-04 04:22:24 +00:00
2018-06-11 06:26:57 +00:00
return 1 ;
}
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
return 0 ;
2017-04-09 06:26:08 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnSlice < char > DqnStr_RemoveLeadTrailChar ( char const * str , i32 str_len , char lead_char , char trail_char )
2017-04-09 06:26:08 +00:00
{
2018-08-05 07:30:54 +00:00
str = DqnChar_TrimWhitespaceAround ( str , str_len , & str_len ) ;
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
if ( str [ 0 ] = = lead_char )
2018-06-11 06:26:57 +00:00
{
str + + ;
2018-08-05 07:30:54 +00:00
str_len - - ;
2018-06-11 06:26:57 +00:00
}
2018-08-05 07:30:54 +00:00
if ( str [ str_len - 1 ] = = trail_char )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
str_len - - ;
2018-06-11 06:26:57 +00:00
}
2018-08-05 07:30:54 +00:00
str = DqnChar_TrimWhitespaceAround ( str , str_len , & str_len ) ;
DqnSlice < char > result = { ( char * ) str , str_len } ;
2018-05-17 11:19:58 +00:00
return result ;
2017-04-09 06:26:08 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE inline DqnSlice < char > DqnStr_RemoveLeadTrailBraces ( char const * str , i32 str_len )
2017-06-20 09:19:52 +00:00
{
2018-08-05 07:30:54 +00:00
DqnSlice < char > result = DqnStr_RemoveLeadTrailChar ( str , str_len , ' { ' , ' } ' ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-06-20 09:19:52 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE inline DqnSlice < char > DqnStr_RemoveLeadTrailQuotes ( char const * str , i32 str_len )
2017-04-09 06:26:08 +00:00
{
2018-08-05 07:30:54 +00:00
DqnSlice < char > result = DqnStr_RemoveLeadTrailChar ( str , str_len , ' " ' , ' " ' ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-04-09 06:26:08 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE inline DqnSlice < char > DqnStr_RemoveLeadTrailBraces ( DqnSlice < char > slice )
2017-04-09 06:26:08 +00:00
{
2018-06-11 06:26:57 +00:00
DqnSlice < char > result = DqnStr_RemoveLeadTrailBraces ( slice . data , slice . len ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-04-09 06:26:08 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE inline DqnSlice < char > DqnStr_RemoveLeadTrailQuotes ( DqnSlice < char > slice )
2017-04-09 06:26:08 +00:00
{
2018-06-11 06:26:57 +00:00
DqnSlice < char > result = DqnStr_RemoveLeadTrailQuotes ( slice . data , slice . len ) ;
return result ;
2017-04-09 06:26:08 +00:00
}
2018-06-11 06:26:57 +00:00
// #DqnWChar
// =================================================================================================
DQN_FILE_SCOPE bool DqnWChar_IsDigit ( wchar_t c )
2017-04-09 06:26:08 +00:00
{
2018-06-11 06:26:57 +00:00
if ( c > = L ' 0 ' & & c < = L ' 9 ' ) return true ;
return false ;
2017-04-09 06:26:08 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE wchar_t DqnWChar_ToLower ( wchar_t c )
2017-05-15 07:45:53 +00:00
{
2018-06-11 06:26:57 +00:00
if ( c > = L ' A ' & & c < = L ' Z ' )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
i32 shift_offset = L ' a ' - L ' A ' ;
return ( c + ( wchar_t ) shift_offset ) ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
return c ;
}
DQN_FILE_SCOPE wchar_t * DqnWChar_SkipWhitespace ( wchar_t * ptr )
{
while ( ptr & & ( * ptr = = ' ' | | * ptr = = ' \r ' | | * ptr = = ' \n ' ) ) ptr + + ;
return ptr ;
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE wchar_t * DqnWChar_FindLastChar ( wchar_t * ptr , wchar_t ch , i32 len , u32 * len_to_char )
2018-06-11 06:26:57 +00:00
{
for ( i32 i = len - 1 ; i > = 0 ; i - - )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ptr [ i ] = = ch )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
if ( len_to_char ) * len_to_char = ( u32 ) len - i ;
2018-06-11 06:26:57 +00:00
return & ptr [ i ] ;
2018-05-17 11:19:58 +00:00
}
}
2018-06-11 06:26:57 +00:00
return nullptr ;
2017-05-15 07:45:53 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnWChar_GetNextLine ( wchar_t * ptr , i32 * line_len )
2017-04-09 06:26:08 +00:00
{
2018-06-11 06:26:57 +00:00
i32 len = 0 ;
ptr = DqnWChar_SkipWhitespace ( ptr ) ;
2017-12-06 08:08:49 +00:00
2018-06-11 06:26:57 +00:00
// Advance pointer to first new line
while ( ptr & & * ptr ! = 0 & & * ptr ! = ' \r ' & & * ptr ! = ' \n ' )
{
ptr + + ;
len + + ;
}
2017-04-09 06:26:08 +00:00
2018-06-11 06:26:57 +00:00
if ( ! ptr | | * ptr = = 0 ) return - 1 ;
2017-12-06 08:08:49 +00:00
2018-06-11 06:26:57 +00:00
// Destroy all new lines
2018-08-05 07:30:54 +00:00
i32 extra_chars = 0 ;
2018-06-11 06:26:57 +00:00
while ( ptr & & ( * ptr = = ' \r ' | | * ptr = = ' \n ' | | * ptr = = ' ' ) )
{
* ptr = 0 ;
ptr + + ;
2018-08-05 07:30:54 +00:00
extra_chars + + ;
2018-06-11 06:26:57 +00:00
}
2017-04-09 06:26:08 +00:00
2018-08-05 07:30:54 +00:00
if ( line_len ) * line_len = len ;
return len + extra_chars ;
2017-04-09 06:26:08 +00:00
}
2018-06-11 06:26:57 +00:00
// #DqnWStr
2018-01-18 09:25:44 +00:00
// =================================================================================================
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE i32 DqnWStr_Cmp ( wchar_t const * a , wchar_t const * b )
2017-04-17 12:48:20 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ! a & & ! b ) return - 1 ;
if ( ! a ) return - 1 ;
if ( ! b ) return - 1 ;
2017-04-17 12:48:20 +00:00
2018-08-05 07:30:54 +00:00
const wchar_t * a_ptr = a ;
const wchar_t * b_ptr = b ;
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
while ( ( * a_ptr ) = = ( * b_ptr ) )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! ( * a_ptr ) ) return 0 ;
a_ptr + + ;
b_ptr + + ;
2018-05-17 11:19:58 +00:00
}
2018-01-18 05:01:37 +00:00
2018-08-05 07:30:54 +00:00
return ( ( ( * a_ptr ) < ( * b_ptr ) ) ? - 1 : 1 ) ;
2018-01-18 05:01:37 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnWStr_FindFirstOccurence ( wchar_t const * src , i32 src_len ,
wchar_t const * find , i32 find_len )
2018-01-18 05:01:37 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ! src | | ! find ) return - 1 ;
2018-08-05 07:30:54 +00:00
if ( src_len = = 0 | | find_len = = 0 ) return - 1 ;
if ( src_len < find_len ) return - 1 ;
2018-01-18 05:01:37 +00:00
2018-08-05 07:30:54 +00:00
for ( i32 index_in_src = 0 ; index_in_src < src_len ; index_in_src + + )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
// NOTE: As we scan through, if the src string we index into becomes
// shorter than the substring we're checking then the substring is not
// contained in the src string.
2018-08-05 07:30:54 +00:00
i32 remaining_len_in_src = src_len - index_in_src ;
if ( remaining_len_in_src < find_len ) break ;
2018-01-18 05:01:37 +00:00
2018-08-05 07:30:54 +00:00
const wchar_t * src_substr = & src [ index_in_src ] ;
2018-06-11 06:26:57 +00:00
i32 index = 0 ;
for ( ; ; )
{
2018-08-05 07:30:54 +00:00
if ( DqnWChar_ToLower ( src_substr [ index ] ) = =
2018-06-11 06:26:57 +00:00
DqnWChar_ToLower ( find [ index ] ) )
{
index + + ;
2018-08-05 07:30:54 +00:00
if ( index > = find_len | | ! find [ index ] )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
return index_in_src ;
2018-06-11 06:26:57 +00:00
}
}
else
{
break ;
}
}
2018-05-17 11:19:58 +00:00
}
2018-01-18 05:01:37 +00:00
2018-06-11 06:26:57 +00:00
// NOTE(doyle): We have early exit, if we reach here, then the substring was
// not found.
return - 1 ;
2018-01-18 05:01:37 +00:00
}
2017-04-17 12:48:20 +00:00
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE bool DqnWStr_HasSubstring ( wchar_t const * src , i32 src_len ,
wchar_t const * find , i32 find_len )
2017-04-09 05:08:31 +00:00
{
2018-08-05 07:30:54 +00:00
if ( DqnWStr_FindFirstOccurence ( src , src_len , find , find_len ) = = - 1 )
2018-06-11 06:26:57 +00:00
return false ;
2017-04-09 05:08:31 +00:00
2018-06-11 06:26:57 +00:00
return true ;
2017-04-09 05:08:31 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE i32 DqnWStr_Len ( wchar_t const * a )
2017-04-09 05:08:31 +00:00
{
2018-06-11 06:26:57 +00:00
i32 result = 0 ;
while ( a & & a [ result ] ) result + + ;
return result ;
2017-04-09 05:08:31 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE i32 DqnWStr_LenDelimitWith ( wchar_t const * a , wchar_t delimiter )
2017-10-28 08:58:09 +00:00
{
2018-06-11 06:26:57 +00:00
i32 result = 0 ;
while ( a & & a [ result ] & & a [ result ] ! = delimiter ) result + + ;
return result ;
2017-10-28 08:58:09 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void DqnWStr_Reverse ( wchar_t * buf , i32 buf_size )
2017-10-28 08:58:09 +00:00
{
2018-06-11 06:26:57 +00:00
if ( ! buf ) return ;
2018-08-05 07:30:54 +00:00
i32 mid = buf_size / 2 ;
2018-06-11 06:26:57 +00:00
for ( i32 i = 0 ; i < mid ; i + + )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
wchar_t tmp = buf [ i ] ;
2018-08-05 07:30:54 +00:00
buf [ i ] = buf [ ( buf_size - 1 ) - i ] ;
buf [ ( buf_size - 1 ) - i ] = tmp ;
2018-05-17 11:19:58 +00:00
}
2017-10-28 08:58:09 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 Dqn_WStrToI32 ( wchar_t const * buf , i32 buf_size )
2017-10-28 08:58:09 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! buf | | buf_size = = 0 ) return 0 ;
2017-10-28 08:58:09 +00:00
2018-06-11 06:26:57 +00:00
i32 index = 0 ;
2018-08-05 07:30:54 +00:00
bool is_minus = false ;
2018-06-11 06:26:57 +00:00
if ( buf [ index ] = = L ' - ' | | buf [ index ] = = L ' + ' )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
if ( buf [ index ] = = L ' - ' ) is_minus = true ;
2018-06-11 06:26:57 +00:00
index + + ;
2018-05-17 11:19:58 +00:00
}
2018-06-11 06:26:57 +00:00
else if ( ! DqnWChar_IsDigit ( buf [ index ] ) )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
return 0 ;
2018-05-17 11:19:58 +00:00
}
2017-10-28 08:58:09 +00:00
2018-06-11 06:26:57 +00:00
i32 result = 0 ;
2018-08-05 07:30:54 +00:00
for ( i32 i = index ; i < buf_size ; i + + )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
if ( DqnWChar_IsDigit ( buf [ i ] ) )
{
result * = 10 ;
result + = ( buf [ i ] - L ' 0 ' ) ;
}
else
{
break ;
}
2018-05-17 11:19:58 +00:00
}
2017-10-28 08:58:09 +00:00
2018-08-05 07:30:54 +00:00
if ( is_minus ) result * = - 1 ;
2018-06-11 06:26:57 +00:00
return result ;
2017-10-28 08:58:09 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 Dqn_I32ToWstr ( i32 value , wchar_t * buf , i32 buf_size )
2018-01-18 05:01:37 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! buf | | buf_size = = 0 ) return 0 ;
2018-01-18 05:01:37 +00:00
2018-06-11 06:26:57 +00:00
if ( value = = 0 )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
buf [ 0 ] = L ' 0 ' ;
return 0 ;
2018-05-17 11:19:58 +00:00
}
2018-01-18 05:01:37 +00:00
2018-06-11 06:26:57 +00:00
// NOTE(doyle): Max 32bit integer (+-)2147483647
2018-08-05 07:30:54 +00:00
i32 char_index = 0 ;
2018-06-11 06:26:57 +00:00
bool negative = false ;
if ( value < 0 ) negative = true ;
2018-01-18 05:01:37 +00:00
2018-08-05 07:30:54 +00:00
if ( negative ) buf [ char_index + + ] = L ' - ' ;
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
i32 val = DQN_ABS ( value ) ;
2018-08-05 07:30:54 +00:00
while ( val ! = 0 & & char_index < buf_size )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
i32 rem = val % 10 ;
2018-08-05 07:30:54 +00:00
buf [ char_index + + ] = ( u8 ) rem + ' 0 ' ;
2018-06-11 06:26:57 +00:00
val / = 10 ;
}
// NOTE(doyle): If string is negative, we only want to reverse starting
// from the second character, so we don't put the negative sign at the end
if ( negative )
{
2018-08-05 07:30:54 +00:00
DqnWStr_Reverse ( buf + 1 , char_index - 1 ) ;
2018-05-17 11:19:58 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
DqnWStr_Reverse ( buf , char_index ) ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
return char_index ;
2017-04-09 05:08:31 +00:00
}
2018-06-11 06:26:57 +00:00
// #DqnRnd
// =================================================================================================
// Public Domain library with thanks to Mattias Gustavsson
// https://github.com/mattiasgustavsson/libs/blob/master/docs/rnd.md
2018-01-29 08:03:20 +00:00
2018-06-11 06:26:57 +00:00
// Convert a randomized u32 value to a float value x in the range 0.0f <= x
// < 1.0f. Contributed by Jonatan Hedborg
2018-01-29 08:03:20 +00:00
2018-06-11 06:26:57 +00:00
// NOTE: This is to abide to strict aliasing rules.
2018-06-11 09:16:54 +00:00
union DqnRnd__U32F32
2017-04-09 05:08:31 +00:00
{
2018-06-11 06:26:57 +00:00
u32 unsigned32 ;
f32 float32 ;
} ;
2018-06-11 09:16:54 +00:00
FILE_SCOPE f32 DqnRnd__F32NormalizedFromU32 ( u32 value )
2018-06-11 06:26:57 +00:00
{
u32 exponent = 127 ;
u32 mantissa = value > > 9 ;
2018-06-11 09:16:54 +00:00
union DqnRnd__U32F32 uf ;
2018-06-11 06:26:57 +00:00
uf . unsigned32 = ( exponent < < 23 | mantissa ) ;
return uf . float32 - 1.0f ;
2017-04-09 05:08:31 +00:00
}
2018-06-11 09:16:54 +00:00
FILE_SCOPE u64 DqnRnd__Murmur3Avalanche64 ( u64 h )
2017-10-28 08:58:09 +00:00
{
2018-06-11 06:26:57 +00:00
h ^ = h > > 33 ;
h * = 0xff51afd7ed558ccd ;
h ^ = h > > 33 ;
h * = 0xc4ceb9fe1a85ec53 ;
h ^ = h > > 33 ;
return h ;
}
2017-10-28 08:58:09 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_UNIX)
2018-06-11 06:26:57 +00:00
# include <x86intrin.h> // __rdtsc
# endif
2017-10-28 08:58:09 +00:00
2018-06-11 09:16:54 +00:00
FILE_SCOPE u32 DqnRnd__MakeSeed ( )
2018-06-11 06:26:57 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_PLATFORM_IMPLEMENTATION) && (defined(DQN_IS_WIN32) || defined(DQN_IS_UNIX))
2018-08-05 07:30:54 +00:00
i64 num_clock_cycles = __rdtsc ( ) ;
return ( u32 ) num_clock_cycles ;
2018-06-11 06:26:57 +00:00
# else
2018-07-04 14:47:11 +00:00
DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
2018-06-11 06:26:57 +00:00
return 0 ;
# endif
2017-10-28 08:58:09 +00:00
}
2018-06-11 09:16:54 +00:00
DqnRndPCG : : DqnRndPCG ( )
2017-10-28 08:58:09 +00:00
{
2018-06-11 09:16:54 +00:00
u32 seed = DqnRnd__MakeSeed ( ) ;
* this = DqnRndPCG ( seed ) ;
2018-06-11 06:26:57 +00:00
}
2018-05-17 11:19:58 +00:00
2018-06-11 09:16:54 +00:00
DqnRndPCG : : DqnRndPCG ( u32 seed )
2018-06-11 06:26:57 +00:00
{
2018-06-11 09:16:54 +00:00
u64 value = ( ( ( u64 ) seed ) < < 1ULL ) | 1ULL ;
value = DqnRnd__Murmur3Avalanche64 ( value ) ;
2018-06-11 06:26:57 +00:00
this - > state [ 0 ] = 0U ;
this - > state [ 1 ] = ( value < < 1ULL ) | 1ULL ;
this - > Next ( ) ;
2018-06-11 09:16:54 +00:00
this - > state [ 0 ] + = DqnRnd__Murmur3Avalanche64 ( value ) ;
2018-06-11 06:26:57 +00:00
this - > Next ( ) ;
}
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
u32 DqnRndPCG : : Next ( )
2017-04-09 05:08:31 +00:00
{
2018-06-11 06:26:57 +00:00
u64 oldstate = this - > state [ 0 ] ;
this - > state [ 0 ] = oldstate * 0x5851f42d4c957f2dULL + this - > state [ 1 ] ;
u32 xorshifted = ( u32 ) ( ( ( oldstate > > 18ULL ) ^ oldstate ) > > 27ULL ) ;
u32 rot = ( u32 ) ( oldstate > > 59ULL ) ;
return ( xorshifted > > rot ) | ( xorshifted < < ( ( - ( i32 ) rot ) & 31 ) ) ;
}
2017-04-09 05:08:31 +00:00
2018-06-11 06:26:57 +00:00
f32 DqnRndPCG : : Nextf ( )
{
2018-06-11 09:16:54 +00:00
f32 result = DqnRnd__F32NormalizedFromU32 ( this - > Next ( ) ) ;
2018-06-11 06:26:57 +00:00
return result ;
2017-04-09 05:08:31 +00:00
}
2018-06-11 06:26:57 +00:00
i32 DqnRndPCG : : Range ( i32 min , i32 max )
2018-01-31 13:08:42 +00:00
{
2018-06-11 06:26:57 +00:00
i32 const range = ( max - min ) + 1 ;
if ( range < = 0 ) return min ;
2018-01-31 13:08:42 +00:00
2018-06-11 06:26:57 +00:00
i32 const value = ( i32 ) ( this - > Nextf ( ) * range ) ;
i32 result = min + value ;
return result ;
}
2018-01-31 13:08:42 +00:00
2018-06-11 06:26:57 +00:00
// #Dqn_*
// =================================================================================================
DQN_FILE_SCOPE inline bool Dqn_BitIsSet ( u32 bits , u32 flag )
{
bool result = ( ( bits & flag ) = = flag ) ;
2018-05-17 11:19:58 +00:00
return result ;
2018-01-31 13:08:42 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE inline u32 Dqn_BitSet ( u32 bits , u32 flag )
2017-04-17 13:12:10 +00:00
{
2018-06-11 06:26:57 +00:00
u32 result = ( bits | flag ) ;
return result ;
}
2017-04-17 13:12:10 +00:00
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE inline u32 Dqn_BitUnset ( u32 bits , u32 flag )
{
u32 result = ( bits & ~ flag ) ;
return result ;
2017-05-03 14:22:30 +00:00
}
2018-06-11 06:26:57 +00:00
DQN_FILE_SCOPE inline u32 Dqn_BitToggle ( u32 bits , u32 flag )
2017-10-28 08:58:09 +00:00
{
2018-06-11 06:26:57 +00:00
u32 result = ( bits ^ flag ) ;
return result ;
}
2017-10-28 08:58:09 +00:00
2018-06-11 06:26:57 +00:00
// #DqnString Impleemntation
// =================================================================================================
2018-08-05 07:30:54 +00:00
void DqnString : : Reserve ( int new_max )
2018-07-29 06:15:23 +00:00
{
2018-08-05 07:30:54 +00:00
if ( new_max > = this - > max )
2018-07-29 06:15:23 +00:00
{
2018-08-29 15:04:56 +00:00
char * new_ptr = ( this - > str ) ? static_cast < char * > ( allocator - > Realloc ( this - > str , new_max * sizeof ( this - > str [ 0 ] ) ) )
: static_cast < char * > ( allocator - > Malloc ( new_max * sizeof ( this - > str [ 0 ] ) ) ) ;
2018-07-29 06:15:23 +00:00
2018-08-05 07:30:54 +00:00
DQN_ALWAYS_ASSERT ( new_ptr ) ;
this - > str = new_ptr ;
this - > max = new_max ;
2018-07-29 06:15:23 +00:00
}
}
2018-07-29 14:34:00 +00:00
void DqnString : : Append ( char const * src , int len_ )
2018-07-29 06:15:23 +00:00
{
if ( len_ = = - 1 )
len_ = DqnStr_Len ( src ) ;
Reserve ( this - > len + len_ ) ;
DqnMem_Copy ( this - > str + this - > len , src , len_ ) ;
this - > len + = len_ ;
this - > str [ this - > len ] = 0 ;
}
2018-08-18 06:14:29 +00:00
// #DqnLogger Implementation
// =================================================================================================
2018-09-10 12:49:17 +00:00
char const * DqnLogger : : LogNoContext ( Type type , char const * fmt , . . . )
{
Context empty_context = { } ;
va_list va ;
va_start ( va , fmt ) ;
char const * result = this - > LogVA ( type , empty_context , fmt , va ) ;
va_end ( va ) ;
return result ;
}
2018-09-13 13:58:48 +00:00
char const * DqnLogger : : Log ( Type type , Context log_context , char const * fmt , . . . )
2018-08-18 06:14:29 +00:00
{
va_list va ;
va_start ( va , fmt ) ;
2018-09-10 12:49:17 +00:00
char const * result = this - > LogVA ( type , log_context , fmt , va ) ;
va_end ( va ) ;
return result ;
}
2018-09-13 13:58:48 +00:00
char const * DqnLogger : : LogVA ( Type type , Context log_context , char const * fmt , va_list va )
2018-09-10 12:49:17 +00:00
{
if ( ! this - > allocator . block )
{
this - > allocator . LazyInit ( DqnMemStack : : MINIMUM_BLOCK_SIZE , Dqn : : ZeroMem : : No , 0 , DqnMemTracker : : None ) ;
}
if ( this - > log_buf . len = = 0 )
{
this - > log_buf_index = 0 ;
this - > log_buf . len = 2048 ;
this - > log_buf . data = ( char * ) this - > allocator . Push_ ( sizeof ( * this - > log_buf . data ) * this - > log_buf . len ) ;
}
bool const have_context = ( log_context . filename_len > 0 ) ;
char const * filename = nullptr ;
for ( isize i = ( log_context . filename_len - 1 ) ; i > = 0 ; i - - )
{
if ( log_context . filename [ i ] = = ' \\ ' )
{
filename = log_context . filename + ( i + 1 ) ;
break ;
}
}
if ( ! filename ) filename = log_context . filename ;
// Determine len
int required_len = 0 ;
# if defined(DQN_PLATFORM_HEADER) && defined(DQN_IS_WIN32)
SYSTEMTIME sys_time = { } ;
GetLocalTime ( & sys_time ) ;
2018-12-15 15:23:04 +00:00
required_len + = stbsp_snprintf ( nullptr , 0 , " %02d-%02d-%02d %02d:%02d:%02d " , sys_time . wYear % 100 , sys_time . wMonth , sys_time . wDay , sys_time . wHour , sys_time . wMinute , sys_time . wSecond ) ;
2018-09-10 12:49:17 +00:00
# endif
if ( have_context )
{
2018-12-15 15:23:04 +00:00
required_len + = stbsp_snprintf ( nullptr , 0 , " %s %05d %s %s: " , filename , log_context . line_num , TypePrefix ( type ) , log_context . function ) ;
2018-09-10 12:49:17 +00:00
}
2018-08-18 06:14:29 +00:00
2018-12-12 13:36:19 +00:00
required_len + = stbsp_snprintf ( nullptr , 0 , " %s " , this - > log_builder . str ) ;
required_len + = stbsp_vsnprintf ( nullptr , 0 , fmt , va ) ;
2018-09-10 12:49:17 +00:00
required_len + = 2 ; // newline + null byte
2018-08-18 06:14:29 +00:00
2018-09-10 12:49:17 +00:00
// Build string
this - > allocator . SetAllocMode ( DqnMemStack : : AllocMode : : Tail ) ;
char * result = ( char * ) this - > allocator . Push_ ( sizeof ( char ) * required_len ) ;
this - > allocator . SetAllocMode ( DqnMemStack : : AllocMode : : Head ) ;
DQN_DEFER { this - > allocator . Pop ( result ) ; } ;
2018-08-18 06:14:29 +00:00
2018-09-10 12:49:17 +00:00
char * result_ptr = result ;
2018-08-18 06:14:29 +00:00
2018-09-10 12:49:17 +00:00
if ( have_context )
{
# if defined(DQN_PLATFORM_HEADER) && defined(DQN_IS_WIN32)
2018-12-15 15:23:04 +00:00
result_ptr + = stbsp_sprintf ( result_ptr , " %02d-%02d-%02d %02d:%02d:%02d " , sys_time . wYear % 100 , sys_time . wMonth , sys_time . wDay , sys_time . wHour , sys_time . wMinute , sys_time . wSecond ) ;
2018-09-10 12:49:17 +00:00
# endif
2018-12-15 15:23:04 +00:00
result_ptr + = stbsp_sprintf ( result_ptr , " %s %05d %s %s: " , filename , log_context . line_num , TypePrefix ( type ) , log_context . function ) ;
2018-09-10 12:49:17 +00:00
}
2018-12-15 15:23:04 +00:00
DqnMem_Copy ( result_ptr , this - > log_builder . str , this - > log_builder . len ) ;
result_ptr + = this - > log_builder . len ;
result_ptr + = stbsp_vsprintf ( result_ptr , fmt , va ) ;
* result_ptr + + = ' \n ' ;
* result_ptr + + = 0 ;
2018-08-18 06:14:29 +00:00
this - > log_builder . Clear ( ) ;
2018-09-10 12:49:17 +00:00
int buf_len_remaining = this - > log_buf . len - this - > log_buf_index ;
if ( required_len > = buf_len_remaining ) // Bad case, the log line was greater than the buf we allocated
{
// TODO(doyle): Handle the bad case
}
else
{
// TODO(doyle): Write to disk
this - > log_buf_index + = ( required_len - 1 ) ; // make index point at the null byte of the string
DqnMem_Copy ( this - > log_buf . str , result , required_len ) ;
}
2018-08-18 06:14:29 +00:00
2018-09-10 12:49:17 +00:00
if ( this - > no_console ) return result ;
if ( this - > no_print_error & & type = = Type : : Error ) return result ;
if ( this - > no_print_debug & & type = = Type : : Debug ) return result ;
2018-08-18 06:14:29 +00:00
if ( this - > no_print_warning & & type = = Type : : Warning ) return result ;
fprintf ( stderr , " %s " , result ) ;
return result ;
}
2018-06-11 06:26:57 +00:00
// #Dqn
// =================================================================================================
2018-08-05 07:30:54 +00:00
i32 Dqn_GetNumSplits ( char const * src , i32 src_len , char split_char )
2017-10-28 08:58:09 +00:00
{
2018-08-05 07:30:54 +00:00
auto result = Dqn_SplitString ( src , src_len , split_char , nullptr , 0 ) ;
2018-06-11 06:26:57 +00:00
return result ;
2017-10-28 08:58:09 +00:00
}
2018-08-05 07:30:54 +00:00
i32 Dqn_SplitString ( char const * src , i32 src_len , char split_char , DqnSlice < char > * array , i32 size )
2017-10-28 08:58:09 +00:00
{
2018-06-11 06:26:57 +00:00
// TODO(doyle): Const correctness
2018-08-05 07:30:54 +00:00
i32 slice_len = 0 ;
i32 array_index = 0 ;
for ( auto i = 0 ; i < src_len ; i + + )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
char * c = ( char * ) ( src + i ) ;
2018-08-05 07:30:54 +00:00
if ( * c = = split_char )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
DqnSlice < char > slice = { c - slice_len , slice_len } ;
2018-06-11 06:26:57 +00:00
if ( array )
{
2018-08-05 07:30:54 +00:00
if ( array_index < size )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
array [ array_index ] = slice ;
2018-06-11 06:26:57 +00:00
}
}
2018-08-05 07:30:54 +00:00
array_index + + ;
slice_len = 0 ;
2018-06-11 06:26:57 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
slice_len + + ;
2018-05-17 11:19:58 +00:00
}
}
2017-10-28 08:58:09 +00:00
2018-08-05 07:30:54 +00:00
DqnSlice < char > last_slice = { ( char * ) src + src_len - slice_len , slice_len } ;
if ( last_slice . len > 0 & & array_index > 0 )
2018-05-17 11:19:58 +00:00
{
2018-06-11 06:26:57 +00:00
if ( array )
{
2018-08-05 07:30:54 +00:00
if ( array_index < size )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
array [ array_index ] = last_slice ;
2018-06-11 06:26:57 +00:00
}
}
2017-10-28 08:58:09 +00:00
2018-08-05 07:30:54 +00:00
array_index + + ;
2018-05-17 11:19:58 +00:00
}
2017-10-28 08:58:09 +00:00
2018-08-05 07:30:54 +00:00
return array_index ;
2017-10-28 08:58:09 +00:00
}
2018-07-12 14:46:08 +00:00
// TODO(doyle): This should maybe be a tokenizer ...
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnJson DqnJson_Get ( char const * buf , i32 buf_len , char const * find_property , i32 find_property_len )
2018-03-08 15:03:05 +00:00
{
2018-06-11 06:26:57 +00:00
DqnJson result = { } ;
2018-08-05 07:30:54 +00:00
if ( ! buf | | buf_len = = 0 | | ! find_property | | find_property_len = = 0 ) return result ;
2018-07-14 16:17:34 +00:00
2018-07-12 14:46:08 +00:00
char const * tmp = DqnChar_SkipWhitespace ( buf ) ;
2018-08-05 07:30:54 +00:00
buf_len = static_cast < int > ( ( buf + buf_len ) - tmp ) ;
2018-07-12 14:46:08 +00:00
buf = tmp ;
2018-08-05 07:30:54 +00:00
bool const find_structure_in_global_scope = ( find_property_len = = 1 & & ( find_property [ 0 ] = = ' { ' | | find_property [ 0 ] = = ' [ ' ) ) ;
2018-07-17 14:47:13 +00:00
2018-08-05 07:30:54 +00:00
if ( ( buf [ 0 ] = = ' { ' | | buf [ 1 ] = = ' [ ' ) & & ! find_structure_in_global_scope )
2018-07-12 14:46:08 +00:00
{
buf + + ;
2018-08-05 07:30:54 +00:00
buf_len - - ;
2018-07-12 14:46:08 +00:00
}
2018-03-08 15:03:05 +00:00
2018-07-05 02:20:19 +00:00
TryNext :
2018-07-12 14:46:08 +00:00
char const * locate = nullptr ;
2018-08-05 07:30:54 +00:00
for ( i32 index_in_buf = 0 ; index_in_buf < buf_len ; + + index_in_buf )
2018-07-12 14:46:08 +00:00
{
2018-08-05 07:30:54 +00:00
i32 remaining_len_in_src = buf_len - index_in_buf ;
if ( remaining_len_in_src < find_property_len ) break ;
2018-07-12 14:46:08 +00:00
2018-08-05 07:30:54 +00:00
char const * buf_substr = buf + index_in_buf ;
if ( ! find_structure_in_global_scope )
2018-07-12 14:46:08 +00:00
{
2018-08-05 07:30:54 +00:00
if ( buf_substr [ 0 ] = = ' { ' | | buf_substr [ 0 ] = = ' [ ' )
2018-07-12 14:46:08 +00:00
{
2018-08-05 07:30:54 +00:00
int bracket_count = 0 ;
int brace_count = 0 ;
int * search_char_count = nullptr ;
if ( buf_substr [ 0 ] = = ' [ ' )
2018-07-17 14:47:13 +00:00
{
2018-08-05 07:30:54 +00:00
bracket_count + + ;
search_char_count = & bracket_count ;
2018-07-17 14:47:13 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
brace_count + + ;
search_char_count = & brace_count ;
2018-07-17 14:47:13 +00:00
}
2018-07-12 14:46:08 +00:00
2018-08-05 07:30:54 +00:00
for ( + + index_in_buf ; ( * search_char_count ) ! = 0 ; + + index_in_buf )
2018-07-17 14:47:13 +00:00
{
2018-08-05 07:30:54 +00:00
buf_substr = buf + index_in_buf ;
if ( ! buf_substr [ 0 ] )
2018-07-17 14:47:13 +00:00
return result ;
2018-08-05 07:30:54 +00:00
if ( buf_substr [ 0 ] = = ' { ' ) + + brace_count ;
else if ( buf_substr [ 0 ] = = ' } ' ) - - brace_count ;
else if ( buf_substr [ 0 ] = = ' [ ' ) + + bracket_count ;
else if ( buf_substr [ 0 ] = = ' ] ' ) - - bracket_count ;
2018-07-17 14:47:13 +00:00
else continue ;
}
2018-07-12 14:46:08 +00:00
}
}
2018-08-05 07:30:54 +00:00
if ( DqnStr_Cmp ( buf_substr , find_property , find_property_len , Dqn : : IgnoreCase : : No ) = = 0 )
2018-07-12 14:46:08 +00:00
{
2018-08-05 07:30:54 +00:00
locate = buf + index_in_buf ;
2018-07-12 14:46:08 +00:00
break ;
}
}
2018-06-11 06:26:57 +00:00
if ( ! locate ) return result ;
2018-03-08 15:03:05 +00:00
2018-06-11 06:26:57 +00:00
// NOTE: if find property is '{' we are looking for an object in array or the global scope etc
// which doesn't have a specific property name
2018-08-05 07:30:54 +00:00
char const * start_of_val = locate ;
char const * buf_ptr = start_of_val ;
if ( ! find_structure_in_global_scope )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
// NOTE: When true, the find_property already includes the quotation marks, so don't need to check.
if ( ! ( find_property [ 0 ] = = ' " ' & & find_property [ find_property_len - 1 ] = = ' " ' ) )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
if ( locate [ - 1 ] ! = ' " ' | | locate [ find_property_len ] ! = ' " ' )
2018-07-05 02:20:19 +00:00
{
2018-08-05 07:30:54 +00:00
buf_len - = static_cast < i32 > ( ( ( locate - buf ) + find_property_len ) ) ;
buf = locate + find_property_len ;
2018-07-05 02:20:19 +00:00
goto TryNext ;
}
2018-06-11 06:26:57 +00:00
}
2018-03-08 15:03:05 +00:00
2018-08-05 07:30:54 +00:00
if ( ! ( locate [ find_property_len + 1 ] & & locate [ find_property_len + 1 ] = = ' : ' ) )
2018-06-11 06:26:57 +00:00
{
return result ;
}
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
start_of_val = locate + find_property_len + 2 ;
start_of_val = DqnChar_SkipWhitespace ( start_of_val ) ;
buf_ptr = start_of_val ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
i32 brace_count = 0 , bracket_count = 0 ;
if ( buf_ptr [ 0 ] = = ' [ ' | | buf_ptr [ 0 ] = = ' { ' )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
start_of_val + + ;
2018-06-11 06:26:57 +00:00
2018-08-05 07:30:54 +00:00
i32 * search_char_count = nullptr ;
if ( * buf_ptr + + = = ' [ ' )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
bracket_count + + ;
search_char_count = & bracket_count ;
2018-07-12 14:46:08 +00:00
2018-08-05 07:30:54 +00:00
while ( buf_ptr [ 0 ] ! = ' { ' & & buf_ptr [ 0 ] ! = ' [ ' & & buf_ptr [ 0 ] ! = ' " ' & & ! DqnChar_IsAlphaNum ( buf_ptr [ 0 ] ) & & ! buf_ptr [ 0 ] )
buf_ptr + + ;
2018-07-12 14:46:08 +00:00
2018-08-05 07:30:54 +00:00
if ( ! buf_ptr [ 0 ] )
2018-07-12 14:46:08 +00:00
return result ;
2018-08-05 07:30:54 +00:00
const b32 array_of_primitives = ( DqnChar_IsAlphaNum ( buf_ptr [ 0 ] ) | | buf_ptr [ 0 ] = = ' " ' ) ;
result . type = ( array_of_primitives ) ? DqnJson : : Type : : ArrayOfPrimitives : DqnJson : : Type : : ArrayOfObjects ;
2018-05-17 11:19:58 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
brace_count + + ;
result . type = DqnJson : : Type : : Object ;
search_char_count = & brace_count ;
2018-05-17 11:19:58 +00:00
}
2018-07-12 14:46:08 +00:00
if ( result . type = = DqnJson : : Type : : ArrayOfPrimitives )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
for ( result . num_entries = 1 ; ; )
2018-07-12 14:46:08 +00:00
{
2018-08-05 07:30:54 +00:00
while ( buf_ptr [ 0 ] & & ( buf_ptr [ 0 ] ! = ' , ' & & buf_ptr [ 0 ] ! = ' ] ' ) )
buf_ptr + + ;
2018-07-12 14:46:08 +00:00
2018-08-05 07:30:54 +00:00
if ( buf_ptr [ 0 ] = = ' , ' )
2018-07-12 14:46:08 +00:00
{
2018-08-05 07:30:54 +00:00
result . num_entries + + ;
buf_ptr + + ;
2018-07-12 14:46:08 +00:00
continue ;
}
2018-03-08 15:03:05 +00:00
2018-08-05 07:30:54 +00:00
if ( ! buf_ptr [ 0 ] )
2018-07-12 14:46:08 +00:00
return result ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
if ( buf_ptr [ 0 ] = = ' ] ' )
2018-07-12 14:46:08 +00:00
break ;
}
}
else
{
2018-08-05 07:30:54 +00:00
for ( ; ( * search_char_count ) ! = 0 ; + + buf_ptr )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! buf_ptr [ 0 ] )
2018-07-12 14:46:08 +00:00
return result ;
2018-08-05 07:30:54 +00:00
if ( buf_ptr [ 0 ] = = ' { ' ) + + brace_count ;
else if ( buf_ptr [ 0 ] = = ' } ' ) - - brace_count ;
else if ( buf_ptr [ 0 ] = = ' [ ' ) + + bracket_count ;
else if ( buf_ptr [ 0 ] = = ' ] ' ) - - bracket_count ;
2018-07-12 14:46:08 +00:00
else continue ;
if ( result . type = = DqnJson : : Type : : ArrayOfObjects )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
if ( brace_count = = 0 & & bracket_count = = 1 )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
result . num_entries + + ;
2018-06-11 06:26:57 +00:00
}
}
else
{
2018-08-05 07:30:54 +00:00
if ( brace_count = = 1 & & bracket_count = = 0 )
2018-06-11 06:26:57 +00:00
{
2018-08-05 07:30:54 +00:00
result . num_entries + + ;
2018-06-11 06:26:57 +00:00
}
}
}
2018-07-12 14:46:08 +00:00
// Don't include the open and closing braces/brackets.
2018-08-05 07:30:54 +00:00
buf_ptr - - ;
2018-06-11 06:26:57 +00:00
}
2018-07-12 14:46:08 +00:00
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
else if ( buf_ptr [ 0 ] = = ' " ' | | DqnChar_IsAlphaNum ( buf_ptr [ 0 ] ) | | buf_ptr [ 0 ] = = ' - ' )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
while ( buf_ptr [ 0 ] & & ( buf_ptr [ 0 ] ! = ' \n ' & & buf_ptr [ 0 ] ! = ' , ' & & buf_ptr [ 0 ] ! = ' } ' ) )
buf_ptr + + ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
if ( ! buf_ptr [ 0 ] )
2018-06-11 06:26:57 +00:00
return result ;
2018-08-05 07:30:54 +00:00
result . num_entries = 1 ;
2018-05-17 11:19:58 +00:00
}
else
{
2018-06-11 06:26:57 +00:00
return result ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
result . value . data = ( char * ) start_of_val ;
result . value . len = static_cast < i32 > ( buf_ptr - result . value . data ) ;
2018-06-11 06:26:57 +00:00
result . value . data = DqnChar_TrimWhitespaceAround ( result . value . data , result . value . len , & result . value . len ) ;
return result ;
2018-03-08 15:03:05 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnSlice < char > const buf , DqnSlice < char > const find_property )
2018-03-08 15:03:05 +00:00
{
2018-08-05 07:30:54 +00:00
DqnJson result = DqnJson_Get ( buf . data , buf . len , find_property . data , find_property . len ) ;
2018-06-11 06:26:57 +00:00
return result ;
2018-03-08 15:03:05 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnSlice < char const > const buf , DqnSlice < char const > const find_property )
2018-03-08 15:03:05 +00:00
{
2018-08-05 07:30:54 +00:00
DqnJson result = DqnJson_Get ( buf . data , buf . len , find_property . data , find_property . len ) ;
2018-06-11 06:26:57 +00:00
return result ;
2018-03-08 15:03:05 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnSlice < char > const buf , DqnSlice < char const > const find_property )
2018-03-08 15:03:05 +00:00
{
2018-08-05 07:30:54 +00:00
DqnJson result = DqnJson_Get ( buf . data , buf . len , find_property . data , find_property . len ) ;
2018-06-11 06:26:57 +00:00
return result ;
2018-03-08 15:03:05 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnJson const input , DqnSlice < char > const find_property )
2018-03-08 15:03:05 +00:00
{
2018-08-05 07:30:54 +00:00
DqnJson result = DqnJson_Get ( input . value . data , input . value . len , find_property . data , find_property . len ) ;
2018-05-17 11:19:58 +00:00
return result ;
2018-03-08 15:03:05 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE DqnJson DqnJson_Get ( DqnJson const input , DqnSlice < char const > const find_property )
2018-03-08 15:03:05 +00:00
{
2018-08-05 07:30:54 +00:00
DqnJson result = DqnJson_Get ( input . value . data , input . value . len , find_property . data , find_property . len ) ;
2018-05-17 11:19:58 +00:00
return result ;
2018-03-08 15:03:05 +00:00
}
2018-06-25 10:51:18 +00:00
DQN_FILE_SCOPE DqnJson DqnJson_GetNextArrayItem ( DqnJson * iterator )
2018-03-08 15:03:05 +00:00
{
2018-06-11 06:26:57 +00:00
DqnJson result = { } ;
2018-08-05 07:30:54 +00:00
if ( ! iterator - > IsArray ( ) | | iterator - > num_entries < = 0 )
2018-06-11 06:26:57 +00:00
return result ;
2018-03-08 15:03:05 +00:00
2018-07-12 14:46:08 +00:00
if ( iterator - > type = = DqnJson : : Type : : ArrayOfObjects )
2018-06-11 06:26:57 +00:00
{
2018-08-19 01:59:04 +00:00
if ( result = DqnJson_Get ( iterator - > value , DQN_BUFFER_STR_LIT ( " { " ) ) )
2018-07-12 14:46:08 +00:00
{
char const * end = iterator - > value . data + iterator - > value . len ;
iterator - > value . data = result . value . data + result . value . len ;
2018-08-05 07:30:54 +00:00
- - iterator - > num_entries ;
2018-07-12 14:46:08 +00:00
while ( iterator - > value . data [ 0 ] & & * iterator - > value . data + + ! = ' } ' )
;
2018-03-08 15:03:05 +00:00
2018-07-12 14:46:08 +00:00
iterator - > value . data = DqnChar_SkipWhitespace ( iterator - > value . data ) ;
if ( iterator - > value . data [ 0 ] & & iterator - > value . data [ 0 ] = = ' , ' )
iterator - > value . data + + ;
iterator - > value . data = DqnChar_SkipWhitespace ( iterator - > value . data ) ;
iterator - > value . len = ( iterator - > value . data ) ? static_cast < i32 > ( end - iterator - > value . data ) : 0 ;
}
}
else
{
char const * end = iterator - > value . data + iterator - > value . len ;
result . value . data = iterator - > value . data ;
2018-08-05 07:30:54 +00:00
- - iterator - > num_entries ;
2018-07-12 14:46:08 +00:00
2018-08-05 07:30:54 +00:00
if ( iterator - > num_entries = = 0 )
2018-07-12 14:46:08 +00:00
{
while ( iterator - > value . data [ 0 ] & & iterator - > value . data [ 0 ] ! = ' ] ' )
+ + iterator - > value . data ;
}
else
{
while ( iterator - > value . data [ 0 ] & & iterator - > value . data [ 0 ] ! = ' , ' )
+ + iterator - > value . data ;
}
2018-03-08 15:03:05 +00:00
2018-06-11 06:26:57 +00:00
2018-07-12 14:46:08 +00:00
result . value . len = static_cast < i32 > ( iterator - > value . data - result . value . data ) ;
iterator - > value . data = DqnChar_SkipWhitespace ( + + iterator - > value . data ) ;
iterator - > value . len = ( iterator - > value . data ) ? static_cast < i32 > ( end - iterator - > value . data ) : 0 ;
2018-06-11 06:26:57 +00:00
}
2018-03-08 15:03:05 +00:00
2018-05-17 11:19:58 +00:00
return result ;
2018-03-08 15:03:05 +00:00
}
2018-06-11 06:26:57 +00:00
# ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
2018-01-19 12:56:25 +00:00
# endif
2018-07-04 14:47:11 +00:00
// #DqnSprintf - STB_Sprintf
// =================================================================================================
# include <stdlib.h> // for va_arg()
2018-01-03 13:40:27 +00:00
2018-06-11 06:26:57 +00:00
# define stbsp__uint32 unsigned int
# define stbsp__int32 signed int
2017-12-06 08:08:49 +00:00
2018-06-11 06:26:57 +00:00
# ifdef _MSC_VER
# define stbsp__uint64 unsigned __int64
# define stbsp__int64 signed __int64
# else
# define stbsp__uint64 unsigned long long
# define stbsp__int64 signed long long
2018-01-19 12:56:25 +00:00
# endif
2018-06-11 06:26:57 +00:00
# define stbsp__uint16 unsigned short
2018-01-25 07:55:05 +00:00
2018-07-04 14:47:11 +00:00
# ifndef stbsp__uintptr
2018-06-11 06:26:57 +00:00
# if defined(__ppc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
# define stbsp__uintptr stbsp__uint64
# else
# define stbsp__uintptr stbsp__uint32
# endif
# endif
2017-10-28 08:58:09 +00:00
2018-07-04 14:47:11 +00:00
# ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC)
# if defined(_MSC_VER) && (_MSC_VER < 1900)
2018-06-11 06:26:57 +00:00
# define STB_SPRINTF_MSVC_MODE
# endif
# endif
2017-10-28 08:58:09 +00:00
2018-07-04 14:47:11 +00:00
# ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses
2018-06-11 06:26:57 +00:00
# define STBSP__UNALIGNED(code)
# else
# define STBSP__UNALIGNED(code) code
# endif
2017-10-28 08:58:09 +00:00
2018-06-11 06:26:57 +00:00
# ifndef STB_SPRINTF_NOFLOAT
// internal float utility functions
2018-07-04 14:47:11 +00:00
static stbsp__int32 stbsp__real_to_str ( char const * * start , stbsp__uint32 * len , char * out , stbsp__int32 * decimal_pos , double value , stbsp__uint32 frac_digits ) ;
static stbsp__int32 stbsp__real_to_parts ( stbsp__int64 * bits , stbsp__int32 * expo , double value ) ;
2018-06-11 06:26:57 +00:00
# define STBSP__SPECIAL 0x7000
# endif
2017-10-28 08:58:09 +00:00
2018-07-04 14:47:11 +00:00
static char stbsp__period = ' . ' ;
static char stbsp__comma = ' , ' ;
static char stbsp__digitpair [ 201 ] =
" 0001020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 "
" 7778798081828384858687888990919293949596979899 " ;
2017-10-28 08:58:09 +00:00
2018-07-04 14:47:11 +00:00
STBSP__PUBLICDEF void STB_SPRINTF_DECORATE ( set_separators ) ( char pcomma , char pperiod )
2017-12-06 08:08:49 +00:00
{
2018-07-04 14:47:11 +00:00
stbsp__period = pperiod ;
stbsp__comma = pcomma ;
2017-12-06 08:08:49 +00:00
}
2018-06-11 06:26:57 +00:00
# define STBSP__LEFTJUST 1
# define STBSP__LEADINGPLUS 2
# define STBSP__LEADINGSPACE 4
# define STBSP__LEADING_0X 8
# define STBSP__LEADINGZERO 16
# define STBSP__INTMAX 32
# define STBSP__TRIPLET_COMMA 64
# define STBSP__NEGATIVE 128
# define STBSP__METRIC_SUFFIX 256
# define STBSP__HALFWIDTH 512
# define STBSP__METRIC_NOSPACE 1024
# define STBSP__METRIC_1024 2048
# define STBSP__METRIC_JEDEC 4096
static void stbsp__lead_sign ( stbsp__uint32 fl , char * sign )
2017-10-28 08:58:09 +00:00
{
2018-07-04 14:47:11 +00:00
sign [ 0 ] = 0 ;
if ( fl & STBSP__NEGATIVE ) {
sign [ 0 ] = 1 ;
sign [ 1 ] = ' - ' ;
} else if ( fl & STBSP__LEADINGSPACE ) {
sign [ 0 ] = 1 ;
sign [ 1 ] = ' ' ;
} else if ( fl & STBSP__LEADINGPLUS ) {
sign [ 0 ] = 1 ;
sign [ 1 ] = ' + ' ;
}
}
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( vsprintfcb ) ( STBSP_SPRINTFCB * callback , void * user , char * buf , char const * fmt , va_list va )
{
static char hex [ ] = " 0123456789abcdefxp " ;
static char hexu [ ] = " 0123456789ABCDEFXP " ;
char * bf ;
char const * f ;
int tlen = 0 ;
bf = buf ;
f = fmt ;
for ( ; ; ) {
stbsp__int32 fw , pr , tz ;
stbsp__uint32 fl ;
// macros for the callback buffer stuff
# define stbsp__chk_cb_bufL(bytes) \
{ \
int len = ( int ) ( bf - buf ) ; \
if ( ( len + ( bytes ) ) > = STB_SPRINTF_MIN ) { \
tlen + = len ; \
if ( 0 = = ( bf = buf = callback ( buf , user , len ) ) ) \
goto done ; \
} \
}
# define stbsp__chk_cb_buf(bytes) \
{ \
if ( callback ) { \
stbsp__chk_cb_bufL ( bytes ) ; \
} \
}
# define stbsp__flush_cb() \
{ \
stbsp__chk_cb_bufL ( STB_SPRINTF_MIN - 1 ) ; \
} // flush if there is even one byte in the buffer
# define stbsp__cb_buf_clamp(cl, v) \
cl = v ; \
if ( callback ) { \
int lg = STB_SPRINTF_MIN - ( int ) ( bf - buf ) ; \
if ( cl > lg ) \
cl = lg ; \
}
// fast copy everything up to the next % (or end of string)
for ( ; ; ) {
while ( ( ( stbsp__uintptr ) f ) & 3 ) {
schk1 :
if ( f [ 0 ] = = ' % ' )
goto scandd ;
schk2 :
if ( f [ 0 ] = = 0 )
goto endfmt ;
stbsp__chk_cb_buf ( 1 ) ;
* bf + + = f [ 0 ] ;
+ + f ;
}
for ( ; ; ) {
// Check if the next 4 bytes contain %(0x25) or end of string.
// Using the 'hasless' trick:
// https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
stbsp__uint32 v , c ;
v = * ( stbsp__uint32 * ) f ;
c = ( ~ v ) & 0x80808080 ;
if ( ( ( v ^ 0x25252525 ) - 0x01010101 ) & c )
goto schk1 ;
if ( ( v - 0x01010101 ) & c )
goto schk2 ;
if ( callback )
if ( ( STB_SPRINTF_MIN - ( int ) ( bf - buf ) ) < 4 )
goto schk1 ;
* ( stbsp__uint32 * ) bf = v ;
bf + = 4 ;
f + = 4 ;
}
2018-06-11 06:26:57 +00:00
}
2018-07-04 14:47:11 +00:00
scandd :
+ + f ;
// ok, we have a percent, read the modifiers first
fw = 0 ;
pr = - 1 ;
fl = 0 ;
tz = 0 ;
// flags
for ( ; ; ) {
switch ( f [ 0 ] ) {
// if we have left justify
case ' - ' :
fl | = STBSP__LEFTJUST ;
+ + f ;
continue ;
// if we have leading plus
case ' + ' :
fl | = STBSP__LEADINGPLUS ;
+ + f ;
continue ;
// if we have leading space
case ' ' :
fl | = STBSP__LEADINGSPACE ;
+ + f ;
continue ;
// if we have leading 0x
case ' # ' :
fl | = STBSP__LEADING_0X ;
+ + f ;
continue ;
// if we have thousand commas
case ' \' ' :
fl | = STBSP__TRIPLET_COMMA ;
+ + f ;
continue ;
// if we have kilo marker (none->kilo->kibi->jedec)
case ' $ ' :
if ( fl & STBSP__METRIC_SUFFIX ) {
if ( fl & STBSP__METRIC_1024 ) {
fl | = STBSP__METRIC_JEDEC ;
} else {
fl | = STBSP__METRIC_1024 ;
}
} else {
fl | = STBSP__METRIC_SUFFIX ;
2018-06-11 06:26:57 +00:00
}
2018-07-04 14:47:11 +00:00
+ + f ;
continue ;
// if we don't want space between metric suffix and number
case ' _ ' :
fl | = STBSP__METRIC_NOSPACE ;
+ + f ;
continue ;
// if we have leading zero
case ' 0 ' :
fl | = STBSP__LEADINGZERO ;
+ + f ;
goto flags_done ;
default : goto flags_done ;
}
}
flags_done :
// get the field width
if ( f [ 0 ] = = ' * ' ) {
fw = va_arg ( va , stbsp__uint32 ) ;
+ + f ;
} else {
while ( ( f [ 0 ] > = ' 0 ' ) & & ( f [ 0 ] < = ' 9 ' ) ) {
fw = fw * 10 + f [ 0 ] - ' 0 ' ;
f + + ;
}
}
// get the precision
if ( f [ 0 ] = = ' . ' ) {
+ + f ;
if ( f [ 0 ] = = ' * ' ) {
pr = va_arg ( va , stbsp__uint32 ) ;
+ + f ;
} else {
pr = 0 ;
while ( ( f [ 0 ] > = ' 0 ' ) & & ( f [ 0 ] < = ' 9 ' ) ) {
pr = pr * 10 + f [ 0 ] - ' 0 ' ;
f + + ;
2018-06-11 06:26:57 +00:00
}
2018-07-04 14:47:11 +00:00
}
2017-06-22 04:00:11 +00:00
}
2018-07-04 14:47:11 +00:00
// handle integer size overrides
switch ( f [ 0 ] ) {
2018-06-11 06:26:57 +00:00
// are we halfwidth?
2018-07-04 14:47:11 +00:00
case ' h ' :
fl | = STBSP__HALFWIDTH ;
+ + f ;
break ;
2018-06-11 06:26:57 +00:00
// are we 64-bit (unix style)
2018-07-04 14:47:11 +00:00
case ' l ' :
+ + f ;
if ( f [ 0 ] = = ' l ' ) {
fl | = STBSP__INTMAX ;
+ + f ;
}
break ;
2018-06-11 06:26:57 +00:00
// are we 64-bit on intmax? (c99)
2018-07-04 14:47:11 +00:00
case ' j ' :
fl | = STBSP__INTMAX ;
+ + f ;
break ;
// are we 64-bit on size_t or ptrdiff_t? (c99)
case ' z ' :
case ' t ' :
fl | = ( ( sizeof ( char * ) = = 8 ) ? STBSP__INTMAX : 0 ) ;
+ + f ;
break ;
2018-06-11 06:26:57 +00:00
// are we 64-bit (msft style)
2018-07-04 14:47:11 +00:00
case ' I ' :
if ( ( f [ 1 ] = = ' 6 ' ) & & ( f [ 2 ] = = ' 4 ' ) ) {
fl | = STBSP__INTMAX ;
f + = 3 ;
} else if ( ( f [ 1 ] = = ' 3 ' ) & & ( f [ 2 ] = = ' 2 ' ) ) {
f + = 3 ;
} else {
fl | = ( ( sizeof ( void * ) = = 8 ) ? STBSP__INTMAX : 0 ) ;
+ + f ;
}
break ;
2018-06-11 06:26:57 +00:00
default : break ;
2018-07-04 14:47:11 +00:00
}
2017-06-20 09:19:52 +00:00
2018-07-04 14:47:11 +00:00
// handle each replacement
switch ( f [ 0 ] ) {
# define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307
char num [ STBSP__NUMSZ ] ;
char lead [ 8 ] ;
char tail [ 8 ] ;
char * s ;
char const * h ;
stbsp__uint32 l , n , cs ;
stbsp__uint64 n64 ;
# ifndef STB_SPRINTF_NOFLOAT
double fv ;
# endif
stbsp__int32 dp ;
char const * sn ;
2017-06-20 09:19:52 +00:00
2018-06-11 06:26:57 +00:00
case ' s ' :
2018-07-04 14:47:11 +00:00
// get the string
s = va_arg ( va , char * ) ;
if ( s = = 0 )
s = ( char * ) " null " ;
// get the length
sn = s ;
for ( ; ; ) {
if ( ( ( ( stbsp__uintptr ) sn ) & 3 ) = = 0 )
break ;
2018-06-11 06:26:57 +00:00
lchk :
2018-07-04 14:47:11 +00:00
if ( sn [ 0 ] = = 0 )
goto ld ;
+ + sn ;
}
n = 0xffffffff ;
if ( pr > = 0 ) {
n = ( stbsp__uint32 ) ( sn - s ) ;
if ( n > = ( stbsp__uint32 ) pr )
goto ld ;
n = ( ( stbsp__uint32 ) ( pr - n ) ) > > 2 ;
}
while ( n ) {
stbsp__uint32 v = * ( stbsp__uint32 * ) sn ;
if ( ( v - 0x01010101 ) & ( ~ v ) & 0x80808080UL )
goto lchk ;
sn + = 4 ;
- - n ;
}
goto lchk ;
ld :
l = ( stbsp__uint32 ) ( sn - s ) ;
// clamp to precision
if ( l > ( stbsp__uint32 ) pr )
l = pr ;
lead [ 0 ] = 0 ;
tail [ 0 ] = 0 ;
pr = 0 ;
dp = 0 ;
cs = 0 ;
// copy the string in
goto scopy ;
2017-06-22 04:00:11 +00:00
2018-06-11 06:26:57 +00:00
case ' c ' : // char
2018-07-04 14:47:11 +00:00
// get the character
s = num + STBSP__NUMSZ - 1 ;
* s = ( char ) va_arg ( va , int ) ;
l = 1 ;
lead [ 0 ] = 0 ;
tail [ 0 ] = 0 ;
pr = 0 ;
dp = 0 ;
cs = 0 ;
goto scopy ;
2017-06-22 04:00:11 +00:00
2018-06-11 06:26:57 +00:00
case ' n ' : // weird write-bytes specifier
2018-07-04 14:47:11 +00:00
{
int * d = va_arg ( va , int * ) ;
* d = tlen + ( int ) ( bf - buf ) ;
} break ;
2017-06-22 04:00:11 +00:00
2018-06-11 06:26:57 +00:00
# ifdef STB_SPRINTF_NOFLOAT
2018-07-04 14:47:11 +00:00
case ' A ' : // float
case ' a ' : // hex float
case ' G ' : // float
case ' g ' : // float
case ' E ' : // float
case ' e ' : // float
case ' f ' : // float
va_arg ( va , double ) ; // eat it
s = ( char * ) " No float " ;
l = 8 ;
lead [ 0 ] = 0 ;
tail [ 0 ] = 0 ;
pr = 0 ;
dp = 0 ;
cs = 0 ;
goto scopy ;
2018-06-11 06:26:57 +00:00
# else
2018-07-04 14:47:11 +00:00
case ' A ' : // hex float
2018-06-11 06:26:57 +00:00
case ' a ' : // hex float
2018-07-04 14:47:11 +00:00
h = ( f [ 0 ] = = ' A ' ) ? hexu : hex ;
fv = va_arg ( va , double ) ;
if ( pr = = - 1 )
pr = 6 ; // default is 6
// read the double into a string
if ( stbsp__real_to_parts ( ( stbsp__int64 * ) & n64 , & dp , fv ) )
fl | = STBSP__NEGATIVE ;
s = num + 64 ;
stbsp__lead_sign ( fl , lead ) ;
if ( dp = = - 1023 )
dp = ( n64 ) ? - 1022 : 0 ;
else
n64 | = ( ( ( stbsp__uint64 ) 1 ) < < 52 ) ;
n64 < < = ( 64 - 56 ) ;
if ( pr < 15 )
n64 + = ( ( ( ( stbsp__uint64 ) 8 ) < < 56 ) > > ( pr * 4 ) ) ;
// add leading chars
# ifdef STB_SPRINTF_MSVC_MODE
* s + + = ' 0 ' ;
* s + + = ' x ' ;
# else
lead [ 1 + lead [ 0 ] ] = ' 0 ' ;
lead [ 2 + lead [ 0 ] ] = ' x ' ;
lead [ 0 ] + = 2 ;
# endif
* s + + = h [ ( n64 > > 60 ) & 15 ] ;
n64 < < = 4 ;
if ( pr )
* s + + = stbsp__period ;
sn = s ;
// print the bits
n = pr ;
if ( n > 13 )
n = 13 ;
if ( pr > ( stbsp__int32 ) n )
tz = pr - n ;
pr = 0 ;
while ( n - - ) {
* s + + = h [ ( n64 > > 60 ) & 15 ] ;
n64 < < = 4 ;
}
// print the expo
tail [ 1 ] = h [ 17 ] ;
if ( dp < 0 ) {
tail [ 2 ] = ' - ' ;
dp = - dp ;
} else
tail [ 2 ] = ' + ' ;
n = ( dp > = 1000 ) ? 6 : ( ( dp > = 100 ) ? 5 : ( ( dp > = 10 ) ? 4 : 3 ) ) ;
tail [ 0 ] = ( char ) n ;
for ( ; ; ) {
tail [ n ] = ' 0 ' + dp % 10 ;
if ( n < = 3 )
break ;
- - n ;
dp / = 10 ;
}
dp = ( int ) ( s - sn ) ;
l = ( int ) ( s - ( num + 64 ) ) ;
s = num + 64 ;
cs = 1 + ( 3 < < 24 ) ;
goto scopy ;
2017-06-20 09:19:52 +00:00
2018-06-11 06:26:57 +00:00
case ' G ' : // float
case ' g ' : // float
2018-07-04 14:47:11 +00:00
h = ( f [ 0 ] = = ' G ' ) ? hexu : hex ;
fv = va_arg ( va , double ) ;
if ( pr = = - 1 )
pr = 6 ;
else if ( pr = = 0 )
pr = 1 ; // default is 6
// read the double into a string
if ( stbsp__real_to_str ( & sn , & l , num , & dp , fv , ( pr - 1 ) | 0x80000000 ) )
fl | = STBSP__NEGATIVE ;
// clamp the precision and delete extra zeros after clamp
n = pr ;
if ( l > ( stbsp__uint32 ) pr )
l = pr ;
while ( ( l > 1 ) & & ( pr ) & & ( sn [ l - 1 ] = = ' 0 ' ) ) {
- - pr ;
- - l ;
}
// should we use %e
if ( ( dp < = - 4 ) | | ( dp > ( stbsp__int32 ) n ) ) {
if ( pr > ( stbsp__int32 ) l )
pr = l - 1 ;
else if ( pr )
- - pr ; // when using %e, there is one digit before the decimal
goto doexpfromg ;
}
// this is the insane action to get the pr to match %g sematics for %f
if ( dp > 0 ) {
pr = ( dp < ( stbsp__int32 ) l ) ? l - dp : 0 ;
} else {
pr = - dp + ( ( pr > ( stbsp__int32 ) l ) ? l : pr ) ;
}
goto dofloatfromg ;
2017-06-20 12:23:34 +00:00
2018-06-11 06:26:57 +00:00
case ' E ' : // float
case ' e ' : // float
2018-07-04 14:47:11 +00:00
h = ( f [ 0 ] = = ' E ' ) ? hexu : hex ;
fv = va_arg ( va , double ) ;
if ( pr = = - 1 )
pr = 6 ; // default is 6
// read the double into a string
if ( stbsp__real_to_str ( & sn , & l , num , & dp , fv , pr | 0x80000000 ) )
fl | = STBSP__NEGATIVE ;
doexpfromg :
tail [ 0 ] = 0 ;
stbsp__lead_sign ( fl , lead ) ;
if ( dp = = STBSP__SPECIAL ) {
s = ( char * ) sn ;
cs = 0 ;
pr = 0 ;
goto scopy ;
}
s = num + 64 ;
// handle leading chars
* s + + = sn [ 0 ] ;
if ( pr )
* s + + = stbsp__period ;
// handle after decimal
if ( ( l - 1 ) > ( stbsp__uint32 ) pr )
l = pr + 1 ;
for ( n = 1 ; n < l ; n + + )
* s + + = sn [ n ] ;
// trailing zeros
tz = pr - ( l - 1 ) ;
pr = 0 ;
// dump expo
tail [ 1 ] = h [ 0xe ] ;
dp - = 1 ;
if ( dp < 0 ) {
tail [ 2 ] = ' - ' ;
dp = - dp ;
} else
tail [ 2 ] = ' + ' ;
# ifdef STB_SPRINTF_MSVC_MODE
n = 5 ;
# else
n = ( dp > = 100 ) ? 5 : 4 ;
# endif
tail [ 0 ] = ( char ) n ;
for ( ; ; ) {
tail [ n ] = ' 0 ' + dp % 10 ;
if ( n < = 3 )
break ;
- - n ;
dp / = 10 ;
}
cs = 1 + ( 3 < < 24 ) ; // how many tens
goto flt_lead ;
2017-06-20 12:23:34 +00:00
2018-06-11 06:26:57 +00:00
case ' f ' : // float
2018-07-04 14:47:11 +00:00
fv = va_arg ( va , double ) ;
doafloat :
// do kilos
if ( fl & STBSP__METRIC_SUFFIX ) {
2018-06-11 06:26:57 +00:00
double divisor ;
2018-07-04 14:47:11 +00:00
divisor = 1000.0f ;
if ( fl & STBSP__METRIC_1024 )
divisor = 1024.0 ;
while ( fl < 0x4000000 ) {
if ( ( fv < divisor ) & & ( fv > - divisor ) )
break ;
fv / = divisor ;
fl + = 0x1000000 ;
2018-06-11 06:26:57 +00:00
}
2018-07-04 14:47:11 +00:00
}
if ( pr = = - 1 )
pr = 6 ; // default is 6
// read the double into a string
if ( stbsp__real_to_str ( & sn , & l , num , & dp , fv , pr ) )
fl | = STBSP__NEGATIVE ;
dofloatfromg :
tail [ 0 ] = 0 ;
stbsp__lead_sign ( fl , lead ) ;
if ( dp = = STBSP__SPECIAL ) {
s = ( char * ) sn ;
cs = 0 ;
pr = 0 ;
goto scopy ;
}
s = num + 64 ;
// handle the three decimal varieties
if ( dp < = 0 ) {
stbsp__int32 i ;
// handle 0.000*000xxxx
* s + + = ' 0 ' ;
if ( pr )
* s + + = stbsp__period ;
n = - dp ;
if ( ( stbsp__int32 ) n > pr )
n = pr ;
i = n ;
while ( i ) {
if ( ( ( ( stbsp__uintptr ) s ) & 3 ) = = 0 )
break ;
* s + + = ' 0 ' ;
- - i ;
}
while ( i > = 4 ) {
* ( stbsp__uint32 * ) s = 0x30303030 ;
s + = 4 ;
i - = 4 ;
}
while ( i ) {
* s + + = ' 0 ' ;
- - i ;
}
if ( ( stbsp__int32 ) ( l + n ) > pr )
l = pr - n ;
i = l ;
while ( i ) {
* s + + = * sn + + ;
- - i ;
}
tz = pr - ( n + l ) ;
cs = 1 + ( 3 < < 24 ) ; // how many tens did we write (for commas below)
} else {
cs = ( fl & STBSP__TRIPLET_COMMA ) ? ( ( 600 - ( stbsp__uint32 ) dp ) % 3 ) : 0 ;
if ( ( stbsp__uint32 ) dp > = l ) {
// handle xxxx000*000.0
n = 0 ;
for ( ; ; ) {
if ( ( fl & STBSP__TRIPLET_COMMA ) & & ( + + cs = = 4 ) ) {
cs = 0 ;
* s + + = stbsp__comma ;
} else {
* s + + = sn [ n ] ;
+ + n ;
if ( n > = l )
break ;
}
}
if ( n < ( stbsp__uint32 ) dp ) {
n = dp - n ;
if ( ( fl & STBSP__TRIPLET_COMMA ) = = 0 ) {
while ( n ) {
if ( ( ( ( stbsp__uintptr ) s ) & 3 ) = = 0 )
break ;
* s + + = ' 0 ' ;
- - n ;
}
while ( n > = 4 ) {
* ( stbsp__uint32 * ) s = 0x30303030 ;
s + = 4 ;
n - = 4 ;
}
}
while ( n ) {
if ( ( fl & STBSP__TRIPLET_COMMA ) & & ( + + cs = = 4 ) ) {
cs = 0 ;
* s + + = stbsp__comma ;
} else {
* s + + = ' 0 ' ;
- - n ;
}
}
}
cs = ( int ) ( s - ( num + 64 ) ) + ( 3 < < 24 ) ; // cs is how many tens
if ( pr ) {
* s + + = stbsp__period ;
tz = pr ;
}
} else {
// handle xxxxx.xxxx000*000
n = 0 ;
for ( ; ; ) {
if ( ( fl & STBSP__TRIPLET_COMMA ) & & ( + + cs = = 4 ) ) {
cs = 0 ;
* s + + = stbsp__comma ;
} else {
* s + + = sn [ n ] ;
+ + n ;
if ( n > = ( stbsp__uint32 ) dp )
break ;
}
}
cs = ( int ) ( s - ( num + 64 ) ) + ( 3 < < 24 ) ; // cs is how many tens
if ( pr )
* s + + = stbsp__period ;
if ( ( l - dp ) > ( stbsp__uint32 ) pr )
l = pr + dp ;
while ( n < l ) {
* s + + = sn [ n ] ;
+ + n ;
}
tz = pr - ( l - dp ) ;
}
}
pr = 0 ;
// handle k,m,g,t
if ( fl & STBSP__METRIC_SUFFIX ) {
2018-06-11 06:26:57 +00:00
char idx ;
2018-07-04 14:47:11 +00:00
idx = 1 ;
if ( fl & STBSP__METRIC_NOSPACE )
idx = 0 ;
tail [ 0 ] = idx ;
tail [ 1 ] = ' ' ;
{
if ( fl > > 24 ) { // SI kilo is 'k', JEDEC and SI kibits are 'K'.
if ( fl & STBSP__METRIC_1024 )
tail [ idx + 1 ] = " _KMGT " [ fl > > 24 ] ;
else
tail [ idx + 1 ] = " _kMGT " [ fl > > 24 ] ;
idx + + ;
// If printing kibits and not in jedec, add the 'i'.
if ( fl & STBSP__METRIC_1024 & & ! ( fl & STBSP__METRIC_JEDEC ) ) {
tail [ idx + 1 ] = ' i ' ;
idx + + ;
}
tail [ 0 ] = idx ;
}
}
} ;
flt_lead :
// get the length that we copied
l = ( stbsp__uint32 ) ( s - ( num + 64 ) ) ;
s = num + 64 ;
goto scopy ;
2017-06-22 04:00:11 +00:00
# endif
2017-06-20 12:23:34 +00:00
2018-06-11 06:26:57 +00:00
case ' B ' : // upper binary
case ' b ' : // lower binary
2018-07-04 14:47:11 +00:00
h = ( f [ 0 ] = = ' B ' ) ? hexu : hex ;
lead [ 0 ] = 0 ;
if ( fl & STBSP__LEADING_0X ) {
lead [ 0 ] = 2 ;
lead [ 1 ] = ' 0 ' ;
lead [ 2 ] = h [ 0xb ] ;
}
l = ( 8 < < 4 ) | ( 1 < < 8 ) ;
goto radixnum ;
2017-06-20 09:19:52 +00:00
2018-06-11 06:26:57 +00:00
case ' o ' : // octal
2018-07-04 14:47:11 +00:00
h = hexu ;
lead [ 0 ] = 0 ;
if ( fl & STBSP__LEADING_0X ) {
lead [ 0 ] = 1 ;
lead [ 1 ] = ' 0 ' ;
}
l = ( 3 < < 4 ) | ( 3 < < 8 ) ;
goto radixnum ;
2017-06-20 09:19:52 +00:00
2018-06-11 06:26:57 +00:00
case ' p ' : // pointer
2018-07-04 14:47:11 +00:00
fl | = ( sizeof ( void * ) = = 8 ) ? STBSP__INTMAX : 0 ;
pr = sizeof ( void * ) * 2 ;
fl & = ~ STBSP__LEADINGZERO ; // 'p' only prints the pointer with zeros
// fall through - to X
case ' X ' : // upper hex
case ' x ' : // lower hex
h = ( f [ 0 ] = = ' X ' ) ? hexu : hex ;
l = ( 4 < < 4 ) | ( 4 < < 8 ) ;
lead [ 0 ] = 0 ;
if ( fl & STBSP__LEADING_0X ) {
lead [ 0 ] = 2 ;
lead [ 1 ] = ' 0 ' ;
lead [ 2 ] = h [ 16 ] ;
}
radixnum :
// get the number
if ( fl & STBSP__INTMAX )
n64 = va_arg ( va , stbsp__uint64 ) ;
else
n64 = va_arg ( va , stbsp__uint32 ) ;
s = num + STBSP__NUMSZ ;
dp = 0 ;
// clear tail, and clear leading if value is zero
tail [ 0 ] = 0 ;
if ( n64 = = 0 ) {
lead [ 0 ] = 0 ;
if ( pr = = 0 ) {
l = 0 ;
cs = ( ( ( l > > 4 ) & 15 ) ) < < 24 ;
goto scopy ;
}
}
// convert to string
for ( ; ; ) {
* - - s = h [ n64 & ( ( 1 < < ( l > > 8 ) ) - 1 ) ] ;
n64 > > = ( l > > 8 ) ;
if ( ! ( ( n64 ) | | ( ( stbsp__int32 ) ( ( num + STBSP__NUMSZ ) - s ) < pr ) ) )
break ;
if ( fl & STBSP__TRIPLET_COMMA ) {
+ + l ;
if ( ( l & 15 ) = = ( ( l > > 4 ) & 15 ) ) {
l & = ~ 15 ;
* - - s = stbsp__comma ;
}
}
} ;
// get the tens and the comma pos
cs = ( stbsp__uint32 ) ( ( num + STBSP__NUMSZ ) - s ) + ( ( ( ( l > > 4 ) & 15 ) ) < < 24 ) ;
// get the length that we copied
l = ( stbsp__uint32 ) ( ( num + STBSP__NUMSZ ) - s ) ;
// copy it
goto scopy ;
2017-06-20 09:19:52 +00:00
2018-06-11 06:26:57 +00:00
case ' u ' : // unsigned
case ' i ' :
case ' d ' : // integer
2018-07-04 14:47:11 +00:00
// get the integer and abs it
if ( fl & STBSP__INTMAX ) {
stbsp__int64 i64 = va_arg ( va , stbsp__int64 ) ;
n64 = ( stbsp__uint64 ) i64 ;
if ( ( f [ 0 ] ! = ' u ' ) & & ( i64 < 0 ) ) {
n64 = ( stbsp__uint64 ) - i64 ;
fl | = STBSP__NEGATIVE ;
}
} else {
stbsp__int32 i = va_arg ( va , stbsp__int32 ) ;
n64 = ( stbsp__uint32 ) i ;
if ( ( f [ 0 ] ! = ' u ' ) & & ( i < 0 ) ) {
n64 = ( stbsp__uint32 ) - i ;
fl | = STBSP__NEGATIVE ;
}
}
2018-05-17 11:19:58 +00:00
2018-07-04 14:47:11 +00:00
# ifndef STB_SPRINTF_NOFLOAT
if ( fl & STBSP__METRIC_SUFFIX ) {
if ( n64 < 1024 )
pr = 0 ;
else if ( pr = = - 1 )
pr = 1 ;
fv = ( double ) ( stbsp__int64 ) n64 ;
goto doafloat ;
}
# endif
2018-05-17 11:19:58 +00:00
2018-07-04 14:47:11 +00:00
// convert to string
s = num + STBSP__NUMSZ ;
l = 0 ;
for ( ; ; ) {
// do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
char * o = s - 8 ;
if ( n64 > = 100000000 ) {
n = ( stbsp__uint32 ) ( n64 % 100000000 ) ;
n64 / = 100000000 ;
} else {
n = ( stbsp__uint32 ) n64 ;
n64 = 0 ;
}
if ( ( fl & STBSP__TRIPLET_COMMA ) = = 0 ) {
do {
s - = 2 ;
* ( stbsp__uint16 * ) s = * ( stbsp__uint16 * ) & stbsp__digitpair [ ( n % 100 ) * 2 ] ;
n / = 100 ;
} while ( n ) ;
}
while ( n ) {
if ( ( fl & STBSP__TRIPLET_COMMA ) & & ( l + + = = 3 ) ) {
l = 0 ;
* - - s = stbsp__comma ;
- - o ;
} else {
* - - s = ( char ) ( n % 10 ) + ' 0 ' ;
n / = 10 ;
}
}
if ( n64 = = 0 ) {
if ( ( s [ 0 ] = = ' 0 ' ) & & ( s ! = ( num + STBSP__NUMSZ ) ) )
+ + s ;
break ;
}
while ( s ! = o )
if ( ( fl & STBSP__TRIPLET_COMMA ) & & ( l + + = = 3 ) ) {
l = 0 ;
* - - s = stbsp__comma ;
- - o ;
} else {
* - - s = ' 0 ' ;
}
}
tail [ 0 ] = 0 ;
stbsp__lead_sign ( fl , lead ) ;
// get the length that we copied
l = ( stbsp__uint32 ) ( ( num + STBSP__NUMSZ ) - s ) ;
if ( l = = 0 ) {
* - - s = ' 0 ' ;
l = 1 ;
}
cs = l + ( 3 < < 24 ) ;
if ( pr < 0 )
pr = 0 ;
scopy :
// get fw=leading/trailing space, pr=leading zeros
if ( pr < ( stbsp__int32 ) l )
pr = l ;
n = pr + lead [ 0 ] + tail [ 0 ] + tz ;
if ( fw < ( stbsp__int32 ) n )
fw = n ;
fw - = n ;
pr - = l ;
// handle right justify and leading zeros
if ( ( fl & STBSP__LEFTJUST ) = = 0 ) {
if ( fl & STBSP__LEADINGZERO ) // if leading zeros, everything is in pr
{
pr = ( fw > pr ) ? fw : pr ;
fw = 0 ;
} else {
fl & = ~ STBSP__TRIPLET_COMMA ; // if no leading zeros, then no commas
}
}
// copy the spaces and/or zeros
if ( fw + pr ) {
stbsp__int32 i ;
stbsp__uint32 c ;
// copy leading spaces (or when doing %8.4d stuff)
if ( ( fl & STBSP__LEFTJUST ) = = 0 )
while ( fw > 0 ) {
stbsp__cb_buf_clamp ( i , fw ) ;
fw - = i ;
while ( i ) {
if ( ( ( ( stbsp__uintptr ) bf ) & 3 ) = = 0 )
break ;
* bf + + = ' ' ;
- - i ;
}
while ( i > = 4 ) {
* ( stbsp__uint32 * ) bf = 0x20202020 ;
bf + = 4 ;
i - = 4 ;
}
while ( i ) {
* bf + + = ' ' ;
- - i ;
}
stbsp__chk_cb_buf ( 1 ) ;
}
// copy leader
sn = lead + 1 ;
while ( lead [ 0 ] ) {
stbsp__cb_buf_clamp ( i , lead [ 0 ] ) ;
lead [ 0 ] - = ( char ) i ;
while ( i ) {
* bf + + = * sn + + ;
- - i ;
}
stbsp__chk_cb_buf ( 1 ) ;
}
2018-05-17 11:19:58 +00:00
2018-07-04 14:47:11 +00:00
// copy leading zeros
c = cs > > 24 ;
cs & = 0xffffff ;
cs = ( fl & STBSP__TRIPLET_COMMA ) ? ( ( stbsp__uint32 ) ( c - ( ( pr + cs ) % ( c + 1 ) ) ) ) : 0 ;
while ( pr > 0 ) {
stbsp__cb_buf_clamp ( i , pr ) ;
pr - = i ;
if ( ( fl & STBSP__TRIPLET_COMMA ) = = 0 ) {
while ( i ) {
if ( ( ( ( stbsp__uintptr ) bf ) & 3 ) = = 0 )
break ;
* bf + + = ' 0 ' ;
- - i ;
}
while ( i > = 4 ) {
* ( stbsp__uint32 * ) bf = 0x30303030 ;
bf + = 4 ;
i - = 4 ;
}
}
while ( i ) {
if ( ( fl & STBSP__TRIPLET_COMMA ) & & ( cs + + = = c ) ) {
cs = 0 ;
* bf + + = stbsp__comma ;
} else
* bf + + = ' 0 ' ;
- - i ;
}
stbsp__chk_cb_buf ( 1 ) ;
}
}
// copy leader if there is still one
sn = lead + 1 ;
while ( lead [ 0 ] ) {
stbsp__int32 i ;
stbsp__cb_buf_clamp ( i , lead [ 0 ] ) ;
lead [ 0 ] - = ( char ) i ;
while ( i ) {
* bf + + = * sn + + ;
- - i ;
}
stbsp__chk_cb_buf ( 1 ) ;
}
// copy the string
n = l ;
while ( n ) {
stbsp__int32 i ;
stbsp__cb_buf_clamp ( i , n ) ;
n - = i ;
STBSP__UNALIGNED ( while ( i > = 4 ) {
* ( stbsp__uint32 * ) bf = * ( stbsp__uint32 * ) s ;
bf + = 4 ;
s + = 4 ;
i - = 4 ;
} )
while ( i ) {
* bf + + = * s + + ;
- - i ;
}
stbsp__chk_cb_buf ( 1 ) ;
}
// copy trailing zeros
while ( tz ) {
stbsp__int32 i ;
stbsp__cb_buf_clamp ( i , tz ) ;
tz - = i ;
while ( i ) {
if ( ( ( ( stbsp__uintptr ) bf ) & 3 ) = = 0 )
break ;
* bf + + = ' 0 ' ;
- - i ;
}
while ( i > = 4 ) {
* ( stbsp__uint32 * ) bf = 0x30303030 ;
bf + = 4 ;
i - = 4 ;
}
while ( i ) {
* bf + + = ' 0 ' ;
- - i ;
}
stbsp__chk_cb_buf ( 1 ) ;
}
// copy tail if there is one
sn = tail + 1 ;
while ( tail [ 0 ] ) {
stbsp__int32 i ;
stbsp__cb_buf_clamp ( i , tail [ 0 ] ) ;
tail [ 0 ] - = ( char ) i ;
while ( i ) {
* bf + + = * sn + + ;
- - i ;
}
stbsp__chk_cb_buf ( 1 ) ;
}
// handle the left justify
if ( fl & STBSP__LEFTJUST )
if ( fw > 0 ) {
while ( fw ) {
stbsp__int32 i ;
stbsp__cb_buf_clamp ( i , fw ) ;
fw - = i ;
while ( i ) {
if ( ( ( ( stbsp__uintptr ) bf ) & 3 ) = = 0 )
break ;
* bf + + = ' ' ;
- - i ;
}
while ( i > = 4 ) {
* ( stbsp__uint32 * ) bf = 0x20202020 ;
bf + = 4 ;
i - = 4 ;
}
while ( i - - )
* bf + + = ' ' ;
stbsp__chk_cb_buf ( 1 ) ;
}
}
break ;
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
default : // unknown, just copy code
2018-07-04 14:47:11 +00:00
s = num + STBSP__NUMSZ - 1 ;
* s = f [ 0 ] ;
l = 1 ;
fw = fl = 0 ;
lead [ 0 ] = 0 ;
tail [ 0 ] = 0 ;
pr = 0 ;
dp = 0 ;
cs = 0 ;
goto scopy ;
}
+ + f ;
}
endfmt :
if ( ! callback )
* bf = 0 ;
else
stbsp__flush_cb ( ) ;
2018-05-17 11:19:58 +00:00
2018-07-04 14:47:11 +00:00
done :
return tlen + ( int ) ( bf - buf ) ;
2017-06-22 04:00:11 +00:00
}
2017-04-10 11:31:11 +00:00
2018-06-11 06:26:57 +00:00
// cleanup
# undef STBSP__LEFTJUST
# undef STBSP__LEADINGPLUS
# undef STBSP__LEADINGSPACE
# undef STBSP__LEADING_0X
# undef STBSP__LEADINGZERO
# undef STBSP__INTMAX
# undef STBSP__TRIPLET_COMMA
# undef STBSP__NEGATIVE
# undef STBSP__METRIC_SUFFIX
# undef STBSP__NUMSZ
# undef stbsp__chk_cb_bufL
# undef stbsp__chk_cb_buf
# undef stbsp__flush_cb
# undef stbsp__cb_buf_clamp
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
// ============================================================================
// wrapper functions
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( sprintf ) ( char * buf , char const * fmt , . . . )
2017-06-20 12:23:34 +00:00
{
2018-07-04 14:47:11 +00:00
int result ;
va_list va ;
va_start ( va , fmt ) ;
result = STB_SPRINTF_DECORATE ( vsprintfcb ) ( 0 , 0 , buf , fmt , va ) ;
va_end ( va ) ;
return result ;
2017-06-22 04:00:11 +00:00
}
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
typedef struct stbsp__context {
char * buf ;
int count ;
char tmp [ STB_SPRINTF_MIN ] ;
2018-06-11 06:26:57 +00:00
} stbsp__context ;
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
static char * stbsp__clamp_callback ( char * buf , void * user , int len )
2017-06-22 04:00:11 +00:00
{
2018-07-04 14:47:11 +00:00
stbsp__context * c = ( stbsp__context * ) user ;
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
if ( len > c - > count )
len = c - > count ;
2018-06-11 06:26:57 +00:00
2018-07-04 14:47:11 +00:00
if ( len ) {
if ( buf ! = c - > buf ) {
char * s , * d , * se ;
d = c - > buf ;
s = buf ;
se = buf + len ;
do {
* d + + = * s + + ;
} while ( s < se ) ;
}
c - > buf + = len ;
c - > count - = len ;
}
if ( c - > count < = 0 )
return 0 ;
return ( c - > count > = STB_SPRINTF_MIN ) ? c - > buf : c - > tmp ; // go direct into buffer if you can
}
static char * stbsp__count_clamp_callback ( char * buf , void * user , int len )
{
( void ) buf ;
stbsp__context * c = ( stbsp__context * ) user ;
c - > count + = len ;
return c - > tmp ; // go direct into buffer if you can
2017-06-22 04:00:11 +00:00
}
2017-04-10 11:31:11 +00:00
2018-06-11 06:26:57 +00:00
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( vsnprintf ) ( char * buf , int count , char const * fmt , va_list va )
2017-06-22 04:00:11 +00:00
{
2018-07-04 14:47:11 +00:00
stbsp__context c ;
int l ;
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
if ( ( count = = 0 ) & & ! buf )
{
c . count = 0 ;
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
STB_SPRINTF_DECORATE ( vsprintfcb ) ( stbsp__count_clamp_callback , & c , c . tmp , fmt , va ) ;
l = c . count ;
}
else
{
if ( count = = 0 )
return 0 ;
2018-06-11 06:26:57 +00:00
2018-07-04 14:47:11 +00:00
c . buf = buf ;
c . count = count ;
2018-06-11 06:26:57 +00:00
2018-07-04 14:47:11 +00:00
STB_SPRINTF_DECORATE ( vsprintfcb ) ( stbsp__clamp_callback , & c , stbsp__clamp_callback ( 0 , & c , 0 ) , fmt , va ) ;
// zero-terminate
l = ( int ) ( c . buf - buf ) ;
if ( l > = count ) // should never be greater, only equal (or less) than count
l = count - 1 ;
buf [ l ] = 0 ;
}
return l ;
2017-06-22 04:00:11 +00:00
}
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( snprintf ) ( char * buf , int count , char const * fmt , . . . )
2017-06-22 04:00:11 +00:00
{
2018-07-04 14:47:11 +00:00
int result ;
va_list va ;
va_start ( va , fmt ) ;
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
result = STB_SPRINTF_DECORATE ( vsnprintf ) ( buf , count , fmt , va ) ;
va_end ( va ) ;
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
return result ;
2017-06-20 12:23:34 +00:00
}
2017-04-10 11:31:11 +00:00
2018-07-04 14:47:11 +00:00
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE ( vsprintf ) ( char * buf , char const * fmt , va_list va )
2017-06-22 04:00:11 +00:00
{
2018-07-04 14:47:11 +00:00
return STB_SPRINTF_DECORATE ( vsprintfcb ) ( 0 , 0 , buf , fmt , va ) ;
2018-06-11 06:26:57 +00:00
}
2017-04-10 11:31:11 +00:00
2018-06-11 06:26:57 +00:00
// =======================================================================
// low level float utility functions
2017-04-10 11:31:11 +00:00
2018-06-11 06:26:57 +00:00
# ifndef STB_SPRINTF_NOFLOAT
2017-06-21 09:01:36 +00:00
2018-06-11 06:26:57 +00:00
// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
2018-07-04 14:47:11 +00:00
# define STBSP__COPYFP(dest, src) \
{ \
int cn ; \
for ( cn = 0 ; cn < 8 ; cn + + ) \
( ( char * ) & dest ) [ cn ] = ( ( char * ) & src ) [ cn ] ; \
}
2018-06-11 06:26:57 +00:00
// get float info
2018-07-04 14:47:11 +00:00
static stbsp__int32 stbsp__real_to_parts ( stbsp__int64 * bits , stbsp__int32 * expo , double value )
2017-06-22 04:00:11 +00:00
{
2018-07-04 14:47:11 +00:00
double d ;
stbsp__int64 b = 0 ;
2018-05-17 11:19:58 +00:00
2018-07-04 14:47:11 +00:00
// load value and round at the frac_digits
d = value ;
2018-06-11 06:26:57 +00:00
2018-07-04 14:47:11 +00:00
STBSP__COPYFP ( b , d ) ;
2018-05-17 11:19:58 +00:00
2018-07-04 14:47:11 +00:00
* bits = b & ( ( ( ( stbsp__uint64 ) 1 ) < < 52 ) - 1 ) ;
* expo = ( stbsp__int32 ) ( ( ( b > > 52 ) & 2047 ) - 1023 ) ;
return ( stbsp__int32 ) ( b > > 63 ) ;
2017-06-22 04:00:11 +00:00
}
2017-06-20 12:23:34 +00:00
2018-07-04 14:47:11 +00:00
static double const stbsp__bot [ 23 ] = {
1e+000 , 1e+001 , 1e+002 , 1e+003 , 1e+004 , 1e+005 , 1e+006 , 1e+007 , 1e+008 , 1e+009 , 1e+010 , 1e+011 ,
1e+012 , 1e+013 , 1e+014 , 1e+015 , 1e+016 , 1e+017 , 1e+018 , 1e+019 , 1e+020 , 1e+021 , 1e+022
} ;
static double const stbsp__negbot [ 22 ] = {
1e-001 , 1e-002 , 1e-003 , 1e-004 , 1e-005 , 1e-006 , 1e-007 , 1e-008 , 1e-009 , 1e-010 , 1e-011 ,
1e-012 , 1e-013 , 1e-014 , 1e-015 , 1e-016 , 1e-017 , 1e-018 , 1e-019 , 1e-020 , 1e-021 , 1e-022
} ;
static double const stbsp__negboterr [ 22 ] = {
- 5.551115123125783e-018 , - 2.0816681711721684e-019 , - 2.0816681711721686e-020 , - 4.7921736023859299e-021 , - 8.1803053914031305e-022 , 4.5251888174113741e-023 ,
4.5251888174113739e-024 , - 2.0922560830128471e-025 , - 6.2281591457779853e-026 , - 3.6432197315497743e-027 , 6.0503030718060191e-028 , 2.0113352370744385e-029 ,
- 3.0373745563400371e-030 , 1.1806906454401013e-032 , - 7.7705399876661076e-032 , 2.0902213275965398e-033 , - 7.1542424054621921e-034 , - 7.1542424054621926e-035 ,
2.4754073164739869e-036 , 5.4846728545790429e-037 , 9.2462547772103625e-038 , - 4.8596774326570872e-039
} ;
static double const stbsp__top [ 13 ] = {
1e+023 , 1e+046 , 1e+069 , 1e+092 , 1e+115 , 1e+138 , 1e+161 , 1e+184 , 1e+207 , 1e+230 , 1e+253 , 1e+276 , 1e+299
} ;
static double const stbsp__negtop [ 13 ] = {
1e-023 , 1e-046 , 1e-069 , 1e-092 , 1e-115 , 1e-138 , 1e-161 , 1e-184 , 1e-207 , 1e-230 , 1e-253 , 1e-276 , 1e-299
} ;
static double const stbsp__toperr [ 13 ] = {
8388608 ,
6.8601809640529717e+028 ,
- 7.253143638152921e+052 ,
- 4.3377296974619174e+075 ,
- 1.5559416129466825e+098 ,
- 3.2841562489204913e+121 ,
- 3.7745893248228135e+144 ,
- 1.7356668416969134e+167 ,
- 3.8893577551088374e+190 ,
- 9.9566444326005119e+213 ,
6.3641293062232429e+236 ,
- 5.2069140800249813e+259 ,
- 5.2504760255204387e+282
} ;
static double const stbsp__negtoperr [ 13 ] = {
3.9565301985100693e-040 , - 2.299904345391321e-063 , 3.6506201437945798e-086 , 1.1875228833981544e-109 ,
- 5.0644902316928607e-132 , - 6.7156837247865426e-155 , - 2.812077463003139e-178 , - 5.7778912386589953e-201 ,
7.4997100559334532e-224 , - 4.6439668915134491e-247 , - 6.3691100762962136e-270 , - 9.436808465446358e-293 ,
8.0970921678014997e-317
} ;
2018-05-17 11:19:58 +00:00
2018-07-04 14:47:11 +00:00
# if defined(_MSC_VER) && (_MSC_VER <= 1200)
static stbsp__uint64 const stbsp__powten [ 20 ] = {
1 ,
10 ,
100 ,
1000 ,
10000 ,
100000 ,
1000000 ,
10000000 ,
100000000 ,
1000000000 ,
10000000000 ,
100000000000 ,
1000000000000 ,
10000000000000 ,
100000000000000 ,
1000000000000000 ,
10000000000000000 ,
100000000000000000 ,
1000000000000000000 ,
10000000000000000000U
} ;
2018-06-11 06:26:57 +00:00
# define stbsp__tento19th ((stbsp__uint64)1000000000000000000)
# else
2018-07-04 14:47:11 +00:00
static stbsp__uint64 const stbsp__powten [ 20 ] = {
1 ,
10 ,
100 ,
1000 ,
10000 ,
100000 ,
1000000 ,
10000000 ,
100000000 ,
1000000000 ,
10000000000ULL ,
100000000000ULL ,
1000000000000ULL ,
10000000000000ULL ,
100000000000000ULL ,
1000000000000000ULL ,
10000000000000000ULL ,
100000000000000000ULL ,
1000000000000000000ULL ,
10000000000000000000ULL
} ;
2018-06-11 06:26:57 +00:00
# define stbsp__tento19th (1000000000000000000ULL)
# endif
2018-05-17 11:19:58 +00:00
2018-07-04 14:47:11 +00:00
# define stbsp__ddmulthi(oh, ol, xh, yh) \
{ \
double ahi = 0 , alo , bhi = 0 , blo ; \
stbsp__int64 bt ; \
oh = xh * yh ; \
STBSP__COPYFP ( bt , xh ) ; \
bt & = ( ( ~ ( stbsp__uint64 ) 0 ) < < 27 ) ; \
STBSP__COPYFP ( ahi , bt ) ; \
alo = xh - ahi ; \
STBSP__COPYFP ( bt , yh ) ; \
bt & = ( ( ~ ( stbsp__uint64 ) 0 ) < < 27 ) ; \
STBSP__COPYFP ( bhi , bt ) ; \
blo = yh - bhi ; \
ol = ( ( ahi * bhi - oh ) + ahi * blo + alo * bhi ) + alo * blo ; \
}
# define stbsp__ddtoS64(ob, xh, xl) \
{ \
double ahi = 0 , alo , vh , t ; \
ob = ( stbsp__int64 ) ph ; \
vh = ( double ) ob ; \
ahi = ( xh - vh ) ; \
t = ( ahi - xh ) ; \
alo = ( xh - ( ahi - t ) ) - ( vh + t ) ; \
ob + = ( stbsp__int64 ) ( ahi + alo + xl ) ; \
}
# define stbsp__ddrenorm(oh, ol) \
{ \
double s ; \
s = oh + ol ; \
ol = ol - ( s - oh ) ; \
oh = s ; \
}
# define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh);
# define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl);
static void stbsp__raise_to_power10 ( double * ohi , double * olo , double d , stbsp__int32 power ) // power can be -323 to +350
{
double ph , pl ;
if ( ( power > = 0 ) & & ( power < = 22 ) ) {
stbsp__ddmulthi ( ph , pl , d , stbsp__bot [ power ] ) ;
} else {
stbsp__int32 e , et , eb ;
double p2h , p2l ;
e = power ;
if ( power < 0 )
e = - e ;
et = ( e * 0x2c9 ) > > 14 ; /* %23 */
if ( et > 13 )
et = 13 ;
eb = e - ( et * 23 ) ;
ph = d ;
pl = 0.0 ;
if ( power < 0 ) {
if ( eb ) {
- - eb ;
stbsp__ddmulthi ( ph , pl , d , stbsp__negbot [ eb ] ) ;
stbsp__ddmultlos ( ph , pl , d , stbsp__negboterr [ eb ] ) ;
}
if ( et ) {
stbsp__ddrenorm ( ph , pl ) ;
- - et ;
stbsp__ddmulthi ( p2h , p2l , ph , stbsp__negtop [ et ] ) ;
stbsp__ddmultlo ( p2h , p2l , ph , pl , stbsp__negtop [ et ] , stbsp__negtoperr [ et ] ) ;
ph = p2h ;
pl = p2l ;
}
} else {
if ( eb ) {
e = eb ;
if ( eb > 22 )
eb = 22 ;
e - = eb ;
stbsp__ddmulthi ( ph , pl , d , stbsp__bot [ eb ] ) ;
if ( e ) {
stbsp__ddrenorm ( ph , pl ) ;
stbsp__ddmulthi ( p2h , p2l , ph , stbsp__bot [ e ] ) ;
stbsp__ddmultlos ( p2h , p2l , stbsp__bot [ e ] , pl ) ;
ph = p2h ;
pl = p2l ;
}
}
if ( et ) {
stbsp__ddrenorm ( ph , pl ) ;
- - et ;
stbsp__ddmulthi ( p2h , p2l , ph , stbsp__top [ et ] ) ;
stbsp__ddmultlo ( p2h , p2l , ph , pl , stbsp__top [ et ] , stbsp__toperr [ et ] ) ;
ph = p2h ;
pl = p2l ;
}
2018-06-11 06:26:57 +00:00
}
2018-07-04 14:47:11 +00:00
}
stbsp__ddrenorm ( ph , pl ) ;
* ohi = ph ;
* olo = pl ;
2017-06-22 04:00:11 +00:00
}
2018-06-11 06:26:57 +00:00
// given a float value, returns the significant bits in bits, and the position of the
// decimal point in decimal_pos. +/-INF and NAN are specified by special values
// returned in the decimal_pos parameter.
// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
2018-07-04 14:47:11 +00:00
static stbsp__int32 stbsp__real_to_str ( char const * * start , stbsp__uint32 * len , char * out , stbsp__int32 * decimal_pos , double value , stbsp__uint32 frac_digits )
{
double d ;
stbsp__int64 bits = 0 ;
stbsp__int32 expo , e , ng , tens ;
d = value ;
STBSP__COPYFP ( bits , d ) ;
expo = ( stbsp__int32 ) ( ( bits > > 52 ) & 2047 ) ;
ng = ( stbsp__int32 ) ( bits > > 63 ) ;
if ( ng )
d = - d ;
if ( expo = = 2047 ) // is nan or inf?
{
* start = ( bits & ( ( ( ( stbsp__uint64 ) 1 ) < < 52 ) - 1 ) ) ? " NaN " : " Inf " ;
* decimal_pos = STBSP__SPECIAL ;
* len = 3 ;
2018-06-11 06:26:57 +00:00
return ng ;
2018-07-04 14:47:11 +00:00
}
if ( expo = = 0 ) // is zero or denormal
{
if ( ( bits < < 1 ) = = 0 ) // do zero
{
* decimal_pos = 1 ;
* start = out ;
out [ 0 ] = ' 0 ' ;
* len = 1 ;
return ng ;
}
// find the right expo for denormals
{
stbsp__int64 v = ( ( stbsp__uint64 ) 1 ) < < 51 ;
while ( ( bits & v ) = = 0 ) {
- - expo ;
v > > = 1 ;
}
}
}
// find the decimal exponent as well as the decimal bits of the value
{
double ph , pl ;
// log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
tens = expo - 1023 ;
tens = ( tens < 0 ) ? ( ( tens * 617 ) / 2048 ) : ( ( ( tens * 1233 ) / 4096 ) + 1 ) ;
// move the significant bits into position and stick them into an int
stbsp__raise_to_power10 ( & ph , & pl , d , 18 - tens ) ;
// get full as much precision from double-double as possible
stbsp__ddtoS64 ( bits , ph , pl ) ;
// check if we undershot
if ( ( ( stbsp__uint64 ) bits ) > = stbsp__tento19th )
+ + tens ;
}
// now do the rounding in integer land
frac_digits = ( frac_digits & 0x80000000 ) ? ( ( frac_digits & 0x7ffffff ) + 1 ) : ( tens + frac_digits ) ;
if ( ( frac_digits < 24 ) ) {
stbsp__uint32 dg = 1 ;
if ( ( stbsp__uint64 ) bits > = stbsp__powten [ 9 ] )
dg = 10 ;
while ( ( stbsp__uint64 ) bits > = stbsp__powten [ dg ] ) {
+ + dg ;
if ( dg = = 20 )
goto noround ;
}
if ( frac_digits < dg ) {
stbsp__uint64 r ;
// add 0.5 at the right position and round
e = dg - frac_digits ;
if ( ( stbsp__uint32 ) e > = 24 )
goto noround ;
r = stbsp__powten [ e ] ;
bits = bits + ( r / 2 ) ;
if ( ( stbsp__uint64 ) bits > = stbsp__powten [ dg ] )
+ + tens ;
bits / = r ;
}
noround : ;
}
// kill long trailing runs of zeros
if ( bits ) {
stbsp__uint32 n ;
for ( ; ; ) {
if ( bits < = 0xffffffff )
break ;
if ( bits % 1000 )
goto donez ;
bits / = 1000 ;
}
n = ( stbsp__uint32 ) bits ;
while ( ( n % 1000 ) = = 0 )
n / = 1000 ;
bits = n ;
donez : ;
}
// convert to string
out + = 64 ;
e = 0 ;
for ( ; ; ) {
stbsp__uint32 n ;
char * o = out - 8 ;
// do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
if ( bits > = 100000000 ) {
n = ( stbsp__uint32 ) ( bits % 100000000 ) ;
bits / = 100000000 ;
} else {
n = ( stbsp__uint32 ) bits ;
bits = 0 ;
}
while ( n ) {
out - = 2 ;
* ( stbsp__uint16 * ) out = * ( stbsp__uint16 * ) & stbsp__digitpair [ ( n % 100 ) * 2 ] ;
n / = 100 ;
e + = 2 ;
}
if ( bits = = 0 ) {
if ( ( e ) & & ( out [ 0 ] = = ' 0 ' ) ) {
+ + out ;
- - e ;
}
break ;
}
while ( out ! = o ) {
* - - out = ' 0 ' ;
+ + e ;
}
}
* decimal_pos = tens ;
* start = out ;
* len = e ;
return ng ;
2017-06-20 12:23:34 +00:00
}
2017-04-10 11:31:11 +00:00
2018-06-11 06:26:57 +00:00
# undef stbsp__ddmulthi
# undef stbsp__ddrenorm
# undef stbsp__ddmultlo
# undef stbsp__ddmultlos
# undef STBSP__SPECIAL
# undef STBSP__COPYFP
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
# endif // STB_SPRINTF_NOFLOAT
2018-05-17 11:19:58 +00:00
2018-06-11 06:26:57 +00:00
// clean up
# undef stbsp__uint16
# undef stbsp__uint32
# undef stbsp__int32
# undef stbsp__uint64
# undef stbsp__int64
# undef STBSP__UNALIGNED
2017-06-22 04:00:11 +00:00
# ifdef __GNUC__
2018-06-11 06:26:57 +00:00
# pragma GCC diagnostic pop
2017-06-22 04:00:11 +00:00
# endif
# endif // DQN_IMPLEMENTATION
2017-04-10 11:31:11 +00:00
2018-06-13 13:16:05 +00:00
# if defined(DQN__XPLATFORM_LAYER)
2018-01-18 09:25:44 +00:00
// #XPlatform (Win32 & Unix)
// =================================================================================================
2017-06-22 04:00:11 +00:00
// Functions in the Cross Platform are guaranteed to be supported in both Unix
// and Win32
2018-07-09 14:53:54 +00:00
# ifdef DQN_IS_UNIX
2018-05-17 11:19:58 +00:00
# include <stdio.h> // Basic File I/O // TODO(doyle): Syscall versions
2017-06-25 15:57:51 +00:00
2018-05-17 11:19:58 +00:00
# include <dirent.h> // readdir()/opendir()/closedir()
# include <sys/stat.h> // file size query
# include <sys/time.h> // high resolution timer
# include <time.h> // timespec
# include <unistd.h> // unlink()
2017-06-22 08:10:44 +00:00
# endif
2018-08-29 15:04:56 +00:00
# define DQN_FILE__LIST_DIR(name) DQN_FILE_SCOPE char **name(char const *dir, i32 *num_files, DqnAllocator *allocator)
2018-03-08 15:03:05 +00:00
2018-01-18 09:25:44 +00:00
// XPlatform > #DqnFile
// =================================================================================================
2018-07-09 14:53:54 +00:00
# ifdef DQN_IS_WIN32
2018-05-18 13:19:50 +00:00
2018-06-25 14:33:46 +00:00
FILE_SCOPE bool
DqnFile__Win32Open ( wchar_t const * path , DqnFile * file , u32 flags , DqnFile : : Action action )
2017-06-22 04:00:11 +00:00
{
2018-05-17 11:19:58 +00:00
if ( ! file | | ! path ) return false ;
2018-05-18 13:19:50 +00:00
u32 const WIN32_GENERIC_READ = 0x80000000L ;
u32 const WIN32_GENERIC_WRITE = 0x40000000L ;
u32 const WIN32_GENERIC_EXECUTE = 0x20000000L ;
u32 const WIN32_GENERIC_ALL = 0x10000000L ;
u32 const WIN32_CREATE_NEW = 1 ;
u32 const WIN32_CREATE_ALWAYS = 2 ;
u32 const WIN32_OPEN_EXISTING = 3 ;
u32 const WIN32_OPEN_ALWAYS = 4 ;
u32 const WIN32_TRUNCATE_EXISTING = 5 ;
u32 const WIN32_FILE_ATTRIBUTE_NORMAL = 0x00000080 ;
2018-08-05 07:30:54 +00:00
DWORD win32_flag = 0 ;
2018-07-28 05:16:01 +00:00
if ( flags & DqnFile : : Flag : : All )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
win32_flag = WIN32_GENERIC_ALL ;
2018-05-17 11:19:58 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
if ( flags & DqnFile : : Flag : : FileRead ) win32_flag | = WIN32_GENERIC_READ ;
if ( flags & DqnFile : : Flag : : FileWrite ) win32_flag | = WIN32_GENERIC_WRITE ;
if ( flags & DqnFile : : Flag : : Execute ) win32_flag | = WIN32_GENERIC_EXECUTE ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
DWORD win32_action = 0 ;
2018-05-17 11:19:58 +00:00
switch ( action )
{
// Allow fall through
default : DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
2018-08-05 07:30:54 +00:00
case DqnFile : : Action : : OpenOnly : win32_action = WIN32_OPEN_EXISTING ; break ;
case DqnFile : : Action : : ClearIfExist : win32_action = WIN32_TRUNCATE_EXISTING ; break ;
case DqnFile : : Action : : CreateIfNotExist : win32_action = WIN32_CREATE_NEW ; break ;
case DqnFile : : Action : : ForceCreate : win32_action = WIN32_CREATE_ALWAYS ; break ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
HANDLE handle = CreateFileW ( path , win32_flag , 0 , nullptr , win32_action ,
2018-05-18 13:19:50 +00:00
WIN32_FILE_ATTRIBUTE_NORMAL , nullptr ) ;
2018-05-17 11:19:58 +00:00
if ( handle = = INVALID_HANDLE_VALUE )
{
return false ;
}
LARGE_INTEGER size ;
if ( GetFileSizeEx ( handle , & size ) = = 0 )
{
file - > Close ( ) ;
DqnWin32_DisplayLastError ( " GetFileSizeEx() failed " ) ;
return false ;
}
file - > handle = handle ;
file - > size = ( usize ) size . QuadPart ;
file - > flags = flags ;
return true ;
2017-04-10 11:31:11 +00:00
}
2018-06-25 14:33:46 +00:00
DQN_FILE__LIST_DIR ( DqnFile__PlatformListDir )
2017-06-20 12:23:34 +00:00
{
2018-05-17 11:19:58 +00:00
if ( ! dir ) return nullptr ;
2018-08-05 07:30:54 +00:00
i32 curr_num_files = 0 ;
wchar_t wide_dir [ MAX_PATH ] = { 0 } ;
DqnWin32_UTF8ToWChar ( dir , wide_dir , DQN_ARRAY_COUNT ( wide_dir ) ) ;
2018-05-17 11:19:58 +00:00
// Enumerate number of files first
{
2018-08-05 07:30:54 +00:00
WIN32_FIND_DATAW find_data = { 0 } ;
HANDLE find_handle = FindFirstFileW ( wide_dir , & find_data ) ;
if ( find_handle = = INVALID_HANDLE_VALUE )
2018-05-17 11:19:58 +00:00
{
2018-06-13 13:16:05 +00:00
DQN__WIN32_ERROR_BOX ( " FindFirstFile() failed. " , nullptr ) ;
2018-05-17 11:19:58 +00:00
return nullptr ;
}
2018-09-02 11:17:14 +00:00
DQN_DEFER { FindClose ( find_handle ) ; } ;
2018-08-05 07:30:54 +00:00
bool stay_in_loop = true ;
while ( stay_in_loop )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
BOOL result = FindNextFileW ( find_handle , & find_data ) ;
2018-05-17 11:19:58 +00:00
if ( result = = 0 )
{
DWORD error = GetLastError ( ) ;
2018-05-18 13:19:50 +00:00
u32 const WIN32_ERROR_NO_MORE_FILES = 18L ;
if ( error ! = WIN32_ERROR_NO_MORE_FILES )
2018-05-17 11:19:58 +00:00
{
DqnWin32_DisplayErrorCode ( error , " FindNextFileW() failed " ) ;
}
2018-08-05 07:30:54 +00:00
stay_in_loop = false ;
2018-05-17 11:19:58 +00:00
}
else
{
2018-08-05 07:30:54 +00:00
curr_num_files + + ;
2018-05-17 11:19:58 +00:00
}
}
}
2018-08-05 07:30:54 +00:00
if ( curr_num_files = = 0 )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
* num_files = 0 ;
2018-05-17 11:19:58 +00:00
return nullptr ;
}
{
2018-08-05 07:30:54 +00:00
WIN32_FIND_DATAW init_find = { } ;
HANDLE find_handle = FindFirstFileW ( wide_dir , & init_find ) ;
if ( find_handle = = INVALID_HANDLE_VALUE )
2018-05-17 11:19:58 +00:00
{
2018-06-13 13:16:05 +00:00
DQN__WIN32_ERROR_BOX ( " FindFirstFile() failed. " , nullptr ) ;
2018-08-05 07:30:54 +00:00
* num_files = 0 ;
2018-05-17 11:19:58 +00:00
return nullptr ;
}
2018-09-02 11:17:14 +00:00
DQN_DEFER { FindClose ( find_handle ) ; } ;
2018-09-10 12:49:17 +00:00
char * * list = ( char * * ) allocator - > Malloc ( sizeof ( * list ) * ( curr_num_files ) , Dqn : : ZeroMem : : Yes ) ;
2018-05-17 11:19:58 +00:00
if ( ! list )
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Memory allocation failed, required: %$_d " , sizeof ( * list ) * curr_num_files ) ;
2018-08-05 07:30:54 +00:00
* num_files = 0 ;
2018-05-17 11:19:58 +00:00
return nullptr ;
}
2018-09-10 12:49:17 +00:00
DQN_FOR_EACH ( i , curr_num_files )
2018-05-17 11:19:58 +00:00
{
// TODO(doyle): Max path is bad.
2018-09-10 12:49:17 +00:00
size_t bytes_required = sizeof ( * * list ) * MAX_PATH ;
list [ i ] = ( char * ) allocator - > Malloc ( bytes_required , Dqn : : ZeroMem : : Yes ) ;
2018-05-17 11:19:58 +00:00
if ( ! list [ i ] )
{
2018-09-10 12:49:17 +00:00
DQN_FOR_EACH ( j , i )
allocator - > Free ( list [ j ] , bytes_required ) ;
2018-05-17 11:19:58 +00:00
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Memory allocation failed, required: %$_d " , sizeof ( * * list ) * MAX_PATH ) ;
2018-08-05 07:30:54 +00:00
* num_files = 0 ;
2018-05-17 11:19:58 +00:00
return nullptr ;
}
}
2018-08-05 07:30:54 +00:00
i32 list_index = 0 ;
WIN32_FIND_DATAW find_data = { 0 } ;
while ( FindNextFileW ( find_handle , & find_data ) ! = 0 )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
DqnWin32_WCharToUTF8 ( find_data . cFileName , list [ list_index + + ] , MAX_PATH ) ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
* num_files = curr_num_files ;
2018-05-17 11:19:58 +00:00
return list ;
}
2017-06-20 12:23:34 +00:00
}
2018-07-09 14:53:54 +00:00
# endif // DQN_IS_WIN32
2017-04-10 11:31:11 +00:00
2018-07-09 14:53:54 +00:00
# ifdef DQN_IS_UNIX
2018-07-05 01:26:04 +00:00
FILE_SCOPE bool DqnFile__UnixGetFileSize ( char const * path , usize * size )
2017-06-26 11:46:28 +00:00
{
2018-08-05 07:30:54 +00:00
struct stat file_stat = { } ;
stat ( path , & file_stat ) ;
2018-08-27 14:25:08 +00:00
if ( size ) * size = file_stat . st_size ;
2017-06-26 11:46:28 +00:00
2018-08-27 14:25:08 +00:00
if ( file_stat . st_size ! = 0 )
2018-07-10 05:52:12 +00:00
return true ;
2018-06-25 14:33:46 +00:00
// NOTE: Can occur in some instances where files are generated on demand, i.e. /proc/cpuinfo.
// But there can also be zero-byte files, we can't be sure. So manual check by counting bytes
2018-08-27 14:25:08 +00:00
if ( size & & FILE * file = fopen ( path , " rb " ) )
2018-05-17 11:19:58 +00:00
{
2018-09-02 11:17:14 +00:00
DQN_DEFER { fclose ( file ) ; } ;
2018-07-10 05:52:12 +00:00
while ( fgetc ( file ) ! = EOF )
2018-07-27 04:43:39 +00:00
{
( * size ) + + ;
}
2018-05-17 11:19:58 +00:00
}
2018-06-25 14:33:46 +00:00
return true ;
2017-06-26 11:46:28 +00:00
}
2018-06-25 14:33:46 +00:00
FILE_SCOPE bool
DqnFile__UnixOpen ( char const * path , DqnFile * file , u32 flags , DqnFile : : Action action )
2017-06-20 12:23:34 +00:00
{
2018-05-17 11:19:58 +00:00
char operation = 0 ;
2018-08-05 07:30:54 +00:00
bool update_flag = false ;
2018-05-17 11:19:58 +00:00
2018-07-28 05:16:01 +00:00
if ( flags & DqnFile : : Flag : : FileWrite )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
update_flag = true ;
2018-05-17 11:19:58 +00:00
switch ( action )
{
default : DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
case DqnFile : : Action : : OpenOnly :
{
operation = ' r ' ;
}
break ;
case DqnFile : : Action : : CreateIfNotExist :
case DqnFile : : Action : : ClearIfExist :
case DqnFile : : Action : : ForceCreate :
{
operation = ' w ' ;
}
break ;
}
}
2018-07-28 05:16:01 +00:00
else if ( ( flags & DqnFile : : Flag : : FileRead ) | |
( flags & DqnFile : : Flag : : Execute ) )
2018-05-17 11:19:58 +00:00
{
2018-07-28 05:16:01 +00:00
if ( flags & DqnFile : : Flag : : Execute )
2018-05-17 11:19:58 +00:00
{
// TODO(doyle): Logging, UNIX doesn't have execute param for file
// handles. Execution goes through system()
}
operation = ' r ' ;
}
DQN_ASSERT ( operation ! = 0 ) ;
2018-06-25 14:33:46 +00:00
// TODO(doyle): What about not reading as a binary file and appending to end of file.
2018-08-05 07:30:54 +00:00
u32 mode_index = 0 ;
2018-05-17 11:19:58 +00:00
char mode [ 4 ] = { } ;
2018-08-05 07:30:54 +00:00
mode [ mode_index + + ] = operation ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
if ( update_flag ) mode [ mode_index + + ] = ' + ' ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
mode [ mode_index + + ] = ' b ' ;
DQN_ASSERT ( mode_index < = DQN_ARRAY_COUNT ( mode ) - 1 ) ;
2018-05-17 11:19:58 +00:00
// TODO(doyle): Use open syscall
// TODO(doyle): Query errno
2018-07-05 01:26:04 +00:00
if ( ! DqnFile__UnixGetFileSize ( path , & file - > size ) )
2018-05-17 11:19:58 +00:00
{
return false ;
}
2018-07-10 05:52:12 +00:00
file - > handle = fopen ( path , mode ) ;
2018-06-25 14:33:46 +00:00
file - > flags = flags ;
2018-07-04 14:47:11 +00:00
return true ;
2017-06-20 12:23:34 +00:00
}
2017-04-10 11:31:11 +00:00
2018-06-25 14:33:46 +00:00
DQN_FILE__LIST_DIR ( DqnFile__PlatformListDir )
2017-04-10 11:31:11 +00:00
{
2018-05-17 11:19:58 +00:00
if ( ! dir ) return nullptr ;
// Enumerate num files
2018-08-05 07:30:54 +00:00
i32 curr_num_files = 0 ;
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
DIR * const dir_handle = opendir ( dir ) ;
if ( ! dir_handle ) return nullptr ;
2018-09-02 11:17:14 +00:00
DQN_DEFER { closedir ( dir_handle ) ; } ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
struct dirent * dir_file = readdir ( dir_handle ) ;
while ( dir_file )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
curr_num_files + + ;
dir_file = readdir ( dir_handle ) ;
2018-05-17 11:19:58 +00:00
}
}
2018-08-05 07:30:54 +00:00
if ( curr_num_files = = 0 )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
* num_files = 0 ;
2018-05-17 11:19:58 +00:00
return nullptr ;
}
// Create file list
{
2018-08-05 07:30:54 +00:00
DIR * const dir_handle = opendir ( dir ) ;
if ( ! dir_handle ) return nullptr ;
2018-09-02 11:17:14 +00:00
DQN_DEFER { closedir ( dir_handle ) ; } ;
2018-05-17 11:19:58 +00:00
2018-08-29 15:04:56 +00:00
char * * list = ( char * * ) allocator - > Calloc ( 1 , sizeof ( * list ) * curr_num_files ) ;
2018-05-17 11:19:58 +00:00
if ( ! list )
{
2018-08-05 07:30:54 +00:00
DQN_LOGE ( " Memory allocation failed, required: %$_d " , sizeof ( * list ) * curr_num_files ) ;
* num_files = 0 ;
2018-05-17 11:19:58 +00:00
return nullptr ;
}
2018-08-05 07:30:54 +00:00
struct dirent * dir_file = readdir ( dir_handle ) ;
for ( auto i = 0 ; i < curr_num_files ; i + + )
2018-05-17 11:19:58 +00:00
{
2018-08-29 15:04:56 +00:00
list [ i ] = ( char * ) allocator - > Calloc ( 1 , sizeof ( * * list ) * DQN_ARRAY_COUNT ( dir_file - > d_name ) ) ;
2018-05-17 11:19:58 +00:00
if ( ! list [ i ] )
{
for ( auto j = 0 ; j < i ; j + + ) api - > Free ( list [ j ] ) ;
2018-08-05 07:30:54 +00:00
DQN_LOGE ( " Memory allocation failed, required: %$_d " , sizeof ( * * list ) * DQN_ARRAY_COUNT ( dir_file - > d_name ) ) ;
* num_files = 0 ;
2018-05-17 11:19:58 +00:00
return nullptr ;
}
}
2018-08-05 07:30:54 +00:00
u32 list_index = 0 ;
* num_files = curr_num_files ;
while ( dir_file )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
DqnMem_Copy ( list [ list_index + + ] , dir_file - > d_name , DQN_ARRAY_COUNT ( dir_file - > d_name ) ) ;
dir_file = readdir ( dir_handle ) ;
2018-05-17 11:19:58 +00:00
}
return list ;
}
2017-06-20 12:23:34 +00:00
}
2018-07-09 14:53:54 +00:00
# endif // DQN_IS_UNIX
2018-06-25 14:33:46 +00:00
2018-06-09 04:29:19 +00:00
bool DqnFile : : Open ( char const * path , u32 flags_ , Action action )
2018-01-03 13:40:27 +00:00
{
2018-05-17 11:19:58 +00:00
if ( ! path ) return false ;
2017-04-14 15:27:11 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
// TODO(doyle): MAX PATH is baad
2018-08-05 07:30:54 +00:00
wchar_t wide_path [ MAX_PATH ] = { } ;
DqnWin32_UTF8ToWChar ( path , wide_path , DQN_ARRAY_COUNT ( wide_path ) ) ;
return DqnFile__Win32Open ( wide_path , this , flags_ , action ) ;
2017-06-22 04:00:11 +00:00
# else
2018-07-04 14:47:11 +00:00
return DqnFile__UnixOpen ( path , this , flags_ , action ) ;
2017-06-22 04:00:11 +00:00
# endif
2017-06-20 12:23:34 +00:00
}
2017-04-14 15:27:11 +00:00
2018-06-09 04:29:19 +00:00
bool DqnFile : : Open ( wchar_t const * path , u32 flags_ , Action action )
2017-06-20 12:23:34 +00:00
{
2018-05-17 11:19:58 +00:00
if ( ! path ) return false ;
2017-06-20 12:23:34 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-06-25 14:33:46 +00:00
return DqnFile__Win32Open ( path , this , flags_ , action ) ;
2018-01-03 13:40:27 +00:00
2017-06-22 04:00:11 +00:00
# else
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
return false ;
2017-06-22 04:00:11 +00:00
# endif
}
2018-09-02 11:17:14 +00:00
usize DqnFile : : Write ( u8 const * buf , usize num_bytes_to_write )
2017-06-22 04:00:11 +00:00
{
2018-05-17 11:19:58 +00:00
// TODO(doyle): Implement when it's needed
2018-09-02 11:17:14 +00:00
usize file_offset = 0 ;
2018-08-05 07:30:54 +00:00
usize num_bytes_written = 0 ;
2017-06-22 04:00:11 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-09-02 11:17:14 +00:00
DQN_ALWAYS_ASSERTM ( file_offset = = 0 , " File writing into offset is not implemented. " ) ;
2018-08-05 07:30:54 +00:00
DWORD bytes_to_write = ( DWORD ) num_bytes_to_write ;
DWORD bytes_written ;
BOOL result = WriteFile ( this - > handle , ( void * ) buf , bytes_to_write , & bytes_written , nullptr ) ;
2017-06-22 04:00:11 +00:00
2018-08-05 07:30:54 +00:00
num_bytes_written = ( usize ) bytes_written ;
2018-05-17 11:19:58 +00:00
// TODO(doyle): Better logging system
if ( result = = 0 )
{
2018-06-13 13:16:05 +00:00
DQN__WIN32_ERROR_BOX ( " ReadFile() failed. " , nullptr ) ;
2018-05-17 11:19:58 +00:00
}
2017-06-22 04:00:11 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
const usize ITEMS_TO_WRITE = 1 ;
2018-08-05 07:30:54 +00:00
if ( fwrite ( buf , num_bytes_to_write , ITEMS_TO_WRITE , ( FILE * ) this - > handle ) = = ITEMS_TO_WRITE )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
num_bytes_written = ITEMS_TO_WRITE * num_bytes_to_write ;
2018-05-17 11:19:58 +00:00
}
2017-06-22 04:00:11 +00:00
# endif
2018-08-05 07:30:54 +00:00
return num_bytes_written ;
2017-04-14 15:27:11 +00:00
}
2018-08-05 07:30:54 +00:00
usize DqnFile : : Read ( u8 * buf , usize num_bytes_to_read )
2017-04-14 15:27:11 +00:00
{
2018-08-05 07:30:54 +00:00
usize num_bytes_read = 0 ;
2018-08-03 13:30:58 +00:00
if ( buf & & this - > handle )
2018-05-17 11:19:58 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
DWORD bytes_to_read = ( DWORD ) num_bytes_to_read ;
DWORD bytes_read = 0 ;
HANDLE win32_handle = this - > handle ;
2017-06-21 07:48:00 +00:00
2018-08-05 07:30:54 +00:00
BOOL result = ReadFile ( win32_handle , ( void * ) buf , bytes_to_read , & bytes_read , nullptr ) ;
2017-06-22 04:00:11 +00:00
2018-08-05 07:30:54 +00:00
num_bytes_read = ( usize ) bytes_read ;
2018-05-17 11:19:58 +00:00
// TODO(doyle): 0 also means it is completing async, but still valid
if ( result = = 0 )
{
2018-06-13 13:16:05 +00:00
DQN__WIN32_ERROR_BOX ( " ReadFile() failed. " , nullptr ) ;
2018-05-17 11:19:58 +00:00
}
2017-06-22 04:00:11 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
// TODO(doyle): Syscall version
const usize ITEMS_TO_READ = 1 ;
2018-08-05 07:30:54 +00:00
if ( fread ( buf , num_bytes_to_read , ITEMS_TO_READ , ( FILE * ) this - > handle ) = = ITEMS_TO_READ )
2018-05-17 11:19:58 +00:00
{
rewind ( ( FILE * ) this - > handle ) ;
2018-08-05 07:30:54 +00:00
num_bytes_read = ITEMS_TO_READ * num_bytes_to_read ;
2018-05-17 11:19:58 +00:00
}
else
{
// TODO(doyle): Logging, failed read
}
2017-06-22 04:00:11 +00:00
# endif
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
return num_bytes_read ;
2017-04-14 15:27:11 +00:00
}
2018-08-29 15:04:56 +00:00
u8 * DqnFile_ReadAll ( wchar_t const * path , usize * buf_size , DqnAllocator * allocator )
2017-10-28 08:58:09 +00:00
{
2018-05-17 11:19:58 +00:00
// TODO(doyle): Logging
2018-08-05 07:30:54 +00:00
usize required_size = 0 ;
if ( ! DqnFile_Size ( path , & required_size ) | | required_size = = 0 )
2018-05-17 11:19:58 +00:00
return nullptr ;
2017-10-28 08:58:09 +00:00
2018-08-29 15:04:56 +00:00
auto * buf = ( u8 * ) allocator - > Malloc ( required_size ) ;
2018-08-05 07:30:54 +00:00
if ( DqnFile_ReadAll ( path , buf , required_size ) )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
* buf_size = required_size ;
2018-05-17 11:19:58 +00:00
return buf ;
}
2017-10-28 08:58:09 +00:00
2018-09-10 12:49:17 +00:00
allocator - > Free ( buf , required_size ) ;
2018-05-17 11:19:58 +00:00
return nullptr ;
2017-10-28 08:58:09 +00:00
}
2018-08-29 15:04:56 +00:00
DQN_FILE_SCOPE u8 * DqnFile_ReadAll ( char const * path , usize * buf_size , DqnAllocator * allocator )
2017-10-28 08:58:09 +00:00
{
2018-05-17 11:19:58 +00:00
// TODO(doyle): Logging
2018-08-05 07:30:54 +00:00
usize required_size = 0 ;
if ( ! DqnFile_Size ( path , & required_size ) | | required_size = = 0 )
2018-07-10 13:01:18 +00:00
return nullptr ;
2017-10-28 08:58:09 +00:00
2018-08-29 15:04:56 +00:00
auto * buf = ( u8 * ) allocator - > Malloc ( required_size ) ;
2018-08-05 07:30:54 +00:00
if ( DqnFile_ReadAll ( path , buf , required_size ) )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
* buf_size = required_size ;
2018-05-17 11:19:58 +00:00
return buf ;
}
2017-07-26 11:31:17 +00:00
2018-09-10 12:49:17 +00:00
allocator - > Free ( buf , required_size ) ;
2018-05-17 11:19:58 +00:00
return nullptr ;
2017-07-26 11:31:17 +00:00
}
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE u8 * DqnFile_ReadAll ( wchar_t const * path , usize * buf_size , DqnMemStack * stack )
2018-08-03 13:30:58 +00:00
{
u8 * result = nullptr ;
DqnFile file = { } ;
if ( ! file . Open ( path , DqnFile : : Flag : : FileRead , DqnFile : : Action : : OpenOnly ) )
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Could not open file: %s " , path ) ;
2018-08-03 13:30:58 +00:00
return result ;
}
2018-09-02 11:17:14 +00:00
DQN_DEFER { file . Close ( ) ; } ;
2018-08-03 13:30:58 +00:00
2018-09-10 12:49:17 +00:00
result = DQN_MEMSTACK_PUSH_ARRAY ( stack , u8 , file . size ) ;
2018-08-05 07:30:54 +00:00
usize bytes_read = file . Read ( result , file . size ) ;
if ( bytes_read = = file . size )
2018-08-03 13:30:58 +00:00
{
2018-08-05 07:30:54 +00:00
* buf_size = file . size ;
2018-08-03 13:30:58 +00:00
}
else
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " bytes_read != file.size " , bytes_read , file . size ) ;
2018-08-03 13:30:58 +00:00
}
return result ;
}
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE u8 * DqnFile_ReadAll ( char const * path , usize * buf_size , DqnMemStack * stack )
2018-08-03 13:30:58 +00:00
{
u8 * result = nullptr ;
DqnFile file = { } ;
if ( ! file . Open ( path , DqnFile : : Flag : : FileRead , DqnFile : : Action : : OpenOnly ) )
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Could not open file: %s " , path ) ;
2018-08-03 13:30:58 +00:00
return result ;
}
2018-09-02 11:17:14 +00:00
DQN_DEFER { file . Close ( ) ; } ;
2018-08-03 13:30:58 +00:00
2018-09-10 12:49:17 +00:00
result = DQN_MEMSTACK_PUSH_ARRAY ( stack , u8 , file . size ) ;
2018-08-05 07:30:54 +00:00
usize bytes_read = file . Read ( result , file . size ) ;
if ( bytes_read = = file . size )
2018-08-03 13:30:58 +00:00
{
2018-08-05 07:30:54 +00:00
* buf_size = file . size ;
2018-08-03 13:30:58 +00:00
}
else
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " bytes_read != file.size " , bytes_read , file . size ) ;
2018-08-03 13:30:58 +00:00
}
return result ;
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE bool DqnFile_WriteAll ( char const * path , u8 const * buf , usize const buf_size )
2018-07-28 05:16:01 +00:00
{
DqnFile file = { } ;
if ( ! file . Open ( path , DqnFile : : Flag : : FileReadWrite , DqnFile : : Action : : ForceCreate ) )
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Could not open file at: %s " , path ) ;
2018-07-28 05:16:01 +00:00
return false ;
}
2018-09-02 11:17:14 +00:00
DQN_DEFER { file . Close ( ) ; } ;
usize bytes_written = file . Write ( buf , buf_size ) ;
2018-08-05 07:30:54 +00:00
if ( bytes_written ! = buf_size )
2018-07-28 05:16:01 +00:00
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Bytes written did not match the buffer size, %zu != %zu " , bytes_written , buf_size ) ;
2018-07-28 05:16:01 +00:00
return false ;
}
return true ;
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE bool DqnFile_WriteAll ( wchar_t const * path , u8 const * buf , usize const buf_size )
2018-07-28 05:16:01 +00:00
{
DqnFile file = { } ;
if ( ! file . Open ( path , DqnFile : : Flag : : FileReadWrite , DqnFile : : Action : : ForceCreate ) )
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Could not open file at: %s " , path ) ;
2018-07-28 05:16:01 +00:00
return false ;
}
2018-09-02 11:17:14 +00:00
DQN_DEFER { file . Close ( ) ; } ;
usize bytes_written = file . Write ( buf , buf_size ) ;
2018-08-05 07:30:54 +00:00
if ( bytes_written ! = buf_size )
2018-07-28 05:16:01 +00:00
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Bytes written did not match the buffer size, %zu != %zu " , bytes_written , buf_size ) ;
2018-07-28 05:16:01 +00:00
return false ;
}
return true ;
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE bool DqnFile_ReadAll ( wchar_t const * path , u8 * buf , usize buf_size )
2017-10-28 08:58:09 +00:00
{
2018-05-17 11:19:58 +00:00
DqnFile file = { } ;
2018-07-28 05:16:01 +00:00
bool result = file . Open ( path , DqnFile : : Flag : : FileRead , DqnFile : : Action : : OpenOnly ) ;
2018-09-02 11:17:14 +00:00
DQN_DEFER { file . Close ( ) ; } ;
2017-10-28 08:58:09 +00:00
2018-05-17 11:19:58 +00:00
// TODO(doyle): Logging
2018-08-05 07:30:54 +00:00
if ( file . size > buf_size | | ! result )
2018-05-17 11:19:58 +00:00
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Insufficient buffer size given: %zu, required: %zu \n " , buf_size , file . size ) ;
2018-07-10 13:01:18 +00:00
return false ;
2018-05-17 11:19:58 +00:00
}
2017-10-28 08:58:09 +00:00
2018-08-05 07:30:54 +00:00
usize bytes_read = file . Read ( buf , file . size ) ;
DQN_ASSERT ( bytes_read = = file . size ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-10-28 08:58:09 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE bool DqnFile_ReadAll ( char const * path , u8 * buf , usize buf_size )
2017-06-26 11:46:28 +00:00
{
2018-05-17 11:19:58 +00:00
DqnFile file = { } ;
2018-07-28 05:16:01 +00:00
bool result = file . Open ( path , DqnFile : : Flag : : FileRead , DqnFile : : Action : : OpenOnly ) ;
2018-09-02 11:17:14 +00:00
DQN_DEFER { file . Close ( ) ; } ;
2017-06-26 11:46:28 +00:00
2018-08-05 07:30:54 +00:00
if ( ! result | | file . size > buf_size )
2018-05-17 11:19:58 +00:00
{
2018-07-10 13:01:18 +00:00
return false ;
2018-05-17 11:19:58 +00:00
}
2017-06-26 11:46:28 +00:00
2018-08-05 07:30:54 +00:00
usize bytes_read = file . Read ( buf , file . size ) ;
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( bytes_read = = file . size , " %zu != %zu " , bytes_read , file . size ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-06-26 11:46:28 +00:00
}
2018-01-03 13:40:27 +00:00
void DqnFile : : Close ( )
2017-04-14 15:27:11 +00:00
{
2018-05-17 11:19:58 +00:00
if ( this - > handle )
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
CloseHandle ( this - > handle ) ;
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
fclose ( ( FILE * ) this - > handle ) ;
2017-06-22 04:00:11 +00:00
# endif
2018-05-17 11:19:58 +00:00
this - > handle = nullptr ;
}
2018-01-03 13:40:27 +00:00
2018-05-17 11:19:58 +00:00
this - > size = 0 ;
this - > flags = 0 ;
2017-06-21 07:48:00 +00:00
}
2017-04-14 15:27:11 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
DQN_COMPILE_ASSERT ( sizeof ( DWORD ) = = sizeof ( u32 ) ) ;
2017-06-26 11:46:28 +00:00
# endif
2018-01-18 09:25:44 +00:00
2018-07-10 13:01:18 +00:00
bool DqnFile_Size ( wchar_t const * path , usize * size )
{
DqnFileInfo info = { } ;
if ( DqnFile_GetInfo ( path , & info ) )
2018-05-17 11:19:58 +00:00
{
2018-08-27 14:25:08 +00:00
if ( size ) * size = info . size ;
2018-05-17 11:19:58 +00:00
return true ;
}
2017-06-26 11:46:28 +00:00
2018-05-17 11:19:58 +00:00
return false ;
2017-06-26 11:46:28 +00:00
}
2018-07-10 13:01:18 +00:00
bool DqnFile_Size ( char const * path , usize * size )
2017-06-26 11:46:28 +00:00
{
2018-01-18 09:25:44 +00:00
// TODO(doyle): Logging
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
// TODO(doyle): MAX PATH is baad
2018-08-05 07:30:54 +00:00
wchar_t wide_path [ MAX_PATH ] = { 0 } ;
DqnWin32_UTF8ToWChar ( path , wide_path , DQN_ARRAY_COUNT ( wide_path ) ) ;
return DqnFile_Size ( wide_path , size ) ;
2017-06-26 11:46:28 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
// TODO(doyle): Error logging
2018-07-05 01:26:04 +00:00
bool result = DqnFile__UnixGetFileSize ( path , size ) ;
return result ;
2017-06-26 11:46:28 +00:00
# endif
}
2018-07-29 05:09:35 +00:00
bool DqnFile_MakeDir ( char const * path )
{
# if defined(DQN_IS_WIN32)
// TODO(doyle): Handle error and this is super incomplete. Cannot create
// directories recursively
CreateDirectoryA ( path , nullptr /*lpSecurityAttributes*/ ) ;
return true ;
# else
return false ;
# endif
}
2018-07-10 13:01:18 +00:00
bool DqnFile_GetInfo ( wchar_t const * path , DqnFileInfo * info )
2018-01-18 05:01:37 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
auto FileTimeToSeconds = [ ] ( FILETIME const * time ) - > i64 {
2018-08-05 07:30:54 +00:00
ULARGE_INTEGER time_large_int = { } ;
time_large_int . LowPart = time - > dwLowDateTime ;
time_large_int . HighPart = time - > dwHighDateTime ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
u64 result = ( time_large_int . QuadPart / 10000000ULL ) - 11644473600ULL ;
2018-05-17 11:19:58 +00:00
return result ;
} ;
2018-08-05 07:30:54 +00:00
WIN32_FILE_ATTRIBUTE_DATA attrib_data = { } ;
if ( GetFileAttributesExW ( path , GetFileExInfoStandard , & attrib_data ) )
2018-05-17 11:19:58 +00:00
{
2018-08-27 14:25:08 +00:00
if ( info )
{
info - > create_time_in_s = FileTimeToSeconds ( & attrib_data . ftCreationTime ) ;
info - > last_access_time_in_s = FileTimeToSeconds ( & attrib_data . ftLastAccessTime ) ;
info - > last_write_time_in_s = FileTimeToSeconds ( & attrib_data . ftLastWriteTime ) ;
// TODO(doyle): What if usize is < Quad.part?
LARGE_INTEGER large_int = { } ;
large_int . HighPart = attrib_data . nFileSizeHigh ;
large_int . LowPart = attrib_data . nFileSizeLow ;
info - > size = ( usize ) large_int . QuadPart ;
}
2018-05-17 11:19:58 +00:00
return true ;
}
2018-01-18 05:01:37 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
// NOTE: Wide char not supported on unix
DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
2018-01-18 05:01:37 +00:00
# endif
2018-05-17 11:19:58 +00:00
return false ;
2018-01-18 05:01:37 +00:00
}
2018-07-10 13:01:18 +00:00
bool DqnFile_GetInfo ( char const * path , DqnFileInfo * info )
2018-01-18 05:01:37 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
// TODO(doyle): MAX PATH is baad
2018-08-05 07:30:54 +00:00
wchar_t wide_path [ MAX_PATH ] = { } ;
DqnWin32_UTF8ToWChar ( path , wide_path , DQN_ARRAY_COUNT ( wide_path ) ) ;
return DqnFile_GetInfo ( wide_path , info ) ;
2018-01-18 05:01:37 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-08-05 07:30:54 +00:00
struct stat file_stat = { } ;
if ( stat ( path , & file_stat ) )
2018-05-17 11:19:58 +00:00
{
return false ;
}
2018-08-27 14:25:08 +00:00
if ( info )
{
info - > size = file_stat . st_size ;
info - > create_time_in_s = 0 ;
info - > last_write_time_in_s = file_stat . st_mtime ;
info - > last_access_time_in_s = file_stat . st_atime ;
}
2018-05-17 11:19:58 +00:00
return true ;
2018-01-18 05:01:37 +00:00
# endif
}
2018-07-10 13:01:18 +00:00
bool DqnFile_Delete ( char const * path )
2017-06-21 07:48:00 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-07-10 13:01:18 +00:00
bool result = DeleteFileA ( path ) ;
2018-07-04 14:47:11 +00:00
# else
2018-07-10 13:01:18 +00:00
bool result = ( unlink ( path ) = = 0 ) ;
2017-06-22 04:00:11 +00:00
# endif
2018-07-10 13:01:18 +00:00
return result ;
2017-06-22 04:00:11 +00:00
}
2018-07-10 13:01:18 +00:00
bool DqnFile_Delete ( wchar_t const * path )
2017-06-26 11:46:28 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-07-10 13:01:18 +00:00
bool result = DeleteFileW ( path ) ;
return result ;
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
return false ;
2017-06-26 11:46:28 +00:00
# endif
}
2017-10-29 00:41:26 +00:00
2018-07-10 13:01:18 +00:00
bool DqnFile_Copy ( char const * src , char const * dest )
2018-01-24 03:58:03 +00:00
{
2018-05-17 11:19:58 +00:00
// TODO(doyle): Logging
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
BOOL result = ( CopyFileA ( src , dest , /*FailIfExist*/ false ) ! = 0 ) ;
if ( result = = 0 )
DqnWin32_DisplayLastError ( " CopyFile failed: " ) ;
2018-07-10 13:01:18 +00:00
2018-05-17 11:19:58 +00:00
return ( result ! = 0 ) ;
2018-01-24 03:58:03 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
return false ;
2018-01-24 03:58:03 +00:00
# endif
}
2018-07-10 13:01:18 +00:00
bool DqnFile_Copy ( wchar_t const * src , wchar_t const * dest )
2018-01-24 03:58:03 +00:00
{
2018-05-17 11:19:58 +00:00
// TODO(doyle): Logging
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-07-10 13:01:18 +00:00
BOOL result = ( CopyFileW ( src , dest , /*FailIfExist*/ false ) ! = 0 ) ;
if ( result = = 0 )
DqnWin32_DisplayLastError ( " CopyFile failed: " ) ;
2018-01-24 03:58:03 +00:00
2018-07-10 13:01:18 +00:00
return result ;
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
return false ;
2018-01-24 03:58:03 +00:00
# endif
}
2018-08-29 15:04:56 +00:00
char * * DqnFile_ListDir ( char const * dir , i32 * num_files , DqnAllocator * allocator )
2017-06-22 04:00:11 +00:00
{
2018-08-29 15:04:56 +00:00
char * * result = DqnFile__PlatformListDir ( dir , num_files , allocator ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-06-22 04:00:11 +00:00
}
2018-08-29 15:04:56 +00:00
void DqnFile_ListDirFree ( char * * file_list , i32 num_files , DqnAllocator * allocator )
2017-06-22 04:00:11 +00:00
{
2018-08-05 07:30:54 +00:00
if ( file_list )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
for ( isize i = 0 ; i < num_files ; i + + )
2018-05-17 11:19:58 +00:00
{
2018-09-10 12:49:17 +00:00
// TODO(doyle): Free needs size? Thanks to munmap on linux
if ( file_list [ i ] ) allocator - > Free ( file_list [ i ] , DqnStr_Len ( file_list [ i ] ) ) ;
2018-08-05 07:30:54 +00:00
file_list [ i ] = nullptr ;
2018-05-17 11:19:58 +00:00
}
2017-06-22 04:00:11 +00:00
2018-09-10 12:49:17 +00:00
allocator - > Free ( file_list , num_files * sizeof ( file_list ) ) ;
2018-05-17 11:19:58 +00:00
}
2017-04-14 15:27:11 +00:00
}
2017-06-21 09:01:36 +00:00
2018-01-18 09:25:44 +00:00
// XPlatform > #DqnTimer
// =================================================================================================
2018-07-09 14:53:54 +00:00
# if defined (DQN_IS_WIN32)
2017-06-22 08:10:44 +00:00
FILE_SCOPE f64 DqnTimerInternal_Win32QueryPerfCounterTimeInMs ( )
2017-04-14 15:27:11 +00:00
{
2018-08-05 07:30:54 +00:00
LOCAL_PERSIST LARGE_INTEGER query_perf_freq = { 0 } ;
if ( query_perf_freq . QuadPart = = 0 )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
QueryPerformanceFrequency ( & query_perf_freq ) ;
DQN_ASSERT ( query_perf_freq . QuadPart ! = 0 ) ;
2018-05-17 11:19:58 +00:00
}
2017-06-20 12:23:34 +00:00
2018-08-05 07:30:54 +00:00
LARGE_INTEGER qpc_result ;
QueryPerformanceCounter ( & qpc_result ) ;
2017-06-20 12:23:34 +00:00
2018-05-17 11:19:58 +00:00
// Convert to microseconds first then divide by ticks per second then to milliseconds
2018-08-05 07:30:54 +00:00
qpc_result . QuadPart * = 1000000 ;
f64 timestamp = qpc_result . QuadPart / ( f64 ) query_perf_freq . QuadPart ;
2018-05-17 11:19:58 +00:00
timestamp / = 1000.0f ;
return timestamp ;
2017-04-14 15:27:11 +00:00
}
2017-06-22 08:10:44 +00:00
# endif
2017-04-14 15:27:11 +00:00
2017-06-22 08:10:44 +00:00
DQN_FILE_SCOPE f64 DqnTimer_NowInMs ( )
2017-04-14 15:27:11 +00:00
{
2018-05-17 11:19:58 +00:00
f64 result = 0 ;
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
result = DQN_MAX ( DqnTimerInternal_Win32QueryPerfCounterTimeInMs ( ) , 0 ) ;
2017-06-22 08:10:44 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-08-05 07:30:54 +00:00
struct timespec time_spec = { 0 } ;
if ( clock_gettime ( CLOCK_MONOTONIC , & time_spec ) )
2018-05-17 11:19:58 +00:00
{
// TODO(doyle): Failed logging
DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
}
else
{
2018-08-05 07:30:54 +00:00
result = ( f64 ) ( ( time_spec . tv_sec * 1000.0f ) + ( time_spec . tv_nsec / 1000000.0f ) ) ;
2018-05-17 11:19:58 +00:00
}
2017-06-22 08:10:44 +00:00
2017-06-20 12:23:34 +00:00
# endif
2018-05-17 11:19:58 +00:00
return result ;
2017-06-20 12:23:34 +00:00
} ;
2017-04-14 15:27:11 +00:00
2017-06-22 08:10:44 +00:00
DQN_FILE_SCOPE f64 DqnTimer_NowInS ( ) { return DqnTimer_NowInMs ( ) / 1000.0f ; }
2018-01-18 09:25:44 +00:00
// XPlatform > #DqnLock
// =================================================================================================
2018-07-15 10:09:55 +00:00
bool DqnLock : : Init ( )
2017-06-20 12:23:34 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
if ( InitializeCriticalSectionEx ( & this - > win32_handle , this - > win32_spin_count , 0 ) )
2018-07-15 10:09:55 +00:00
{
2018-05-17 11:19:58 +00:00
return true ;
2018-07-15 10:09:55 +00:00
}
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
// NOTE: Static initialise, pre-empt a lock so that it gets initialised as per documentation
2018-08-05 07:30:54 +00:00
this - > unix_handle = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP ;
2018-07-27 04:43:39 +00:00
this - > Acquire ( ) ;
this - > Release ( ) ;
2018-05-17 11:19:58 +00:00
return true ;
2017-06-20 12:23:34 +00:00
# endif
2018-05-17 11:19:58 +00:00
return false ;
2017-04-14 15:27:11 +00:00
}
2018-07-15 10:09:55 +00:00
void DqnLock : : Acquire ( )
2017-04-14 15:27:11 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
EnterCriticalSection ( & this - > win32_handle ) ;
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
// TODO(doyle): Better error handling
2018-08-05 07:30:54 +00:00
i32 error = pthread_mutex_lock ( & this - > unix_handle ) ;
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( error = = 0 ) ;
2017-06-20 12:23:34 +00:00
# endif
}
2017-04-14 15:27:11 +00:00
2018-07-15 10:09:55 +00:00
void DqnLock : : Release ( )
2017-06-20 12:23:34 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
LeaveCriticalSection ( & this - > win32_handle ) ;
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
// TODO(doyle): better error handling
2018-08-05 07:30:54 +00:00
i32 error = pthread_mutex_unlock ( & this - > unix_handle ) ;
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( error = = 0 ) ;
2017-06-20 12:23:34 +00:00
# endif
}
2018-07-15 10:09:55 +00:00
void DqnLock : : Delete ( )
2017-06-20 12:23:34 +00:00
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
DeleteCriticalSection ( & this - > win32_handle ) ;
2018-07-04 14:47:11 +00:00
# else
2018-08-05 07:30:54 +00:00
i32 error = pthread_mutex_destroy ( & this - > unix_handle ) ;
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( error = = 0 ) ;
2017-06-20 12:23:34 +00:00
# endif
}
2018-01-18 09:25:44 +00:00
// XPlatform > #DqnJobQueue
// =================================================================================================
2018-08-05 07:30:54 +00:00
typedef void * DqnThreadCallbackInternal ( void * thread_param ) ;
2018-02-02 08:34:44 +00:00
usize DQN_JOB_QUEUE_INTERNAL_THREAD_DEFAULT_STACK_SIZE = 0 ;
2017-06-25 15:57:51 +00:00
2018-06-09 04:29:19 +00:00
FILE_SCOPE u32 DqnJobQueueInternal_ThreadCreate ( usize stackSize ,
2018-08-05 07:30:54 +00:00
DqnThreadCallbackInternal * thread_callback ,
void * thread_param , u32 num_threads )
2017-04-14 15:27:11 +00:00
{
2018-08-05 07:30:54 +00:00
u32 num_threads_created = 0 ;
2017-04-14 15:27:11 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
DQN_ASSERT ( stackSize = = 0 | | ! thread_callback ) ;
for ( u32 i = 0 ; i < num_threads ; i + + )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
HANDLE handle = CreateThread ( nullptr , stackSize , ( LPTHREAD_START_ROUTINE ) thread_callback ,
thread_param , 0 , nullptr ) ;
2018-05-17 11:19:58 +00:00
CloseHandle ( handle ) ;
2018-08-05 07:30:54 +00:00
num_threads_created + + ;
2018-05-17 11:19:58 +00:00
}
2017-04-14 15:27:11 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
// TODO(doyle): Better error handling
pthread_attr_t attribute = { } ;
DQN_ASSERT ( pthread_attr_init ( & attribute ) = = 0 ) ;
2017-06-25 15:57:51 +00:00
2018-05-17 11:19:58 +00:00
// Allows us to use pthread_join() which lets us wait till a thread finishes execution
DQN_ASSERT ( pthread_attr_setdetachstate ( & attribute , PTHREAD_CREATE_JOINABLE ) = = 0 ) ;
2017-06-25 15:57:51 +00:00
2018-05-17 11:19:58 +00:00
// TODO(doyle): Persist thread handles
2018-08-05 07:30:54 +00:00
for ( u32 i = 0 ; i < num_threads ; i + + )
2018-05-17 11:19:58 +00:00
{
pthread_t thread = { } ;
2018-08-05 07:30:54 +00:00
pthread_create ( & thread , & attribute , thread_callback , thread_param ) ;
num_threads_created + + ;
2018-05-17 11:19:58 +00:00
}
2017-06-25 15:57:51 +00:00
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( pthread_attr_destroy ( & attribute ) = = 0 ) ;
2017-06-20 12:23:34 +00:00
# endif
2018-08-05 07:30:54 +00:00
DQN_ASSERT ( num_threads_created = = num_threads ) ;
return num_threads_created ;
2017-04-14 15:27:11 +00:00
}
2018-08-05 07:30:54 +00:00
FILE_SCOPE void * DqnJobQueueInternal_ThreadCallback ( void * thread_param )
2017-06-20 12:23:34 +00:00
{
2018-08-05 07:30:54 +00:00
DqnJobQueue * queue = ( DqnJobQueue * ) thread_param ;
2018-05-17 11:19:58 +00:00
for ( ; ; )
{
if ( ! DqnJobQueue_TryExecuteNextJob ( queue ) )
{
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
WaitForSingleObjectEx ( queue - > semaphore , INFINITE , false ) ;
2017-06-25 15:57:51 +00:00
# else
2018-07-04 14:47:11 +00:00
sem_wait ( & queue - > semaphore ) ;
2017-06-25 15:57:51 +00:00
# endif
2018-05-17 11:19:58 +00:00
}
}
2017-06-25 15:57:51 +00:00
}
2018-08-05 07:30:54 +00:00
FILE_SCOPE bool DqnJobQueueInternal_CreateSemaphore ( DqnJobQueue * queue , u32 init_signal_count , u32 max_signal_count )
2017-06-25 15:57:51 +00:00
{
2018-05-17 11:19:58 +00:00
if ( ! queue ) return false ;
2017-06-25 15:57:51 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
queue - > semaphore = ( void * ) CreateSemaphoreA ( nullptr , init_signal_count , max_signal_count , nullptr ) ;
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( queue - > semaphore ) ;
2017-06-25 15:57:51 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
// TODO(doyle): Use max count for unix
// TODO(doyle): Error handling
const u32 UNIX_DONT_SHARE_BETWEEN_PROCESSES = 0 ;
i32 error = sem_init ( & queue - > semaphore , UNIX_DONT_SHARE_BETWEEN_PROCESSES , 0 ) ;
DQN_ASSERT ( error = = 0 ) ;
2017-06-25 15:57:51 +00:00
2018-08-05 07:30:54 +00:00
for ( u32 i = 0 ; i < init_signal_count ; i + + )
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( sem_post ( & queue - > semaphore ) = = 0 ) ;
2017-06-25 15:57:51 +00:00
# endif
2018-05-17 11:19:58 +00:00
return true ;
2017-06-25 15:57:51 +00:00
}
2018-06-09 04:29:19 +00:00
FILE_SCOPE bool DqnJobQueueInternal_ReleaseSemaphore ( DqnJobQueue * queue )
2017-06-25 15:57:51 +00:00
{
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( queue ) ;
2017-06-25 15:57:51 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( queue - > semaphore ) ;
ReleaseSemaphore ( queue - > semaphore , 1 , nullptr ) ;
2017-06-25 15:57:51 +00:00
2018-07-04 14:47:11 +00:00
# else
2018-05-17 11:19:58 +00:00
// TODO(doyle): Error handling
DQN_ASSERT ( sem_post ( & queue - > semaphore ) = = 0 ) ;
2017-06-20 12:23:34 +00:00
# endif
2018-07-04 14:47:11 +00:00
2018-05-17 11:19:58 +00:00
return true ;
2017-04-14 15:27:11 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE bool DqnJobQueue_Init ( DqnJobQueue * queue , DqnJob * job_list , u32 job_list_size , u32 num_threads )
2017-04-14 15:27:11 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! queue | | ! job_list | | job_list_size = = 0 | | num_threads = = 0 ) return false ;
queue - > job_list = job_list ;
queue - > size = job_list_size ;
2017-04-14 15:27:11 +00:00
2018-08-05 07:30:54 +00:00
DQN_ASSERT ( DqnJobQueueInternal_CreateSemaphore ( queue , 0 , num_threads ) ) ;
2017-06-20 12:23:34 +00:00
2018-05-17 11:19:58 +00:00
// Create threads
2018-08-05 07:30:54 +00:00
u32 num_threads_created = DqnJobQueueInternal_ThreadCreate (
2018-05-17 11:19:58 +00:00
DQN_JOB_QUEUE_INTERNAL_THREAD_DEFAULT_STACK_SIZE , DqnJobQueueInternal_ThreadCallback ,
2018-08-05 07:30:54 +00:00
( void * ) queue , num_threads ) ;
DQN_ASSERT ( num_threads = = num_threads_created ) ;
2017-06-23 13:12:20 +00:00
2018-05-17 11:19:58 +00:00
return true ;
2017-04-14 15:27:11 +00:00
}
2018-06-09 04:29:19 +00:00
DQN_FILE_SCOPE bool DqnJobQueue_AddJob ( DqnJobQueue * queue , const DqnJob job )
2017-04-14 15:27:11 +00:00
{
2018-05-17 11:19:58 +00:00
i32 newJobInsertIndex = ( queue - > jobInsertIndex + 1 ) % queue - > size ;
if ( newJobInsertIndex = = queue - > jobToExecuteIndex ) return false ;
2017-04-14 15:27:11 +00:00
2018-08-05 07:30:54 +00:00
queue - > job_list [ queue - > jobInsertIndex ] = job ;
2017-04-14 15:27:11 +00:00
2018-08-05 07:30:54 +00:00
DqnAtomic_Add32 ( & queue - > num_jobs_queued , 1 ) ;
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( DqnJobQueueInternal_ReleaseSemaphore ( queue ) ) ;
2017-04-14 15:27:11 +00:00
2018-05-17 11:19:58 +00:00
queue - > jobInsertIndex = newJobInsertIndex ;
return true ;
2017-06-20 12:23:34 +00:00
}
2017-04-14 15:27:11 +00:00
2018-06-09 04:29:19 +00:00
DQN_FILE_SCOPE void DqnJobQueue_BlockAndCompleteAllJobs ( DqnJobQueue * queue )
2017-06-24 04:59:48 +00:00
{
2018-05-17 11:19:58 +00:00
while ( DqnJobQueue_TryExecuteNextJob ( queue ) | | ! DqnJobQueue_AllJobsComplete ( queue ) )
;
2017-06-24 04:59:48 +00:00
}
2018-06-09 04:29:19 +00:00
DQN_FILE_SCOPE bool DqnJobQueue_TryExecuteNextJob ( DqnJobQueue * queue )
2017-06-20 12:23:34 +00:00
{
2018-05-17 11:19:58 +00:00
if ( ! queue ) return false ;
2017-06-24 04:59:48 +00:00
2018-05-17 11:19:58 +00:00
i32 originalJobToExecute = queue - > jobToExecuteIndex ;
if ( originalJobToExecute ! = queue - > jobInsertIndex )
{
i32 newJobIndexForNextThread = ( originalJobToExecute + 1 ) % queue - > size ;
i32 index = DqnAtomic_CompareSwap32 ( & queue - > jobToExecuteIndex , newJobIndexForNextThread ,
originalJobToExecute ) ;
// NOTE: If we weren't successful at the interlock, another thread has
// taken the work and we can't know if there's more work or not. So
// irrespective of that result, return true to let the thread check
// again for more work.
if ( index = = originalJobToExecute )
{
2018-08-05 07:30:54 +00:00
DqnJob job = queue - > job_list [ index ] ;
job . callback ( queue , job . user_data ) ;
DqnAtomic_Add32 ( & queue - > num_jobs_queued , - 1 ) ;
2018-05-17 11:19:58 +00:00
}
2017-04-14 15:27:11 +00:00
2018-05-17 11:19:58 +00:00
return true ;
}
2017-06-20 12:23:34 +00:00
2018-05-17 11:19:58 +00:00
return false ;
2017-04-14 15:27:11 +00:00
}
2018-06-09 04:29:19 +00:00
DQN_FILE_SCOPE bool DqnJobQueue_AllJobsComplete ( DqnJobQueue * queue )
2017-04-14 15:27:11 +00:00
{
2018-05-17 11:19:58 +00:00
if ( ! queue ) return false ;
2017-06-24 04:59:48 +00:00
2018-08-05 07:30:54 +00:00
bool result = ( queue - > num_jobs_queued = = 0 ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-06-20 12:23:34 +00:00
}
2017-04-14 15:27:11 +00:00
2018-08-05 07:30:54 +00:00
bool DqnJobQueue : : Init ( DqnJob * job_list_ , u32 job_list_size , u32 num_threads )
2017-06-23 13:12:20 +00:00
{
2018-08-05 07:30:54 +00:00
bool result = DqnJobQueue_Init ( this , job_list_ , job_list_size , num_threads ) ;
2018-05-17 11:19:58 +00:00
return result ;
2017-06-23 13:12:20 +00:00
}
2017-06-24 04:59:48 +00:00
bool DqnJobQueue : : AddJob ( const DqnJob job ) { return DqnJobQueue_AddJob ( this , job ) ; }
void DqnJobQueue : : BlockAndCompleteAllJobs ( ) { DqnJobQueue_BlockAndCompleteAllJobs ( this ) ; }
bool DqnJobQueue : : TryExecuteNextJob ( ) { return DqnJobQueue_TryExecuteNextJob ( this ) ; }
bool DqnJobQueue : : AllJobsComplete ( ) { return DqnJobQueue_AllJobsComplete ( this ) ; }
2017-06-23 13:12:20 +00:00
2018-01-18 09:25:44 +00:00
// XPlatform > #DqnAtomic
// =================================================================================================
2017-06-25 15:57:51 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
DQN_COMPILE_ASSERT ( sizeof ( LONG ) = = sizeof ( i32 ) ) ;
2017-06-25 15:57:51 +00:00
# endif
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnAtomic_CompareSwap32 ( i32 volatile * dest , i32 swap_val , i32 compare_val )
2017-06-25 15:57:51 +00:00
{
2018-05-17 11:19:58 +00:00
i32 result = 0 ;
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
result = ( i32 ) InterlockedCompareExchange ( ( LONG volatile * ) dest , ( LONG ) swap_val , ( LONG ) compare_val ) ;
2017-06-25 15:57:51 +00:00
# else
2018-08-05 07:30:54 +00:00
result = __sync_val_compare_and_swap ( dest , compare_val , swap_val ) ;
2017-06-25 15:57:51 +00:00
# endif
2018-05-17 11:19:58 +00:00
return result ;
2017-06-25 15:57:51 +00:00
}
2018-06-09 04:29:19 +00:00
DQN_FILE_SCOPE i32 DqnAtomic_Add32 ( i32 volatile * src , i32 value )
2017-06-25 15:57:51 +00:00
{
2018-05-17 11:19:58 +00:00
i32 result = 0 ;
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-05-17 11:19:58 +00:00
result = ( i32 ) InterlockedAdd ( ( LONG volatile * ) src , value ) ;
2017-06-25 15:57:51 +00:00
# else
2018-07-04 14:47:11 +00:00
result = __sync_add_and_fetch ( src , value ) ;
2017-06-25 15:57:51 +00:00
# endif
2018-05-17 11:19:58 +00:00
return result ;
2017-06-25 15:57:51 +00:00
}
2018-06-25 14:33:46 +00:00
// XPlatform > #DqnOS
2018-01-18 09:25:44 +00:00
// =================================================================================================
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_UNIX)
2018-07-10 05:52:12 +00:00
# include <sys/mman.h>
2018-07-09 14:53:54 +00:00
# endif
2018-08-05 07:30:54 +00:00
void * DqnOS_VAlloc ( isize size , void * base_addr )
2018-07-09 14:53:54 +00:00
{
void * result = nullptr ;
# if defined (DQN_IS_WIN32)
2018-08-05 07:30:54 +00:00
result = VirtualAlloc ( base_addr , size , MEM_RESERVE | MEM_COMMIT , PAGE_READWRITE ) ;
2018-12-15 14:58:11 +00:00
DQN_ASSERT_MSG ( result , " VirtualAlloc failed: %s \n " , DqnWin32_GetLastError ( ) ) ;
2018-07-09 14:53:54 +00:00
# else
result = mmap (
2018-08-05 07:30:54 +00:00
base_addr , size , PROT_READ | PROT_WRITE , MAP_PRIVATE | MAP_ANONYMOUS , - 1 /*fd*/ , 0 /*offset into fd*/ ) ;
2018-07-09 14:53:54 +00:00
DQN_ASSERT ( result ! = MAP_FAILED ) ;
# endif
return result ;
}
void DqnOS_VFree ( void * address , isize size )
{
# if defined (DQN_IS_WIN32)
BOOL result = VirtualFree ( address , 0 /*size*/ , MEM_RELEASE ) ;
( void ) size ;
DQN_ASSERT ( result ) ;
# else
int result = munmap ( address , size ) ;
DQN_ASSERT ( result ! = 0 ) ;
# endif
}
2018-06-25 14:33:46 +00:00
# define DQN_OS_GET_THREADS_AND_CORES(name) \
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void name ( u32 * const num_cores , u32 * const num_threads_per_core )
2018-03-08 15:03:05 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_UNIX)
2018-06-25 14:33:46 +00:00
DQN_OS_GET_THREADS_AND_CORES ( DqnOS_GetThreadsAndCores )
2017-06-25 15:57:51 +00:00
{
2018-08-05 07:30:54 +00:00
if ( ! num_threads_per_core & & ! num_cores ) return ;
2017-06-25 15:57:51 +00:00
2018-05-17 11:19:58 +00:00
// TODO(doyle): Not exactly standard
2017-06-25 15:57:51 +00:00
2018-05-17 11:19:58 +00:00
usize fileSize = 0 ;
2018-08-05 07:30:54 +00:00
if ( u8 * read_buffer = DqnFile_ReadAll ( " /proc/cpuinfo " , & fileSize ) )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
char const * src_ptr = reinterpret_cast < char * > ( read_buffer ) ;
usize src_len = fileSize ;
2017-06-25 15:57:51 +00:00
# define DQN_ADVANCE_CHAR_PTR_AND_LEN_INTERNAL(ptr, len, offset) \
2018-05-17 11:19:58 +00:00
ptr + = offset ; \
len - = offset
2018-08-05 07:30:54 +00:00
if ( num_threads_per_core )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
* num_threads_per_core = 0 ;
2018-05-17 11:19:58 +00:00
// Find the offset to the processor field and move to it
2018-08-19 01:59:04 +00:00
DqnBuffer < char const > processor = DQN_BUFFER_STR_LIT ( " processor " ) ;
2018-08-05 07:30:54 +00:00
i32 processorOffset = DqnStr_FindFirstOccurence ( src_ptr , src_len , processor . data , processor . len ) ;
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( processorOffset ! = - 1 ) ;
2018-08-05 07:30:54 +00:00
DQN_ADVANCE_CHAR_PTR_AND_LEN_INTERNAL ( src_ptr , src_len , processorOffset ) ;
2018-05-17 11:19:58 +00:00
// Find the offset to the colon delimiter and advance to 1 after it
2018-08-05 07:30:54 +00:00
i32 colon_offset = DqnStr_FindFirstOccurence ( src_ptr , src_len , " : " , 1 ) + 1 ;
DQN_ASSERT ( colon_offset ! = - 1 ) ;
DQN_ADVANCE_CHAR_PTR_AND_LEN_INTERNAL ( src_ptr , src_len , colon_offset ) ;
2018-05-17 11:19:58 +00:00
// Read num processors, i.e. logical cores/hyper threads
2018-08-05 07:30:54 +00:00
* num_threads_per_core = Dqn_StrToI64 ( src_ptr , src_len ) ;
if ( * num_threads_per_core = = 0 ) * num_threads_per_core = 1 ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
if ( num_cores )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
* num_cores = 0 ;
2018-05-17 11:19:58 +00:00
// Find the offset to the cpu cores field and move to it
2018-08-19 01:59:04 +00:00
DqnBuffer < char const > cpuCores = DQN_BUFFER_STR_LIT ( " cpu cores " ) ;
i32 cpu_cores_offset = DqnStr_FindFirstOccurence ( src_ptr , src_len , cpuCores . data , cpuCores . len ) ;
2018-08-05 07:30:54 +00:00
DQN_ASSERT ( cpu_cores_offset ! = - 1 ) ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
DQN_ADVANCE_CHAR_PTR_AND_LEN_INTERNAL ( src_ptr , src_len , cpu_cores_offset ) ;
2018-05-17 11:19:58 +00:00
// Find the offset to the colon delimiter and advance to 1 after it
2018-08-05 07:30:54 +00:00
i32 colon_offset = DqnStr_FindFirstOccurence ( src_ptr , src_len , " : " , 1 ) + 1 ;
DQN_ASSERT ( colon_offset ! = - 1 ) ;
DQN_ADVANCE_CHAR_PTR_AND_LEN_INTERNAL ( src_ptr , src_len , colon_offset ) ;
2018-05-17 11:19:58 +00:00
// Read num cores value, i.e. physical cores
2018-08-05 07:30:54 +00:00
* num_cores = Dqn_StrToI64 ( src_ptr , src_len ) ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_DEFAULT_HEAP_ALLOCATOR - > Free ( read_buffer ) ;
2018-05-17 11:19:58 +00:00
}
else
{
// TODO(doyle): Out of mem
DQN_ASSERT ( DQN_INVALID_CODE_PATH ) ;
}
2017-06-25 15:57:51 +00:00
}
2018-07-09 14:53:54 +00:00
# endif // DQN_IS_UNIX
2017-06-25 15:57:51 +00:00
2018-07-09 14:53:54 +00:00
# if defined(DQN_IS_WIN32)
2018-06-25 14:33:46 +00:00
DQN_OS_GET_THREADS_AND_CORES ( DqnOS_GetThreadsAndCores )
2017-06-25 15:57:51 +00:00
{
2018-08-05 07:30:54 +00:00
if ( num_threads_per_core )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
SYSTEM_INFO system_info ;
GetNativeSystemInfo ( & system_info ) ;
* num_threads_per_core = system_info . dwNumberOfProcessors ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
if ( num_cores )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
* num_cores = 0 ;
DWORD required_size = 0 ;
u8 insufficient_buf = { 0 } ;
2018-05-17 11:19:58 +00:00
GetLogicalProcessorInformationEx (
2018-08-05 07:30:54 +00:00
RelationProcessorCore ,
( SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * ) insufficient_buf ,
& required_size ) ;
2018-05-17 11:19:58 +00:00
2018-08-05 07:30:54 +00:00
auto * raw_proc_info_array = ( u8 * ) DqnMem_Calloc ( required_size ) ;
if ( ! raw_proc_info_array )
2018-05-17 11:19:58 +00:00
{
2018-09-13 13:58:48 +00:00
DQN_LOGGER_E ( dqn_lib_context_ . logger , " Could not allocate memory for array required: %$d \n " ) ;
2018-05-17 11:19:58 +00:00
return ;
}
if ( GetLogicalProcessorInformationEx (
2018-08-05 07:30:54 +00:00
RelationProcessorCore ,
( SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * ) raw_proc_info_array ,
& required_size ) )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * logical_proc_info =
( SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * ) raw_proc_info_array ;
DWORD bytes_read = 0 ;
2018-05-17 11:19:58 +00:00
do
{
// NOTE: High efficiency value has greater performance and less efficiency.
2018-08-05 07:30:54 +00:00
PROCESSOR_RELATIONSHIP * procInfo = & logical_proc_info - > Processor ;
2018-05-17 11:19:58 +00:00
// u32 efficiency = procInfo->EfficiencyClass;
2018-08-05 07:30:54 +00:00
( * num_cores ) + + ;
DQN_ASSERT ( logical_proc_info - > Relationship = = RelationProcessorCore ) ;
2018-05-17 11:19:58 +00:00
DQN_ASSERT ( procInfo - > GroupCount = = 1 ) ;
2018-08-05 07:30:54 +00:00
bytes_read + = logical_proc_info - > Size ;
logical_proc_info =
( SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * ) ( ( u8 * ) logical_proc_info +
logical_proc_info - > Size ) ;
} while ( bytes_read < required_size ) ;
2018-05-17 11:19:58 +00:00
}
else
{
DqnWin32_DisplayLastError ( " GetLogicalProcessorInformationEx() failed " ) ;
}
2018-08-05 07:30:54 +00:00
DqnMem_Free ( raw_proc_info_array ) ;
2018-05-17 11:19:58 +00:00
}
2017-06-25 15:57:51 +00:00
}
2018-07-09 14:53:54 +00:00
# endif // DQN_IS_WIN32
2017-06-25 15:57:51 +00:00
2018-07-09 14:53:54 +00:00
# ifdef DQN_IS_WIN32
2018-01-18 09:25:44 +00:00
// #DqnWin32
// =================================================================================================
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnWin32_UTF8ToWChar ( char const * in , wchar_t * out , i32 out_len )
2017-06-20 12:23:34 +00:00
{
2018-08-05 07:30:54 +00:00
i32 result = MultiByteToWideChar ( CP_UTF8 , 0 , in , - 1 , out , out_len ) ;
2017-04-14 15:27:11 +00:00
2018-05-17 11:19:58 +00:00
if ( result = = 0xFFFD | | 0 )
{
2018-06-13 13:16:05 +00:00
DQN__WIN32_ERROR_BOX ( " WideCharToMultiByte() failed. " , nullptr ) ;
2018-05-17 11:19:58 +00:00
return - 1 ;
}
2017-06-20 12:23:34 +00:00
2018-05-17 11:19:58 +00:00
return result ;
2017-04-14 15:27:11 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE i32 DqnWin32_WCharToUTF8 ( wchar_t const * in , char * out , i32 out_len )
2017-04-14 15:27:11 +00:00
{
2018-05-17 11:19:58 +00:00
i32 result =
2018-08-05 07:30:54 +00:00
WideCharToMultiByte ( CP_UTF8 , 0 , in , - 1 , out , out_len , nullptr , nullptr ) ;
2017-04-14 15:27:11 +00:00
2018-05-17 11:19:58 +00:00
if ( result = = 0xFFFD | | 0 )
{
2018-06-13 13:16:05 +00:00
DQN__WIN32_ERROR_BOX ( " WideCharToMultiByte() failed. " , nullptr ) ;
2018-05-17 11:19:58 +00:00
return - 1 ;
}
2017-06-20 12:23:34 +00:00
2018-05-17 11:19:58 +00:00
return result ;
2017-04-14 15:27:11 +00:00
}
2018-06-09 04:29:19 +00:00
DQN_FILE_SCOPE void DqnWin32_GetClientDim ( HWND window , LONG * width , LONG * height )
2017-04-14 15:27:11 +00:00
{
2018-05-17 11:19:58 +00:00
RECT rect ;
GetClientRect ( window , & rect ) ;
if ( width ) * width = rect . right - rect . left ;
if ( height ) * height = rect . bottom - rect . top ;
2017-06-20 12:23:34 +00:00
}
2017-04-14 15:27:11 +00:00
2018-06-09 04:29:19 +00:00
DQN_FILE_SCOPE void DqnWin32_GetRectDim ( RECT const rect , LONG * width , LONG * height )
2017-06-20 12:23:34 +00:00
{
2018-05-17 11:19:58 +00:00
if ( width ) * width = rect . right - rect . left ;
if ( height ) * height = rect . bottom - rect . top ;
2017-06-20 12:23:34 +00:00
}
2018-08-18 06:08:29 +00:00
DQN_FILE_SCOPE char const * DqnWin32_GetLastError ( )
2017-06-20 12:23:34 +00:00
{
2018-08-18 06:08:29 +00:00
LOCAL_PERSIST char err_msg [ 2048 ] ;
err_msg [ 0 ] = 0 ;
DWORD error = GetLastError ( ) ;
2018-05-17 11:19:58 +00:00
FormatMessageA ( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
2018-08-05 07:30:54 +00:00
nullptr , error , 0 , err_msg , DQN_ARRAY_COUNT ( err_msg ) , nullptr ) ;
2017-06-20 12:23:34 +00:00
2018-08-18 06:08:29 +00:00
return err_msg ;
}
DQN_FILE_SCOPE void DqnWin32_DisplayLastError ( char const * err_prefix )
{
2018-08-05 07:30:54 +00:00
if ( err_prefix )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
char formatted_err [ 2048 ] = { 0 } ;
2018-12-12 13:36:19 +00:00
stbsp_sprintf ( formatted_err , " %s: %s " , err_prefix , DqnWin32_GetLastError ( ) ) ;
2018-08-05 07:30:54 +00:00
DQN__WIN32_ERROR_BOX ( formatted_err , nullptr ) ;
2018-05-17 11:19:58 +00:00
}
else
{
2018-08-18 06:08:29 +00:00
DQN__WIN32_ERROR_BOX ( DqnWin32_GetLastError ( ) , nullptr ) ;
2018-05-17 11:19:58 +00:00
}
2017-06-20 12:23:34 +00:00
}
2018-06-13 13:16:05 +00:00
const i32 DQN__WIN32_INTERNAL_ERROR_MSG_SIZE = 2048 ;
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void DqnWin32_DisplayErrorCode ( DWORD error , char const * err_prefix )
2017-06-20 12:23:34 +00:00
{
2018-08-05 07:30:54 +00:00
char err_msg [ DQN__WIN32_INTERNAL_ERROR_MSG_SIZE ] = { 0 } ;
2018-05-17 11:19:58 +00:00
FormatMessageA ( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
2018-08-05 07:30:54 +00:00
nullptr , error , 0 , err_msg , DQN_ARRAY_COUNT ( err_msg ) , nullptr ) ;
2017-06-20 12:23:34 +00:00
2018-08-05 07:30:54 +00:00
char formatted_err [ 2048 ] = { 0 } ;
2018-12-12 13:36:19 +00:00
stbsp_sprintf ( formatted_err , " %s: %s " , err_prefix , err_msg ) ;
2018-08-05 07:30:54 +00:00
DQN__WIN32_ERROR_BOX ( formatted_err , nullptr ) ;
2017-06-20 12:23:34 +00:00
}
2018-08-05 07:30:54 +00:00
DQN_FILE_SCOPE void DqnWin32_OutputDebugString ( char const * fmt_str , . . . )
2017-06-20 12:23:34 +00:00
{
2018-06-13 13:16:05 +00:00
char str [ DQN__WIN32_INTERNAL_ERROR_MSG_SIZE ] = { 0 } ;
2017-06-20 12:23:34 +00:00
2018-08-05 07:30:54 +00:00
va_list va ;
va_start ( va , fmt_str ) ;
2018-05-17 11:19:58 +00:00
{
2018-12-12 13:36:19 +00:00
i32 num_copied = stbsp_vsprintf ( str , fmt_str , va ) ;
2018-08-05 07:30:54 +00:00
DQN_ASSERT ( num_copied < DQN_ARRAY_COUNT ( str ) ) ;
2018-05-17 11:19:58 +00:00
}
2018-08-05 07:30:54 +00:00
va_end ( va ) ;
2017-06-20 12:23:34 +00:00
2018-05-17 11:19:58 +00:00
OutputDebugStringA ( str ) ;
2017-04-14 15:27:11 +00:00
}
2018-09-10 12:49:17 +00:00
DQN_FILE_SCOPE void DqnWin32_GetExeNameAndDirectory ( DqnMemStack * mem_stack , DqnBuffer < wchar_t > * exe_name , DqnBuffer < wchar_t > * exe_directory )
{
if ( ! exe_name & & ! exe_directory ) return ;
int exe_buf_len = 512 ;
wchar_t * exe_buf = nullptr ;
2018-09-13 13:58:48 +00:00
int exe_len = exe_buf_len ;
while ( exe_buf_len = = exe_len )
2018-09-10 12:49:17 +00:00
{
if ( exe_buf )
exe_buf_len + = 128 ;
2018-09-13 13:58:48 +00:00
exe_buf = DQN_MEMSTACK_PUSH_BACK_ARRAY ( mem_stack , wchar_t , exe_buf_len ) ;
2018-09-10 12:49:17 +00:00
DQN_DEFER { mem_stack - > Pop ( exe_buf ) ; } ;
2018-09-13 13:58:48 +00:00
exe_len = GetModuleFileNameW ( nullptr , exe_buf , exe_buf_len ) ;
2018-09-10 12:49:17 +00:00
}
DqnAllocator allocator ( mem_stack ) ;
2018-08-05 07:30:54 +00:00
i32 last_slash_index = 0 ;
2018-09-13 13:58:48 +00:00
for ( i32 i = ( exe_len - 1 ) ; i > = 0 ; - - i )
2018-05-17 11:19:58 +00:00
{
2018-09-13 13:58:48 +00:00
if ( exe_buf [ i ] = = ' \\ ' )
2018-05-17 11:19:58 +00:00
{
2018-08-05 07:30:54 +00:00
last_slash_index = i ;
2018-05-17 11:19:58 +00:00
break ;
}
}
2018-09-13 13:58:48 +00:00
if ( exe_name )
{
wchar_t * start_of_exe_name = exe_buf + last_slash_index + 1 ;
wchar_t * end = exe_buf + exe_len ;
* exe_name = DqnBuffer_CopyAndNullTerminate ( & allocator , start_of_exe_name , static_cast < int > ( end - start_of_exe_name ) ) ;
}
if ( exe_directory )
* exe_directory = DqnBuffer_CopyAndNullTerminate ( & allocator , exe_buf , last_slash_index ) ;
2017-04-14 15:27:11 +00:00
}
2018-09-10 12:49:17 +00:00
FILE_SCOPE inline u64 DqnWin32__FileTimeToEpoch ( FILETIME file_time )
{
ULARGE_INTEGER file_time_ularge = { } ;
file_time_ularge . LowPart = file_time . dwLowDateTime ;
file_time_ularge . HighPart = file_time . dwHighDateTime ;
u64 result = ( file_time_ularge . QuadPart - 116444736000000000LL ) / 10000000ULL ;
return result ;
}
DQN_FILE_SCOPE u64 DqnWin32_EpochTimeUTC ( )
{
FILETIME file_time ;
GetSystemTimeAsFileTime ( & file_time ) ;
u64 result = DqnWin32__FileTimeToEpoch ( file_time ) ;
return result ;
}
DQN_FILE_SCOPE u64 DqnWin32_EpochTimeLocal ( )
{
SYSTEMTIME sys_time ;
GetLocalTime ( & sys_time ) ;
FILETIME file_time ;
bool converted = SystemTimeToFileTime ( & sys_time , & file_time ) ;
( void ) converted ; DQN_ASSERT ( converted ) ;
u64 result = DqnWin32__FileTimeToEpoch ( file_time ) ;
return result ;
}
2018-07-09 14:53:54 +00:00
# endif // DQN_IS_WIN32
2018-07-04 14:47:11 +00:00
# endif // DQN__XPLATFORM_LAYER