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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "buffer.h"
#include <string.h>
#include <stdlib.h>

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 version [97b258e329].































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 version [f74e4bde0b].









































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "map.h"
#include <stdlib.h>

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 version [36b154fb5a].

























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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 version [84789a032f].



















































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#include "say.h"

#ifdef __has_include
#	if __has_include(<unistd.h>)
#		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 <unistd.h>
#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, "<null>");
						else buffer_pushs(buf, str);
					}
					break;
				}
				case 's': {
					strp const* a = *(arg++);
					if (a -> ptr == nullptr) buffer_pushs(buf, "<null>");
					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, "<fixed-point precision error>");
								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 version [229d1e6ce9].



















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
#pragma once
#include <stdio.h>
#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);