From e5332f1aa9226a0934ae616828e1dbf603d50d91 Mon Sep 17 00:00:00 2001
From: Richard Nyberg <rnyberg@murmeldjur.se>
Date: Fri, 6 Feb 2009 20:03:56 +0100
Subject: [PATCH] Hashtable tweaks.

o Added ability to set the ratio items:buckets.
o One can remove items while iterating.
o _htbl_tov now allocates the result array, _htbl_fillv acts as the old
  _htbl_tov did.
---
 misc/hashtable.c | 86 +++++++++++++++++++++++++++++++++++++-----------
 misc/hashtable.h | 36 +++++++++++++-------
 2 files changed, 92 insertions(+), 30 deletions(-)

diff --git a/misc/hashtable.c b/misc/hashtable.c
index b124749..519af5f 100644
--- a/misc/hashtable.c
+++ b/misc/hashtable.c
@@ -11,13 +11,14 @@ struct _htbl {
     size_t size;
     size_t keyoff;
     size_t chainoff;
+    float ratio;
 };
 
 #define KEYP(tbl, o) ((void *)(o) + (tbl)->keyoff)
 #define CHAINP(tbl, o) *(struct _any **)((void *)(o) + (tbl)->chainoff)
 
 struct _htbl *
-_htbl_create(int (*eq)(const void *, const void *),
+_htbl_create(float ratio, int (*eq)(const void *, const void *),
     uint32_t (*hash)(const void *), size_t keyoff, size_t chainoff)
 {
     struct _htbl *tbl = calloc(1, sizeof(*tbl));
@@ -25,6 +26,7 @@ _htbl_create(int (*eq)(const void *, const void *),
         return NULL;
     tbl->size = 0;
     tbl->buckcnt = 1;
+    tbl->ratio = ratio;
     tbl->keyoff = keyoff;
     tbl->chainoff = chainoff;
     tbl->hash = hash;
@@ -94,7 +96,7 @@ _htbl_insert(struct _htbl *tbl, struct _any *o)
 {
     bucket_insert(tbl, o);
     tbl->size++;
-    if (tbl->size > tbl->buckcnt * 4 / 5)
+    if (tbl->size > tbl->buckcnt * tbl->ratio)
         _htbl_grow(tbl);
 }
 
@@ -113,23 +115,20 @@ struct _any *
 _htbl_remove(struct _htbl *tbl, const void *key)
 {
     size_t bi = tbl->hash(key) % tbl->buckcnt;
-    struct _any *p = NULL, *o = tbl->buckets[bi];
+    struct _any **p = &tbl->buckets[bi], *o = tbl->buckets[bi];
     while (o != NULL && !tbl->eq(KEYP(tbl, o), key)) {
-        p = o;
-        o = CHAINP(tbl, o);
+        p = &CHAINP(tbl, o);
+        o = *p;
     }
     if (o != NULL) {
-        if (p == NULL)
-            tbl->buckets[bi] = CHAINP(tbl, o);
-        else
-            CHAINP(tbl, p) = CHAINP(tbl, o);
+        *p = CHAINP(tbl, o);
         tbl->size--;
     }
     return o;
 }
 
 void
-_htbl_tov(struct _htbl *tbl, struct _any **v)
+_htbl_fillv(struct _htbl *tbl, struct _any **v)
 {
     size_t vi = 0;
     size_t bi = 0;
@@ -145,32 +144,81 @@ _htbl_tov(struct _htbl *tbl, struct _any **v)
     }
 }
 
+struct _any **
+_htbl_tov(struct _htbl *tbl)
+{
+    struct _any **v = malloc(sizeof(*v));
+    if (v != NULL)
+        _htbl_fillv(tbl, v);
+    return v;
+}
+
 size_t
 _htbl_size(struct _htbl *tbl)
 {
     return tbl->size;
 }
 
-void
-_htbl_iter_init(struct _htbl *tbl, struct htbl_iter *it)
+static void
+iter_next_bucket(struct htbl_iter *it)
 {
+    while (it->tbl->buckets[it->bi] == NULL)
+        it->bi++;
+    it->obj = it->tbl->buckets[it->bi];
+    it->ptr = &it->tbl->buckets[it->bi];
+}
+
+struct _any *
+_htbl_iter_first(struct _htbl *tbl, struct htbl_iter *it)
+{
+    if (tbl->size == 0)
+        return NULL;
+
     it->tbl = tbl;
+    it->cnt = 1;
     it->bi = 0;
-    it->cnt = 0;
-    it->obj = NULL;
+    iter_next_bucket(it);
+    return it->obj;
 }
 
 struct _any *
 _htbl_iter_next(struct htbl_iter *it)
 {
+    struct _any *tmp;
     if (it->cnt == it->tbl->size)
         return NULL;
-    it->obj = it->cnt == 0 ?
-        it->tbl->buckets[it->bi] : CHAINP(it->tbl, it->obj);
-    while (it->obj == NULL) {
+
+    it->cnt++;
+    if ((tmp = CHAINP(it->tbl, it->obj)) != NULL) {
+        it->ptr = &CHAINP(it->tbl, it->obj);
+        it->obj = tmp;
+    } else {
         it->bi++;
-        it->obj = it->tbl->buckets[it->bi];
+        iter_next_bucket(it);
+    }
+    return it->obj;
+}
+
+#include <stdio.h>
+
+struct _any *
+_htbl_iter_del(struct htbl_iter *it)
+{
+    struct _any *tmp;
+
+    it->tbl->size--;
+    if (it->cnt > it->tbl->size) {
+        *it->ptr = NULL;
+        return NULL;
+    }
+
+    if ((tmp = CHAINP(it->tbl, it->obj)) != NULL) {
+        *it->ptr = tmp;
+        it->obj = tmp;
+    } else {
+        *it->ptr = NULL;
+        it->bi++;
+        iter_next_bucket(it);
     }
-    it->cnt++;
     return it->obj;
 }
diff --git a/misc/hashtable.h b/misc/hashtable.h
index 84a8e0b..adc4229 100644
--- a/misc/hashtable.h
+++ b/misc/hashtable.h
@@ -5,30 +5,34 @@ struct htbl_iter {
     struct _htbl *tbl;
     size_t bi;
     size_t cnt;
+    struct _any **ptr;
     struct _any *obj;
 };
 
-struct _htbl *_htbl_create(int (*equal)(const void *, const void *),
+struct _htbl *_htbl_create(float ratio,
+    int (*equal)(const void *, const void *),
     uint32_t (*hash)(const void *), size_t keyoff, size_t chainoff);
 void _htbl_free(struct _htbl *tbl);
 void _htbl_insert(struct _htbl *tbl, struct _any *o);
 struct _any *_htbl_remove(struct _htbl *tbl, const void *key);
 struct _any *_htbl_find(struct _htbl *tbl, const void *key);
-void _htbl_tov(struct _htbl *tb, struct _any **v);
+void _htbl_fillv(struct _htbl *tbl, struct _any **v);
+struct _any **_htbl_tov(struct _htbl *tbl);
 size_t _htbl_size(struct _htbl *tbl);
-void _htbl_iter_init(struct _htbl *tbl, struct htbl_iter *it);
+struct _any *_htbl_iter_first(struct _htbl *tbl, struct htbl_iter *it);
 struct _any *_htbl_iter_next(struct htbl_iter *it);
+struct _any *_htbl_iter_del(struct htbl_iter *it);
 
 #define HTBL_ENTRY(name) struct _any *name
 
 #define HTBL_TYPE(name, type, ktype, kname, cname) \
 __attribute__((always_inline)) static inline struct name * \
-name##_create(int (*equal)(const void *, const void *), \
+name##_create(float ratio, int (*equal)(const void *, const void *), \
     uint32_t (*hash)(const void *)) \
 { \
     return (struct name *) \
-      _htbl_create(equal, hash, offsetof(struct type, kname), \
-        offsetof(struct type, cname)); \
+        _htbl_create(ratio, equal, hash, offsetof(struct type, kname), \
+            offsetof(struct type, cname)); \
 } \
 \
 __attribute__((always_inline)) static inline struct type * \
@@ -54,10 +58,15 @@ name##_insert(struct name *tbl, struct type *o) \
 { \
     _htbl_insert((struct _htbl *)tbl, (struct _any *)o); \
 } \
+__attribute__((always_inline)) static inline struct type ** \
+name##_tov(struct name *tbl) \
+{ \
+    return (struct type **) _htbl_tov((struct _htbl *)tbl); \
+} \
 __attribute__((always_inline)) static inline void \
-name##_tov(struct name *tbl, struct type **v) \
+name##_fillv(struct name *tbl, struct type **v) \
 { \
-    _htbl_tov((struct _htbl *)tbl, (struct _any **)v); \
+    _htbl_fillv((struct _htbl *)tbl, (struct _any **)v); \
 } \
 \
 __attribute__((always_inline)) static inline size_t \
@@ -66,13 +75,18 @@ name##_size(struct name *tbl) \
     return _htbl_size((struct _htbl *)tbl); \
 } \
 \
-__attribute__((always_inline)) static inline void \
-name##_iter_init(struct name *tbl, struct htbl_iter *it) \
+__attribute__((always_inline)) static inline struct type * \
+name##_iter_first(struct name *tbl, struct htbl_iter *it) \
 { \
-    _htbl_iter_init((struct _htbl *)tbl, it); \
+    return (struct type *)_htbl_iter_first((struct _htbl *)tbl, it); \
 } \
 \
 __attribute__((always_inline)) static inline struct type * \
+name##_iter_del(struct htbl_iter *it) \
+{ \
+    return (struct type *)_htbl_iter_del(it); \
+} \
+__attribute__((always_inline)) static inline struct type * \
 name##_iter_next(struct htbl_iter *it) \
 { \
     return (struct type *)_htbl_iter_next(it); \