diff options
Diffstat (limited to 'src/kvstorage.c')
-rw-r--r-- | src/kvstorage.c | 1560 |
1 files changed, 0 insertions, 1560 deletions
diff --git a/src/kvstorage.c b/src/kvstorage.c deleted file mode 100644 index caa131afe..000000000 --- a/src/kvstorage.c +++ /dev/null @@ -1,1560 +0,0 @@ -/* Copyright (c) 2011, Vsevolod Stakhov - * 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. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED ''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 AUTHOR 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. - */ - -#include "config.h" -#include "kvstorage.h" -#include "main.h" -#include "radix.h" -#ifdef WITH_JUDY -#include <Judy.h> -#endif - -#define MAX_EXPIRE_STEPS 10 - -/** Create new kv storage */ -struct rspamd_kv_storage * -rspamd_kv_storage_new (gint id, - const gchar *name, - struct rspamd_kv_cache *cache, - struct rspamd_kv_backend *backend, - struct rspamd_kv_expire *expire, - gsize max_elts, - gsize max_memory, - gboolean no_overwrite) -{ - struct rspamd_kv_storage *new; - - new = g_slice_alloc (sizeof (struct rspamd_kv_storage)); - new->elts = 0; - new->memory = 0; - - new->cache = cache; - new->backend = backend; - new->expire = expire; - - new->max_elts = max_elts; - new->max_memory = max_memory; - - new->id = id; - - new->no_overwrite = no_overwrite; - - if (name != NULL) { - new->name = g_strdup (name); - } - else { - /* Name is absent, use ID as name */ - new->name = g_malloc (sizeof ("18446744073709551616")); - rspamd_snprintf (new->name, sizeof ("18446744073709551616"), "%d", id); - } -#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30)) - g_rw_lock_init (&new->rwlock); -#else - g_static_rw_lock_init (&new->rwlock); -#endif - - - /* Init structures */ - if (new->cache->init_func) { - new->cache->init_func (new->cache); - } - if (new->backend && new->backend->init_func) { - new->backend->init_func (new->backend); - } - if (new->expire && new->expire->init_func) { - new->expire->init_func (new->expire); - } - - return new; -} - -/** Internal insertion to the kv storage from backend */ -gboolean -rspamd_kv_storage_insert_cache (struct rspamd_kv_storage *storage, - gpointer key, - guint keylen, - gpointer data, - gsize len, - gint flags, - guint expire, - struct rspamd_kv_element **pelt) -{ - gint steps = 0; - struct rspamd_kv_element *elt; - - RW_W_LOCK (&storage->rwlock); - /* Hard limit */ - if (storage->max_memory > 0) { - if (len > storage->max_memory) { - msg_info ( - "<%s>: trying to insert value of length %z while limit is %z", - storage->name, - len, - storage->max_memory); - RW_W_UNLOCK (&storage->rwlock); - return FALSE; - } - - /* Now check limits */ - while (storage->memory + len > storage->max_memory) { - if (storage->expire) { - storage->expire->step_func (storage->expire, storage, time ( - NULL), steps); - } - else { - msg_warn ( - "<%s>: storage is full and no expire function is defined", - storage->name); - } - if (++steps > MAX_EXPIRE_STEPS) { - RW_W_UNLOCK (&storage->rwlock); - msg_warn ("<%s>: cannot expire enough keys in storage", - storage->name); - return FALSE; - } - } - } - - /* Insert elt to the cache */ - - elt = storage->cache->insert_func (storage->cache, key, keylen, data, len); - - - /* Copy data */ - elt->flags = flags; - elt->expire = expire; - - if (pelt != NULL) { - *pelt = elt; - } - - /* Insert to the expire */ - if (storage->expire) { - storage->expire->insert_func (storage->expire, elt); - } - - storage->elts++; - storage->memory += ELT_SIZE (elt); - RW_W_UNLOCK (&storage->rwlock); - - return TRUE; -} - -/** Insert new element to the kv storage */ -gboolean -rspamd_kv_storage_insert (struct rspamd_kv_storage *storage, - gpointer key, - guint keylen, - gpointer data, - gsize len, - gint flags, - guint expire) -{ - gint steps = 0; - struct rspamd_kv_element *elt; - gboolean res = TRUE; - glong longval; - - /* Hard limit */ - RW_W_LOCK (&storage->rwlock); - if (storage->max_memory > 0) { - if (len + sizeof (struct rspamd_kv_element) + keylen >= - storage->max_memory) { - msg_warn ( - "<%s>: trying to insert value of length %z while limit is %z", - storage->name, - len, - storage->max_memory); - RW_W_UNLOCK (&storage->rwlock); - return FALSE; - } - - /* Now check limits */ - while (storage->memory + len + keylen > storage->max_memory) { - if (storage->expire) { - storage->expire->step_func (storage->expire, storage, time ( - NULL), steps); - } - else { - msg_warn ( - "<%s>: storage is full and no expire function is defined", - storage->name); - } - if (++steps > MAX_EXPIRE_STEPS) { - RW_W_UNLOCK (&storage->rwlock); - msg_warn ("<%s>: cannot expire enough keys in storage", - storage->name); - return FALSE; - } - } - } - if (storage->max_elts > 0 && storage->elts > storage->max_elts) { - /* More expire */ - steps = 0; - while (storage->elts > storage->max_elts) { - if (storage->expire) { - storage->expire->step_func (storage->expire, storage, time ( - NULL), steps); - } - else { - msg_warn ( - "<%s>: storage is full and no expire function is defined", - storage->name); - } - if (++steps > MAX_EXPIRE_STEPS) { - RW_W_UNLOCK (&storage->rwlock); - msg_warn ("<%s>: cannot expire enough keys in storage", - storage->name); - return FALSE; - } - } - } - - /* First try to search it in cache */ - - elt = storage->cache->lookup_func (storage->cache, key, keylen); - if (elt) { - if (!storage->no_overwrite) { - /* Remove old elt */ - if (storage->expire) { - storage->expire->delete_func (storage->expire, elt); - } - storage->memory -= ELT_SIZE (elt); - storage->cache->steal_func (storage->cache, elt); - if (elt->flags & KV_ELT_DIRTY) { - /* Element is in backend storage queue */ - elt->flags |= KV_ELT_NEED_FREE; - } - else { - g_slice_free1 (ELT_SIZE (elt), elt); - } - } - else { - /* Just do incref and nothing more */ - if (storage->backend && storage->backend->incref_func) { - if (storage->backend->incref_func (storage->backend, key, - keylen)) { - RW_W_UNLOCK (&storage->rwlock); - return TRUE; - } - else { - RW_W_UNLOCK (&storage->rwlock); - return FALSE; - } - } - } - } - - /* Insert elt to the cache */ - - /* First of all check element for integer */ - if (rspamd_strtol (data, len, &longval)) { - elt = storage->cache->insert_func (storage->cache, - key, - keylen, - &longval, - sizeof (glong)); - if (elt == NULL) { - return FALSE; - } - else { - elt->flags |= KV_ELT_INTEGER; - } - } - else { - elt = storage->cache->insert_func (storage->cache, - key, - keylen, - data, - len); - if (elt == NULL) { - RW_W_UNLOCK (&storage->rwlock); - return FALSE; - } - } - - elt->flags |= flags; - elt->expire = expire; - if (expire == 0) { - elt->flags |= KV_ELT_PERSISTENT; - } - - /* Place to the backend */ - if (storage->backend) { - res = - storage->backend->insert_func (storage->backend, key, keylen, elt); - } - - /* Insert to the expire */ - if (storage->expire) { - storage->expire->insert_func (storage->expire, elt); - } - - storage->elts++; - storage->memory += ELT_SIZE (elt); - RW_W_UNLOCK (&storage->rwlock); - - return res; -} - -/** Replace an element in the kv storage */ -gboolean -rspamd_kv_storage_replace (struct rspamd_kv_storage *storage, - gpointer key, - guint keylen, - struct rspamd_kv_element *elt) -{ - gboolean res = TRUE; - gint steps = 0; - - /* Hard limit */ - if (storage->max_memory > 0) { - if (elt->size > storage->max_memory) { - msg_info ( - "<%s>: trying to replace value of length %z while limit is %z", - storage->name, - elt->size, - storage->max_memory); - return FALSE; - } - - /* Now check limits */ - while (storage->memory + ELT_SIZE (elt) > storage->max_memory) { - if (storage->expire) { - RW_W_LOCK (&storage->rwlock); - storage->expire->step_func (storage->expire, storage, time ( - NULL), steps); - RW_W_UNLOCK (&storage->rwlock); - } - else { - msg_warn ( - "<%s>: storage is full and no expire function is defined", - storage->name); - } - if (++steps > MAX_EXPIRE_STEPS) { - msg_warn ("<%s>: cannot expire enough keys in storage", - storage->name); - return FALSE; - } - } - } - - RW_W_LOCK (&storage->rwlock); - /* Insert elt to the cache */ - res = storage->cache->replace_func (storage->cache, key, keylen, elt); - - /* Place to the backend */ - if (res && storage->backend) { - res = - storage->backend->replace_func (storage->backend, key, keylen, elt); - } - RW_W_UNLOCK (&storage->rwlock); - - return res; -} - -/** Increment value in kvstorage */ -gboolean -rspamd_kv_storage_increment (struct rspamd_kv_storage *storage, - gpointer key, - guint keylen, - glong *value) -{ - struct rspamd_kv_element *elt = NULL, *belt; - glong *lp; - - /* First try to look at cache */ - RW_W_LOCK (&storage->rwlock); - elt = storage->cache->lookup_func (storage->cache, key, keylen); - - if (elt == NULL && storage->backend) { - belt = storage->backend->lookup_func (storage->backend, key, keylen); - if (belt) { - /* Put this element into cache */ - if ((belt->flags & KV_ELT_INTEGER) != 0) { - RW_W_UNLOCK (&storage->rwlock); - rspamd_kv_storage_insert_cache (storage, ELT_KEY ( - belt), keylen, ELT_DATA (belt), - belt->size, belt->flags, - belt->expire, &elt); - RW_W_LOCK (&storage->rwlock); - } - if ((belt->flags & KV_ELT_DIRTY) == 0) { - g_free (belt); - } - } - } - if (elt && (elt->flags & KV_ELT_INTEGER) != 0) { - lp = &ELT_LONG (elt); - /* Handle need expire here */ - if (elt->flags & KV_ELT_NEED_EXPIRE) { - *lp = *value; - } - else { - *lp += *value; - *value = *lp; - } - elt->age = time (NULL); - if (storage->backend) { - if (storage->backend->replace_func (storage->backend, key, keylen, - elt)) { - RW_W_UNLOCK (&storage->rwlock); - return TRUE; - } - else { - RW_W_UNLOCK (&storage->rwlock); - return FALSE; - } - } - else { - RW_W_UNLOCK (&storage->rwlock); - return TRUE; - } - } - - RW_W_UNLOCK (&storage->rwlock); - - return FALSE; -} - -/** Lookup an element inside kv storage */ -struct rspamd_kv_element * -rspamd_kv_storage_lookup (struct rspamd_kv_storage *storage, - gpointer key, - guint keylen, - time_t now) -{ - struct rspamd_kv_element *elt = NULL, *belt; - - /* First try to look at cache */ - RW_R_LOCK (&storage->rwlock); - elt = storage->cache->lookup_func (storage->cache, key, keylen); - - /* Next look at the backend */ - if (elt == NULL && storage->backend) { - belt = storage->backend->lookup_func (storage->backend, key, keylen); - - if (belt) { - /* Put this element into cache */ - if ((belt->flags & KV_ELT_DIRTY) == 0) { - belt->flags |= KV_ELT_NEED_INSERT; - return belt; - } - else { - elt = belt; - } - } - } - - if (elt && (elt->flags & KV_ELT_PERSISTENT) == 0 && elt->expire > 0) { - /* Check expiration */ - if (now - elt->age > (gint)elt->expire) { - /* Set need expire as we have no write lock here */ - elt->flags |= KV_ELT_NEED_EXPIRE; - elt = NULL; - } - } - - /* RWlock is still locked */ - return elt; -} - -/** Expire an element from kv storage */ -struct rspamd_kv_element * -rspamd_kv_storage_delete (struct rspamd_kv_storage *storage, - gpointer key, - guint keylen) -{ - struct rspamd_kv_element *elt; - - /* First delete key from cache */ - RW_W_LOCK (&storage->rwlock); - elt = storage->cache->delete_func (storage->cache, key, keylen); - - /* Now delete from backend */ - if (storage->backend) { - storage->backend->delete_func (storage->backend, key, keylen); - } - /* Notify expire */ - if (elt) { - if (storage->expire) { - storage->expire->delete_func (storage->expire, elt); - } - storage->elts--; - storage->memory -= elt->size; - if ((elt->flags & KV_ELT_DIRTY) != 0) { - elt->flags |= KV_ELT_NEED_FREE; - } - else { - g_slice_free1 (ELT_SIZE (elt), elt); - } - } - - RW_W_UNLOCK (&storage->rwlock); - - return elt; -} - -/** Destroy kv storage */ -void -rspamd_kv_storage_destroy (struct rspamd_kv_storage *storage) -{ - RW_W_LOCK (&storage->rwlock); - if (storage->backend && storage->backend->destroy_func) { - storage->backend->destroy_func (storage->backend); - } - if (storage->expire && storage->expire->destroy_func) { - storage->expire->destroy_func (storage->expire); - } - if (storage->cache && storage->cache->destroy_func) { - storage->cache->destroy_func (storage->cache); - } - - g_free (storage->name); - - RW_W_UNLOCK (&storage->rwlock); - g_slice_free1 (sizeof (struct rspamd_kv_storage), storage); -} - -/** Insert array */ -gboolean -rspamd_kv_storage_insert_array (struct rspamd_kv_storage *storage, - gpointer key, - guint keylen, - guint elt_size, - gpointer data, - gsize len, - gint flags, - guint expire) -{ - struct rspamd_kv_element *elt; - guint *es; - gpointer arr_data; - - /* Make temporary copy */ - arr_data = g_slice_alloc (len + sizeof (guint)); - es = arr_data; - *es = elt_size; - memcpy (arr_data, (gchar *)data + sizeof (guint), len); - if (!rspamd_kv_storage_insert_cache (storage, key, keylen, arr_data, len + - sizeof (guint), - flags, expire, &elt)) { - g_slice_free1 (len + sizeof (guint), arr_data); - return FALSE; - } - /* Now set special data of element */ - elt->flags |= KV_ELT_ARRAY; - g_slice_free1 (len + sizeof (guint), arr_data); - /* Place to the backend */ - - if (storage->backend) { - return storage->backend->insert_func (storage->backend, key, keylen, - elt); - } - - return TRUE; -} - -/** Set element inside array */ -gboolean -rspamd_kv_storage_set_array (struct rspamd_kv_storage *storage, - gpointer key, - guint keylen, - guint elt_num, - gpointer data, - gsize len, - time_t now) -{ - struct rspamd_kv_element *elt; - guint *es; - gpointer target; - - elt = rspamd_kv_storage_lookup (storage, key, keylen, now); - if (elt == NULL) { - return FALSE; - } - - if ((elt->flags & KV_ELT_ARRAY) == 0) { - return FALSE; - } - /* Get element size */ - es = (guint *)ELT_DATA (elt); - if (elt_num > (elt->size - sizeof (guint)) / (*es)) { - /* Invalid index */ - return FALSE; - } - target = (gchar *)ELT_DATA (elt) + sizeof (guint) + (*es) * elt_num; - if (len != *es) { - /* Invalid size */ - return FALSE; - } - memcpy (target, data, len); - /* Place to the backend */ - if (storage->backend) { - return storage->backend->replace_func (storage->backend, - key, - keylen, - elt); - } - - return TRUE; -} - -/** Get element inside array */ -gboolean -rspamd_kv_storage_get_array (struct rspamd_kv_storage *storage, - gpointer key, - guint keylen, - guint elt_num, - gpointer *data, - gsize *len, - time_t now) -{ - struct rspamd_kv_element *elt; - guint *es; - gpointer target; - - elt = rspamd_kv_storage_lookup (storage, key, keylen, now); - if (elt == NULL) { - return FALSE; - } - - if ((elt->flags & KV_ELT_ARRAY) == 0) { - return FALSE; - } - /* Get element size */ - es = (guint *)ELT_DATA (elt); - if (elt_num > (elt->size - sizeof (guint)) / (*es)) { - /* Invalid index */ - return FALSE; - } - target = ELT_DATA (elt) + sizeof (guint) + (*es) * elt_num; - - *len = *es; - *data = target; - - return TRUE; -} - -/** - * LRU expire functions - */ - -struct rspamd_kv_lru_expire { - expire_init init_func; /*< this callback is called on kv storage initialization */ - expire_insert insert_func; /*< this callback is called when element is inserted */ - expire_step step_func; /*< this callback is used when cache is full */ - expire_delete delete_func; /*< this callback is called when an element is deleted */ - expire_destroy destroy_func; /*< this callback is used for destroying all elements inside expire */ - - TAILQ_HEAD (eltq, rspamd_kv_element) head; -}; - -/** - * Insert an element into expire queue - */ -static void -rspamd_lru_insert (struct rspamd_kv_expire *e, struct rspamd_kv_element *elt) -{ - struct rspamd_kv_lru_expire *expire = (struct rspamd_kv_lru_expire *)e; - - /* Get a proper queue */ - TAILQ_INSERT_TAIL (&expire->head, elt, entry); -} -/** - * Delete an element from expire queue - */ -static void -rspamd_lru_delete (struct rspamd_kv_expire *e, struct rspamd_kv_element *elt) -{ - struct rspamd_kv_lru_expire *expire = (struct rspamd_kv_lru_expire *)e; - - /* Unlink element */ - TAILQ_REMOVE (&expire->head, elt, entry); -} - -/** - * Expire elements - */ -static gboolean -rspamd_lru_expire_step (struct rspamd_kv_expire *e, - struct rspamd_kv_storage *storage, - time_t now, - gboolean forced) -{ - struct rspamd_kv_lru_expire *expire = (struct rspamd_kv_lru_expire *)e; - struct rspamd_kv_element *elt, *oldest_elt = NULL, *temp; - time_t diff; - gboolean res = FALSE; - - elt = TAILQ_FIRST (&expire->head); - if (elt && - (forced || (elt->flags & (KV_ELT_PERSISTENT | KV_ELT_DIRTY)) == 0)) { - diff = elt->expire - (now - elt->age); - if (diff > 0 || (forced && elt->expire == 0)) { - oldest_elt = elt; - } - else { - /* This element is already expired */ - storage->cache->steal_func (storage->cache, elt); - storage->memory -= ELT_SIZE (elt); - storage->elts--; - TAILQ_REMOVE (&expire->head, elt, entry); - /* Free memory */ - if ((elt->flags & (KV_ELT_DIRTY | KV_ELT_NEED_INSERT)) != 0) { - elt->flags |= KV_ELT_NEED_FREE; - } - else { - g_slice_free1 (ELT_SIZE (elt), elt); - } - res = TRUE; - /* Check other elements in this queue */ - TAILQ_FOREACH_SAFE (elt, &expire->head, entry, temp) - { - if ((!forced && - (elt->flags & (KV_ELT_PERSISTENT | KV_ELT_DIRTY)) != 0) || - (gint)elt->expire < (now - elt->age)) { - break; - } - storage->memory -= ELT_SIZE (elt); - storage->elts--; - storage->cache->steal_func (storage->cache, elt); - TAILQ_REMOVE (&expire->head, elt, entry); - /* Free memory */ - if ((elt->flags & (KV_ELT_DIRTY | KV_ELT_NEED_INSERT)) != 0) { - elt->flags |= KV_ELT_NEED_FREE; - } - else { - g_slice_free1 (ELT_SIZE (elt), elt); - } - - } - } - } - - if (!res && oldest_elt != NULL) { - storage->memory -= ELT_SIZE (oldest_elt); - storage->elts--; - storage->cache->steal_func (storage->cache, oldest_elt); - TAILQ_REMOVE (&expire->head, oldest_elt, entry); - /* Free memory */ - if ((oldest_elt->flags & (KV_ELT_DIRTY | KV_ELT_NEED_INSERT)) != 0) { - oldest_elt->flags |= KV_ELT_NEED_FREE; - } - else { - g_slice_free1 (ELT_SIZE (oldest_elt), oldest_elt); - } - } - - return TRUE; -} - -/** - * Destroy LRU expire memory - */ -static void -rspamd_lru_destroy (struct rspamd_kv_expire *e) -{ - struct rspamd_kv_lru_expire *expire = (struct rspamd_kv_lru_expire *)e; - - g_slice_free1 (sizeof (struct rspamd_kv_lru_expire), expire); -} - -/** - * Create new LRU cache - */ -struct rspamd_kv_expire * -rspamd_lru_expire_new (void) -{ - struct rspamd_kv_lru_expire *new; - - new = g_slice_alloc (sizeof (struct rspamd_kv_lru_expire)); - TAILQ_INIT (&new->head); - - /* Set callbacks */ - new->init_func = NULL; - new->insert_func = rspamd_lru_insert; - new->delete_func = rspamd_lru_delete; - new->step_func = rspamd_lru_expire_step; - new->destroy_func = rspamd_lru_destroy; - - return (struct rspamd_kv_expire *)new; -} - -/* - * KV cache hash table - */ -struct rspamd_kv_hash_cache { - cache_init init_func; /*< this callback is called on kv storage initialization */ - cache_insert insert_func; /*< this callback is called when element is inserted */ - cache_replace replace_func; /*< this callback is called when element is replace */ - cache_lookup lookup_func; /*< this callback is used for lookup of element */ - cache_delete delete_func; /*< this callback is called when an element is deleted */ - cache_steal steal_func; /*< this callback is used to replace duplicates in cache */ - cache_destroy destroy_func; /*< this callback is used for destroying all elements inside cache */ - GHashTable *hash; -}; - -/** - * Insert an element inside cache - */ -static struct rspamd_kv_element * -rspamd_kv_hash_insert (struct rspamd_kv_cache *c, - gpointer key, - guint keylen, - gpointer value, - gsize len) -{ - struct rspamd_kv_element *elt; - struct rspamd_kv_hash_cache *cache = (struct rspamd_kv_hash_cache *)c; - struct rspamd_kv_element search_elt; - - search_elt.keylen = keylen; - search_elt.p = key; - - if ((elt = g_hash_table_lookup (cache->hash, &search_elt)) == NULL) { - elt = g_slice_alloc ( - sizeof (struct rspamd_kv_element) + len + keylen + 1); - elt->age = time (NULL); - elt->keylen = keylen; - elt->size = len; - elt->flags = 0; - memcpy (ELT_KEY (elt), key, keylen + 1); - memcpy (ELT_DATA (elt), value, len); - elt->p = &elt->data; - g_hash_table_insert (cache->hash, elt, elt); - } - else { - g_hash_table_steal (cache->hash, elt); - if ((elt->flags & KV_ELT_DIRTY) != 0) { - elt->flags |= KV_ELT_NEED_FREE; - } - else { - /* Free it by self */ - g_slice_free1 (ELT_SIZE (elt), elt); - } - elt = g_slice_alloc ( - sizeof (struct rspamd_kv_element) + len + keylen + 1); - elt->age = time (NULL); - elt->keylen = keylen; - elt->size = len; - elt->flags = 0; - memcpy (ELT_KEY (elt), key, keylen + 1); - memcpy (ELT_DATA (elt), value, len); - elt->p = &elt->data; - g_hash_table_insert (cache->hash, elt, elt); - } - - return elt; -} - -/** - * Lookup an item inside hash - */ -static struct rspamd_kv_element * -rspamd_kv_hash_lookup (struct rspamd_kv_cache *c, gpointer key, guint keylen) -{ - struct rspamd_kv_hash_cache *cache = (struct rspamd_kv_hash_cache *)c; - struct rspamd_kv_element search_elt; - - search_elt.keylen = keylen; - search_elt.p = key; - - return g_hash_table_lookup (cache->hash, &search_elt); -} - -/** - * Replace an element inside cache - */ -static gboolean -rspamd_kv_hash_replace (struct rspamd_kv_cache *c, - gpointer key, - guint keylen, - struct rspamd_kv_element *elt) -{ - struct rspamd_kv_hash_cache *cache = (struct rspamd_kv_hash_cache *)c; - struct rspamd_kv_element *oldelt, search_elt; - - search_elt.keylen = keylen; - search_elt.p = key; - - if ((oldelt = g_hash_table_lookup (cache->hash, &search_elt)) != NULL) { - g_hash_table_steal (cache->hash, oldelt); - - if ((oldelt->flags & KV_ELT_DIRTY) != 0) { - oldelt->flags |= KV_ELT_NEED_FREE; - } - else { - /* Free it by self */ - g_slice_free1 (ELT_SIZE (oldelt), oldelt); - } - g_hash_table_insert (cache->hash, elt, elt); - return TRUE; - } - - return FALSE; -} - -/** - * Delete an element from cache - */ -static struct rspamd_kv_element * -rspamd_kv_hash_delete (struct rspamd_kv_cache *c, gpointer key, guint keylen) -{ - struct rspamd_kv_hash_cache *cache = (struct rspamd_kv_hash_cache *)c; - struct rspamd_kv_element *elt; - struct rspamd_kv_element search_elt; - - search_elt.keylen = keylen; - search_elt.p = key; - - elt = g_hash_table_lookup (cache->hash, &search_elt); - if (elt) { - g_hash_table_steal (cache->hash, &search_elt); - } - return elt; -} - -/** - * Steal an element from cache - */ -static void -rspamd_kv_hash_steal (struct rspamd_kv_cache *c, struct rspamd_kv_element *elt) -{ - struct rspamd_kv_hash_cache *cache = (struct rspamd_kv_hash_cache *)c; - - g_hash_table_steal (cache->hash, elt); -} - -/** - * Destroy the whole cache - */ - -static void -rspamd_kv_hash_destroy_cb (gpointer key, gpointer value, gpointer unused) -{ - struct rspamd_kv_element *elt = value; - - g_slice_free1 (ELT_SIZE (elt), elt); -} - -static void -rspamd_kv_hash_destroy (struct rspamd_kv_cache *c) -{ - struct rspamd_kv_hash_cache *cache = (struct rspamd_kv_hash_cache *)c; - - g_hash_table_foreach (cache->hash, rspamd_kv_hash_destroy_cb, NULL); - g_hash_table_destroy (cache->hash); - g_slice_free1 (sizeof (struct rspamd_kv_hash_cache), cache); -} - -/** - * Make hash for element - */ -#define rot(x,k) (((x) << (k)) ^ ((x) >> (32 - (k)))) -#define mix(a,b,c) \ - { \ - a -= c; a ^= rot (c, 4); c += b; \ - b -= a; b ^= rot (a, 6); a += c; \ - c -= b; c ^= rot (b, 8); b += a; \ - a -= c; a ^= rot (c,16); c += b; \ - b -= a; b ^= rot (a,19); a += c; \ - c -= b; c ^= rot (b, 4); b += a; \ - } -#define final(a,b,c) \ - { \ - c ^= b; c -= rot (b,14); \ - a ^= c; a -= rot (c,11); \ - b ^= a; b -= rot (a,25); \ - c ^= b; c -= rot (b,16); \ - a ^= c; a -= rot (c,4); \ - b ^= a; b -= rot (a,14); \ - c ^= b; c -= rot (b,24); \ - } -/* - * The hash function used here is by Bob Jenkins, 1996: - * <http://burtleburtle.net/bob/hash/doobs.html> - * "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. - * You may use this code any way you wish, private, educational, - * or commercial. It's free." - * - */ -guint -kv_elt_hash_func (gconstpointer e) -{ - struct rspamd_kv_element *elt = (struct rspamd_kv_element *)e; - guint32 a, b, c; - union { const void *ptr; size_t i; } u; - guint length; - - /* Set up the internal state */ - length = elt->keylen; - a = b = c = 0xdeadbeef + length; - - u.ptr = elt->p; - if (((u.i & 0x3) == 0)) { - const guint32 *k = (const guint32 *)elt->p; /* read 32-bit chunks */ - - /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ - while (length > 12) { - a += k[0]; - b += k[1]; - c += k[2]; - mix (a,b,c); - length -= 12; - k += 3; - } - - /*----------------------------- handle the last (probably partial) block */ - /* - * "k[2]&0xffffff" actually reads beyond the end of the string, but - * then masks off the part it's not allowed to read. Because the - * string is aligned, the masked-off tail is in the same word as the - * rest of the string. Every machine with memory protection I've seen - * does it on word boundaries, so is OK with this. But VALGRIND will - * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). - */ - switch (length) - { - case 12: c += k[2]; b += k[1]; a += k[0]; break; - case 11: c += k[2] & 0xffffff; b += k[1]; a += k[0]; break; - case 10: c += k[2] & 0xffff; b += k[1]; a += k[0]; break; - case 9: c += k[2] & 0xff; b += k[1]; a += k[0]; break; - case 8: b += k[1]; a += k[0]; break; - case 7: b += k[1] & 0xffffff; a += k[0]; break; - case 6: b += k[1] & 0xffff; a += k[0]; break; - case 5: b += k[1] & 0xff; a += k[0]; break; - case 4: a += k[0]; break; - case 3: a += k[0] & 0xffffff; break; - case 2: a += k[0] & 0xffff; break; - case 1: a += k[0] & 0xff; break; - case 0: return c; /* zero length strings require no mixing */ - } - - } else if (((u.i & 0x1) == 0)) { - const guint16 *k = (const guint16 *)elt->p; /* read 16-bit chunks */ - const guint8 *k8; - - /*--------------- all but last block: aligned reads and different mixing */ - while (length > 12) { - a += k[0] + (((guint32)k[1]) << 16); - b += k[2] + (((guint32)k[3]) << 16); - c += k[4] + (((guint32)k[5]) << 16); - mix (a,b,c); - length -= 12; - k += 6; - } - - /*----------------------------- handle the last (probably partial) block */ - k8 = (const guint8 *)k; - switch (length) - { - case 12: c += k[4] + (((guint32)k[5]) << 16); - b += k[2] + (((guint32)k[3]) << 16); - a += k[0] + (((guint32)k[1]) << 16); - break; - case 11: c += ((guint32)k8[10]) << 16; /* @fallthrough */ - case 10: c += k[4]; /* @fallthrough@ */ - b += k[2] + (((guint32)k[3]) << 16); - a += k[0] + (((guint32)k[1]) << 16); - break; - case 9: c += k8[8]; /* @fallthrough */ - case 8: b += k[2] + (((guint32)k[3]) << 16); - a += k[0] + (((guint32)k[1]) << 16); - break; - case 7: b += ((guint32)k8[6]) << 16; /* @fallthrough */ - case 6: b += k[2]; - a += k[0] + (((guint32)k[1]) << 16); - break; - case 5: b += k8[4]; /* @fallthrough */ - case 4: a += k[0] + (((guint32)k[1]) << 16); - break; - case 3: a += ((guint32)k8[2]) << 16; /* @fallthrough */ - case 2: a += k[0]; - break; - case 1: a += k8[0]; - break; - case 0: return c; /* zero length strings require no mixing */ - } - - } else { /* need to read the key one byte at a time */ - const guint8 *k = elt->p; - - /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ - while (length > 12) - { - a += k[0]; - a += ((guint32)k[1]) << 8; - a += ((guint32)k[2]) << 16; - a += ((guint32)k[3]) << 24; - b += k[4]; - b += ((guint32)k[5]) << 8; - b += ((guint32)k[6]) << 16; - b += ((guint32)k[7]) << 24; - c += k[8]; - c += ((guint32)k[9]) << 8; - c += ((guint32)k[10]) << 16; - c += ((guint32)k[11]) << 24; - mix (a,b,c); - length -= 12; - k += 12; - } - - /*-------------------------------- last block: affect all 32 bits of (c) */ - switch (length) /* all the case statements fall through */ - { - case 12: c += ((guint32)k[11]) << 24; - case 11: c += ((guint32)k[10]) << 16; - case 10: c += ((guint32)k[9]) << 8; - case 9: c += k[8]; - case 8: b += ((guint32)k[7]) << 24; - case 7: b += ((guint32)k[6]) << 16; - case 6: b += ((guint32)k[5]) << 8; - case 5: b += k[4]; - case 4: a += ((guint32)k[3]) << 24; - case 3: a += ((guint32)k[2]) << 16; - case 2: a += ((guint32)k[1]) << 8; - case 1: a += k[0]; - break; - case 0: return c; /* zero length strings require no mixing */ - } - } - - final (a,b,c); - return c; /* zero length strings require no mixing */ -} - -gboolean -kv_elt_compare_func (gconstpointer e1, gconstpointer e2) -{ - struct rspamd_kv_element *elt1 = (struct rspamd_kv_element *) e1, - *elt2 = (struct rspamd_kv_element *) e2; - - if (elt1->keylen == elt2->keylen) { - return memcmp (elt1->p, elt2->p, elt1->keylen) == 0; - } - - return FALSE; -} - -/** - * Create new hash kv cache - */ -struct rspamd_kv_cache * -rspamd_kv_hash_new (void) -{ - struct rspamd_kv_hash_cache *new; - - new = g_slice_alloc (sizeof (struct rspamd_kv_hash_cache)); - new->hash = g_hash_table_new_full (kv_elt_hash_func, - kv_elt_compare_func, - NULL, - NULL); - new->init_func = NULL; - new->insert_func = rspamd_kv_hash_insert; - new->lookup_func = rspamd_kv_hash_lookup; - new->replace_func = rspamd_kv_hash_replace; - new->delete_func = rspamd_kv_hash_delete; - new->steal_func = rspamd_kv_hash_steal; - new->destroy_func = rspamd_kv_hash_destroy; - - return (struct rspamd_kv_cache *)new; -} - -/* - * Radix cache hash table - */ -struct rspamd_kv_radix_cache { - cache_init init_func; /*< this callback is called on kv storage initialization */ - cache_insert insert_func; /*< this callback is called when element is inserted */ - cache_replace replace_func; /*< this callback is called when element is replace */ - cache_lookup lookup_func; /*< this callback is used for lookup of element */ - cache_delete delete_func; /*< this callback is called when an element is deleted */ - cache_steal steal_func; /*< this callback is used to replace duplicates in cache */ - cache_destroy destroy_func; /*< this callback is used for destroying all elements inside cache */ - radix_tree_t *tree; -}; - -/** - * Validate a key for radix - */ -static guint32 -rspamd_kv_radix_validate (gpointer key, guint keylen) -{ - struct in_addr addr; - - if (inet_aton (key, &addr) == 0) { - return 0; - } - - return addr.s_addr; -} - -/** - * Insert an element inside cache - */ -static struct rspamd_kv_element * -rspamd_kv_radix_insert (struct rspamd_kv_cache *c, - gpointer key, - guint keylen, - gpointer value, - gsize len) -{ - struct rspamd_kv_element *elt; - struct rspamd_kv_radix_cache *cache = (struct rspamd_kv_radix_cache *)c; - guint32 rkey = rspamd_kv_radix_validate (key, keylen); - - if (rkey == 0) { - return NULL; - } - - elt = (struct rspamd_kv_element *)radix32tree_find (cache->tree, rkey); - if ((uintptr_t)elt == RADIX_NO_VALUE) { - elt = g_slice_alloc ( - sizeof (struct rspamd_kv_element) + len + keylen + 1); - elt->age = time (NULL); - elt->keylen = keylen; - elt->size = len; - elt->flags = 0; - memcpy (ELT_KEY (elt), key, keylen + 1); - memcpy (ELT_DATA (elt), value, len); - elt->p = &elt->data; - radix32tree_insert (cache->tree, rkey, 0xffffffff, (uintptr_t)elt); - } - else { - radix32tree_delete (cache->tree, rkey, 0xffffffff); - if ((elt->flags & KV_ELT_DIRTY) != 0) { - elt->flags |= KV_ELT_NEED_FREE; - } - else { - /* Free it by self */ - g_slice_free1 (ELT_SIZE (elt), elt); - } - elt = g_slice_alloc ( - sizeof (struct rspamd_kv_element) + len + keylen + 1); - elt->age = time (NULL); - elt->keylen = keylen; - elt->size = len; - elt->flags = 0; - memcpy (ELT_KEY (elt), key, keylen + 1); - memcpy (ELT_DATA (elt), value, len); - elt->p = &elt->data; - radix32tree_insert (cache->tree, rkey, 0xffffffff, (uintptr_t)elt); - } - - return elt; -} - -/** - * Lookup an item inside radix - */ -static struct rspamd_kv_element * -rspamd_kv_radix_lookup (struct rspamd_kv_cache *c, gpointer key, guint keylen) -{ - struct rspamd_kv_radix_cache *cache = (struct rspamd_kv_radix_cache *)c; - guint32 rkey = rspamd_kv_radix_validate (key, keylen); - struct rspamd_kv_element *elt; - - elt = (struct rspamd_kv_element *)radix32tree_find (cache->tree, rkey); - if ((uintptr_t)elt == RADIX_NO_VALUE) { - return NULL; - } - - return elt; -} - -/** - * Replace an element inside cache - */ -static gboolean -rspamd_kv_radix_replace (struct rspamd_kv_cache *c, - gpointer key, - guint keylen, - struct rspamd_kv_element *elt) -{ - struct rspamd_kv_radix_cache *cache = (struct rspamd_kv_radix_cache *)c; - guint32 rkey = rspamd_kv_radix_validate (key, keylen); - struct rspamd_kv_element *oldelt; - - oldelt = (struct rspamd_kv_element *)radix32tree_find (cache->tree, rkey); - if ((uintptr_t)oldelt != RADIX_NO_VALUE) { - radix32tree_delete (cache->tree, rkey, 0xffffffff); - - if ((oldelt->flags & KV_ELT_DIRTY) != 0) { - oldelt->flags |= KV_ELT_NEED_FREE; - } - else { - /* Free it by self */ - g_slice_free1 (ELT_SIZE (oldelt), oldelt); - } - radix32tree_insert (cache->tree, rkey, 0xffffffff, (uintptr_t)elt); - return TRUE; - } - - return FALSE; -} - -/** - * Delete an element from cache - */ -static struct rspamd_kv_element * -rspamd_kv_radix_delete (struct rspamd_kv_cache *c, gpointer key, guint keylen) -{ - struct rspamd_kv_radix_cache *cache = (struct rspamd_kv_radix_cache *)c; - struct rspamd_kv_element *elt; - guint32 rkey = rspamd_kv_radix_validate (key, keylen); - - elt = (struct rspamd_kv_element *)radix32tree_find (cache->tree, rkey); - if ((uintptr_t)elt != RADIX_NO_VALUE) { - radix32tree_delete (cache->tree, rkey, 0xffffffff); - } - else { - return NULL; - } - return elt; -} - -/** - * Delete an element from cache - */ -static void -rspamd_kv_radix_steal (struct rspamd_kv_cache *c, struct rspamd_kv_element *elt) -{ - struct rspamd_kv_radix_cache *cache = (struct rspamd_kv_radix_cache *)c; - guint32 rkey = rspamd_kv_radix_validate (ELT_KEY (elt), elt->keylen); - - - radix32tree_delete (cache->tree, rkey, 0xffffffff); -} - -/** - * Destroy the whole cache - */ -static void -rspamd_kv_radix_destroy (struct rspamd_kv_cache *c) -{ - struct rspamd_kv_radix_cache *cache = (struct rspamd_kv_radix_cache *)c; - - radix_tree_free (cache->tree); - g_slice_free1 (sizeof (struct rspamd_kv_radix_cache), cache); -} - -/** - * Create new radix kv cache - */ -struct rspamd_kv_cache * -rspamd_kv_radix_new (void) -{ - struct rspamd_kv_radix_cache *new; - - new = g_slice_alloc (sizeof (struct rspamd_kv_radix_cache)); - new->tree = radix_tree_create (); - new->init_func = NULL; - new->insert_func = rspamd_kv_radix_insert; - new->lookup_func = rspamd_kv_radix_lookup; - new->replace_func = rspamd_kv_radix_replace; - new->delete_func = rspamd_kv_radix_delete; - new->steal_func = rspamd_kv_radix_steal; - new->destroy_func = rspamd_kv_radix_destroy; - - return (struct rspamd_kv_cache *)new; -} - - -#ifdef WITH_JUDY -/* - * KV cache hash table - */ -struct rspamd_kv_judy_cache { - cache_init init_func; /*< this callback is called on kv storage initialization */ - cache_insert insert_func; /*< this callback is called when element is inserted */ - cache_replace replace_func; /*< this callback is called when element is replace */ - cache_lookup lookup_func; /*< this callback is used for lookup of element */ - cache_delete delete_func; /*< this callback is called when an element is deleted */ - cache_steal steal_func; /*< this callback is used to replace duplicates in cache */ - cache_destroy destroy_func; /*< this callback is used for destroying all elements inside cache */ - Pvoid_t judy; -}; - - -/** - * Lookup an item inside judy - */ -static struct rspamd_kv_element * -rspamd_kv_judy_lookup (struct rspamd_kv_cache *c, gpointer key, guint keylen) -{ - struct rspamd_kv_judy_cache *cache = (struct rspamd_kv_judy_cache *)c; - struct rspamd_kv_element *elt = NULL, **pelt; - - JHSG (pelt, cache->judy, key, keylen); - if (pelt != NULL) { - elt = *pelt; - } - return elt; -} - -/** - * Delete an element from cache - */ -static struct rspamd_kv_element * -rspamd_kv_judy_delete (struct rspamd_kv_cache *c, gpointer key, guint keylen) -{ - struct rspamd_kv_judy_cache *cache = (struct rspamd_kv_judy_cache *)c; - struct rspamd_kv_element *elt; - gint rc; - - elt = rspamd_kv_judy_lookup (c, key, keylen); - if (elt) { - JHSD (rc, cache->judy, ELT_KEY (elt), elt->keylen); - } - return elt; -} - -/** - * Steal an element from cache - */ -static void -rspamd_kv_judy_steal (struct rspamd_kv_cache *c, struct rspamd_kv_element *elt) -{ - struct rspamd_kv_judy_cache *cache = (struct rspamd_kv_judy_cache *)c; - gint rc; - - JHSD (rc, cache->judy, ELT_KEY (elt), elt->keylen); -} - -/** - * Insert an element inside cache - */ -static struct rspamd_kv_element * -rspamd_kv_judy_insert (struct rspamd_kv_cache *c, - gpointer key, - guint keylen, - gpointer value, - gsize len) -{ - struct rspamd_kv_element *elt, **pelt; - struct rspamd_kv_judy_cache *cache = (struct rspamd_kv_judy_cache *)c; - - if ((elt = rspamd_kv_judy_lookup (c, key, keylen)) == NULL) { - elt = g_slice_alloc ( - sizeof (struct rspamd_kv_element) + len + keylen + 1); - elt->age = time (NULL); - elt->keylen = keylen; - elt->size = len; - elt->flags = 0; - memcpy (ELT_KEY (elt), key, keylen); - memcpy (ELT_DATA (elt), value, len); - JHSI (pelt, cache->judy, ELT_KEY (elt), elt->keylen); - elt->p = &elt->data; - *pelt = elt; - } - else { - rspamd_kv_judy_steal (c, elt); - if ((elt->flags & KV_ELT_DIRTY) != 0) { - elt->flags |= KV_ELT_NEED_FREE; - } - else { - /* Free it by self */ - g_slice_free1 (ELT_SIZE (elt), elt); - } - elt = g_slice_alloc0 ( - sizeof (struct rspamd_kv_element) + len + keylen + 1); - elt->age = time (NULL); - elt->keylen = keylen; - elt->size = len; - elt->flags = 0; - memcpy (ELT_KEY (elt), key, keylen); - memcpy (ELT_DATA (elt), value, len); - elt->p = &elt->data; - JHSI (pelt, cache->judy, ELT_KEY (elt), elt->keylen); - *pelt = elt; - } - - return elt; -} - -/** - * Replace an element inside cache - */ -static gboolean -rspamd_kv_judy_replace (struct rspamd_kv_cache *c, - gpointer key, - guint keylen, - struct rspamd_kv_element *elt) -{ - struct rspamd_kv_judy_cache *cache = (struct rspamd_kv_judy_cache *)c; - struct rspamd_kv_element *oldelt, **pelt; - - if ((oldelt = rspamd_kv_judy_lookup (c, key, keylen)) != NULL) { - rspamd_kv_judy_steal (c, elt); - - if ((oldelt->flags & KV_ELT_DIRTY) != 0) { - oldelt->flags |= KV_ELT_NEED_FREE; - } - else { - /* Free it by self */ - g_slice_free1 (ELT_SIZE (oldelt), oldelt); - } - JHSI (pelt, cache->judy, ELT_KEY (elt), elt->keylen); - *pelt = elt; - return TRUE; - } - - return FALSE; -} - -/** - * Destroy the whole cache - */ -static void -rspamd_kv_judy_destroy (struct rspamd_kv_cache *c) -{ - struct rspamd_kv_judy_cache *cache = (struct rspamd_kv_judy_cache *)c; - glong bytes; - - JHSFA (bytes, cache->judy); - g_slice_free1 (sizeof (struct rspamd_kv_judy_cache), cache); -} - -/** - * Judy tree - */ -struct rspamd_kv_cache * -rspamd_kv_judy_new (void) -{ - struct rspamd_kv_judy_cache *new; - - new = g_slice_alloc (sizeof (struct rspamd_kv_judy_cache)); - new->judy = NULL; - new->init_func = NULL; - new->insert_func = rspamd_kv_judy_insert; - new->lookup_func = rspamd_kv_judy_lookup; - new->replace_func = rspamd_kv_judy_replace; - new->delete_func = rspamd_kv_judy_delete; - new->steal_func = rspamd_kv_judy_steal; - new->destroy_func = rspamd_kv_judy_destroy; - - return (struct rspamd_kv_cache *)new; -} -#endif |