Created
November 29, 2023 04:15
-
-
Save michael-grunder/b9429a0fc9560e176841b1f3ba1bf659 to your computer and use it in GitHub Desktop.
Toy program to convert uint8_t strings into uint8_t values.
This file contains hidden or 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
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <time.h> | |
#include <sys/time.h> | |
#include <locale.h> | |
#define MAX_DIGITS 3 | |
#define TABLE_SIZE 3750194 | |
struct numbers { | |
char *ptr; | |
uint8_t *dgt; | |
}; | |
uint8_t *g_lookup; | |
long long ustime(void) { | |
struct timeval tv; | |
gettimeofday(&tv, NULL); | |
return tv.tv_sec * 1000000 + tv.tv_usec; | |
} | |
void printTiming(const char *what, long long n, long long tm) { | |
double sec = tm / 1000000.00, psec = n / sec; | |
printf("Executed %lld '%s' calls in %f sec (%'lu/sec)\n", | |
n, what, sec, (unsigned long)psec); | |
} | |
static void *zcalloc(size_t n) { | |
void *ptr; | |
ptr = calloc(1, n); | |
if (ptr == NULL) { | |
fprintf(stderr, "Error: Failed to allocate %zu bytes\n", n); | |
exit(1); | |
} | |
return ptr; | |
} | |
static size_t countDigits(uint8_t n) { | |
if (n >= 100) | |
return 3; | |
else if (n >= 10) | |
return 2; | |
else | |
return 1; | |
} | |
struct numbers createNumbers(size_t n) { | |
struct numbers res = {0}; | |
res.ptr = zcalloc(3 * n + 1); | |
res.dgt = zcalloc(sizeof(*res.dgt) * n); | |
if (res.ptr == NULL || res.dgt == NULL) | |
abort(); | |
char *ptr = res.ptr; | |
for (size_t i = 0; i < n; i++) { | |
uint8_t rng = rand() % 256; | |
res.dgt[i] = sprintf(ptr, "%u", rng); | |
ptr += res.dgt[i]; | |
} | |
return res; | |
} | |
void freeNumbers(struct numbers *nums) { | |
if (nums == NULL) | |
return; | |
free(nums->ptr); | |
free(nums->dgt); | |
} | |
typedef union { | |
struct { | |
uint32_t v24: 24; | |
uint8_t unused; | |
} v24; | |
uint16_t v16; | |
uint8_t v8; | |
} vli; | |
#ifdef SAFE | |
static inline size_t calcIndexValue(char *num, size_t len) { | |
vli aux = {0}; | |
if (len == 3) { | |
memcpy(&aux, num, sizeof(aux)); | |
return aux.v24.v24; | |
} else if (len == 2) { | |
memcpy(&aux, num, 2); | |
return aux.v16; | |
} else { | |
memcpy(&aux, num, 1); | |
return aux.v8; | |
} | |
} | |
#else | |
static inline size_t calcIndexValue(char *num, size_t len) { | |
vli aux; | |
// We are assuming we can always copy sizeof(aux) bytes | |
memcpy(&aux, num, sizeof(aux)); | |
if (len == 3) { | |
return aux.v24.v24; | |
} else if (len == 2) { | |
return aux.v16; | |
} else { | |
return aux.v8; | |
} | |
} | |
#endif | |
static inline uint8_t parse_u8(char *num, size_t len) { | |
return g_lookup[calcIndexValue(num, len)]; | |
} | |
void initLookup(void) { | |
g_lookup = calloc(TABLE_SIZE, sizeof(*g_lookup)); | |
for (uint16_t i = 0; i < 256; i++) { | |
size_t len, idx; | |
char buf[24]; | |
len = snprintf(buf, sizeof(buf), "%u", i); | |
idx = calcIndexValue(buf, len); | |
g_lookup[idx] = i; | |
} | |
} | |
void getIndexes(void) { | |
for (uint16_t i = 0; i < 256; i++) { | |
size_t len, idx; | |
char buf[24]; | |
len = snprintf(buf, sizeof(buf), "%u", i); | |
idx = calcIndexValue(buf, len); | |
printf("%zu : %s\n", idx, buf); | |
} | |
} | |
static void validateConversion(size_t n, char *num, size_t len) { | |
char tmp[24] = {0}; | |
uint8_t chk_val, alg_val; | |
memcpy(tmp, num, len); | |
alg_val = parse_u8(num, len); | |
chk_val = atoi(tmp); | |
if (alg_val != chk_val) { | |
fprintf(stderr, "Error: [%zu] %u doesn't match expected %u for input '%s'\n", | |
n, alg_val, chk_val, tmp); | |
exit(1); | |
} | |
// fprintf(stderr, "OK: '%s' -> %u\n", tmp, alg_val); | |
} | |
int main(int argc, const char **argv) { | |
size_t count, total = 0; | |
struct numbers nums; | |
uint8_t val; | |
char *ptr; | |
initLookup(); | |
srand(ustime()); | |
setlocale(LC_NUMERIC, ""); | |
count = argc > 1 ? atoi(argv[1]) : 10000000; | |
if (count < 1) { | |
fprintf(stderr, "Error: Must generate at least one number\n"); | |
exit(1); | |
} | |
printf("Generating %zu random uint8_t values...", count); | |
nums = createNumbers(count); | |
printf("done\n"); | |
ptr = nums.ptr; | |
long long t1 = ustime(); | |
for (size_t i = 0; i < count; i++) { | |
val = parse_u8(ptr, nums.dgt[i]); | |
#if VERIFY | |
validateConversion(i, ptr, nums.dgt[i]); | |
#endif | |
total += val; | |
ptr += nums.dgt[i]; | |
} | |
long long t2 = ustime(); | |
// So we don't optimize away the conversions | |
printf("Total value of all numbers: %zu\n", total); | |
printTiming("CALLS", count, t2 - t1); | |
freeNumbers(&nums); | |
free(g_lookup); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment