Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

hash.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /*
  2. * Copyright (c) 2009-2012, Vsevolod Stakhov
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
  17. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  18. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  19. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  20. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  22. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. */
  24. #include "config.h"
  25. #include "hash.h"
  26. #define HASH_TABLE_MIN_SIZE 19
  27. #define HASH_TABLE_MAX_SIZE 13845163
  28. /*
  29. * Performs a lookup in the hash table. Virtually all hash operations
  30. * will use this function internally.
  31. */
  32. static inline struct rspamd_hash_node **
  33. rspamd_hash_lookup_node (rspamd_hash_t * hash, gconstpointer key, guint * hash_return)
  34. {
  35. struct rspamd_hash_node **node_ptr, *node;
  36. guint hash_value;
  37. hash_value = (*hash->hash_func) (key);
  38. if (hash->shared) {
  39. memory_pool_rlock_rwlock (hash->lock);
  40. }
  41. node_ptr = &hash->nodes[hash_value % hash->size];
  42. if (hash_return)
  43. *hash_return = hash_value;
  44. /* Hash table lookup needs to be fast.
  45. * We therefore remove the extra conditional of testing
  46. * whether to call the key_equal_func or not from
  47. * the inner loop.
  48. *
  49. * Additional optimisation: first check if our full hash
  50. * values are equal so we can avoid calling the full-blown
  51. * key equality function in most cases.
  52. */
  53. if (hash->key_equal_func) {
  54. while ((node = *node_ptr)) {
  55. if (node->key_hash == hash_value && hash->key_equal_func (node->key, key)) {
  56. break;
  57. }
  58. node_ptr = &(*node_ptr)->next;
  59. }
  60. }
  61. else {
  62. while ((node = *node_ptr)) {
  63. if (node->key == key) {
  64. break;
  65. }
  66. node_ptr = &(*node_ptr)->next;
  67. }
  68. }
  69. if (hash->shared) {
  70. memory_pool_runlock_rwlock (hash->lock);
  71. }
  72. return node_ptr;
  73. }
  74. /*
  75. * Removes a node from the hash table and updates the node count.
  76. * No table resize is performed.
  77. */
  78. static void
  79. rspamd_hash_remove_node (rspamd_hash_t * hash, struct rspamd_hash_node ***node_ptr_ptr)
  80. {
  81. struct rspamd_hash_node **node_ptr, *node;
  82. if (hash->shared) {
  83. memory_pool_wlock_rwlock (hash->lock);
  84. }
  85. node_ptr = *node_ptr_ptr;
  86. node = *node_ptr;
  87. *node_ptr = node->next;
  88. hash->nnodes--;
  89. if (hash->shared) {
  90. memory_pool_wunlock_rwlock (hash->lock);
  91. }
  92. }
  93. /*
  94. * Resizes the hash table to the optimal size based on the number of
  95. * nodes currently held.
  96. */
  97. static void
  98. rspamd_hash_resize (rspamd_hash_t * hash)
  99. {
  100. struct rspamd_hash_node **new_nodes;
  101. struct rspamd_hash_node *node, *next;
  102. guint hash_val;
  103. gint new_size, i;
  104. new_size = g_spaced_primes_closest (hash->nnodes);
  105. new_size = CLAMP (new_size, HASH_TABLE_MIN_SIZE, HASH_TABLE_MAX_SIZE);
  106. if (hash->shared) {
  107. new_nodes = memory_pool_alloc_shared (hash->pool, sizeof (struct rspamd_hash_node *) * new_size);
  108. }
  109. else {
  110. new_nodes = memory_pool_alloc (hash->pool, sizeof (struct rspamd_hash_node *) * new_size);
  111. }
  112. if (hash->shared) {
  113. memory_pool_wlock_rwlock (hash->lock);
  114. }
  115. for (i = 0; i < hash->size; i++) {
  116. for (node = hash->nodes[i]; node; node = next) {
  117. next = node->next;
  118. hash_val = node->key_hash % new_size;
  119. node->next = new_nodes[hash_val];
  120. new_nodes[hash_val] = node;
  121. }
  122. }
  123. hash->nodes = new_nodes;
  124. hash->size = new_size;
  125. if (hash->shared) {
  126. memory_pool_wunlock_rwlock (hash->lock);
  127. }
  128. }
  129. /*
  130. * Resizes the hash table, if needed.
  131. */
  132. static inline void
  133. rspamd_hash_maybe_resize (rspamd_hash_t * hash)
  134. {
  135. gint nnodes = hash->nnodes;
  136. gint size = hash->size;
  137. if ((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) || (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE)) {
  138. rspamd_hash_resize (hash);
  139. }
  140. }
  141. /* Create new hash in specified pool */
  142. rspamd_hash_t *
  143. rspamd_hash_new (memory_pool_t * pool, GHashFunc hash_func, GEqualFunc key_equal_func)
  144. {
  145. rspamd_hash_t *hash;
  146. hash = memory_pool_alloc (pool, sizeof (rspamd_hash_t));
  147. hash->size = HASH_TABLE_MIN_SIZE;
  148. hash->nnodes = 0;
  149. hash->hash_func = hash_func ? hash_func : g_direct_hash;
  150. hash->key_equal_func = key_equal_func;
  151. hash->nodes = memory_pool_alloc0 (pool, sizeof (struct rspamd_hash_node *) * hash->size);
  152. hash->shared = 0;
  153. hash->pool = pool;
  154. return hash;
  155. }
  156. /*
  157. * Create new hash in specified pool using shared memory
  158. */
  159. rspamd_hash_t *
  160. rspamd_hash_new_shared (memory_pool_t * pool, GHashFunc hash_func, GEqualFunc key_equal_func, gint size)
  161. {
  162. rspamd_hash_t *hash;
  163. hash = memory_pool_alloc_shared (pool, sizeof (rspamd_hash_t));
  164. hash->size = size;
  165. hash->nnodes = 0;
  166. hash->hash_func = hash_func ? hash_func : g_direct_hash;
  167. hash->key_equal_func = key_equal_func;
  168. hash->nodes = memory_pool_alloc0_shared (pool, sizeof (struct rspamd_hash_node *) * hash->size);
  169. hash->shared = 1;
  170. /* Get mutex from pool for locking on insert/remove operations */
  171. hash->lock = memory_pool_get_rwlock (pool);
  172. hash->pool = pool;
  173. return hash;
  174. }
  175. /*
  176. * Insert item in hash
  177. */
  178. void
  179. rspamd_hash_insert (rspamd_hash_t * hash, gpointer key, gpointer value)
  180. {
  181. struct rspamd_hash_node **node_ptr, *node;
  182. guint key_hash;
  183. g_return_if_fail (hash != NULL);
  184. node_ptr = rspamd_hash_lookup_node (hash, key, &key_hash);
  185. if (hash->shared) {
  186. memory_pool_wlock_rwlock (hash->lock);
  187. }
  188. if ((node = *node_ptr)) {
  189. node->key = key;
  190. node->value = value;
  191. }
  192. else {
  193. if (hash->shared) {
  194. node = memory_pool_alloc_shared (hash->pool, sizeof (struct rspamd_hash_node));
  195. }
  196. else {
  197. node = memory_pool_alloc (hash->pool, sizeof (struct rspamd_hash_node));
  198. }
  199. node->key = key;
  200. node->value = value;
  201. node->key_hash = key_hash;
  202. node->next = NULL;
  203. *node_ptr = node;
  204. hash->nnodes++;
  205. }
  206. if (hash->shared) {
  207. memory_pool_wunlock_rwlock (hash->lock);
  208. }
  209. if (!hash->shared) {
  210. rspamd_hash_maybe_resize (hash);
  211. }
  212. }
  213. /*
  214. * Remove item from hash
  215. */
  216. gboolean
  217. rspamd_hash_remove (rspamd_hash_t * hash, gpointer key)
  218. {
  219. struct rspamd_hash_node **node_ptr;
  220. g_return_val_if_fail (hash != NULL, FALSE);
  221. node_ptr = rspamd_hash_lookup_node (hash, key, NULL);
  222. if (*node_ptr == NULL)
  223. return FALSE;
  224. rspamd_hash_remove_node (hash, &node_ptr);
  225. rspamd_hash_maybe_resize (hash);
  226. return TRUE;
  227. }
  228. /*
  229. * Lookup item from hash
  230. */
  231. gpointer
  232. rspamd_hash_lookup (rspamd_hash_t * hash, gpointer key)
  233. {
  234. struct rspamd_hash_node *node;
  235. g_return_val_if_fail (hash != NULL, NULL);
  236. node = *rspamd_hash_lookup_node (hash, key, NULL);
  237. return node ? node->value : NULL;
  238. }
  239. /*
  240. * Iterate throught hash
  241. */
  242. void
  243. rspamd_hash_foreach (rspamd_hash_t * hash, GHFunc func, gpointer user_data)
  244. {
  245. struct rspamd_hash_node *node;
  246. gint i;
  247. g_return_if_fail (hash != NULL);
  248. g_return_if_fail (func != NULL);
  249. if (hash->shared) {
  250. memory_pool_rlock_rwlock (hash->lock);
  251. }
  252. for (i = 0; i < hash->size; i++) {
  253. for (node = hash->nodes[i]; node; node = node->next) {
  254. (*func) (node->key, node->value, user_data);
  255. }
  256. }
  257. if (hash->shared) {
  258. memory_pool_runlock_rwlock (hash->lock);
  259. }
  260. }
  261. /**
  262. * LRU hashing
  263. */
  264. static void
  265. rspamd_lru_hash_destroy_node (gpointer v)
  266. {
  267. rspamd_lru_element_t *node = v;
  268. if (node->hash->value_destroy) {
  269. node->hash->value_destroy (node->data);
  270. }
  271. g_slice_free1 (sizeof (rspamd_lru_element_t), node);
  272. }
  273. static rspamd_lru_element_t*
  274. rspamd_lru_create_node (rspamd_lru_hash_t *hash, gpointer key, gpointer value, time_t now)
  275. {
  276. rspamd_lru_element_t *node;
  277. node = g_slice_alloc (sizeof (rspamd_lru_element_t));
  278. node->hash = hash;
  279. node->data = value;
  280. node->key = key;
  281. node->store_time = now;
  282. return node;
  283. }
  284. /**
  285. * Create new lru hash with GHashTable as storage
  286. * @param maxsize maximum elements in a hash
  287. * @param maxage maximum age of elemnt
  288. * @param hash_func pointer to hash function
  289. * @param key_equal_func pointer to function for comparing keys
  290. * @return new rspamd_hash object
  291. */
  292. rspamd_lru_hash_t*
  293. rspamd_lru_hash_new (GHashFunc hash_func, GEqualFunc key_equal_func, gint maxsize, gint maxage,
  294. GDestroyNotify key_destroy, GDestroyNotify value_destroy)
  295. {
  296. rspamd_lru_hash_t *new;
  297. new = g_malloc (sizeof (rspamd_lru_hash_t));
  298. new->storage = g_hash_table_new_full (hash_func, key_equal_func, key_destroy, rspamd_lru_hash_destroy_node);
  299. new->maxage = maxage;
  300. new->maxsize = maxsize;
  301. new->value_destroy = value_destroy;
  302. new->key_destroy = NULL;
  303. new->q = g_queue_new ();
  304. new->insert_func = (lru_cache_insert_func)g_hash_table_replace;
  305. new->lookup_func = (lru_cache_lookup_func)g_hash_table_lookup;
  306. new->delete_func = (lru_cache_delete_func)g_hash_table_remove;
  307. new->destroy_func = (lru_cache_destroy_func)g_hash_table_destroy;
  308. return new;
  309. }
  310. /**
  311. * Create new lru hash with custom storage
  312. * @param maxsize maximum elements in a hash
  313. * @param maxage maximum age of elemnt
  314. * @param hash_func pointer to hash function
  315. * @param key_equal_func pointer to function for comparing keys
  316. * @return new rspamd_hash object
  317. */
  318. rspamd_lru_hash_t*
  319. rspamd_lru_hash_new_full (GHashFunc hash_func, GEqualFunc key_equal_func,
  320. gint maxsize, gint maxage, GDestroyNotify key_destroy, GDestroyNotify value_destroy,
  321. gpointer storage, lru_cache_insert_func insert_func, lru_cache_lookup_func lookup_func,
  322. lru_cache_delete_func delete_func)
  323. {
  324. rspamd_lru_hash_t *new;
  325. new = g_malloc (sizeof (rspamd_lru_hash_t));
  326. new->storage = storage;
  327. new->maxage = maxage;
  328. new->maxsize = maxsize;
  329. new->value_destroy = value_destroy;
  330. new->key_destroy = key_destroy;
  331. new->q = g_queue_new ();
  332. new->insert_func = insert_func;
  333. new->lookup_func = lookup_func;
  334. new->delete_func = delete_func;
  335. new->destroy_func = NULL;
  336. return new;
  337. }
  338. /**
  339. * Lookup item from hash
  340. * @param hash hash object
  341. * @param key key to find
  342. * @return value of key or NULL if key is not found
  343. */
  344. gpointer
  345. rspamd_lru_hash_lookup (rspamd_lru_hash_t *hash, gpointer key, time_t now)
  346. {
  347. rspamd_lru_element_t *res;
  348. if ((res = hash->lookup_func (hash->storage, key)) != NULL) {
  349. if (hash->maxage > 0) {
  350. if (now - res->store_time > hash->maxage) {
  351. /* Expire elements from queue tail */
  352. res = g_queue_pop_tail (hash->q);
  353. while (res != NULL && now - res->store_time > hash->maxage) {
  354. hash->delete_func (hash->storage, res->key);
  355. res = g_queue_pop_tail (hash->q);
  356. }
  357. /* Restore last element */
  358. if (res != NULL) {
  359. g_queue_push_tail (hash->q, res);
  360. }
  361. return NULL;
  362. }
  363. }
  364. }
  365. if (res) {
  366. return res->data;
  367. }
  368. return NULL;
  369. }
  370. /**
  371. * Insert item in hash
  372. * @param hash hash object
  373. * @param key key to insert
  374. * @param value value of key
  375. */
  376. void
  377. rspamd_lru_hash_insert (rspamd_lru_hash_t *hash, gpointer key, gpointer value, time_t now)
  378. {
  379. rspamd_lru_element_t *res;
  380. gint removed = 0;
  381. if ((gint)g_queue_get_length (hash->q) >= hash->maxsize) {
  382. /* Expire some elements */
  383. res = g_queue_pop_tail (hash->q);
  384. if (hash->maxage > 0) {
  385. while (res != NULL && now - res->store_time > hash->maxage) {
  386. if (res->key != NULL) {
  387. hash->delete_func (hash->storage, res->key);
  388. }
  389. res = g_queue_pop_tail (hash->q);
  390. removed ++;
  391. }
  392. }
  393. /* If elements are already removed do not expire extra elements */
  394. if (removed != 0 && res != NULL) {
  395. g_queue_push_tail (hash->q, res);
  396. }
  397. }
  398. res = rspamd_lru_create_node (hash, key, value, now);
  399. hash->insert_func (hash->storage, key, res);
  400. g_queue_push_head (hash->q, res);
  401. }
  402. void
  403. rspamd_lru_hash_destroy (rspamd_lru_hash_t *hash)
  404. {
  405. if (hash->destroy_func) {
  406. hash->destroy_func (hash->storage);
  407. }
  408. g_queue_free (hash->q);
  409. g_free (hash);
  410. }
  411. /*
  412. * vi:ts=4
  413. */