util  Check-in [0894d03fbf]

Overview
Comment:add generic routines from vesper
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 0894d03fbff624002b8ff1e608bd36b44ab006a579494241e6ddad2ae24482c9
User & Date: lexi on 2023-04-15 20:47:04
Other Links: manifest | tags
Context
2024-07-10
19:07
periodic update check-in: f7c93df9f4 user: lexi tags: trunk
2023-04-15
20:47
add generic routines from vesper check-in: 0894d03fbf user: lexi tags: trunk
2023-04-13
07:26
relicense to EUPL check-in: 0f5a51907d user: lexi tags: trunk
Changes

Added clib/buffer.c version [a88ca60244].

            1  +#include "buffer.h"
            2  +#include <string.h>
            3  +#include <stdlib.h>
            4  +
            5  +void buffer_init(buffer* b) {
            6  +	*b = (buffer) {
            7  +		.sz = 0,
            8  +		.run = 256,
            9  +	};
           10  +	b -> ptr = malloc(b -> run);
           11  +}
           12  +
           13  +buffer buffer_mk() { buffer b; buffer_init(&b); return b; }
           14  +
           15  +void buffer_push(buffer* b, strp const data) {
           16  +	size_t run = b->run;
           17  +	while (b -> sz + data.sz > run) run *= 2;
           18  +	if (run != b->run) {
           19  +		b->ptr = realloc(b->ptr, run);
           20  +		b->run = run;
           21  +	}
           22  +	memcpy(&b->ptr[b->sz], data.ptr, data.sz);
           23  +	b->sz += data.sz;
           24  +}
           25  +
           26  +strp buffer_crush(buffer* b, size_t max) {
           27  +	if (b->run>max) {
           28  +		b->run = max;
           29  +		if (b->sz > max) {
           30  +			free(b->ptr);
           31  +			b->sz = 0;
           32  +			b->ptr = malloc(b->run);
           33  +		} else {
           34  +			b->ptr = realloc(b->ptr, b->run);
           35  +		}
           36  +	}
           37  +	return (strp){b->sz, b->ptr};
           38  +}
           39  +
           40  +size_t buffer_pushs(buffer* b, char const* data) {
           41  +	size_t len = strlen(data);
           42  +	buffer_push(b, (strp){len, .cptr = data});
           43  +	return len;
           44  +}
           45  +
           46  +void buffer_clear(buffer* b) {
           47  +	b -> sz = 0;
           48  +}

Added clib/buffer.h version [97b258e329].

            1  +#pragma once
            2  +#include "type.h"
            3  +
            4  +typedef struct buffer {
            5  +	size_t sz, run;
            6  +	char* ptr;
            7  +} buffer;
            8  +
            9  +void buffer_init(buffer* b);
           10  +buffer buffer_mk();
           11  +void buffer_push(buffer* b, strp const data);
           12  +strp buffer_crush(buffer* b, size_t max);
           13  +size_t buffer_pushs(buffer* b, char const* data);
           14  +#define buffer_pushl(b, d) (buffer_push(b, (strp){sizeof(d),(d)}))
           15  +void buffer_clear(buffer* b);

