ADDED clib/buffer.c Index: clib/buffer.c ================================================================== --- clib/buffer.c +++ clib/buffer.c @@ -0,0 +1,48 @@ +#include "buffer.h" +#include +#include + +void buffer_init(buffer* b) { + *b = (buffer) { + .sz = 0, + .run = 256, + }; + b -> ptr = malloc(b -> run); +} + +buffer buffer_mk() { buffer b; buffer_init(&b); return b; } + +void buffer_push(buffer* b, strp const data) { + size_t run = b->run; + while (b -> sz + data.sz > run) run *= 2; + if (run != b->run) { + b->ptr = realloc(b->ptr, run); + b->run = run; + } + memcpy(&b->ptr[b->sz], data.ptr, data.sz); + b->sz += data.sz; +} + +strp buffer_crush(buffer* b, size_t max) { + if (b->run>max) { + b->run = max; + if (b->sz > max) { + free(b->ptr); + b->sz = 0; + b->ptr = malloc(b->run); + } else { + b->ptr = realloc(b->ptr, b->run); + } + } + return (strp){b->sz, b->ptr}; +} + +size_t buffer_pushs(buffer* b, char const* data) { + size_t len = strlen(data); + buffer_push(b, (strp){len, .cptr = data}); + return len; +} + +void buffer_clear(buffer* b) { + b -> sz = 0; +} ADDED clib/buffer.h Index: clib/buffer.h ================================================================== --- clib/buffer.h +++ clib/buffer.h @@ -0,0 +1,15 @@ +#pragma once +#include "type.h" + +typedef struct buffer { + size_t sz, run; + char* ptr; +} buffer; + +void buffer_init(buffer* b); +buffer buffer_mk(); +void buffer_push(buffer* b, strp const data); +strp buffer_crush(buffer* b, size_t max); +size_t buffer_pushs(buffer* b, char const* data); +#define buffer_pushl(b, d) (buffer_push(b, (strp){sizeof(d),(d)})) +void buffer_clear(buffer* b); ADDED clib/map.c Index: clib/map.c ================================================================== --- clib/map.c +++ clib/map.c @@ -0,0 +1,148 @@ +#include "map.h" +#include + +map* mapNew(size_t init_sz) { + map* m = malloc( + sizeof(map) + + sizeof(mapBox)*init_sz + ); + m -> sz = init_sz; + m -> dtor = nullptr; + for (size_t i = 0; i < init_sz; ++i) + m -> boxes[i] = (mapBox) {}; + //m -> weak = false; + return m; +} + +mapBox* mapBoxFor(map* m, strp key) { + hash h = hashNew(key); + return &m->boxes[h % (hash)m->sz]; +} + +void mapBoxInsert(mapBox* b, mapLink* l) { + l -> next = nullptr; + l -> prev = b -> tail; + + if (b -> tail == nullptr) { + b -> head = b -> tail = l; + } else { + b -> tail -> next = l; + b -> tail = l; + } +} + +mapLink* mapEnter(map* m, strp key, mapValue val) { + mapResult r = mapFind(m, key); + if (r.link) { + return r.link; + } else { + mapBox* b = r.box; + mapLink* l = malloc(sizeof(mapLink) + key.sz); + l -> ref = val; + strMov(&l -> key, key); + mapBoxInsert(b, l); + //if(!m->weak) vspRef(val); + return nullptr; + } +} + +bool mapClobber(map* m, strp key, mapValue val, void (*dtor)(mapValue)) { + mapLink* link = mapEnter(m, key, val); + if (link) { + if (!dtor) dtor = m -> dtor; + if (dtor) (*dtor)(link -> ref); + link -> ref = val; + return true; + } + return false; +} + +mapResult mapFind(map* m, strp key) { + mapBox* b = mapBoxFor(m, key); + for(mapLink* l = b -> head; l != nullptr;) { + if(strEq(strRef(&l->key), key)) { + return (mapResult) { + .map = m, + .box = b, + .link = l, + }; + } + } + return (mapResult){.map = m, .box = b}; +} +mapValue mapRef(map* m, strp key) { + mapResult r = mapFind(m, key); + if (r.link == nullptr) return mapValue_null; + return r.link -> ref; +} +void mapErase(map* m, strp key) { + mapResult r = mapFind(m, key); + if (r.link -> prev) r.link -> prev -> next = r.link -> next; + if (r.link -> next) r.link -> next -> prev = r.link -> prev; + if (r.box -> head == r.link) r.box -> head = r.link -> next; + if (r.box -> tail == r.link) r.box -> tail = r.link -> prev; + //if (!m -> weak) vspUnref(r.link -> ref); + free(r.link); +} +map* mapDel(map* m) { + for (size_t i = 0; i < m -> sz; ++i) { + mapBox* b = &m->boxes[i]; + if (b -> head == nullptr) continue; + for(mapLink* l = b -> head; l != nullptr;) { + mapLink* next = l -> next; + //if(!m->weak) vspUnref(l->ref); + free(l); + l = next; + } + } + free(m); +} + +map* mapResize(map* om, size_t newsz) { + /* expensive!! */ + map* nm = mapNew(newsz); + //nm -> weak = om -> weak; + for (size_t i = 0; i < om -> sz; ++i) { + mapBox* b = &om->boxes[i]; + if (b -> head == nullptr) continue; + for(mapLink* l = b -> head; l != nullptr;) { + mapBox* nb = mapBoxFor(nm, strRef(&l->key)); + mapBoxInsert(nb, l); + } + } + free(om); + return nm; +} + +static bool mapEqPredicate(mapValue a, void* b) { + return a.ip = ((mapValue*)b) -> ip; /* real fucky */ +} + +mapResult mapRFindPred(map* m, void* val, mapPredicate p) { + for (size_t i = 0; i < m->sz; ++ i) { + mapBox* b = &m -> boxes[i]; + if (b -> head == nullptr) continue; + for (mapLink* l = b -> head; l != nullptr; l = l -> next) { + if ((*p)(l->ref, val)) return (mapResult) { + .map = m, + .box = b, + .link = l, + }; + } + } + return (mapResult) {m}; +} + +mapResult mapRFind(map* m, mapValue val) { + return mapRFindPred(m, &val, mapEqPredicate); +} + +strp mapRRefPred(map* m, void* val, mapPredicate p) { + mapResult r = mapRFindPred(m, val, p); + if (r.link == nullptr) return (strp){}; + return strRef(&r.link -> key); +} + +strp mapRRef(map* m, mapValue val) { + return mapRRefPred(m, &val, mapEqPredicate); +} ADDED clib/map.h Index: clib/map.h ================================================================== --- clib/map.h +++ clib/map.h @@ -0,0 +1,76 @@ +#pragma once +#include "type.h" +#define mapValue_null ((mapValue){.p=nullptr}) + +#ifndef _mapValue_size +#define _mapValue_size ( \ + sizeof(intmax_t) > sizeof(double) ? \ + sizeof(intmax_t) : sizeof(double) \ +) +#endif + +typedef union mapValue { + /* ensure proper alignment & offer convenience + * handles for punning */ + size_t sz; + ptrdiff_t ofs; + intptr_t ip; + uintmax_t u; + intmax_t s; + double d; + void * p; void const* p_ro; + char * str; char const* str_ro; + uint8_t* bytes; uint8_t const* bytes_ro; + /* maps can pack very small structures directly into + * the entry. if you use this feature, either define + * _mapValue_size as a project global yourself, or + * check to make sure your values fit into it on a + * given architecture. i think this is guaranteed + * to have at least 8 bytes of space (64-types are + * mandated as of C11, possibly earleir?) but i'm + * not 100% on that.*/ + char str_pak [_mapValue_size]; + uint8_t bytes_pak [_mapValue_size]; +} mapValue; + +typedef struct mapLink { + struct mapLink* next, * prev; + mapValue ref; + strv key; +} mapLink; + +typedef struct mapBox { + mapLink* head, * tail; +} mapBox; + +typedef typeof(bool (*)(mapValue, void*)) mapPredicate; +typedef typeof(void (*)(mapValue)) mapDTor; + +typedef struct map { + size_t sz; + //bool weak; + mapDTor dtor; + mapBox boxes []; +} map; + +typedef struct mapResult { + map* map; + mapBox* box; + mapLink* link; +} mapResult; + + +map* mapNew(size_t init_sz); +mapBox* mapBoxFor(map* map, strp key); +void mapBoxInsert(mapBox* b, mapLink* l); +mapLink* mapEnter(map* m, strp key, mapValue val); +bool mapClobber(map* m, strp key, mapValue val, mapDTor dtor); +mapResult mapFind(map* map, strp key); +mapValue mapRef(map* m, strp key); +mapResult mapRFindPred(map* map, void* arg, mapPredicate match); +mapResult mapRFind(map* m, mapValue val); +strp mapRRefPred(map* m, void* val, mapPredicate p); +strp mapRRef(map* m, mapValue val); +void mapErase(map* map, strp key); +map* mapDel(map* map); +map* mapResize(map* map, size_t newsz); ADDED clib/say.c Index: clib/say.c ================================================================== --- clib/say.c +++ clib/say.c @@ -0,0 +1,233 @@ +#include "say.h" + +#ifdef __has_include +# if __has_include() +# define _say_use_ansi +# define _say_use_fd +# endif +#elif defined(__unix__) || defined(__linux__) || defined(__unix) +# define _say_use_ansi +# define _say_use_fd +#endif + +#ifdef _say_use_fd +# include +#endif + +static void buffer_pushm +( buffer* buf, + uintmax_t val, + bool neg, + uint8_t base, + bool ucase +) { + char nb [48] = {}; + size_t digit = 48; + do { + uintmax_t v = val%base; + if (v < 0xa) { + nb[-- digit] = '0' + v; + } else { + if (base <= 36) { + nb[-- digit] = (ucase?'A':'a') + v - 10; + } else { + if (v<36) nb[-- digit] = 'a' + v - 10; + else nb[-- digit] = 'A' + v - 36; + } + } + val /= base; + } while(val > 0); + if (neg) nb[-- digit] = '-'; + buffer_push(buf, (strp){(sizeof nb) - digit, &nb[digit]}); +} + +size_t saybuf(buffer* buf, FILE* dest, const char* fmt, say_arg* args) { + say_arg const* arg = args; +# ifdef _say_use_fd + bool rich = dest == nullptr? true : isatty(fileno(dest)); +# elif defined(_say_use_ansi) + bool const rich = true; +# endif + +# define pushraw(s) do{if(canPrint)buffer_pushl(buf, s);}while(0) +# define pushcond(ch, won, woff) \ + case ch: if (on) pushraw(won); else pushraw(woff); break +# define ansisw(ch, num) pushcond(ch, "\x1b[" #num "m", "\x1b[2" #num "m") +# define ansiclr(ch, num) \ + case (ch) : pushraw("\x1b[3" #num "m"); break; \ + case (ch)-0x20: pushraw("\x1b[4" #num "m"); break; \ + + uint8_t base = 10; + enum {normal, only_when_rich, only_when_plain, silent} exclude = normal; + for (size_t i = 0; fmt[i] != 0; ++i) { + char m = fmt[i]; + char n = fmt[i+1]; + bool canPrint; + switch (exclude) { + case normal: canPrint = true; break; + case silent: canPrint = false; break; + case only_when_rich: +# ifdef _say_use_ansi + canPrint = rich; +# else + canPrint = false; +# endif + break; + case only_when_plain: +# ifdef _say_use_ansi + canPrint = !rich; +# else + canPrint = true; +# endif + break; + } + if (n == 0) goto push; /* prevent bugs on broken seqs */ + if (m == '[') { + bool on = true; + size_t param = 0; + while((m=fmt[++i]) != ']') { +# ifdef _say_use_ansi + if (rich) { + switch(m) { + case '+': on = true; break; + case '-': on = false; break; + case '=': pushraw("\x1b[m"); break; + + ansisw('e', 1); ansisw('d', 2); + ansisw('i', 3); ansisw('u', 4); + ansisw('h', 7); + + ansiclr('k', 0); ansiclr('r', 1); + ansiclr('g', 2); ansiclr('y', 3); + ansiclr('b', 4); ansiclr('p', 5); + ansiclr('c', 6); ansiclr('w', 7); + ansiclr('x', 9); + + case 'L': pushraw("\x1b[2J"); + default: goto fallback; + } + continue; + } +# endif + + fallback: switch (m) { + case '^': exclude = only_when_rich; break; + case '~': exclude = only_when_plain; break; + case '|': exclude = normal; break; + case '#': exclude = silent; break; + case '?': exclude = *((bool*)*(arg++)) ? normal : silent; break; + case '@': base = param; param = 0; break; + case '$': arg = args[param]; param = 0; break; + default: goto enter_param; + } continue; + + enter_param: if (m >= '0' && m <= '9') { + param *= 10; + param += m - '0'; + } + } + } else if (m == '%') { + ++i; + switch (n) { + case '[': m = '['; + case '%': goto push; + case 'z': { + char const* str = (char const*)*(arg++); + if (canPrint) { + if (str == nullptr) buffer_pushs(buf, ""); + else buffer_pushs(buf, str); + } + break; + } + case 's': { + strp const* a = *(arg++); + if (a -> ptr == nullptr) buffer_pushs(buf, ""); + else buffer_push(buf, *a); + break; + } + case 'i': case 'I': + case 'u': case 'U': { + bool neg=false; uintmax_t mag; + if (n=='i') { + intmax_t s = *((intmax_t*)*(arg++)); + if (s < 0) { mag = -s; neg = true; } + else mag = s; + } else mag = *((uintmax_t*)*(arg++)); + buffer_pushm(buf, mag, neg, base, n < 'a'); + break; + } + case 'f': case 'F': {// fraction + fraction f = *((fraction*)*(arg++)); + buffer_pushm(buf, fractionNum(f), fractionNeg(f), base, n<'a'); + buffer_pushs(buf, "/"); + buffer_pushm(buf, fractionDenom(f), false, base, n<'a'); + } break; + case 'x': case 'X': {// fixed + fixed f = *((fixed*)*(arg++)); + bool neg = fixedVal(f) < 0; + f = fixedAbs(f); + buffer_pushm(buf, fixedIntPart(f), neg, base, n<'a'); + uint32_t fracpt = fixedFracPart(f); + if (fracpt != 0) { + intmax_t mul = base; + /* find prec multiplicand */ + while (mul < fixedPrec(f)) { + if (mul * base < mul) { + buffer_pushs(buf, ""); + break; + } + mul *= base; + } + uintmax_t r = ((intmax_t)fracpt * mul*mul) / ((intmax_t)fixedPrec(f)*mul); + buffer_pushs(buf, "."); + buffer_pushm(buf, r, false, base, n<'a'); + } + } break; + case 'n': case 'N': {// inexact + } break; + case '~': { + char const* subfmt = (char const*)*(arg++); + say_arg* subargs = (say_arg*)*(arg++); + saybuf(buf, dest, subfmt, subargs); + break; + } + } + } else goto push; + continue; + + push: if (canPrint) buffer_push(buf, (strp){1, &m}); + } + return arg - args; +# undef ansiclr +# undef ansisw +# undef pushcond +# undef pushraw +} + +static inline void +sayprep(FILE* dest) { +# ifndef _say_use_fd + setvbuf(dest, nullptr, _IONBF, 0); // hax +# endif +} + +void sayto(FILE* dest, char const* fmt, say_arg* args) { + static _Thread_local buffer buf = {}; + if (buf.run == 0) buffer_init(&buf); + else buf.sz = 0; + + saybuf(&buf, dest, fmt, args); + +# ifdef _say_use_fd + write(fileno(dest), buf.ptr, buf.sz); +# else + fprintf(dest, "%.*s", (int)buf.sz, buf.ptr); +# endif + buffer_crush(&buf, 2048); +} + +void say(char const* fmt, say_arg* args) { + static bool stprep = false; + if (!stprep) { stprep=true; sayprep(stderr); }; + sayto(stderr, fmt, args); +} ADDED clib/say.h Index: clib/say.h ================================================================== --- clib/say.h +++ clib/say.h @@ -0,0 +1,9 @@ +#pragma once +#include +#include "type.h" +#include "buffer.h" +typedef void const* say_arg; +typedef typeof(say_arg[]) saylist; +size_t saybuf(buffer* buf, FILE* dest, const char* fmt, say_arg* args); +void sayto(FILE* dest, char const* fmt, say_arg* args); +void say(const char* fmt, say_arg* args);