URI: 
       drw: minor improvement to the nomatches cache - dmenu - dynamic menu
  HTML git clone git://git.suckless.org/dmenu
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 7ab0cb5ef0e19352fc5d64ae0d57a5cf4540acbf
   DIR parent 0fe460dbd469a1d5b6a7140d0e1801935e4a923b
  HTML Author: NRK <nrk@disroot.org>
       Date:   Fri,  7 Jul 2023 17:00:42 +0600
       
       drw: minor improvement to the nomatches cache
       
       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.
       
       Diffstat:
         M dmenu.c                             |       1 -
         M drw.c                               |      23 ++++++++++++-----------
         M util.h                              |       1 +
       
       3 files changed, 13 insertions(+), 12 deletions(-)
       ---
   DIR diff --git a/dmenu.c b/dmenu.c
       @@ -22,7 +22,6 @@
        /* macros */
        #define INTERSECT(x,y,w,h,r)  (MAX(0, MIN((x)+(w),(r).x_org+(r).width)  - MAX((x),(r).x_org)) \
                                     * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
       -#define LENGTH(X)             (sizeof X / sizeof X[0])
        #define TEXTW(X)              (drw_fontset_getwidth(drw, (X)) + lrpad)
        
        /* enums */
   DIR diff --git a/drw.c b/drw.c
       @@ -238,8 +238,8 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
        int
        drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
        {
       -        int i, ty, ellipsis_x = 0;
       -        unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
       +        int ty, ellipsis_x = 0;
       +        unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
                XftDraw *d = NULL;
                Fnt *usedfont, *curfont, *nextfont;
                int utf8strlen, utf8charlen, render = x || y || w || h;
       @@ -251,9 +251,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
                XftResult result;
                int charexists = 0, overflow = 0;
                /* keep track of a couple codepoints for which we have no match. */
       -        enum { nomatches_len = 64 };
       -        static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches;
       -        static unsigned int ellipsis_width = 0;
       +        static unsigned int nomatches[128], ellipsis_width;
        
                if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
                        return 0;
       @@ -338,11 +336,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
                                 * character must be drawn. */
                                charexists = 1;
        
       -                        for (i = 0; i < nomatches_len; ++i) {
       -                                /* avoid calling XftFontMatch if we know we won't find a match */
       -                                if (utf8codepoint == nomatches.codepoint[i])
       -                                        goto no_match;
       -                        }
       +                        hash = (unsigned int)utf8codepoint;
       +                        hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
       +                        hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
       +                        h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
       +                        h1 = (hash >> 17) % LENGTH(nomatches);
       +                        /* avoid expensive XftFontMatch call when we know we won't find a match */
       +                        if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
       +                                goto no_match;
        
                                fccharset = FcCharSetCreate();
                                FcCharSetAddChar(fccharset, utf8codepoint);
       @@ -371,7 +372,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
                                                curfont->next = usedfont;
                                        } else {
                                                xfont_free(usedfont);
       -                                        nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
       +                                        nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
        no_match:
                                                usedfont = drw->fonts;
                                        }
   DIR diff --git a/util.h b/util.h
       @@ -3,6 +3,7 @@
        #define MAX(A, B)               ((A) > (B) ? (A) : (B))
        #define MIN(A, B)               ((A) < (B) ? (A) : (B))
        #define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))
       +#define LENGTH(X)               (sizeof (X) / sizeof (X)[0])
        
        void die(const char *fmt, ...);
        void *ecalloc(size_t nmemb, size_t size);