Added clib/map.c version [f74e4bde0b].

            1  +#include "map.h"
            2  +#include <stdlib.h>
            3  +
            4  +map* mapNew(size_t init_sz) {
            5  +	map* m = malloc(
            6  +		sizeof(map) +
            7  +		sizeof(mapBox)*init_sz
            8  +	);
            9  +	m -> sz = init_sz;
           10  +	m -> dtor = nullptr;
           11  +	for (size_t i = 0; i < init_sz; ++i)
           12  +		m -> boxes[i] = (mapBox) {};
           13  +	//m -> weak = false;
           14  +	return m;
           15  +}
           16  +
           17  +mapBox* mapBoxFor(map* m, strp key) {
           18  +	hash h = hashNew(key);
           19  +	return &m->boxes[h % (hash)m->sz];
           20  +}
           21  +
           22  +void mapBoxInsert(mapBox* b, mapLink* l) {
           23  +	l -> next = nullptr;
           24  +	l -> prev = b -> tail;
           25  +
           26  +	if (b -> tail == nullptr) {
           27  +		b -> head = b -> tail = l;
           28  +	} else {
           29  +		b -> tail -> next = l;
           30  +		b -> tail = l;
           31  +	}
           32  +}
           33  +
           34  +mapLink* mapEnter(map* m, strp key, mapValue val) {
           35  +	mapResult r = mapFind(m, key);
           36  +	if (r.link) {
           37  +		return r.link;
           38  +	} else {
           39  +		mapBox* b = r.box;
           40  +		mapLink* l = malloc(sizeof(mapLink) + key.sz);
           41  +		l -> ref = val;
           42  +		strMov(&l -> key, key);
           43  +		mapBoxInsert(b, l);
           44  +		//if(!m->weak) vspRef(val);
           45  +		return nullptr;
           46  +	}
           47  +}
           48  +
           49  +bool mapClobber(map* m, strp key, mapValue val, void (*dtor)(mapValue)) {
           50  +	mapLink* link = mapEnter(m, key, val);
           51  +	if (link) {
           52  +		if (!dtor) dtor = m -> dtor;
           53  +		if (dtor) (*dtor)(link -> ref);
           54  +		link -> ref = val;
           55  +		return true;
           56  +	}
           57  +	return false;
           58  +}
           59  +
           60  +mapResult mapFind(map* m, strp key) {
           61  +	mapBox* b = mapBoxFor(m, key);
           62  +	for(mapLink* l = b -> head; l != nullptr;) {
           63  +		if(strEq(strRef(&l->key), key)) {
           64  +			return (mapResult) {
           65  +				.map = m,
           66  +				.box = b,
           67  +				.link = l,
           68  +			};
           69  +		}
           70  +	}
           71  +	return (mapResult){.map = m, .box = b};
           72  +}
           73  +mapValue mapRef(map* m, strp key) {
           74  +	mapResult r = mapFind(m, key);
           75  +	if (r.link == nullptr) return mapValue_null;
           76  +	return r.link -> ref;
           77  +}
           78  +void mapErase(map* m, strp key) {
           79  +	mapResult r = mapFind(m, key);
           80  +	if (r.link -> prev) r.link -> prev -> next = r.link -> next;
           81  +	if (r.link -> next) r.link -> next -> prev = r.link -> prev;
           82  +	if (r.box -> head == r.link) r.box -> head = r.link -> next;
           83  +	if (r.box -> tail == r.link) r.box -> tail = r.link -> prev;
           84  +	//if (!m -> weak) vspUnref(r.link -> ref);
           85  +	free(r.link);
           86  +}
           87  +map* mapDel(map* m) {
           88  +	for (size_t i = 0; i < m -> sz; ++i) {
           89  +		mapBox* b = &m->boxes[i];
           90  +		if (b -> head == nullptr) continue;
           91  +		for(mapLink* l = b -> head; l != nullptr;) {
           92  +			mapLink* next = l -> next;
           93  +			//if(!m->weak) vspUnref(l->ref);
           94  +			free(l);
           95  +			l = next;
           96  +		}
           97  +	}
           98  +	free(m);
           99  +}
          100  +
          101  +map* mapResize(map* om, size_t newsz) {
          102  +	/* expensive!! */
          103  +	map* nm = mapNew(newsz);
          104  +	//nm -> weak = om -> weak;
          105  +	for (size_t i = 0; i < om -> sz; ++i) {
          106  +		mapBox* b = &om->boxes[i];
          107  +		if (b -> head == nullptr) continue;
          108  +		for(mapLink* l = b -> head; l != nullptr;) {
          109  +			mapBox* nb = mapBoxFor(nm, strRef(&l->key)); 
          110  +			mapBoxInsert(nb, l);
          111  +		}
          112  +	}
          113  +	free(om);
          114  +	return nm;
          115  +}
          116  +
          117  +static bool mapEqPredicate(mapValue a, void* b) {
          118  +	return a.ip = ((mapValue*)b) -> ip; /* real fucky */
          119  +}
          120  +
          121  +mapResult mapRFindPred(map* m, void* val, mapPredicate p) {
          122  +	for (size_t i = 0; i < m->sz; ++ i) {
          123  +		mapBox* b = &m -> boxes[i];
          124  +		if (b -> head == nullptr) continue;
          125  +		for (mapLink* l = b -> head; l != nullptr; l = l -> next) {
          126  +			if ((*p)(l->ref, val)) return (mapResult) {
          127  +				.map = m,
          128  +				.box = b,
          129  +				.link = l,
          130  +			};
          131  +		}
          132  +	}
          133  +	return (mapResult) {m};
          134  +}
          135  +
          136  +mapResult mapRFind(map* m, mapValue val) {
          137  +	return mapRFindPred(m, &val, mapEqPredicate);
          138  +}
          139  +
          140  +strp mapRRefPred(map* m, void* val, mapPredicate p) {
          141  +	mapResult r = mapRFindPred(m, val, p);
          142  +	if (r.link == nullptr) return (strp){};
          143  +	return strRef(&r.link -> key);
          144  +}
          145  +
          146  +strp mapRRef(map* m, mapValue val) {
          147  +	return mapRRefPred(m, &val, mapEqPredicate);
          148  +}

