]> source.dussan.org Git - rspamd.git/commitdiff
[Rework] Core: Improve structure of lru hash, get rid of GHashTable
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 10 Dec 2018 12:20:05 +0000 (12:20 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 10 Dec 2018 12:20:05 +0000 (12:20 +0000)
src/fuzzy_storage.c
src/libutil/hash.c
src/libutil/hash.h

index 2d53d969b257b53035c61843f8b6c9bb8810d8ab..36b41113dd4cd870cd390f0d16be5d81a7a2c28e 100644 (file)
@@ -2201,11 +2201,9 @@ rspamd_fuzzy_storage_stat_key (struct fuzzy_key_stat *key_stat)
 static ucl_object_t *
 rspamd_fuzzy_stat_to_ucl (struct rspamd_fuzzy_storage_ctx *ctx, gboolean ip_stat)
 {
-       GHashTableIter it, ip_it;
-       GHashTable *ip_hash;
        struct fuzzy_key_stat *key_stat;
+       GHashTableIter it;
        struct fuzzy_key *key;
-       rspamd_lru_element_t *lru_elt;
        ucl_object_t *obj, *keys_obj, *elt, *ip_elt, *ip_cur;
        gpointer k, v;
        gint i;
@@ -2226,22 +2224,18 @@ rspamd_fuzzy_stat_to_ucl (struct rspamd_fuzzy_storage_ctx *ctx, gboolean ip_stat
                        elt = rspamd_fuzzy_storage_stat_key (key_stat);
 
                        if (key_stat->last_ips && ip_stat) {
-                               ip_hash = rspamd_lru_hash_get_htable (key_stat->last_ips);
-
-                               if (ip_hash) {
-                                       g_hash_table_iter_init (&ip_it, ip_hash);
-                                       ip_elt = ucl_object_typed_new (UCL_OBJECT);
-
-                                       while (g_hash_table_iter_next (&ip_it, &k, &v)) {
-                                               lru_elt = v;
-                                               ip_cur = rspamd_fuzzy_storage_stat_key (
-                                                               rspamd_lru_hash_element_data (lru_elt));
-                                               ucl_object_insert_key (ip_elt, ip_cur,
-                                                               rspamd_inet_address_to_string (k), 0, true);
-                                       }
+                               i = 0;
+
+                               ip_elt = ucl_object_typed_new (UCL_OBJECT);
 
-                                       ucl_object_insert_key (elt, ip_elt, "ips", 0, false);
+                               while ((i = rspamd_lru_hash_foreach (key_stat->last_ips,
+                                               i, &k, &v)) != -1) {
+                                       ip_cur = rspamd_fuzzy_storage_stat_key (v);
+                                       ucl_object_insert_key (ip_elt, ip_cur,
+                                                       rspamd_inet_address_to_string (k), 0, true);
                                }
+
+                               ucl_object_insert_key (elt, ip_elt, "ips", 0, false);
                        }
 
                        ucl_object_insert_key (keys_obj, elt, keyname, 0, true);
@@ -2268,27 +2262,21 @@ rspamd_fuzzy_stat_to_ucl (struct rspamd_fuzzy_storage_ctx *ctx, gboolean ip_stat
                        false);
 
        if (ctx->errors_ips && ip_stat) {
-               ip_hash = rspamd_lru_hash_get_htable (ctx->errors_ips);
-
-               if (ip_hash) {
-                       g_hash_table_iter_init (&ip_it, ip_hash);
-                       ip_elt = ucl_object_typed_new (UCL_OBJECT);
+               i = 0;
 
-                       while (g_hash_table_iter_next (&ip_it, &k, &v)) {
-                               lru_elt = v;
+               ip_elt = ucl_object_typed_new (UCL_OBJECT);
 
-                               ucl_object_insert_key (ip_elt,
-                                               ucl_object_fromint (*(guint64 *)
-                                                               rspamd_lru_hash_element_data (lru_elt)),
-                                               rspamd_inet_address_to_string (k), 0, true);
-                       }
-
-                       ucl_object_insert_key (obj,
-                                       ip_elt,
-                                       "errors_ips",
-                                       0,
-                                       false);
+               while ((i = rspamd_lru_hash_foreach (ctx->errors_ips, i, &k, &v)) != -1) {
+                       ucl_object_insert_key (ip_elt,
+                                       ucl_object_fromint (*(guint64 *)v),
+                                       rspamd_inet_address_to_string (k), 0, true);
                }
+
+               ucl_object_insert_key (obj,
+                               ip_elt,
+                               "errors_ips",
+                               0,
+                               false);
        }
 
        /* Checked by epoch */
index bc438830f2e227cfcfcd0ebda63b0a6c0f117727..086eba8d1a2d31f8acc97ff807f4f3d4d8f072d2 100644 (file)
@@ -16,6 +16,7 @@
 #include "config.h"
 #include "hash.h"
 #include "util.h"
+#include "khash.h"
 
 /**
  * LRU hashing
@@ -25,14 +26,23 @@ static const guint log_base = 10;
 static const guint eviction_candidates = 16;
 static const gdouble lfu_base_value = 5.0;
 
+struct rspamd_lru_volatile_element_s;
+
 struct rspamd_lru_hash_s {
        guint maxsize;
        guint eviction_min_prio;
        guint eviction_used;
+       struct rspamd_lru_element_s **eviction_pool;
+
        GDestroyNotify value_destroy;
        GDestroyNotify key_destroy;
-       struct rspamd_lru_element_s **eviction_pool;
-       GHashTable *tbl;
+       GHashFunc hfunc;
+       GEqualFunc eqfunc;
+
+       khint_t n_buckets, size, n_occupied, upper_bound;
+       khint32_t *flags;
+       gpointer *keys;
+       struct rspamd_lru_volatile_element_s *vals;
 };
 
 enum rspamd_lru_element_flags {
@@ -46,8 +56,6 @@ struct rspamd_lru_element_s {
        guint8 eviction_pos;
        guint8 flags;
        gpointer data;
-       gpointer key;
-       rspamd_lru_hash_t *hash;
 };
 
 struct rspamd_lru_volatile_element_s {
@@ -59,20 +67,240 @@ typedef struct rspamd_lru_volatile_element_s rspamd_lru_vol_element_t;
 
 #define TIME_TO_TS(t) ((guint16)(((t) / 60) & 0xFFFFU))
 
-static void
-rspamd_lru_destroy_node (gpointer value)
+static rspamd_lru_vol_element_t *
+rspamd_lru_hash_get (const rspamd_lru_hash_t *h, gconstpointer key)
+{
+       if (h->n_buckets) {
+               khint_t k, i, last, mask, step = 0;
+               mask = h->n_buckets - 1;
+               k = h->hfunc (key);
+               i = k & mask;
+               last = i;
+
+               while (!__ac_isempty(h->flags, i) &&
+                       (__ac_isdel(h->flags, i) || !h->eqfunc(h->keys[i], key))) {
+                       i = (i + (++step)) & mask;
+                       if (i == last) {
+                               return NULL;
+                       }
+               }
+
+               return __ac_iseither(h->flags, i) ? NULL : &h->vals[i];
+       }
+
+       return NULL;
+}
+
+static int
+rspamd_lru_hash_resize (rspamd_lru_hash_t *h,
+                                               khint_t new_n_buckets)
 {
-       rspamd_lru_element_t *elt = (rspamd_lru_element_t *)value;
+       /* 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 *) g_malloc(__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 */
+                       gpointer *new_keys = (gpointer *) g_realloc((void *) h->keys,
+                                       new_n_buckets * sizeof (gpointer));
+
+                       if (!new_keys) {
+                               g_free(new_flags);
+                               return -1;
+                       }
+
+                       h->keys = new_keys;
+                       rspamd_lru_vol_element_t *new_vals =
+                                       (rspamd_lru_vol_element_t *) g_realloc((void *) h->vals,
+                                                       new_n_buckets * sizeof (rspamd_lru_vol_element_t));
+                       if (!new_vals) {
+                               g_free(new_flags);
+                               return -1;
+                       }
+
+                       h->vals = new_vals;
+               }
+               /* Shrink */
+       }
+
+       if (j) {
+               /* rehashing is needed */
+               h->eviction_used = 0;
+
+               for (j = 0; j != h->n_buckets; ++j) {
+                       if (__ac_iseither(h->flags, j) == 0) {
+                               gpointer key = h->keys[j];
+                               rspamd_lru_vol_element_t val;
+                               khint_t new_mask;
+                               new_mask = new_n_buckets - 1;
+                               val = h->vals[j];
+                               val.e.eviction_pos = (guint8)-1;
+                               __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 = h->hfunc(key);
+                                       i = k & new_mask;
+
+                                       while (!__ac_isempty(new_flags, i)) {
+                                               i = (i + (++step)) & new_mask;
+                                       }
 
-       if (elt) {
-               if (elt->hash && elt->hash->key_destroy) {
-                       elt->hash->key_destroy (elt->key);
+                                       __ac_set_isempty_false(new_flags, i);
+
+                                       if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) {
+                                               /* kick out the existing element */
+                                               {
+                                                       gpointer tmp = h->keys[i];
+                                                       h->keys[i] = key;
+                                                       key = tmp;
+                                               }
+                                               {
+                                                       rspamd_lru_vol_element_t tmp = h->vals[i];
+                                                       h->vals[i] = val;
+                                                       val = tmp;
+                                                       val.e.eviction_pos = (guint8)-1;
+                                               }
+                                               __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;
+                                               h->vals[i] = val;
+                                               break;
+                                       }
+                               }
+                       }
                }
-               if (elt->hash && elt->hash->value_destroy) {
-                       elt->hash->value_destroy (elt->data);
+
+               if (h->n_buckets > new_n_buckets) {
+                       /* shrink the hash table */
+                       h->keys = (gpointer *) g_realloc((void *) h->keys,
+                                       new_n_buckets * sizeof (gpointer));
+                       h->vals = (rspamd_lru_vol_element_t *) g_realloc((void *) h->vals,
+                                       new_n_buckets * sizeof (rspamd_lru_vol_element_t));
                }
 
-               g_free (elt);
+               g_free(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;
+}
+
+static rspamd_lru_vol_element_t *
+rspamd_lru_hash_put (rspamd_lru_hash_t *h, gpointer 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 (rspamd_lru_hash_resize (h, h->n_buckets - 1) < 0) {
+                               /* clear "deleted" elements */
+                               *ret = -1;
+                               return NULL;
+                       }
+               }
+               else if (rspamd_lru_hash_resize (h, h->n_buckets + 1) < 0) {
+                       /* expand the hash table */
+                       *ret = -1;
+                       return NULL;
+               }
+       }
+
+       khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0;
+       x = site = h->n_buckets;
+       k = h->hfunc(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) ||
+                          !h->eqfunc (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 {
+               /* Don't touch h->keys[x] if present and not deleted */
+               *ret = 0;
+       }
+
+       return &h->vals[x];
+}
+
+static void
+rspamd_lru_hash_del (rspamd_lru_hash_t *h, rspamd_lru_vol_element_t *elt)
+{
+       khint_t x = elt - h->vals;
+
+       if (x != h->n_buckets && !__ac_iseither(h->flags, x)) {
+               __ac_set_isdel_true(h->flags, x);
+               --h->size;
+
+               if (h->key_destroy) {
+                       h->key_destroy (h->keys[x]);
+               }
+
+               if (h->value_destroy) {
+                       h->value_destroy (elt->e.data);
+               }
        }
 }
 
@@ -193,38 +421,6 @@ rspamd_lru_hash_maybe_evict (rspamd_lru_hash_t *hash,
        return FALSE;
 }
 
-static rspamd_lru_element_t *
-rspamd_lru_create_node (rspamd_lru_hash_t *hash,
-                                               gpointer key,
-                                               gpointer value,
-                                               time_t now,
-                                               guint ttl)
-{
-       rspamd_lru_element_t *node;
-       rspamd_lru_vol_element_t *vnode;
-
-       if (ttl == 0) {
-               node = g_malloc (sizeof (rspamd_lru_element_t));
-               node->flags = RSPAMD_LRU_ELEMENT_NORMAL;
-       }
-       else {
-               vnode = g_malloc (sizeof (rspamd_lru_vol_element_t));
-               vnode->creation_time = now;
-               vnode->ttl = ttl;
-               node = &vnode->e;
-               node->flags = RSPAMD_LRU_ELEMENT_VOLATILE;
-       }
-
-       node->data = value;
-       node->key = key;
-       node->hash = hash;
-       node->lg_usages = (guint8)lfu_base_value;
-       node->last = TIME_TO_TS (now);
-       node->eviction_pos = -1;
-
-       return node;
-}
-
 static void
 rspamd_lru_hash_remove_node (rspamd_lru_hash_t *hash, rspamd_lru_element_t *elt)
 {
@@ -232,36 +428,7 @@ rspamd_lru_hash_remove_node (rspamd_lru_hash_t *hash, rspamd_lru_element_t *elt)
                rspamd_lru_hash_remove_evicted (hash, elt);
        }
 
-       g_hash_table_remove (hash->tbl, elt->key);
-}
-
-static rspamd_lru_element_t *
-rspamd_lru_eviction_full_update (rspamd_lru_hash_t *hash, time_t now)
-{
-       GHashTableIter it;
-       gpointer k, v;
-       rspamd_lru_element_t *cur, *selected = NULL;
-
-       g_hash_table_iter_init (&it, hash->tbl);
-       now = TIME_TO_TS (now);
-
-       while (g_hash_table_iter_next (&it, &k, &v)) {
-               cur = v;
-
-               rspamd_lru_hash_decrease_counter (cur, now);
-
-               if (rspamd_lru_hash_maybe_evict (hash, cur)) {
-
-                       if (selected && cur->lg_usages < selected->lg_usages) {
-                               selected = cur;
-                       }
-                       else if (selected == NULL) {
-                               selected = cur;
-                       }
-               }
-       }
-
-       return selected;
+       rspamd_lru_hash_del (hash, (rspamd_lru_vol_element_t *)elt);
 }
 
 static void
@@ -270,6 +437,7 @@ rspamd_lru_hash_evict (rspamd_lru_hash_t *hash, time_t now)
        double r;
        guint i;
        rspamd_lru_element_t *elt = NULL;
+       guint nexpired = 0;
 
        /*
         * We either evict one node from the eviction list
@@ -280,9 +448,38 @@ rspamd_lru_hash_evict (rspamd_lru_hash_t *hash, time_t now)
        r = rspamd_random_double_fast ();
 
        if (r < ((double)eviction_candidates) / hash->maxsize) {
-               elt = rspamd_lru_eviction_full_update (hash, now);
+               /* Full hash scan */
+               rspamd_lru_vol_element_t *cur;
+               rspamd_lru_element_t *selected = NULL;
+
+               kh_foreach_value_ptr (hash, cur, {
+                       rspamd_lru_element_t *node = &cur->e;
+
+                       if (node->flags & RSPAMD_LRU_ELEMENT_VOLATILE) {
+                               /* If element is expired, just remove it */
+                               if (now - cur->creation_time > cur->ttl) {
+                                       rspamd_lru_hash_remove_node (hash, node);
+
+                                       nexpired ++;
+                                       continue;
+                               }
+                       }
+                       else {
+                               rspamd_lru_hash_decrease_counter (node, now);
+
+                               if (rspamd_lru_hash_maybe_evict (hash, node)) {
+                                       if (selected && node->lg_usages < selected->lg_usages) {
+                                               selected = node;
+                                       }
+                                       else if (selected == NULL) {
+                                               selected = node;
+                                       }
+                               }
+                       }
+               });
        }
        else {
+               /* Fast random eviction */
                for (i = 0; i < hash->eviction_used; i ++) {
                        elt = hash->eviction_pool[i];
 
@@ -292,41 +489,44 @@ rspamd_lru_hash_evict (rspamd_lru_hash_t *hash, time_t now)
                }
        }
 
-       g_assert (elt != NULL);
-       rspamd_lru_hash_remove_node (hash, elt);
+       if (elt && nexpired == 0) {
+               rspamd_lru_hash_remove_node (hash, elt);
+       }
 }
 
 rspamd_lru_hash_t *
-rspamd_lru_hash_new_full (
-               gint maxsize,
-               GDestroyNotify key_destroy,
-               GDestroyNotify value_destroy,
-               GHashFunc hf,
-               GEqualFunc cmpf)
+rspamd_lru_hash_new_full (gint maxsize,
+                                                 GDestroyNotify key_destroy,
+                                                 GDestroyNotify value_destroy,
+                                                 GHashFunc hf,
+                                                 GEqualFunc cmpf)
 {
-       rspamd_lru_hash_t *new;
+       rspamd_lru_hash_t *h;
 
        if (maxsize < eviction_candidates * 2) {
                maxsize = eviction_candidates * 2;
        }
 
-       new = g_malloc0 (sizeof (rspamd_lru_hash_t));
-       new->tbl = g_hash_table_new_full (hf, cmpf, NULL, rspamd_lru_destroy_node);
-       new->eviction_pool = g_malloc0 (sizeof (rspamd_lru_element_t *) *
+       h = g_malloc0 (sizeof (rspamd_lru_hash_t));
+       h->hfunc = hf;
+       h->eqfunc = cmpf;
+       h->eviction_pool = g_malloc0 (sizeof (rspamd_lru_element_t *) *
                        eviction_candidates);
-       new->maxsize = maxsize;
-       new->value_destroy = value_destroy;
-       new->key_destroy = key_destroy;
-       new->eviction_min_prio = G_MAXUINT;
+       h->maxsize = maxsize;
+       h->value_destroy = value_destroy;
+       h->key_destroy = key_destroy;
+       h->eviction_min_prio = G_MAXUINT;
 
-       return new;
+       /* Preallocate some elements */
+       rspamd_lru_hash_resize (h, MIN (h->maxsize, 128));
+
+       return h;
 }
 
 rspamd_lru_hash_t *
-rspamd_lru_hash_new (
-               gint maxsize,
-               GDestroyNotify key_destroy,
-               GDestroyNotify value_destroy)
+rspamd_lru_hash_new (gint maxsize,
+                                        GDestroyNotify key_destroy,
+                                        GDestroyNotify value_destroy)
 {
        return rspamd_lru_hash_new_full (maxsize,
                        key_destroy, value_destroy,
@@ -339,12 +539,12 @@ rspamd_lru_hash_lookup (rspamd_lru_hash_t *hash, gconstpointer key, time_t now)
        rspamd_lru_element_t *res;
        rspamd_lru_vol_element_t *vnode;
 
-       res = g_hash_table_lookup (hash->tbl, key);
-       if (res != NULL) {
+       vnode = rspamd_lru_hash_get (hash, (gpointer)key);
+       if (vnode != NULL) {
+               res = &vnode->e;
 
                if (res->flags & RSPAMD_LRU_ELEMENT_VOLATILE) {
                        /* Check ttl */
-                       vnode = (rspamd_lru_vol_element_t *)res;
 
                        if (now - vnode->creation_time > vnode->ttl) {
                                rspamd_lru_hash_remove_node (hash, res);
@@ -368,12 +568,12 @@ gboolean
 rspamd_lru_hash_remove (rspamd_lru_hash_t *hash,
                gconstpointer key)
 {
-       rspamd_lru_element_t *res;
+       rspamd_lru_vol_element_t *res;
 
-       res = g_hash_table_lookup (hash->tbl, key);
+       res = rspamd_lru_hash_get (hash, key);
 
        if (res != NULL) {
-               rspamd_lru_hash_remove_node (hash, res);
+               rspamd_lru_hash_remove_node (hash, &res->e);
 
                return TRUE;
        }
@@ -382,44 +582,113 @@ rspamd_lru_hash_remove (rspamd_lru_hash_t *hash,
 }
 
 void
-rspamd_lru_hash_insert (rspamd_lru_hash_t *hash, gpointer key, gpointer value,
-       time_t now, guint ttl)
+rspamd_lru_hash_insert (rspamd_lru_hash_t *hash,
+                                               gpointer key,
+                                               gpointer value,
+                                               time_t now,
+                                               guint ttl)
 {
-       rspamd_lru_element_t *res;
+       rspamd_lru_element_t *node;
+       rspamd_lru_vol_element_t *vnode;
+       gint ret;
 
-       res = g_hash_table_lookup (hash->tbl, key);
+       vnode = rspamd_lru_hash_put (hash, key, &ret);
+       node = &vnode->e;
 
-       if (res != NULL) {
-               rspamd_lru_hash_remove_node (hash, res);
+       if (ret == 0) {
+               /* Existing element, be carefull about destructors */
+               if (hash->value_destroy) {
+                       /* Remove old data */
+                       hash->value_destroy (vnode->e.data);
+               }
+
+               if (hash->key_destroy) {
+                       /* Here are dragons! */
+                       goffset off = vnode - hash->vals;
+
+                       hash->key_destroy (hash->keys[off]);
+                       hash->keys[off] = key;
+               }
+       }
+
+
+       if (ttl == 0) {
+               node->flags = RSPAMD_LRU_ELEMENT_NORMAL;
        }
        else {
-               if (g_hash_table_size (hash->tbl) >= hash->maxsize) {
+               vnode->creation_time = now;
+               vnode->ttl = ttl;
+               node->flags = RSPAMD_LRU_ELEMENT_VOLATILE;
+       }
+
+       node->data = value;
+       node->lg_usages = (guint8)lfu_base_value;
+       node->last = TIME_TO_TS (now);
+       node->eviction_pos = -1;
+
+       if (ret != 0) {
+               /* Also need to check maxsize */
+               if (kh_size (hash) >= hash->maxsize) {
                        rspamd_lru_hash_evict (hash, now);
                }
        }
 
-       res = rspamd_lru_create_node (hash, key, value, now, ttl);
-       g_hash_table_insert (hash->tbl, key, res);
-       rspamd_lru_hash_maybe_evict (hash, res);
+       rspamd_lru_hash_maybe_evict (hash, node);
 }
 
 void
 rspamd_lru_hash_destroy (rspamd_lru_hash_t *hash)
 {
-       g_hash_table_unref (hash->tbl);
-       g_free (hash->eviction_pool);
-       g_free (hash);
-}
-
+       if (hash) {
+               if (hash->key_destroy || hash->value_destroy) {
+                       gpointer k;
+                       rspamd_lru_vol_element_t cur;
+
+                       kh_foreach (hash, k, cur, {
+                               if (hash->key_destroy) {
+                                       hash->key_destroy (k);
+                               }
+                               if (hash->value_destroy) {
+                                       hash->value_destroy (cur.e.data);
+                               }
+                       });
+               }
 
-GHashTable *
-rspamd_lru_hash_get_htable (rspamd_lru_hash_t *hash)
-{
-       return hash->tbl;
+               g_free (hash->keys);
+               g_free (hash->vals);
+               g_free (hash->flags);
+               g_free (hash->eviction_pool);
+               g_free (hash);
+       }
 }
 
 gpointer
 rspamd_lru_hash_element_data (rspamd_lru_element_t *elt)
 {
        return elt->data;
+}
+
+int
+rspamd_lru_hash_foreach (rspamd_lru_hash_t *h, int it, gpointer *k,
+                                                gpointer *v)
+{
+       gint i;
+       g_assert (it >= 0);
+
+       for (i = it; i != kh_end (h); ++i) {
+               if (!kh_exist (h, i)) {
+                       continue;
+               }
+
+               *k = h->keys[i];
+               *v = h->vals[i].e.data;
+
+               break;
+       }
+
+       if (i == kh_end (h)) {
+               return -1;
+       }
+
+       return i;
 }
\ No newline at end of file
index 7638d6397e8d7926230a6c914ba86953d4de7e2a..f983a0be88135ac7e5d34860c804e0968018fe02 100644 (file)
@@ -23,10 +23,9 @@ typedef struct rspamd_lru_element_s rspamd_lru_element_t;
  * @param key_equal_func pointer to function for comparing keys
  * @return new rspamd_hash object
  */
-rspamd_lru_hash_t * rspamd_lru_hash_new (
-       gint maxsize,
-       GDestroyNotify key_destroy,
-       GDestroyNotify value_destroy);
+rspamd_lru_hash_t * rspamd_lru_hash_new (gint maxsize,
+                                                                                GDestroyNotify key_destroy,
+                                                                                GDestroyNotify value_destroy);
 
 
 /**
@@ -37,12 +36,11 @@ rspamd_lru_hash_t * rspamd_lru_hash_new (
  * @param key_equal_func pointer to function for comparing keys
  * @return new rspamd_hash object
  */
-rspamd_lru_hash_t * rspamd_lru_hash_new_full (
-       gint maxsize,
-       GDestroyNotify key_destroy,
-       GDestroyNotify value_destroy,
-       GHashFunc hfunc,
-       GEqualFunc eqfunc);
+rspamd_lru_hash_t * rspamd_lru_hash_new_full (gint maxsize,
+                                                                                         GDestroyNotify key_destroy,
+                                                                                         GDestroyNotify value_destroy,
+                                                                                         GHashFunc hfunc,
+                                                                                         GEqualFunc eqfunc);
 
 /**
  * Lookup item from hash
@@ -51,8 +49,8 @@ rspamd_lru_hash_t * rspamd_lru_hash_new_full (
  * @return value of key or NULL if key is not found
  */
 gpointer rspamd_lru_hash_lookup (rspamd_lru_hash_t *hash,
-       gconstpointer key,
-       time_t now);
+                                                                gconstpointer key,
+                                                                time_t now);
 
 /**
  * Removes key from LRU cache
@@ -61,7 +59,7 @@ gpointer rspamd_lru_hash_lookup (rspamd_lru_hash_t *hash,
  * @return TRUE if key has been found and removed
  */
 gboolean rspamd_lru_hash_remove (rspamd_lru_hash_t *hash,
-               gconstpointer key);
+                                                                gconstpointer key);
 /**
  * Insert item in hash
  * @param hash hash object
@@ -69,10 +67,10 @@ gboolean rspamd_lru_hash_remove (rspamd_lru_hash_t *hash,
  * @param value value of key
  */
 void rspamd_lru_hash_insert (rspamd_lru_hash_t *hash,
-       gpointer key,
-       gpointer value,
-       time_t now,
-       guint ttl);
+                                                        gpointer key,
+                                                        gpointer value,
+                                                        time_t now,
+                                                        guint ttl);
 
 /**
  * Remove lru hash
@@ -82,18 +80,13 @@ void rspamd_lru_hash_insert (rspamd_lru_hash_t *hash,
 void rspamd_lru_hash_destroy (rspamd_lru_hash_t *hash);
 
 /**
- * Get hash table for this lru hash (use rspamd_lru_element_t as data)
- */
-GHashTable *rspamd_lru_hash_get_htable (rspamd_lru_hash_t *hash);
-
-/**
- * Get element's data
- * @param elt
- * @return
+ * Iterate over lru hash. Iterations must start from it=0 and are done when it==-1
+ * @param hash
+ * @param it
+ * @param k
+ * @param v
+ * @return new it or -1 if iteration has been reached over
  */
-gpointer rspamd_lru_hash_element_data (rspamd_lru_element_t *elt);
+int rspamd_lru_hash_foreach (rspamd_lru_hash_t *hash, int it, gpointer *k,
+               gpointer *v);
 #endif
-
-/*
- * vi:ts=4
- */