Initial commit
This commit is contained in:
commit
ab5fa39957
46
.clang-format
Normal file
46
.clang-format
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
ColumnLimit: 80,
|
||||
TabWidth: 4,
|
||||
IndentWidth: 4, # 1 tab
|
||||
UseTab: ForIndentation,
|
||||
BreakBeforeBraces: Allman,
|
||||
PointerBindsToType: false,
|
||||
###
|
||||
AlwaysBreakAfterDefinitionReturnType: false,
|
||||
AlwaysBreakTemplateDeclarations: true,
|
||||
AlwaysBreakBeforeMultilineStrings: true,
|
||||
IndentFunctionDeclarationAfterType: false,
|
||||
#
|
||||
AccessModifierOffset: -4, # 1 tab
|
||||
AlignAfterOpenBracket: true,
|
||||
AlignConsecutiveAssignments: true,
|
||||
AlignTrailingComments: true,
|
||||
#
|
||||
AllowAllParametersOfDeclarationOnNextLine: true,
|
||||
AllowShortBlocksOnASingleLine: false,
|
||||
AllowShortIfStatementsOnASingleLine: true,
|
||||
AllowShortLoopsOnASingleLine: false,
|
||||
#
|
||||
BinPackArguments: true,
|
||||
BinPackParameters: true,
|
||||
#
|
||||
BreakConstructorInitializersBeforeComma: true,
|
||||
ConstructorInitializerIndentWidth: 0,
|
||||
#
|
||||
IndentCaseLabels: true,
|
||||
#
|
||||
MaxEmptyLinesToKeep: 1,
|
||||
NamespaceIndentation: None,
|
||||
#
|
||||
SpaceBeforeAssignmentOperators: true,
|
||||
SpaceInEmptyParentheses: false,
|
||||
SpacesBeforeTrailingComments: 1,
|
||||
SpacesInAngles: false,
|
||||
SpacesInCStyleCastParentheses: false,
|
||||
SpacesInParentheses: false,
|
||||
SpacesInSquareBrackets: false,
|
||||
#
|
||||
Cpp11BracedListStyle: true,
|
||||
Standard: Cpp11,
|
||||
#
|
||||
}
|
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
tags
|
||||
*.swp
|
||||
*.swo
|
||||
bin/
|
||||
.vs/
|
||||
*.db
|
52
build.bat
Normal file
52
build.bat
Normal file
@ -0,0 +1,52 @@
|
||||
@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or
|
||||
@REM vcvarsall.bat to setup command-line compiler.
|
||||
|
||||
@echo OFF
|
||||
set ProjectName=dqnt_unit_test
|
||||
set CompileEntryPoint=..\dqnt_unit_test.cpp
|
||||
|
||||
REM Build tags file
|
||||
ctags -R
|
||||
|
||||
REM Check if build tool is on path
|
||||
REM >nul, 2>nul will remove the output text from the where command
|
||||
where cl.exe >nul 2>nul
|
||||
if %errorlevel%==1 call msvc86.bat
|
||||
|
||||
REM Drop compilation files into build folder
|
||||
IF NOT EXIST bin mkdir bin
|
||||
pushd bin
|
||||
|
||||
REM EHa- disable exception handling (we don't use)
|
||||
REM GR- disable c runtime type information (we don't use)
|
||||
|
||||
REM MD use dynamic runtime library
|
||||
REM MT use static runtime library, so build and link it into exe
|
||||
|
||||
REM Oi enable intrinsics optimisation, let us use CPU intrinsics if there is one
|
||||
REM instead of generating a call to external library (i.e. CRT).
|
||||
|
||||
REM Zi enables debug data, Z7 combines the debug files into one.
|
||||
|
||||
REM W4 warning level 4
|
||||
REM WX treat warnings as errors
|
||||
REM wd4100 ignore: unused argument parameters
|
||||
REM wd4201 ignore: nonstandard extension used: nameless struct/union
|
||||
REM wd4189 ignore: local variable is initialised but not referenced
|
||||
|
||||
set CompileFlags=-EHa- -GR- -Oi -MT -Z7 -W4 -WX -wd4100 -wd4201 -wd4189 -FC
|
||||
|
||||
REM Include directories
|
||||
set IncludeFlags=
|
||||
|
||||
REM Link libraries
|
||||
set LinkLibraries=user32.lib gdi32.lib msimg32.lib
|
||||
|
||||
REM incrmenetal:no, turn incremental builds off
|
||||
REM opt:ref, try to remove functions from libs that are referenced at all
|
||||
set LinkFlags=-incremental:no -opt:ref
|
||||
|
||||
cl %CompileFlags% %CompileEntryPoint% %IncludeFlags% /link %LinkLibraries% %LinkFlags% /nologo /OUT:"%ProjectName%.exe"
|
||||
|
||||
popd
|
||||
|
514
dqnt.h
Normal file
514
dqnt.h
Normal file
@ -0,0 +1,514 @@
|
||||
#ifndef DQNT_H
|
||||
#define DQNT_H
|
||||
|
||||
/*
|
||||
#define DQNT_IMPLEMENTATION // Enable the implementation
|
||||
#define DQNT_MAKE_STATIC // Make all functions be static
|
||||
#include "dqnt.h"
|
||||
*/
|
||||
|
||||
#ifdef DQNT_MAKE_STATIC
|
||||
#define DQNT_FILE_SCOPE static
|
||||
#else
|
||||
#define DQNT_FILE_SCOPE
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// HEADER
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include "stdint.h"
|
||||
|
||||
#define LOCAL_PERSIST static
|
||||
#define FILE_SCOPE static
|
||||
|
||||
typedef uint64_t u64;
|
||||
typedef uint32_t u32;
|
||||
typedef uint16_t u16;
|
||||
typedef uint8_t u8;
|
||||
|
||||
typedef int32_t i64;
|
||||
typedef int32_t i32;
|
||||
typedef int64_t i16;
|
||||
|
||||
typedef double f64;
|
||||
typedef float f32;
|
||||
|
||||
#define DQNT_INVALID_CODE_PATH 0
|
||||
#define DQNT_ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
|
||||
#define DQNT_ASSERT(expr) if (!(expr)) { (*((i32 *)0)) = 0; }
|
||||
|
||||
#define DQNT_MATH_PI 3.14159265359f
|
||||
#define DQNT_MATH_ABS(x) (((x) < 0) ? (-(x)) : (x))
|
||||
#define DQNT_MATH_DEGREES_TO_RADIANS(x) ((x * (MATH_PI / 180.0f)))
|
||||
#define DQNT_MATH_RADIANS_TO_DEGREES(x) ((x * (180.0f / MATH_PI)))
|
||||
#define DQNT_MATH_MAX(a, b) ((a) < (b) ? (b) : (a))
|
||||
#define DQNT_MATH_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define DQNT_MATH_SQUARED(x) ((x) * (x))
|
||||
#define DQNT_MATH_SQRT(x) (sqrtf(x))
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vec2
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef union dqnt_v2 {
|
||||
struct { f32 x, y; };
|
||||
struct { f32 w, h; };
|
||||
struct { f32 min, max; };
|
||||
f32 e[2];
|
||||
} dqnt_v2;
|
||||
|
||||
DQNT_FILE_SCOPE dqnt_v2 V2(f32 x, f32 y);
|
||||
// Create a 2d-vector using ints and typecast to floats
|
||||
DQNT_FILE_SCOPE dqnt_v2 V2i(i32 x, i32 y);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vec3
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef union dqnt_v3
|
||||
{
|
||||
struct { f32 x, y, z; };
|
||||
struct { f32 r, g, b; };
|
||||
f32 e[3];
|
||||
} dqnt_v3;
|
||||
|
||||
DQNT_FILE_SCOPE dqnt_v3 V3i(i32 x, i32 y, i32 z);
|
||||
DQNT_FILE_SCOPE dqnt_v3 V3(f32 x, f32 y, f32 z);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vec3
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef union dqnt_v4
|
||||
{
|
||||
struct { f32 x, y, z, w; };
|
||||
struct { f32 r, g, b, a; };
|
||||
f32 e[4];
|
||||
dqnt_v2 vec2[2];
|
||||
} dqnt_v4;
|
||||
|
||||
DQNT_FILE_SCOPE dqnt_v3 V3i(i32 x, i32 y, i32 z);
|
||||
DQNT_FILE_SCOPE dqnt_v4 V4(f32 x, f32 y, f32 z, f32 w);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Other Math
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct Rect
|
||||
{
|
||||
dqnt_v2 min;
|
||||
dqnt_v2 max;
|
||||
} dqnt_rect;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// String Ops
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
DQNT_FILE_SCOPE bool dqnt_char_is_digit (char c);
|
||||
DQNT_FILE_SCOPE bool dqnt_char_is_alpha (char c);
|
||||
DQNT_FILE_SCOPE bool dqnt_char_is_alphanum(char c);
|
||||
|
||||
DQNT_FILE_SCOPE i32 dqnt_strcmp (const char *a, const char *b);
|
||||
// Returns the length without the null terminator
|
||||
DQNT_FILE_SCOPE i32 dqnt_strlen (const char *a);
|
||||
DQNT_FILE_SCOPE char *dqnt_strncpy(char *dest, const char *src, i32 numChars);
|
||||
|
||||
#define DQNT_I32_TO_STR_MAX_BUF_SIZE 11
|
||||
DQNT_FILE_SCOPE bool dqnt_str_reverse(char *buf, const i32 bufSize);
|
||||
DQNT_FILE_SCOPE i32 dqnt_str_to_i32 (char *const buf, const i32 bufSize);
|
||||
DQNT_FILE_SCOPE void dqnt_i32_to_str (i32 value, char *buf, i32 bufSize);
|
||||
|
||||
DQNT_FILE_SCOPE i32 dqnt_wstrcmp(const wchar_t *a, const wchar_t *b);
|
||||
DQNT_FILE_SCOPE void dqnt_wstrcat(const wchar_t *a, i32 lenA, const wchar_t *b, i32 lenB, wchar_t *out, i32 outLen);
|
||||
DQNT_FILE_SCOPE i32 dqnt_wstrlen(const wchar_t *a);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Timer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
DQNT_FILE_SCOPE f64 dqnt_time_now_in_s();
|
||||
DQNT_FILE_SCOPE f64 dqnt_time_now_in_ms();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// PCG (Permuted Congruential Generator) Random Number Generator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct DqntRandPCGState
|
||||
{
|
||||
u64 state[2];
|
||||
} DqntRandPCGState;
|
||||
|
||||
// You can manually specify a seed by calling init_with_seed, otherwise it
|
||||
// automatically creates a seed using rdtsc. The generator is not valid until
|
||||
// it's been seeded.
|
||||
DQNT_FILE_SCOPE void dqnt_rnd_pcg_init_with_seed(DqntRandPCGState *pcg, u32 seed);
|
||||
DQNT_FILE_SCOPE void dqnt_rnd_pcg_init(DqntRandPCGState *pcg);
|
||||
|
||||
// Returns a random number N between [0, 0xFFFFFFFF]
|
||||
DQNT_FILE_SCOPE u32 dqnt_rnd_pcg_next (DqntRandPCGState *pcg);
|
||||
// Returns a random float N between [0.0, 1.0f]
|
||||
DQNT_FILE_SCOPE f32 dqnt_rnd_pcg_nextf(DqntRandPCGState *pcg);
|
||||
// Returns a random integer N between [min, max]
|
||||
DQNT_FILE_SCOPE i32 dqnt_rnd_pcg_range(DqntRandPCGState *pcg, i32 min, i32 max);
|
||||
|
||||
#endif /* DQNT_H */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef DQNT_IMPLEMENTATION
|
||||
#undef DQNT_IMPLEMENTATION
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DQNT_WIN32
|
||||
|
||||
#include "Windows.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vec2
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
DQNT_FILE_SCOPE inline dqnt_v2 dqnt_vec2(f32 x, f32 y)
|
||||
{
|
||||
dqnt_v2 result = {};
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE inline dqnt_v2 dqnt_vec2i(i32 x, i32 y)
|
||||
{
|
||||
dqnt_v2 result = dqnt_vec2((f32)x, (f32)y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE dqnt_v2 dqnt_vec2_constrain_to_ratio(dqnt_v2 dim,
|
||||
dqnt_v2 ratio)
|
||||
{
|
||||
dqnt_v2 result = {};
|
||||
f32 numRatioIncrementsToWidth = (f32)(dim.w / ratio.w);
|
||||
f32 numRatioIncrementsToHeight = (f32)(dim.h / ratio.h);
|
||||
|
||||
f32 leastIncrementsToSide =
|
||||
DQNT_MATH_MIN(numRatioIncrementsToHeight, numRatioIncrementsToWidth);
|
||||
|
||||
result.w = (f32)(ratio.w * leastIncrementsToSide);
|
||||
result.h = (f32)(ratio.h * leastIncrementsToSide);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vec3
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
DQNT_FILE_SCOPE inline dqnt_v3 dqnt_vec3(f32 x, f32 y, f32 z)
|
||||
{
|
||||
dqnt_v3 result = {};
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
result.z = z;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE inline dqnt_v3 dqnt_vec3i(i32 x, i32 y, i32 z)
|
||||
{
|
||||
dqnt_v3 result = dqnt_vec3((f32)x, (f32)y, (f32)z);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Vec4
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
DQNT_FILE_SCOPE inline dqnt_v4 dqnt_vec4(f32 x, f32 y, f32 z, f32 w)
|
||||
{
|
||||
dqnt_v4 result = {x, y, z, w};
|
||||
return result;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE inline dqnt_v4 dqnt_vec4i(i32 x, i32 y, i32 z, i32 w) {
|
||||
dqnt_v4 result = dqnt_vec4((f32)x, (f32)y, (f32)z, (f32)w);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// String Ops
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
DQNT_FILE_SCOPE bool dqnt_char_is_digit(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE bool dqnt_char_is_alpha(char c)
|
||||
{
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE bool dqnt_char_is_alphanum(char c)
|
||||
{
|
||||
if (dqnt_char_is_alpha(c) || dqnt_char_is_digit(c)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE i32 dqnt_strcmp(const char *a, const char *b)
|
||||
{
|
||||
if (!a && !b) return -1;
|
||||
if (!a) return -1;
|
||||
if (!b) return -1;
|
||||
|
||||
while ((*a) == (*b))
|
||||
{
|
||||
if (!(*a)) return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return (((*a) < (*b)) ? -1 : 1);
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE i32 dqnt_strlen(const char *a)
|
||||
{
|
||||
i32 result = 0;
|
||||
while (a && a[result]) result++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE char *dqnt_strncpy(char *dest, const char *src, i32 numChars)
|
||||
{
|
||||
if (!dest) return NULL;
|
||||
if (!src) return dest;
|
||||
|
||||
for (i32 i = 0; i < numChars; i++)
|
||||
dest[i] = src[i];
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE bool dqnt_str_reverse(char *buf, const i32 bufSize)
|
||||
{
|
||||
if (!buf) return false;
|
||||
i32 mid = bufSize / 2;
|
||||
|
||||
for (i32 i = 0; i < mid; i++)
|
||||
{
|
||||
char tmp = buf[i];
|
||||
buf[i] = buf[(bufSize - 1) - i];
|
||||
buf[(bufSize - 1) - i] = tmp;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE i32 dqnt_str_to_i32(char *const buf, const i32 bufSize)
|
||||
{
|
||||
if (!buf || bufSize == 0) return 0;
|
||||
|
||||
i32 index = 0;
|
||||
bool isNegative = false;
|
||||
if (buf[index] == '-' || buf[index] == '+')
|
||||
{
|
||||
if (buf[index] == '-') isNegative = true;
|
||||
index++;
|
||||
}
|
||||
else if (!dqnt_char_is_digit(buf[index]))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
i32 result = 0;
|
||||
for (i32 i = index; i < bufSize; i++)
|
||||
{
|
||||
if (dqnt_char_is_digit(buf[i]))
|
||||
{
|
||||
result *= 10;
|
||||
result += (buf[i] - '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNegative) result *= -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE void dqnt_i32_to_str(i32 value, char *buf, i32 bufSize)
|
||||
{
|
||||
if (!buf || bufSize == 0) return;
|
||||
|
||||
if (value == 0)
|
||||
{
|
||||
buf[0] = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE(doyle): Max 32bit integer (+-)2147483647
|
||||
i32 charIndex = 0;
|
||||
bool negative = false;
|
||||
if (value < 0) negative = true;
|
||||
|
||||
if (negative) buf[charIndex++] = '-';
|
||||
|
||||
i32 val = DQNT_MATH_ABS(value);
|
||||
while (val != 0 && charIndex < bufSize)
|
||||
{
|
||||
i32 rem = val % 10;
|
||||
buf[charIndex++] = (u8)rem + '0';
|
||||
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)
|
||||
{
|
||||
dqnt_str_reverse(buf + 1, charIndex - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
dqnt_str_reverse(buf, charIndex);
|
||||
}
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE i32 dqnt_wstrcmp(const wchar_t *a, const wchar_t *b)
|
||||
{
|
||||
if (!a && !b) return -1;
|
||||
if (!a) return -1;
|
||||
if (!b) return -1;
|
||||
|
||||
while ((*a) == (*b))
|
||||
{
|
||||
if (!(*a)) return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return (((*a) < (*b)) ? -1 : 1);
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE i32 dqnt_wstrlen(const wchar_t *a)
|
||||
{
|
||||
i32 result = 0;
|
||||
while (a && a[result]) result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Timer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef DQNT_WIN32
|
||||
inline FILE_SCOPE f64 dqnt_win32_query_perf_counter_time_in_s_internal()
|
||||
{
|
||||
LOCAL_PERSIST LARGE_INTEGER queryPerformanceFrequency = {};
|
||||
if (queryPerformanceFrequency.QuadPart == 0)
|
||||
{
|
||||
QueryPerformanceFrequency(&queryPerformanceFrequency);
|
||||
DQNT_ASSERT(queryPerformanceFrequency.QuadPart != 0);
|
||||
}
|
||||
|
||||
LARGE_INTEGER qpcResult;
|
||||
QueryPerformanceCounter(&qpcResult);
|
||||
|
||||
// Convert to seconds
|
||||
f64 timestamp =
|
||||
(f64)(qpcResult.QuadPart / queryPerformanceFrequency.QuadPart);
|
||||
return timestamp;
|
||||
}
|
||||
#endif
|
||||
|
||||
f64 dqnt_time_now_in_s()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return dqnt_win32_query_perf_counter_time_in_s_internal();
|
||||
#else
|
||||
DQNT_ASSERT(DQNT_INVALID_CODE_PATH);
|
||||
return 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
f64 dqnt_time_now_in_ms()
|
||||
{
|
||||
return dqnt_time_now_in_s() * 1000.0f;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// PCG (Permuted Congruential Generator) Random Number Generator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Public Domain library with thanks to Mattias Gustavsson
|
||||
// https://github.com/mattiasgustavsson/libs/blob/master/docs/rnd.md
|
||||
|
||||
// Convert a randomized u32 value to a float value x in the range 0.0f <= x
|
||||
// < 1.0f. Contributed by Jonatan Hedborg
|
||||
FILE_SCOPE f32 dqnt_rnd_f32_normalized_from_u32_internal(u32 value)
|
||||
{
|
||||
u32 exponent = 127;
|
||||
u32 mantissa = value >> 9;
|
||||
u32 result = (exponent << 23) | mantissa;
|
||||
f32 fresult = *(f32 *)(&result);
|
||||
return fresult - 1.0f;
|
||||
}
|
||||
|
||||
FILE_SCOPE u64 dqnt_rnd_murmur3_avalanche64_internal(u64 h)
|
||||
{
|
||||
h ^= h >> 33;
|
||||
h *= 0xff51afd7ed558ccd;
|
||||
h ^= h >> 33;
|
||||
h *= 0xc4ceb9fe1a85ec53;
|
||||
h ^= h >> 33;
|
||||
return h;
|
||||
}
|
||||
|
||||
FILE_SCOPE u32 dqnt_rnd_make_seed_internal()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
__int64 numClockCycles = __rdtsc();
|
||||
return (u32)numClockCycles;
|
||||
#else
|
||||
unsigned long long numClockCycles = rdtsc();
|
||||
return (u32)numClockCycles;
|
||||
#endif
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE void dqnt_rnd_pcg_init_with_seed(DqntRandPCGState *pcg, u32 seed)
|
||||
{
|
||||
u64 value = (((u64)seed) << 1ULL) | 1ULL;
|
||||
value = dqnt_rnd_murmur3_avalanche64_internal(value);
|
||||
pcg->state[0] = 0U;
|
||||
pcg->state[1] = (value << 1ULL) | 1ULL;
|
||||
dqnt_rnd_pcg_next(pcg);
|
||||
pcg->state[0] += dqnt_rnd_murmur3_avalanche64_internal(value);
|
||||
dqnt_rnd_pcg_next(pcg);
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE void dqnt_rnd_pcg_init(DqntRandPCGState *pcg)
|
||||
{
|
||||
u32 seed = dqnt_rnd_make_seed_internal();
|
||||
dqnt_rnd_pcg_init_with_seed(pcg, seed);
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE u32 dqnt_rnd_pcg_next(DqntRandPCGState *pcg)
|
||||
{
|
||||
u64 oldstate = pcg->state[0];
|
||||
pcg->state[0] = oldstate * 0x5851f42d4c957f2dULL + pcg->state[1];
|
||||
u32 xorshifted = (u32)(((oldstate >> 18ULL) ^ oldstate) >> 27ULL);
|
||||
u32 rot = (u32)(oldstate >> 59ULL);
|
||||
return (xorshifted >> rot) | (xorshifted << ((-(i32)rot) & 31));
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE f32 dqnt_rnd_pcg_nextf(DqntRandPCGState *pcg)
|
||||
{
|
||||
return dqnt_rnd_f32_normalized_from_u32_internal(dqnt_rnd_pcg_next(pcg));
|
||||
}
|
||||
|
||||
DQNT_FILE_SCOPE i32 dqnt_rnd_pcg_range(DqntRandPCGState *pcg, i32 min, i32 max)
|
||||
{
|
||||
i32 const range = (max - min) + 1;
|
||||
if (range <= 0) return min;
|
||||
i32 const value = (i32)(dqnt_rnd_pcg_nextf(pcg) * range);
|
||||
return min + value;
|
||||
}
|
||||
|
||||
#endif /* DQNT_IMPLEMENTATION */
|
363
dqnt_unit_test.cpp
Normal file
363
dqnt_unit_test.cpp
Normal file
@ -0,0 +1,363 @@
|
||||
#define DQNT_IMPLEMENTATION
|
||||
#define DQNT_MAKE_STATIC
|
||||
#include "dqnt.h"
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
void dqnt_strings_test()
|
||||
{
|
||||
{ // Char Checks
|
||||
DQNT_ASSERT(dqnt_char_is_alpha('a') == true);
|
||||
DQNT_ASSERT(dqnt_char_is_alpha('A') == true);
|
||||
DQNT_ASSERT(dqnt_char_is_alpha('0') == false);
|
||||
DQNT_ASSERT(dqnt_char_is_alpha('@') == false);
|
||||
DQNT_ASSERT(dqnt_char_is_alpha(' ') == false);
|
||||
DQNT_ASSERT(dqnt_char_is_alpha('\n') == false);
|
||||
|
||||
DQNT_ASSERT(dqnt_char_is_digit('1') == true);
|
||||
DQNT_ASSERT(dqnt_char_is_digit('n') == false);
|
||||
DQNT_ASSERT(dqnt_char_is_digit('N') == false);
|
||||
DQNT_ASSERT(dqnt_char_is_digit('*') == false);
|
||||
DQNT_ASSERT(dqnt_char_is_digit(' ') == false);
|
||||
DQNT_ASSERT(dqnt_char_is_digit('\n') == false);
|
||||
|
||||
DQNT_ASSERT(dqnt_char_is_alphanum('1') == true);
|
||||
DQNT_ASSERT(dqnt_char_is_alphanum('a') == true);
|
||||
DQNT_ASSERT(dqnt_char_is_alphanum('A') == true);
|
||||
DQNT_ASSERT(dqnt_char_is_alphanum('*') == false);
|
||||
DQNT_ASSERT(dqnt_char_is_alphanum(' ') == false);
|
||||
DQNT_ASSERT(dqnt_char_is_alphanum('\n') == false);
|
||||
|
||||
printf("dqnt_strings_test(): char_checks: Completed successfully\n");
|
||||
}
|
||||
|
||||
// String Checks
|
||||
{
|
||||
// strcmp
|
||||
{
|
||||
char *a = "str_a";
|
||||
|
||||
// Check simple compares
|
||||
{
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "str_a") == +0);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "str_b") == -1);
|
||||
DQNT_ASSERT(dqnt_strcmp("str_b", a) == +1);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "") == +1);
|
||||
DQNT_ASSERT(dqnt_strcmp("", "") == 0);
|
||||
|
||||
// NOTE: Check that the string has not been trashed.
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "str_a") == +0);
|
||||
}
|
||||
|
||||
// Check ops against null
|
||||
{
|
||||
DQNT_ASSERT(dqnt_strcmp(NULL, NULL) != +0);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, NULL) != +0);
|
||||
DQNT_ASSERT(dqnt_strcmp(NULL, a) != +0);
|
||||
}
|
||||
|
||||
printf("dqnt_strings_test(): strcmp: Completed successfully\n");
|
||||
}
|
||||
|
||||
// strlen
|
||||
{
|
||||
char *a = "str_a";
|
||||
DQNT_ASSERT(dqnt_strlen(a) == 5);
|
||||
DQNT_ASSERT(dqnt_strlen("") == 0);
|
||||
DQNT_ASSERT(dqnt_strlen(" a ") == 6);
|
||||
DQNT_ASSERT(dqnt_strlen("a\n") == 2);
|
||||
|
||||
// NOTE: Check that the string has not been trashed.
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "str_a") == 0);
|
||||
|
||||
DQNT_ASSERT(dqnt_strlen(NULL) == 0);
|
||||
|
||||
printf("dqnt_strings_test(): strlen: Completed successfully\n");
|
||||
}
|
||||
|
||||
// strncpy
|
||||
{
|
||||
{
|
||||
char *a = "str_a";
|
||||
char b[10] = {};
|
||||
// Check copy into empty array
|
||||
{
|
||||
char *result = dqnt_strncpy(b, a, dqnt_strlen(a));
|
||||
DQNT_ASSERT(dqnt_strcmp(b, "str_a") == 0);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "str_a") == 0);
|
||||
DQNT_ASSERT(dqnt_strcmp(result, "str_a") == 0);
|
||||
DQNT_ASSERT(dqnt_strlen(result) == 5);
|
||||
}
|
||||
|
||||
// Check copy into array offset, overlap with old results
|
||||
{
|
||||
char *newResult = dqnt_strncpy(&b[1], a, dqnt_strlen(a));
|
||||
DQNT_ASSERT(dqnt_strcmp(newResult, "str_a") == 0);
|
||||
DQNT_ASSERT(dqnt_strlen(newResult) == 5);
|
||||
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "str_a") == 0);
|
||||
DQNT_ASSERT(dqnt_strlen(a) == 5);
|
||||
|
||||
DQNT_ASSERT(dqnt_strcmp(b, "sstr_a") == 0);
|
||||
DQNT_ASSERT(dqnt_strlen(b) == 6);
|
||||
}
|
||||
}
|
||||
|
||||
// Check strncpy with NULL pointers
|
||||
{
|
||||
DQNT_ASSERT(dqnt_strncpy(NULL, NULL, 5) == NULL);
|
||||
|
||||
char *a = "str";
|
||||
char *result = dqnt_strncpy(a, NULL, 5);
|
||||
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "str") == 0);
|
||||
DQNT_ASSERT(dqnt_strcmp(result, "str") == 0);
|
||||
DQNT_ASSERT(dqnt_strcmp(result, a) == 0);
|
||||
}
|
||||
|
||||
// Check strncpy with 0 chars to copy
|
||||
{
|
||||
char *a = "str";
|
||||
char *b = "ing";
|
||||
|
||||
char *result = dqnt_strncpy(a, b, 0);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "str") == 0);
|
||||
DQNT_ASSERT(dqnt_strcmp(b, "ing") == 0);
|
||||
DQNT_ASSERT(dqnt_strcmp(result, "str") == 0);
|
||||
}
|
||||
|
||||
printf("dqnt_strings_test(): strncpy: Completed successfully\n");
|
||||
}
|
||||
|
||||
// str_reverse
|
||||
{
|
||||
// Basic reverse operations
|
||||
{
|
||||
char a[] = "aba";
|
||||
DQNT_ASSERT(dqnt_str_reverse(a, dqnt_strlen(a)) == true);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "aba") == 0);
|
||||
|
||||
DQNT_ASSERT(dqnt_str_reverse(a, 2) == true);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "baa") == 0);
|
||||
|
||||
DQNT_ASSERT(dqnt_str_reverse(a, dqnt_strlen(a)) == true);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "aab") == 0);
|
||||
|
||||
DQNT_ASSERT(dqnt_str_reverse(&a[1], 2) == true);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "aba") == 0);
|
||||
|
||||
DQNT_ASSERT(dqnt_str_reverse(a, 0) == true);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "aba") == 0);
|
||||
}
|
||||
|
||||
// Try reverse empty string
|
||||
{
|
||||
char a[] = "";
|
||||
DQNT_ASSERT(dqnt_str_reverse(a, dqnt_strlen(a)) == true);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "") == 0);
|
||||
}
|
||||
|
||||
// Try reverse single char string
|
||||
{
|
||||
char a[] = "a";
|
||||
DQNT_ASSERT(dqnt_str_reverse(a, dqnt_strlen(a)) == true);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "a") == 0);
|
||||
|
||||
DQNT_ASSERT(dqnt_str_reverse(a, 0) == true);
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "a") == 0);
|
||||
}
|
||||
|
||||
printf(
|
||||
"dqnt_strings_test(): str_reverse: Completed successfully\n");
|
||||
}
|
||||
|
||||
// str_to_i32
|
||||
{
|
||||
char *a = "123";
|
||||
DQNT_ASSERT(dqnt_str_to_i32(a, dqnt_strlen(a)) == 123);
|
||||
|
||||
char *b = "-123";
|
||||
DQNT_ASSERT(dqnt_str_to_i32(b, dqnt_strlen(b)) == -123);
|
||||
DQNT_ASSERT(dqnt_str_to_i32(b, 1) == 0);
|
||||
DQNT_ASSERT(dqnt_str_to_i32(&b[1], dqnt_strlen(&b[1])) == 123);
|
||||
|
||||
char *c = "-0";
|
||||
DQNT_ASSERT(dqnt_str_to_i32(c, dqnt_strlen(c)) == 0);
|
||||
|
||||
char *d = "+123";
|
||||
DQNT_ASSERT(dqnt_str_to_i32(d, dqnt_strlen(d)) == 123);
|
||||
DQNT_ASSERT(dqnt_str_to_i32(&d[1], dqnt_strlen(&d[1])) == 123);
|
||||
|
||||
printf("dqnt_strings_test(): str_to_i32: Completed successfully\n");
|
||||
}
|
||||
|
||||
// i32_to_str
|
||||
{
|
||||
char a[DQNT_I32_TO_STR_MAX_BUF_SIZE] = {};
|
||||
dqnt_i32_to_str(+100, a, DQNT_ARRAY_COUNT(a));
|
||||
DQNT_ASSERT(dqnt_strcmp(a, "100") == 0);
|
||||
|
||||
char b[DQNT_I32_TO_STR_MAX_BUF_SIZE] = {};
|
||||
dqnt_i32_to_str(-100, b, DQNT_ARRAY_COUNT(b));
|
||||
DQNT_ASSERT(dqnt_strcmp(b, "-100") == 0);
|
||||
|
||||
char c[DQNT_I32_TO_STR_MAX_BUF_SIZE] = {};
|
||||
dqnt_i32_to_str(0, c, DQNT_ARRAY_COUNT(c));
|
||||
DQNT_ASSERT(dqnt_strcmp(c, "0") == 0);
|
||||
|
||||
printf("dqnt_strings_test(): str_to_i32: Completed successfully\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Wide String Checks
|
||||
{
|
||||
// wstrcmp
|
||||
{
|
||||
wchar_t *a = L"str_a";
|
||||
|
||||
// Check simple compares
|
||||
{
|
||||
DQNT_ASSERT(dqnt_wstrcmp(a, L"str_a") == +0);
|
||||
DQNT_ASSERT(dqnt_wstrcmp(a, L"str_b") == -1);
|
||||
DQNT_ASSERT(dqnt_wstrcmp(L"str_b", a) == +1);
|
||||
DQNT_ASSERT(dqnt_wstrcmp(a, L"") == +1);
|
||||
DQNT_ASSERT(dqnt_wstrcmp(L"", L"") == 0);
|
||||
|
||||
// NOTE: Check that the string has not been trashed.
|
||||
DQNT_ASSERT(dqnt_wstrcmp(a, L"str_a") == +0);
|
||||
}
|
||||
|
||||
// Check ops against null
|
||||
{
|
||||
DQNT_ASSERT(dqnt_wstrcmp(NULL, NULL) != +0);
|
||||
DQNT_ASSERT(dqnt_wstrcmp(a, NULL) != +0);
|
||||
DQNT_ASSERT(dqnt_wstrcmp(NULL, a) != +0);
|
||||
}
|
||||
|
||||
printf("dqnt_strings_test(): wstrcmp: Completed successfully\n");
|
||||
}
|
||||
|
||||
// wstrlen
|
||||
{
|
||||
wchar_t *a = L"str_a";
|
||||
DQNT_ASSERT(dqnt_wstrlen(a) == 5);
|
||||
DQNT_ASSERT(dqnt_wstrlen(L"") == 0);
|
||||
DQNT_ASSERT(dqnt_wstrlen(L" a ") == 6);
|
||||
DQNT_ASSERT(dqnt_wstrlen(L"a\n") == 2);
|
||||
|
||||
// NOTE: Check that the string has not been trashed.
|
||||
DQNT_ASSERT(dqnt_wstrcmp(a, L"str_a") == 0);
|
||||
|
||||
DQNT_ASSERT(dqnt_wstrlen(NULL) == 0);
|
||||
|
||||
printf("dqnt_strings_test(): wstrlen: Completed successfully\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("dqnt_strings_test(): Completed successfully\n");
|
||||
}
|
||||
|
||||
#include "Windows.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
void dqnt_other_test()
|
||||
{
|
||||
{ // Test Win32 Sleep
|
||||
// NOTE: Win32 Sleep is not granular to a certain point so sleep excessively to meet
|
||||
u32 sleepInMs = 100;
|
||||
f64 startInMs = dqnt_time_now_in_ms();
|
||||
Sleep(sleepInMs);
|
||||
f64 endInMs = dqnt_time_now_in_ms();
|
||||
|
||||
DQNT_ASSERT(startInMs < endInMs);
|
||||
printf("dqnt_other_test(): time_now: Completed successfully\n");
|
||||
}
|
||||
printf("dqnt_other_test(): Completed successfully\n");
|
||||
}
|
||||
|
||||
void dqnt_random_test() {
|
||||
|
||||
DqntRandPCGState pcg;
|
||||
dqnt_rnd_pcg_init(&pcg);
|
||||
for (i32 i = 0; i < 10; i++)
|
||||
{
|
||||
i32 min = -100;
|
||||
i32 max = 100000;
|
||||
i32 result = dqnt_rnd_pcg_range(&pcg, min, max);
|
||||
DQNT_ASSERT(result >= min && result <= max)
|
||||
|
||||
f32 randF32 = dqnt_rnd_pcg_nextf(&pcg);
|
||||
DQNT_ASSERT(randF32 >= 0.0f && randF32 <= 1.0f);
|
||||
printf("dqnt_random_test(): rnd_pcg: Completed successfully\n");
|
||||
}
|
||||
|
||||
printf("dqnt_random_test(): Completed successfully\n");
|
||||
}
|
||||
|
||||
void dqnt_vec_test()
|
||||
{
|
||||
{ // V2
|
||||
{
|
||||
dqnt_v2 vec = dqnt_vec2(5.5f, 5.0f);
|
||||
DQNT_ASSERT(vec.x == 5.5f && vec.y == 5.0f);
|
||||
DQNT_ASSERT(vec.w == 5.5f && vec.h == 5.0f);
|
||||
}
|
||||
|
||||
{
|
||||
dqnt_v2 vec = dqnt_vec2i(3, 5);
|
||||
DQNT_ASSERT(vec.x == 3 && vec.y == 5.0f);
|
||||
DQNT_ASSERT(vec.w == 3 && vec.h == 5.0f);
|
||||
}
|
||||
|
||||
{ // constrain_to_ratio
|
||||
dqnt_v2 ratio = dqnt_vec2(16, 9);
|
||||
dqnt_v2 dim = dqnt_vec2(2000, 1080);
|
||||
dqnt_v2 result = dqnt_vec2_constrain_to_ratio(dim, ratio);
|
||||
DQNT_ASSERT(result.w == 1920 && result.h == 1080);
|
||||
}
|
||||
|
||||
printf("dqnt_vec_test(): vec2: Completed successfully\n");
|
||||
}
|
||||
|
||||
{ // V3
|
||||
{
|
||||
dqnt_v3 vec = dqnt_vec3(5.5f, 5.0f, 5.875f);
|
||||
DQNT_ASSERT(vec.x == 5.5f && vec.y == 5.0f && vec.z == 5.875f);
|
||||
DQNT_ASSERT(vec.r == 5.5f && vec.g == 5.0f && vec.b == 5.875f);
|
||||
}
|
||||
|
||||
{
|
||||
dqnt_v3 vec = dqnt_vec3(3, 4, 5);
|
||||
DQNT_ASSERT(vec.x == 3 && vec.y == 4 && vec.z == 5);
|
||||
DQNT_ASSERT(vec.r == 3 && vec.g == 4 && vec.b == 5);
|
||||
}
|
||||
|
||||
printf("dqnt_vec_test(): vec3: Completed successfully\n");
|
||||
}
|
||||
|
||||
{ // V4
|
||||
{
|
||||
dqnt_v4 vec = dqnt_vec4(5.5f, 5.0f, 5.875f, 5.928f);
|
||||
DQNT_ASSERT(vec.x == 5.5f && vec.y == 5.0f && vec.z == 5.875f && vec.w == 5.928f);
|
||||
DQNT_ASSERT(vec.r == 5.5f && vec.g == 5.0f && vec.b == 5.875f && vec.a == 5.928f);
|
||||
}
|
||||
|
||||
{
|
||||
dqnt_v4 vec = dqnt_vec4i(3, 4, 5, 6);
|
||||
DQNT_ASSERT(vec.x == 3 && vec.y == 4 && vec.z == 5 && vec.w == 6);
|
||||
DQNT_ASSERT(vec.r == 3 && vec.g == 4 && vec.b == 5 && vec.a == 6);
|
||||
}
|
||||
|
||||
printf("dqnt_vec_test(): vec4: Completed successfully\n");
|
||||
}
|
||||
|
||||
printf("dqnt_vec_test(): Completed successfully\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
dqnt_strings_test();
|
||||
dqnt_random_test();
|
||||
dqnt_vec_test();
|
||||
dqnt_other_test();
|
||||
return 0;
|
||||
}
|
29
misc/dqnt_unit_test.sln
Normal file
29
misc/dqnt_unit_test.sln
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{911E67C6-3D85-4FCE-B560-20A9C3E3FF48}") = "dqnt_unit_test", "..\bin\dqnt_unit_test.exe", "{87785192-6F49-4F85-AA0D-F0AFA5CCCDDA}"
|
||||
ProjectSection(DebuggerProjectSystem) = preProject
|
||||
PortSupplier = 00000000-0000-0000-0000-000000000000
|
||||
Executable = C:\git\dqnt\bin\dqnt_unit_test.exe
|
||||
RemoteMachine = THAI-PC
|
||||
StartingDirectory = C:\git\dqnt
|
||||
Environment = Default
|
||||
LaunchingEngine = 00000000-0000-0000-0000-000000000000
|
||||
UseLegacyDebugEngines = No
|
||||
LaunchSQLEngine = No
|
||||
AttachLaunchAction = No
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{87785192-6F49-4F85-AA0D-F0AFA5CCCDDA}.Release|x86.ActiveCfg = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
Loading…
Reference in New Issue
Block a user