130 lines
2.6 KiB
C
130 lines
2.6 KiB
C
|
// 2 may 2019
|
||
|
#include <string.h>
|
||
|
#include "timer.h"
|
||
|
|
||
|
// This is based on the algorithm that Go uses for time.Duration.
|
||
|
// Of course, we're not expressing it the same way...
|
||
|
struct timerStringPart {
|
||
|
char suffix;
|
||
|
char suffix2;
|
||
|
int mode;
|
||
|
uint32_t maxOrMod;
|
||
|
int precision;
|
||
|
};
|
||
|
|
||
|
enum {
|
||
|
modeMaxAndStop,
|
||
|
modeFracModContinue,
|
||
|
};
|
||
|
|
||
|
static const struct timerStringPart parts[] = {
|
||
|
{ 'n', 's', modeMaxAndStop, 1000, 0 },
|
||
|
{ 'u', 's', modeMaxAndStop, 1000000, 3 },
|
||
|
{ 'm', 's', modeMaxAndStop, 1000000000, 6 },
|
||
|
{ 's', 0, modeFracModContinue, 60, 9 },
|
||
|
{ 'm', 0, modeFracModContinue, 60, 0 },
|
||
|
{ 'h', 0, modeFracModContinue, 60, 0 },
|
||
|
{ 0, 0, 0, 0, 0 },
|
||
|
};
|
||
|
|
||
|
static int fillFracPart(char *buf, int precision, int start, uint64_t *unsec)
|
||
|
{
|
||
|
int i;
|
||
|
int print;
|
||
|
uint64_t digit;
|
||
|
|
||
|
print = 0;
|
||
|
for (i = 0; i < precision; i++) {
|
||
|
digit = *unsec % 10;
|
||
|
print = print || (digit != 0);
|
||
|
if (print) {
|
||
|
buf[start - 1] = "0123456789"[digit];
|
||
|
start--;
|
||
|
}
|
||
|
*unsec /= 10;
|
||
|
}
|
||
|
if (print) {
|
||
|
buf[start - 1] = '.';
|
||
|
start--;
|
||
|
}
|
||
|
return start;
|
||
|
}
|
||
|
|
||
|
static int fillIntPart(char *buf, int start, uint64_t unsec)
|
||
|
{
|
||
|
if (unsec == 0) {
|
||
|
buf[start - 1] = '0';
|
||
|
start--;
|
||
|
return start;
|
||
|
}
|
||
|
while (unsec != 0) {
|
||
|
buf[start - 1] = "0123456789"[unsec % 10];
|
||
|
start--;
|
||
|
unsec /= 10;
|
||
|
}
|
||
|
return start;
|
||
|
}
|
||
|
|
||
|
void timerDurationString(timerDuration d, char buf[timerDurationStringLen])
|
||
|
{
|
||
|
uint64_t unsec;
|
||
|
int neg;
|
||
|
int start;
|
||
|
const struct timerStringPart *p;
|
||
|
|
||
|
memset(buf, 0, timerDurationStringLen * sizeof (char));
|
||
|
start = 32;
|
||
|
|
||
|
if (d == 0) {
|
||
|
buf[0] = '0';
|
||
|
buf[1] = 's';
|
||
|
return;
|
||
|
}
|
||
|
unsec = (uint64_t) d;
|
||
|
neg = 0;
|
||
|
if (d < 0) {
|
||
|
#ifdef _MSC_VER
|
||
|
// TODO figure out a more explicit way to do this; until then, just go with what the standard says should happen, because it's what we want (TODO verify this)
|
||
|
#pragma warning(push)
|
||
|
#pragma warning(disable: 4146)
|
||
|
#endif
|
||
|
unsec = -unsec;
|
||
|
#ifdef _MSC_VER
|
||
|
#pragma warning(pop)
|
||
|
#endif
|
||
|
neg = 1;
|
||
|
}
|
||
|
|
||
|
for (p = parts; p->suffix != 0; p++) {
|
||
|
if (p->mode == modeMaxAndStop && unsec < p->maxOrMod) {
|
||
|
if (p->suffix2 != 0) {
|
||
|
buf[start - 1] = p->suffix2;
|
||
|
start--;
|
||
|
}
|
||
|
buf[start - 1] = p->suffix;
|
||
|
start--;
|
||
|
start = fillFracPart(buf, p->precision, start, &unsec);
|
||
|
start = fillIntPart(buf, start, unsec);
|
||
|
break;
|
||
|
}
|
||
|
if (p->mode == modeFracModContinue && unsec != 0) {
|
||
|
if (p->suffix2 != 0) {
|
||
|
buf[start - 1] = p->suffix2;
|
||
|
start--;
|
||
|
}
|
||
|
buf[start - 1] = p->suffix;
|
||
|
start--;
|
||
|
start = fillFracPart(buf, p->precision, start, &unsec);
|
||
|
start = fillIntPart(buf, start, unsec % p->maxOrMod);
|
||
|
unsec /= p->maxOrMod;
|
||
|
// and move on to the next one
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (neg) {
|
||
|
buf[start - 1] = '-';
|
||
|
start--;
|
||
|
}
|
||
|
memmove(buf, buf + start, 33 - start);
|
||
|
}
|