From 2f174da6aba06d64138a0b6145aea17f250045d0 Mon Sep 17 00:00:00 2001 From: Andreas Fankhauser hiddenalpha.ch Date: Sun, 1 Mar 2026 13:53:32 +0100 Subject: Some initial tinkering --- src/quotprint.c | 387 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 src/quotprint.c (limited to 'src') diff --git a/src/quotprint.c b/src/quotprint.c new file mode 100644 index 0000000..902d265 --- /dev/null +++ b/src/quotprint.c @@ -0,0 +1,387 @@ + + +#include +#include +#include +#include +#include +#include +#include + + +#define FLG_isEnc (1<<0) +#define FLG_isDec (1<<1) +#define FLG_isError (1<<2) + +#ifndef BUF_CAP /* aka "buffer capacity" */ +# define BUF_CAP 0x10000 +#endif + +#ifndef INLINE +# define INLINE(T) /*TODO static inline*/ T +#endif +#ifndef PRIVATE +# define PRIVATE(T) static T +#endif + +#ifndef REGISTER +# define REGISTER /*no-op*/ +#endif + +#define STR_QUOT_(S) #S +#define STR_QUOT(S) STR_QUOT_(S) + + +typedef struct FalseSharingBarrier { void* _[64/sizeof(void*)]; } FalseSharingBarrier; + + +typedef struct Buf { + int len; + char buf[BUF_CAP]; +} Buf; + + +#define App_mAGIC 0x9C344C73 +#define DEFINE_App(D, S) struct App*const D=(void*)S; do{ \ + assert(D); assert(D->mAGIC == App_mAGIC); }while(0) +typedef struct App { + int mAGIC; + int flg; + pthread_t thrdWr, thrdRd; + FalseSharingBarrier _IQExiA5MsnEuw7eW; + /* + * shared in between processing and destination thread */ + char *dstBufReadyToFill; + char *dstBufReadyToFlush; + pthread_mutex_t mutxDst; + pthread_cond_t condDst; + FalseSharingBarrier _EY0zt7tDeaLkpOmA; + /* + * shared in between source and processing thread */ + char *srcBufReadyToConsume; + char *srcBufReadyToFill; + pthread_mutex_t mutxSrc; + pthread_cond_t condSrc; + FalseSharingBarrier _yZYEomgBwCcRRKws; + /* + * double-buffering for each (read/write) side. */ + FalseSharingBarrier _8mjY6g2I7tL3wQDE; + Buf dstBufOne; + FalseSharingBarrier _FkFIIpC2JzIaKuJe; + Buf dstBufTwo; + FalseSharingBarrier _d3xluckkcebLiGzO; + Buf srcBufOne; + FalseSharingBarrier _y629LZsTzNbe2rka; + Buf srcBufTwo; +} App; + + +INLINE(void) +printHelp( void ){ + printf("\n" + " quotprint - QuotedPrintable codec\n" + " Version: " STR_QUOT(PROJECT_VERSION) "\n" + " \n" + " -e `encode` to QuotedPrintable\n" + " -d `decode` from QuotedPrintable\n" + " \n" + "\n"); +} + + +INLINE(int) +parseArgs( int argc, char**argv, App*this ){ + int err; + assert(argv[0]); assert(this); + /* 1st prime config based on filename. Eg to support `quotprintenc` and + * `quotprintdec`. */ + char *basename = strrchr(argv[0], '/'); + basename = (basename) ? basename +1 : argv[0]; + if( !strcasecmp(basename, "quotprintenc") ) + { this->flg |= FLG_isEnc; } + if( !strcasecmp(basename, "quotprintdec") ) + { this->flg |= FLG_isDec; } + /* then handle regular args */ + int iA = 0; +nextArg: + iA += 1; + char *arg = argv[iA]; + if( !arg ){ + goto verify; + }else if( !strcmp(arg, "--help") ){ + return -2; + }else if( !strcmp(arg, "-e") ){ + this->flg |= FLG_isEnc; + }else if( !strcmp(arg, "-d") ){ + this->flg |= FLG_isDec; + }else{ + fprintf(stderr, "EINVAL: %s\n", arg); + return -1; + } + goto nextArg; +verify: + err = FLG_isEnc | FLG_isDec; + if( (this->flg & err) == err || (this->flg & err) == 0 ){ + fprintf(stderr, "EINVAL: Choose exactly ONE of `encode` XOR `decode`.\n"); + return -1; + } + return 0; +} + + +PRIVATE(void*) +mainOfOutputThread( void*_ ){ + /* TODO shutdown if error on other thread */ + int err; + #define BUF (this->dstBufReadyToFlush) + #define LEN *(((int*)0) \ + + ((uintptr_t)(BUF == this->dstBufOne.buf) * (uintptr_t)&this->dstBufOne.len) \ + + ((uintptr_t)(BUF != this->dstBufOne.buf) * (uintptr_t)&this->dstBufTwo.len) \ + ) + DEFINE_App(this, _); + { + err = pthread_mutex_lock(&this->mutxDst); assert(!err); + }awaitBuffer:{ + while( !BUF ) + pthread_cond_wait(&this->condDst, &this->mutxDst); + goto writeChunk; + }writeChunk:{ + assert(LEN > 0); assert(BUF); + err = fprintf(stdout, "%.*s", LEN, BUF); + if( err ){ + err = -errno; + /*TODO maybe handle closed sink gracefully*/ + this->flg |= FLG_isError; + fprintf(stderr, "ERRNO_%d: fprintf(stdout)\n", -err); + } + /*clear buf and set unassigned to inidicate we're ready for another one*/ + LEN = 0; + BUF = NULL; + err = pthread_cond_broadcast(&this->condDst); assert(!err); + goto awaitBuffer; + }{ + err = pthread_mutex_unlock(&this->mutxDst); assert(!err); + assert(!"TODO_3LHezbEJ2sjcryPR"); + } + #undef BUF + #undef LEN +} + + +PRIVATE(void*) +mainOfInputThread( void*_ ){ + /* TODO shutdown if error on other thread */ + int err; + DEFINE_App(this, _); + #define BUF (this->srcBufReadyToFill) + /*TODO branchless*/ + #define LEN (*((BUF == this->srcBufOne.buf) ? (&this->srcBufOne.len) : (&this->srcBufTwo.len))) + { + err = pthread_mutex_lock(&this->mutxSrc); assert(!err); + goto awaitBuffer; + }awaitBuffer:{ + while( !BUF ) + pthread_cond_wait(&this->condSrc, &this->mutxSrc); + goto readNextChunk; + }readNextChunk:{ + #define SRC stdin + assert(BUF); assert(LEN == 0); + err = fread(BUF, 1, BUF_CAP, SRC); + if( err == 0 && ferror(SRC) ){ + err = -errno; + /*TODO maybe handle closed sink gracefully*/ + this->flg |= FLG_isError; + fprintf(stderr, "ERRNO_%d: " STR_QUOT(PROJECT_NAME) ": fread()\n", -err); + goto resolveVoid; + } + if( err == 0 && feof(SRC) ){ + assert(!"TODO_JcpEQzXe09haSQBK EOF"); + } + assert(err > 0); + /* mark buffer filled and unassing to indicate we're ready for another + * one to fill */ + LEN = err; + BUF = NULL; + err = pthread_cond_broadcast(&this->condSrc); assert(!err); + err = pthread_mutex_unlock(&this->mutxSrc); assert(!err); + goto awaitBuffer; + #undef SRC + }resolveVoid:{ + return NULL; + } + #undef BUF + #undef LEN +} + + +INLINE(int) +quotPrintEncode( App*this ){ + int err, iSrc, iDst; + { + #define BUF_DST (this->dstBufReadyToFill) + #define LEN_DST *(((int*)0) \ + + ((uintptr_t)(BUF_DST == this->dstBufOne.buf) * (uintptr_t)&this->dstBufOne.len) \ + + ((uintptr_t)(BUF_DST == this->dstBufTwo.buf) * (uintptr_t)&this->dstBufTwo.len) \ + ) + #define BUF_SRC (this->srcBufReadyToConsume) + #define LEN_SRC *(((int*)0) \ + + ((uintptr_t)(BUF_SRC == this->srcBufOne.buf) * (uintptr_t)&this->srcBufOne.len) \ + + ((uintptr_t)(BUF_SRC == this->srcBufTwo.buf) * (uintptr_t)&this->srcBufTwo.len) \ + ) + }awaitBuffers:{ + err = pthread_mutex_lock(&this->mutxDst); assert(!err); + while( !BUF_DST ) + pthread_cond_wait(&this->condDst, &this->mutxDst); + err = pthread_mutex_unlock(&this->mutxDst); assert(!err); + }{ + err = pthread_mutex_lock(&this->mutxSrc); assert(!err); + while( !BUF_SRC ) + pthread_cond_wait(&this->condSrc, &this->mutxSrc); + err = pthread_mutex_unlock(&this->mutxSrc); assert(!err); + }{ + /*both buffers are ready now*/ + iSrc = iDst = 0; + for(;;){ + if( iDst >= LEN_DST ) + { assert(!"TODO_HYo5e9nXVCpDf8UL"); } + if( iSrc >= LEN_SRC ) + { assert(!"TODO_WUrQL6GwlSueGI6v"); } + uint_fast8_t c = BUF_SRC[iSrc]; + switch( c ){ + case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: + case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: + case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: + case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: + case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F: + case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: + case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: + case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: + case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F: + case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: + case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: + case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F: + case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F: + case 0xA0: case 0xA1: case 0xA2: case 0xA3: case 0xA4: case 0xA5: case 0xA6: case 0xA7: + case 0xA8: case 0xA9: case 0xAA: case 0xAB: case 0xAC: case 0xAD: case 0xAE: case 0xAF: + case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: + case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF: + case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC4: case 0xC5: case 0xC6: case 0xC7: + case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: + case 0xD0: case 0xD1: case 0xD2: case 0xD3: case 0xD4: case 0xD5: case 0xD6: case 0xD7: + case 0xD8: case 0xD9: case 0xDA: case 0xDB: case 0xDC: case 0xDD: case 0xDE: case 0xDF: + case 0xE0: case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7: + case 0xE8: case 0xE9: case 0xEA: case 0xEB: case 0xEC: case 0xED: case 0xEE: case 0xEF: + case 0xF0: case 0xF1: case 0xF2: case 0xF3: case 0xF4: case 0xF5: case 0xF6: case 0xF7: + case 0xF8: case 0xF9: case 0xFA: case 0xFB: case 0xFC: case 0xFD: case 0xFE: case 0xFF: + BUF_DST[iDst++] = c; + } + } + }/*update buffer states*/{ + err = pthread_mutex_lock(&this->mutxDst); assert(!err); + LEN_DST = iDst; + BUF_DST = NULL; + err = pthread_mutex_unlock(&this->mutxDst); assert(!err); + }{ + err = pthread_mutex_lock(&this->mutxSrc); assert(!err); + LEN_SRC = 0; + BUF_SRC = NULL; + err = pthread_mutex_unlock(&this->mutxSrc); assert(!err); + goto awaitBuffers; + }/*resolveWithErr*/{ + assert(err <= 0); + pthread_mutex_unlock(&this->mutxSrc); + return err; + #undef BUF + #undef LEN + } +} + + +INLINE(int) +quotPrintDecode( App*this ){ + assert(!"TODO_i5TKHBVqBa0lRhqy"); +} + + +INLINE(int) +run( App*this ){ + int err; + /*assign initial buffers BEFORE we lanch threads, so we dont need mutx*/{ + this->srcBufReadyToFill = this->srcBufOne.buf; + this->dstBufReadyToFill = this->dstBufOne.buf; + }/*launch a thread for writing*/{ + err = pthread_create(&this->thrdWr, NULL, mainOfOutputThread, this); + if( err ){ + assert(err > 0); + err = -err; goto resolveWithErr; + } + }/*launch a thread for reading*/{ + err = pthread_create(&this->thrdRd, NULL, mainOfInputThread, this); + if( err ){ + assert(err > 0); + err = -err; goto resolveWithErr; + } + }/*use main thread to do the processing*/{ + if( this->flg & FLG_isEnc ){ + err = quotPrintEncode(this); assert(err <= 0); + goto resolveWithErr; + }else if( this->flg & FLG_isDec ){ + err = quotPrintDecode(this); assert(err <= 0); + goto resolveWithErr; + } + assert(!"unreachable"); + }resolveWithErr:{ + return err; + } +} + + +int +main( int argc, char**argv ){ + int err; + int isThreadingIsInit = 0; + static App app_ = { + .mAGIC = App_mAGIC, + }; + static App*const this = &app_; + { + switch( parseArgs(argc, argv, this) ){ + case 0: break; /*OK*/ + case -1: err = 1; goto resolveWithErr; + case -2: printHelp(); err = 0; goto resolveWithErr; + default: assert(!"unreachable"); + } + }{ + #define Buf_init(BUF) do{ (BUF)->len = 0; }while(0) + #define Buf_fini(BUF) do{ }while(0) + Buf_init(&this->dstBufOne); + Buf_init(&this->dstBufTwo); + Buf_init(&this->srcBufOne); + Buf_init(&this->srcBufTwo); + isThreadingIsInit = 1; + #undef Buf_init + }{ + err = (run(this) != 0) * 1; + goto resolveWithErr; + }resolveWithErr:{ + int const errBk = err; + if( isThreadingIsInit ){ + Buf_fini(this->dstBufOne); + Buf_fini(this->dstBufTwo); + Buf_fini(this->srcBufOne); + Buf_fini(this->srcBufTwo); + } + err = errBk; + assert(err == 0 || err == 1); + return err; + #undef Buf_fini + } +} + -- cgit v1.3.1