cannam@226
|
1 /*
|
cannam@226
|
2 Copyright 2011-2014 David Robillard <http://drobilla.net>
|
cannam@226
|
3
|
cannam@226
|
4 Permission to use, copy, modify, and/or distribute this software for any
|
cannam@226
|
5 purpose with or without fee is hereby granted, provided that the above
|
cannam@226
|
6 copyright notice and this permission notice appear in all copies.
|
cannam@226
|
7
|
cannam@226
|
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
cannam@226
|
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
cannam@226
|
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
cannam@226
|
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
cannam@226
|
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
cannam@226
|
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
cannam@226
|
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
cannam@226
|
15 */
|
cannam@226
|
16
|
cannam@226
|
17 #include <assert.h>
|
cannam@226
|
18 #include <stdint.h>
|
cannam@226
|
19 #include <stdlib.h>
|
cannam@226
|
20 #include <string.h>
|
cannam@226
|
21
|
cannam@245
|
22 #include "hash.h"
|
cannam@226
|
23
|
cannam@226
|
24 /**
|
cannam@226
|
25 Primes, each slightly less than twice its predecessor, and as far away
|
cannam@226
|
26 from powers of two as possible.
|
cannam@226
|
27 */
|
cannam@226
|
28 static const unsigned sizes[] = {
|
cannam@226
|
29 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317,
|
cannam@226
|
30 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843,
|
cannam@226
|
31 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741, 0
|
cannam@226
|
32 };
|
cannam@226
|
33
|
cannam@226
|
34 typedef struct ZixHashEntry {
|
cannam@226
|
35 struct ZixHashEntry* next; ///< Next entry in bucket
|
cannam@226
|
36 uint32_t hash; ///< Non-modulo hash value
|
cannam@226
|
37 // Value follows here (access with zix_hash_value)
|
cannam@226
|
38 } ZixHashEntry;
|
cannam@226
|
39
|
cannam@226
|
40 struct ZixHashImpl {
|
cannam@226
|
41 ZixHashFunc hash_func;
|
cannam@226
|
42 ZixEqualFunc equal_func;
|
cannam@226
|
43 ZixHashEntry** buckets;
|
cannam@226
|
44 const unsigned* n_buckets;
|
cannam@226
|
45 size_t value_size;
|
cannam@226
|
46 unsigned count;
|
cannam@226
|
47 };
|
cannam@226
|
48
|
cannam@226
|
49 static inline void*
|
cannam@226
|
50 zix_hash_value(ZixHashEntry* entry)
|
cannam@226
|
51 {
|
cannam@226
|
52 return entry + 1;
|
cannam@226
|
53 }
|
cannam@226
|
54
|
cannam@226
|
55 ZIX_API ZixHash*
|
cannam@226
|
56 zix_hash_new(ZixHashFunc hash_func,
|
cannam@226
|
57 ZixEqualFunc equal_func,
|
cannam@226
|
58 size_t value_size)
|
cannam@226
|
59 {
|
cannam@226
|
60 ZixHash* hash = (ZixHash*)malloc(sizeof(ZixHash));
|
cannam@226
|
61 if (hash) {
|
cannam@226
|
62 hash->hash_func = hash_func;
|
cannam@226
|
63 hash->equal_func = equal_func;
|
cannam@226
|
64 hash->n_buckets = &sizes[0];
|
cannam@226
|
65 hash->value_size = value_size;
|
cannam@226
|
66 hash->count = 0;
|
cannam@226
|
67 if (!(hash->buckets = (ZixHashEntry**)calloc(*hash->n_buckets,
|
cannam@226
|
68 sizeof(ZixHashEntry*)))) {
|
cannam@226
|
69 free(hash);
|
cannam@226
|
70 return NULL;
|
cannam@226
|
71 }
|
cannam@226
|
72 }
|
cannam@226
|
73 return hash;
|
cannam@226
|
74 }
|
cannam@226
|
75
|
cannam@226
|
76 ZIX_API void
|
cannam@226
|
77 zix_hash_free(ZixHash* hash)
|
cannam@226
|
78 {
|
cannam@226
|
79 for (unsigned b = 0; b < *hash->n_buckets; ++b) {
|
cannam@226
|
80 ZixHashEntry* bucket = hash->buckets[b];
|
cannam@226
|
81 for (ZixHashEntry* e = bucket; e;) {
|
cannam@226
|
82 ZixHashEntry* next = e->next;
|
cannam@226
|
83 free(e);
|
cannam@226
|
84 e = next;
|
cannam@226
|
85 }
|
cannam@226
|
86 }
|
cannam@226
|
87
|
cannam@226
|
88 free(hash->buckets);
|
cannam@226
|
89 free(hash);
|
cannam@226
|
90 }
|
cannam@226
|
91
|
cannam@226
|
92 ZIX_API size_t
|
cannam@226
|
93 zix_hash_size(const ZixHash* hash)
|
cannam@226
|
94 {
|
cannam@226
|
95 return hash->count;
|
cannam@226
|
96 }
|
cannam@226
|
97
|
cannam@226
|
98 static inline void
|
cannam@226
|
99 insert_entry(ZixHashEntry** bucket, ZixHashEntry* entry)
|
cannam@226
|
100 {
|
cannam@226
|
101 entry->next = *bucket;
|
cannam@226
|
102 *bucket = entry;
|
cannam@226
|
103 }
|
cannam@226
|
104
|
cannam@226
|
105 static inline ZixStatus
|
cannam@226
|
106 rehash(ZixHash* hash, unsigned new_n_buckets)
|
cannam@226
|
107 {
|
cannam@226
|
108 ZixHashEntry** new_buckets = (ZixHashEntry**)calloc(
|
cannam@226
|
109 new_n_buckets, sizeof(ZixHashEntry*));
|
cannam@226
|
110 if (!new_buckets) {
|
cannam@226
|
111 return ZIX_STATUS_NO_MEM;
|
cannam@226
|
112 }
|
cannam@226
|
113
|
cannam@226
|
114 const unsigned old_n_buckets = *hash->n_buckets;
|
cannam@226
|
115 for (unsigned b = 0; b < old_n_buckets; ++b) {
|
cannam@226
|
116 for (ZixHashEntry* e = hash->buckets[b]; e;) {
|
cannam@226
|
117 ZixHashEntry* const next = e->next;
|
cannam@226
|
118 const unsigned h = e->hash % new_n_buckets;
|
cannam@226
|
119 insert_entry(&new_buckets[h], e);
|
cannam@226
|
120 e = next;
|
cannam@226
|
121 }
|
cannam@226
|
122 }
|
cannam@226
|
123
|
cannam@226
|
124 free(hash->buckets);
|
cannam@226
|
125 hash->buckets = new_buckets;
|
cannam@226
|
126
|
cannam@226
|
127 return ZIX_STATUS_SUCCESS;
|
cannam@226
|
128 }
|
cannam@226
|
129
|
cannam@226
|
130 static inline ZixHashEntry*
|
cannam@226
|
131 find_entry(const ZixHash* hash,
|
cannam@226
|
132 const void* key,
|
cannam@226
|
133 const unsigned h,
|
cannam@226
|
134 const unsigned h_nomod)
|
cannam@226
|
135 {
|
cannam@226
|
136 for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) {
|
cannam@226
|
137 if (e->hash == h_nomod && hash->equal_func(zix_hash_value(e), key)) {
|
cannam@226
|
138 return e;
|
cannam@226
|
139 }
|
cannam@226
|
140 }
|
cannam@226
|
141 return NULL;
|
cannam@226
|
142 }
|
cannam@226
|
143
|
cannam@226
|
144 ZIX_API const void*
|
cannam@226
|
145 zix_hash_find(const ZixHash* hash, const void* value)
|
cannam@226
|
146 {
|
cannam@226
|
147 const unsigned h_nomod = hash->hash_func(value);
|
cannam@226
|
148 const unsigned h = h_nomod % *hash->n_buckets;
|
cannam@226
|
149 ZixHashEntry* const entry = find_entry(hash, value, h, h_nomod);
|
cannam@226
|
150 return entry ? zix_hash_value(entry) : 0;
|
cannam@226
|
151 }
|
cannam@226
|
152
|
cannam@226
|
153 ZIX_API ZixStatus
|
cannam@226
|
154 zix_hash_insert(ZixHash* hash, const void* value, const void** inserted)
|
cannam@226
|
155 {
|
cannam@226
|
156 unsigned h_nomod = hash->hash_func(value);
|
cannam@226
|
157 unsigned h = h_nomod % *hash->n_buckets;
|
cannam@226
|
158
|
cannam@226
|
159 ZixHashEntry* elem = find_entry(hash, value, h, h_nomod);
|
cannam@226
|
160 if (elem) {
|
cannam@226
|
161 assert(elem->hash == h_nomod);
|
cannam@226
|
162 if (inserted) {
|
cannam@226
|
163 *inserted = zix_hash_value(elem);
|
cannam@226
|
164 }
|
cannam@226
|
165 return ZIX_STATUS_EXISTS;
|
cannam@226
|
166 }
|
cannam@226
|
167
|
cannam@226
|
168 elem = (ZixHashEntry*)malloc(sizeof(ZixHashEntry) + hash->value_size);
|
cannam@226
|
169 if (!elem) {
|
cannam@226
|
170 return ZIX_STATUS_NO_MEM;
|
cannam@226
|
171 }
|
cannam@226
|
172 elem->next = NULL;
|
cannam@226
|
173 elem->hash = h_nomod;
|
cannam@226
|
174 memcpy(elem + 1, value, hash->value_size);
|
cannam@226
|
175
|
cannam@226
|
176 const unsigned next_n_buckets = *(hash->n_buckets + 1);
|
cannam@226
|
177 if (next_n_buckets != 0 && (hash->count + 1) >= next_n_buckets) {
|
cannam@226
|
178 if (!rehash(hash, next_n_buckets)) {
|
cannam@226
|
179 h = h_nomod % *(++hash->n_buckets);
|
cannam@226
|
180 }
|
cannam@226
|
181 }
|
cannam@226
|
182
|
cannam@226
|
183 insert_entry(&hash->buckets[h], elem);
|
cannam@226
|
184 ++hash->count;
|
cannam@226
|
185 if (inserted) {
|
cannam@226
|
186 *inserted = zix_hash_value(elem);
|
cannam@226
|
187 }
|
cannam@226
|
188 return ZIX_STATUS_SUCCESS;
|
cannam@226
|
189 }
|
cannam@226
|
190
|
cannam@226
|
191 ZIX_API ZixStatus
|
cannam@226
|
192 zix_hash_remove(ZixHash* hash, const void* value)
|
cannam@226
|
193 {
|
cannam@226
|
194 const unsigned h_nomod = hash->hash_func(value);
|
cannam@226
|
195 const unsigned h = h_nomod % *hash->n_buckets;
|
cannam@226
|
196
|
cannam@226
|
197 ZixHashEntry** next_ptr = &hash->buckets[h];
|
cannam@226
|
198 for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) {
|
cannam@226
|
199 if (h_nomod == e->hash &&
|
cannam@226
|
200 hash->equal_func(zix_hash_value(e), value)) {
|
cannam@226
|
201 *next_ptr = e->next;
|
cannam@226
|
202 free(e);
|
cannam@226
|
203 return ZIX_STATUS_SUCCESS;
|
cannam@226
|
204 }
|
cannam@226
|
205 next_ptr = &e->next;
|
cannam@226
|
206 }
|
cannam@226
|
207
|
cannam@226
|
208 if (hash->n_buckets != sizes) {
|
cannam@226
|
209 const unsigned prev_n_buckets = *(hash->n_buckets - 1);
|
cannam@226
|
210 if (hash->count - 1 <= prev_n_buckets) {
|
cannam@226
|
211 if (!rehash(hash, prev_n_buckets)) {
|
cannam@226
|
212 --hash->n_buckets;
|
cannam@226
|
213 }
|
cannam@226
|
214 }
|
cannam@226
|
215 }
|
cannam@226
|
216
|
cannam@226
|
217 --hash->count;
|
cannam@226
|
218 return ZIX_STATUS_NOT_FOUND;
|
cannam@226
|
219 }
|
cannam@226
|
220
|
cannam@226
|
221 ZIX_API void
|
cannam@226
|
222 zix_hash_foreach(ZixHash* hash,
|
cannam@226
|
223 ZixHashVisitFunc f,
|
cannam@226
|
224 void* user_data)
|
cannam@226
|
225 {
|
cannam@226
|
226 for (unsigned b = 0; b < *hash->n_buckets; ++b) {
|
cannam@226
|
227 ZixHashEntry* bucket = hash->buckets[b];
|
cannam@226
|
228 for (ZixHashEntry* e = bucket; e; e = e->next) {
|
cannam@226
|
229 f(zix_hash_value(e), user_data);
|
cannam@226
|
230 }
|
cannam@226
|
231 }
|
cannam@226
|
232 }
|