4 /* this is a hashmap using a fixed number of buckets,
5 which in turn are of type bmap. this combines the advantages of both
7 limitations: max no of buckets and items per bucket is 2^32-1 each.
8 speed is almost identical to khash with small number of items per
9 bucket. with 100.000 items it's about 15% slower.
11 unlike bmap, _find(), insert(), etc return an iterator instead of indices.
12 the iterator needs to be used for e.g. _getkey(), etc.
19 #include <unistd.h> /* ssize_t */
22 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
25 typedef uint64_t hbmap_iter
;
27 #define hbmap_impl(NAME, KEYTYPE, VALTYPE, NUMBUCKETS) \
29 unsigned (*hash_func)(const KEYTYPE); \
33 bmap_impl(, KEYTYPE, VALTYPE) buckets[NUMBUCKETS]; \
36 #define hbmap(KEYTYPE, VALTYPE, NUMBUCKETS) \
37 hbmap_impl(, KEYTYPE, VALTYPE, NUMBUCKETS)
39 #define hbmap_decl(ID, KEYTYPE, VALTYPE, NUMBUCKETS) \
40 hbmap_impl(hbmap_ ## ID, KEYTYPE, VALTYPE, NUMBUCKETS)
42 #define hbmap_proto(NUMBUCKETS) \
43 hbmap_impl(, void*, void*, NUMBUCKETS)
45 #define hbmap_getbucketcount(X) ARRAY_SIZE((X)->buckets)
47 #define hbmap_struct_size_impl(NUMBUCKETS) ( \
48 offsetof(hbmap_proto(1), buckets) + \
49 NUMBUCKETS * sizeof(bmap_proto) \
52 #define hbmap_init_impl(X, COMPAREFUNC, HASHFUNC, NUMBUCKETS) do{\
53 memset(X, 0, hbmap_struct_size_impl(NUMBUCKETS)); \
54 ((hbmap_proto(1)*)(void*)(X))->hash_func = (void*)HASHFUNC; \
55 bmap_proto *p = (void*)(&((hbmap_proto(1)*)(void*)(X))->buckets[0]); \
56 size_t i; for(i=0; i<NUMBUCKETS; ++i) \
57 p[i].compare = COMPAREFUNC; \
61 /* bmap_compare_func is a typical compare function used for qsort, etc such as strcmp
64 #define hbmap_init(X, COMPAREFUNC, HASHFUNC) \
65 hbmap_init_impl(X, COMPAREFUNC, HASHFUNC, hbmap_getbucketcount(X))
67 static inline void* hbmap_new(bmap_compare_func fn
, void* hash_func
, size_t numbuckets
) {
68 void *nyu
= malloc(hbmap_struct_size_impl(numbuckets
));
69 if(nyu
) hbmap_init_impl(nyu
, fn
, hash_func
, numbuckets
);
75 0: free only internal mem
80 #define hbmap_fini(X, FREEFLAGS) do { \
81 size_t i; for(i=0; i < hbmap_getbucketcount(X); ++i) \
82 { bmap_fini(&(X)->buckets[i], FREEFLAGS); } \
85 /* internal stuff needed for iterator impl */
87 #define hbmap_iter_bucket(I) ( (I) >> 32)
88 #define hbmap_iter_index(I) ( (I) & 0xffffffff )
89 #define hbmap_iter_makebucket(I) ( (I) << 32)
91 #define hbmap_iter_bucket_valid(X, ITER, NUMBUCKETS) ( \
92 hbmap_iter_bucket(ITER) < NUMBUCKETS )
93 #define hbmap_iter_index_valid(X, ITER) ( \
94 hbmap_iter_index(ITER) < bmap_getsize(& \
96 (void*)(&((hbmap_proto(1)*)(void*)(X))->buckets[0]) \
97 ))[hbmap_iter_bucket(ITER)]) \
100 #define hbmap_iter_valid(X, ITER) (\
101 hbmap_iter_bucket_valid(X, ITER, hbmap_getbucketcount(X)) && \
102 hbmap_iter_index_valid(X, ITER))
104 #define hbmap_next_step(X, ITER) ( \
105 hbmap_iter_index_valid(X, (ITER)+1) ? (ITER)+1 : \
106 hbmap_iter_makebucket(hbmap_iter_bucket(ITER)+1) \
109 static hbmap_iter
hbmap_next_valid_impl(void *h
, hbmap_iter iter
, size_t nbucks
) {
110 do iter
= hbmap_next_step(h
, iter
);
111 while(hbmap_iter_bucket_valid(h
, iter
, nbucks
) && !hbmap_iter_index_valid(h
, iter
));
115 /* public API continues */
117 /* note that if you use foreach to delete items, the iterator isn't aware of that
118 and will skip over the next item. you need to use something like:
119 hbmap_foreach(map, i) { while(hbmap_iter_index_valid(map, i)) hbmap_delete(map, i); }
121 #define hbmap_foreach(X, ITER_VAR) \
122 for(ITER_VAR = hbmap_iter_valid(X, (hbmap_iter)0) ? 0 \
123 : hbmap_next_valid_impl(X, 0, hbmap_getbucketcount(X)); \
124 hbmap_iter_valid(X, ITER_VAR); \
125 ITER_VAR = hbmap_next_valid_impl(X, ITER_VAR, hbmap_getbucketcount(X)))
127 #define hbmap_getkey(X, ITER) \
128 bmap_getkey(&(X)->buckets[hbmap_iter_bucket(ITER)], hbmap_iter_index(ITER))
130 #define hbmap_getval(X, ITER) \
131 bmap_getval(&(X)->buckets[hbmap_iter_bucket(ITER)], hbmap_iter_index(ITER))
133 #define hbmap_setvalue(X, VAL, ITER) \
134 bmap_setvalue(&(X)->buckets[hbmap_iter_bucket(ITER)], VAL, hbmap_iter_index(ITER))
136 #define hbmap_getkeysize(X) (bmap_getkeysize(&(X)->buckets[0]))
137 #define hbmap_getvalsize(X) (bmap_getvalsize(&(X)->buckets[0]))
139 #define hbmap_buckindex_impl(X, KEY) \
140 ( (X)->hash_func(KEY) % hbmap_getbucketcount(X) )
142 #define hbmap_find(X, KEY) ( \
143 ( (X)->tmp.it = hbmap_iter_makebucket(hbmap_buckindex_impl(X, KEY) ) ), \
144 ((X)->tmp.it |= (int64_t) bmap_find(&(X)->buckets[ hbmap_iter_bucket((X)->tmp.it) ], KEY)), \
147 #define hbmap_contains(X, KEY) (hbmap_find(X, KEY) != (hbmap_iter)-1)
149 /* unlike hbmap_getkey/val with index, this returns a pointer-to-item, or NULL */
150 #define hbmap_get(X, KEY) ( \
151 ( hbmap_find(X, KEY) == (hbmap_iter) -1 ) ? 0 : &hbmap_getval(X, (X)->tmp.it) \
154 /* same as hbmap_insert, but inserts blindly without checking for existing items.
155 this is faster and can be used when it's impossible that duplicate
157 #define hbmap_insert_nocheck(X, KEY, VAL) ( \
158 ( (X)->tmp.it = hbmap_iter_makebucket(hbmap_buckindex_impl(X, KEY) ) ), \
159 ((X)->tmp.it |= (int64_t) bmap_insert_nocheck(&(X)->buckets[hbmap_iter_bucket((X)->tmp.it)], KEY, VAL)), \
162 /* insert item into mapping, overwriting existing items with the same key */
163 /* return index of new item, or -1. overwrites existing items. */
164 #define hbmap_insert(X, KEY, VAL) ( \
165 ( hbmap_find(X, KEY) == (hbmap_iter) -1 ) ? hbmap_insert_nocheck(X, KEY, VAL) : \
166 ( hbmap_setvalue(X, VAL, (X)->tmp.it), (X)->tmp.it ) \
169 #define hbmap_delete(X, ITER) ( \
170 bmap_delete(&(X)->buckets[hbmap_iter_bucket(ITER)], hbmap_iter_index(ITER)), 1)