rspamd/contrib/libucl/ucl_hash.c

498 строки
11 KiB
C
Исходник Обычный вид История

2015-02-21 19:11:12 +01:00
/* 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"
2015-02-21 19:11:12 +01:00
#include "cryptobox.h"
#include "libutil/str_util.h"
#include "ucl.h"
2015-04-28 15:39:25 +02:00
#include <time.h>
2015-05-17 17:10:29 +02:00
#include <limits.h>
2015-04-28 15:39:25 +02:00
2015-02-21 19:11:12 +01:00
struct ucl_hash_elt {
const ucl_object_t *obj;
struct ucl_hash_elt *prev, *next;
2015-02-21 19:11:12 +01:00
};
struct ucl_hash_struct {
void *hash;
struct ucl_hash_elt *head;
2015-02-21 19:11:12 +01:00
bool caseless;
};
2015-04-28 15:39:25 +02:00
static uint64_t
ucl_hash_seed (void)
{
static uint64_t seed;
if (seed == 0) {
#ifdef UCL_RANDOM_FUNCTION
seed = UCL_RANDOM_FUNCTION;
#else
2015-04-28 15:39:25 +02:00
/* Not very random but can be useful for our purposes */
seed = time (NULL);
#endif
2015-04-28 15:39:25 +02:00
}
return seed;
}
extern const guchar lc_map[256];
2015-05-17 17:10:29 +02:00
static inline uint32_t
ucl_hash_func (const ucl_object_t *o)
{
2019-08-12 19:35:59 +02:00
return (uint32_t)rspamd_cryptobox_fast_hash (o->key, o->keylen, 0xb9a1ef83c4561c95ULL);
2015-05-17 17:10:29 +02:00
}
2015-02-21 19:11:12 +01:00
static inline int
ucl_hash_equal (const ucl_object_t *k1, const ucl_object_t *k2)
{
if (k1->keylen == k2->keylen) {
2016-02-18 12:42:50 +01:00
return memcmp (k1->key, k2->key, k1->keylen) == 0;
2015-02-21 19:11:12 +01:00
}
return 0;
}
KHASH_INIT (ucl_hash_node, const ucl_object_t *, struct ucl_hash_elt *, 1,
2015-02-21 19:11:12 +01:00
ucl_hash_func, ucl_hash_equal)
static inline uint32_t
ucl_hash_caseless_func (const ucl_object_t *o)
{
2015-04-28 15:39:25 +02:00
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;
2019-03-06 15:39:34 +01:00
uint64_t h = 0xe5ae6ab1ef9f3b54ULL;
rspamd_cryptobox_fast_hash_state_t hst;
2015-04-28 15:39:25 +02:00
fp = len - leftover;
2019-03-06 15:39:34 +01:00
rspamd_cryptobox_fast_hash_init (&hst, h);
2015-04-28 15:39:25 +02:00
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];
2019-03-06 15:39:34 +01:00
rspamd_cryptobox_fast_hash_update (&hst, &u, sizeof (u));
2015-02-21 19:11:12 +01:00
}
2015-04-28 15:39:25 +02:00
u.pp = 0;
switch (leftover) {
case 3:
u.c.c3 = lc_map[(unsigned char)s[i++]];
case 2:
2019-03-06 15:39:34 +01:00
/* fallthrough */
2015-04-28 15:39:25 +02:00
u.c.c2 = lc_map[(unsigned char)s[i++]];
case 1:
2019-03-06 15:39:34 +01:00
/* fallthrough */
2015-04-28 15:39:25 +02:00
u.c.c1 = lc_map[(unsigned char)s[i]];
2019-03-06 15:39:34 +01:00
rspamd_cryptobox_fast_hash_update (&hst, &u, sizeof (u));
2015-04-28 15:39:25 +02:00
break;
2015-02-21 19:11:12 +01:00
}
2019-08-12 19:35:59 +02:00
return (uint32_t)rspamd_cryptobox_fast_hash_final (&hst);
2015-02-21 19:11:12 +01:00
}
2015-05-17 17:10:29 +02:00
2015-02-21 19:11:12 +01:00
2019-03-06 15:39:34 +01:00
static inline bool
2015-02-21 19:11:12 +01:00
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;
2015-02-21 19:11:12 +01:00
}
2019-03-06 15:39:34 +01:00
return false;
2015-02-21 19:11:12 +01:00
}
KHASH_INIT (ucl_hash_caseless_node, const ucl_object_t *, struct ucl_hash_elt *, 1,
2015-02-21 19:11:12 +01:00
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) {
2019-04-25 13:13:09 +02:00
void *h;
new->head = NULL;
2015-02-21 19:11:12 +01:00
new->caseless = ignore_case;
if (ignore_case) {
2019-04-25 13:13:09 +02:00
h = (void *)kh_init (ucl_hash_caseless_node);
2015-02-21 19:11:12 +01:00
}
else {
2019-04-25 13:13:09 +02:00
h = (void *)kh_init (ucl_hash_node);
2015-02-21 19:11:12 +01:00
}
2019-04-25 13:13:09 +02:00
if (h == NULL) {
UCL_FREE (sizeof (ucl_hash_t), new);
return NULL;
}
new->hash = h;
2015-02-21 19:11:12 +01:00
}
return new;
}
2016-02-08 19:26:58 +01:00
void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func)
2015-02-21 19:11:12 +01:00
{
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;
2015-02-21 19:11:12 +01:00
for (k = kh_begin (h); k != kh_end (h); ++k) {
if (kh_exist (h, k)) {
cur = (kh_value (h, k))->obj;
2015-02-21 19:11:12 +01:00
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) *)
2019-04-25 13:13:09 +02:00
hashlin->hash;
2015-02-21 19:11:12 +01:00
kh_destroy (ucl_hash_caseless_node, h);
}
else {
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
2019-04-25 13:13:09 +02:00
hashlin->hash;
2015-02-21 19:11:12 +01:00
kh_destroy (ucl_hash_node, h);
}
struct ucl_hash_elt *cur, *tmp;
DL_FOREACH_SAFE(hashlin->head, cur, tmp) {
UCL_FREE(sizeof(*cur), cur);
}
2015-02-21 19:11:12 +01:00
UCL_FREE (sizeof (*hashlin), hashlin);
}
2019-04-25 13:13:09 +02:00
bool
2015-02-21 19:11:12 +01:00
ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
2019-04-25 13:13:09 +02:00
const char *key, unsigned keylen)
2015-02-21 19:11:12 +01:00
{
khiter_t k;
int ret;
struct ucl_hash_elt **pelt, *elt;
2015-02-21 19:11:12 +01:00
if (hashlin == NULL) {
2019-04-25 13:13:09 +02:00
return false;
2015-02-21 19:11:12 +01:00
}
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);
2015-02-21 19:11:12 +01:00
elt->obj = obj;
}
else if (ret < 0) {
goto e0;
}
2015-02-21 19:11:12 +01:00
}
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);
2015-02-21 19:11:12 +01:00
elt->obj = obj;
2019-04-25 13:13:09 +02:00
} else if (ret < 0) {
goto e0;
2015-02-21 19:11:12 +01:00
}
}
2019-04-25 13:13:09 +02:00
return true;
e0:
return false;
2015-02-21 19:11:12 +01:00
}
void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
2019-04-25 13:13:09 +02:00
const ucl_object_t *new)
2015-02-21 19:11:12 +01:00
{
khiter_t k;
int ret;
struct ucl_hash_elt *elt, *nelt;
2015-02-21 19:11:12 +01:00
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);
2015-02-21 19:11:12 +01:00
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);
2015-02-21 19:11:12 +01:00
}
}
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);
2015-02-21 19:11:12 +01:00
}
}
}
struct ucl_hash_real_iter {
const struct ucl_hash_elt *cur;
2015-02-21 19:11:12 +01:00
};
2019-04-25 13:13:09 +02:00
#define UHI_SETERR(ep, ern) {if (ep != NULL) *ep = (ern);}
2015-02-21 19:11:12 +01:00
const void*
2019-04-25 13:13:09 +02:00
ucl_hash_iterate2 (ucl_hash_t *hashlin, ucl_hash_iter_t *iter, int *ep)
2015-02-21 19:11:12 +01:00
{
struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(*iter);
const ucl_object_t *ret = NULL;
if (hashlin == NULL) {
2019-04-25 13:13:09 +02:00
UHI_SETERR(ep, EINVAL);
2015-02-21 19:11:12 +01:00
return NULL;
}
if (it == NULL) {
it = UCL_ALLOC (sizeof (*it));
2015-07-28 17:57:15 +02:00
if (it == NULL) {
2019-04-25 13:13:09 +02:00
UHI_SETERR(ep, ENOMEM);
2015-07-28 17:57:15 +02:00
return NULL;
}
it->cur = hashlin->head;
2015-02-21 19:11:12 +01:00
}
2019-04-25 13:13:09 +02:00
UHI_SETERR(ep, 0);
if (it->cur) {
ret = it->cur->obj;
it->cur = it->cur->next;
2015-02-21 19:11:12 +01:00
}
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;
2015-02-21 19:11:12 +01:00
}
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) *)
2019-04-25 13:13:09 +02:00
hashlin->hash;
2015-02-21 19:11:12 +01:00
k = kh_get (ucl_hash_caseless_node, h, &search);
if (k != kh_end (h)) {
elt = kh_value (h, k);
2015-02-21 19:11:12 +01:00
ret = elt->obj;
}
}
else {
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
2019-04-25 13:13:09 +02:00
hashlin->hash;
2015-02-21 19:11:12 +01:00
k = kh_get (ucl_hash_node, h, &search);
if (k != kh_end (h)) {
elt = kh_value (h, k);
2015-02-21 19:11:12 +01:00
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) *)
2019-04-25 13:13:09 +02:00
hashlin->hash;
2015-02-21 19:11:12 +01:00
k = kh_get (ucl_hash_caseless_node, h, obj);
if (k != kh_end (h)) {
elt = kh_value (h, k);
DL_DELETE(hashlin->head, elt);
2015-02-21 19:11:12 +01:00
kh_del (ucl_hash_caseless_node, h, k);
UCL_FREE(sizeof(*elt), elt);
2015-02-21 19:11:12 +01:00
}
}
else {
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
2019-04-25 13:13:09 +02:00
hashlin->hash;
2015-02-21 19:11:12 +01:00
k = kh_get (ucl_hash_node, h, obj);
if (k != kh_end (h)) {
elt = kh_value (h, k);
DL_DELETE(hashlin->head, elt);
2015-02-21 19:11:12 +01:00
kh_del (ucl_hash_node, h, k);
UCL_FREE(sizeof(*elt), elt);
2015-02-21 19:11:12 +01:00
}
}
}
2019-04-25 13:13:09 +02:00
bool
ucl_hash_reserve (ucl_hash_t *hashlin, size_t sz)
{
if (hashlin == NULL) {
2019-04-25 13:13:09 +02:00
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);
}
}
2019-04-25 13:13:09 +02:00
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);
}
}
}
}