drw: minor improvement to the nomatches cache 7ab0cb5e
1. use `unsigned int` to store the codepoints, this avoids waste on
   common case where `long` is 64bits. and POSIX guarantees `int` to be
   at least 32bits so there's no risk of truncation.
2. since switching to `unsigned int` cuts down the memory requirement by
   half, double the cache size from 64 to 128.
3. instead of a linear search, use a simple hash-table for O(1) lookups.
NRK · 2023-07-07 17:00 3 file(s) · +13 −12
dmenu.c +0 −1
22 22
/* macros */
23 23
#define INTERSECT(x,y,w,h,r)  (MAX(0, MIN((x)+(w),(r).x_org+(r).width)  - MAX((x),(r).x_org)) \
24 24
                             * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
25 -
#define LENGTH(X)             (sizeof X / sizeof X[0])
26 25
#define TEXTW(X)              (drw_fontset_getwidth(drw, (X)) + lrpad)
27 26
28 27
/* enums */
drw.c +12 −11
238 238
int
239 239
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
240 240
{
241 -
	int i, ty, ellipsis_x = 0;
242 -
	unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
241 +
	int ty, ellipsis_x = 0;
242 +
	unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
243 243
	XftDraw *d = NULL;
244 244
	Fnt *usedfont, *curfont, *nextfont;
245 245
	int utf8strlen, utf8charlen, render = x || y || w || h;
251 251
	XftResult result;
252 252
	int charexists = 0, overflow = 0;
253 253
	/* keep track of a couple codepoints for which we have no match. */
254 -
	enum { nomatches_len = 64 };
255 -
	static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
256 -
	static unsigned int ellipsis_width = 0;
254 +
	static unsigned int nomatches[128], ellipsis_width;
257 255
258 256
	if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
259 257
		return 0;
338 336
			 * character must be drawn. */
339 337
			charexists = 1;
340 338
341 -
			for (i = 0; i < nomatches_len; ++i) {
342 -
				/* avoid calling XftFontMatch if we know we won't find a match */
343 -
				if (utf8codepoint == nomatches.codepoint[i])
344 -
					goto no_match;
345 -
			}
339 +
			hash = (unsigned int)utf8codepoint;
340 +
			hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
341 +
			hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
342 +
			h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
343 +
			h1 = (hash >> 17) % LENGTH(nomatches);
344 +
			/* avoid expensive XftFontMatch call when we know we won't find a match */
345 +
			if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
346 +
				goto no_match;
346 347
347 348
			fccharset = FcCharSetCreate();
348 349
			FcCharSetAddChar(fccharset, utf8codepoint);
371 372
					curfont->next = usedfont;
372 373
				} else {
373 374
					xfont_free(usedfont);
374 -
					nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
375 +
					nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
375 376
no_match:
376 377
					usedfont = drw->fonts;
377 378
				}
util.h +1 −0
3 3
#define MAX(A, B)               ((A) > (B) ? (A) : (B))
4 4
#define MIN(A, B)               ((A) < (B) ? (A) : (B))
5 5
#define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))
6 +
#define LENGTH(X)               (sizeof (X) / sizeof (X)[0])
6 7
7 8
void die(const char *fmt, ...);
8 9
void *ecalloc(size_t nmemb, size_t size);