summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Fankhauser hiddenalpha.ch2026-03-01 13:53:32 +0100
committerAndreas Fankhauser hiddenalpha.ch2026-03-01 13:53:32 +0100
commit2f174da6aba06d64138a0b6145aea17f250045d0 (patch)
tree74921b248b28a7f8085fc31d0f1d8d053a427005
parenta4a4668619bcfdcf701a0cafb7ed688cd35ff399 (diff)
downloadquotprint-2f174da6aba06d64138a0b6145aea17f250045d0.tar.gz
quotprint-2f174da6aba06d64138a0b6145aea17f250045d0.zip
Some initial tinkering
-rw-r--r--.gitignore3
-rw-r--r--README0
-rwxr-xr-xconfigure64
-rw-r--r--src/quotprint.c387
4 files changed, 454 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5bfdd12
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+Makefile
+build
+.gdb_history
diff --git a/README b/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README
diff --git a/configure b/configure
new file mode 100755
index 0000000..06e4c1e
--- /dev/null
+++ b/configure
@@ -0,0 +1,64 @@
+#!/usr/bin/env lua
+
+
+do
+
+ function getCFLAGSArr()return{
+ "-Wall", "-Werror", "-pedantic", "-std=c99", "-fmax-errors=1", "-ggdb", "-O0",
+ }end
+
+ function getLDFLAGSAarr()return{ }end
+
+end
+
+
+function main()
+ local snk = io.open("Makefile", "wb")
+ snk:write([=[
+
+.SILENT:
+
+CC=gcc
+LD=gcc
+MKDIRS=mkdir -p
+RIMRAF=rm -rf
+
+PROJECT_NAME=quotprint
+PROJECT_VERSION=$(shell git describe --tags || date -u +0.0.0-%Y%m%d.%H%M%S)
+
+CFLAGS=]=])for _, f in ipairs(getCFLAGSArr())do snk:write(" ")snk:write(f)end snk:write([=[ \
+ -DPROJECT_NAME=$(PROJECT_NAME) \
+ -DPROJECT_VERSION=$(PROJECT_VERSION) \
+
+DFLAGS=]=])for _, f in ipairs(getLDFLAGSAarr())do snk:write(" ")snk:write(f)end snk:write("\n"..[=[
+
+
+.PHONY: default
+default: build/bin/quotprint
+
+
+.PHONY: clean
+clean:
+ $(RIMRAF) build/bin/* build/obj/*
+
+
+build/bin/quotprint:
+build/bin/quotprint: build/obj/quotprint.o
+ printf ' LN\t%s\n' '$@'
+ $(MKDIRS) "$(shell dirname "$@")"
+ $(LD) -o $@ $^ $(LDFLAGS)
+
+
+build/obj/quotprint.o:
+build/obj/quotprint.o: src/quotprint.c
+ printf ' CC\t%s\n' '$@'
+ $(MKDIRS) "$(shell dirname "$@")"
+ $(CC) -c -o $@ $^ $(CFLAGS)
+
+
+]=])
+ snk:close()
+end
+
+main()
+
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 <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+
+#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
+ }
+}
+