commit ab5fa3995789e86a270de8f25b98cd6f4972c82b Author: Doyle Thai Date: Sun Apr 9 15:08:31 2017 +1000 Initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..82ec7e2 --- /dev/null +++ b/.clang-format @@ -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, + # +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d67854b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +tags +*.swp +*.swo +bin/ +.vs/ +*.db diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..ffa3631 --- /dev/null +++ b/build.bat @@ -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 + diff --git a/dqnt.h b/dqnt.h new file mode 100644 index 0000000..981a608 --- /dev/null +++ b/dqnt.h @@ -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 */ diff --git a/dqnt_unit_test.cpp b/dqnt_unit_test.cpp new file mode 100644 index 0000000..1ec5d5c --- /dev/null +++ b/dqnt_unit_test.cpp @@ -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; +} diff --git a/misc/dqnt_unit_test.sln b/misc/dqnt_unit_test.sln new file mode 100644 index 0000000..f9702af --- /dev/null +++ b/misc/dqnt_unit_test.sln @@ -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