summaryrefslogtreecommitdiffstats
path: root/contrib/libucl/ucl_hash.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 21:30:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 21:30:40 +0000
commit133a45c109da5310add55824db21af5239951f93 (patch)
treeba6ac4c0a950a0dda56451944315d66409923918 /contrib/libucl/ucl_hash.c
parentInitial commit. (diff)
downloadrspamd-upstream.tar.xz
rspamd-upstream.zip
Adding upstream version 3.8.1.upstream/3.8.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/libucl/ucl_hash.c')
-rw-r--r--contrib/libucl/ucl_hash.c498
1 files changed, 498 insertions, 0 deletions
diff --git a/contrib/libucl/ucl_hash.c b/contrib/libucl/ucl_hash.c
new file mode 100644
index 0000000..a26c26f
--- /dev/null
+++ b/contrib/libucl/ucl_hash.c
@@ -0,0 +1,498 @@
+/* Copyright (c) 2013, 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 "ucl_internal.h"
+#include "ucl_hash.h"
+#include "khash.h"
+#include "utlist.h"
+
+#include "cryptobox.h"
+#include "libutil/str_util.h"
+#include "ucl.h"
+
+#include <time.h>
+#include <limits.h>
+
+struct ucl_hash_elt {
+ const ucl_object_t *obj;
+ struct ucl_hash_elt *prev, *next;
+};
+
+struct ucl_hash_struct {
+ void *hash;
+ struct ucl_hash_elt *head;
+ bool caseless;
+};
+
+static uint64_t
+ucl_hash_seed (void)
+{
+ static uint64_t seed;
+ if (seed == 0) {
+#ifdef UCL_RANDOM_FUNCTION
+ seed = UCL_RANDOM_FUNCTION;
+#else
+ /* Not very random but can be useful for our purposes */
+ seed = time (NULL);
+#endif
+ }
+
+ return seed;
+}
+
+extern const guchar lc_map[256];
+
+static inline uint32_t
+ucl_hash_func (const ucl_object_t *o)
+{
+ return (uint32_t)rspamd_cryptobox_fast_hash (o->key, o->keylen, 0xb9a1ef83c4561c95ULL);
+}
+
+static inline int
+ucl_hash_equal (const ucl_object_t *k1, const ucl_object_t *k2)
+{
+ if (k1->keylen == k2->keylen) {
+ return memcmp (k1->key, k2->key, k1->keylen) == 0;
+ }
+
+ return 0;
+}
+
+KHASH_INIT (ucl_hash_node, const ucl_object_t *, struct ucl_hash_elt *, 1,
+ ucl_hash_func, ucl_hash_equal)
+
+static inline uint32_t
+ucl_hash_caseless_func (const ucl_object_t *o)
+{
+ unsigned len = o->keylen;
+ unsigned leftover = o->keylen % 4;
+ unsigned fp, i;
+ const uint8_t* s = (const uint8_t*)o->key;
+ union {
+ struct {
+ unsigned char c1, c2, c3, c4;
+ } c;
+ uint32_t pp;
+ } u;
+ uint64_t h = 0xe5ae6ab1ef9f3b54ULL;
+ rspamd_cryptobox_fast_hash_state_t hst;
+
+ fp = len - leftover;
+ rspamd_cryptobox_fast_hash_init (&hst, h);
+
+ for (i = 0; i != fp; i += 4) {
+ u.c.c1 = s[i], u.c.c2 = s[i + 1], u.c.c3 = s[i + 2], u.c.c4 = s[i + 3];
+ u.c.c1 = lc_map[u.c.c1];
+ u.c.c2 = lc_map[u.c.c2];
+ u.c.c3 = lc_map[u.c.c3];
+ u.c.c4 = lc_map[u.c.c4];
+ rspamd_cryptobox_fast_hash_update (&hst, &u, sizeof (u));
+ }
+
+ u.pp = 0;
+ switch (leftover) {
+ case 3:
+ u.c.c3 = lc_map[(unsigned char)s[i++]];
+ case 2:
+ /* fallthrough */
+ u.c.c2 = lc_map[(unsigned char)s[i++]];
+ case 1:
+ /* fallthrough */
+ u.c.c1 = lc_map[(unsigned char)s[i]];
+ rspamd_cryptobox_fast_hash_update (&hst, &u, sizeof (u));
+ break;
+ }
+
+ return (uint32_t)rspamd_cryptobox_fast_hash_final (&hst);
+}
+
+
+static inline bool
+ucl_hash_caseless_equal (const ucl_object_t *k1, const ucl_object_t *k2)
+{
+ if (k1->keylen == k2->keylen) {
+ return rspamd_lc_cmp (k1->key, k2->key, k1->keylen) == 0;
+ }
+
+ return false;
+}
+
+KHASH_INIT (ucl_hash_caseless_node, const ucl_object_t *, struct ucl_hash_elt *, 1,
+ ucl_hash_caseless_func, ucl_hash_caseless_equal)
+
+ucl_hash_t*
+ucl_hash_create (bool ignore_case)
+{
+ ucl_hash_t *new;
+
+ new = UCL_ALLOC (sizeof (ucl_hash_t));
+ if (new != NULL) {
+ void *h;
+ new->head = NULL;
+ new->caseless = ignore_case;
+ if (ignore_case) {
+ h = (void *)kh_init (ucl_hash_caseless_node);
+ }
+ else {
+ h = (void *)kh_init (ucl_hash_node);
+ }
+ if (h == NULL) {
+ UCL_FREE (sizeof (ucl_hash_t), new);
+ return NULL;
+ }
+ new->hash = h;
+ }
+ return new;
+}
+
+void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func)
+{
+
+ if (hashlin == NULL) {
+ return;
+ }
+
+ if (func != NULL) {
+ /* Iterate over the hash first */
+ khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+ hashlin->hash;
+ khiter_t k;
+ const ucl_object_t *cur, *tmp;
+
+ for (k = kh_begin (h); k != kh_end (h); ++k) {
+ if (kh_exist (h, k)) {
+ cur = (kh_value (h, k))->obj;
+ while (cur != NULL) {
+ tmp = cur->next;
+ func (__DECONST (ucl_object_t *, cur));
+ cur = tmp;
+ }
+ }
+ }
+ }
+
+ if (hashlin->caseless) {
+ khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+ hashlin->hash;
+ kh_destroy (ucl_hash_caseless_node, h);
+ }
+ else {
+ khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+ hashlin->hash;
+ kh_destroy (ucl_hash_node, h);
+ }
+
+ struct ucl_hash_elt *cur, *tmp;
+
+ DL_FOREACH_SAFE(hashlin->head, cur, tmp) {
+ UCL_FREE(sizeof(*cur), cur);
+ }
+
+ UCL_FREE (sizeof (*hashlin), hashlin);
+}
+
+bool
+ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
+ const char *key, unsigned keylen)
+{
+ khiter_t k;
+ int ret;
+ struct ucl_hash_elt **pelt, *elt;
+
+ if (hashlin == NULL) {
+ return false;
+ }
+
+ if (hashlin->caseless) {
+ khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+ hashlin->hash;
+ k = kh_put (ucl_hash_caseless_node, h, obj, &ret);
+ if (ret > 0) {
+ elt = UCL_ALLOC(sizeof(*elt));
+ pelt = &kh_value (h, k);
+ *pelt = elt;
+ DL_APPEND(hashlin->head, elt);
+ elt->obj = obj;
+ }
+ else if (ret < 0) {
+ goto e0;
+ }
+ }
+ else {
+ khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+ hashlin->hash;
+ k = kh_put (ucl_hash_node, h, obj, &ret);
+ if (ret > 0) {
+ elt = UCL_ALLOC(sizeof(*elt));
+ pelt = &kh_value (h, k);
+ *pelt = elt;
+ DL_APPEND(hashlin->head, elt);
+ elt->obj = obj;
+ } else if (ret < 0) {
+ goto e0;
+ }
+ }
+ return true;
+ e0:
+ return false;
+}
+
+void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
+ const ucl_object_t *new)
+{
+ khiter_t k;
+ int ret;
+ struct ucl_hash_elt *elt, *nelt;
+
+ if (hashlin == NULL) {
+ return;
+ }
+
+ if (hashlin->caseless) {
+ khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+ hashlin->hash;
+ k = kh_put (ucl_hash_caseless_node, h, old, &ret);
+ if (ret == 0) {
+ elt = kh_value(h, k);
+ kh_del (ucl_hash_caseless_node, h, k);
+ k = kh_put (ucl_hash_caseless_node, h, new, &ret);
+ nelt = UCL_ALLOC(sizeof(*nelt));
+ nelt->obj = new;
+ kh_value(h, k) = nelt;
+ DL_REPLACE_ELEM(hashlin->head, elt, nelt);
+ UCL_FREE(sizeof(*elt), elt);
+ }
+ }
+ else {
+ khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+ hashlin->hash;
+ k = kh_put (ucl_hash_node, h, old, &ret);
+ if (ret == 0) {
+ elt = kh_value (h, k);
+ kh_del (ucl_hash_node, h, k);
+ k = kh_put (ucl_hash_node, h, new, &ret);
+ nelt = UCL_ALLOC(sizeof(*nelt));
+ nelt->obj = new;
+ kh_value(h, k) = nelt;
+ DL_REPLACE_ELEM(hashlin->head, elt, nelt);
+ UCL_FREE(sizeof(*elt), elt);
+ }
+ }
+}
+
+struct ucl_hash_real_iter {
+ const struct ucl_hash_elt *cur;
+};
+
+#define UHI_SETERR(ep, ern) {if (ep != NULL) *ep = (ern);}
+
+const void*
+ucl_hash_iterate2 (ucl_hash_t *hashlin, ucl_hash_iter_t *iter, int *ep)
+{
+ struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(*iter);
+ const ucl_object_t *ret = NULL;
+
+ if (hashlin == NULL) {
+ UHI_SETERR(ep, EINVAL);
+ return NULL;
+ }
+
+ if (it == NULL) {
+ it = UCL_ALLOC (sizeof (*it));
+
+ if (it == NULL) {
+ UHI_SETERR(ep, ENOMEM);
+ return NULL;
+ }
+
+ it->cur = hashlin->head;
+ }
+
+ UHI_SETERR(ep, 0);
+ if (it->cur) {
+ ret = it->cur->obj;
+ it->cur = it->cur->next;
+ }
+ else {
+ UCL_FREE (sizeof (*it), it);
+ *iter = NULL;
+ return NULL;
+ }
+
+ *iter = it;
+
+ return ret;
+}
+
+bool
+ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter)
+{
+ struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(iter);
+
+ return it->cur != NULL;
+}
+
+
+const ucl_object_t*
+ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen)
+{
+ khiter_t k;
+ const ucl_object_t *ret = NULL;
+ ucl_object_t search;
+ struct ucl_hash_elt *elt;
+
+ search.key = key;
+ search.keylen = keylen;
+
+ if (hashlin == NULL) {
+ return NULL;
+ }
+
+ if (hashlin->caseless) {
+ khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+ hashlin->hash;
+
+ k = kh_get (ucl_hash_caseless_node, h, &search);
+ if (k != kh_end (h)) {
+ elt = kh_value (h, k);
+ ret = elt->obj;
+ }
+ }
+ else {
+ khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+ hashlin->hash;
+ k = kh_get (ucl_hash_node, h, &search);
+ if (k != kh_end (h)) {
+ elt = kh_value (h, k);
+ ret = elt->obj;
+ }
+ }
+
+ return ret;
+}
+
+void
+ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
+{
+ khiter_t k;
+ struct ucl_hash_elt *elt;
+
+ if (hashlin == NULL) {
+ return;
+ }
+
+ if (hashlin->caseless) {
+ khash_t(ucl_hash_caseless_node) *h = (khash_t(ucl_hash_caseless_node) *)
+ hashlin->hash;
+
+ k = kh_get (ucl_hash_caseless_node, h, obj);
+ if (k != kh_end (h)) {
+ elt = kh_value (h, k);
+ DL_DELETE(hashlin->head, elt);
+ kh_del (ucl_hash_caseless_node, h, k);
+ UCL_FREE(sizeof(*elt), elt);
+ }
+ }
+ else {
+ khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+ hashlin->hash;
+ k = kh_get (ucl_hash_node, h, obj);
+ if (k != kh_end (h)) {
+ elt = kh_value (h, k);
+ DL_DELETE(hashlin->head, elt);
+ kh_del (ucl_hash_node, h, k);
+ UCL_FREE(sizeof(*elt), elt);
+ }
+ }
+}
+
+bool
+ucl_hash_reserve (ucl_hash_t *hashlin, size_t sz)
+{
+ if (hashlin == NULL) {
+ return false;
+ }
+
+ if (sz > kh_size((khash_t(ucl_hash_node) *)hashlin->hash)) {
+ if (hashlin->caseless) {
+ khash_t(ucl_hash_caseless_node) *h = (khash_t(
+ ucl_hash_caseless_node) *)
+ hashlin->hash;
+ kh_resize (ucl_hash_caseless_node, h, sz * 2);
+ } else {
+ khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+ hashlin->hash;
+ kh_resize (ucl_hash_node, h, sz * 2);
+ }
+ }
+
+ return true;
+}
+
+static int
+ucl_hash_cmp_icase (const void *a, const void *b)
+{
+ const struct ucl_hash_elt *oa = (const struct ucl_hash_elt *)a,
+ *ob = (const struct ucl_hash_elt *)b;
+
+ if (oa->obj->keylen == ob->obj->keylen) {
+ return rspamd_lc_cmp (oa->obj->key, ob->obj->key, oa->obj->keylen);
+ }
+
+ return ((int)(oa->obj->keylen)) - ob->obj->keylen;
+}
+
+static int
+ucl_hash_cmp_case_sens (const void *a, const void *b)
+{
+ const struct ucl_hash_elt *oa = (const struct ucl_hash_elt *)a,
+ *ob = (const struct ucl_hash_elt *)b;
+
+ if (oa->obj->keylen == ob->obj->keylen) {
+ return memcmp (oa->obj->key, ob->obj->key, oa->obj->keylen);
+ }
+
+ return ((int)(oa->obj->keylen)) - ob->obj->keylen;
+}
+
+void
+ucl_hash_sort (ucl_hash_t *hashlin, enum ucl_object_keys_sort_flags fl)
+{
+
+ if (fl & UCL_SORT_KEYS_ICASE) {
+ DL_SORT(hashlin->head, ucl_hash_cmp_icase);
+ }
+ else {
+ DL_SORT(hashlin->head, ucl_hash_cmp_case_sens);
+ }
+
+ if (fl & UCL_SORT_KEYS_RECURSIVE) {
+ struct ucl_hash_elt *elt;
+
+ DL_FOREACH(hashlin->head, elt) {
+ if (ucl_object_type (elt->obj) == UCL_OBJECT) {
+ ucl_hash_sort (elt->obj->value.ov, fl);
+ }
+ }
+ }
+} \ No newline at end of file