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 */