]>
git.ipfire.org Git - thirdparty/kmod.git/blob - shared/hash.c
99057efa84a6b02f19dd8c60317d2496261aef97
2 * libkmod - interface to kernel module operations
4 * Copyright (C) 2011-2013 ProFUSION embedded systems
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #include <shared/hash.h>
27 #include <shared/util.h>
35 struct hash_entry
*entries
;
43 unsigned int n_buckets
;
44 void (*free_value
)(void *value
);
45 struct hash_bucket buckets
[];
48 struct hash
*hash_new(unsigned int n_buckets
,
49 void (*free_value
)(void *value
))
53 n_buckets
= ALIGN_POWER2(n_buckets
);
54 hash
= calloc(1, sizeof(struct hash
) +
55 n_buckets
* sizeof(struct hash_bucket
));
58 hash
->n_buckets
= n_buckets
;
59 hash
->free_value
= free_value
;
60 hash
->step
= n_buckets
/ 32;
63 else if (hash
->step
> 64)
68 void hash_free(struct hash
*hash
)
70 struct hash_bucket
*bucket
, *bucket_end
;
75 bucket
= hash
->buckets
;
76 bucket_end
= bucket
+ hash
->n_buckets
;
77 for (; bucket
< bucket_end
; bucket
++) {
78 if (hash
->free_value
) {
79 struct hash_entry
*entry
, *entry_end
;
80 entry
= bucket
->entries
;
81 entry_end
= entry
+ bucket
->used
;
82 for (; entry
< entry_end
; entry
++)
83 hash
->free_value((void *)entry
->value
);
85 free(bucket
->entries
);
90 static inline unsigned int hash_superfast(const char *key
, unsigned int len
)
92 /* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html)
93 * used by WebCore (http://webkit.org/blog/8/hashtables-part-2/)
94 * EFL's eina and possible others.
96 unsigned int tmp
, hash
= len
, rem
= len
& 3;
101 for (; len
> 0; len
--) {
102 hash
+= get_unaligned((uint16_t *) key
);
103 tmp
= (get_unaligned((uint16_t *)(key
+ 2)) << 11) ^ hash
;
104 hash
= (hash
<< 16) ^ tmp
;
109 /* Handle end cases */
112 hash
+= get_unaligned((uint16_t *) key
);
114 hash
^= key
[2] << 18;
119 hash
+= get_unaligned((uint16_t *) key
);
130 /* Force "avalanching" of final 127 bits */
142 * add or replace key in hash map.
144 * none of key or value are copied, just references are remembered as is,
145 * make sure they are live while pair exists in hash!
147 int hash_add(struct hash
*hash
, const char *key
, const void *value
)
149 unsigned int keylen
= strlen(key
);
150 unsigned int hashval
= hash_superfast(key
, keylen
);
151 unsigned int pos
= hashval
& (hash
->n_buckets
- 1);
152 struct hash_bucket
*bucket
= hash
->buckets
+ pos
;
153 struct hash_entry
*entry
, *entry_end
;
155 if (bucket
->used
+ 1 >= bucket
->total
) {
156 unsigned new_total
= bucket
->total
+ hash
->step
;
157 size_t size
= new_total
* sizeof(struct hash_entry
);
158 struct hash_entry
*tmp
= realloc(bucket
->entries
, size
);
161 bucket
->entries
= tmp
;
162 bucket
->total
= new_total
;
165 entry
= bucket
->entries
;
166 entry_end
= entry
+ bucket
->used
;
167 for (; entry
< entry_end
; entry
++) {
168 int c
= strcmp(key
, entry
->key
);
170 if (hash
->free_value
)
171 hash
->free_value((void *)entry
->value
);
173 entry
->value
= value
;
176 memmove(entry
+ 1, entry
,
177 (entry_end
- entry
) * sizeof(struct hash_entry
));
183 entry
->value
= value
;
189 /* similar to hash_add(), but fails if key already exists */
190 int hash_add_unique(struct hash
*hash
, const char *key
, const void *value
)
192 unsigned int keylen
= strlen(key
);
193 unsigned int hashval
= hash_superfast(key
, keylen
);
194 unsigned int pos
= hashval
& (hash
->n_buckets
- 1);
195 struct hash_bucket
*bucket
= hash
->buckets
+ pos
;
196 struct hash_entry
*entry
, *entry_end
;
198 if (bucket
->used
+ 1 >= bucket
->total
) {
199 unsigned new_total
= bucket
->total
+ hash
->step
;
200 size_t size
= new_total
* sizeof(struct hash_entry
);
201 struct hash_entry
*tmp
= realloc(bucket
->entries
, size
);
204 bucket
->entries
= tmp
;
205 bucket
->total
= new_total
;
208 entry
= bucket
->entries
;
209 entry_end
= entry
+ bucket
->used
;
210 for (; entry
< entry_end
; entry
++) {
211 int c
= strcmp(key
, entry
->key
);
215 memmove(entry
+ 1, entry
,
216 (entry_end
- entry
) * sizeof(struct hash_entry
));
222 entry
->value
= value
;
228 static int hash_entry_cmp(const void *pa
, const void *pb
)
230 const struct hash_entry
*a
= pa
;
231 const struct hash_entry
*b
= pb
;
232 return strcmp(a
->key
, b
->key
);
235 void *hash_find(const struct hash
*hash
, const char *key
)
237 unsigned int keylen
= strlen(key
);
238 unsigned int hashval
= hash_superfast(key
, keylen
);
239 unsigned int pos
= hashval
& (hash
->n_buckets
- 1);
240 const struct hash_bucket
*bucket
= hash
->buckets
+ pos
;
241 const struct hash_entry se
= {
245 const struct hash_entry
*entry
= bsearch(
246 &se
, bucket
->entries
, bucket
->used
,
247 sizeof(struct hash_entry
), hash_entry_cmp
);
250 return (void *)entry
->value
;
253 int hash_del(struct hash
*hash
, const char *key
)
255 unsigned int keylen
= strlen(key
);
256 unsigned int hashval
= hash_superfast(key
, keylen
);
257 unsigned int pos
= hashval
& (hash
->n_buckets
- 1);
258 unsigned int steps_used
, steps_total
;
259 struct hash_bucket
*bucket
= hash
->buckets
+ pos
;
260 struct hash_entry
*entry
, *entry_end
;
261 const struct hash_entry se
= {
266 entry
= bsearch(&se
, bucket
->entries
, bucket
->used
,
267 sizeof(struct hash_entry
), hash_entry_cmp
);
271 if (hash
->free_value
)
272 hash
->free_value((void *)entry
->value
);
274 entry_end
= bucket
->entries
+ bucket
->used
;
275 memmove(entry
, entry
+ 1,
276 (entry_end
- entry
) * sizeof(struct hash_entry
));
281 steps_used
= bucket
->used
/ hash
->step
;
282 steps_total
= bucket
->total
/ hash
->step
;
283 if (steps_used
+ 1 < steps_total
) {
284 size_t size
= (steps_used
+ 1) *
285 hash
->step
* sizeof(struct hash_entry
);
286 struct hash_entry
*tmp
= realloc(bucket
->entries
, size
);
288 bucket
->entries
= tmp
;
289 bucket
->total
= (steps_used
+ 1) * hash
->step
;
296 unsigned int hash_get_count(const struct hash
*hash
)
301 void hash_iter_init(const struct hash
*hash
, struct hash_iter
*iter
)
308 bool hash_iter_next(struct hash_iter
*iter
, const char **key
,
311 const struct hash_bucket
*b
= iter
->hash
->buckets
+ iter
->bucket
;
312 const struct hash_entry
*e
;
316 if (iter
->entry
>= b
->used
) {
319 for (iter
->bucket
++; iter
->bucket
< iter
->hash
->n_buckets
;
321 b
= iter
->hash
->buckets
+ iter
->bucket
;
327 if (iter
->bucket
>= iter
->hash
->n_buckets
)
331 e
= b
->entries
+ iter
->entry
;