/* 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. */ #ifndef KVSTORAGE_H_ #define KVSTORAGE_H_ #include "config.h" struct rspamd_kv_cache; struct rspamd_kv_backend; struct rspamd_kv_storage; struct rspamd_kv_expire; struct rspamd_kv_element; /* Locking definitions */ #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30)) #define RW_R_LOCK g_rw_lock_reader_lock #define RW_R_UNLOCK g_rw_lock_reader_unlock #define RW_W_LOCK g_rw_lock_writer_lock #define RW_W_UNLOCK g_rw_lock_writer_unlock #else #define RW_R_LOCK g_static_rw_lock_reader_lock #define RW_R_UNLOCK g_static_rw_lock_reader_unlock #define RW_W_LOCK g_static_rw_lock_writer_lock #define RW_W_UNLOCK g_static_rw_lock_writer_unlock #endif /* Callbacks for cache */ typedef void (*cache_init)(struct rspamd_kv_cache *cache); typedef struct rspamd_kv_element* (*cache_insert)(struct rspamd_kv_cache *cache, gpointer key, guint keylen, gpointer value, gsize len); typedef gboolean (*cache_replace)(struct rspamd_kv_cache *cache, gpointer key, guint keylen, struct rspamd_kv_element *elt); typedef struct rspamd_kv_element* (*cache_lookup)(struct rspamd_kv_cache *cache, gpointer key, guint keylen); typedef struct rspamd_kv_element* (*cache_delete)(struct rspamd_kv_cache *cache, gpointer key, guint keylen); typedef void (*cache_steal)(struct rspamd_kv_cache *cache, struct rspamd_kv_element* elt); typedef void (*cache_destroy)(struct rspamd_kv_cache *cache); /* Callbacks for backend */ typedef void (*backend_init)(struct rspamd_kv_backend *backend); typedef gboolean (*backend_insert)(struct rspamd_kv_backend *backend, gpointer key, guint keylen, struct rspamd_kv_element *elt); typedef gboolean (*backend_replace)(struct rspamd_kv_backend *backend, gpointer key, guint keylen, struct rspamd_kv_element *elt); typedef struct rspamd_kv_element* (*backend_lookup)(struct rspamd_kv_backend *backend, gpointer key, guint keylen); typedef void (*backend_delete)(struct rspamd_kv_backend *backend, gpointer key, guint keylen); typedef gboolean (*backend_sync)(struct rspamd_kv_backend *backend); typedef gboolean (*backend_incref)(struct rspamd_kv_backend *backend, gpointer key, guint keylen); typedef void (*backend_destroy)(struct rspamd_kv_backend *backend); /* Callbacks for expire */ typedef void (*expire_init)(struct rspamd_kv_expire *expire); typedef void (*expire_insert)(struct rspamd_kv_expire *expire, struct rspamd_kv_element *elt); typedef void (*expire_delete)(struct rspamd_kv_expire *expire, struct rspamd_kv_element *elt); typedef gboolean (*expire_step)(struct rspamd_kv_expire *expire, struct rspamd_kv_storage *storage, time_t now, gboolean forced); typedef void (*expire_destroy)(struct rspamd_kv_expire *expire); /* Flags of element */ enum rspamd_kv_flags { KV_ELT_ARRAY = 1 << 0, KV_ELT_PERSISTENT = 1 << 1, KV_ELT_DIRTY = 1 << 2, KV_ELT_OUSTED = 1 << 3, KV_ELT_NEED_FREE = 1 << 4, KV_ELT_INTEGER = 1 << 5, KV_ELT_NEED_INSERT = 1 << 6, KV_ELT_NEED_EXPIRE = 1 << 7 }; #define ELT_DATA(elt) (gchar *)(elt)->data + (elt)->keylen + 1 #define ELT_LONG(elt) *((glong *)((elt)->data + (elt)->keylen + 1)) #define ELT_KEY(elt) (gchar *)(elt)->data #define ELT_SIZE(elt) elt->size + sizeof(struct rspamd_kv_element) + elt->keylen + 1 /* Common structures description */ struct rspamd_kv_element { time_t age; /*< age of element */ guint32 expire; /*< expire of element */ enum rspamd_kv_flags flags; /*< element flags */ gsize size; /*< size of element */ TAILQ_ENTRY (rspamd_kv_element) entry; /*< list entry */ guint keylen; /*< length of key */ gpointer p; /*< pointer to data */ gchar data[1]; /*< expandable data */ }; struct rspamd_kv_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 */ }; struct rspamd_kv_backend { backend_init init_func; /*< this callback is called on kv storage initialization */ backend_insert insert_func; /*< this callback is called when element is inserted */ backend_replace replace_func; /*< this callback is called when element is replaced */ backend_lookup lookup_func; /*< this callback is used for lookup of element */ backend_delete delete_func; /*< this callback is called when an element is deleted */ backend_sync sync_func; /*< this callback is called when backend need to be synced */ backend_incref incref_func; /*< this callback is called when element must be ref'd */ backend_destroy destroy_func; /*< this callback is used for destroying all elements inside backend */ }; struct rspamd_kv_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 */ }; /* Main kv storage structure */ struct rspamd_kv_storage { struct rspamd_kv_cache *cache; struct rspamd_kv_backend *backend; struct rspamd_kv_expire *expire; gsize elts; /*< current elements count in a storage */ gsize max_elts; /*< maximum number of elements in a storage */ gsize memory; /*< memory eaten */ gsize max_memory; /*< memory limit */ gint id; /* char ID */ gchar *name; /* numeric ID */ gboolean no_overwrite; /* do not overwrite data with the same keys */ #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30)) GRWLock rwlock; /* rwlock in new glib */ #else GStaticRWLock rwlock; /* rwlock for threaded access */ #endif }; /** 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); /** 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); /** Insert element only in cache */ 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); /** 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); /** Increment value in kvstorage */ gboolean rspamd_kv_storage_increment (struct rspamd_kv_storage *storage, gpointer key, guint keylen, glong *value); /** 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); /** Expire an element from kv storage */ struct rspamd_kv_element* rspamd_kv_storage_delete (struct rspamd_kv_storage *storage, gpointer key, guint keylen); /** Destroy kv storage */ void rspamd_kv_storage_destroy (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); /** 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); /** 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); /* Hash table functions */ guint kv_elt_hash_func (gconstpointer e); gboolean kv_elt_compare_func (gconstpointer e1, gconstpointer e2); /** * LRU expire */ struct rspamd_kv_expire* rspamd_lru_expire_new (); /** * Ordinary hash */ struct rspamd_kv_cache* rspamd_kv_hash_new (void); /** * Radix tree */ struct rspamd_kv_cache* rspamd_kv_radix_new (void); #ifdef WITH_JUDY /** * Judy tree */ struct rspamd_kv_cache* rspamd_kv_judy_new (void); #endif #endif /* KVSTORAGE_H_ */