You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

hash.c 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "hash.h"
  18. #include "util.h"
  19. /**
  20. * LRU hashing
  21. */
  22. struct rspamd_lru_hash_s {
  23. gint maxsize;
  24. gint maxage;
  25. GDestroyNotify value_destroy;
  26. GDestroyNotify key_destroy;
  27. GHashTable *tbl;
  28. GQueue *exp; /* Elements are inserted to the tail and removed from the front */
  29. };
  30. static void
  31. rspamd_lru_destroy_node (gpointer value)
  32. {
  33. rspamd_lru_element_t *elt = (rspamd_lru_element_t *)value;
  34. if (elt) {
  35. if (elt->hash && elt->hash->key_destroy) {
  36. elt->hash->key_destroy (elt->key);
  37. }
  38. if (elt->hash && elt->hash->value_destroy) {
  39. elt->hash->value_destroy (elt->data);
  40. }
  41. if (elt->hash && elt->link) {
  42. g_queue_delete_link (elt->hash->exp, elt->link);
  43. }
  44. g_slice_free1 (sizeof (*elt), elt);
  45. }
  46. }
  47. static rspamd_lru_element_t *
  48. rspamd_lru_create_node (rspamd_lru_hash_t *hash,
  49. gpointer key,
  50. gpointer value,
  51. time_t now,
  52. guint ttl)
  53. {
  54. rspamd_lru_element_t *node;
  55. node = g_slice_alloc (sizeof (rspamd_lru_element_t));
  56. node->data = value;
  57. node->key = key;
  58. node->store_time = now;
  59. node->ttl = ttl;
  60. node->hash = hash;
  61. return node;
  62. }
  63. rspamd_lru_hash_t *
  64. rspamd_lru_hash_new_full (
  65. gint maxsize,
  66. gint maxage,
  67. GDestroyNotify key_destroy,
  68. GDestroyNotify value_destroy,
  69. GHashFunc hf,
  70. GEqualFunc cmpf)
  71. {
  72. rspamd_lru_hash_t *new;
  73. new = g_slice_alloc (sizeof (rspamd_lru_hash_t));
  74. new->tbl = g_hash_table_new_full (hf, cmpf, NULL, rspamd_lru_destroy_node);
  75. new->exp = g_queue_new ();
  76. new->maxage = maxage;
  77. new->maxsize = maxsize;
  78. new->value_destroy = value_destroy;
  79. new->key_destroy = key_destroy;
  80. return new;
  81. }
  82. rspamd_lru_hash_t *
  83. rspamd_lru_hash_new (
  84. gint maxsize,
  85. gint maxage,
  86. GDestroyNotify key_destroy,
  87. GDestroyNotify value_destroy)
  88. {
  89. return rspamd_lru_hash_new_full (maxsize, maxage,
  90. key_destroy, value_destroy,
  91. rspamd_strcase_hash, rspamd_strcase_equal);
  92. }
  93. gpointer
  94. rspamd_lru_hash_lookup (rspamd_lru_hash_t *hash, gconstpointer key, time_t now)
  95. {
  96. rspamd_lru_element_t *res;
  97. GList *cur, *tmp;
  98. res = g_hash_table_lookup (hash->tbl, key);
  99. if (res != NULL) {
  100. if (res->ttl != 0) {
  101. if (now - res->store_time > res->ttl) {
  102. g_hash_table_remove (hash->tbl, key);
  103. return NULL;
  104. }
  105. }
  106. if (hash->maxage > 0) {
  107. if (now - res->store_time > hash->maxage) {
  108. /* Expire elements from queue head */
  109. cur = hash->exp->head;
  110. while (cur) {
  111. tmp = cur->next;
  112. res = (rspamd_lru_element_t *)cur->data;
  113. if (now - res->store_time > hash->maxage) {
  114. /* That would also remove element from the queue */
  115. g_hash_table_remove (hash->tbl, res->key);
  116. }
  117. else {
  118. break;
  119. }
  120. cur = tmp;
  121. }
  122. return NULL;
  123. }
  124. }
  125. else {
  126. res->store_time = now;
  127. /* Reinsert element to the tail */
  128. g_queue_unlink (hash->exp, res->link);
  129. g_queue_push_tail_link (hash->exp, res->link);
  130. }
  131. return res->data;
  132. }
  133. return NULL;
  134. }
  135. void
  136. rspamd_lru_hash_insert (rspamd_lru_hash_t *hash, gpointer key, gpointer value,
  137. time_t now, guint ttl)
  138. {
  139. rspamd_lru_element_t *res;
  140. gint removed = 0;
  141. GList *cur, *tmp;
  142. res = g_hash_table_lookup (hash->tbl, key);
  143. if (res != NULL) {
  144. g_hash_table_remove (hash->tbl, key);
  145. }
  146. else {
  147. if (hash->maxsize > 0 &&
  148. (gint)g_hash_table_size (hash->tbl) >= hash->maxsize) {
  149. /* Expire some elements */
  150. if (hash->maxage > 0) {
  151. cur = hash->exp->head;
  152. while (cur) {
  153. tmp = cur->next;
  154. res = (rspamd_lru_element_t *)cur->data;
  155. if (now - res->store_time > hash->maxage) {
  156. /* That would also remove element from the queue */
  157. g_hash_table_remove (hash->tbl, res->key);
  158. removed ++;
  159. }
  160. else {
  161. break;
  162. }
  163. cur = tmp;
  164. }
  165. }
  166. if (removed == 0) {
  167. /* Just unlink the element at the head */
  168. res = (rspamd_lru_element_t *)hash->exp->head->data;
  169. g_hash_table_remove (hash->tbl, res->key);
  170. }
  171. }
  172. }
  173. res = rspamd_lru_create_node (hash, key, value, now, ttl);
  174. g_hash_table_insert (hash->tbl, key, res);
  175. g_queue_push_tail (hash->exp, res);
  176. res->link = hash->exp->tail;
  177. }
  178. void
  179. rspamd_lru_hash_destroy (rspamd_lru_hash_t *hash)
  180. {
  181. g_hash_table_unref (hash->tbl);
  182. g_queue_free (hash->exp);
  183. g_slice_free1 (sizeof (rspamd_lru_hash_t), hash);
  184. }
  185. GHashTable *
  186. rspamd_lru_hash_get_htable (rspamd_lru_hash_t *hash)
  187. {
  188. return hash->tbl;
  189. }
  190. GQueue *
  191. rspamd_lru_hash_get_queue (rspamd_lru_hash_t *hash)
  192. {
  193. return hash->exp;
  194. }
  195. /*
  196. * vi:ts=4
  197. */