Added clib/map.h version [36b154fb5a].

            1  +#pragma once
            2  +#include "type.h"
            3  +#define mapValue_null ((mapValue){.p=nullptr})
            4  +
            5  +#ifndef _mapValue_size
            6  +#define _mapValue_size ( \
            7  +	sizeof(intmax_t) > sizeof(double) ? \
            8  +	sizeof(intmax_t) : sizeof(double) \
            9  +)
           10  +#endif
           11  +	
           12  +typedef union mapValue {
           13  +	/* ensure proper alignment & offer convenience 
           14  +	 * handles for punning */
           15  +	size_t      sz;
           16  +	ptrdiff_t   ofs;
           17  +	intptr_t    ip;
           18  +	uintmax_t   u;
           19  +	intmax_t    s;
           20  +	double      d;
           21  +	void   *    p;     void    const* p_ro;
           22  +	char   *    str;   char    const* str_ro;
           23  +	uint8_t*    bytes; uint8_t const* bytes_ro;
           24  +	/* maps can pack very small structures directly into
           25  +	 * the entry. if you use this feature, either define
           26  +	 * _mapValue_size as a project global yourself, or
           27  +	 * check to make sure your values fit into it on a
           28  +	 * given architecture. i think this is guaranteed
           29  +	 * to have at least 8 bytes of space (64-types are
           30  +	 * mandated as of C11, possibly earleir?) but i'm
           31  +	 * not 100% on that.*/
           32  +	char        str_pak   [_mapValue_size];
           33  +	uint8_t     bytes_pak [_mapValue_size];
           34  +} mapValue;
           35  +
           36  +typedef struct mapLink {
           37  +	struct mapLink* next, * prev;
           38  +	mapValue ref;
           39  +	strv key;
           40  +} mapLink;
           41  +
           42  +typedef struct mapBox {
           43  +	mapLink* head, * tail;
           44  +} mapBox;
           45  +
           46  +typedef typeof(bool (*)(mapValue, void*)) mapPredicate;
           47  +typedef typeof(void (*)(mapValue))        mapDTor;
           48  +
           49  +typedef struct map {
           50  +	size_t sz;
           51  +	//bool weak;
           52  +	mapDTor dtor;
           53  +	mapBox boxes [];
           54  +} map;
           55  +
           56  +typedef struct mapResult {
           57  +	map* map;
           58  +	mapBox* box;
           59  +	mapLink* link;
           60  +} mapResult;
           61  +
           62  +
           63  +map*      mapNew(size_t init_sz);
           64  +mapBox*   mapBoxFor(map* map, strp key);
           65  +void      mapBoxInsert(mapBox* b, mapLink* l);
           66  +mapLink*  mapEnter(map* m, strp key, mapValue val);
           67  +bool      mapClobber(map* m, strp key, mapValue val, mapDTor dtor);
           68  +mapResult mapFind(map* map, strp key);
           69  +mapValue  mapRef(map* m, strp key);
           70  +mapResult mapRFindPred(map* map, void* arg, mapPredicate match);
           71  +mapResult mapRFind(map* m, mapValue val);
           72  +strp      mapRRefPred(map* m, void* val, mapPredicate p);
           73  +strp      mapRRef(map* m, mapValue val);
           74  +void      mapErase(map* map, strp key);
           75  +map*      mapDel(map* map);
           76  +map*      mapResize(map* map, size_t newsz);

