From 1c75c9d7c537e8ed9a83e2613759b98476d2c1de Mon Sep 17 00:00:00 2001 From: "H. Utku Maden" Date: Sun, 20 Aug 2023 18:08:14 +0300 Subject: [PATCH] Initial commit. --- .gitignore | 2 + README.md | 3 + include/mx/assert.h | 12 +++ include/mx/base.h | 12 +++ include/mx/digest.h | 40 ++++++++++ include/mx/options.h | 169 ++++++++++++++++++++++++++++++++++++++++ src/digest.c | 57 ++++++++++++++ src/options.c | 178 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 473 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 include/mx/assert.h create mode 100644 include/mx/base.h create mode 100644 include/mx/digest.h create mode 100644 include/mx/options.h create mode 100644 src/digest.c create mode 100644 src/options.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7eb3e54 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/.vs* +**/.atom diff --git a/README.md b/README.md new file mode 100644 index 0000000..371eb92 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +libmx +====== +libmx is a collection of libraries for my personal use in my C projects. diff --git a/include/mx/assert.h b/include/mx/assert.h new file mode 100644 index 0000000..64cb100 --- /dev/null +++ b/include/mx/assert.h @@ -0,0 +1,12 @@ +#ifndef _MX_ASSERT_H_ +#define _MX_ASSERT_H_ + +#include +#include + +#define MX_ASSERT(cond, msg) assert((cond) && ""msg) +#define MX_ASSERT_PTR(ptr, message) assert((ptr) && ""message) +#define MX_ASSERT_SELFPTR(ptr) MX_ASSERT_PTR(ptr, "Instance pointer "#ptr" must be valid.") +#define MX_ASSERT_OOM(ptr) MX_ASSERT_PTR(ptr, "Out of memory.") + +#endif diff --git a/include/mx/base.h b/include/mx/base.h new file mode 100644 index 0000000..a4c12f3 --- /dev/null +++ b/include/mx/base.h @@ -0,0 +1,12 @@ +#ifndef _MX_BASE_H_ +#define _MX_BASE_H_ + +#include +#include +#include + +#define MX_API +#define MX_IMPL +#define MX_INLINE inline static + +#endif \ No newline at end of file diff --git a/include/mx/digest.h b/include/mx/digest.h new file mode 100644 index 0000000..985e534 --- /dev/null +++ b/include/mx/digest.h @@ -0,0 +1,40 @@ +#ifndef _MX_DIGEST_H_ +#define _MX_DIGEST_H_ + +#include + +/** + * @brief Common function pointer function for all mx digest functions. + * @param[out] digest The digest result. + * @param[in] src The source buffer. + * @param[in] length The length of the buffer. + */ +typedef void (*mx_digest_function)(void *digest, const char *src, size_t length); + +typedef uint32_t adler32_t; /**< The adler32 hash.*/ +typedef uint32_t fvn0_t; /**< The FVN-0 hash. */ +typedef uint32_t fvn1_t; /**< The FVN-1 hash. */ +typedef uint32_t fvn1a_t; /**< The FVN-1a hash. */ + +// typedef uint16_t crc16_t; /**< CRC16-CCIT checksum. */ +// typedef uint32_t crc32_t; /**< CRC32-IEEE checksum. */ +// typedef uint8_t md5sum_t[8]; /**< MD5SUM checksum. */ +// typedef uint8_t sha0_t [8]; /**< SHA0 checksum. */ +// typedef uint8_t sha128_t[8]; /**< SHA128 checksum. */ +// typedef uint8_t sha256_t[8]; /**< SHA256 checksum. */ + +MX_API void mx_adler32(adler32_t *digest, const char *src, size_t length); +MX_API void mx_fvn0 (fvn0_t *digest, const char *src, size_t length); +MX_API void mx_fvn1 (fvn1_t *digest, const char *src, size_t length); +MX_API void mx_fvn1a (fvn1a_t *digest, const char *src, size_t length); + +// MX_API void mx_crc16 (crc16_t *digest, const char *src, size_t length); +// MX_API void mx_crc32 (crc32_t *digest, const char *src, size_t length); +// MX_API void mx_md5sum (md5sum_t *digest, const char *src, size_t length); +// MX_API void mx_sha0 (sha0_t *digest, const char *src, size_t length); +// MX_API void mx_sha128 (sha128_t *digest, const char *src, size_t length); +// MX_API void mx_sha256 (sha256_t *digest, const char *src, size_t length); + +MX_API int mx_compare_digest(const void* a, const void *b, size_t size); + +#endif \ No newline at end of file diff --git a/include/mx/options.h b/include/mx/options.h new file mode 100644 index 0000000..eeca748 --- /dev/null +++ b/include/mx/options.h @@ -0,0 +1,169 @@ +#ifndef _MX_GETOPTS_H_ +#define _MX_GETOPTS_H_ + +/** + * @file options.h MX Library Options Parser + * This is a very basic options parser built into my applications. It does what + * getopts.h does on GNU except it's a whole lot less bloated. Simple is the + * best. + * + * It works in two modes, pointer to structure mode and static mode. Static mode + * works like the old unsafe, not thread safe, not reentrant strtok and familiar + * static functions. Pointer mode allows you to pass the structure itself. + * Pointer mode functions are postfixed with `_r`. + * + * @example mx_options_static Static Mode Usage. + * @code{c} + * #include + * #include + * + * int main(int argc, char** argv) + * { + * mx_optkind_t kind; + * mx_options_begin(MX_OPT_DEFAULT, argc, argv); + * while (kind = mx_options_next()) + * { + * switch (kind) + * { + * case MX_OPTION_PAIR: + * printf("key:%s value:%s\n", mx_option_key, mx_option_value); + * break; + * default: + * printf("key:%s\n", mx_option_key); + * break; + * } + * } + * } + * @endcode + * + * @example mx_options_pointer Pointer Mode Usage + * @code{c} + * #include + * #include + * + * int main(int argc, char** argv) + * { + * mx_options_t opt; + * mx_optkind_t kind; + * mx_options_begin_r(&opt, MX_OPT_DEFAULT, argc, argv); + * while (kind = mx_options_next_r(&opt)) + * { + * switch (kind) + * { + * case MX_OPTION_PAIR: + * printf("key:%s value:%s\n", opt.key, opt.value); + * break; + * default: + * printf("key:%s\n", opt.key); + * break; + * } + * } + * + * mx_options_end_r(&opt); // Free any resources allocated. + * } + * @endcode + */ + +#include + +/** + * Option kind. + */ +typedef enum mx_optkind_t +{ + MX_OPT_END, /**< End iterator. */ + MX_OPT_POSITIONAL, /**< Positional. */ + MX_OPT_SHORT, /**< (UNIX ONLY) Short flag(s). */ + MX_OPT_LONG, /**< Long flag. */ + MX_OPT_PAIR, /**< Key with value.*/ + MX_OPT_DASH, /**< (UNIX ONLY) A single dash. */ + MX_OPT_DDASH, /**< (UNIX ONLY) A double dash. */ +} mx_optkind_t; + +/** + * Option parse flags. + */ +typedef enum mx_optflag_t +{ + MX_OPT_UNIX = 1 << 0, /**< Parse UNIX style options. [-h, --help, --include=a]*/ + MX_OPT_DOS = 1 << 1, /**< Parse DOS style options. [/?, /help, /include:a] */ + /** Default option style is both. */ + MX_OPT_DEFAULT = MX_OPT_UNIX | MX_OPT_DOS, +} mx_optflag_t; + +/** + * Option parse storage struct. + */ +typedef struct mx_options_t +{ + int argc; /**< Number of arguments. */ + const char **argv; /**< Array of arguments. */ + mx_optflag_t flags; /**< Parse flags. */ + int i; /**< Internal iterator. */ + mx_optkind_t kind; /**< Last option kind. */ + const char *key; /**< Last key. */ + const char *value; /**< Last value. */ +} mx_options_t; + +/** + * Begin parsing options with structure. + * @param[in] self Storage. + * @param[in] flags Parse flags. + * @param[in] argc Number of arguments. + * @param[in] argv Array of arguments. + */ +MX_API void mx_options_begin_r(mx_options_t *self, mx_optflag_t flags, int argc, const char **argv); + +/** + * Next token. + * @param[in] self Storage. + * @returns Option kind. + */ +MX_API mx_optkind_t mx_options_next_r(mx_options_t *self); + +/** + * Stop using the parser. + * @param[in] self Storage. + */ +MX_API void mx_options_end_r(mx_options_t *self); + +/** + * Internal function that returns a pointer to a static options struct. + */ +MX_INLINE mx_options_t* __mx_options_ptr() +{ + static mx_options_t opts = { }; + return &opts; +} + +/** + * Begin parsing options. + * @param[in] flags Parse flags. + * @param[in] argc Number of arguments. + * @param[in] argv Array of arguments. +*/ +MX_INLINE void mx_options_begin(mx_optflag_t flags, int argc, char **argv) +{ + mx_options_begin_r(__mx_options_ptr(), flags, argc, argv); +} + +/** + * Next token. + * @returns Option kind. + */ +MX_INLINE mx_optkind_t mx_options_next() +{ + return mx_options_next_r(__mx_options_ptr()); +} + +/** + * Current option value. + */ +#define mx_option_key (__mx_options_ptr()->key) + +/** + * Current option key. + */ +#define mx_option_value (__mx_options_ptr()->value) + +#endif \ No newline at end of file diff --git a/src/digest.c b/src/digest.c new file mode 100644 index 0000000..b853fa1 --- /dev/null +++ b/src/digest.c @@ -0,0 +1,57 @@ +#include +#include + +const uint32_t FVN_BASIS = 0x811c9dc5; +const uint32_t FVN_PRIME = 0x01000193; + +MX_IMPL int mx_compare_digest(const void* a, const void *b, size_t size) +{ + return memcmp(a, b, size); +} + +MX_IMPL void mx_adler32(adler32_t *digest, const char *src, size_t length) +{ + const uint32_t modulo = 65521; + uint32_t a = 1, b = 0; + + while (length--) + { + a = (a + *(src++)) % modulo; + b = (a + b) % modulo; + } + + return (b << 16) | (a << 0); +} + +MX_IMPL void mx_fvn0(fvn0_t *digest, const char *src, size_t length) +{ + *digest = 0; + + for (size_t i = 0; i < length; i++) + { + *digest *= FVN_PRIME; + *digest ^= (fvn0_t)src[i]; + } +} + +MX_IMPL void mx_fvn1(fvn1_t *digest, const char *src, size_t length) +{ + *digest = FVN_BASIS; + + for (size_t i = 0; i < length; i++) + { + *digest *= FVN_PRIME; + *digest ^= src[i]; + } +} + +MX_IMPL void mx_fvn1a(fvn1a_t *digest, const char *src, size_t length) +{ + *digest = FVN_BASIS; + + for (size_t i = 0; i < length; i++) + { + *digest ^= src[i]; + *digest *= FVN_PRIME; + } +} \ No newline at end of file diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..95fdad4 --- /dev/null +++ b/src/options.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include + +MX_INLINE char* mx_strdup(const char **pptr, char *nstr, size_t len) +{ + if (len == 0) + len = strlen(nstr); + + char *ptr = realloc(*pptr, len + 1); + MX_ASSERT_OOM(ptr); + + memcpy(ptr, nstr, len); + ptr[len] = '\0'; + + return (*pptr = ptr); +} + +MX_IMPL void mx_options_begin_r(mx_options_t *self, mx_optflag_t flags, int argc, const char **argv) +{ + MX_ASSERT_SELFPTR(self); + MX_ASSERT_PTR(argv, "Argument array must be non-null."); + MX_ASSERT(argc > 0, "Arguments count must be greater than zero."); + + self->flags = flags; + self->argc = argv; + self->argc = argc; + self->i = 0; + self->kind = MX_OPT_END; + self->key = NULL; + self->value = NULL; +} + +MX_IMPL mx_optkind_t mx_options_next_r(mx_options_t *self) +{ + MX_ASSERT_SELFPTR(self); + + if (self->i >= self->argc) + { + return (self->kind = MX_OPT_END); + } + + char *current = self->argv[self->i]; + char *next = (self->i + 1 < self->argc) ? self->argv[self->i + 1] : NULL; + size_t clen = strlen(current); + + if ((self->flags & MX_OPT_UNIX) && current[0] == '-') + { + /* - indicates a UNIX flag sequence. */ + + if (current[1] == '-') + { + if (current[2] == '\0') + { + /* -- */ + self->i++; + return (self->kind = MX_OPT_DDASH); + } + else + { + /* --long-flag or --key=pair*/ + + char *equals = strchr(current, '='); + + if (equals) + { + /* The equals is in this token. */ + size_t len = (size_t)(equals - ¤t[2]); + mx_strdup(&self->key, ¤t[2], len); + + if (equals[1] == '\0') + { + /* The value is in the next token. */ + next = next ? next : ""; + mx_strdup(&self->value, next, 0); + + self->i++; + } + else + { + /* The value is also in this token.*/ + mx_strdup(&self->value, &equals[1], 0); + } + + self->i++; + return (self->kind = MX_OPT_PAIR); + } + else if (next && next[0] == '=') + { + /* The equals is in the next token. */ + mx_strdup(&self->key, ¤t[2], clen-2); + mx_strdup(&self->value, &next[1], 0); + + self->i += 2; + return (self->kind = MX_OPT_PAIR); + } + + /* Don't treat this as a key pair. It's a long flag.*/ + mx_strdup(&self->key, current, clen-2); + self->i++; + return (self->kind = MX_OPT_LONG); + } + } + else if (current[1] == '\0') + { + self->i++; + return (self->kind = MX_OPT_DASH); + } + else + { + mx_strdup(&self->key,¤t[1], clen - 1); + self->i++; + return (self->kind = MX_OPT_SHORT); + } + } + + if ((self->flags & MX_OPT_DOS) && current[0] == '/') + { + /* / indicated a DOS flag sequence. */ + + char *colon = strchr(current, ':'); + + if (colon) + { + /* The colon is in this token. */ + if (colon[1] == '\0') + { + /* The value is in the next token. */ + next = next ? next : ""; + mx_strdup(&self->value, next, 0); + self->i++; + } + else + { + /* The value is in this token. */ + mx_strdup(&self->value, &colon[1], 0); + } + + self->i++; + mx_strdup(&self->key, ¤t[1], (size_t)(colon-¤t[1])); + return (self->kind = MX_OPT_PAIR); + } + else if (next && next[0]==':') + { + /* The colon is in the next token. */ + mx_strdup(&self->key, ¤t[1], clen-1); + mx_strdup(&self->value, &next[1], 0); + self->i+=2; + return (self->kind = MX_OPT_PAIR); + } + else + { + self->i++; + mx_strdup(&self->key, ¤t[1], clen - 1); + return (self->kind = MX_OPT_LONG); + } + } + + /* Otherwise treate as a positional argument. */ + { + char *ptr = realloc(self->value, clen); + MX_ASSERT_OOM(ptr); + + self->i++; + self->value = memcpy(ptr, current, clen); + return (self->kind = MX_OPT_POSITIONAL); + } +} + +MX_IMPL void mx_options_end_r(mx_options_t *self) +{ + if (self) + { + free(self->key); + free(self->value); + } +} \ No newline at end of file