commit 323ceb5cbc54b4d70002a4f4e6e5ae9d308793c4
parent a06777c1797439f43e6b8a1026a53c5e6ac9298e
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(-)
diff --git a/Makefile b/Makefile
@@ -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
diff --git a/NOTES b/NOTES
@@ -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
diff --git a/README.md b/README.md
@@ -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)
 
diff --git a/button.c b/button.c
@@ -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
@@ -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)
 {
@@ -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;
@@ -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,
@@ -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;
 }
@@ -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);
 }
 
diff --git a/button.h b/button.h
@@ -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
@@ -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 {
diff --git a/grid.c b/grid.c
@@ -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
diff --git a/grid.h b/grid.h
@@ -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
diff --git a/khash.h b/khash.h
@@ -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 */
diff --git a/ltk.c b/ltk.c
@@ -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
@@ -21,8 +21,8 @@
  * SOFTWARE.
  */
 
+#include "text-hb.h"
 #include "ltk.h"
-#include "text.h"
 
 void ltk_init(const char *theme_path)
 {
@@ -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);
 }
 
@@ -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;
 }
@@ -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);
@@ -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);
 	}
 }
 
@@ -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;
@@ -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;
diff --git a/ltk.h b/ltk.h
@@ -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
@@ -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 {
@@ -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;
@@ -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;
 
@@ -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);
 
diff --git a/test1.c b/test1.c
@@ -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;
@@ -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();
 }
diff --git a/text-hb.c b/text-hb.c
@@ -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;
+}
diff --git a/text-hb.h b/text-hb.h
@@ -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
diff --git a/text.c b/text.c
@@ -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;
diff --git a/text/Makefile b/text/Makefile
@@ -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
diff --git a/text/text-hb.ubernew.c b/text/text-hb.ubernew.c
@@ -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);
diff --git a/text/text-hb.uberubernew.c b/text/text-hb.uberubernew.c
@@ -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;
+}
diff --git a/uthash.h b/uthash.h
@@ -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 */