]>
git.ipfire.org Git - thirdparty/kmod.git/blob - libkmod/libkmod-hash.c
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
21 #include <shared/util.h>
24 #include "libkmod-hash.h"
26 #include "libkmod-util.h"
37 struct hash_entry
*entries
;
45 unsigned int n_buckets
;
46 void (*free_value
)(void *value
);
47 struct hash_bucket buckets
[];
50 struct hash
*hash_new(unsigned int n_buckets
,
51 void (*free_value
)(void *value
))
55 n_buckets
= ALIGN_POWER2(n_buckets
);
56 hash
= calloc(1, sizeof(struct hash
) +
57 n_buckets
* sizeof(struct hash_bucket
));
60 hash
->n_buckets
= n_buckets
;
61 hash
->free_value
= free_value
;
62 hash
->step
= n_buckets
/ 32;
65 else if (hash
->step
> 64)
70 void hash_free(struct hash
*hash
)
72 struct hash_bucket
*bucket
, *bucket_end
;
77 bucket
= hash
->buckets
;
78 bucket_end
= bucket
+ hash
->n_buckets
;
79 for (; bucket
< bucket_end
; bucket
++) {
80 if (hash
->free_value
) {
81 struct hash_entry
*entry
, *entry_end
;
82 entry
= bucket
->entries
;
83 entry_end
= entry
+ bucket
->used
;
84 for (; entry
< entry_end
; entry
++)
85 hash
->free_value((void *)entry
->value
);
87 free(bucket
->entries
);
92 static inline unsigned int hash_superfast(const char *key
, unsigned int len
)
94 /* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html)
95 * used by WebCore (http://webkit.org/blog/8/hashtables-part-2/)
96 * EFL's eina and possible others.
98 unsigned int tmp
, hash
= len
, rem
= len
& 3;
103 for (; len
> 0; len
--) {
104 hash
+= get_unaligned((uint16_t *) key
);
105 tmp
= (get_unaligned((uint16_t *)(key
+ 2)) << 11) ^ hash
;
106 hash
= (hash
<< 16) ^ tmp
;
111 /* Handle end cases */
114 hash
+= get_unaligned((uint16_t *) key
);
116 hash
^= key
[2] << 18;
121 hash
+= get_unaligned((uint16_t *) key
);
132 /* Force "avalanching" of final 127 bits */
144 * add or replace key in hash map.
146 * none of key or value are copied, just references are remembered as is,
147 * make sure they are live while pair exists in hash!
149 int hash_add(struct hash
*hash
, const char *key
, const void *value
)
151 unsigned int keylen
= strlen(key
);
152 unsigned int hashval
= hash_superfast(key
, keylen
);
153 unsigned int pos
= hashval
& (hash
->n_buckets
- 1);
154 struct hash_bucket
*bucket
= hash
->buckets
+ pos
;
155 struct hash_entry
*entry
, *entry_end
;
157 if (bucket
->used
+ 1 >= bucket
->total
) {
158 unsigned new_total
= bucket
->total
+ hash
->step
;
159 size_t size
= new_total
* sizeof(struct hash_entry
);
160 struct hash_entry
*tmp
= realloc(bucket
->entries
, size
);
163 bucket
->entries
= tmp
;
164 bucket
->total
= new_total
;
167 entry
= bucket
->entries
;
168 entry_end
= entry
+ bucket
->used
;
169 for (; entry
< entry_end
; entry
++) {
170 int c
= strcmp(key
, entry
->key
);
172 if (hash
->free_value
)
173 hash
->free_value((void *)entry
->value
);
175 entry
->value
= value
;
178 memmove(entry
+ 1, entry
,
179 (entry_end
- entry
) * sizeof(struct hash_entry
));
185 entry
->value
= value
;
191 /* similar to hash_add(), but fails if key already exists */
192 int hash_add_unique(struct hash
*hash
, const char *key
, const void *value
)
194 unsigned int keylen
= strlen(key
);
195 unsigned int hashval
= hash_superfast(key
, keylen
);
196 unsigned int pos
= hashval
& (hash
->n_buckets
- 1);
197 struct hash_bucket
*bucket
= hash
->buckets
+ pos
;
198 struct hash_entry
*entry
, *entry_end
;
200 if (bucket
->used
+ 1 >= bucket
->total
) {
201 unsigned new_total
= bucket
->total
+ hash
->step
;
202 size_t size
= new_total
* sizeof(struct hash_entry
);
203 struct hash_entry
*tmp
= realloc(bucket
->entries
, size
);
206 bucket
->entries
= tmp
;
207 bucket
->total
= new_total
;
210 entry
= bucket
->entries
;
211 entry_end
= entry
+ bucket
->used
;
212 for (; entry
< entry_end
; entry
++) {
213 int c
= strcmp(key
, entry
->key
);
217 memmove(entry
+ 1, entry
,
218 (entry_end
- entry
) * sizeof(struct hash_entry
));
224 entry
->value
= value
;
230 static int hash_entry_cmp(const void *pa
, const void *pb
)
232 const struct hash_entry
*a
= pa
;
233 const struct hash_entry
*b
= pb
;
234 return strcmp(a
->key
, b
->key
);
237 void *hash_find(const struct hash
*hash
, const char *key
)
239 unsigned int keylen
= strlen(key
);
240 unsigned int hashval
= hash_superfast(key
, keylen
);
241 unsigned int pos
= hashval
& (hash
->n_buckets
- 1);
242 const struct hash_bucket
*bucket
= hash
->buckets
+ pos
;
243 const struct hash_entry se
= {
247 const struct hash_entry
*entry
= bsearch(
248 &se
, bucket
->entries
, bucket
->used
,
249 sizeof(struct hash_entry
), hash_entry_cmp
);
252 return (void *)entry
->value
;
255 int hash_del(struct hash
*hash
, const char *key
)
257 unsigned int keylen
= strlen(key
);
258 unsigned int hashval
= hash_superfast(key
, keylen
);
259 unsigned int pos
= hashval
& (hash
->n_buckets
- 1);
260 unsigned int steps_used
, steps_total
;
261 struct hash_bucket
*bucket
= hash
->buckets
+ pos
;
262 struct hash_entry
*entry
, *entry_end
;
263 const struct hash_entry se
= {
268 entry
= bsearch(&se
, bucket
->entries
, bucket
->used
,
269 sizeof(struct hash_entry
), hash_entry_cmp
);
273 if (hash
->free_value
)
274 hash
->free_value((void *)entry
->value
);
276 entry_end
= bucket
->entries
+ bucket
->used
;
277 memmove(entry
, entry
+ 1,
278 (entry_end
- entry
) * sizeof(struct hash_entry
));
283 steps_used
= bucket
->used
/ hash
->step
;
284 steps_total
= bucket
->total
/ hash
->step
;
285 if (steps_used
+ 1 < steps_total
) {
286 size_t size
= (steps_used
+ 1) *
287 hash
->step
* sizeof(struct hash_entry
);
288 struct hash_entry
*tmp
= realloc(bucket
->entries
, size
);
290 bucket
->entries
= tmp
;
291 bucket
->total
= (steps_used
+ 1) * hash
->step
;
298 unsigned int hash_get_count(const struct hash
*hash
)
303 void hash_iter_init(const struct hash
*hash
, struct hash_iter
*iter
)
310 bool hash_iter_next(struct hash_iter
*iter
, const char **key
,
313 const struct hash_bucket
*b
= iter
->hash
->buckets
+ iter
->bucket
;
314 const struct hash_entry
*e
;
318 if (iter
->entry
>= b
->used
) {
321 for (iter
->bucket
++; iter
->bucket
< iter
->hash
->n_buckets
;
323 b
= iter
->hash
->buckets
+ iter
->bucket
;
329 if (iter
->bucket
>= iter
->hash
->n_buckets
)
333 e
= b
->entries
+ iter
->entry
;