]> git.ipfire.org Git - thirdparty/kmod.git/blame - libkmod/libkmod-hash.c
Add missing doc for function argument
[thirdparty/kmod.git] / libkmod / libkmod-hash.c
CommitLineData
7db08652
GSB
1/*
2 * libkmod - interface to kernel module operations
3 *
4 * Copyright (C) 2011 ProFUSION embedded systems
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
cb451f35
LDM
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
7db08652
GSB
10 *
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.
15 *
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
19 */
20
21#include "libkmod.h"
529148ea 22#include "libkmod-hash.h"
7db08652
GSB
23
24#include <stdlib.h>
25#include <string.h>
26#include <errno.h>
27
822913d7 28struct hash_entry {
7db08652
GSB
29 const char *key;
30 const void *value;
31};
32
822913d7
LDM
33struct hash_bucket {
34 struct hash_entry *entries;
7db08652
GSB
35 unsigned int used;
36 unsigned int total;
37};
38
822913d7 39struct hash {
7db08652
GSB
40 unsigned int count;
41 unsigned int step;
42 unsigned int n_buckets;
43 void (*free_value)(void *value);
822913d7 44 struct hash_bucket buckets[];
7db08652
GSB
45};
46
822913d7 47struct hash *hash_new(unsigned int n_buckets,
c35347f1 48 void (*free_value)(void *value))
7db08652 49{
822913d7
LDM
50 struct hash *hash = calloc(1, sizeof(struct hash) +
51 n_buckets * sizeof(struct hash_bucket));
7db08652
GSB
52 if (hash == NULL)
53 return NULL;
54 hash->n_buckets = n_buckets;
55 hash->free_value = free_value;
56 hash->step = n_buckets / 32;
57 if (hash->step == 0)
58 hash->step = 4;
59 else if (hash->step > 64)
60 hash->step = 64;
61 return hash;
62}
63
822913d7 64void hash_free(struct hash *hash)
7db08652 65{
822913d7 66 struct hash_bucket *bucket, *bucket_end;
7db08652
GSB
67 bucket = hash->buckets;
68 bucket_end = bucket + hash->n_buckets;
69 for (; bucket < bucket_end; bucket++) {
70 if (hash->free_value) {
822913d7 71 struct hash_entry *entry, *entry_end;
7db08652
GSB
72 entry = bucket->entries;
73 entry_end = entry + bucket->used;
74 for (; entry < entry_end; entry++)
75 hash->free_value((void *)entry->value);
76 }
77 free(bucket->entries);
78 }
79 free(hash);
80}
81
82static inline unsigned int hash_superfast(const char *key, unsigned int len)
83{
84 /* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html)
85 * used by WebCore (http://webkit.org/blog/8/hashtables-part-2/)
86 * EFL's eina and possible others.
87 */
88 unsigned int tmp, hash = len, rem = len & 3;
89 const unsigned short *itr = (const unsigned short *)key;
90
91 len /= 4;
92
93 /* Main loop */
94 for (; len > 0; len--) {
95 hash += itr[0];
96 tmp = (itr[1] << 11) ^ hash;
97 hash = (hash << 16) ^ tmp;
98 itr += 2;
99 hash += hash >> 11;
100 }
101
102 /* Handle end cases */
103 switch (rem) {
104 case 3:
105 hash += *itr;
106 hash ^= hash << 16;
107 hash ^= key[2] << 18;
108 hash += hash >> 11;
109 break;
110
111 case 2:
112 hash += *itr;
113 hash ^= hash << 11;
114 hash += hash >> 17;
115 break;
116
117 case 1:
118 hash += *(const char *)itr;
119 hash ^= hash << 10;
120 hash += hash >> 1;
121 }
122
123 /* Force "avalanching" of final 127 bits */
124 hash ^= hash << 3;
125 hash += hash >> 5;
126 hash ^= hash << 4;
127 hash += hash >> 17;
128 hash ^= hash << 25;
129 hash += hash >> 6;
130
131 return hash;
132}
133
134/*
135 * add or replace key in hash map.
136 *
137 * none of key or value are copied, just references are remembered as is,
138 * make sure they are live while pair exists in hash!
139 */
822913d7 140int hash_add(struct hash *hash, const char *key, const void *value)
7db08652
GSB
141{
142 unsigned int keylen = strlen(key);
143 unsigned int hashval = hash_superfast(key, keylen);
144 unsigned int pos = hashval % hash->n_buckets;
822913d7
LDM
145 struct hash_bucket *bucket = hash->buckets + pos;
146 struct hash_entry *entry, *entry_end;
7db08652
GSB
147
148 if (bucket->used + 1 >= bucket->total) {
149 unsigned new_total = bucket->total + hash->step;
822913d7
LDM
150 size_t size = new_total * sizeof(struct hash_entry);
151 struct hash_entry *tmp = realloc(bucket->entries, size);
7db08652
GSB
152 if (tmp == NULL)
153 return -errno;
154 bucket->entries = tmp;
155 bucket->total = new_total;
156 }
157
158 entry = bucket->entries;
159 entry_end = entry + bucket->used;
160 for (; entry < entry_end; entry++) {
161 int c = strcmp(key, entry->key);
162 if (c == 0) {
163 hash->free_value((void *)entry->value);
164 entry->value = value;
165 return 0;
166 } else if (c < 0) {
167 memmove(entry + 1, entry,
822913d7 168 (entry_end - entry) * sizeof(struct hash_entry));
7db08652
GSB
169 break;
170 }
171 }
172
173 entry->key = key;
174 entry->value = value;
175 bucket->used++;
176 hash->count++;
177 return 0;
178}
179
d7073807
LDM
180/* similar to hash_add(), but fails if key already exists */
181int hash_add_unique(struct hash *hash, const char *key, const void *value)
182{
183 unsigned int keylen = strlen(key);
184 unsigned int hashval = hash_superfast(key, keylen);
185 unsigned int pos = hashval % hash->n_buckets;
186 struct hash_bucket *bucket = hash->buckets + pos;
187 struct hash_entry *entry, *entry_end;
188
189 if (bucket->used + 1 >= bucket->total) {
190 unsigned new_total = bucket->total + hash->step;
191 size_t size = new_total * sizeof(struct hash_entry);
192 struct hash_entry *tmp = realloc(bucket->entries, size);
193 if (tmp == NULL)
194 return -errno;
195 bucket->entries = tmp;
196 bucket->total = new_total;
197 }
198
199 entry = bucket->entries;
200 entry_end = entry + bucket->used;
201 for (; entry < entry_end; entry++) {
202 int c = strcmp(key, entry->key);
203 if (c == 0)
204 return -EEXIST;
205 else if (c < 0) {
206 memmove(entry + 1, entry,
207 (entry_end - entry) * sizeof(struct hash_entry));
208 break;
209 }
210 }
211
212 entry->key = key;
213 entry->value = value;
214 bucket->used++;
215 hash->count++;
216 return 0;
217}
218
822913d7 219static int hash_entry_cmp(const void *pa, const void *pb)
7db08652 220{
822913d7
LDM
221 const struct hash_entry *a = pa;
222 const struct hash_entry *b = pb;
7db08652
GSB
223 return strcmp(a->key, b->key);
224}
225
822913d7 226void *hash_find(const struct hash *hash, const char *key)
7db08652
GSB
227{
228 unsigned int keylen = strlen(key);
229 unsigned int hashval = hash_superfast(key, keylen);
230 unsigned int pos = hashval % hash->n_buckets;
822913d7
LDM
231 const struct hash_bucket *bucket = hash->buckets + pos;
232 const struct hash_entry se = {
7db08652
GSB
233 .key = key,
234 .value = NULL
235 };
822913d7 236 const struct hash_entry *entry = bsearch(
7db08652 237 &se, bucket->entries, bucket->used,
822913d7 238 sizeof(struct hash_entry), hash_entry_cmp);
7db08652
GSB
239 if (entry == NULL)
240 return NULL;
241 return (void *)entry->value;
242}
243
822913d7 244int hash_del(struct hash *hash, const char *key)
7db08652
GSB
245{
246 unsigned int keylen = strlen(key);
247 unsigned int hashval = hash_superfast(key, keylen);
248 unsigned int pos = hashval % hash->n_buckets;
249 unsigned int steps_used, steps_total;
822913d7
LDM
250 struct hash_bucket *bucket = hash->buckets + pos;
251 struct hash_entry *entry, *entry_end;
252 const struct hash_entry se = {
7db08652
GSB
253 .key = key,
254 .value = NULL
255 };
256
257 entry = bsearch(&se, bucket->entries, bucket->used,
822913d7 258 sizeof(struct hash_entry), hash_entry_cmp);
7db08652
GSB
259 if (entry == NULL)
260 return -ENOENT;
261
262 entry_end = bucket->entries + bucket->used;
263 memmove(entry, entry + 1,
822913d7 264 (entry_end - entry) * sizeof(struct hash_entry));
7db08652
GSB
265
266 bucket->used--;
267 hash->count--;
268
269 steps_used = bucket->used / hash->step;
270 steps_total = bucket->total / hash->step;
271 if (steps_used + 1 < steps_total) {
272 size_t size = (steps_used + 1) *
822913d7
LDM
273 hash->step * sizeof(struct hash_entry);
274 struct hash_entry *tmp = realloc(bucket->entries, size);
7db08652
GSB
275 if (tmp) {
276 bucket->entries = tmp;
277 bucket->total = (steps_used + 1) * hash->step;
278 }
279 }
280
281 return 0;
282}
d7073807
LDM
283
284unsigned int hash_get_count(const struct hash *hash)
285{
286 return hash->count;
287}
8d1278d0
LDM
288
289void hash_iter_init(const struct hash *hash, struct hash_iter *iter)
290{
291 iter->hash = hash;
292 iter->bucket = 0;
293 iter->entry = -1;
294}
295
296bool hash_iter_next(struct hash_iter *iter, const char **key,
297 const void **value)
298{
299 const struct hash_bucket *b = iter->hash->buckets + iter->bucket;
300 const struct hash_entry *e;
301
302 iter->entry++;
303
304 if (iter->entry >= b->used) {
305 iter->entry = 0;
306
307 for (iter->bucket++; iter->bucket < iter->hash->n_buckets;
308 iter->bucket++) {
309 b = iter->hash->buckets + iter->bucket;
310
311 if (b->used > 0)
312 break;
313 }
314
315 if (iter->bucket >= iter->hash->n_buckets)
316 return false;
317 }
318
319 e = b->entries + iter->entry;
320
321 if (value != NULL)
322 *value = e->value;
323 if (key != NULL)
324 *key = e->key;
325
326 return true;
327}