2 * Copyright 2012 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials
14 * provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
29 * The views and conclusions contained in the software and
30 * documentation are those of the authors and should not be
31 * interpreted as representing official policies, either expressed
32 * or implied, of GRNET S.A.
36 * originally by gtsouk@cslab.ece.ntua.gr
37 * -- kkourt@cslab.ece.ntua.gr
40 #include <xtypes/xhash.h>
42 #define UNUSED (~(xhashidx)0) /* this entry was never used */
43 #define DUMMY ((~(xhashidx)0)-1) /* this entry was used, but now its empty */
45 //#define VAL_OVERLOAD
46 //#define KEY_OVERLOAD
47 //#define NO_OVERLOAD /* use separate bitarray -- not implemented */
49 #define PERTURB_SHIFT 5
51 static inline xhashidx hash_int(xhashidx key)
56 static inline int cmp_int(xhashidx key1, xhashidx key2)
58 return (key1 == key2);
61 static inline xhashidx hash_string(xhashidx key)
63 //assume a valid NULL terminated string
65 //function to access key if in container
66 char *string = (char *) key;
67 unsigned int i, len = strlen(string);
68 xhashidx hv = string[0] << 7;
69 for (i = 1; i <= len; i++) {
70 hv = (hv * 1000003) ^ string[i];
75 // XSEGLOG("String %s (%lx). Hash value: %llu",
76 // string, string, hv);
80 static inline int cmp_string(xhashidx key1, xhashidx key2)
82 char *string1 = (char *) key1;
83 char *string2 = (char *) key2;
84 int value = !strcmp(string1, string2);
85 // XSEGLOG("String1 %s (%lx), string2: %s(%lx), r: %d",
86 // string1, (unsigned long) string1,
87 // string2, (unsigned long) string2,
93 typedef int (*xhash_cmp_fun_t)(xhashidx key1, xhashidx key2);
94 typedef xhashidx (*xhash_hash_fun_t)(xhashidx key);
96 struct types_functions {
97 // int (*cmp_fun)(xhashidx key1, xhashidx key2);
98 // xhashidx (*hash_fun)(xhashidx key);
99 xhash_cmp_fun_t cmp_fun;
100 xhash_hash_fun_t hash_fun;
103 static struct types_functions types_fun[] = {
104 { .cmp_fun = cmp_int, .hash_fun = hash_int },
105 { .cmp_fun = cmp_string, .hash_fun = hash_string }
110 set_dummy_key(xhash_t *xhash, xhashidx idx)
112 xhashidx *kvs = xhash_kvs(xhash);
117 set_dummy_val(xhash_t *xhash, xhashidx idx)
119 xhashidx *vals = xhash_vals(xhash);
124 item_dummy(xhash_t *xhash, xhashidx idx, bool vals)
128 xhashidx *kvs = xhash_kvs(xhash);
131 ret = (kvs[idx] == DUMMY);
133 #if defined(VAL_OVERLOAD)
134 pvals = xhash_vals(xhash);
135 ret = (pvals[idx] == DUMMY);
136 #elif defined(KEY_OVERLOAD)
137 ret = (kvs[idx] == DUMMY);
144 static void set_dummy_item(xhash_t *xhash, xhashidx idx, bool vals)
147 set_dummy_key(xhash, idx);
152 set_dummy_val(xhash, idx);
154 #elif defined(KEY_OVERLOAD)
155 set_dummy_key(xhash, idx);
160 set_unused_key(xhash_t *xhash, xhashidx idx)
162 xhashidx *kvs = xhash_kvs(xhash);
167 set_unused_val(xhash_t *xhash, xhashidx idx)
169 xhashidx *vals = xhash_vals(xhash);
174 val_unused(xhash_t *xhash, xhashidx idx)
176 xhashidx *vals = xhash_vals(xhash);
177 return vals[idx] == UNUSED;
181 item_unused(xhash_t *xhash, xhashidx idx, bool vals)
183 xhashidx *kvs = xhash_kvs(xhash);
185 return kvs[idx] == UNUSED;
188 #if defined(VAL_OVERLOAD)
189 return val_unused(xhash, idx);
190 #elif defined(KEY_OVERLOAD)
191 return kvs[idx] == UNUSED;
196 static inline unsigned item_valid(xhash_t *xhash, xhashidx idx, bool vals)
198 return !(item_dummy(xhash, idx, vals) || item_unused(xhash, idx, vals));
201 static void __attribute__((unused))
202 assert_key(xhashidx key)
204 assert((key != UNUSED) && (key != DUMMY));
208 assert_val(xhashidx val)
210 assert((val != UNUSED) && (val != DUMMY));
214 assert_kv(xhashidx k, xhashidx v)
216 #if defined(KEY_OVERLOAD)
218 #elif defined(VAL_OVERLOAD)
225 xhash_init__(xhash_t *xhash, xhashidx size_shift, xhashidx minsize_shift,
226 xhashidx limit, enum xhash_type type, bool vals)
228 xhashidx nr_items = 1UL << size_shift;
229 xhashidx *kvs = (xhashidx *) ((char *) xhash + sizeof(struct xhash));
232 XPTRSET(&xhash->kvs, kvs);
236 for (i=0; i < nr_items; i++)
241 for (i=0; i < nr_items; i++){
242 #if defined(VAL_OVERLOAD)
243 kvs[nr_items + i] = UNUSED;
244 #elif defined(KEY_OVERLOAD)
250 xhash->dummies = xhash->used = 0;
251 xhash->size_shift = size_shift;
252 xhash->minsize_shift = minsize_shift;
253 xhash->limit = limit;
256 ZEROSTAT(xhash->inserts);
257 ZEROSTAT(xhash->deletes);
258 ZEROSTAT(xhash->lookups);
259 ZEROSTAT(xhash->bounces);
263 get_alloc_size(xhashidx size_shift, bool vals)
265 xhashidx nr_items = 1UL << size_shift;
266 size_t keys_size = nr_items*sizeof(xhashidx);
267 size_t alloc_size = vals ? keys_size<<1 : keys_size;
268 return sizeof(struct xhash) + alloc_size;
273 xhash_new__(xhashidx size_shift, xhashidx minsize_shift, xhashidx limit,
274 enum xhash_type type, bool vals)
277 xhash = xtypes_malloc(get_alloc_size(size_shift, vals));
279 XSEGLOG("couldn't malloc\n");
283 xhash_init__(xhash, size_shift, minsize_shift, limit, type, vals);
290 xhash_resize__(struct xhash *xhash, xhashidx new_size_shift, xhashidx new_limit,
293 return xhash_new__(new_size_shift, xhash->minsize_shift, new_limit,
298 xhash_delete__(xhash_t *xhash, xhashidx key, bool vals)
300 // XSEGLOG("Deleting %lx", key);
301 xhash_cmp_fun_t cmp_fun = types_fun[xhash->type].cmp_fun;
302 xhash_hash_fun_t hash_fun = types_fun[xhash->type].hash_fun;
303 xhashidx perturb = hash_fun(key);
304 xhashidx mask = xhash_size(xhash)-1;
305 xhashidx idx = hash_fun(key) & mask;
306 xhashidx *kvs = xhash_kvs(xhash);
309 if ( item_unused(xhash, idx, vals) ){
310 return -XHASH_ENOENT;
313 if ( !item_dummy(xhash, idx, vals) && cmp_fun(kvs[idx],key)){
314 INCSTAT(xhash->deletes);
315 set_dummy_item(xhash, idx, vals);
317 //fprintf(stderr, "rm: used: %lu\n", xhash->used);
322 INCSTAT(xhash->bounces);
323 idx = ((idx<<2) + idx + 1 + perturb) & mask;
324 perturb >>= PERTURB_SHIFT;
329 xhash_grow_size_shift(xhash_t *xhash)
331 xhashidx old_size_shift = xhash->size_shift;
332 xhashidx new_size_shift;
336 //printf("used: %lu\n", u);
337 if (u/2 + u >= ((xhashidx)1 << old_size_shift)) {
338 new_size_shift = old_size_shift + 1;
340 new_size_shift = old_size_shift;
343 return new_size_shift;
347 xhash_shrink_size_shift(xhash_t *xhash)
349 xhashidx old_size_shift = xhash->size_shift;
350 xhashidx new_size_shift;
351 new_size_shift = old_size_shift - 1;
352 if (new_size_shift < xhash->minsize_shift) {
353 new_size_shift = xhash->minsize_shift;
355 return new_size_shift;
359 grow_check(xhash_t *xhash)
361 xhashidx size_shift = xhash->size_shift;
362 xhashidx u = xhash->used + xhash->dummies;
363 xhashidx size = (xhashidx)1UL<<size_shift;
364 return ((u/2 + u) >= size) ? true : false;
368 shrink_check(xhash_t *xhash)
370 xhashidx size_shift = xhash->size_shift;
371 xhashidx size = (xhashidx)1<<size_shift;
372 xhashidx u = xhash->used;
373 return (4*u < size && size_shift > xhash->minsize_shift) ? true : false;
382 xhash_get_alloc_size(xhashidx size_shift)
384 return get_alloc_size(size_shift, true);
388 xhash_new(xhashidx minsize_shift, xhashidx limit, enum xhash_type type)
390 return xhash_new__(minsize_shift, minsize_shift, limit, type, true);
393 void xhash_free(struct xhash *xhash)
398 void xhash_init(struct xhash *xhash, xhashidx minsize_shift, xhashidx limit,
399 enum xhash_type type)
401 xhash_init__(xhash, minsize_shift, minsize_shift, limit, type, true);
406 xhash_grow(struct xhash *xhash)
408 xhashidx new_size_shift = xhash_grow_size_shift(xhash);
409 return xhash_resize(xhash, new_size_shift);
413 xhash_shrink(struct xhash *xhash)
415 xhashidx new_size_shift = xhash_shrink_size_shift(xhash);
416 return xhash_resize(xhash, new_size_shift);
419 static inline xhash_t*
420 xhash_grow_check(xhash_t *xhash)
422 if (grow_check(xhash))
423 return xhash_grow(xhash);
429 #define PHASH_UPDATE(xhash, key, val, vals_flag) \
431 xhash_cmp_fun_t cmp_fun = types_fun[xhash->type].cmp_fun; \
432 xhash_hash_fun_t hash_fun = types_fun[xhash->type].hash_fun; \
433 xhashidx size = 1UL<<(xhash->size_shift); \
434 xhashidx perturb = hash_fun(key); \
435 xhashidx mask = size-1; \
436 xhashidx idx = hash_fun(key) & mask; \
437 xhashidx *kvs = xhash_kvs(xhash); \
439 INCSTAT(xhash->inserts); \
441 if ( !item_valid(xhash, idx, vals_flag) ){ \
442 PHUPD_SET__(xhash, idx, key, val); \
445 if (cmp_fun(kvs[idx], key)){ \
446 PHUPD_UPDATE__(xhash, idx, key, val); \
450 again: __attribute__((unused)) \
451 INCSTAT(xhash->bounces); \
452 idx = ((idx<<2) + idx + 1 + perturb) & mask; \
453 perturb >>= PERTURB_SHIFT; \
458 set_val(xhash_t *p, xhashidx idx, xhashidx key, xhashidx val)
460 xhashidx *kvs = xhash_kvs(p);
461 xhashidx *vals = xhash_vals(p);
464 //XSEGLOG("Seting idx %llu to key: %lx, val: %lx", idx, key, val);
467 void static inline xhash_upd_set(xhash_t *p, xhashidx idx, xhashidx key, xhashidx val)
469 xhashidx *kvs = xhash_kvs(p);
470 xhashidx *vals = xhash_vals(p);
471 if (item_dummy(p, idx, true))
476 //XSEGLOG("Seting idx %llu to key: %lx, val: %lx", idx, key, val);
480 inc_val(xhash_t *p, xhashidx idx, xhashidx val)
482 xhashidx *vals = xhash_vals(p);
486 void xhash_insert__(struct xhash *xhash, xhashidx key, xhashidx val)
488 //XSEGLOG("inserting %lx", key);
489 //fprintf(stderr, "insert: (%lu,%lu)\n", key, val);
490 #define PHUPD_UPDATE__(_p, _i, _k, _v) set_val(_p, _i, _k, _v)
491 #define PHUPD_SET__(_p, _i, _k, _v) xhash_upd_set(_p, _i, _k, _v)
492 PHASH_UPDATE(xhash, key, val, true)
493 #undef PHUPD_UPDATE__
497 int xhash_insert(struct xhash *xhash, xhashidx key, xhashidx val)
499 if (xhash->limit && xhash->used >= xhash->limit)
500 return -XHASH_ENOSPC;
501 if (grow_check(xhash))
502 return -XHASH_ERESIZE;
503 xhash_insert__(xhash, key, val);
508 void xhash_freql_update__(struct xhash *xhash, xhashidx key, xhashidx val)
510 #define PHUPD_UPDATE__(_p, _i, _k, _v) inc_val(_p, _i, _v)
511 #define PHUPD_SET__(_p, _i, _k, _v) xhash_upd_set(_p, _i, _k, _v)
512 PHASH_UPDATE(xhash, key, val, true)
513 #undef PHUPD_UPDATE__
517 int xhash_freql_update(struct xhash *xhash, xhashidx key, xhashidx val)
519 if (grow_check(xhash))
520 return -XHASH_ERESIZE;
521 xhash_freql_update__(xhash, key, val);
526 xhash_resize(xhash_t *xhash, xhashidx new_size_shift, xhashidx new_limit,
529 //XSEGLOG("Resizing xhash from %llu to %llu", xhash->size_shift, new_size_shift);
533 new = xhash_new__(new_size_shift, xhash->minsize_shift, new_limit,
536 xhash_init__(new, new_size_shift, xhash->minsize_shift, new_limit,
542 //fprintf(stderr, "resizing: (%lu,%lu,%lu)\n", xhash->size_shift, xhash->used, xhash->dummies);
543 for (i = 0; i < xhash_size(xhash); i++) {
544 if (item_valid(xhash, i, true)){
545 //fprintf(stderr, "rs: inserting (%lu,%lu)\n", item->k, item->v);
546 xhash_insert__(new, *(xhash_kvs(xhash) + i), *(xhash_vals(xhash) + i));
556 * note that his function does not modify the internal structure of the hash
557 * and thus its safe to use it for updating values during a xhash_iterate()
559 int xhash_update(struct xhash *xhash, xhashidx key, xhashidx val) {
561 //fprintf(stderr, "update: (%lu,%lu)\n", key, val);
562 #define PHUPD_UPDATE__(_p, _i, _k, _v) set_val(_p, _i, _k, _v)
563 #define PHUPD_SET__(_p, _i, _k, _v) goto again
564 PHASH_UPDATE(xhash, key, val, true)
565 #undef PHUPD_UPDATE__
572 int xhash_delete(struct xhash *xhash, xhashidx key)
574 if (shrink_check(xhash))
575 return -XHASH_ERESIZE;
576 return xhash_delete__(xhash, key, true);
579 int xhash_lookup__(xhash_t *xhash, xhashidx key, xhashidx *idx_ret, bool vals)
581 //XSEGLOG("looking up %lx", key);
582 xhash_cmp_fun_t cmp_fun = types_fun[xhash->type].cmp_fun;
583 xhash_hash_fun_t hash_fun = types_fun[xhash->type].hash_fun;
584 xhashidx size_shift = xhash->size_shift;
585 xhashidx size = (1UL)<<size_shift;
586 xhashidx perturb = hash_fun(key);
587 xhashidx mask = size-1;
588 xhashidx idx = hash_fun(key) & mask;
589 xhashidx *kvs = xhash_kvs(xhash);
591 INCSTAT(xhash->lookups);
593 //XSEGLOG("size %llu, perturb %llu idx %llu mask %llu",
594 // size, perturb, idx, mask);
595 if ( item_unused(xhash, idx, vals) )
596 return -XHASH_EEXIST;
598 if ( !item_dummy(xhash, idx, vals) && cmp_fun(kvs[idx],key)){
603 INCSTAT(xhash->bounces);
604 idx = ((idx<<2) + idx + 1 + perturb) & mask;
605 perturb >>= PERTURB_SHIFT;
609 int xhash_lookup(struct xhash *xhash, xhashidx key, xhashidx *val)
612 int ret = xhash_lookup__(xhash, key, &idx, true);
614 xhashidx *values = xhash_vals(xhash);
620 //FIXME iteration broken
622 xhash_iter_init(xhash_t *xhash, xhash_iter_t *pi)
624 pi->cnt = pi->loc = 0;
628 xhash_iterate__(xhash_t *xhash, bool vals,
629 xhash_iter_t *pi, xhashidx *key_ret, xhashidx *idx_ret)
631 xhashidx idx = pi->loc;
632 xhashidx size = (xhashidx)1<<xhash->size_shift;
633 xhashidx *kvs = xhash_kvs(xhash);
634 INCSTAT(xhash->lookups);
636 if (xhash->used == pi->cnt || idx >= size)
639 if (item_valid(xhash, idx, vals)){
651 int xhash_iterate(xhash_t *xhash, xhash_iter_t *pi, xhashidx *key, xhashidx *val)
654 int ret = xhash_iterate__(xhash, true, pi, key, &idx);
656 xhashidx *vals = xhash_vals(xhash);
662 void xhash_print(xhash_t *xhash)
668 xhash_iter_init(xhash, &pi);
669 XSEGLOG("PHASH(%p):\n", xhash);
671 ret = xhash_iterate(xhash, &pi, &key, &val);
675 XSEGLOG(" 0x%017lx : 0x%017lx\n", key, val);
685 " insert : I <key> <val> \n"
686 " update : U <key> <val> (->v += val if exists) \n"
688 " delete : D <key> \n"
693 int main(int argc, char **argv)
696 char *s, buf[BUFLEN];
700 ph = xhash_new(2, INTEGER);
703 s = fgets(buf, BUFLEN-1, stdin);
710 ret = sscanf(s+1, "%lu %lu", &key, &val);
712 xhash_insert(ph, key, val);
717 ret = sscanf(s+1, "%lu %lu", &key, &val);
719 xhash_freql_update(ph, key, val);
724 ret = sscanf(s+1, "%lu", &key);
726 ret = xhash_lookup(ph, key, &val);
728 printf("%lu\n", val);
736 ret = sscanf(s+1, "%lu", &key);
738 xhash_delete(ph, key);
743 printf("%lu\n", xhash_elements(ph));
771 pset_new(xhashidx minsize_shift)
774 pset = malloc(sizeof(pset_t));
779 xhash_init__(&pset->ph_, minsize_shift, false);
784 pset_init(pset_t *pset, xhashidx minsize_shift)
786 xhash_init__(&pset->ph_, minsize_shift, false);
790 pset_free(pset_t *pset)
792 xhash_tfree(&pset->ph_);
797 pset_tfree(pset_t *pset)
799 xhash_tfree(&pset->ph_);
803 pset_resize(pset_t *pset, xhashidx new_size_shift)
806 xhash_cp(&(old.ph_), &pset->ph_);
808 xhash_resize__(&pset->ph_, new_size_shift, false);
809 for (xhashidx i = 0; i < pset_size(&old); i++) {
810 if (item_valid(&(old.ph_), i, false)){
811 //fprintf(stderr, "rs: inserting (%lu,%lu)\n", item->k, item->v);
812 pset_insert(pset, old.ph_.kvs[i]);
819 pset_grow(pset_t *pset)
821 xhashidx new_size_shift = grow_size_shift(&pset->ph_);
822 pset_resize(pset, new_size_shift);
826 pset_grow_check(pset_t *pset)
828 if (grow_check(&pset->ph_))
832 void static inline pset_upd_set(xhash_t *p, xhashidx idx, xhashidx key)
834 if (item_dummy(p, idx, false))
840 void pset_insert(pset_t *pset, xhashidx key)
842 xhash_t *ph = &pset->ph_;
844 pset_grow_check(pset);
845 #define PHUPD_UPDATE__(_p, _i, _k, _v) do { } while (0)
846 #define PHUPD_SET__(_p, _i, _k, _v) pset_upd_set(_p, _i, _k)
847 PHASH_UPDATE(ph, key, 0xdeadbabe, false)
848 #undef PHUPD_UPDATE__
853 pset_shrink(pset_t *pset)
855 xhashidx new_size_shift = shrink_size_shift(&pset->ph_);
856 pset_resize(pset, new_size_shift);
859 int pset_delete(pset_t *pset, xhashidx key)
861 if (pset->ph_.used == 0)
865 xhashidx size_shift = pset->ph_.size_shift;
866 xhashidx size = (xhashidx)1<<size_shift;
867 xhashidx u = pset->ph_.used;
870 return xhash_delete__(&pset->ph_, key, false);
873 bool pset_lookup(pset_t *pset, xhashidx key)
876 return !!xhash_lookup__(&pset->ph_, key, &idx, false);
879 int pset_iterate(pset_t *pset, xhash_iter_t *pi, xhashidx *key)
882 int ret = xhash_iterate__(&pset->ph_, false, pi, key, &idx);
886 void pset_print(pset_t *pset)
892 pset_iter_init(pset, &pi);
893 printf("PSET(%p):\n", pset);
895 ret = pset_iterate(pset, &pi, &key);
899 printf(" 0x%017lx\n", key);
904 #if defined(PSET_MAIN)
909 " insert : I <key> <val> \n"
911 " delete : D <key> \n"
916 int main(int argc, char **argv)
919 char *s, buf[BUFLEN];
926 s = fgets(buf, BUFLEN-1, stdin);
933 ret = sscanf(s+1, "%lu", &key);
935 pset_insert(ps, key);
940 ret = sscanf(s+1, "%lu", &key);
942 ret = pset_lookup(ps, key);
943 printf("%lu -> %s\n", key, ret ? "true" : "false");
948 ret = sscanf(s+1, "%lu", &key);
950 pset_delete(ps, key);
955 printf("%lu\n", pset_elements(ps));
981 #include <linux/module.h>
982 #include <xtypes/xhash_exports.h>
985 // vim:expandtab:tabstop=8:shiftwidth=4:softtabstop=4