123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- /*
- * Copyright (c) 2009-2012, 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 BY AUTHOR ''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 "hash.h"
-
- #define HASH_TABLE_MIN_SIZE 19
- #define HASH_TABLE_MAX_SIZE 13845163
-
- /*
- * Performs a lookup in the hash table. Virtually all hash operations
- * will use this function internally.
- */
- static inline struct rspamd_hash_node **
- rspamd_hash_lookup_node (rspamd_hash_t * hash, gconstpointer key, guint * hash_return)
- {
- struct rspamd_hash_node **node_ptr, *node;
- guint hash_value;
- hash_value = (*hash->hash_func) (key);
-
- if (hash->shared) {
- memory_pool_rlock_rwlock (hash->lock);
- }
- node_ptr = &hash->nodes[hash_value % hash->size];
-
- if (hash_return)
- *hash_return = hash_value;
-
- /* Hash table lookup needs to be fast.
- * We therefore remove the extra conditional of testing
- * whether to call the key_equal_func or not from
- * the inner loop.
- *
- * Additional optimisation: first check if our full hash
- * values are equal so we can avoid calling the full-blown
- * key equality function in most cases.
- */
- if (hash->key_equal_func) {
- while ((node = *node_ptr)) {
- if (node->key_hash == hash_value && hash->key_equal_func (node->key, key)) {
- break;
- }
- node_ptr = &(*node_ptr)->next;
- }
- }
- else {
- while ((node = *node_ptr)) {
- if (node->key == key) {
- break;
- }
- node_ptr = &(*node_ptr)->next;
- }
- }
- if (hash->shared) {
- memory_pool_runlock_rwlock (hash->lock);
- }
- return node_ptr;
- }
-
- /*
- * Removes a node from the hash table and updates the node count.
- * No table resize is performed.
- */
- static void
- rspamd_hash_remove_node (rspamd_hash_t * hash, struct rspamd_hash_node ***node_ptr_ptr)
- {
- struct rspamd_hash_node **node_ptr, *node;
-
- if (hash->shared) {
- memory_pool_wlock_rwlock (hash->lock);
- }
- node_ptr = *node_ptr_ptr;
- node = *node_ptr;
-
- *node_ptr = node->next;
-
- hash->nnodes--;
- if (hash->shared) {
- memory_pool_wunlock_rwlock (hash->lock);
- }
- }
-
- /*
- * Resizes the hash table to the optimal size based on the number of
- * nodes currently held.
- */
- static void
- rspamd_hash_resize (rspamd_hash_t * hash)
- {
- struct rspamd_hash_node **new_nodes;
- struct rspamd_hash_node *node, *next;
- guint hash_val;
- gint new_size, i;
-
- new_size = g_spaced_primes_closest (hash->nnodes);
- new_size = CLAMP (new_size, HASH_TABLE_MIN_SIZE, HASH_TABLE_MAX_SIZE);
-
- if (hash->shared) {
- new_nodes = memory_pool_alloc_shared (hash->pool, sizeof (struct rspamd_hash_node *) * new_size);
- }
- else {
- new_nodes = memory_pool_alloc (hash->pool, sizeof (struct rspamd_hash_node *) * new_size);
- }
-
- if (hash->shared) {
- memory_pool_wlock_rwlock (hash->lock);
- }
-
- for (i = 0; i < hash->size; i++) {
- for (node = hash->nodes[i]; node; node = next) {
- next = node->next;
- hash_val = node->key_hash % new_size;
- node->next = new_nodes[hash_val];
- new_nodes[hash_val] = node;
- }
- }
-
- hash->nodes = new_nodes;
- hash->size = new_size;
-
- if (hash->shared) {
- memory_pool_wunlock_rwlock (hash->lock);
- }
- }
-
- /*
- * Resizes the hash table, if needed.
- */
- static inline void
- rspamd_hash_maybe_resize (rspamd_hash_t * hash)
- {
- gint nnodes = hash->nnodes;
- gint size = hash->size;
-
- if ((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) || (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE)) {
- rspamd_hash_resize (hash);
- }
- }
-
- /* Create new hash in specified pool */
- rspamd_hash_t *
- rspamd_hash_new (memory_pool_t * pool, GHashFunc hash_func, GEqualFunc key_equal_func)
- {
- rspamd_hash_t *hash;
-
- hash = memory_pool_alloc (pool, sizeof (rspamd_hash_t));
- hash->size = HASH_TABLE_MIN_SIZE;
- hash->nnodes = 0;
- hash->hash_func = hash_func ? hash_func : g_direct_hash;
- hash->key_equal_func = key_equal_func;
- hash->nodes = memory_pool_alloc0 (pool, sizeof (struct rspamd_hash_node *) * hash->size);
- hash->shared = 0;
- hash->pool = pool;
-
- return hash;
- }
-
- /*
- * Create new hash in specified pool using shared memory
- */
- rspamd_hash_t *
- rspamd_hash_new_shared (memory_pool_t * pool, GHashFunc hash_func, GEqualFunc key_equal_func, gint size)
- {
- rspamd_hash_t *hash;
-
- hash = memory_pool_alloc_shared (pool, sizeof (rspamd_hash_t));
- hash->size = size;
- hash->nnodes = 0;
- hash->hash_func = hash_func ? hash_func : g_direct_hash;
- hash->key_equal_func = key_equal_func;
- hash->nodes = memory_pool_alloc0_shared (pool, sizeof (struct rspamd_hash_node *) * hash->size);
- hash->shared = 1;
- /* Get mutex from pool for locking on insert/remove operations */
- hash->lock = memory_pool_get_rwlock (pool);
- hash->pool = pool;
-
- return hash;
- }
-
- /*
- * Insert item in hash
- */
- void
- rspamd_hash_insert (rspamd_hash_t * hash, gpointer key, gpointer value)
- {
- struct rspamd_hash_node **node_ptr, *node;
- guint key_hash;
-
- g_return_if_fail (hash != NULL);
- node_ptr = rspamd_hash_lookup_node (hash, key, &key_hash);
-
- if (hash->shared) {
- memory_pool_wlock_rwlock (hash->lock);
- }
- if ((node = *node_ptr)) {
- node->key = key;
- node->value = value;
- }
- else {
- if (hash->shared) {
- node = memory_pool_alloc_shared (hash->pool, sizeof (struct rspamd_hash_node));
- }
- else {
- node = memory_pool_alloc (hash->pool, sizeof (struct rspamd_hash_node));
- }
-
- node->key = key;
- node->value = value;
- node->key_hash = key_hash;
- node->next = NULL;
-
- *node_ptr = node;
- hash->nnodes++;
- }
- if (hash->shared) {
- memory_pool_wunlock_rwlock (hash->lock);
- }
-
- if (!hash->shared) {
- rspamd_hash_maybe_resize (hash);
- }
- }
-
- /*
- * Remove item from hash
- */
- gboolean
- rspamd_hash_remove (rspamd_hash_t * hash, gpointer key)
- {
- struct rspamd_hash_node **node_ptr;
-
- g_return_val_if_fail (hash != NULL, FALSE);
-
- node_ptr = rspamd_hash_lookup_node (hash, key, NULL);
- if (*node_ptr == NULL)
- return FALSE;
-
- rspamd_hash_remove_node (hash, &node_ptr);
- rspamd_hash_maybe_resize (hash);
-
- return TRUE;
- }
-
- /*
- * Lookup item from hash
- */
- gpointer
- rspamd_hash_lookup (rspamd_hash_t * hash, gpointer key)
- {
- struct rspamd_hash_node *node;
- g_return_val_if_fail (hash != NULL, NULL);
-
- node = *rspamd_hash_lookup_node (hash, key, NULL);
-
- return node ? node->value : NULL;
- }
-
- /*
- * Iterate throught hash
- */
- void
- rspamd_hash_foreach (rspamd_hash_t * hash, GHFunc func, gpointer user_data)
- {
- struct rspamd_hash_node *node;
- gint i;
-
- g_return_if_fail (hash != NULL);
- g_return_if_fail (func != NULL);
-
- if (hash->shared) {
- memory_pool_rlock_rwlock (hash->lock);
- }
- for (i = 0; i < hash->size; i++) {
- for (node = hash->nodes[i]; node; node = node->next) {
- (*func) (node->key, node->value, user_data);
- }
- }
- if (hash->shared) {
- memory_pool_runlock_rwlock (hash->lock);
- }
- }
-
- /**
- * LRU hashing
- */
-
- static void
- rspamd_lru_hash_destroy_node (gpointer v)
- {
- rspamd_lru_element_t *node = v;
-
- if (node->hash->value_destroy) {
- node->hash->value_destroy (node->data);
- }
-
- g_slice_free1 (sizeof (rspamd_lru_element_t), node);
- }
-
- static rspamd_lru_element_t*
- rspamd_lru_create_node (rspamd_lru_hash_t *hash, gpointer key, gpointer value, time_t now)
- {
- rspamd_lru_element_t *node;
-
- node = g_slice_alloc (sizeof (rspamd_lru_element_t));
- node->hash = hash;
- node->data = value;
- node->key = key;
- node->store_time = now;
-
- return node;
- }
-
- /**
- * Create new lru hash with GHashTable as storage
- * @param maxsize maximum elements in a hash
- * @param maxage maximum age of elemnt
- * @param hash_func pointer to hash function
- * @param key_equal_func pointer to function for comparing keys
- * @return new rspamd_hash object
- */
- rspamd_lru_hash_t*
- rspamd_lru_hash_new (GHashFunc hash_func, GEqualFunc key_equal_func, gint maxsize, gint maxage,
- GDestroyNotify key_destroy, GDestroyNotify value_destroy)
- {
- rspamd_lru_hash_t *new;
-
- new = g_malloc (sizeof (rspamd_lru_hash_t));
- new->storage = g_hash_table_new_full (hash_func, key_equal_func, key_destroy, rspamd_lru_hash_destroy_node);
- new->maxage = maxage;
- new->maxsize = maxsize;
- new->value_destroy = value_destroy;
- new->key_destroy = NULL;
- new->q = g_queue_new ();
- new->insert_func = (lru_cache_insert_func)g_hash_table_replace;
- new->lookup_func = (lru_cache_lookup_func)g_hash_table_lookup;
- new->delete_func = (lru_cache_delete_func)g_hash_table_remove;
- new->destroy_func = (lru_cache_destroy_func)g_hash_table_destroy;
-
- return new;
- }
- /**
- * Create new lru hash with custom storage
- * @param maxsize maximum elements in a hash
- * @param maxage maximum age of elemnt
- * @param hash_func pointer to hash function
- * @param key_equal_func pointer to function for comparing keys
- * @return new rspamd_hash object
- */
- rspamd_lru_hash_t*
- rspamd_lru_hash_new_full (GHashFunc hash_func, GEqualFunc key_equal_func,
- gint maxsize, gint maxage, GDestroyNotify key_destroy, GDestroyNotify value_destroy,
- gpointer storage, lru_cache_insert_func insert_func, lru_cache_lookup_func lookup_func,
- lru_cache_delete_func delete_func)
- {
- rspamd_lru_hash_t *new;
-
- new = g_malloc (sizeof (rspamd_lru_hash_t));
- new->storage = storage;
- new->maxage = maxage;
- new->maxsize = maxsize;
- new->value_destroy = value_destroy;
- new->key_destroy = key_destroy;
- new->q = g_queue_new ();
- new->insert_func = insert_func;
- new->lookup_func = lookup_func;
- new->delete_func = delete_func;
- new->destroy_func = NULL;
-
- return new;
- }
-
- /**
- * Lookup item from hash
- * @param hash hash object
- * @param key key to find
- * @return value of key or NULL if key is not found
- */
- gpointer
- rspamd_lru_hash_lookup (rspamd_lru_hash_t *hash, gpointer key, time_t now)
- {
- rspamd_lru_element_t *res;
-
- if ((res = hash->lookup_func (hash->storage, key)) != NULL) {
- if (hash->maxage > 0) {
- if (now - res->store_time > hash->maxage) {
- /* Expire elements from queue tail */
- res = g_queue_pop_tail (hash->q);
-
- while (res != NULL && now - res->store_time > hash->maxage) {
- hash->delete_func (hash->storage, res->key);
- res = g_queue_pop_tail (hash->q);
- }
- /* Restore last element */
- if (res != NULL) {
- g_queue_push_tail (hash->q, res);
- }
-
- return NULL;
- }
- }
- }
-
- if (res) {
- return res->data;
- }
-
- return NULL;
- }
- /**
- * Insert item in hash
- * @param hash hash object
- * @param key key to insert
- * @param value value of key
- */
- void
- rspamd_lru_hash_insert (rspamd_lru_hash_t *hash, gpointer key, gpointer value, time_t now)
- {
- rspamd_lru_element_t *res;
- gint removed = 0;
-
- if ((gint)g_queue_get_length (hash->q) >= hash->maxsize) {
- /* Expire some elements */
- res = g_queue_pop_tail (hash->q);
- if (hash->maxage > 0) {
- while (res != NULL && now - res->store_time > hash->maxage) {
- if (res->key != NULL) {
- hash->delete_func (hash->storage, res->key);
- }
- res = g_queue_pop_tail (hash->q);
- removed ++;
- }
- }
- /* If elements are already removed do not expire extra elements */
- if (removed != 0 && res != NULL) {
- g_queue_push_tail (hash->q, res);
- }
- }
-
- res = rspamd_lru_create_node (hash, key, value, now);
- hash->insert_func (hash->storage, key, res);
- g_queue_push_head (hash->q, res);
- }
-
- void
- rspamd_lru_hash_destroy (rspamd_lru_hash_t *hash)
- {
- if (hash->destroy_func) {
- hash->destroy_func (hash->storage);
- }
- g_queue_free (hash->q);
- g_free (hash);
- }
-
- /*
- * vi:ts=4
- */
|