2 Copyright 2011-2014 David Robillard <http://drobilla.net>
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 Primes, each slightly less than twice its predecessor, and as far away
26 from powers of two as possible.
28 static const unsigned sizes
[] = {
29 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317,
30 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843,
31 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741, 0
34 typedef struct ZixHashEntry
{
35 struct ZixHashEntry
* next
; ///< Next entry in bucket
36 uint32_t hash
; ///< Non-modulo hash value
37 // Value follows here (access with zix_hash_value)
41 ZixHashFunc hash_func
;
42 ZixEqualFunc equal_func
;
43 ZixHashEntry
** buckets
;
44 const unsigned* n_buckets
;
50 zix_hash_value(ZixHashEntry
* entry
)
56 zix_hash_new(ZixHashFunc hash_func
,
57 ZixEqualFunc equal_func
,
60 ZixHash
* hash
= (ZixHash
*)malloc(sizeof(ZixHash
));
62 hash
->hash_func
= hash_func
;
63 hash
->equal_func
= equal_func
;
64 hash
->n_buckets
= &sizes
[0];
65 hash
->value_size
= value_size
;
67 if (!(hash
->buckets
= (ZixHashEntry
**)calloc(*hash
->n_buckets
,
68 sizeof(ZixHashEntry
*)))) {
77 zix_hash_free(ZixHash
* hash
)
79 for (unsigned b
= 0; b
< *hash
->n_buckets
; ++b
) {
80 ZixHashEntry
* bucket
= hash
->buckets
[b
];
81 for (ZixHashEntry
* e
= bucket
; e
;) {
82 ZixHashEntry
* next
= e
->next
;
93 zix_hash_size(const ZixHash
* hash
)
99 insert_entry(ZixHashEntry
** bucket
, ZixHashEntry
* entry
)
101 entry
->next
= *bucket
;
105 static inline ZixStatus
106 rehash(ZixHash
* hash
, unsigned new_n_buckets
)
108 ZixHashEntry
** new_buckets
= (ZixHashEntry
**)calloc(
109 new_n_buckets
, sizeof(ZixHashEntry
*));
111 return ZIX_STATUS_NO_MEM
;
114 const unsigned old_n_buckets
= *hash
->n_buckets
;
115 for (unsigned b
= 0; b
< old_n_buckets
; ++b
) {
116 for (ZixHashEntry
* e
= hash
->buckets
[b
]; e
;) {
117 ZixHashEntry
* const next
= e
->next
;
118 const unsigned h
= e
->hash
% new_n_buckets
;
119 insert_entry(&new_buckets
[h
], e
);
125 hash
->buckets
= new_buckets
;
127 return ZIX_STATUS_SUCCESS
;
130 static inline ZixHashEntry
*
131 find_entry(const ZixHash
* hash
,
134 const unsigned h_nomod
)
136 for (ZixHashEntry
* e
= hash
->buckets
[h
]; e
; e
= e
->next
) {
137 if (e
->hash
== h_nomod
&& hash
->equal_func(zix_hash_value(e
), key
)) {
145 zix_hash_find(const ZixHash
* hash
, const void* value
)
147 const unsigned h_nomod
= hash
->hash_func(value
);
148 const unsigned h
= h_nomod
% *hash
->n_buckets
;
149 ZixHashEntry
* const entry
= find_entry(hash
, value
, h
, h_nomod
);
150 return entry
? zix_hash_value(entry
) : 0;
154 zix_hash_insert(ZixHash
* hash
, const void* value
, const void** inserted
)
156 unsigned h_nomod
= hash
->hash_func(value
);
157 unsigned h
= h_nomod
% *hash
->n_buckets
;
159 ZixHashEntry
* elem
= find_entry(hash
, value
, h
, h_nomod
);
161 assert(elem
->hash
== h_nomod
);
163 *inserted
= zix_hash_value(elem
);
165 return ZIX_STATUS_EXISTS
;
168 elem
= (ZixHashEntry
*)malloc(sizeof(ZixHashEntry
) + hash
->value_size
);
170 return ZIX_STATUS_NO_MEM
;
173 elem
->hash
= h_nomod
;
174 memcpy(elem
+ 1, value
, hash
->value_size
);
176 const unsigned next_n_buckets
= *(hash
->n_buckets
+ 1);
177 if (next_n_buckets
!= 0 && (hash
->count
+ 1) >= next_n_buckets
) {
178 if (!rehash(hash
, next_n_buckets
)) {
179 h
= h_nomod
% *(++hash
->n_buckets
);
183 insert_entry(&hash
->buckets
[h
], elem
);
186 *inserted
= zix_hash_value(elem
);
188 return ZIX_STATUS_SUCCESS
;
192 zix_hash_remove(ZixHash
* hash
, const void* value
)
194 const unsigned h_nomod
= hash
->hash_func(value
);
195 const unsigned h
= h_nomod
% *hash
->n_buckets
;
197 ZixHashEntry
** next_ptr
= &hash
->buckets
[h
];
198 for (ZixHashEntry
* e
= hash
->buckets
[h
]; e
; e
= e
->next
) {
199 if (h_nomod
== e
->hash
&&
200 hash
->equal_func(zix_hash_value(e
), value
)) {
203 return ZIX_STATUS_SUCCESS
;
208 if (hash
->n_buckets
!= sizes
) {
209 const unsigned prev_n_buckets
= *(hash
->n_buckets
- 1);
210 if (hash
->count
- 1 <= prev_n_buckets
) {
211 if (!rehash(hash
, prev_n_buckets
)) {
218 return ZIX_STATUS_NOT_FOUND
;
222 zix_hash_foreach(ZixHash
* hash
,
226 for (unsigned b
= 0; b
< *hash
->n_buckets
; ++b
) {
227 ZixHashEntry
* bucket
= hash
->buckets
[b
];
228 for (ZixHashEntry
* e
= bucket
; e
; e
= e
->next
) {
229 f(zix_hash_value(e
), user_data
);