6 #include <cstdlib> // for malloc() etc
7 #include <cstring> // for memset()
9 #include <stdint.h> // for uint32_t
13 #ifndef kroundup32 // FIXME: doesn't work for 64-bit integers
14 #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
17 #define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
18 #define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
19 #define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
20 #define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
21 #define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
22 #define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
23 #define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
24 #define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
25 #define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
27 #define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
29 template<class T
, class Hash
, class Eq
= std::equal_to
<T
>, typename khint_t
= uint32_t>
31 khint_t n_buckets
, count
, n_occupied
, upper_bound
;
35 KHash() : n_buckets(0), count(0), n_occupied(0), upper_bound(0), flags(NULL
), keys(NULL
) {};
36 ~KHash() { std::free(flags
); std::free(keys
); };
37 khint_t
capacity(void) const { return n_buckets
; };
38 khint_t
size(void) const { return count
; };
39 khint_t
begin(void) const { return 0; };
40 khint_t
end(void) const { return n_buckets
; };
42 void exist(khint_t x
) const { return !__ac_iseither(flags
, x
); };
43 T
&at(khint_t x
) { return keys
[x
]; };
45 khint_t
get(const T
&key
) const {
47 khint_t k
, i
, last
, mask
, step
= 0;
49 k
= Hash()(key
); i
= k
& mask
;
51 while (!__ac_isempty(flags
, i
) && (__ac_isdel(flags
, i
) || !Eq()(keys
[i
], key
))) {
52 i
= (i
+ (++step
)) & mask
;
53 if (i
== last
) return n_buckets
;
55 return __ac_iseither(flags
, i
)? n_buckets
: i
;
59 int resize(khint_t new_n_buckets
) {
60 uint32_t *new_flags
= 0;
63 kroundup32(new_n_buckets
);
64 if (new_n_buckets
< 4) new_n_buckets
= 4;
65 if (count
>= (new_n_buckets
>>1) + (new_n_buckets
>>2)) j
= 0; /* requested count is too small */
66 else { /* hash table count to be changed (shrink or expand); rehash */
67 new_flags
= (uint32_t*)std::malloc(__ac_fsize(new_n_buckets
) * sizeof(uint32_t));
68 if (!new_flags
) return -1;
69 ::memset(new_flags
, 0xaa, __ac_fsize(new_n_buckets
) * sizeof(uint32_t));
70 if (n_buckets
< new_n_buckets
) { /* expand */
71 T
*new_keys
= (T
*)std::realloc((void *)keys
, new_n_buckets
* sizeof(T
));
72 if (!new_keys
) { std::free(new_flags
); return -1; }
74 } /* otherwise shrink */
77 if (j
) { /* rehashing is needed */
78 for (j
= 0; j
!= n_buckets
; ++j
) {
79 if (__ac_iseither(flags
, j
) == 0) {
82 new_mask
= new_n_buckets
- 1;
83 __ac_set_isdel_true(flags
, j
);
84 while (1) { /* kick-out process; sort of like in Cuckoo hashing */
85 khint_t k
, i
, step
= 0;
88 while (!__ac_isempty(new_flags
, i
)) i
= (i
+ (++step
)) & new_mask
;
89 __ac_set_isempty_false(new_flags
, i
);
90 if (i
< n_buckets
&& __ac_iseither(flags
, i
) == 0) { /* kick out the existing element */
91 { T tmp
= keys
[i
]; keys
[i
] = key
; key
= tmp
; }
92 __ac_set_isdel_true(flags
, i
); /* mark it as deleted in the old hash table */
93 } else { /* write the element and jump out of the loop */
100 if (n_buckets
> new_n_buckets
) /* shrink the hash table */
101 keys
= (T
*)std::realloc((void *)keys
, new_n_buckets
* sizeof(T
));
102 std::free(flags
); /* free the working space */
104 n_buckets
= new_n_buckets
;
106 upper_bound
= (n_buckets
>>1) + (n_buckets
>>2);
111 khint_t
put(const T
&key
, int *ret
) {
113 if (n_occupied
>= upper_bound
) { /* update the hash table */
114 if (n_buckets
> (count
<<1)) {
115 if (resize(n_buckets
- 1) < 0) { /* clear "deleted" elements */
116 *ret
= -1; return n_buckets
;
118 } else if (resize(n_buckets
+ 1) < 0) { /* expand the hash table */
119 *ret
= -1; return n_buckets
;
121 } /* TODO: to implement automatically shrinking; resize() already support shrinking */
123 khint_t k
, i
, site
, last
, mask
= n_buckets
- 1, step
= 0;
124 x
= site
= n_buckets
; k
= Hash()(key
); i
= k
& mask
;
125 if (__ac_isempty(flags
, i
)) x
= i
; /* for speed up */
128 while (!__ac_isempty(flags
, i
) && (__ac_isdel(flags
, i
) || !Eq()(keys
[i
], key
))) {
129 if (__ac_isdel(flags
, i
)) site
= i
;
130 i
= (i
+ (++step
)) & mask
;
131 if (i
== last
) { x
= site
; break; }
133 if (x
== n_buckets
) {
134 if (__ac_isempty(flags
, i
) && site
!= n_buckets
) x
= site
;
139 if (__ac_isempty(flags
, x
)) { /* not present at all */
141 __ac_set_isboth_false(flags
, x
);
142 ++count
; ++n_occupied
;
144 } else if (__ac_isdel(flags
, x
)) { /* deleted */
146 __ac_set_isboth_false(flags
, x
);
149 } else *ret
= 0; /* Don't touch keys[x] if present and not deleted */
153 void del(khint_t x
) {
154 if (x
!= n_buckets
&& !__ac_iseither(flags
, x
)) {
155 __ac_set_isdel_true(flags
, x
);
161 } // end of namespace klib