From 0e511a94c303fd31eb2e711de9a09caaafa7cde8 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Mon, 21 Jan 2019 19:16:20 +0100 Subject: [PATCH] Now it's a shared library. --- .gitignore | 3 +- Makefile | 23 ++- example.c | 118 +++++++++++++ main.c => lib/sfuid.c | 397 +++++++++++++++++++----------------------- lib/sfuid.h | 28 +++ 5 files changed, 342 insertions(+), 227 deletions(-) create mode 100644 example.c rename main.c => lib/sfuid.c (50%) create mode 100644 lib/sfuid.h diff --git a/.gitignore b/.gitignore index 2f07c75..41194e1 100644 --- a/.gitignore +++ b/.gitignore @@ -52,5 +52,4 @@ Mkfile.old dkms.conf # Executables -F6jigYck4v -benchmark +example diff --git a/Makefile b/Makefile index 1f29800..3307a58 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,22 @@ -all: F6jigYck4v +all: libsfuid.so example -benchmark: main.c - gcc -lm -DBENCHMARK -o $@ $< +libsfuid.so: lib/sfuid.o + @echo Linking shared object... + @gcc -shared -o $@ $^ -F6jigYck4v: main.c - gcc -lm -o $@ $< +example: example.o libsfuid.so + @echo Linking example program... + @gcc -L. -lsfuid -lm -o $@ $< + +example.o: lib/sfuid.h +lib/sfuid.o: lib/sfuid.h + +%.o: %.c + @echo Compiling $<... + @gcc -Wall -fPIC -c $< -o $@ clean: - rm F6jigYck4v + @rm *.o + @rm lib/*.o + @rm *.so example diff --git a/example.c b/example.c new file mode 100644 index 0000000..84afd3a --- /dev/null +++ b/example.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/sfuid.h" + +#define error(...) fprintf(stderr, __VA_ARGS__) + +void help(const char* name) { + printf("SYNOPSIS: %s [OPTIONS] INPUT\n\n", name); + printf("options: \n"); + printf(" -e encode INPUT (default)\n"); + printf(" -d decode INPUT\n"); + printf(" -l LENGTH set the length (default: 10)\n"); + printf(" -c CHARSET set the charset (default: a-zA-Z0-9)\n"); + printf(" -o OFFSET set the offset (default: 5)\n"); + printf(" -h show this help message\n\n"); +} + +int main(int argc, char** argv) { + bool decodeMode = false; + + int opt; + char* endptr; + + sfuid_settings_t settings = sfuid_default_settings; + + while((opt = getopt(argc, argv, "o:c:l:dehy")) != -1) { + switch(opt) { + case 'o': + settings.offset = strtol(optarg, &endptr, 10); + if (*endptr != 0) { + error("Argument for -o has to be a number.\nTry -h to get help.\n"); + exit(2); + } + break; + case 'l': + settings.length = strtol(optarg, &endptr, 10); + if (*endptr != 0) { + error("Argument of -l has to be a number.\nTry -h to get help.\n"); + exit(2); + } + break; + case 'c': + settings.charset = optarg; + break; + case 'e': + decodeMode = false; + break; + case 'd': + decodeMode = true; + break; + case 'h': + help(argv[0]); + exit(0); + case 'y': + settings.charset = "0-9A-Za-z-_"; + break; + default: + error("Unknown option.\nTry -h to get help.\n"); + exit(2); + } + } + + if (optind >= argc) { + error("Expected data after options.\nTry -h to get help.\n"); + exit(2); + } + + int tmp; + + tmp = sfuid_init(settings); + if (tmp != SFUID_ERROR_NONE) { + error("Error: %s\n", sfuid_error(tmp)); + exit(4); + } + + if (decodeMode) { + const char* string = argv[optind]; + + uint64_t value; + + tmp = sfuid_decode(string, &value); + if (tmp != SFUID_ERROR_NONE) { + error("Error: %s\n", sfuid_error(tmp)); + exit(4); + } + + printf("%" PRIu64 "\n", value); + } else { + uint64_t value = strtoull(argv[optind], &endptr, 10); + if (*endptr != 0) { + error("Value has to be a number for encoding.\n"); + exit(2); + } + if (value == ULLONG_MAX && errno == ERANGE) { + error("Value overflows the internal datatype.\n"); + exit(2); + } + + char* string = malloc(settings.length); + + tmp = sfuid_encode(value, string); + + if (tmp != SFUID_ERROR_NONE) { + error("Error: %s\n", sfuid_error(tmp)); + exit(4); + } + + printf("%s\n", string); + free(string); + } +} diff --git a/main.c b/lib/sfuid.c similarity index 50% rename from main.c rename to lib/sfuid.c index 587ca4f..11c0dd7 100644 --- a/main.c +++ b/lib/sfuid.c @@ -4,34 +4,50 @@ #include #include #include -#include #include -#include -#include + +#include "sfuid.h" + +const sfuid_settings_t sfuid_default_settings = { + .length = 10, + .charset = "a-zA-Z0-9", + .offset = 5 +}; + +sfuid_settings_t settings; +bool settingsValid = false; + +struct { + uint64_t max; + uint64_t prime; + char* charset; + int inverseCharset[(1 << 7)]; +} properties = { + .charset = NULL, + .inverseCharset = {-1} +}; + typedef __uint128_t uint128_t; typedef __int128_t int128_t; -const char* charset = "a-zA-Z0-9"; -int inverseCharset[(1 << 7)] = {-1}; -unsigned int resultLength = 10; -unsigned int offset = 5; -bool verbose = false; - -#define verbose(...) if (verbose) printf(__VA_ARGS__); -#define error(...) fprintf(stderr, __VA_ARGS__) +#ifdef DEBUG + #define verbose(...) printf(__VA_ARGS__); +#else + #define verbose(...) +#endif int convertToString(char* string, uint64_t number) { - int charsetLength = strlen(charset); + int charsetLength = strlen(properties.charset); int length = 0; do { - string[length++] = charset[number % charsetLength]; + string[length++] = properties.charset[number % charsetLength]; number /= charsetLength; } while(number > 0); - for(; length < resultLength; length++) { - string[length] = charset[0]; + for(; length < settings.length; length++) { + string[length] = properties.charset[0]; } for(int i = 0; i < length / 2; i++) { @@ -42,27 +58,25 @@ int convertToString(char* string, uint64_t number) { string[length] = '\0'; - return length; + return SFUID_ERROR_NONE; } -uint64_t convertFromString(const char* string) { - uint64_t result = 0; - +int convertFromString(const char* string, uint64_t* result) { int stringLength = strlen(string); - int charsetLength = strlen(charset); + int charsetLength = strlen(properties.charset); + + *result = 0; for (int i = 0; i < stringLength; i++) { - bool found = false; - int tmp = inverseCharset[string[i]]; + int tmp = properties.inverseCharset[(int) string[i]]; if (tmp < 0) { - error("Parsing error.\nUnexpected character '%c'.\n", string[i]); - exit(3); + return SFUID_ERROR_PARSING; } uint64_t val = ((uint64_t) pow(charsetLength, stringLength - i - 1)); - result += ((uint64_t) tmp) * val; + *result += ((uint64_t) tmp) * val; } - return result; + return SFUID_ERROR_NONE; } uint64_t primelist[] = { @@ -137,7 +151,7 @@ uint64_t multiplicativeInverse(uint64_t prime, uint64_t maxValue) { return x1; } -uint64_t decode(uint64_t value, uint64_t prime, uint64_t maxValue) { +int decode(uint64_t* value, uint64_t prime, uint64_t maxValue) { /* * (x * p) mod m = v * (x * p) == v (mod m) @@ -146,7 +160,7 @@ uint64_t decode(uint64_t value, uint64_t prime, uint64_t maxValue) { * => p * i == i (mod m) */ - uint128_t tmp = value; + uint128_t tmp = *value; uint64_t inverse = multiplicativeInverse(prime, maxValue); @@ -154,96 +168,9 @@ uint64_t decode(uint64_t value, uint64_t prime, uint64_t maxValue) { tmp *= inverse; tmp &= (maxValue); + *value = tmp; - return (uint64_t) tmp; -} - -typedef struct settings { - uint64_t max; - uint64_t prime; -} settings_t; - -settings_t getSettings() { - verbose("charset length: %d\n", strlen(charset)); - double maxValueDouble = pow(strlen(charset), resultLength); - uint64_t maxValue; - int maxBits; - - if (maxValueDouble >= pow(2, 65)) { - verbose("Max value %.0lf > 2^64, caping to 64 bits.\n", maxValueDouble); - maxBits = 64; - } else { - maxValue = (uint64_t) maxValueDouble; - verbose("theoer max: %llu\n", maxValue); - maxBits = findMax(maxValue); - - if (maxBits <= 0) { - error("Length or charset is too big. This should not have happend.\n"); - exit(3); - } - } - - if (maxBits == 64) { - /* - * we want to avoid undefined behaviour. - * so instead of using an underflow we'll - * make this one explicit. - */ - maxValue = UINT64_MAX; - - } else { - maxValue = 1ll << maxBits; - maxValue--; - } - - verbose("used bits: %d\n", maxBits); - verbose("max: %llu\n", maxValue); - verbose("usage: %.1f%\n", (100.0 * maxValue / maxValueDouble)); - - uint64_t prime = findPrime(maxBits); - - verbose("prime: %llu\n", prime); - - return (settings_t) {.max = maxValue, .prime = prime}; -} - -void toString(char* string, uint64_t value, settings_t settings) { - #ifdef BENCHMARK - struct timespec before, after; - clock_gettime(CLOCK_MONOTONIC, &before); - #endif - - value = encode(value, settings.prime, settings.max); - verbose("encoded: %llu\n", value); - convertToString(string, value); - - #ifdef BENCHMARK - clock_gettime(CLOCK_MONOTONIC, &after); - - uint64_t diff = (int64_t)(after.tv_sec - before.tv_sec) * (int64_t) 1000000000UL + (int64_t)(after.tv_nsec - before.tv_nsec); - fprintf(stderr, "Time for conversion: %llu ns\n", diff); - #endif -} - -uint64_t fromString(const char* string, settings_t settings) { - #ifdef BENCHMARK - struct timespec before, after; - clock_gettime(CLOCK_MONOTONIC, &before); - #endif - - uint64_t result = convertFromString(string); - verbose("encoded: %llu\n", result); - result = decode(result, settings.prime, settings.max); - - #ifdef BENCHMARK - clock_gettime(CLOCK_MONOTONIC, &after); - - uint64_t diff = (int64_t)(after.tv_sec - before.tv_sec) * (int64_t) 1000000000UL + (int64_t)(after.tv_nsec - before.tv_nsec); - fprintf(stderr, "Time for conversion: %llu ns\n", diff); - #endif - - - return result; + return SFUID_ERROR_NONE; } struct { @@ -264,14 +191,15 @@ struct { } }; + void prepareCharset() { verbose("prepareing charset\n"); - int size = strlen(charset); + int size = strlen(settings.charset); const char* tmp; for(int i = 0; i < ((sizeof shortcuts) / (sizeof shortcuts[0])); i++) { - tmp = charset; + tmp = settings.charset; while((tmp = strstr(tmp, shortcuts[i].needle)) != NULL) { tmp++; size += strlen(shortcuts[i].replacement) - strlen(shortcuts[i].needle); @@ -279,7 +207,7 @@ void prepareCharset() { } char* inflatedCharset = malloc(size + 1); - strcpy(inflatedCharset, charset); + strcpy(inflatedCharset, settings.charset); while(true) { char* first = NULL; @@ -303,114 +231,145 @@ void prepareCharset() { //verbose("full charset: %s\n", inflatedCharset); - charset = inflatedCharset; + if (properties.charset != NULL) + free(properties.charset); + + properties.charset = inflatedCharset; } void prepareInverseCharset() { - for(int i = 0; i < strlen(charset); i++) { - inverseCharset[charset[i]] = i; + for(int i = 0; i < 127; i++) { + properties.inverseCharset[i] = -1; + } + for(int i = 0; i < strlen(properties.charset); i++) { + properties.inverseCharset[(int) properties.charset[i]] = i; } } -void help(const char* name) { - printf("SYNOPSIS: %s [OPTIONS] INPUT\n\n", name); - printf("options: \n"); - printf(" -e encode INPUT (default)\n"); - printf(" -d decode INPUT\n"); - printf(" -l LENGTH set the length (default: 10)\n"); - printf(" -c CHARSET set the charset (default: a-zA-Z0-9)\n"); - printf(" -o OFFSET set the offset (default: 5)\n"); - printf(" -v enable verbose mode\n"); - printf(" -h show this help message\n\n"); -} - -int main(int argc, char** argv) { - bool decodeMode = false; - - int opt; - char* endptr; - - while((opt = getopt(argc, argv, "o:c:l:devhy")) != -1) { - switch(opt) { - case 'o': - offset = strtol(optarg, &endptr, 10); - if (*endptr != 0) { - error("Argument for -o has to be a number.\nTry -h to get help.\n"); - exit(2); - } - break; - case 'l': - resultLength = strtol(optarg, &endptr, 10); - if (*endptr != 0) { - error("Argument of -l has to be a number.\nTry -h to get help.\n"); - exit(2); - } - break; - case 'c': - charset = optarg; - break; - case 'e': - decodeMode = false; - break; - case 'd': - decodeMode = true; - break; - case 'v': - verbose = true; - break; - case 'h': - help(argv[0]); - exit(0); - case 'y': - charset = "0-9A-Za-z-_"; - break; - default: - error("Unknown option.\nTry -h to get help.\n"); - exit(2); - } - } - - if (optind >= argc) { - error("Expected data after options.\nTry -h to get help.\n"); - exit(2); - } - - verbose("charset: %s\n", charset); - verbose("length: %d\n", resultLength); - verbose("offset: %d\n", offset); - +int setProperties() { prepareCharset(); + prepareInverseCharset(); - settings_t settings = getSettings(); + verbose("charset length: %d\n", strlen(properties.charset)); + double maxValueDouble = pow(strlen(properties.charset), settings.length); + uint64_t maxValue; + int maxBits; - if (decodeMode) { - prepareInverseCharset(); - verbose("decode mode\n"); - const char* string = argv[optind]; - verbose("string: %s\n", string); - uint64_t value = fromString(string, settings); - value -= offset; - printf("%llu\n", value); + if (maxValueDouble >= pow(2, 65)) { + verbose("Max value %.0lf > 2^64, caping to 64 bits.\n", maxValueDouble); + maxBits = 64; } else { - uint64_t value = strtoull(argv[optind], &endptr, 10); - if (*endptr != 0) { - error("Value has to be a number for encoding.\n"); - exit(2); - } - if (value == ULLONG_MAX && errno == ERANGE) { - error("Value overflows the internal datatype.\n"); - exit(2); - } - value += offset; - if (value > settings.max) { - error("value is too big!\nThe maximum value for the current settings is %llu.\n", settings.max - offset); - exit(1); - } - verbose("encode mode\n"); - verbose("value: %llu\n", value) - char* string = malloc(resultLength); - toString(string, value, settings); - printf("%s\n", string); - free(string); + maxValue = (uint64_t) maxValueDouble; + verbose("theoer max: %llu\n", maxValue); + maxBits = findMax(maxValue); } + + if (maxBits == 64) { + /* + * we want to avoid undefined behaviour. + * so instead of using an underflow we'll + * make this one explicit. + */ + maxValue = UINT64_MAX; + + } else { + maxValue = 1ll << maxBits; + maxValue--; + } + + verbose("used bits: %d\n", maxBits); + verbose("max: %llu\n", maxValue); + verbose("usage: %.1f%\n", (100.0 * maxValue / maxValueDouble)); + + uint64_t prime = findPrime(maxBits); + + verbose("prime: %llu\n", prime); + + properties.max = maxValue; + properties.prime = prime; + + return SFUID_ERROR_NONE; +} + +int checkCharset() { + int tmp = strlen(properties.charset); + if (tmp <= 0) + return SFUID_ERROR_CHARSET; + for(int i = 0; i < tmp; i++) { + if (properties.charset[i] < 0 || properties.charset[i] > 127) + return SFUID_ERROR_CHARSET; + for(int j = i+1; j < tmp; j++) { + if (properties.charset[i] == properties.charset[j]) + return SFUID_ERROR_CHARSET; + } + } + return SFUID_ERROR_NONE; +} + +int sfuid_init(sfuid_settings_t _settings) { + settings = _settings; + settingsValid = false; + + if (settings.length == 0) { + return SFUID_ERROR_LENGTH; + } + + int tmp = setProperties(); + if (tmp != SFUID_ERROR_NONE) + return tmp; + + tmp = checkCharset(); + if (tmp != SFUID_ERROR_NONE) + return tmp; + + settingsValid = true; + + return SFUID_ERROR_NONE; +} + +int sfuid_encode(uint64_t value, char* string) { + if (!settingsValid) + return SFUID_ERROR_SETTINGS_INVALID; + + value += settings.offset; + + if (value > properties.max) + return SFUID_ERROR_VALUE; + + value = encode(value, properties.prime, properties.max); + int tmp = convertToString(string, value); + if (tmp != SFUID_ERROR_NONE) + return tmp; + + return SFUID_ERROR_NONE; +} + +int sfuid_decode(const char* string, uint64_t* value) { + int tmp = convertFromString(string, value); + if (tmp != SFUID_ERROR_NONE) + return tmp; + + tmp = decode(value, properties.prime, properties.max); + if (tmp != SFUID_ERROR_NONE) + return tmp; + + *value -= settings.offset; + + return SFUID_ERROR_NONE; +} + +const char* errors[] = { + "No error.", + "Length parameter not valid.", + "Charset parameter not valid.", + "Error while parsing input.", + "Settings not valid.", + "Value not valid." +}; + +const char* sfuid_error(int error) { + if (error < 0 || error >= ((sizeof errors) / sizeof errors[0])) { + return "Unknown error."; + } + return errors[error]; } diff --git a/lib/sfuid.h b/lib/sfuid.h new file mode 100644 index 0000000..832c157 --- /dev/null +++ b/lib/sfuid.h @@ -0,0 +1,28 @@ +#ifndef SFUID_H +#define SFUID_H + +#include + +#define SFUID_ERROR_NONE 0 +#define SFUID_ERROR_LENGTH 1 +#define SFUID_ERROR_CHARSET 2 +#define SFUID_ERROR_PARSING 3 +#define SFUID_ERROR_SETTINGS_INVALID 4 +#define SFUID_ERROR_VALUE 5 + + +typedef struct { + unsigned int length; + const char* charset; + uint64_t offset; +} sfuid_settings_t; + +extern const sfuid_settings_t sfuid_default_settings; + +int sfuid_init(sfuid_settings_t settings); +int sfuid_encode(uint64_t value, char* string); +int sfuid_decode(const char* string, uint64_t* value); + +const char* sfuid_error(int error); + +#endif