Added clib/say.c version [84789a032f].

            1  +#include "say.h"
            2  +
            3  +#ifdef __has_include
            4  +#	if __has_include(<unistd.h>)
            5  +#		define _say_use_ansi
            6  +#		define _say_use_fd
            7  +#	endif
            8  +#elif defined(__unix__) || defined(__linux__) || defined(__unix)
            9  +#	define _say_use_ansi
           10  +#	define _say_use_fd
           11  +#endif
           12  +
           13  +#ifdef _say_use_fd
           14  +#	include <unistd.h>
           15  +#endif
           16  +
           17  +static void buffer_pushm
           18  +(	buffer*   buf,
           19  +	uintmax_t val,
           20  +	bool      neg,
           21  +	uint8_t   base,
           22  +	bool      ucase
           23  +) {
           24  +	char nb [48] = {};
           25  +	size_t digit = 48;
           26  +	do {
           27  +		uintmax_t v = val%base;
           28  +		if (v < 0xa) {
           29  +			nb[-- digit] = '0' + v;
           30  +		} else {
           31  +			if (base <= 36) {
           32  +				nb[-- digit] = (ucase?'A':'a') + v - 10;
           33  +			} else {
           34  +				if (v<36) nb[-- digit] = 'a' + v - 10;
           35  +				     else nb[-- digit] = 'A' + v - 36;
           36  +			}
           37  +		}
           38  +		val /= base;
           39  +	} while(val > 0);
           40  +	if (neg) nb[-- digit] = '-';
           41  +	buffer_push(buf, (strp){(sizeof nb) - digit, &nb[digit]});
           42  +}
           43  +
           44  +size_t saybuf(buffer* buf, FILE* dest, const char* fmt, say_arg* args) {
           45  +	say_arg const* arg = args;
           46  +#	ifdef _say_use_fd
           47  +		bool rich = dest == nullptr? true : isatty(fileno(dest));
           48  +#	elif defined(_say_use_ansi)
           49  +		bool const rich = true;
           50  +#	endif
           51  +
           52  +#	define pushraw(s) do{if(canPrint)buffer_pushl(buf, s);}while(0)
           53  +#	define pushcond(ch, won, woff) \
           54  +		case ch: if (on) pushraw(won); else pushraw(woff); break
           55  +#	define ansisw(ch, num) pushcond(ch, "\x1b[" #num "m", "\x1b[2" #num "m")
           56  +#	define ansiclr(ch, num) \
           57  +		case (ch)     : pushraw("\x1b[3" #num "m"); break; \
           58  +		case (ch)-0x20: pushraw("\x1b[4" #num "m"); break; \
           59  +
           60  +	uint8_t base = 10;
           61  +	enum {normal, only_when_rich, only_when_plain, silent} exclude = normal;
           62  +	for (size_t i = 0; fmt[i] != 0; ++i) {
           63  +		char m = fmt[i];
           64  +		char n = fmt[i+1];
           65  +		bool canPrint;
           66  +		switch (exclude) {
           67  +			case normal: canPrint = true;  break;
           68  +			case silent: canPrint = false; break;
           69  +			case only_when_rich:
           70  +#				ifdef _say_use_ansi
           71  +					canPrint = rich;
           72  +#				else
           73  +					canPrint = false;
           74  +#				endif
           75  +				break;
           76  +			case only_when_plain:
           77  +#				ifdef _say_use_ansi
           78  +					canPrint = !rich;
           79  +#				else
           80  +					canPrint = true;
           81  +#				endif
           82  +				break;
           83  +		}
           84  +		if (n == 0) goto push; /* prevent bugs on broken seqs */
           85  +		if (m == '[') {
           86  +			bool on = true;
           87  +			size_t param = 0;
           88  +			while((m=fmt[++i]) != ']') {
           89  +#				ifdef _say_use_ansi
           90  +				if (rich) {
           91  +					switch(m) {
           92  +						case '+': on = true;  break;
           93  +						case '-': on = false; break;
           94  +						case '=': pushraw("\x1b[m"); break;
           95  +
           96  +						ansisw('e', 1); ansisw('d', 2);
           97  +						ansisw('i', 3); ansisw('u', 4);
           98  +						ansisw('h', 7);
           99  +
          100  +						ansiclr('k', 0); ansiclr('r', 1);
          101  +						ansiclr('g', 2); ansiclr('y', 3);
          102  +						ansiclr('b', 4); ansiclr('p', 5);
          103  +						ansiclr('c', 6); ansiclr('w', 7);
          104  +						ansiclr('x', 9);
          105  +
          106  +						case 'L': pushraw("\x1b[2J");
          107  +						default: goto fallback;
          108  +					}
          109  +					continue;
          110  +				}
          111  +#				endif
          112  +
          113  +				fallback: switch (m) {
          114  +					case '^': exclude = only_when_rich; break;
          115  +					case '~': exclude = only_when_plain; break;
          116  +					case '|': exclude = normal; break;
          117  +					case '#': exclude = silent; break;
          118  +					case '?': exclude = *((bool*)*(arg++)) ? normal : silent; break;
          119  +					case '@': base = param;      param = 0; break;
          120  +					case '$': arg = args[param]; param = 0; break;
          121  +					default: goto enter_param;
          122  +				} continue;
          123  +
          124  +				enter_param: if (m >= '0' && m <= '9') {
          125  +					param *= 10;
          126  +					param += m - '0';
          127  +				}
          128  +			}
          129  +		} else if (m == '%') {
          130  +			++i;
          131  +			switch (n) {
          132  +				case '[': m = '[';
          133  +				case '%': goto push;
          134  +				case 'z': {
          135  +					char const* str = (char const*)*(arg++);
          136  +					if (canPrint) {
          137  +						if (str == nullptr) buffer_pushs(buf, "<null>");
          138  +						else buffer_pushs(buf, str);
          139  +					}
          140  +					break;
          141  +				}
          142  +				case 's': {
          143  +					strp const* a = *(arg++);
          144  +					if (a -> ptr == nullptr) buffer_pushs(buf, "<null>");
          145  +					else buffer_push(buf, *a);
          146  +					break;
          147  +				}
          148  +				case 'i': case 'I':
          149  +				case 'u': case 'U': {
          150  +					bool neg=false; uintmax_t mag;
          151  +					if (n=='i') {
          152  +						intmax_t s = *((intmax_t*)*(arg++));
          153  +						if (s < 0) { mag = -s; neg = true; }
          154  +						else mag = s;
          155  +					} else mag = *((uintmax_t*)*(arg++));
          156  +					buffer_pushm(buf, mag, neg, base, n < 'a');
          157  +					break;
          158  +				}
          159  +				case 'f': case 'F': {// fraction
          160  +					fraction f = *((fraction*)*(arg++));
          161  +					buffer_pushm(buf, fractionNum(f), fractionNeg(f), base, n<'a');
          162  +					buffer_pushs(buf, "/");
          163  +					buffer_pushm(buf, fractionDenom(f), false, base, n<'a');
          164  +				} break;
          165  +				case 'x': case 'X': {// fixed
          166  +					fixed f = *((fixed*)*(arg++));
          167  +					bool neg = fixedVal(f) < 0;
          168  +					f = fixedAbs(f);
          169  +					buffer_pushm(buf, fixedIntPart(f), neg, base, n<'a');
          170  +					uint32_t fracpt = fixedFracPart(f);
          171  +					if (fracpt != 0) {
          172  +						intmax_t mul = base;
          173  +						/* find prec multiplicand */
          174  +						while (mul < fixedPrec(f)) {
          175  +							if (mul * base < mul) {
          176  +								buffer_pushs(buf, "<fixed-point precision error>");
          177  +								break;
          178  +							}
          179  +							mul *= base;
          180  +						}
          181  +						uintmax_t r = ((intmax_t)fracpt * mul*mul) / ((intmax_t)fixedPrec(f)*mul);
          182  +						buffer_pushs(buf, ".");
          183  +						buffer_pushm(buf, r, false, base, n<'a');
          184  +					}
          185  +				} break;
          186  +				case 'n': case 'N': {// inexact
          187  +				} break;
          188  +				case '~': {
          189  +					char const* subfmt = (char const*)*(arg++);
          190  +					say_arg* subargs = (say_arg*)*(arg++);
          191  +					saybuf(buf, dest, subfmt, subargs);
          192  +					break;
          193  +				}
          194  +			}
          195  +		} else goto push;
          196  +		continue;
          197  +
          198  +		push: if (canPrint) buffer_push(buf, (strp){1, &m});
          199  +	}
          200  +	return arg - args;
          201  +#	undef ansiclr
          202  +#	undef ansisw
          203  +#	undef pushcond
          204  +#	undef pushraw
          205  +}
          206  +
          207  +static inline void
          208  +sayprep(FILE* dest) {
          209  +#	ifndef _say_use_fd
          210  +		setvbuf(dest, nullptr, _IONBF, 0); // hax
          211  +#	endif
          212  +}
          213  +
          214  +void sayto(FILE* dest, char const* fmt, say_arg* args) {
          215  +	static _Thread_local buffer buf = {};
          216  +	if (buf.run == 0) buffer_init(&buf);
          217  +	             else buf.sz = 0;
          218  +
          219  +	saybuf(&buf, dest, fmt, args);
          220  +
          221  +#	ifdef _say_use_fd
          222  +		write(fileno(dest), buf.ptr, buf.sz);
          223  +#	else
          224  +		fprintf(dest, "%.*s", (int)buf.sz, buf.ptr);
          225  +#	endif
          226  +	buffer_crush(&buf, 2048);
          227  +}
          228  +
          229  +void say(char const* fmt, say_arg* args) {
          230  +	static bool stprep = false;
          231  +	if (!stprep) { stprep=true; sayprep(stderr); };
          232  +	sayto(stderr, fmt, args);
          233  +}

Added clib/say.h version [229d1e6ce9].

            1  +#pragma once
            2  +#include <stdio.h>
            3  +#include "type.h"
            4  +#include "buffer.h"
            5  +typedef void const* say_arg;
            6  +typedef typeof(say_arg[]) saylist;
            7  +size_t saybuf(buffer* buf, FILE* dest, const char* fmt, say_arg* args);
            8  +void sayto(FILE* dest, char const* fmt, say_arg* args);
            9  +void say(const char* fmt, say_arg* args);