URI:
       tImprove text rendering; combine with actual widgets; add cleanup functions - ltkx - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltkx.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 323ceb5cbc54b4d70002a4f4e6e5ae9d308793c4
   DIR parent a06777c1797439f43e6b8a1026a53c5e6ac9298e
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sun, 12 Aug 2018 19:49:58 +0200
       
       Improve text rendering; combine with actual widgets; add cleanup functions
       
       Yeah, it's probably probably still pretty bad. Oh well...
       
       Diffstat:
         M Makefile                            |       6 +++---
         A NOTES                               |       6 ++++++
         M README.md                           |       2 +-
         M button.c                            |      84 ++++++++++++++++++-------------
         M button.h                            |      19 +++++++++----------
         M grid.c                              |       2 +-
         M grid.h                              |       2 +-
         A khash.h                             |     627 +++++++++++++++++++++++++++++++
         M ltk.c                               |      40 +++++++++++++++++++-------------
         M ltk.h                               |      16 ++++++++++------
         M test1.c                             |      28 +++++++++-------------------
         A text-hb.c                           |     399 ++++++++++++++++++++++++++++++
         A text-hb.h                           |     126 +++++++++++++++++++++++++++++++
         M text.c                              |       2 +-
         M text/Makefile                       |       2 +-
         M text/text-hb.ubernew.c              |       2 ++
         A text/text-hb.uberubernew.c          |     473 ++++++++++++++++++++++++++++++
         D uthash.h                            |    1074 -------------------------------
       
       18 files changed, 1741 insertions(+), 1169 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       t@@ -1,7 +1,7 @@
       -LIBS = -lX11 -lm -L/usr/X11R6/lib
       +LIBS = -lm `pkg-config --libs x11 harfbuzz`
        STD = -std=c99
       -FLAGS = -g -w -fcommon -Wall -Werror -Wextra -I/usr/X11R6/include #-pedantic
       -CFILES = text.c ltk.c ini.c grid.c button.c test1.c
       +FLAGS = -g -w -fcommon -Wall -Werror -Wextra `pkg-config --cflags x11 harfbuzz`#-pedantic
       +CFILES = text-hb.c ltk.c ini.c grid.c button.c test1.c
        
        all: test1.c
                gcc $(STD) $(FLAGS) $(LIBS) $(CFILES) -o test
   DIR diff --git a/NOTES b/NOTES
       t@@ -0,0 +1,6 @@
       +Maybe use XCheckWindowEvent? - this would eliminate need for hash (could just store window ids in array)
       +-> Is it okay to block until event found? Loading bars, etc. should still work since control is in another
       +   function (or even thread?) which has to call ltk_update_progress_bar or something similar.
       +HarfBuzz - need to add option to LTK functions to allow user to choose language system manually
       +LtkTextSegment - just use array instead of linked list - just need to get actual number of glyphs in advance from hb
       +Add void* to LtkWidget to hold specific widget instead of doing weird casts to simulate OOP
   DIR diff --git a/README.md b/README.md
       t@@ -4,7 +4,7 @@ This is work in progress. Please do not attempt to actually use any of the code.
        
        ## Licenses of Other Libraries Used
        
       -[uthash](https://troydhanson.github.io/uthash/) by Troy D. Hanson: [BSD Revised](https://troydhanson.github.io/uthash/license.html)
       +[khash](https://github.com/attractivechaos/klib) by Attractive Chaos: [MIT](https://github.com/attractivechaos/klib/blob/31cb0301482a762af0adbfc85dff1632cecc2bb4/khash.h#L3)
        
        [inih](https://github.com/benhoyt/inih) by Ben Hoyt: [New BSD](https://github.com/benhoyt/inih/blob/master/LICENSE.txt)
        
   DIR diff --git a/button.c b/button.c
       t@@ -1,6 +1,6 @@
        /*
         * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
       t@@ -22,6 +22,8 @@
         */
        
        #include "ltk.h"
       +#include "text-hb.h"
       +#include "button.h"
        
        void ltk_button_ini_handler(LtkTheme *theme, const char *prop, const char *value)
        {
       t@@ -64,22 +66,6 @@ void ltk_button_ini_handler(LtkTheme *theme, const char *prop, const char *value
                }
        }
        
       -/* FIXME: this is probably really slow and there is probably a really simple alternative */
       -unsigned long ltk_blend_pixel(XColor fg, XColor bg, double a)
       -{
       -        XColor blended;
       -        if (a == 1.0)
       -                return fg.pixel;
       -        else if (a == 0.0)
       -                return bg.pixel;
       -        blended.red = (int)((fg.red - bg.red) * a + bg.red);
       -        blended.green = (int)((fg.green - bg.green) * a + bg.green);
       -        blended.blue = (int)((fg.blue - bg.blue) * a + bg.blue);
       -        XAllocColor(ltk_global->display, ltk_global->colormap, &blended);
       -
       -        return blended.pixel;
       -}
       -
        void ltk_draw_button(LtkButton *button)
        {
                LtkButtonTheme *theme = ltk_global->theme->button;
       t@@ -87,52 +73,67 @@ void ltk_draw_button(LtkButton *button)
                LtkRect rect = button->widget.rect;
                XColor border;
                XColor fill;
       +        XImage *img;
                switch (button->widget.state) {
                case LTK_NORMAL:
                        border = theme->border;
                        fill = theme->fill;
       +                img = button->text;
                        break;
                case LTK_HOVERACTIVE:
                case LTK_HOVER:
                        border = theme->border_hover;
                        fill = theme->fill_hover;
       +                img = button->text_hover;
                        break;
                case LTK_PRESSED:
                        border = theme->border_pressed;
                        fill = theme->fill_pressed;
       +                img = button->text_pressed;
                        break;
                case LTK_ACTIVE:
                        border = theme->border_active;
                        fill = theme->fill_active;
       +                img = button->text_active;
                        break;
                case LTK_DISABLED:
                        border = theme->border_disabled;
                        fill = theme->fill_disabled;
       +                img = button->text_disabled;
                        break;
                default:
                        ltk_fatal("No style found for button!\n");
                }
                XSetForeground(ltk_global->display, window->gc, fill.pixel);
       -        XFillRectangle(ltk_global->display, window->xwindow, window->gc,
       -                       rect.x, rect.y, rect.w, rect.h);
       +        XFillRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
       +        /* FIXME: Why did I do this? */
                if (theme->border_width < 1) return;
                XSetForeground(ltk_global->display, window->gc, border.pixel);
       -        XSetLineAttributes(ltk_global->display, window->gc, theme->border_width,
       -                           LineSolid, CapButt, JoinMiter);
       -        XDrawRectangle(ltk_global->display, window->xwindow, window->gc,
       -                       rect.x, rect.y, rect.w, rect.h);
       -        int height = theme->font_size;
       -        int width = button->text_width;
       -        int i, j;
       -        for (i = 0; i < height; i++)
       -        {
       -                for (j = 0; j < width; j++)
       -                {
       -//                        XSetForeground(ltk_global->display, window->gc, (button->text_bitmap[i * width + j] / 255.0) * theme->text_color.pixel + ((255 - button->text_bitmap[i * width + j]) / 255.0) * fill.pixel);
       -                        XSetForeground(ltk_global->display, window->gc, ltk_blend_pixel(theme->text_color, fill, (button->text_bitmap[i * width + j] / 255.0)));
       -                        XDrawPoint(ltk_global->display, window->xwindow, window->gc, rect.x + j, rect.y + i);
       +        XSetLineAttributes(ltk_global->display, window->gc, theme->border_width, LineSolid, CapButt, JoinMiter);
       +        XDrawRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
       +        if (!img) {
       +                img = ltk_render_text_segment(button->ts, ltk_global->display, window->xwindow, window->gc, ltk_global->colormap, theme->text_color, fill);
       +                /* FIXME: any nicer way to do this? */
       +                switch (button->widget.state) {
       +                        case LTK_NORMAL:
       +                                button->text = img;
       +                                break;
       +                        case LTK_HOVERACTIVE:
       +                        case LTK_HOVER:
       +                                button->text_hover = img;
       +                                break;
       +                        case LTK_PRESSED:
       +                                button->text_pressed = img;
       +                                break;
       +                        case LTK_ACTIVE:
       +                                button->text_active = img;
       +                                break;
       +                        case LTK_DISABLED:
       +                                button->text_disabled = img;
       +                                break;
                        }
                }
       +        XPutImage(ltk_global->display, window->xwindow, window->gc, img, 0, 0, rect.x + theme->border_width, rect.y + theme->border_width, button->ts->w, button->ts->h);
        }
        
        LtkButton *ltk_create_button(LtkWindow *window, const char *text,
       t@@ -149,8 +150,14 @@ LtkButton *ltk_create_button(LtkWindow *window, const char *text,
        
                button->callback = callback;
                LtkTheme *theme = ltk_global->theme;
       -        button->text_width = ltk_text_width(text, theme->window->font, theme->button->font_size);
       -        button->text_bitmap = ltk_render_text(text, theme->window->font, theme->button->font_size, button->text_width);
       +        button->ts = ltk_create_text_segment(ltk_global->tm, text, ltk_global->default_font, theme->button->font_size);
       +        button->widget.rect.w = button->ts->w + theme->button->border_width * 2;
       +        button->widget.rect.h = button->ts->h + theme->button->border_width * 2;
       +        button->text = NULL;
       +        button->text_pressed = NULL;
       +        button->text_hover = NULL;
       +        button->text_disabled = NULL;
       +        button->text_active = NULL;
        
                return button;
        }
       t@@ -161,7 +168,12 @@ void ltk_destroy_button(void *widget)
                if (!button) {
                        printf("WARNING: Tried to destroy NULL button.\n");
                }
       -        free(button->text_bitmap);
       +        if (button->text) XDestroyImage(button->text);
       +        if (button->text_hover) XDestroyImage(button->text_hover);
       +        if (button->text_pressed) XDestroyImage(button->text_pressed);
       +        if (button->text_active) XDestroyImage(button->text_active);
       +        if (button->text_disabled) XDestroyImage(button->text_disabled);
       +        ltk_destroy_text_segment(button->ts);
                free(button);
        }
        
   DIR diff --git a/button.h b/button.h
       t@@ -1,6 +1,6 @@
        /*
         * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
       t@@ -24,18 +24,17 @@
        #ifndef _LTK_BUTTON_H_
        #define _LTK_BUTTON_H_
        
       +#include "text-hb.h"
       +
        typedef struct {
                LtkWidget widget;
                void (*callback) (void);
       -
       -        int text_width;
       -        char *text_raw;
       -        unsigned char *text_bitmap;
       -        Pixmap text;
       -        Pixmap text_hover;
       -        Pixmap text_pressed;
       -        Pixmap text_active;
       -        Pixmap text_disabled;
       +        LtkTextSegment *ts;
       +        XImage *text;
       +        XImage *text_hover;
       +        XImage *text_pressed;
       +        XImage *text_active;
       +        XImage *text_disabled;
        } LtkButton;
        
        typedef struct LtkButtonTheme {
   DIR diff --git a/grid.c b/grid.c
       t@@ -1,6 +1,6 @@
        /*
         * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
   DIR diff --git a/grid.h b/grid.h
       t@@ -1,6 +1,6 @@
        /*
         * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
   DIR diff --git a/khash.h b/khash.h
       t@@ -0,0 +1,627 @@
       +/* The MIT License
       +
       +   Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
       +
       +   Permission is hereby granted, free of charge, to any person obtaining
       +   a copy of this software and associated documentation files (the
       +   "Software"), to deal in the Software without restriction, including
       +   without limitation the rights to use, copy, modify, merge, publish,
       +   distribute, sublicense, and/or sell copies of the Software, and to
       +   permit persons to whom the Software is furnished to do so, subject to
       +   the following conditions:
       +
       +   The above copyright notice and this permission notice shall be
       +   included in all copies or substantial portions of the Software.
       +
       +   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
       +   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
       +   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       +   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +   SOFTWARE.
       +*/
       +
       +/*
       +  An example:
       +
       +#include "khash.h"
       +KHASH_MAP_INIT_INT(32, char)
       +int main() {
       +        int ret, is_missing;
       +        khiter_t k;
       +        khash_t(32) *h = kh_init(32);
       +        k = kh_put(32, h, 5, &ret);
       +        kh_value(h, k) = 10;
       +        k = kh_get(32, h, 10);
       +        is_missing = (k == kh_end(h));
       +        k = kh_get(32, h, 5);
       +        kh_del(32, h, k);
       +        for (k = kh_begin(h); k != kh_end(h); ++k)
       +                if (kh_exist(h, k)) kh_value(h, k) = 1;
       +        kh_destroy(32, h);
       +        return 0;
       +}
       +*/
       +
       +/*
       +  2013-05-02 (0.2.8):
       +
       +        * Use quadratic probing. When the capacity is power of 2, stepping function
       +          i*(i+1)/2 guarantees to traverse each bucket. It is better than double
       +          hashing on cache performance and is more robust than linear probing.
       +
       +          In theory, double hashing should be more robust than quadratic probing.
       +          However, my implementation is probably not for large hash tables, because
       +          the second hash function is closely tied to the first hash function,
       +          which reduce the effectiveness of double hashing.
       +
       +        Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
       +
       +  2011-12-29 (0.2.7):
       +
       +    * Minor code clean up; no actual effect.
       +
       +  2011-09-16 (0.2.6):
       +
       +        * The capacity is a power of 2. This seems to dramatically improve the
       +          speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
       +
       +           - http://code.google.com/p/ulib/
       +           - http://nothings.org/computer/judy/
       +
       +        * Allow to optionally use linear probing which usually has better
       +          performance for random input. Double hashing is still the default as it
       +          is more robust to certain non-random input.
       +
       +        * Added Wang's integer hash function (not used by default). This hash
       +          function is more robust to certain non-random input.
       +
       +  2011-02-14 (0.2.5):
       +
       +    * Allow to declare global functions.
       +
       +  2009-09-26 (0.2.4):
       +
       +    * Improve portability
       +
       +  2008-09-19 (0.2.3):
       +
       +        * Corrected the example
       +        * Improved interfaces
       +
       +  2008-09-11 (0.2.2):
       +
       +        * Improved speed a little in kh_put()
       +
       +  2008-09-10 (0.2.1):
       +
       +        * Added kh_clear()
       +        * Fixed a compiling error
       +
       +  2008-09-02 (0.2.0):
       +
       +        * Changed to token concatenation which increases flexibility.
       +
       +  2008-08-31 (0.1.2):
       +
       +        * Fixed a bug in kh_get(), which has not been tested previously.
       +
       +  2008-08-31 (0.1.1):
       +
       +        * Added destructor
       +*/
       +
       +
       +#ifndef __AC_KHASH_H
       +#define __AC_KHASH_H
       +
       +/*!
       +  @header
       +
       +  Generic hash table library.
       + */
       +
       +#define AC_VERSION_KHASH_H "0.2.8"
       +
       +#include <stdlib.h>
       +#include <string.h>
       +#include <limits.h>
       +
       +/* compiler specific configuration */
       +
       +#if UINT_MAX == 0xffffffffu
       +typedef unsigned int khint32_t;
       +#elif ULONG_MAX == 0xffffffffu
       +typedef unsigned long khint32_t;
       +#endif
       +
       +#if ULONG_MAX == ULLONG_MAX
       +typedef unsigned long khint64_t;
       +#else
       +typedef unsigned long long khint64_t;
       +#endif
       +
       +#ifndef kh_inline
       +#ifdef _MSC_VER
       +#define kh_inline __inline
       +#else
       +#define kh_inline inline
       +#endif
       +#endif /* kh_inline */
       +
       +#ifndef klib_unused
       +#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
       +#define klib_unused __attribute__ ((__unused__))
       +#else
       +#define klib_unused
       +#endif
       +#endif /* klib_unused */
       +
       +typedef khint32_t khint_t;
       +typedef khint_t khiter_t;
       +
       +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
       +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
       +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
       +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
       +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
       +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
       +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
       +
       +#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
       +
       +#ifndef kroundup32
       +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
       +#endif
       +
       +#ifndef kcalloc
       +#define kcalloc(N,Z) calloc(N,Z)
       +#endif
       +#ifndef kmalloc
       +#define kmalloc(Z) malloc(Z)
       +#endif
       +#ifndef krealloc
       +#define krealloc(P,Z) realloc(P,Z)
       +#endif
       +#ifndef kfree
       +#define kfree(P) free(P)
       +#endif
       +
       +static const double __ac_HASH_UPPER = 0.77;
       +
       +#define __KHASH_TYPE(name, khkey_t, khval_t) \
       +        typedef struct kh_##name##_s { \
       +                khint_t n_buckets, size, n_occupied, upper_bound; \
       +                khint32_t *flags; \
       +                khkey_t *keys; \
       +                khval_t *vals; \
       +        } kh_##name##_t;
       +
       +#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)                                                 \
       +        extern kh_##name##_t *kh_init_##name(void);                                                        \
       +        extern void kh_destroy_##name(kh_##name##_t *h);                                        \
       +        extern void kh_clear_##name(kh_##name##_t *h);                                                \
       +        extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key);         \
       +        extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
       +        extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
       +        extern void kh_del_##name(kh_##name##_t *h, khint_t x);
       +
       +#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
       +        SCOPE kh_##name##_t *kh_init_##name(void) {                                                        \
       +                return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t));                \
       +        }                                                                                                                                        \
       +        SCOPE void kh_destroy_##name(kh_##name##_t *h)                                                \
       +        {                                                                                                                                        \
       +                if (h) {                                                                                                                \
       +                        kfree((void *)h->keys); kfree(h->flags);                                        \
       +                        kfree((void *)h->vals);                                                                                \
       +                        kfree(h);                                                                                                        \
       +                }                                                                                                                                \
       +        }                                                                                                                                        \
       +        SCOPE void kh_clear_##name(kh_##name##_t *h)                                                \
       +        {                                                                                                                                        \
       +                if (h && h->flags) {                                                                                        \
       +                        memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
       +                        h->size = h->n_occupied = 0;                                                                \
       +                }                                                                                                                                \
       +        }                                                                                                                                        \
       +        SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key)         \
       +        {                                                                                                                                        \
       +                if (h->n_buckets) {                                                                                                \
       +                        khint_t k, i, last, mask, step = 0; \
       +                        mask = h->n_buckets - 1;                                                                        \
       +                        k = __hash_func(key); i = k & mask;                                                        \
       +                        last = i; \
       +                        while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
       +                                i = (i + (++step)) & mask; \
       +                                if (i == last) return h->n_buckets;                                                \
       +                        }                                                                                                                        \
       +                        return __ac_iseither(h->flags, i)? h->n_buckets : i;                \
       +                } else return 0;                                                                                                \
       +        }                                                                                                                                        \
       +        SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
       +        { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
       +                khint32_t *new_flags = 0;                                                                                \
       +                khint_t j = 1;                                                                                                        \
       +                {                                                                                                                                \
       +                        kroundup32(new_n_buckets);                                                                         \
       +                        if (new_n_buckets < 4) new_n_buckets = 4;                                        \
       +                        if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0;        /* requested size is too small */ \
       +                        else { /* hash table size to be changed (shrink or expand); rehash */ \
       +                                new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t));        \
       +                                if (!new_flags) return -1;                                                                \
       +                                memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
       +                                if (h->n_buckets < new_n_buckets) {        /* expand */                \
       +                                        khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
       +                                        if (!new_keys) { kfree(new_flags); return -1; }                \
       +                                        h->keys = new_keys;                                                                        \
       +                                        if (kh_is_map) {                                                                        \
       +                                                khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
       +                                                if (!new_vals) { kfree(new_flags); return -1; }        \
       +                                                h->vals = new_vals;                                                                \
       +                                        }                                                                                                        \
       +                                } /* otherwise shrink */                                                                \
       +                        }                                                                                                                        \
       +                }                                                                                                                                \
       +                if (j) { /* rehashing is needed */                                                                \
       +                        for (j = 0; j != h->n_buckets; ++j) {                                                \
       +                                if (__ac_iseither(h->flags, j) == 0) {                                        \
       +                                        khkey_t key = h->keys[j];                                                        \
       +                                        khval_t val;                                                                                \
       +                                        khint_t new_mask;                                                                        \
       +                                        new_mask = new_n_buckets - 1;                                                 \
       +                                        if (kh_is_map) val = h->vals[j];                                        \
       +                                        __ac_set_isdel_true(h->flags, j);                                        \
       +                                        while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
       +                                                khint_t k, i, step = 0; \
       +                                                k = __hash_func(key);                                                        \
       +                                                i = k & new_mask;                                                                \
       +                                                while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
       +                                                __ac_set_isempty_false(new_flags, i);                        \
       +                                                if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
       +                                                        { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
       +                                                        if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
       +                                                        __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
       +                                                } else { /* write the element and jump out of the loop */ \
       +                                                        h->keys[i] = key;                                                        \
       +                                                        if (kh_is_map) h->vals[i] = val;                        \
       +                                                        break;                                                                                \
       +                                                }                                                                                                \
       +                                        }                                                                                                        \
       +                                }                                                                                                                \
       +                        }                                                                                                                        \
       +                        if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
       +                                h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
       +                                if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
       +                        }                                                                                                                        \
       +                        kfree(h->flags); /* free the working space */                                \
       +                        h->flags = new_flags;                                                                                \
       +                        h->n_buckets = new_n_buckets;                                                                \
       +                        h->n_occupied = h->size;                                                                        \
       +                        h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
       +                }                                                                                                                                \
       +                return 0;                                                                                                                \
       +        }                                                                                                                                        \
       +        SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
       +        {                                                                                                                                        \
       +                khint_t x;                                                                                                                \
       +                if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
       +                        if (h->n_buckets > (h->size<<1)) {                                                        \
       +                                if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
       +                                        *ret = -1; return h->n_buckets;                                                \
       +                                }                                                                                                                \
       +                        } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
       +                                *ret = -1; return h->n_buckets;                                                        \
       +                        }                                                                                                                        \
       +                } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
       +                {                                                                                                                                \
       +                        khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
       +                        x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
       +                        if (__ac_isempty(h->flags, i)) x = i; /* for speed up */        \
       +                        else {                                                                                                                \
       +                                last = i; \
       +                                while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
       +                                        if (__ac_isdel(h->flags, i)) site = i;                                \
       +                                        i = (i + (++step)) & mask; \
       +                                        if (i == last) { x = site; break; }                                        \
       +                                }                                                                                                                \
       +                                if (x == h->n_buckets) {                                                                \
       +                                        if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
       +                                        else x = i;                                                                                        \
       +                                }                                                                                                                \
       +                        }                                                                                                                        \
       +                }                                                                                                                                \
       +                if (__ac_isempty(h->flags, x)) { /* not present at all */                \
       +                        h->keys[x] = key;                                                                                        \
       +                        __ac_set_isboth_false(h->flags, x);                                                        \
       +                        ++h->size; ++h->n_occupied;                                                                        \
       +                        *ret = 1;                                                                                                        \
       +                } else if (__ac_isdel(h->flags, x)) { /* deleted */                                \
       +                        h->keys[x] = key;                                                                                        \
       +                        __ac_set_isboth_false(h->flags, x);                                                        \
       +                        ++h->size;                                                                                                        \
       +                        *ret = 2;                                                                                                        \
       +                } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
       +                return x;                                                                                                                \
       +        }                                                                                                                                        \
       +        SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x)                                \
       +        {                                                                                                                                        \
       +                if (x != h->n_buckets && !__ac_iseither(h->flags, x)) {                        \
       +                        __ac_set_isdel_true(h->flags, x);                                                        \
       +                        --h->size;                                                                                                        \
       +                }                                                                                                                                \
       +        }
       +
       +#define KHASH_DECLARE(name, khkey_t, khval_t)                                                         \
       +        __KHASH_TYPE(name, khkey_t, khval_t)                                                                 \
       +        __KHASH_PROTOTYPES(name, khkey_t, khval_t)
       +
       +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
       +        __KHASH_TYPE(name, khkey_t, khval_t)                                                                 \
       +        __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
       +
       +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
       +        KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
       +
       +/* --- BEGIN OF HASH FUNCTIONS --- */
       +
       +/*! @function
       +  @abstract     Integer hash function
       +  @param  key   The integer [khint32_t]
       +  @return       The hash value [khint_t]
       + */
       +#define kh_int_hash_func(key) (khint32_t)(key)
       +/*! @function
       +  @abstract     Integer comparison function
       + */
       +#define kh_int_hash_equal(a, b) ((a) == (b))
       +/*! @function
       +  @abstract     64-bit integer hash function
       +  @param  key   The integer [khint64_t]
       +  @return       The hash value [khint_t]
       + */
       +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
       +/*! @function
       +  @abstract     64-bit integer comparison function
       + */
       +#define kh_int64_hash_equal(a, b) ((a) == (b))
       +/*! @function
       +  @abstract     const char* hash function
       +  @param  s     Pointer to a null terminated string
       +  @return       The hash value
       + */
       +static kh_inline khint_t __ac_X31_hash_string(const char *s)
       +{
       +        khint_t h = (khint_t)*s;
       +        if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
       +        return h;
       +}
       +/*! @function
       +  @abstract     Another interface to const char* hash function
       +  @param  key   Pointer to a null terminated string [const char*]
       +  @return       The hash value [khint_t]
       + */
       +#define kh_str_hash_func(key) __ac_X31_hash_string(key)
       +/*! @function
       +  @abstract     Const char* comparison function
       + */
       +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
       +
       +static kh_inline khint_t __ac_Wang_hash(khint_t key)
       +{
       +    key += ~(key << 15);
       +    key ^=  (key >> 10);
       +    key +=  (key << 3);
       +    key ^=  (key >> 6);
       +    key += ~(key << 11);
       +    key ^=  (key >> 16);
       +    return key;
       +}
       +#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
       +
       +/* --- END OF HASH FUNCTIONS --- */
       +
       +/* Other convenient macros... */
       +
       +/*!
       +  @abstract Type of the hash table.
       +  @param  name  Name of the hash table [symbol]
       + */
       +#define khash_t(name) kh_##name##_t
       +
       +/*! @function
       +  @abstract     Initiate a hash table.
       +  @param  name  Name of the hash table [symbol]
       +  @return       Pointer to the hash table [khash_t(name)*]
       + */
       +#define kh_init(name) kh_init_##name()
       +
       +/*! @function
       +  @abstract     Destroy a hash table.
       +  @param  name  Name of the hash table [symbol]
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       + */
       +#define kh_destroy(name, h) kh_destroy_##name(h)
       +
       +/*! @function
       +  @abstract     Reset a hash table without deallocating memory.
       +  @param  name  Name of the hash table [symbol]
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       + */
       +#define kh_clear(name, h) kh_clear_##name(h)
       +
       +/*! @function
       +  @abstract     Resize a hash table.
       +  @param  name  Name of the hash table [symbol]
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @param  s     New size [khint_t]
       + */
       +#define kh_resize(name, h, s) kh_resize_##name(h, s)
       +
       +/*! @function
       +  @abstract     Insert a key to the hash table.
       +  @param  name  Name of the hash table [symbol]
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @param  k     Key [type of keys]
       +  @param  r     Extra return code: -1 if the operation failed;
       +                0 if the key is present in the hash table;
       +                1 if the bucket is empty (never used); 2 if the element in
       +                                the bucket has been deleted [int*]
       +  @return       Iterator to the inserted element [khint_t]
       + */
       +#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
       +
       +/*! @function
       +  @abstract     Retrieve a key from the hash table.
       +  @param  name  Name of the hash table [symbol]
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @param  k     Key [type of keys]
       +  @return       Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
       + */
       +#define kh_get(name, h, k) kh_get_##name(h, k)
       +
       +/*! @function
       +  @abstract     Remove a key from the hash table.
       +  @param  name  Name of the hash table [symbol]
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @param  k     Iterator to the element to be deleted [khint_t]
       + */
       +#define kh_del(name, h, k) kh_del_##name(h, k)
       +
       +/*! @function
       +  @abstract     Test whether a bucket contains data.
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @param  x     Iterator to the bucket [khint_t]
       +  @return       1 if containing data; 0 otherwise [int]
       + */
       +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
       +
       +/*! @function
       +  @abstract     Get key given an iterator
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @param  x     Iterator to the bucket [khint_t]
       +  @return       Key [type of keys]
       + */
       +#define kh_key(h, x) ((h)->keys[x])
       +
       +/*! @function
       +  @abstract     Get value given an iterator
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @param  x     Iterator to the bucket [khint_t]
       +  @return       Value [type of values]
       +  @discussion   For hash sets, calling this results in segfault.
       + */
       +#define kh_val(h, x) ((h)->vals[x])
       +
       +/*! @function
       +  @abstract     Alias of kh_val()
       + */
       +#define kh_value(h, x) ((h)->vals[x])
       +
       +/*! @function
       +  @abstract     Get the start iterator
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @return       The start iterator [khint_t]
       + */
       +#define kh_begin(h) (khint_t)(0)
       +
       +/*! @function
       +  @abstract     Get the end iterator
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @return       The end iterator [khint_t]
       + */
       +#define kh_end(h) ((h)->n_buckets)
       +
       +/*! @function
       +  @abstract     Get the number of elements in the hash table
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @return       Number of elements in the hash table [khint_t]
       + */
       +#define kh_size(h) ((h)->size)
       +
       +/*! @function
       +  @abstract     Get the number of buckets in the hash table
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @return       Number of buckets in the hash table [khint_t]
       + */
       +#define kh_n_buckets(h) ((h)->n_buckets)
       +
       +/*! @function
       +  @abstract     Iterate over the entries in the hash table
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @param  kvar  Variable to which key will be assigned
       +  @param  vvar  Variable to which value will be assigned
       +  @param  code  Block of code to execute
       + */
       +#define kh_foreach(h, kvar, vvar, code) { khint_t __i;                \
       +        for (__i = kh_begin(h); __i != kh_end(h); ++__i) {                \
       +                if (!kh_exist(h,__i)) continue;                                                \
       +                (kvar) = kh_key(h,__i);                                                                \
       +                (vvar) = kh_val(h,__i);                                                                \
       +                code;                                                                                                \
       +        } }
       +
       +/*! @function
       +  @abstract     Iterate over the values in the hash table
       +  @param  h     Pointer to the hash table [khash_t(name)*]
       +  @param  vvar  Variable to which value will be assigned
       +  @param  code  Block of code to execute
       + */
       +#define kh_foreach_value(h, vvar, code) { khint_t __i;                \
       +        for (__i = kh_begin(h); __i != kh_end(h); ++__i) {                \
       +                if (!kh_exist(h,__i)) continue;                                                \
       +                (vvar) = kh_val(h,__i);                                                                \
       +                code;                                                                                                \
       +        } }
       +
       +/* More conenient interfaces */
       +
       +/*! @function
       +  @abstract     Instantiate a hash set containing integer keys
       +  @param  name  Name of the hash table [symbol]
       + */
       +#define KHASH_SET_INIT_INT(name)                                                                                \
       +        KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
       +
       +/*! @function
       +  @abstract     Instantiate a hash map containing integer keys
       +  @param  name  Name of the hash table [symbol]
       +  @param  khval_t  Type of values [type]
       + */
       +#define KHASH_MAP_INIT_INT(name, khval_t)                                                                \
       +        KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
       +
       +/*! @function
       +  @abstract     Instantiate a hash map containing 64-bit integer keys
       +  @param  name  Name of the hash table [symbol]
       + */
       +#define KHASH_SET_INIT_INT64(name)                                                                                \
       +        KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
       +
       +/*! @function
       +  @abstract     Instantiate a hash map containing 64-bit integer keys
       +  @param  name  Name of the hash table [symbol]
       +  @param  khval_t  Type of values [type]
       + */
       +#define KHASH_MAP_INIT_INT64(name, khval_t)                                                                \
       +        KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
       +
       +typedef const char *kh_cstr_t;
       +/*! @function
       +  @abstract     Instantiate a hash map containing const char* keys
       +  @param  name  Name of the hash table [symbol]
       + */
       +#define KHASH_SET_INIT_STR(name)                                                                                \
       +        KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
       +
       +/*! @function
       +  @abstract     Instantiate a hash map containing const char* keys
       +  @param  name  Name of the hash table [symbol]
       +  @param  khval_t  Type of values [type]
       + */
       +#define KHASH_MAP_INIT_STR(name, khval_t)                                                                \
       +        KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
       +
       +#endif /* __AC_KHASH_H */
   DIR diff --git a/ltk.c b/ltk.c
       t@@ -1,6 +1,6 @@
        /*
         * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
       t@@ -21,8 +21,8 @@
         * SOFTWARE.
         */
        
       +#include "text-hb.h"
        #include "ltk.h"
       -#include "text.h"
        
        void ltk_init(const char *theme_path)
        {
       t@@ -32,19 +32,24 @@ void ltk_init(const char *theme_path)
                ltk->screen = DefaultScreen(ltk->display);
                ltk->colormap = DefaultColormap(ltk->display, ltk->screen);
                ltk->theme = ltk_load_theme(theme_path);
       -        ltk->window_hash = NULL;
       +        ltk->window_hash = kh_init(winhash);
                ltk->wm_delete_msg = XInternAtom(ltk->display, "WM_DELETE_WINDOW", False);
       +        ltk->tm = ltk_init_text();
       +        ltk->default_font = ltk_get_font(ltk->tm, ltk->theme->window->font);
        }
        
        void ltk_clean_up(void)
        {
                LtkWindow *window;
       -        for (window = ltk_global->window_hash; window != NULL;
       -             window = window->hh.next) {
       -                ltk_destroy_window(window);
       +        for (int k = kh_begin(ltk_global->window_hash); k != kh_end(ltk_global->window_hash); k++) {
       +                if (kh_exist(ltk_global->window_hash, k)) {
       +                        ltk_destroy_window(kh_value(ltk_global->window_hash, k));
       +                }
                }
       +        kh_destroy(winhash, ltk_global->window_hash);
                XCloseDisplay(ltk_global->display);
                ltk_destroy_theme(ltk_global->theme);
       +        ltk_destroy_text_manager(ltk_global->tm);
                free(ltk_global);
        }
        
       t@@ -141,7 +146,9 @@ LtkWindow *ltk_create_window(const char *title, int x, int y,
                             ButtonPressMask | ButtonReleaseMask |
                             StructureNotifyMask | PointerMotionMask);
        
       -        HASH_ADD_INT(ltk_global->window_hash, xwindow, window);
       +        int ret;
       +        int k = kh_put(winhash, ltk_global->window_hash, window->xwindow, &ret);
       +        kh_value(ltk_global->window_hash, k) = window;
        
                return window;
        }
       t@@ -155,7 +162,8 @@ void ltk_remove_window(LtkWindow * window)
        
        void ltk_destroy_window(LtkWindow * window)
        {
       -        HASH_DEL(ltk_global->window_hash, window);
       +        int k = kh_get(winhash, ltk_global->window_hash, window->xwindow);
       +        kh_del(winhash, ltk_global->window_hash, k);
                LtkWidget *ptr = window->root_widget;
                if (ptr)
                        ptr->destroy(ptr);
       t@@ -199,7 +207,7 @@ void ltk_window_ini_handler(LtkTheme *theme, const char *prop, const char *value
                } else if (strcmp(prop, "fg") == 0) {
                        theme->window->fg = ltk_create_xcolor(value);
                } else if (strcmp(prop, "font") == 0) {
       -                theme->window->font = ltk_load_font(value);
       +                theme->window->font = strdup(value);
                }
        }
        
       t@@ -258,18 +266,17 @@ void ltk_destroy_theme(LtkTheme * theme)
                free(theme);
        }
        
       -char *ltk_read_file(const char *path)
       +char *ltk_read_file(const char *path, unsigned long *len)
        {
                FILE *f;
       -        long len;
                char *file_contents;
                f = fopen(path, "rb");
                fseek(f, 0, SEEK_END);
       -        len = ftell(f);
       +        *len = ftell(f);
                fseek(f, 0, SEEK_SET);
       -        file_contents = malloc(len + 1);
       -        fread(file_contents, 1, len, f);
       -        file_contents[len] = '\0';
       +        file_contents = malloc(*len + 1);
       +        fread(file_contents, 1, *len, f);
       +        file_contents[*len] = '\0';
                fclose(f);
        
                return file_contents;
       t@@ -424,7 +431,8 @@ void ltk_handle_event(XEvent event)
        {
                LtkWindow *window;
                LtkWidget *root_widget;
       -        HASH_FIND_INT(ltk_global->window_hash, &event.xany.window, window);
       +        int k = kh_get(winhash, ltk_global->window_hash, event.xany.window);
       +        window = kh_value(ltk_global->window_hash, k);
                if (!window)
                        return;
                root_widget = window->root_widget;
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -1,6 +1,6 @@
        /*
         * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
       t@@ -29,7 +29,7 @@
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
        #include "ini.h"
       -#include "uthash.h"
       +#include "khash.h"
        #include "stb_truetype.h"
        
        typedef struct {
       t@@ -85,18 +85,20 @@ typedef struct LtkWidget {
                unsigned short sticky;
        } LtkWidget;
        
       +/* Window hash */
       +KHASH_MAP_INIT_INT(winhash, LtkWindow*)
       +
        typedef struct LtkWindow {
                Window xwindow;
                GC gc;
                void *root_widget;
                void (*other_event) (void *, XEvent event);
                LtkRect rect;
       -        UT_hash_handle hh;
        } LtkWindow;
        
        typedef struct LtkWindowTheme {
                int border_width;
       -        stbtt_fontinfo font;
       +        char *font;
                XColor fg;
                XColor bg;
        } LtkWindowTheme;
       t@@ -113,10 +115,12 @@ LtkTheme *ltk_load_theme(const char *path);
        
        typedef struct {
                LtkTheme *theme;
       +        LtkTextManager *tm;
       +        uint16_t default_font;
                Display *display;
                int screen;
                Colormap colormap;
       -        LtkWindow *window_hash;
       +        khash_t(winhash) *window_hash;
                Atom wm_delete_msg;
        } Ltk;
        
       t@@ -145,7 +149,7 @@ void ltk_destroy_theme(LtkTheme * theme);
        
        int ltk_collide_rect(LtkRect rect, int x, int y);
        
       -char *ltk_read_file(const char *path);
       +char *ltk_read_file(const char *path, unsigned long *len);
        
        void ltk_change_active_widget_state(void *widget, LtkWidgetState state);
        
   DIR diff --git a/test1.c b/test1.c
       t@@ -16,8 +16,7 @@ void bob2(void *widget, XEvent event)
        int main(int argc, char *argv[])
        {
                ltk_init("themes/default.ini");
       -        LtkWindow *window1 =
       -            ltk_create_window("Cool Window!", 0, 0, 500, 500);
       +        LtkWindow *window1 = ltk_create_window("Cool Window!", 0, 0, 500, 500);
        /*    LtkWindow *window2 = ltk_create_window("Cool Window!", 0, 0, 500, 500);*/
                LtkGrid *grid1 = ltk_create_grid(window1, 2, 2);
                window1->root_widget = grid1;
       t@@ -26,24 +25,15 @@ int main(int argc, char *argv[])
                ltk_set_column_weight(grid1, 0, 1);
                ltk_set_column_weight(grid1, 1, 1);
                /* Test callback functions */
       -        LtkButton *button1 =
       -            ltk_create_button(window1, "I'm a button!", &bob1);
       -        ltk_grid_widget(button1, grid1, 0, 0, 1, 1,
       -                        LTK_STICKY_LEFT | LTK_STICKY_RIGHT);
       +        LtkButton *button1 = ltk_create_button(window1, "I'm a button!", &bob1);
       +        ltk_grid_widget(button1, grid1, 0, 0, 1, 1, LTK_STICKY_LEFT | LTK_STICKY_RIGHT);
                /* Test manual callback functions */
       -        LtkButton *button2 =
       -            ltk_create_button(window1, "I'm a button!", NULL);
       +        LtkButton *button2 = ltk_create_button(window1, "I'm a button!", NULL);
                button2->widget.mouse_release = &bob2;
       -        ltk_grid_widget(button2, grid1, 0, 1, 1, 1,
       -                        LTK_STICKY_TOP | LTK_STICKY_BOTTOM);
       -        LtkButton *button3 =
       -            ltk_create_button(window1, "I'm a button!", NULL);
       -        ltk_grid_widget(button3, grid1, 1, 0, 1, 1,
       -                        LTK_STICKY_TOP | LTK_STICKY_BOTTOM |
       -                        LTK_STICKY_RIGHT);
       -        LtkButton *button4 =
       -            ltk_create_button(window1, "I'm a button!", NULL);
       -        ltk_grid_widget(button4, grid1, 1, 1, 1, 1,
       -                        LTK_STICKY_LEFT | LTK_STICKY_BOTTOM);
       +        ltk_grid_widget(button2, grid1, 0, 1, 1, 1, LTK_STICKY_TOP | LTK_STICKY_BOTTOM);
       +        LtkButton *button3 = ltk_create_button(window1, "I'm a button!", NULL);
       +        ltk_grid_widget(button3, grid1, 1, 0, 1, 1, LTK_STICKY_TOP | LTK_STICKY_BOTTOM | LTK_STICKY_RIGHT);
       +        LtkButton *button4 = ltk_create_button(window1, "I'm a button!", NULL);
       +        ltk_grid_widget(button4, grid1, 1, 1, 1, 1, LTK_STICKY_LEFT | LTK_STICKY_BOTTOM);
                ltk_mainloop();
        }
   DIR diff --git a/text-hb.c b/text-hb.c
       t@@ -0,0 +1,399 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2017, 2018 lumidify <nobody@lumidify.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <stdint.h>
       +#include <limits.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include "ltk.h"
       +#include "khash.h"
       +#include "text-hb.h"
       +
       +LtkTextManager *
       +ltk_init_text(void)
       +{
       +        LtkTextManager *tm = malloc(sizeof(LtkTextManager));
       +        if (!tm) {
       +                printf("Memory exhausted when trying to create text manager.");
       +                exit(1);
       +        }
       +        tm->font_paths = kh_init(fontid);
       +        tm->font_cache = kh_init(fontstruct);
       +        tm->glyph_cache = kh_init(glyphcache);
       +        tm->font_id_cur = 0;
       +
       +        return tm;
       +}
       +
       +void
       +ltk_destroy_text_manager(LtkTextManager *tm)
       +{
       +        int k;
       +
       +        kh_destroy(fontid, tm->font_paths);
       +
       +        for (k = kh_begin(tm->font_cache); k != kh_end(tm->font_cache); k++) {
       +                if (kh_exist(tm->font_cache, k)) {
       +                        ltk_destroy_font(kh_value(tm->font_cache, k));
       +                }
       +        }
       +        kh_destroy(fontstruct, tm->font_cache);
       +
       +        for (k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) {
       +                if (kh_exist(tm->glyph_cache, k)) {
       +                        ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k));
       +                }
       +        }
       +        kh_destroy(glyphcache, tm->glyph_cache);
       +
       +        free(tm);
       +}
       +
       +LtkGlyphInfo *
       +ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale)
       +{
       +        LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
       +        if (!glyph) {
       +                printf("Out of memory!\n");
       +                exit(1);
       +        }
       +        
       +        glyph->id = id;
       +        glyph->alphamap = stbtt_GetGlyphBitmap(
       +                &font->info, scale, scale, id, &glyph->w,
       +                &glyph->h, &glyph->xoff, &glyph->yoff
       +        );
       +
       +        return glyph;
       +}
       +
       +void
       +ltk_destroy_glyph_info(LtkGlyphInfo *gi)
       +{
       +        free(gi->alphamap);
       +        free(gi);
       +}
       +
       +LtkGlyphInfo *
       +ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache)
       +{
       +        int ret;
       +        khint_t k;
       +        LtkGlyphInfo *glyph;
       +        k = kh_get(glyphinfo, cache, id);
       +        if (k == kh_end(cache)) {
       +                glyph = ltk_create_glyph_info(font, id, scale);
       +                glyph->refs = 0;
       +                /* FIXME: error checking with ret */
       +                k = kh_put(glyphinfo, cache, id, &ret);
       +                kh_value(cache, k) = glyph;
       +        } else {
       +                glyph = kh_value(cache, k);
       +                glyph->refs++;
       +        }
       +
       +        return glyph;
       +}
       +
       +khint_t
       +ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size)
       +{
       +        khash_t(glyphinfo) *cache = kh_init(glyphinfo);
       +        int ret;
       +        khint_t k;
       +        /* I guess I can just ignore ret for now */
       +        k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret);
       +        kh_value(tm->glyph_cache, k) = cache;
       +
       +        return k;
       +}
       +
       +void
       +ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache)
       +{
       +        int k;
       +        for (k = kh_begin(cache); k != kh_end(cache); k++) {
       +                if (kh_exist(cache, k)) {
       +                        ltk_destroy_glyph_info(kh_value(cache, k));
       +                }
       +        }
       +        kh_destroy(glyphinfo, cache);
       +}
       +
       +LtkFont *
       +ltk_create_font(char *path, uint16_t id)
       +{
       +        long len;
       +        LtkFont *font = malloc(sizeof(LtkFont));
       +        if (!font) {
       +                fprintf(stderr, "Out of memory!\n");
       +                exit(1);
       +        }
       +        char *contents = ltk_read_file(path, &len);
       +        if (!stbtt_InitFont(&font->info, contents, 0))
       +        {
       +                fprintf(stderr, "Failed to load font %s\n", path);
       +                exit(1);
       +        }
       +        /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */
       +        hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
       +        hb_face_t *face = hb_face_create(blob, 0);
       +        /* FIXME: need to use destroy function in order for the original file data to be freed? */
       +        hb_blob_destroy(blob);
       +        font->hb = hb_font_create(face);
       +        hb_face_destroy(face);
       +        hb_ot_font_set_funcs(font->hb);
       +        font->id = id;
       +        font->refs = 0;
       +
       +        return font;
       +}
       +
       +void
       +ltk_destroy_font(LtkFont *font)
       +{
       +        free(font->info.data);
       +        hb_font_destroy(font->hb);
       +        free(font);
       +}
       +
       +/* FIXME: need to figure out how exactly the whole font system is going to work, especially with default fonts, etc. */
       +uint16_t
       +ltk_load_font(LtkTextManager *tm, char *path)
       +{
       +        LtkFont *font = ltk_create_font(path, tm->font_id_cur++);
       +        int ret;
       +        khint_t k;
       +        k = kh_put(fontid, tm->font_paths, path, &ret);
       +        kh_value(tm->font_paths, k) = font->id;
       +        k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret);
       +        kh_value(tm->font_cache, k) = font;
       +
       +        return font->id;
       +}
       +
       +uint16_t
       +ltk_get_font(LtkTextManager *tm, char *path)
       +{
       +        int ret;
       +        khint_t k;
       +        uint16_t id;
       +        k = kh_get(fontid, tm->font_paths, path);
       +        if (k == kh_end(tm->font_paths)) {
       +                id = ltk_load_font(tm, path);
       +        } else {
       +                id = kh_value(tm->font_paths, k);
       +        }
       +
       +        return id;
       +}
       +
       +/* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large
       +   -> in case I want to get rid of uint_16_t, etc. */
       +LtkTextSegment *
       +ltk_create_text_segment(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size)
       +{
       +        /* (x1*, y1*): top left corner (relative to origin and absolute)
       +           (x2*, y2*): bottom right corner (relative to origin and absolute) */
       +        LtkFont *font;
       +        khash_t(glyphinfo) *glyph_cache;
       +        khint_t k;
       +
       +        k = kh_get(fontstruct, tm->font_cache, fontid);
       +        font = kh_value(tm->font_cache, k);
       +        /* FIXME: when should refs be increased? maybe only at the end in case
       +           it has to return for some other reason (out of memory, etc.) */
       +        font->refs++;
       +
       +        uint32_t attr = fontid << 16 + size;
       +        /* FIXME: turn this int ltk_get_glyph_cache */
       +        k = kh_get(glyphcache, tm->glyph_cache, attr);
       +        if (k == kh_end(tm->glyph_cache)) {
       +                k = ltk_create_glyph_cache(tm, fontid, size);
       +        }
       +        glyph_cache = kh_value(tm->glyph_cache, k);
       +
       +        LtkTextSegment *ts = malloc(sizeof(LtkTextSegment));
       +        if (!ts) {
       +                fprintf(stderr, "Out of memory!\n");
       +                exit(1);
       +        }
       +        int tlen = strlen(text);
       +        ts->str = strdup(text);
       +        ts->font_id = fontid;
       +        ts->font_size = size;
       +
       +        hb_buffer_t *buf;
       +        hb_glyph_info_t *ginf, *gi;
       +        hb_glyph_position_t *gpos, *gp;
       +        unsigned int text_len = 0;
       +        int text_bytes = strlen(text);
       +        if (text_bytes < 1) {
       +                printf("WARNING: ltk_render_text_segment: length of text is less than 1.\n");
       +        }
       +
       +        buf = hb_buffer_create();
       +        hb_buffer_set_flags(buf, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
       +        hb_buffer_add_utf8(buf, text, text_bytes, 0, text_bytes);
       +        hb_buffer_guess_segment_properties(buf);
       +        hb_shape(font->hb, buf, NULL, 0);
       +        ginf = hb_buffer_get_glyph_infos(buf, &text_len);
       +        gpos = hb_buffer_get_glyph_positions(buf, &text_len);
       +        float scale = stbtt_ScaleForMappingEmToPixels(&font->info, size);
       +        LtkGlyph *last_glyph = NULL;
       +
       +        int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
       +        int x_abs = 0, y_abs = 0, x1_abs, y1_abs, x2_abs, y2_abs;
       +        for (int i = 0; i < text_len; i++) {
       +                gi = &ginf[i];
       +                gp = &gpos[i];
       +                LtkGlyph *glyph = malloc(sizeof(LtkGlyph));
       +                glyph->info = ltk_get_glyph_info(font, gi->codepoint, scale, glyph_cache);
       +                /* FIXME: round instead of just casting */
       +                glyph->x_offset = (int)(gp->x_offset * scale);
       +                glyph->y_offset = (int)(gp->y_offset * scale);
       +                glyph->x_advance = (int)(gp->x_advance * scale);
       +                glyph->y_advance = (int)(gp->y_advance * scale);
       +                glyph->next = NULL;
       +                if (i == 0) {
       +                        ts->start_glyph = glyph;
       +                } else {
       +                        last_glyph->next = glyph;
       +                }
       +                last_glyph = glyph;
       +
       +                /* Calculate position in order to determine full size of text segment */
       +                x1_abs = x_abs + glyph->info->xoff + glyph->x_offset;
       +                y1_abs = y_abs + glyph->info->yoff - glyph->y_offset;
       +                x2_abs = x1_abs + glyph->info->w;
       +                y2_abs = y1_abs + glyph->info->h;
       +                if (x1_abs < x_min) x_min = x1_abs;
       +                if (y1_abs < y_min) y_min = y1_abs;
       +                if (x2_abs > x_max) x_max = x2_abs;
       +                if (y2_abs > y_max) y_max = y2_abs;
       +                x_abs += glyph->x_advance;
       +                y_abs -= glyph->y_advance;
       +        }
       +        /* FIXME: what was this supposed to do?
       +           I think it was supposed to be the start drawing position, but why would this not be 0?
       +           I'm guessing it had something to do with the fact that I need to calculate where the
       +           actual top left corner of the glyph is since harfbuzz gives me the origin - maybe I
       +           should just store that position directly in LtkGlyph? Is there any need to advance, etc.
       +           later on after I've positioned the glyphs? Well, I guess I'll figure that out eventually... */
       +        ts->start_x = -x_min;
       +        ts->start_y = -y_min;
       +        ts->w = x_max - x_min;
       +        ts->h = y_max - y_min;
       +        return ts;
       +}
       +
       +void
       +ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache)
       +{
       +        int k;
       +        if (--glyph->info->refs < 1) {
       +                k = kh_get(glyphinfo, cache, glyph->info->id);
       +                kh_del(glyphinfo, cache, k);
       +                ltk_destroy_glyph_info(glyph->info);
       +        }
       +        free(glyph);
       +}
       +
       +void
       +ltk_destroy_text_segment(LtkTextSegment *ts)
       +{
       +        LtkGlyph *glyph, *next_glyph;
       +        khash_t(glyphinfo) *gcache;
       +        LtkFont *font;
       +        int k;
       +        glyph = ts->start_glyph;
       +        k = kh_get(glyphinfo, ltk_global->tm->glyph_cache, ts->font_id << 16 + ts->font_size);
       +        gcache = kh_value(ltk_global->tm->glyph_cache, k);
       +        do {
       +                next_glyph = glyph->next;
       +                ltk_destroy_glyph(glyph, gcache);
       +        } while (glyph = next_glyph);
       +        k = kh_get(fontstruct, ltk_global->tm->font_cache, ts->font_id);
       +        font = kh_value(ltk_global->tm->font_cache, k);
       +        if (--font->refs < 1) {
       +                kh_del(fontstruct, ltk_global->tm->font_cache, k);
       +                ltk_destroy_font(font);
       +        }
       +        free(ts->str);
       +        free(ts);
       +}
       +
       +XImage *
       +ltk_render_text_segment(
       +        LtkTextSegment *ts,
       +        Display *dpy,
       +        Window window,
       +        GC gc,
       +        Colormap colormap,
       +        XColor fg,
       +        XColor bg)
       +{
       +        /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */
       +        XWindowAttributes attrs;
       +        XGetWindowAttributes(dpy, window, &attrs);
       +        int depth = attrs.depth;
       +        XImage *img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, ts->w, ts->h, 32, 0);
       +        img->data = calloc(img->bytes_per_line, img->height);
       +        XInitImage(img);
       +        int b;
       +        for (int i = 0; i < ts->h; i++) {
       +                b = img->bytes_per_line * i;
       +                for (int j = 0; j < ts->w; j++) {
       +                        img->data[b++] = bg.blue / 257;
       +                        img->data[b++] = bg.green / 257;
       +                        img->data[b++] = bg.red / 257;
       +                        b++;
       +                }
       +        }
       +
       +        LtkGlyph *glyph = ts->start_glyph;
       +        int x_cur = ts->start_x;
       +        int y_cur = ts->start_y;
       +        int x, y;
       +        double a;
       +        unsigned int out_r, out_g, out_b;
       +        do {
       +                x = x_cur + glyph->info->xoff + glyph->x_offset;
       +                y = y_cur + glyph->info->yoff - glyph->y_offset;
       +                for (int i = 0; i < glyph->info->h; i++) {
       +                        for (int j = 0; j < glyph->info->w; j++) {
       +                                b = (y + i) * img->bytes_per_line + (x + j) * 4;
       +                                a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0;
       +                                img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257;
       +                                img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257;
       +                                img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257;
       +                        }
       +                }
       +                x_cur += glyph->x_advance;
       +                y_cur -= glyph->y_advance;
       +        } while (glyph = glyph->next);
       +
       +        return img;
       +}
   DIR diff --git a/text-hb.h b/text-hb.h
       t@@ -0,0 +1,126 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2017, 2018 lumidify <nobody@lumidify.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#ifndef TEXT_HB_H
       +#define TEXT_HB_H
       +
       +#include <harfbuzz/hb.h>
       +#include <harfbuzz/hb-ot.h>
       +#define STB_TRUETYPE_IMPLEMENTATION
       +#include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
       +#include "khash.h"
       +#include <X11/Xlib.h>
       +
       +typedef struct {
       +        stbtt_fontinfo info;
       +        hb_font_t *hb;
       +        uint16_t id;
       +        unsigned int refs;
       +} LtkFont;
       +
       +/* Contains general info on glyphs that doesn't change regardless of the context */
       +typedef struct _LtkGlyphInfo {
       +        unsigned int id;
       +        unsigned char *alphamap;
       +        unsigned int w;
       +        unsigned int h;
       +        unsigned int xoff; /* x offset from origin to top left corner of glyph */
       +        unsigned int yoff; /* y offset from origin to top left corner of glyph */
       +        unsigned int refs;
       +        /* FIXME: does refs need to be long? It could cause problems if a
       +        program tries to cache/"keep alive" a lot of pages of text. */
       +} LtkGlyphInfo;
       +
       +/* Contains glyph info specific to one run of text */
       +typedef struct _LtkGlyph {
       +        LtkGlyphInfo *info;
       +        int x_offset; /* additional x offset given by harfbuzz */
       +        int y_offset; /* additional y offset given by harfbuzz */
       +        int x_advance;
       +        int y_advance;
       +        uint32_t cluster; /* index of char in original text - from harfbuzz */
       +        struct _LtkGlyph *next;
       +} LtkGlyph;
       +
       +typedef struct {
       +        uint16_t font_id;
       +        uint16_t font_size;
       +        unsigned int w;
       +        unsigned int h;
       +        int start_x;
       +        int start_y;
       +        char *str;
       +        LtkGlyph *start_glyph;
       +} LtkTextSegment;
       +
       +/* Hash definitions */
       +/* glyph id -> glyph info struct */
       +KHASH_MAP_INIT_INT(glyphinfo, LtkGlyphInfo*)
       +/* font path, size -> glyph cache hash */
       +KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*)
       +/* font path -> font id */
       +KHASH_MAP_INIT_STR(fontid, uint16_t)
       +/* font id -> font struct */
       +KHASH_MAP_INIT_INT(fontstruct, LtkFont*)
       +
       +typedef struct LtkTextManager_ {
       +        khash_t(fontid) *font_paths;
       +        khash_t(fontstruct) *font_cache;
       +        khash_t(glyphcache) *glyph_cache;
       +        uint16_t font_id_cur;
       +} LtkTextManager;
       +
       +LtkTextManager *ltk_init_text(void);
       +
       +void ltk_destroy_text_manager(LtkTextManager *tm);
       +
       +LtkGlyphInfo *ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale);
       +
       +void ltk_destroy_glyph_info(LtkGlyphInfo *gi);
       +
       +LtkGlyphInfo *ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache);
       +
       +khint_t ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size);
       +
       +void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache);
       +
       +LtkFont *ltk_create_font(char *path, uint16_t id);
       +
       +void ltk_destroy_font(LtkFont *font);
       +
       +/* FIXME: need to figure out how exactly the whole font system is going to work, especially with default fonts, etc. */
       +uint16_t ltk_load_font(LtkTextManager *tm, char *path);
       +
       +uint16_t ltk_get_font(LtkTextManager *tm, char *path);
       +
       +/* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large
       +   -> in case I want to get rid of uint_16_t, etc. */
       +LtkTextSegment *ltk_create_text_segment(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size);
       +
       +void ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache);
       +
       +void ltk_destroy_text_segment(LtkTextSegment *ts);
       +
       +XImage *ltk_render_text_segment(LtkTextSegment *ts, Display *dpy, Window window, GC gc, Colormap colormap, XColor fg, XColor bg);
       +
       +#endif
   DIR diff --git a/text.c b/text.c
       t@@ -92,7 +92,7 @@ int ltk_text_width(uint8_t *text, stbtt_fontinfo fontinfo, int height)
        unsigned char *ltk_render_text(uint8_t *text, stbtt_fontinfo fontinfo, int height, int width)
        {
        /*        int width = ltk_text_width(text, fontinfo, height);*/
       -        unsigned char *bitmap = calloc(sizeof(char), width * height);
       +        unsigned char *bitmap = calloc(width * height, sizeof(char));
                float scale = stbtt_ScaleForPixelHeight(&fontinfo, height);
        
                int ascent, descent, line_gap;
   DIR diff --git a/text/Makefile b/text/Makefile
       t@@ -1,2 +1,2 @@
        all: text-hb.ubernew.c
       -        gcc text-hb.ubernew.c -g -std=c99 -o test -I /usr/X11R6/include -L /usr/X11R6/lib -I /usr/local/include -L /usr/local/lib -lm -lharfbuzz -lX11 -lXrender
       +        gcc text-hb.uberubernew.c -g -std=c99 -o test `pkg-config --cflags --libs x11 harfbuzz` -lm
   DIR diff --git a/text/text-hb.ubernew.c b/text/text-hb.ubernew.c
       t@@ -206,8 +206,10 @@ ltk_create_font(char *path, uint16_t id)
                        fprintf(stderr, "Failed to load font %s\n", path);
                        exit(1);
                }
       +        /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */
                hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
                hb_face_t *face = hb_face_create(blob, 0);
       +        /* FIXME: need to use destroy function in order for the original file data to be freed? */
                hb_blob_destroy(blob);
                font->hb = hb_font_create(face);
                hb_face_destroy(face);
   DIR diff --git a/text/text-hb.uberubernew.c b/text/text-hb.uberubernew.c
       t@@ -0,0 +1,473 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2017, 2018 lumidify <nobody@lumidify.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <stdint.h>
       +#include <limits.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include <harfbuzz/hb.h>
       +#include <harfbuzz/hb-ot.h>
       +#define STB_TRUETYPE_IMPLEMENTATION
       +#include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
       +#include "khash.h"
       +
       +typedef struct {
       +        stbtt_fontinfo info;
       +        hb_font_t *hb;
       +        uint16_t id;
       +        unsigned int refs;
       +} LtkFont;
       +
       +/* Contains general info on glyphs that doesn't change regardless of the context */
       +typedef struct _LtkGlyphInfo {
       +        unsigned char *alphamap;
       +        unsigned int w;
       +        unsigned int h;
       +        unsigned int xoff; /* x offset from origin to top left corner of glyph */
       +        unsigned int yoff; /* y offset from origin to top left corner of glyph */
       +        unsigned int refs;
       +        /* FIXME: does refs need to be long? It could cause problems if a
       +        program tries to cache/"keep alive" a lot of pages of text. */
       +} LtkGlyphInfo;
       +
       +/* Contains glyph info specific to one run of text */
       +typedef struct _LtkGlyph {
       +        LtkGlyphInfo *info;
       +        int x_offset; /* additional x offset given by harfbuzz */
       +        int y_offset; /* additional y offset given by harfbuzz */
       +        int x_advance;
       +        int y_advance;
       +        uint32_t cluster; /* index of char in original text - from harfbuzz */
       +        struct _LtkGlyph *next;
       +} LtkGlyph;
       +
       +typedef struct {
       +        unsigned int w;
       +        unsigned int h;
       +        int start_x;
       +        int start_y;
       +        char *str;
       +        LtkGlyph *start_glyph;
       +} LtkTextSegment;
       +
       +/* Hash definitions */
       +/* glyph id -> glyph info struct */
       +KHASH_MAP_INIT_INT(glyphinfo, LtkGlyphInfo*)
       +/* font path, size -> glyph cache hash */
       +KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*)
       +/* font path -> font id */
       +KHASH_MAP_INIT_STR(fontid, uint16_t)
       +/* font id -> font struct */
       +KHASH_MAP_INIT_INT(fontstruct, LtkFont*)
       +
       +typedef struct LtkTextManager_ {
       +        khash_t(fontid) *font_paths;
       +        khash_t(fontstruct) *font_cache;
       +        khash_t(glyphcache) *glyph_cache;
       +        uint16_t font_id_cur;
       +} LtkTextManager;
       +
       +LtkTextManager *
       +ltk_init_text(void)
       +{
       +        LtkTextManager *tm = malloc(sizeof(LtkTextManager));
       +        if (!tm) {
       +                printf("Memory exhausted when trying to create text manager.");
       +                exit(1);
       +        }
       +        tm->font_paths = kh_init(fontid);
       +        tm->font_cache = kh_init(fontstruct);
       +        tm->glyph_cache = kh_init(glyphcache);
       +        tm->font_id_cur = 0;
       +
       +        return tm;
       +}
       +
       +LtkGlyphInfo *
       +ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale)
       +{
       +        LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
       +        if (!glyph) {
       +                printf("Out of memory!\n");
       +                exit(1);
       +        }
       +        
       +        glyph->alphamap = stbtt_GetGlyphBitmap(
       +                &font->info, scale, scale, id, &glyph->w,
       +                &glyph->h, &glyph->xoff, &glyph->yoff
       +        );
       +
       +        return glyph;
       +}
       +
       +LtkGlyphInfo *
       +ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache)
       +{
       +        int ret;
       +        khint_t k;
       +        LtkGlyphInfo *glyph;
       +        k = kh_get(glyphinfo, cache, id);
       +        if (k == kh_end(cache)) {
       +                glyph = ltk_create_glyph_info(font, id, scale);
       +                glyph->refs = 0;
       +                /* FIXME: error checking with ret */
       +                k = kh_put(glyphinfo, cache, id, &ret);
       +                kh_value(cache, k) = glyph;
       +        } else {
       +                glyph = kh_value(cache, k);
       +                glyph->refs++;
       +        }
       +
       +        return glyph;
       +}
       +
       +
       +khint_t
       +ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size)
       +{
       +        khash_t(glyphinfo) *cache = kh_init(glyphinfo);
       +        int ret;
       +        khint_t k;
       +        /* I guess I can just ignore ret for now */
       +        k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret);
       +        kh_value(tm->glyph_cache, k) = cache;
       +
       +        return k;
       +}
       +
       +char *
       +ltk_load_file(const char *path, unsigned long *len)
       +{
       +        FILE *f;
       +        char *contents;
       +        f = fopen(path, "rb");
       +        fseek(f, 0, SEEK_END);
       +        *len = ftell(f);
       +        fseek(f, 0, SEEK_SET);
       +        contents = malloc(*len + 1);
       +        fread(contents, 1, *len, f);
       +        contents[*len] = '\0';
       +        fclose(f);
       +        return contents;
       +}
       +
       +unsigned long
       +ltk_blend_pixel(Display *display, Colormap colormap, XColor fg, XColor bg, double a)
       +{
       +        if (a >= 1.0) {
       +                return fg.pixel;
       +        } else if (a == 0.0) {
       +                return bg.pixel;
       +        }
       +
       +        XColor blended;
       +        blended.red = (int)((fg.red - bg.red) * a + bg.red);
       +        blended.green = (int)((fg.green - bg.green) * a + bg.green);
       +        blended.blue = (int)((fg.blue - bg.blue) * a + bg.blue);
       +        XAllocColor(display, colormap, &blended);
       +
       +        return blended.pixel;
       +}
       +
       +LtkFont *
       +ltk_create_font(char *path, uint16_t id)
       +{
       +        long len;
       +        LtkFont *font = malloc(sizeof(LtkFont));
       +        if (!font) {
       +                fprintf(stderr, "Out of memory!\n");
       +                exit(1);
       +        }
       +        char *contents = ltk_load_file(path, &len);
       +        if (!stbtt_InitFont(&font->info, contents, 0))
       +        {
       +                fprintf(stderr, "Failed to load font %s\n", path);
       +                exit(1);
       +        }
       +        /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */
       +        hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
       +        hb_face_t *face = hb_face_create(blob, 0);
       +        /* FIXME: need to use destroy function in order for the original file data to be freed? */
       +        hb_blob_destroy(blob);
       +        font->hb = hb_font_create(face);
       +        hb_face_destroy(face);
       +        hb_ot_font_set_funcs(font->hb);
       +        font->id = id;
       +        return font;
       +}
       +
       +/* FIXME: need to figure out how exactly the whole font system is going to work, especially with default fonts, etc. */
       +uint16_t
       +ltk_load_font(LtkTextManager *tm, char *path)
       +{
       +        LtkFont *font = ltk_create_font(path, tm->font_id_cur++);
       +        int ret;
       +        khint_t k;
       +        k = kh_put(fontid, tm->font_paths, path, &ret);
       +        kh_value(tm->font_paths, k) = font->id;
       +        k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret);
       +        kh_value(tm->font_cache, k) = font;
       +
       +        return font->id;
       +}
       +
       +uint16_t
       +ltk_get_font(LtkTextManager *tm, char *path)
       +{
       +        int ret;
       +        khint_t k;
       +        uint16_t id;
       +        k = kh_get(fontid, tm->font_paths, path);
       +        if (k == kh_end(tm->font_paths)) {
       +                id = ltk_load_font(tm, path);
       +        } else {
       +                id = kh_value(tm->font_paths, k);
       +        }
       +
       +        return id;
       +}
       +
       +/* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large
       +   -> in case I want to get rid of uint_16_t, etc. */
       +LtkTextSegment *
       +ltk_create_text_segment(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size)
       +{
       +        /* (x1*, y1*): top left corner (relative to origin and absolute)
       +           (x2*, y2*): bottom right corner (relative to origin and absolute) */
       +        LtkFont *font;
       +        khash_t(glyphinfo) *glyph_cache;
       +        khint_t k;
       +
       +        k = kh_get(fontstruct, tm->font_cache, fontid);
       +        font = kh_value(tm->font_cache, k);
       +        /* FIXME: when should refs be increased? maybe only at the end in case
       +           it has to return for some other reason (out of memory, etc.) */
       +        font->refs++;
       +
       +        uint32_t attr = fontid << 16 + size;
       +        /* FIXME: turn this int ltk_get_glyph_cache */
       +        k = kh_get(glyphcache, tm->glyph_cache, attr);
       +        if (k == kh_end(tm->glyph_cache)) {
       +                k = ltk_create_glyph_cache(tm, fontid, size);
       +        }
       +        glyph_cache = kh_value(tm->glyph_cache, k);
       +
       +        LtkTextSegment *ts = malloc(sizeof(LtkTextSegment));
       +        if (!ts) {
       +                fprintf(stderr, "Out of memory!\n");
       +                exit(1);
       +        }
       +        ts->str = text;
       +
       +        hb_buffer_t *buf;
       +        hb_glyph_info_t *ginf, *gi;
       +        hb_glyph_position_t *gpos, *gp;
       +        unsigned int text_len = 0;
       +        int text_bytes = strlen(text);
       +        if (text_bytes < 1) {
       +                printf("WARNING: ltk_render_text_segment: length of text is less than 1.\n");
       +        }
       +
       +        buf = hb_buffer_create();
       +        hb_buffer_set_flags(buf, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
       +        hb_buffer_add_utf8(buf, text, text_bytes, 0, text_bytes);
       +        hb_buffer_guess_segment_properties(buf);
       +        hb_shape(font->hb, buf, NULL, 0);
       +        ginf = hb_buffer_get_glyph_infos(buf, &text_len);
       +        gpos = hb_buffer_get_glyph_positions(buf, &text_len);
       +        float scale = stbtt_ScaleForMappingEmToPixels(&font->info, size);
       +        LtkGlyph *last_glyph = NULL;
       +
       +        int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
       +        int x_abs = 0, y_abs = 0, x1_abs, y1_abs, x2_abs, y2_abs;
       +        for (int i = 0; i < text_len; i++) {
       +                gi = &ginf[i];
       +                gp = &gpos[i];
       +                LtkGlyph *glyph = malloc(sizeof(LtkGlyph));
       +                glyph->info = ltk_get_glyph_info(font, gi->codepoint, scale, glyph_cache);
       +                /* FIXME: round instead of just casting */
       +                glyph->x_offset = (int)(gp->x_offset * scale);
       +                glyph->y_offset = (int)(gp->y_offset * scale);
       +                glyph->x_advance = (int)(gp->x_advance * scale);
       +                glyph->y_advance = (int)(gp->y_advance * scale);
       +                glyph->next = NULL;
       +                if (i == 0) {
       +                        ts->start_glyph = glyph;
       +                } else {
       +                        last_glyph->next = glyph;
       +                }
       +                last_glyph = glyph;
       +
       +                /* Calculate position in order to determine full size of text segment */
       +                x1_abs = x_abs + glyph->info->xoff + glyph->x_offset;
       +                y1_abs = y_abs + glyph->info->yoff - glyph->y_offset;
       +                x2_abs = x1_abs + glyph->info->w;
       +                y2_abs = y1_abs + glyph->info->h;
       +                if (x1_abs < x_min) x_min = x1_abs;
       +                if (y1_abs < y_min) y_min = y1_abs;
       +                if (x2_abs > x_max) x_max = x2_abs;
       +                if (y2_abs > y_max) y_max = y2_abs;
       +                x_abs += glyph->x_advance;
       +                y_abs -= glyph->y_advance;
       +        }
       +        /* FIXME: what was this supposed to do?
       +           I think it was supposed to be the start drawing position, but why would this not be 0?
       +           I'm guessing it had something to do with the fact that I need to calculate where the
       +           actual top left corner of the glyph is since harfbuzz gives me the origin - maybe I
       +           should just store that position directly in LtkGlyph? Is there any need to advance, etc.
       +           later on after I've positioned the glyphs? Well, I guess I'll figure that out eventually... */
       +        ts->start_x = -x_min;
       +        ts->start_y = -y_min;
       +        ts->w = x_max - x_min;
       +        ts->h = y_max - y_min;
       +        return ts;
       +}
       +
       +XImage *
       +ltk_render_text_segment(
       +        LtkTextSegment *ts,
       +        Display *dpy,
       +        Window window,
       +        GC gc,
       +        Colormap colormap,
       +        XColor fg,
       +        XColor bg)
       +{
       +        XWindowAttributes attrs;
       +        XGetWindowAttributes(dpy, window, &attrs);
       +        int depth = attrs.depth;
       +        XImage *img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, ts->w, ts->h, 32, 0);
       +        img->data = calloc(img->bytes_per_line, img->height);
       +        XInitImage(img);
       +        int b;
       +        for (int i = 0; i < ts->h; i++) {
       +                b = img->bytes_per_line * i;
       +                for (int j = 0; j < ts->w; j++) {
       +                        img->data[b++] = bg.blue / 257;
       +                        img->data[b++] = bg.green / 257;
       +                        img->data[b++] = bg.red / 257;
       +                        b++;
       +                }
       +        }
       +
       +        LtkGlyph *glyph = ts->start_glyph;
       +        int x_cur = ts->start_x;
       +        int y_cur = ts->start_y;
       +        int x, y;
       +        double a;
       +        unsigned int out_r, out_g, out_b;
       +        do {
       +                x = x_cur + glyph->info->xoff + glyph->x_offset;
       +                y = y_cur + glyph->info->yoff - glyph->y_offset;
       +                for (int i = 0; i < glyph->info->h; i++) {
       +                        for (int j = 0; j < glyph->info->w; j++) {
       +                                b = (y + i) * img->bytes_per_line + (x + j) * 4;
       +                                a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0;
       +                                img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257;
       +                                img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257;
       +                                img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257;
       +
       +                                /*
       +                                out_r = (fg.red / 255) * a1 + (uint8_t)img->data[b + 2] * (255 - a1);
       +                                out_g = (fg.green / 255) * a1 + (uint8_t)img->data[b + 1] * (255 - a1);
       +                                out_b = (fg.blue / 255) * a1 + (uint8_t)img->data[b] * (255 - a1);
       +                                out_r = (out_r + 1 + (out_r >> 8)) >> 8;
       +                                out_g = (out_g + 1 + (out_g >> 8)) >> 8;
       +                                out_b = (out_b + 1 + (out_b >> 8)) >> 8;
       +                                */
       +                        }
       +                }
       +                x_cur += glyph->x_advance;
       +                y_cur -= glyph->y_advance;
       +        } while (glyph = glyph->next);
       +
       +        return img;
       +}
       +
       +int main(int argc, char *argv[])
       +{
       +    Display *display;
       +    int screen;
       +    Window window;
       +    GC gc;
       +
       +    unsigned long black, white;
       +    Colormap colormap;
       +    display = XOpenDisplay((char *)0);
       +    screen = DefaultScreen(display);
       +    colormap = DefaultColormap(display, screen);
       +    black = BlackPixel(display, screen);
       +    white = WhitePixel(display, screen);
       +    XSetWindowAttributes wattr;
       +    wattr.background_pixel = white;
       +    wattr.border_pixel = black;
       +    wattr.colormap = colormap;
       +    window = XCreateWindow(display, DefaultRootWindow(display), 0, 0, 1366, 512, 0, 24, CopyFromParent, CopyFromParent, CWBackPixel | CWBorderPixel, &wattr);
       +    XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL);
       +    XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask);
       +    gc = XCreateGC(display, window, 0, 0);
       +    XSetBackground(display, gc, black);
       +    XSetForeground(display, gc, black);
       +    XClearWindow(display, window);
       +    XMapRaised(display, window);
       +    XColor c1, c2;
       +    XParseColor(display, colormap, "#FFFFFF", &c1);
       +    XParseColor(display, colormap, "#FF0000", &c2);
       +    XAllocColor(display, colormap, &c1);
       +    XAllocColor(display, colormap, &c2);
       +
       +    LtkTextManager *tm = ltk_init_text();
       +    uint16_t font_id = ltk_get_font(tm, "NotoNastaliqUrdu-Regular.ttf");
       +    uint16_t font_size = 100;
       +    LtkTextSegment *text = ltk_create_text_segment(tm, "ہمارے بارے میں", font_id, font_size);
       +    //Pixmap pix = ltk_render_text_segment(text, display, window, gc, colormap, c1, c2);
       +    XImage *img = ltk_render_text_segment(text, display, window, gc, colormap, c1, c2);
       +    //XCopyArea(display, pix, window, gc, 0, 0, text->w, text->h, 0, 0);
       +    XPutImage(display, window, gc, img, 0, 0, 0, 0, text->w, text->h);
       +
       +    XEvent event;
       +    KeySym key;
       +    char txt[255];
       +
       +    while(1)
       +    {
       +        XNextEvent(display, &event);
       +        if (event.type == KeyPress && XLookupString(&event.xkey, txt, 255, &key, 0) == 1)
       +        {
       +            //XCopyArea(display, pix, window, gc, 0, 0, text->w, text->h, 0, 0);
       +            XPutImage(display, window, gc, img, 0, 0, 0, 0, text->w, text->h);
       +            if (txt[0] == 'q')
       +            {
       +                XFreeGC(display, gc);
       +                XFreeColormap(display, colormap);
       +                XDestroyWindow(display, window);
       +                XCloseDisplay(display);
       +                exit(0);
       +            }
       +        }
       +    }
       +    
       +    return 0;
       +}
   DIR diff --git a/uthash.h b/uthash.h
       t@@ -1,1074 +0,0 @@
       -/*
       -Copyright (c) 2003-2016, Troy D. Hanson     http://troydhanson.github.com/uthash/
       -All rights reserved.
       -
       -Redistribution and use in source and binary forms, with or without
       -modification, are permitted provided that the following conditions are met:
       -
       -    * Redistributions of source code must retain the above copyright
       -      notice, this list of conditions and the following disclaimer.
       -
       -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
       -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
       -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
       -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
       -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       -*/
       -
       -#ifndef UTHASH_H
       -#define UTHASH_H
       -
       -#define UTHASH_VERSION 2.0.1
       -
       -#include <string.h>   /* memcmp,strlen */
       -#include <stddef.h>   /* ptrdiff_t */
       -#include <stdlib.h>   /* exit() */
       -
       -/* These macros use decltype or the earlier __typeof GNU extension.
       -   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
       -   when compiling c++ source) this code uses whatever method is needed
       -   or, for VS2008 where neither is available, uses casting workarounds. */
       -#if defined(_MSC_VER)   /* MS compiler */
       -#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
       -#define DECLTYPE(x) (decltype(x))
       -#else                   /* VS2008 or older (or VS2010 in C mode) */
       -#define NO_DECLTYPE
       -#define DECLTYPE(x)
       -#endif
       -#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__)
       -#define NO_DECLTYPE
       -#define DECLTYPE(x)
       -#else                   /* GNU, Sun and other compilers */
       -#define DECLTYPE(x) (__typeof(x))
       -#endif
       -
       -#ifdef NO_DECLTYPE
       -#define DECLTYPE_ASSIGN(dst,src)                                                 \
       -do {                                                                             \
       -  char **_da_dst = (char**)(&(dst));                                             \
       -  *_da_dst = (char*)(src);                                                       \
       -} while (0)
       -#else
       -#define DECLTYPE_ASSIGN(dst,src)                                                 \
       -do {                                                                             \
       -  (dst) = DECLTYPE(dst)(src);                                                    \
       -} while (0)
       -#endif
       -
       -/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
       -#if defined(_WIN32)
       -#if defined(_MSC_VER) && _MSC_VER >= 1600
       -#include <stdint.h>
       -#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
       -#include <stdint.h>
       -#else
       -typedef unsigned int uint32_t;
       -typedef unsigned char uint8_t;
       -#endif
       -#elif defined(__GNUC__) && !defined(__VXWORKS__)
       -#include <stdint.h>
       -#else
       -typedef unsigned int uint32_t;
       -typedef unsigned char uint8_t;
       -#endif
       -
       -#ifndef uthash_fatal
       -#define uthash_fatal(msg) exit(-1)        /* fatal error (out of memory,etc) */
       -#endif
       -#ifndef uthash_malloc
       -#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */
       -#endif
       -#ifndef uthash_free
       -#define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */
       -#endif
       -#ifndef uthash_strlen
       -#define uthash_strlen(s) strlen(s)
       -#endif
       -#ifndef uthash_memcmp
       -#define uthash_memcmp(a,b,n) memcmp(a,b,n)
       -#endif
       -
       -#ifndef uthash_noexpand_fyi
       -#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
       -#endif
       -#ifndef uthash_expand_fyi
       -#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
       -#endif
       -
       -/* initial number of buckets */
       -#define HASH_INITIAL_NUM_BUCKETS 32U     /* initial number of buckets        */
       -#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
       -#define HASH_BKT_CAPACITY_THRESH 10U     /* expand when bucket count reaches */
       -
       -/* calculate the element whose hash handle address is hhp */
       -#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
       -/* calculate the hash handle from element address elp */
       -#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho)))
       -
       -#define HASH_VALUE(keyptr,keylen,hashv)                                          \
       -do {                                                                             \
       -  HASH_FCN(keyptr, keylen, hashv);                                               \
       -} while (0)
       -
       -#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out)                 \
       -do {                                                                             \
       -  (out) = NULL;                                                                  \
       -  if (head) {                                                                    \
       -    unsigned _hf_bkt;                                                            \
       -    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt);                  \
       -    if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) {                         \
       -      HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
       -    }                                                                            \
       -  }                                                                              \
       -} while (0)
       -
       -#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
       -do {                                                                             \
       -  unsigned _hf_hashv;                                                            \
       -  HASH_VALUE(keyptr, keylen, _hf_hashv);                                         \
       -  HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out);               \
       -} while (0)
       -
       -#ifdef HASH_BLOOM
       -#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
       -#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
       -#define HASH_BLOOM_MAKE(tbl)                                                     \
       -do {                                                                             \
       -  (tbl)->bloom_nbits = HASH_BLOOM;                                               \
       -  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \
       -  if (!((tbl)->bloom_bv))  { uthash_fatal( "out of memory"); }                   \
       -  memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN);                                \
       -  (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                       \
       -} while (0)
       -
       -#define HASH_BLOOM_FREE(tbl)                                                     \
       -do {                                                                             \
       -  uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \
       -} while (0)
       -
       -#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
       -#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
       -
       -#define HASH_BLOOM_ADD(tbl,hashv)                                                \
       -  HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
       -
       -#define HASH_BLOOM_TEST(tbl,hashv)                                               \
       -  HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
       -
       -#else
       -#define HASH_BLOOM_MAKE(tbl)
       -#define HASH_BLOOM_FREE(tbl)
       -#define HASH_BLOOM_ADD(tbl,hashv)
       -#define HASH_BLOOM_TEST(tbl,hashv) (1)
       -#define HASH_BLOOM_BYTELEN 0U
       -#endif
       -
       -#define HASH_MAKE_TABLE(hh,head)                                                 \
       -do {                                                                             \
       -  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(                                \
       -                  sizeof(UT_hash_table));                                        \
       -  if (!((head)->hh.tbl))  { uthash_fatal( "out of memory"); }                    \
       -  memset((head)->hh.tbl, 0, sizeof(UT_hash_table));                              \
       -  (head)->hh.tbl->tail = &((head)->hh);                                          \
       -  (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                        \
       -  (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;              \
       -  (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                    \
       -  (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                      \
       -          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
       -  if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); }             \
       -  memset((head)->hh.tbl->buckets, 0,                                             \
       -          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
       -  HASH_BLOOM_MAKE((head)->hh.tbl);                                               \
       -  (head)->hh.tbl->signature = HASH_SIGNATURE;                                    \
       -} while (0)
       -
       -#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
       -do {                                                                             \
       -  (replaced) = NULL;                                                             \
       -  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
       -  if (replaced) {                                                                \
       -     HASH_DELETE(hh, head, replaced);                                            \
       -  }                                                                              \
       -  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
       -} while (0)
       -
       -#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
       -do {                                                                             \
       -  (replaced) = NULL;                                                             \
       -  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
       -  if (replaced) {                                                                \
       -     HASH_DELETE(hh, head, replaced);                                            \
       -  }                                                                              \
       -  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
       -} while (0)
       -
       -#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
       -do {                                                                             \
       -  unsigned _hr_hashv;                                                            \
       -  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
       -  HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
       -} while (0)
       -
       -#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn)    \
       -do {                                                                             \
       -  unsigned _hr_hashv;                                                            \
       -  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
       -  HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
       -} while (0)
       -
       -#define HASH_APPEND_LIST(hh, head, add)                                          \
       -do {                                                                             \
       -  (add)->hh.next = NULL;                                                         \
       -  (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);           \
       -  (head)->hh.tbl->tail->next = (add);                                            \
       -  (head)->hh.tbl->tail = &((add)->hh);                                           \
       -} while (0)
       -
       -#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
       -do {                                                                             \
       -  unsigned _ha_bkt;                                                              \
       -  (add)->hh.hashv = (hashval);                                                   \
       -  (add)->hh.key = (char*) (keyptr);                                              \
       -  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
       -  if (!(head)) {                                                                 \
       -    (add)->hh.next = NULL;                                                       \
       -    (add)->hh.prev = NULL;                                                       \
       -    (head) = (add);                                                              \
       -    HASH_MAKE_TABLE(hh, head);                                                   \
       -  } else {                                                                       \
       -    struct UT_hash_handle *_hs_iter = &(head)->hh;                               \
       -    (add)->hh.tbl = (head)->hh.tbl;                                              \
       -    do {                                                                         \
       -      if (cmpfcn(DECLTYPE(head) ELMT_FROM_HH((head)->hh.tbl, _hs_iter), add) > 0) \
       -        break;                                                                   \
       -    } while ((_hs_iter = _hs_iter->next));                                       \
       -    if (_hs_iter) {                                                              \
       -      (add)->hh.next = _hs_iter;                                                 \
       -      if (((add)->hh.prev = _hs_iter->prev)) {                                   \
       -        HH_FROM_ELMT((head)->hh.tbl, _hs_iter->prev)->next = (add);              \
       -      } else {                                                                   \
       -        (head) = (add);                                                          \
       -      }                                                                          \
       -      _hs_iter->prev = (add);                                                    \
       -    } else {                                                                     \
       -      HASH_APPEND_LIST(hh, head, add);                                           \
       -    }                                                                            \
       -  }                                                                              \
       -  (head)->hh.tbl->num_items++;                                                   \
       -  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \
       -  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh);                 \
       -  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \
       -  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
       -  HASH_FSCK(hh, head);                                                           \
       -} while (0)
       -
       -#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn)             \
       -do {                                                                             \
       -  unsigned _hs_hashv;                                                            \
       -  HASH_VALUE(keyptr, keylen_in, _hs_hashv);                                      \
       -  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
       -} while (0)
       -
       -#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
       -  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
       -
       -#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn)                 \
       -  HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
       -
       -#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add)        \
       -do {                                                                             \
       -  unsigned _ha_bkt;                                                              \
       -  (add)->hh.hashv = (hashval);                                                   \
       -  (add)->hh.key = (char*) (keyptr);                                              \
       -  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
       -  if (!(head)) {                                                                 \
       -    (add)->hh.next = NULL;                                                       \
       -    (add)->hh.prev = NULL;                                                       \
       -    (head) = (add);                                                              \
       -    HASH_MAKE_TABLE(hh, head);                                                   \
       -  } else {                                                                       \
       -    (add)->hh.tbl = (head)->hh.tbl;                                              \
       -    HASH_APPEND_LIST(hh, head, add);                                             \
       -  }                                                                              \
       -  (head)->hh.tbl->num_items++;                                                   \
       -  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \
       -  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh);                 \
       -  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \
       -  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
       -  HASH_FSCK(hh, head);                                                           \
       -} while (0)
       -
       -#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
       -do {                                                                             \
       -  unsigned _ha_hashv;                                                            \
       -  HASH_VALUE(keyptr, keylen_in, _ha_hashv);                                      \
       -  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add);      \
       -} while (0)
       -
       -#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add)            \
       -  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
       -
       -#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
       -  HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
       -
       -#define HASH_TO_BKT(hashv,num_bkts,bkt)                                          \
       -do {                                                                             \
       -  bkt = ((hashv) & ((num_bkts) - 1U));                                           \
       -} while (0)
       -
       -/* delete "delptr" from the hash table.
       - * "the usual" patch-up process for the app-order doubly-linked-list.
       - * The use of _hd_hh_del below deserves special explanation.
       - * These used to be expressed using (delptr) but that led to a bug
       - * if someone used the same symbol for the head and deletee, like
       - *  HASH_DELETE(hh,users,users);
       - * We want that to work, but by changing the head (users) below
       - * we were forfeiting our ability to further refer to the deletee (users)
       - * in the patch-up process. Solution: use scratch space to
       - * copy the deletee pointer, then the latter references are via that
       - * scratch pointer rather than through the repointed (users) symbol.
       - */
       -#define HASH_DELETE(hh,head,delptr)                                              \
       -do {                                                                             \
       -    struct UT_hash_handle *_hd_hh_del;                                           \
       -    if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) )  {         \
       -        uthash_free((head)->hh.tbl->buckets,                                     \
       -                    (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
       -        HASH_BLOOM_FREE((head)->hh.tbl);                                         \
       -        uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                      \
       -        head = NULL;                                                             \
       -    } else {                                                                     \
       -        unsigned _hd_bkt;                                                        \
       -        _hd_hh_del = &((delptr)->hh);                                            \
       -        if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) {     \
       -            (head)->hh.tbl->tail =                                               \
       -                (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +               \
       -                (head)->hh.tbl->hho);                                            \
       -        }                                                                        \
       -        if ((delptr)->hh.prev != NULL) {                                         \
       -            ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +                  \
       -                    (head)->hh.tbl->hho))->next = (delptr)->hh.next;             \
       -        } else {                                                                 \
       -            DECLTYPE_ASSIGN(head,(delptr)->hh.next);                             \
       -        }                                                                        \
       -        if (_hd_hh_del->next != NULL) {                                          \
       -            ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next +                     \
       -                    (head)->hh.tbl->hho))->prev =                                \
       -                    _hd_hh_del->prev;                                            \
       -        }                                                                        \
       -        HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);   \
       -        HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);        \
       -        (head)->hh.tbl->num_items--;                                             \
       -    }                                                                            \
       -    HASH_FSCK(hh,head);                                                          \
       -} while (0)
       -
       -
       -/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
       -#define HASH_FIND_STR(head,findstr,out)                                          \
       -    HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out)
       -#define HASH_ADD_STR(head,strfield,add)                                          \
       -    HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add)
       -#define HASH_REPLACE_STR(head,strfield,add,replaced)                             \
       -    HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced)
       -#define HASH_FIND_INT(head,findint,out)                                          \
       -    HASH_FIND(hh,head,findint,sizeof(int),out)
       -#define HASH_ADD_INT(head,intfield,add)                                          \
       -    HASH_ADD(hh,head,intfield,sizeof(int),add)
       -#define HASH_REPLACE_INT(head,intfield,add,replaced)                             \
       -    HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
       -#define HASH_FIND_PTR(head,findptr,out)                                          \
       -    HASH_FIND(hh,head,findptr,sizeof(void *),out)
       -#define HASH_ADD_PTR(head,ptrfield,add)                                          \
       -    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
       -#define HASH_REPLACE_PTR(head,ptrfield,add,replaced)                             \
       -    HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
       -#define HASH_DEL(head,delptr)                                                    \
       -    HASH_DELETE(hh,head,delptr)
       -
       -/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
       - * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
       - */
       -#ifdef HASH_DEBUG
       -#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
       -#define HASH_FSCK(hh,head)                                                       \
       -do {                                                                             \
       -    struct UT_hash_handle *_thh;                                                 \
       -    if (head) {                                                                  \
       -        unsigned _bkt_i;                                                         \
       -        unsigned _count;                                                         \
       -        char *_prev;                                                             \
       -        _count = 0;                                                              \
       -        for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) {       \
       -            unsigned _bkt_count = 0;                                             \
       -            _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                      \
       -            _prev = NULL;                                                        \
       -            while (_thh) {                                                       \
       -               if (_prev != (char*)(_thh->hh_prev)) {                            \
       -                   HASH_OOPS("invalid hh_prev %p, actual %p\n",                  \
       -                    _thh->hh_prev, _prev );                                      \
       -               }                                                                 \
       -               _bkt_count++;                                                     \
       -               _prev = (char*)(_thh);                                            \
       -               _thh = _thh->hh_next;                                             \
       -            }                                                                    \
       -            _count += _bkt_count;                                                \
       -            if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {          \
       -               HASH_OOPS("invalid bucket count %u, actual %u\n",                 \
       -                (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);              \
       -            }                                                                    \
       -        }                                                                        \
       -        if (_count != (head)->hh.tbl->num_items) {                               \
       -            HASH_OOPS("invalid hh item count %u, actual %u\n",                   \
       -                (head)->hh.tbl->num_items, _count );                             \
       -        }                                                                        \
       -        /* traverse hh in app order; check next/prev integrity, count */         \
       -        _count = 0;                                                              \
       -        _prev = NULL;                                                            \
       -        _thh =  &(head)->hh;                                                     \
       -        while (_thh) {                                                           \
       -           _count++;                                                             \
       -           if (_prev !=(char*)(_thh->prev)) {                                    \
       -              HASH_OOPS("invalid prev %p, actual %p\n",                          \
       -                    _thh->prev, _prev );                                         \
       -           }                                                                     \
       -           _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                    \
       -           _thh = ( _thh->next ?  (UT_hash_handle*)((char*)(_thh->next) +        \
       -                                  (head)->hh.tbl->hho) : NULL );                 \
       -        }                                                                        \
       -        if (_count != (head)->hh.tbl->num_items) {                               \
       -            HASH_OOPS("invalid app item count %u, actual %u\n",                  \
       -                (head)->hh.tbl->num_items, _count );                             \
       -        }                                                                        \
       -    }                                                                            \
       -} while (0)
       -#else
       -#define HASH_FSCK(hh,head)
       -#endif
       -
       -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
       - * the descriptor to which this macro is defined for tuning the hash function.
       - * The app can #include <unistd.h> to get the prototype for write(2). */
       -#ifdef HASH_EMIT_KEYS
       -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \
       -do {                                                                             \
       -    unsigned _klen = fieldlen;                                                   \
       -    write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                \
       -    write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen);                      \
       -} while (0)
       -#else
       -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
       -#endif
       -
       -/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
       -#ifdef HASH_FUNCTION
       -#define HASH_FCN HASH_FUNCTION
       -#else
       -#define HASH_FCN HASH_JEN
       -#endif
       -
       -/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
       -#define HASH_BER(key,keylen,hashv)                                               \
       -do {                                                                             \
       -  unsigned _hb_keylen=(unsigned)keylen;                                          \
       -  const unsigned char *_hb_key=(const unsigned char*)(key);                      \
       -  (hashv) = 0;                                                                   \
       -  while (_hb_keylen-- != 0U) {                                                   \
       -      (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++;                         \
       -  }                                                                              \
       -} while (0)
       -
       -
       -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
       - * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
       -#define HASH_SAX(key,keylen,hashv)                                               \
       -do {                                                                             \
       -  unsigned _sx_i;                                                                \
       -  const unsigned char *_hs_key=(const unsigned char*)(key);                      \
       -  hashv = 0;                                                                     \
       -  for(_sx_i=0; _sx_i < keylen; _sx_i++) {                                        \
       -      hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                     \
       -  }                                                                              \
       -} while (0)
       -/* FNV-1a variation */
       -#define HASH_FNV(key,keylen,hashv)                                               \
       -do {                                                                             \
       -  unsigned _fn_i;                                                                \
       -  const unsigned char *_hf_key=(const unsigned char*)(key);                      \
       -  hashv = 2166136261U;                                                           \
       -  for(_fn_i=0; _fn_i < keylen; _fn_i++) {                                        \
       -      hashv = hashv ^ _hf_key[_fn_i];                                            \
       -      hashv = hashv * 16777619U;                                                 \
       -  }                                                                              \
       -} while (0)
       -
       -#define HASH_OAT(key,keylen,hashv)                                               \
       -do {                                                                             \
       -  unsigned _ho_i;                                                                \
       -  const unsigned char *_ho_key=(const unsigned char*)(key);                      \
       -  hashv = 0;                                                                     \
       -  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                        \
       -      hashv += _ho_key[_ho_i];                                                   \
       -      hashv += (hashv << 10);                                                    \
       -      hashv ^= (hashv >> 6);                                                     \
       -  }                                                                              \
       -  hashv += (hashv << 3);                                                         \
       -  hashv ^= (hashv >> 11);                                                        \
       -  hashv += (hashv << 15);                                                        \
       -} while (0)
       -
       -#define HASH_JEN_MIX(a,b,c)                                                      \
       -do {                                                                             \
       -  a -= b; a -= c; a ^= ( c >> 13 );                                              \
       -  b -= c; b -= a; b ^= ( a << 8 );                                               \
       -  c -= a; c -= b; c ^= ( b >> 13 );                                              \
       -  a -= b; a -= c; a ^= ( c >> 12 );                                              \
       -  b -= c; b -= a; b ^= ( a << 16 );                                              \
       -  c -= a; c -= b; c ^= ( b >> 5 );                                               \
       -  a -= b; a -= c; a ^= ( c >> 3 );                                               \
       -  b -= c; b -= a; b ^= ( a << 10 );                                              \
       -  c -= a; c -= b; c ^= ( b >> 15 );                                              \
       -} while (0)
       -
       -#define HASH_JEN(key,keylen,hashv)                                               \
       -do {                                                                             \
       -  unsigned _hj_i,_hj_j,_hj_k;                                                    \
       -  unsigned const char *_hj_key=(unsigned const char*)(key);                      \
       -  hashv = 0xfeedbeefu;                                                           \
       -  _hj_i = _hj_j = 0x9e3779b9u;                                                   \
       -  _hj_k = (unsigned)(keylen);                                                    \
       -  while (_hj_k >= 12U) {                                                         \
       -    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                      \
       -        + ( (unsigned)_hj_key[2] << 16 )                                         \
       -        + ( (unsigned)_hj_key[3] << 24 ) );                                      \
       -    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                      \
       -        + ( (unsigned)_hj_key[6] << 16 )                                         \
       -        + ( (unsigned)_hj_key[7] << 24 ) );                                      \
       -    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                         \
       -        + ( (unsigned)_hj_key[10] << 16 )                                        \
       -        + ( (unsigned)_hj_key[11] << 24 ) );                                     \
       -                                                                                 \
       -     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \
       -                                                                                 \
       -     _hj_key += 12;                                                              \
       -     _hj_k -= 12U;                                                               \
       -  }                                                                              \
       -  hashv += (unsigned)(keylen);                                                   \
       -  switch ( _hj_k ) {                                                             \
       -     case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */        \
       -     case 10: hashv += ( (unsigned)_hj_key[9] << 16 );  /* FALLTHROUGH */        \
       -     case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );   /* FALLTHROUGH */        \
       -     case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );  /* FALLTHROUGH */        \
       -     case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );  /* FALLTHROUGH */        \
       -     case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );   /* FALLTHROUGH */        \
       -     case 5:  _hj_j += _hj_key[4];                      /* FALLTHROUGH */        \
       -     case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );  /* FALLTHROUGH */        \
       -     case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );  /* FALLTHROUGH */        \
       -     case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );   /* FALLTHROUGH */        \
       -     case 1:  _hj_i += _hj_key[0];                                               \
       -  }                                                                              \
       -  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \
       -} while (0)
       -
       -/* The Paul Hsieh hash function */
       -#undef get16bits
       -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \
       -  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
       -#define get16bits(d) (*((const uint16_t *) (d)))
       -#endif
       -
       -#if !defined (get16bits)
       -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \
       -                       +(uint32_t)(((const uint8_t *)(d))[0]) )
       -#endif
       -#define HASH_SFH(key,keylen,hashv)                                               \
       -do {                                                                             \
       -  unsigned const char *_sfh_key=(unsigned const char*)(key);                     \
       -  uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen;                                \
       -                                                                                 \
       -  unsigned _sfh_rem = _sfh_len & 3U;                                             \
       -  _sfh_len >>= 2;                                                                \
       -  hashv = 0xcafebabeu;                                                           \
       -                                                                                 \
       -  /* Main loop */                                                                \
       -  for (;_sfh_len > 0U; _sfh_len--) {                                             \
       -    hashv    += get16bits (_sfh_key);                                            \
       -    _sfh_tmp  = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv;              \
       -    hashv     = (hashv << 16) ^ _sfh_tmp;                                        \
       -    _sfh_key += 2U*sizeof (uint16_t);                                            \
       -    hashv    += hashv >> 11;                                                     \
       -  }                                                                              \
       -                                                                                 \
       -  /* Handle end cases */                                                         \
       -  switch (_sfh_rem) {                                                            \
       -    case 3: hashv += get16bits (_sfh_key);                                       \
       -            hashv ^= hashv << 16;                                                \
       -            hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18;              \
       -            hashv += hashv >> 11;                                                \
       -            break;                                                               \
       -    case 2: hashv += get16bits (_sfh_key);                                       \
       -            hashv ^= hashv << 11;                                                \
       -            hashv += hashv >> 17;                                                \
       -            break;                                                               \
       -    case 1: hashv += *_sfh_key;                                                  \
       -            hashv ^= hashv << 10;                                                \
       -            hashv += hashv >> 1;                                                 \
       -  }                                                                              \
       -                                                                                 \
       -    /* Force "avalanching" of final 127 bits */                                  \
       -    hashv ^= hashv << 3;                                                         \
       -    hashv += hashv >> 5;                                                         \
       -    hashv ^= hashv << 4;                                                         \
       -    hashv += hashv >> 17;                                                        \
       -    hashv ^= hashv << 25;                                                        \
       -    hashv += hashv >> 6;                                                         \
       -} while (0)
       -
       -#ifdef HASH_USING_NO_STRICT_ALIASING
       -/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
       - * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
       - * MurmurHash uses the faster approach only on CPU's where we know it's safe.
       - *
       - * Note the preprocessor built-in defines can be emitted using:
       - *
       - *   gcc -m64 -dM -E - < /dev/null                  (on gcc)
       - *   cc -## a.c (where a.c is a simple test file)   (Sun Studio)
       - */
       -#if (defined(__i386__) || defined(__x86_64__)  || defined(_M_IX86))
       -#define MUR_GETBLOCK(p,i) p[i]
       -#else /* non intel */
       -#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL)
       -#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL)
       -#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL)
       -#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL)
       -#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
       -#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
       -#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
       -#define MUR_TWO_TWO(p)   ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
       -#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >>  8))
       -#else /* assume little endian non-intel */
       -#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
       -#define MUR_TWO_TWO(p)   ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
       -#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) <<  8))
       -#endif
       -#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) :           \
       -                            (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
       -                             (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) :  \
       -                                                      MUR_ONE_THREE(p))))
       -#endif
       -#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
       -#define MUR_FMIX(_h) \
       -do {                 \
       -  _h ^= _h >> 16;    \
       -  _h *= 0x85ebca6bu; \
       -  _h ^= _h >> 13;    \
       -  _h *= 0xc2b2ae35u; \
       -  _h ^= _h >> 16;    \
       -} while (0)
       -
       -#define HASH_MUR(key,keylen,hashv)                                     \
       -do {                                                                   \
       -  const uint8_t *_mur_data = (const uint8_t*)(key);                    \
       -  const int _mur_nblocks = (int)(keylen) / 4;                          \
       -  uint32_t _mur_h1 = 0xf88D5353u;                                      \
       -  uint32_t _mur_c1 = 0xcc9e2d51u;                                      \
       -  uint32_t _mur_c2 = 0x1b873593u;                                      \
       -  uint32_t _mur_k1 = 0;                                                \
       -  const uint8_t *_mur_tail;                                            \
       -  const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \
       -  int _mur_i;                                                          \
       -  for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) {                   \
       -    _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i);                        \
       -    _mur_k1 *= _mur_c1;                                                \
       -    _mur_k1 = MUR_ROTL32(_mur_k1,15);                                  \
       -    _mur_k1 *= _mur_c2;                                                \
       -                                                                       \
       -    _mur_h1 ^= _mur_k1;                                                \
       -    _mur_h1 = MUR_ROTL32(_mur_h1,13);                                  \
       -    _mur_h1 = (_mur_h1*5U) + 0xe6546b64u;                              \
       -  }                                                                    \
       -  _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4));          \
       -  _mur_k1=0;                                                           \
       -  switch((keylen) & 3U) {                                              \
       -    case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \
       -    case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8;  /* FALLTHROUGH */ \
       -    case 1: _mur_k1 ^= (uint32_t)_mur_tail[0];                         \
       -    _mur_k1 *= _mur_c1;                                                \
       -    _mur_k1 = MUR_ROTL32(_mur_k1,15);                                  \
       -    _mur_k1 *= _mur_c2;                                                \
       -    _mur_h1 ^= _mur_k1;                                                \
       -  }                                                                    \
       -  _mur_h1 ^= (uint32_t)(keylen);                                       \
       -  MUR_FMIX(_mur_h1);                                                   \
       -  hashv = _mur_h1;                                                     \
       -} while (0)
       -#endif  /* HASH_USING_NO_STRICT_ALIASING */
       -
       -/* iterate over items in a known bucket to find desired item */
       -#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out)               \
       -do {                                                                             \
       -  if ((head).hh_head != NULL) {                                                  \
       -    DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head));                     \
       -  } else {                                                                       \
       -    (out) = NULL;                                                                \
       -  }                                                                              \
       -  while ((out) != NULL) {                                                        \
       -    if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) {       \
       -      if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) {                \
       -        break;                                                                   \
       -      }                                                                          \
       -    }                                                                            \
       -    if ((out)->hh.hh_next != NULL) {                                             \
       -      DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next));                \
       -    } else {                                                                     \
       -      (out) = NULL;                                                              \
       -    }                                                                            \
       -  }                                                                              \
       -} while (0)
       -
       -/* add an item to a bucket  */
       -#define HASH_ADD_TO_BKT(head,addhh)                                              \
       -do {                                                                             \
       - head.count++;                                                                   \
       - (addhh)->hh_next = head.hh_head;                                                \
       - (addhh)->hh_prev = NULL;                                                        \
       - if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); }                \
       - (head).hh_head=addhh;                                                           \
       - if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH))          \
       -     && ((addhh)->tbl->noexpand != 1U)) {                                        \
       -       HASH_EXPAND_BUCKETS((addhh)->tbl);                                        \
       - }                                                                               \
       -} while (0)
       -
       -/* remove an item from a given bucket */
       -#define HASH_DEL_IN_BKT(hh,head,hh_del)                                          \
       -    (head).count--;                                                              \
       -    if ((head).hh_head == hh_del) {                                              \
       -      (head).hh_head = hh_del->hh_next;                                          \
       -    }                                                                            \
       -    if (hh_del->hh_prev) {                                                       \
       -        hh_del->hh_prev->hh_next = hh_del->hh_next;                              \
       -    }                                                                            \
       -    if (hh_del->hh_next) {                                                       \
       -        hh_del->hh_next->hh_prev = hh_del->hh_prev;                              \
       -    }
       -
       -/* Bucket expansion has the effect of doubling the number of buckets
       - * and redistributing the items into the new buckets. Ideally the
       - * items will distribute more or less evenly into the new buckets
       - * (the extent to which this is true is a measure of the quality of
       - * the hash function as it applies to the key domain).
       - *
       - * With the items distributed into more buckets, the chain length
       - * (item count) in each bucket is reduced. Thus by expanding buckets
       - * the hash keeps a bound on the chain length. This bounded chain
       - * length is the essence of how a hash provides constant time lookup.
       - *
       - * The calculation of tbl->ideal_chain_maxlen below deserves some
       - * explanation. First, keep in mind that we're calculating the ideal
       - * maximum chain length based on the *new* (doubled) bucket count.
       - * In fractions this is just n/b (n=number of items,b=new num buckets).
       - * Since the ideal chain length is an integer, we want to calculate
       - * ceil(n/b). We don't depend on floating point arithmetic in this
       - * hash, so to calculate ceil(n/b) with integers we could write
       - *
       - *      ceil(n/b) = (n/b) + ((n%b)?1:0)
       - *
       - * and in fact a previous version of this hash did just that.
       - * But now we have improved things a bit by recognizing that b is
       - * always a power of two. We keep its base 2 log handy (call it lb),
       - * so now we can write this with a bit shift and logical AND:
       - *
       - *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
       - *
       - */
       -#define HASH_EXPAND_BUCKETS(tbl)                                                 \
       -do {                                                                             \
       -    unsigned _he_bkt;                                                            \
       -    unsigned _he_bkt_i;                                                          \
       -    struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                 \
       -    UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                \
       -    _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                            \
       -             2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket));            \
       -    if (!_he_new_buckets) { uthash_fatal( "out of memory"); }                    \
       -    memset(_he_new_buckets, 0,                                                   \
       -            2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket));             \
       -    tbl->ideal_chain_maxlen =                                                    \
       -       (tbl->num_items >> (tbl->log2_num_buckets+1U)) +                          \
       -       (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U);        \
       -    tbl->nonideal_items = 0;                                                     \
       -    for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++)                \
       -    {                                                                            \
       -        _he_thh = tbl->buckets[ _he_bkt_i ].hh_head;                             \
       -        while (_he_thh != NULL) {                                                \
       -           _he_hh_nxt = _he_thh->hh_next;                                        \
       -           HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt);           \
       -           _he_newbkt = &(_he_new_buckets[ _he_bkt ]);                           \
       -           if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) {                \
       -             tbl->nonideal_items++;                                              \
       -             _he_newbkt->expand_mult = _he_newbkt->count /                       \
       -                                        tbl->ideal_chain_maxlen;                 \
       -           }                                                                     \
       -           _he_thh->hh_prev = NULL;                                              \
       -           _he_thh->hh_next = _he_newbkt->hh_head;                               \
       -           if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev =     \
       -                _he_thh; }                                                       \
       -           _he_newbkt->hh_head = _he_thh;                                        \
       -           _he_thh = _he_hh_nxt;                                                 \
       -        }                                                                        \
       -    }                                                                            \
       -    uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
       -    tbl->num_buckets *= 2U;                                                      \
       -    tbl->log2_num_buckets++;                                                     \
       -    tbl->buckets = _he_new_buckets;                                              \
       -    tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ?         \
       -        (tbl->ineff_expands+1U) : 0U;                                            \
       -    if (tbl->ineff_expands > 1U) {                                               \
       -        tbl->noexpand=1;                                                         \
       -        uthash_noexpand_fyi(tbl);                                                \
       -    }                                                                            \
       -    uthash_expand_fyi(tbl);                                                      \
       -} while (0)
       -
       -
       -/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
       -/* Note that HASH_SORT assumes the hash handle name to be hh.
       - * HASH_SRT was added to allow the hash handle name to be passed in. */
       -#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
       -#define HASH_SRT(hh,head,cmpfcn)                                                 \
       -do {                                                                             \
       -  unsigned _hs_i;                                                                \
       -  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \
       -  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \
       -  if (head != NULL) {                                                            \
       -      _hs_insize = 1;                                                            \
       -      _hs_looping = 1;                                                           \
       -      _hs_list = &((head)->hh);                                                  \
       -      while (_hs_looping != 0U) {                                                \
       -          _hs_p = _hs_list;                                                      \
       -          _hs_list = NULL;                                                       \
       -          _hs_tail = NULL;                                                       \
       -          _hs_nmerges = 0;                                                       \
       -          while (_hs_p != NULL) {                                                \
       -              _hs_nmerges++;                                                     \
       -              _hs_q = _hs_p;                                                     \
       -              _hs_psize = 0;                                                     \
       -              for ( _hs_i = 0; _hs_i  < _hs_insize; _hs_i++ ) {                  \
       -                  _hs_psize++;                                                   \
       -                  _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?              \
       -                          ((void*)((char*)(_hs_q->next) +                        \
       -                          (head)->hh.tbl->hho)) : NULL);                         \
       -                  if (! (_hs_q) ) { break; }                                     \
       -              }                                                                  \
       -              _hs_qsize = _hs_insize;                                            \
       -              while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\
       -                  if (_hs_psize == 0U) {                                         \
       -                      _hs_e = _hs_q;                                             \
       -                      _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?          \
       -                              ((void*)((char*)(_hs_q->next) +                    \
       -                              (head)->hh.tbl->hho)) : NULL);                     \
       -                      _hs_qsize--;                                               \
       -                  } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) {           \
       -                      _hs_e = _hs_p;                                             \
       -                      if (_hs_p != NULL){                                        \
       -                        _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ?        \
       -                                ((void*)((char*)(_hs_p->next) +                  \
       -                                (head)->hh.tbl->hho)) : NULL);                   \
       -                       }                                                         \
       -                      _hs_psize--;                                               \
       -                  } else if ((                                                   \
       -                      cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
       -                             DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
       -                             ) <= 0) {                                           \
       -                      _hs_e = _hs_p;                                             \
       -                      if (_hs_p != NULL){                                        \
       -                        _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ?        \
       -                               ((void*)((char*)(_hs_p->next) +                   \
       -                               (head)->hh.tbl->hho)) : NULL);                    \
       -                       }                                                         \
       -                      _hs_psize--;                                               \
       -                  } else {                                                       \
       -                      _hs_e = _hs_q;                                             \
       -                      _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?          \
       -                              ((void*)((char*)(_hs_q->next) +                    \
       -                              (head)->hh.tbl->hho)) : NULL);                     \
       -                      _hs_qsize--;                                               \
       -                  }                                                              \
       -                  if ( _hs_tail != NULL ) {                                      \
       -                      _hs_tail->next = ((_hs_e != NULL) ?                        \
       -                            ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL);          \
       -                  } else {                                                       \
       -                      _hs_list = _hs_e;                                          \
       -                  }                                                              \
       -                  if (_hs_e != NULL) {                                           \
       -                  _hs_e->prev = ((_hs_tail != NULL) ?                            \
       -                     ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL);              \
       -                  }                                                              \
       -                  _hs_tail = _hs_e;                                              \
       -              }                                                                  \
       -              _hs_p = _hs_q;                                                     \
       -          }                                                                      \
       -          if (_hs_tail != NULL){                                                 \
       -            _hs_tail->next = NULL;                                               \
       -          }                                                                      \
       -          if ( _hs_nmerges <= 1U ) {                                             \
       -              _hs_looping=0;                                                     \
       -              (head)->hh.tbl->tail = _hs_tail;                                   \
       -              DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list));      \
       -          }                                                                      \
       -          _hs_insize *= 2U;                                                      \
       -      }                                                                          \
       -      HASH_FSCK(hh,head);                                                        \
       - }                                                                               \
       -} while (0)
       -
       -/* This function selects items from one hash into another hash.
       - * The end result is that the selected items have dual presence
       - * in both hashes. There is no copy of the items made; rather
       - * they are added into the new hash through a secondary hash
       - * hash handle that must be present in the structure. */
       -#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \
       -do {                                                                             \
       -  unsigned _src_bkt, _dst_bkt;                                                   \
       -  void *_last_elt=NULL, *_elt;                                                   \
       -  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \
       -  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \
       -  if (src != NULL) {                                                             \
       -    for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {     \
       -      for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;                \
       -          _src_hh != NULL;                                                       \
       -          _src_hh = _src_hh->hh_next) {                                          \
       -          _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                       \
       -          if (cond(_elt)) {                                                      \
       -            _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho);               \
       -            _dst_hh->key = _src_hh->key;                                         \
       -            _dst_hh->keylen = _src_hh->keylen;                                   \
       -            _dst_hh->hashv = _src_hh->hashv;                                     \
       -            _dst_hh->prev = _last_elt;                                           \
       -            _dst_hh->next = NULL;                                                \
       -            if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; }             \
       -            if (dst == NULL) {                                                   \
       -              DECLTYPE_ASSIGN(dst,_elt);                                         \
       -              HASH_MAKE_TABLE(hh_dst,dst);                                       \
       -            } else {                                                             \
       -              _dst_hh->tbl = (dst)->hh_dst.tbl;                                  \
       -            }                                                                    \
       -            HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);    \
       -            HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh);            \
       -            (dst)->hh_dst.tbl->num_items++;                                      \
       -            _last_elt = _elt;                                                    \
       -            _last_elt_hh = _dst_hh;                                              \
       -          }                                                                      \
       -      }                                                                          \
       -    }                                                                            \
       -  }                                                                              \
       -  HASH_FSCK(hh_dst,dst);                                                         \
       -} while (0)
       -
       -#define HASH_CLEAR(hh,head)                                                      \
       -do {                                                                             \
       -  if (head != NULL) {                                                            \
       -    uthash_free((head)->hh.tbl->buckets,                                         \
       -                (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \
       -    HASH_BLOOM_FREE((head)->hh.tbl);                                             \
       -    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
       -    (head)=NULL;                                                                 \
       -  }                                                                              \
       -} while (0)
       -
       -#define HASH_OVERHEAD(hh,head)                                                   \
       - ((head != NULL) ? (                                                             \
       - (size_t)(((head)->hh.tbl->num_items   * sizeof(UT_hash_handle))   +             \
       -          ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket))   +             \
       -           sizeof(UT_hash_table)                                   +             \
       -           (HASH_BLOOM_BYTELEN))) : 0U)
       -
       -#ifdef NO_DECLTYPE
       -#define HASH_ITER(hh,head,el,tmp)                                                \
       -for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
       -  (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
       -#else
       -#define HASH_ITER(hh,head,el,tmp)                                                \
       -for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL));      \
       -  (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
       -#endif
       -
       -/* obtain a count of items in the hash */
       -#define HASH_COUNT(head) HASH_CNT(hh,head)
       -#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
       -
       -typedef struct UT_hash_bucket {
       -   struct UT_hash_handle *hh_head;
       -   unsigned count;
       -
       -   /* expand_mult is normally set to 0. In this situation, the max chain length
       -    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
       -    * the bucket's chain exceeds this length, bucket expansion is triggered).
       -    * However, setting expand_mult to a non-zero value delays bucket expansion
       -    * (that would be triggered by additions to this particular bucket)
       -    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
       -    * (The multiplier is simply expand_mult+1). The whole idea of this
       -    * multiplier is to reduce bucket expansions, since they are expensive, in
       -    * situations where we know that a particular bucket tends to be overused.
       -    * It is better to let its chain length grow to a longer yet-still-bounded
       -    * value, than to do an O(n) bucket expansion too often.
       -    */
       -   unsigned expand_mult;
       -
       -} UT_hash_bucket;
       -
       -/* random signature used only to find hash tables in external analysis */
       -#define HASH_SIGNATURE 0xa0111fe1u
       -#define HASH_BLOOM_SIGNATURE 0xb12220f2u
       -
       -typedef struct UT_hash_table {
       -   UT_hash_bucket *buckets;
       -   unsigned num_buckets, log2_num_buckets;
       -   unsigned num_items;
       -   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
       -   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
       -
       -   /* in an ideal situation (all buckets used equally), no bucket would have
       -    * more than ceil(#items/#buckets) items. that's the ideal chain length. */
       -   unsigned ideal_chain_maxlen;
       -
       -   /* nonideal_items is the number of items in the hash whose chain position
       -    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
       -    * hash distribution; reaching them in a chain traversal takes >ideal steps */
       -   unsigned nonideal_items;
       -
       -   /* ineffective expands occur when a bucket doubling was performed, but
       -    * afterward, more than half the items in the hash had nonideal chain
       -    * positions. If this happens on two consecutive expansions we inhibit any
       -    * further expansion, as it's not helping; this happens when the hash
       -    * function isn't a good fit for the key domain. When expansion is inhibited
       -    * the hash will still work, albeit no longer in constant time. */
       -   unsigned ineff_expands, noexpand;
       -
       -   uint32_t signature; /* used only to find hash tables in external analysis */
       -#ifdef HASH_BLOOM
       -   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
       -   uint8_t *bloom_bv;
       -   uint8_t bloom_nbits;
       -#endif
       -
       -} UT_hash_table;
       -
       -typedef struct UT_hash_handle {
       -   struct UT_hash_table *tbl;
       -   void *prev;                       /* prev element in app order      */
       -   void *next;                       /* next element in app order      */
       -   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
       -   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
       -   void *key;                        /* ptr to enclosing struct's key  */
       -   unsigned keylen;                  /* enclosing struct's key len     */
       -   unsigned hashv;                   /* result of hash-fcn(key)        */
       -} UT_hash_handle;
       -
       -#endif /* UTHASH_H */