From bb5cce33f0a425aae4e2105b2c6b33b8d58e0bfa Mon Sep 17 00:00:00 2001 From: snarmph Date: Tue, 5 Oct 2021 17:57:27 +0200 Subject: [PATCH] added: * coroutines * string modified: * http: fixed a couple of bugs * strutils: renamed to os, it is now a header for some generic platform-indipendent code * tracelog: added option to not print newline at the end of a message + bug fixes --- CMakeLists.txt | 4 +- coroutine.c | 11 ++ coroutine.h | 127 +++++++++++++++++++++ http.c | 4 +- http.h | 6 +- strutils.c => os.c | 30 +---- strutils.h => os.h | 14 +-- socket.h | 10 +- str.c | 267 +++++++++++++++++++++++++++++++++++++++++++++ str.h | 61 +++++++++++ strstream.c | 8 +- strstream.h | 2 +- strview.c | 2 +- tracelog.c | 50 +++++---- tracelog.h | 3 + 15 files changed, 523 insertions(+), 76 deletions(-) create mode 100644 coroutine.c create mode 100644 coroutine.h rename strutils.c => os.c (77%) rename strutils.h => os.h (63%) create mode 100644 str.c create mode 100644 str.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8455f56..993c94a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,9 @@ add_library(Colla STATIC http.c strstream.c strview.c - strutils.c + str.c + coroutine.c + os.c ) IF (WIN32) diff --git a/coroutine.c b/coroutine.c new file mode 100644 index 0000000..26d4d82 --- /dev/null +++ b/coroutine.c @@ -0,0 +1,11 @@ +#include "coroutine.h" + +coroutine_t coInit() { + return (coroutine_t) { + .state = 0 + }; +} + +bool coIsDead(coroutine_t co) { + return co.state == -1; +} \ No newline at end of file diff --git a/coroutine.h b/coroutine.h new file mode 100644 index 0000000..a3172b8 --- /dev/null +++ b/coroutine.h @@ -0,0 +1,127 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include // bool +#include // memset + +// heavily inspired by https://gist.github.com/Enichan/5f01140530ff0133fde19c9549a2a973 + +#if 0 +// Usage example: +typedef struct { + int result; + coroutine_t co; +} co_int_t; + +bool coVoid(co_void_t *co) { + costate(co_nostate); + coroutine({ + printf("hello"); yield(); + printf("world"); yield(); + printf("how"); yield(); + printf("is"); yield(); + printf("it"); yield(); + printf("going?\n"); + }); +} + +bool countToTen(co_int_t *co) { + costate(int i; int ii;); + coroutine({ + for(self.i = 0; self.i < 10; ++self.i) { + self.ii += self.i; + yieldVal(self.ii); + } + }); +} + +int main() { + co_void_t covoid = {0}; + while(coVoid(&covoid)) { + printf(" "); + } + + co_int_t coint; + coint.co = coInit(); + while(countToTen(&coint)) { + printf("%d ", coint.result); + } + printf("\n"); + // reset coroutine for next call + coint.co = coInit(); + while(countToTen(&coint)) { + printf("%d ", coint.result); + } + printf("\n"); +} +#endif + +typedef struct { + int state; +} coroutine_t; + +typedef struct { + coroutine_t co; +} co_void_t; + +coroutine_t coInit(); +bool coIsDead(coroutine_t co); + +#define COVAR co +#define COSELF self + +#define costate(...) \ + typedef struct { bool init; __VA_ARGS__ } COSTATE; \ + static COSTATE self = {0} + +#define co_nostate { char __dummy__; } + +#define yieldBreak() \ + COVAR->co.state = -1; \ + self.init = false; \ + return false; + +#define coroutine(...) \ + if(!self.init) { \ + memset(&self, 0, sizeof(self)); \ + self.init = true; \ + } \ + switch(COVAR->co.state) { \ + case 0:; \ + __VA_ARGS__ \ + } \ + yieldBreak(); + +#define yield() _yield(__COUNTER__) +#define _yield(count) \ + do { \ + COVAR->co.state = count; \ + return true; \ + case count:; \ + } while(0); + +#define yieldVal(v) _yieldVal(v, __COUNTER__) +#define _yieldVal(v, count) \ + do { \ + COVAR->co.state = count; \ + COVAR->result = v; \ + return true; \ + case count:; \ + } while(0); + +// increment __COUNTER__ past 0 so we don't get double case errors +#define CONCAT_IMPL(x, y) x##y +#define MACRO_CONCAT(x, y) CONCAT_IMPL(x, y) +#define __INC_COUNTER int MACRO_CONCAT(__counter_tmp_, __COUNTER__) +__INC_COUNTER; + +#undef CONCAT_IMPL +#undef MACRO_CONCAT +#undef __INC_COUNTER + +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file diff --git a/http.c b/http.c index f75b5a6..97bea8b 100644 --- a/http.c +++ b/http.c @@ -4,7 +4,7 @@ #include #include -#include "strutils.h" +#include "os.h" #include "tracelog.h" // == INTERNAL ================================================================ @@ -282,7 +282,7 @@ http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *req) { http_response_t res = resInit(); char *request_str = NULL; - str_ostream_t received = ostrInit(1024); + str_ostream_t received = ostrInitLen(1024); if(!skInit()) { err("couldn't initialize sockets %s", skGetErrorString()); diff --git a/http.h b/http.h index a1c2a7a..ae9b6e7 100644 --- a/http.h +++ b/http.h @@ -75,7 +75,7 @@ typedef struct { char *body; } http_request_t; -http_request_t reqInit(); +http_request_t reqInit(void); void reqFree(http_request_t *ctx); bool reqHasField(http_request_t *ctx, const char *key); @@ -96,7 +96,7 @@ typedef struct { char *body; } http_response_t; -http_response_t resInit(); +http_response_t resInit(void); void resFree(http_response_t *ctx); bool resHasField(http_response_t *ctx, const char *key); @@ -113,7 +113,7 @@ typedef struct { socket_t socket; } http_client_t; -http_client_t hcliInit(); +http_client_t hcliInit(void); void hcliFree(http_client_t *ctx); void hcliSetHost(http_client_t *ctx, const char *hostname); diff --git a/strutils.c b/os.c similarity index 77% rename from strutils.c rename to os.c index f80ad73..d18c477 100644 --- a/strutils.c +++ b/os.c @@ -1,4 +1,4 @@ -#include "strutils.h" +#include "os.h" #include #include @@ -64,30 +64,4 @@ ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp) { ssize_t getline(char **line_ptr, size_t *n, FILE *stream) { return getdelim(line_ptr, n, '\n', stream); } -#endif - -void strToLower(char *str) { - for(char *beg = str; *beg; ++beg) { - *beg = tolower(*beg); - } -} - -void strnToLower(char *str, size_t len) { - for(size_t i = 0; i < len; ++i) { - str[i] = tolower(str[i]); - } -} - -char *cstrdup(const char *str) { - size_t len = strlen(str); - char *buf = malloc(len + 1); - memcpy(buf, str, len); - buf[len] = '\0'; - return buf; -} - -char *cstrToLower(const char *str) { - char *buf = cstrdup(str); - strToLower(buf); - return buf; -} \ No newline at end of file +#endif \ No newline at end of file diff --git a/strutils.h b/os.h similarity index 63% rename from strutils.h rename to os.h index 60fbb55..a941895 100644 --- a/strutils.h +++ b/os.h @@ -9,7 +9,7 @@ extern "C" { #ifdef _WIN32 #include - #include + #include // SSIZE_T typedef SSIZE_T ssize_t; ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp); ssize_t getline(char **line_ptr, size_t *n, FILE *stream); @@ -18,19 +18,9 @@ extern "C" { #define stricmp strcasecmp #define _GNU_SOURCE #include + #include // ssize_t #endif -// prefix str -> changes string -// prefix cstr -> allocates new string and returns it - -// int str - -void strToLower(char *str); -void strnToLower(char *str, size_t len); - -char *cstrdup(const char *str); -char *cstrToLower(const char *str); - #ifdef __cplusplus } // extern "C" #endif \ No newline at end of file diff --git a/socket.h b/socket.h index c45ad5a..625cd67 100644 --- a/socket.h +++ b/socket.h @@ -30,12 +30,12 @@ struct sockaddr; #endif // Initialize sockets, returns true on success -bool skInit(); +bool skInit(void); // Terminates sockets, returns true on success -bool skCleanup(); +bool skCleanup(void); // Opens a socket, check socket_t with skValid -socket_t skOpen(); +socket_t skOpen(void); // Opens a socket using 'protocol', options are // ip, icmp, ggp, tcp, egp, pup, udp, hmp, xns-idp, rdp // check socket_t with skValid @@ -79,9 +79,9 @@ int skReceivePro(socket_t sock, char *buf, int len, int flags); bool skIsValid(socket_t sock); // Returns latest socket error, returns 0 if there is no error -int skGetError(); +int skGetError(void); // Returns a human-readable string from a skGetError -const char *skGetErrorString(); +const char *skGetErrorString(void); #ifdef __cplusplus } // extern "C" diff --git a/str.c b/str.c new file mode 100644 index 0000000..10e4954 --- /dev/null +++ b/str.c @@ -0,0 +1,267 @@ +#include "str.h" + +#include +#include +#include + +str_t strInit(void) { + return (str_t) { + .buf = NULL, + .len = 0 + }; +} + +str_t strInitStr(const char *cstr) { + return strInitBuf(cstr, strlen(cstr)); +} + +str_t strInitView(strview_t view) { + return strInitBuf(view.buf, view.size); +} + +str_t strInitBuf(const char *buf, size_t len) { + str_t str; + str.len = len; + str.buf = malloc(len + 1); + memcpy(str.buf, buf, len); + str.buf[len] = '\0'; + return str; +} + +void strFree(str_t *ctx) { + free(ctx->buf); + ctx->buf = NULL; + ctx->len = 0; +} + +str_t strMove(str_t *ctx) { + str_t str = strInitBuf(ctx->buf, ctx->len); + ctx->buf = NULL; + ctx->len = 0; + return str; +} + +str_t strDup(str_t ctx) { + return strInitBuf(ctx.buf, ctx.len); +} + +strview_t strGetView(str_t *ctx) { + return (strview_t) { + .buf = ctx->buf, + .size = ctx->len + }; +} + +char *strBegin(str_t *ctx) { + return ctx->buf; +} + +char *strEnd(str_t *ctx) { + return ctx->buf ? ctx->buf + ctx->len : NULL; +} + +char strBack(str_t *ctx) { + return ctx->buf ? ctx->buf[ctx->len - 1] : '\0'; +} + +bool strIsEmpty(str_t *ctx) { + return ctx->len == 0; +} + +void strAppend(str_t *ctx, const char *str) { + strAppendBuf(ctx, str, strlen(str)); +} + +void strAppendStr(str_t *ctx, str_t str) { + strAppendBuf(ctx, str.buf, str.len); +} + +void strAppendView(str_t *ctx, strview_t view) { + strAppendBuf(ctx, view.buf, view.size); +} + +void strAppendBuf(str_t *ctx, const char *buf, size_t len) { + size_t oldlen = ctx->len; + ctx->len += len; + ctx->buf = realloc(ctx->buf, ctx->len + 1); + memcpy(ctx->buf + oldlen, buf, len); + ctx->buf[ctx->len] = '\0'; +} + +void strAppendChars(str_t *ctx, char c, size_t count) { + size_t oldlen = ctx->len; + ctx->len += count; + ctx->buf = realloc(ctx->buf, ctx->len + 1); + memset(ctx->buf + oldlen, c, count); + ctx->buf[ctx->len] = '\0'; +} + +void strPush(str_t *ctx, char c) { + strAppendChars(ctx, c, 1); +} + +char strPop(str_t *ctx) { + char c = strBack(ctx); + ctx->buf = realloc(ctx->buf, ctx->len); + ctx->len -= 1; + ctx->buf[ctx->len] = '\0'; + return c; +} + +void strSwap(str_t *ctx, str_t *other) { + char *buf = other->buf; + size_t len = other->len; + other->buf = ctx->buf; + other->len = ctx->len; + ctx->buf = buf; + ctx->len = len; +} + +#include "tracelog.h" + +str_t strSubstr(str_t *ctx, size_t pos, size_t len) { + if(strIsEmpty(ctx)) return strInit(); + if(len == SIZE_MAX || (pos + len) > ctx->len) len = ctx->len - pos; + return strInitBuf(ctx->buf + pos, len); +} + +strview_t strSubview(str_t *ctx, size_t pos, size_t len) { + if(strIsEmpty(ctx)) return strvInit(NULL); + if(len == SIZE_MAX || (pos + len) > ctx->len) len = ctx->len - pos; + return (strview_t) { + .buf = ctx->buf + pos, + .size = len + }; +} + +void strLower(str_t *ctx) { + for(size_t i = 0; i < ctx->len; ++i) { + ctx->buf[i] = tolower(ctx->buf[i]); + } +} + +str_t strToLower(str_t ctx) { + str_t str = strDup(ctx); + strLower(&str); + return str; +} + +#ifdef STR_TESTING +#include +#include "tracelog.h" + +void strTest(void) { + str_t s; + debug("== testing init ================="); + { + s = strInit(); + printf("%s %zu\n", s.buf, s.len); + strFree(&s); + s = strInitStr("hello world"); + printf("\"%s\" %zu\n", s.buf, s.len); + strFree(&s); + uint8_t buf[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; + s = strInitBuf((char *)buf, sizeof(buf)); + printf("\"%s\" %zu\n", s.buf, s.len); + strFree(&s); + } + debug("== testing view ================="); + { + s = strInitStr("hello world"); + strview_t view = strGetView(&s); + printf("\"%.*s\" %zu\n", (int)view.size, view.buf, view.size); + strFree(&s); + } + debug("== testing begin/end ============"); + { + s = strInitStr("hello world"); + char *beg = strBegin(&s); + char *end = strEnd(&s); + printf("[ "); + for(; beg < end; ++beg) { + printf("%c ", *beg); + } + printf("]\n"); + strFree(&s); + } + debug("== testing back/isempty ========="); + { + s = strInitStr("hello world"); + printf("[ "); + while(!strIsEmpty(&s)) { + printf("%c ", strBack(&s)); + strPop(&s); + } + printf("]\n"); + strFree(&s); + } + debug("== testing append ==============="); + { + s = strInitStr("hello "); + printf("\"%s\" %zu\n", s.buf, s.len); + strAppend(&s, "world"); + printf("\"%s\" %zu\n", s.buf, s.len); + strAppendView(&s, strvInit(", how is it ")); + printf("\"%s\" %zu\n", s.buf, s.len); + uint8_t buf[] = { 'g', 'o', 'i', 'n', 'g' }; + strAppendBuf(&s, (char*)buf, sizeof(buf)); + printf("\"%s\" %zu\n", s.buf, s.len); + strAppendChars(&s, '?', 2); + printf("\"%s\" %zu\n", s.buf, s.len); + strFree(&s); + } + debug("== testing push/pop ============="); + { + s = strInit(); + str_t s2 = strInitStr("hello world"); + + printf("%-14s %-14s\n", "s", "s2"); + printf("----------------------------\n"); + while(!strIsEmpty(&s2)) { + printf("%-14s %-14s\n", s.buf, s2.buf); + strPush(&s, strPop(&s2)); + } + printf("%-14s %-14s\n", s.buf, s2.buf); + + strFree(&s); + strFree(&s2); + } + debug("== testing swap ================="); + { + s = strInitStr("hello"); + str_t s2 = strInitStr("world"); + printf("%-8s %-8s\n", "s", "s2"); + printf("----------------\n"); + printf("%-8s %-8s\n", s.buf, s2.buf); + strSwap(&s, &s2); + printf("%-8s %-8s\n", s.buf, s2.buf); + + strFree(&s); + strFree(&s2); + } + debug("== testing substr ==============="); + { + s = strInitStr("hello world"); + printf("s: %s\n", s.buf); + + printf("-- string\n"); + str_t s2 = strSubstr(&s, 0, 5); + printf("0..5: \"%s\"\n", s2.buf); + strFree(&s2); + + s2 = strSubstr(&s, 5, SIZE_MAX); + printf("6..SIZE_MAX: \"%s\"\n", s2.buf); + strFree(&s2); + + printf("-- view\n"); + strview_t v = strSubview(&s, 0, 5); + printf("0..5: \"%.*s\"\n", (int)v.size, v.buf); + v = strSubview(&s, 5, SIZE_MAX); + printf("6..SIZE_MAX: \"%.*s\"\n", (int)v.size, v.buf); + + strFree(&s); + } + + strFree(&s); +} +#endif \ No newline at end of file diff --git a/str.h b/str.h new file mode 100644 index 0000000..52ec7ec --- /dev/null +++ b/str.h @@ -0,0 +1,61 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "strview.h" + +#define STR_TESTING + +typedef struct { + char *buf; + size_t len; +} str_t; + +str_t strInit(void); +str_t strInitStr(const char *cstr); +str_t strInitView(strview_t view); +str_t strInitBuf(const char *buf, size_t len); + +void strFree(str_t *ctx); + +str_t strMove(str_t *ctx); +str_t strDup(str_t ctx); + +strview_t strGetView(str_t *ctx); + +char *strBegin(str_t *ctx); +char *strEnd(str_t *ctx); + +char strBack(str_t *ctx); + +bool strIsEmpty(str_t *ctx); + +void strAppend(str_t *ctx, const char *str); +void strAppendStr(str_t *ctx, str_t str); +void strAppendView(str_t *ctx, strview_t view); +void strAppendBuf(str_t *ctx, const char *buf, size_t len); +void strAppendChars(str_t *ctx, char c, size_t count); + +void strPush(str_t *ctx, char c); +char strPop(str_t *ctx); + +void strSwap(str_t *ctx, str_t *other); + +// if len == SIZE_MAX, copies until end +str_t strSubstr(str_t *ctx, size_t pos, size_t len); +// if len == SIZE_MAX, returns until end +strview_t strSubview(str_t *ctx, size_t pos, size_t len); + +void strLower(str_t *ctx); +str_t strToLower(str_t ctx); + +#ifdef STR_TESTING +void strTest(void); +#endif + +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file diff --git a/strstream.c b/strstream.c index bc6d921..af86381 100644 --- a/strstream.c +++ b/strstream.c @@ -485,9 +485,9 @@ void ostrAppendi64(str_ostream_t *ctx, int64_t val) { void ostrAppendfloat(str_ostream_t *ctx, float val) { char buf[APPEND_BUF_LEN * 3]; - int len = snprintf(buf, sizeof(buf), "%f", (double)val); + int len = snprintf(buf, sizeof(buf), "%g", (double)val); if(len <= 0) { - err("ostrAppendfloat: couldn't write %f", val); + err("ostrAppendfloat: couldn't write %g", (double)val); return; } ostrAppendview(ctx, strvInitLen(buf, len)); @@ -495,9 +495,9 @@ void ostrAppendfloat(str_ostream_t *ctx, float val) { void ostrAppenddouble(str_ostream_t *ctx, double val) { char buf[APPEND_BUF_LEN * 3]; - int len = snprintf(buf, sizeof(buf), "%f", val); + int len = snprintf(buf, sizeof(buf), "%g", val); if(len <= 0) { - err("ostrAppenddouble: couldn't write %f", val); + err("ostrAppenddouble: couldn't write %g", val); return; } ostrAppendview(ctx, strvInitLen(buf, len)); diff --git a/strstream.h b/strstream.h index ece2293..cbb6552 100644 --- a/strstream.h +++ b/strstream.h @@ -68,7 +68,7 @@ typedef struct { size_t allocated; } str_ostream_t; -str_ostream_t ostrInit(); +str_ostream_t ostrInit(void); str_ostream_t ostrInitLen(size_t initial_alloc); str_ostream_t ostrInitStr(const char *buf, size_t len); diff --git a/strview.c b/strview.c index 30430d2..6952185 100644 --- a/strview.c +++ b/strview.c @@ -14,7 +14,7 @@ strview_t strvInit(const char *cstr) { - return strvInitLen(cstr, strlen(cstr)); + return strvInitLen(cstr, cstr ? strlen(cstr) : 0); } strview_t strvInitLen(const char *buf, size_t size) { diff --git a/tracelog.c b/tracelog.c index c55bb3c..1ef0bcb 100644 --- a/tracelog.c +++ b/tracelog.c @@ -30,46 +30,58 @@ #define RESET "" #define BOLD "" #else - #define BLACK "\x1B[30m" - #define RED "\x1B[31m" - #define GREEN "\x1B[32m" - #define YELLOW "\x1B[33m" - #define BLUE "\x1B[34m" - #define MAGENTA "\x1B[35m" - #define CYAN "\x1B[36m" - #define WHITE "\x1B[37m" - #define RESET "\x1B[0m" - #define BOLD "\x1B[1m" + #define BLACK "\033[30m" + #define RED "\033[31m" + #define GREEN "\033[32m" + #define YELLOW "\033[33m" + #define BLUE "\033[22;34m" + #define MAGENTA "\033[35m" + #define CYAN "\033[36m" + #define WHITE "\033[37m" + #define RESET "\033[0m" + #define BOLD "\033[1m" #endif #define MAX_TRACELOG_MSG_LENGTH 1024 +bool use_newline = true; + void traceLog(LogLevel level, const char *fmt, ...) { char buffer[MAX_TRACELOG_MSG_LENGTH]; + memset(buffer, 0, sizeof(buffer)); + const char *beg; switch (level) { - case LogTrace: strcpy(buffer, BOLD WHITE "[TRACE]: " RESET); break; - case LogDebug: strcpy(buffer, BOLD BLUE "[DEBUG]: " RESET); break; - case LogInfo: strcpy(buffer, BOLD GREEN "[INFO]: " RESET); break; - case LogWarning: strcpy(buffer, BOLD YELLOW "[WARNING]: " RESET); break; - case LogError: strcpy(buffer, BOLD RED "[ERROR]: " RESET); break; - case LogFatal: strcpy(buffer, BOLD RED "[FATAL]: " RESET); break; + case LogTrace: beg = BOLD WHITE "[TRACE]: " RESET; break; + case LogDebug: beg = BOLD BLUE "[DEBUG]: " RESET; break; + case LogInfo: beg = BOLD GREEN "[INFO]: " RESET; break; + case LogWarning: beg = BOLD YELLOW "[WARNING]: " RESET; break; + case LogError: beg = BOLD RED "[ERROR]: " RESET; break; + case LogFatal: beg = BOLD RED "[FATAL]: " RESET; break; default: break; } + size_t offset = strlen(beg); + strncpy(buffer, beg, sizeof(buffer)); + va_list args; va_start(args, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, args); + vsnprintf(buffer + offset, sizeof(buffer) - offset, fmt, args); va_end(args); #ifdef TLOG_VS OutputDebugStringA(buffer); - OutputDebugStringA("\n"); + if(use_newline) OutputDebugStringA("\n"); #else - puts(buffer); + printf("%s", buffer); + if(use_newline) puts(""); #endif #ifndef TLOG_DONT_EXIT_ON_FATAL if (level == LogFatal) exit(1); #endif +} + +void traceUseNewline(bool newline) { + use_newline = newline; } \ No newline at end of file diff --git a/tracelog.h b/tracelog.h index 7549c81..0fa35e4 100644 --- a/tracelog.h +++ b/tracelog.h @@ -10,11 +10,14 @@ extern "C" { * -> TLOG_DONT_EXIT_ON_FATAL: don't call 'exit(1)' when using LogFatal */ +#include + typedef enum { LogAll, LogTrace, LogDebug, LogInfo, LogWarning, LogError, LogFatal } LogLevel; void traceLog(LogLevel level, const char *fmt, ...); +void traceUseNewline(bool use_newline); #define trace(...) traceLog(LogTrace, __VA_ARGS__) #define debug(...) traceLog(LogDebug, __VA_ARGS__)