-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Remove printf() call on release builds. This is more deterministic than printf() modifiers. We only use it printing u64, f32 variables. - Add unit tests.
- Loading branch information
Showing
11 changed files
with
964 additions
and
385 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: test | ||
|
||
on: | ||
push: | ||
branches: [ "master" ] | ||
pull_request: | ||
branches: [ "master" ] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: install packages | ||
run: sudo apt-get install -y --no-install-recommends --no-install-suggests | ||
meson ninja-build | ||
liburing-dev | ||
libwayland-dev libwayland-client0 wayland-protocols | ||
|
||
- name: configure | ||
run: meson setup build --buildtype release -Dtest=true | ||
|
||
- name: test | ||
run: meson test -C build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#pragma once | ||
|
||
#if GAMEPAD_IDLE_INHIBIT_DEBUG | ||
#define debug_assert(x) \ | ||
if (!(x)) { \ | ||
__builtin_debugtrap(); \ | ||
} | ||
#else | ||
#define debug_assert(x) | ||
#endif | ||
|
||
#define runtime_assert(x) \ | ||
if (!(x)) { \ | ||
__builtin_trap(); \ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,340 @@ | ||
#pragma once | ||
|
||
#include "assert.h" | ||
#include "type.h" | ||
|
||
struct string { | ||
u8 *value; | ||
u64 length; | ||
}; | ||
|
||
static inline struct string | ||
StringFromZeroTerminated(u8 *src, u64 max) | ||
{ | ||
debug_assert(src != 0); | ||
struct string string = {}; | ||
|
||
string.value = src; | ||
|
||
while (*src) { | ||
string.length++; | ||
if (string.length == max) | ||
break; | ||
src++; | ||
} | ||
|
||
return string; | ||
} | ||
|
||
static inline b8 | ||
IsStringEqual(struct string *left, struct string *right) | ||
{ | ||
if (!left || !right || left->length != right->length) | ||
return 0; | ||
|
||
for (u64 index = 0; index < left->length; index++) { | ||
if (left->value[index] != right->value[index]) | ||
return 0; | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
static inline b8 | ||
IsStringContains(struct string *string, struct string *search) | ||
{ | ||
if (!string || !search || string->length < search->length) | ||
return 0; | ||
|
||
for (u64 stringIndex = 0; stringIndex < string->length; stringIndex++) { | ||
b8 isFound = 1; | ||
for (u64 searchIndex = 0, substringIndex = stringIndex; searchIndex < search->length; | ||
searchIndex++, substringIndex++) { | ||
b8 isEndOfString = substringIndex == string->length; | ||
if (isEndOfString) { | ||
isFound = 0; | ||
break; | ||
} | ||
|
||
b8 isCharactersNotMatching = string->value[substringIndex] != search->value[searchIndex]; | ||
if (isCharactersNotMatching) { | ||
isFound = 0; | ||
break; | ||
} | ||
} | ||
|
||
if (isFound) | ||
return 1; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static inline b8 | ||
IsStringStartsWith(struct string *string, struct string *search) | ||
{ | ||
if (!string || !search || string->length < search->length) | ||
return 0; | ||
|
||
for (u64 searchIndex = 0; searchIndex < search->length; searchIndex++) { | ||
b8 isCharactersNotMatching = string->value[searchIndex] != search->value[searchIndex]; | ||
if (isCharactersNotMatching) | ||
return 0; | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
struct duration { | ||
u64 ns; | ||
}; | ||
|
||
static inline b8 | ||
ParseDuration(struct string *string, struct duration *duration) | ||
{ | ||
if (!string || string->length == 0 || string->length < 3) | ||
return 0; | ||
|
||
// | Duration | Length | | ||
// |----------|-------------| | ||
// | ns | nanosecond | | ||
// | us | microsecond | | ||
// | ms | millisecond | | ||
// | sec | second | | ||
// | min | minute | | ||
// | hr | hour | | ||
// | day | day | | ||
// | wk | week | | ||
|
||
#define UNIT_STRING(variableName, zeroTerminatedString) \ | ||
static struct string variableName = { \ | ||
.value = (u8 *)zeroTerminatedString, \ | ||
.length = sizeof(zeroTerminatedString) - 1, \ | ||
} | ||
UNIT_STRING(nanosecondUnitString, "ns"); | ||
UNIT_STRING(microsecondUnitString, "us"); | ||
UNIT_STRING(millisocondUnitString, "ms"); | ||
UNIT_STRING(secondUnitString, "sec"); | ||
UNIT_STRING(minuteUnitString, "min"); | ||
UNIT_STRING(hourUnitString, "hr"); | ||
UNIT_STRING(dayUnitString, "day"); | ||
UNIT_STRING(weekUnitString, "wk"); | ||
#undef UNIT_STRING | ||
|
||
b8 isUnitExistsInString = | ||
IsStringContains(string, &nanosecondUnitString) || IsStringContains(string, µsecondUnitString) || | ||
IsStringContains(string, &millisocondUnitString) || IsStringContains(string, &secondUnitString) || | ||
IsStringContains(string, &minuteUnitString) || IsStringContains(string, &hourUnitString) || | ||
IsStringContains(string, &dayUnitString) || IsStringContains(string, &weekUnitString); | ||
if (!isUnitExistsInString) { | ||
return 0; | ||
} | ||
|
||
struct duration parsed = {}; | ||
u64 value = 0; | ||
for (u64 index = 0; index < string->length; index++) { | ||
u8 digitCharacter = string->value[index]; | ||
b8 isDigit = digitCharacter >= '0' && digitCharacter <= '9'; | ||
if (!isDigit) { | ||
// - get unit | ||
struct string unitString = {.value = string->value + index, .length = string->length - index}; | ||
if (/* unit: nanosecond */ IsStringStartsWith(&unitString, &nanosecondUnitString)) { | ||
parsed.ns += value; | ||
index += nanosecondUnitString.length; | ||
} else if (/* unit: microsecond */ IsStringStartsWith(&unitString, µsecondUnitString)) { | ||
parsed.ns += value * 1e3L; | ||
index += microsecondUnitString.length; | ||
} else if (/* unit: millisecond */ IsStringStartsWith(&unitString, &millisocondUnitString)) { | ||
parsed.ns += value * 1e6L; | ||
index += millisocondUnitString.length; | ||
} else if (/* unit: second */ IsStringStartsWith(&unitString, &secondUnitString)) { | ||
parsed.ns += value * 1e9L; | ||
index += secondUnitString.length; | ||
} else if (/* unit: minute */ IsStringStartsWith(&unitString, &minuteUnitString)) { | ||
parsed.ns += value * 1e9L * 60; | ||
index += minuteUnitString.length; | ||
} else if (/* unit: hour */ IsStringStartsWith(&unitString, &hourUnitString)) { | ||
parsed.ns += value * 1e9L * 60 * 60; | ||
index += hourUnitString.length; | ||
} else if (/* unit: day */ IsStringStartsWith(&unitString, &dayUnitString)) { | ||
parsed.ns += value * 1e9L * 60 * 60 * 24; | ||
index += dayUnitString.length; | ||
} else if (/* unit: week */ IsStringStartsWith(&unitString, &weekUnitString)) { | ||
parsed.ns += value * 1e9L * 60 * 60 * 24 * 7; | ||
index += weekUnitString.length; | ||
} else { | ||
// unsupported unit | ||
return 0; | ||
} | ||
|
||
// - reset value | ||
value = 0; | ||
continue; | ||
} | ||
|
||
value *= 10; | ||
u8 digit = digitCharacter - (u8)'0'; | ||
value += digit; | ||
} | ||
|
||
*duration = parsed; | ||
|
||
return 1; | ||
} | ||
|
||
static inline b8 | ||
IsDurationLessThan(struct duration *left, struct duration *right) | ||
{ | ||
return left->ns < right->ns; | ||
} | ||
|
||
static inline b8 | ||
IsDurationGraterThan(struct duration *left, struct duration *right) | ||
{ | ||
return left->ns > right->ns; | ||
} | ||
|
||
static inline b8 | ||
ParseU64(struct string *string, u64 *value) | ||
{ | ||
// max u64: 18446744073709551615 | ||
if (!string || string->length > 20) | ||
return 0; | ||
|
||
u64 parsed = 0; | ||
for (u64 index = 0; index < string->length; index++) { | ||
u8 digitCharacter = string->value[index]; | ||
b8 isDigit = digitCharacter >= '0' && digitCharacter <= '9'; | ||
if (!isDigit) { | ||
return 0; | ||
} | ||
|
||
parsed *= 10; | ||
u8 digit = digitCharacter - (u8)'0'; | ||
parsed += digit; | ||
} | ||
|
||
*value = parsed; | ||
return 1; | ||
} | ||
|
||
/* | ||
* string buffer must at least able to hold 1 bytes, at most 20 bytes. | ||
*/ | ||
static inline struct string | ||
FormatU64(struct string *stringBuffer, u64 value) | ||
{ | ||
struct string result = {}; | ||
if (!stringBuffer || stringBuffer->length == 0) | ||
return result; | ||
|
||
// max u64: 18446744073709551615 | ||
static const u64 powersOf10[20] = { | ||
1e00L, 1e01L, 1e02L, 1e03L, 1e04L, 1e05L, 1e06L, 1e07L, 1e08L, 1e09L, | ||
1e10L, 1e11L, 1e12L, 1e13L, 1e14L, 1e15L, 1e16L, 1e17L, 1e18L, 1e19L, | ||
}; | ||
u64 countOfDigits = 1; | ||
while (countOfDigits < 20 && value > powersOf10[countOfDigits]) | ||
countOfDigits++; | ||
|
||
if (countOfDigits > stringBuffer->length) | ||
return result; | ||
|
||
u64 index = 0; | ||
while (countOfDigits > 0) { | ||
u64 power = powersOf10[countOfDigits - 1]; | ||
u64 digit = value / power; | ||
|
||
// turn digit into character | ||
stringBuffer->value[index] = digit + (u8)'0'; | ||
|
||
value -= digit * power; | ||
|
||
index++; | ||
countOfDigits--; | ||
} | ||
|
||
result.value = stringBuffer->value; | ||
result.length = index; // written digits | ||
return result; | ||
} | ||
|
||
static inline struct string | ||
FormatS64(struct string *stringBuffer, s64 value) | ||
{ | ||
struct string result = {}; | ||
if (!stringBuffer || stringBuffer->length == 0) | ||
return result; | ||
|
||
b8 isNegativeValue = value < 0; | ||
if (isNegativeValue) { | ||
value *= -1; | ||
stringBuffer->value[0] = '-'; | ||
stringBuffer->value += 1; | ||
stringBuffer->length -= 1; | ||
} | ||
|
||
result = FormatU64(stringBuffer, value); | ||
return result; | ||
} | ||
|
||
/* | ||
* string buffer must at least able to hold 3 bytes. | ||
* fractionCount [1,8] | ||
*/ | ||
static inline struct string | ||
FormatF32(struct string *stringBuffer, f32 value, u32 fractionCount) | ||
{ | ||
debug_assert(fractionCount >= 1 && fractionCount <= 8); | ||
|
||
struct string result = {}; | ||
if (!stringBuffer || stringBuffer->length <= 3) | ||
return result; | ||
|
||
// 1 - convert integer part to string | ||
// assume value: 10.123 | ||
// integerValue: 10 | ||
struct string stringBufferForInteger = { | ||
.value = stringBuffer->value, | ||
.length = stringBuffer->length, | ||
}; | ||
|
||
b8 isNegativeValue = value < 0; | ||
if (isNegativeValue) { | ||
value *= -1; | ||
stringBufferForInteger.value[0] = '-'; | ||
stringBufferForInteger.value += 1; | ||
stringBufferForInteger.length -= 1; | ||
} | ||
|
||
u32 integerValue = (u32)value; | ||
struct string integerString = FormatU64(&stringBufferForInteger, (u64)integerValue); | ||
if (integerString.length == 0) | ||
return result; | ||
|
||
// 2 - insert point | ||
stringBufferForInteger.value[integerString.length] = '.'; | ||
|
||
// 3 - convert fraction to string | ||
struct string stringBufferForFraction = { | ||
.value = stringBufferForInteger.value + integerString.length + 1, | ||
.length = stringBufferForInteger.length - (integerString.length + 1), | ||
}; | ||
|
||
// assume fractionCount = 2 | ||
// 0.123 = 10.123 - 10.000 | ||
// 12.30 = 0.123 * (10 ^ fractionCount) | ||
// 12 = (int)12.30 | ||
u64 fractionMultiplier = 10; | ||
for (u32 fractionIndex = 1; fractionIndex < fractionCount; fractionIndex++) | ||
fractionMultiplier *= 10; | ||
|
||
u32 fractionValue = (u32)((f32)(value - (f32)integerValue) * (f32)fractionMultiplier); | ||
struct string fractionString = FormatU64(&stringBufferForFraction, fractionValue); | ||
if (fractionString.length == 0) | ||
return result; | ||
|
||
result.value = stringBuffer->value; | ||
result.length = isNegativeValue + integerString.length + 1 + fractionString.length; | ||
return result; | ||
} |
Oops, something went wrong.