blob: 7fe3f807711add1fe39c6c4c2a07c07c5eb53c5a [file] [log] [blame]
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -02001/*
2 * libkmod - interface to kernel module operations
3 *
Lucas De Marchie6b0e492013-01-16 11:27:21 -02004 * Copyright (C) 2011-2013 ProFUSION embedded systems
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -02005 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
Lucas De Marchicb451f32011-12-12 18:24:35 -02008 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020010 *
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
Lucas De Marchidea2dfe2014-12-25 23:32:03 -020017 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020018 */
19
Lucas De Marchic2e42862014-10-03 01:41:42 -030020#include <errno.h>
Lucas De Marchi0db718e2014-10-03 00:29:18 -030021#include <inttypes.h>
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020022#include <stdlib.h>
23#include <string.h>
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020024
Lucas De Marchi0db718e2014-10-03 00:29:18 -030025#include <shared/hash.h>
26#include <shared/util.h>
27
Lucas De Marchi822913d2011-12-27 12:13:54 -020028struct hash_entry {
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020029 const char *key;
30 const void *value;
31};
32
Lucas De Marchi822913d2011-12-27 12:13:54 -020033struct hash_bucket {
34 struct hash_entry *entries;
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020035 unsigned int used;
36 unsigned int total;
37};
38
Lucas De Marchi822913d2011-12-27 12:13:54 -020039struct hash {
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020040 unsigned int count;
41 unsigned int step;
42 unsigned int n_buckets;
43 void (*free_value)(void *value);
Lucas De Marchi822913d2011-12-27 12:13:54 -020044 struct hash_bucket buckets[];
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020045};
46
Lucas De Marchi822913d2011-12-27 12:13:54 -020047struct hash *hash_new(unsigned int n_buckets,
Lucas De Marchic35347f2011-12-12 10:48:02 -020048 void (*free_value)(void *value))
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020049{
Lucas De Marchi82fc7d92013-08-22 01:36:45 -030050 struct hash *hash;
51
52 n_buckets = ALIGN_POWER2(n_buckets);
53 hash = calloc(1, sizeof(struct hash) +
54 n_buckets * sizeof(struct hash_bucket));
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020055 if (hash == NULL)
56 return NULL;
57 hash->n_buckets = n_buckets;
58 hash->free_value = free_value;
59 hash->step = n_buckets / 32;
60 if (hash->step == 0)
61 hash->step = 4;
62 else if (hash->step > 64)
63 hash->step = 64;
64 return hash;
65}
66
Lucas De Marchi822913d2011-12-27 12:13:54 -020067void hash_free(struct hash *hash)
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020068{
Lucas De Marchi822913d2011-12-27 12:13:54 -020069 struct hash_bucket *bucket, *bucket_end;
Dave Reisner4f17bb02012-01-04 19:05:04 -050070
71 if (hash == NULL)
72 return;
73
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020074 bucket = hash->buckets;
75 bucket_end = bucket + hash->n_buckets;
76 for (; bucket < bucket_end; bucket++) {
77 if (hash->free_value) {
Lucas De Marchi822913d2011-12-27 12:13:54 -020078 struct hash_entry *entry, *entry_end;
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020079 entry = bucket->entries;
80 entry_end = entry + bucket->used;
81 for (; entry < entry_end; entry++)
82 hash->free_value((void *)entry->value);
83 }
84 free(bucket->entries);
85 }
86 free(hash);
87}
88
89static inline unsigned int hash_superfast(const char *key, unsigned int len)
90{
91 /* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html)
92 * used by WebCore (http://webkit.org/blog/8/hashtables-part-2/)
93 * EFL's eina and possible others.
94 */
95 unsigned int tmp, hash = len, rem = len & 3;
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -020096
97 len /= 4;
98
99 /* Main loop */
100 for (; len > 0; len--) {
Lucas De Marchiaf9080d2012-05-21 20:43:39 -0300101 hash += get_unaligned((uint16_t *) key);
102 tmp = (get_unaligned((uint16_t *)(key + 2)) << 11) ^ hash;
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200103 hash = (hash << 16) ^ tmp;
Ambroz Bizjaka2c7d3e2012-02-03 18:15:01 -0200104 key += 4;
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200105 hash += hash >> 11;
106 }
107
108 /* Handle end cases */
109 switch (rem) {
110 case 3:
Lucas De Marchiaf9080d2012-05-21 20:43:39 -0300111 hash += get_unaligned((uint16_t *) key);
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200112 hash ^= hash << 16;
113 hash ^= key[2] << 18;
114 hash += hash >> 11;
115 break;
116
117 case 2:
Lucas De Marchiaf9080d2012-05-21 20:43:39 -0300118 hash += get_unaligned((uint16_t *) key);
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200119 hash ^= hash << 11;
120 hash += hash >> 17;
121 break;
122
123 case 1:
Ambroz Bizjaka2c7d3e2012-02-03 18:15:01 -0200124 hash += *key;
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200125 hash ^= hash << 10;
126 hash += hash >> 1;
127 }
128
129 /* Force "avalanching" of final 127 bits */
130 hash ^= hash << 3;
131 hash += hash >> 5;
132 hash ^= hash << 4;
133 hash += hash >> 17;
134 hash ^= hash << 25;
135 hash += hash >> 6;
136
137 return hash;
138}
139
140/*
141 * add or replace key in hash map.
142 *
143 * none of key or value are copied, just references are remembered as is,
144 * make sure they are live while pair exists in hash!
145 */
Lucas De Marchi822913d2011-12-27 12:13:54 -0200146int hash_add(struct hash *hash, const char *key, const void *value)
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200147{
148 unsigned int keylen = strlen(key);
149 unsigned int hashval = hash_superfast(key, keylen);
Lucas De Marchi82fc7d92013-08-22 01:36:45 -0300150 unsigned int pos = hashval & (hash->n_buckets - 1);
Lucas De Marchi822913d2011-12-27 12:13:54 -0200151 struct hash_bucket *bucket = hash->buckets + pos;
152 struct hash_entry *entry, *entry_end;
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200153
154 if (bucket->used + 1 >= bucket->total) {
155 unsigned new_total = bucket->total + hash->step;
Lucas De Marchi822913d2011-12-27 12:13:54 -0200156 size_t size = new_total * sizeof(struct hash_entry);
157 struct hash_entry *tmp = realloc(bucket->entries, size);
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200158 if (tmp == NULL)
159 return -errno;
160 bucket->entries = tmp;
161 bucket->total = new_total;
162 }
163
164 entry = bucket->entries;
165 entry_end = entry + bucket->used;
166 for (; entry < entry_end; entry++) {
167 int c = strcmp(key, entry->key);
168 if (c == 0) {
Leandro Pereira1faec2c2012-10-12 12:28:56 -0300169 if (hash->free_value)
170 hash->free_value((void *)entry->value);
Lukas Anzinger86e19e92014-05-18 18:40:19 +0200171 entry->key = key;
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200172 entry->value = value;
173 return 0;
174 } else if (c < 0) {
175 memmove(entry + 1, entry,
Lucas De Marchi822913d2011-12-27 12:13:54 -0200176 (entry_end - entry) * sizeof(struct hash_entry));
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200177 break;
178 }
179 }
180
181 entry->key = key;
182 entry->value = value;
183 bucket->used++;
184 hash->count++;
185 return 0;
186}
187
Lucas De Marchid7073802011-12-27 12:20:35 -0200188/* similar to hash_add(), but fails if key already exists */
189int hash_add_unique(struct hash *hash, const char *key, const void *value)
190{
191 unsigned int keylen = strlen(key);
192 unsigned int hashval = hash_superfast(key, keylen);
Lucas De Marchi82fc7d92013-08-22 01:36:45 -0300193 unsigned int pos = hashval & (hash->n_buckets - 1);
Lucas De Marchid7073802011-12-27 12:20:35 -0200194 struct hash_bucket *bucket = hash->buckets + pos;
195 struct hash_entry *entry, *entry_end;
196
197 if (bucket->used + 1 >= bucket->total) {
198 unsigned new_total = bucket->total + hash->step;
199 size_t size = new_total * sizeof(struct hash_entry);
200 struct hash_entry *tmp = realloc(bucket->entries, size);
201 if (tmp == NULL)
202 return -errno;
203 bucket->entries = tmp;
204 bucket->total = new_total;
205 }
206
207 entry = bucket->entries;
208 entry_end = entry + bucket->used;
209 for (; entry < entry_end; entry++) {
210 int c = strcmp(key, entry->key);
211 if (c == 0)
212 return -EEXIST;
213 else if (c < 0) {
214 memmove(entry + 1, entry,
215 (entry_end - entry) * sizeof(struct hash_entry));
216 break;
217 }
218 }
219
220 entry->key = key;
221 entry->value = value;
222 bucket->used++;
223 hash->count++;
224 return 0;
225}
226
Lucas De Marchi822913d2011-12-27 12:13:54 -0200227static int hash_entry_cmp(const void *pa, const void *pb)
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200228{
Lucas De Marchi822913d2011-12-27 12:13:54 -0200229 const struct hash_entry *a = pa;
230 const struct hash_entry *b = pb;
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200231 return strcmp(a->key, b->key);
232}
233
Lucas De Marchi822913d2011-12-27 12:13:54 -0200234void *hash_find(const struct hash *hash, const char *key)
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200235{
236 unsigned int keylen = strlen(key);
237 unsigned int hashval = hash_superfast(key, keylen);
Lucas De Marchi82fc7d92013-08-22 01:36:45 -0300238 unsigned int pos = hashval & (hash->n_buckets - 1);
Lucas De Marchi822913d2011-12-27 12:13:54 -0200239 const struct hash_bucket *bucket = hash->buckets + pos;
240 const struct hash_entry se = {
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200241 .key = key,
242 .value = NULL
243 };
Lucas De Marchi822913d2011-12-27 12:13:54 -0200244 const struct hash_entry *entry = bsearch(
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200245 &se, bucket->entries, bucket->used,
Lucas De Marchi822913d2011-12-27 12:13:54 -0200246 sizeof(struct hash_entry), hash_entry_cmp);
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200247 if (entry == NULL)
248 return NULL;
249 return (void *)entry->value;
250}
251
Lucas De Marchi822913d2011-12-27 12:13:54 -0200252int hash_del(struct hash *hash, const char *key)
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200253{
254 unsigned int keylen = strlen(key);
255 unsigned int hashval = hash_superfast(key, keylen);
Lucas De Marchi82fc7d92013-08-22 01:36:45 -0300256 unsigned int pos = hashval & (hash->n_buckets - 1);
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200257 unsigned int steps_used, steps_total;
Lucas De Marchi822913d2011-12-27 12:13:54 -0200258 struct hash_bucket *bucket = hash->buckets + pos;
259 struct hash_entry *entry, *entry_end;
260 const struct hash_entry se = {
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200261 .key = key,
262 .value = NULL
263 };
264
265 entry = bsearch(&se, bucket->entries, bucket->used,
Lucas De Marchi822913d2011-12-27 12:13:54 -0200266 sizeof(struct hash_entry), hash_entry_cmp);
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200267 if (entry == NULL)
268 return -ENOENT;
269
Leandro Pereira1faec2c2012-10-12 12:28:56 -0300270 if (hash->free_value)
271 hash->free_value((void *)entry->value);
272
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200273 entry_end = bucket->entries + bucket->used;
274 memmove(entry, entry + 1,
Lucas De Marchi822913d2011-12-27 12:13:54 -0200275 (entry_end - entry) * sizeof(struct hash_entry));
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200276
277 bucket->used--;
278 hash->count--;
279
280 steps_used = bucket->used / hash->step;
281 steps_total = bucket->total / hash->step;
282 if (steps_used + 1 < steps_total) {
283 size_t size = (steps_used + 1) *
Lucas De Marchi822913d2011-12-27 12:13:54 -0200284 hash->step * sizeof(struct hash_entry);
285 struct hash_entry *tmp = realloc(bucket->entries, size);
Gustavo Sverzut Barbieri7db08652011-12-05 00:17:37 -0200286 if (tmp) {
287 bucket->entries = tmp;
288 bucket->total = (steps_used + 1) * hash->step;
289 }
290 }
291
292 return 0;
293}
Lucas De Marchid7073802011-12-27 12:20:35 -0200294
295unsigned int hash_get_count(const struct hash *hash)
296{
297 return hash->count;
298}
Lucas De Marchi8d1278d2011-12-27 13:27:01 -0200299
300void hash_iter_init(const struct hash *hash, struct hash_iter *iter)
301{
302 iter->hash = hash;
303 iter->bucket = 0;
304 iter->entry = -1;
305}
306
307bool hash_iter_next(struct hash_iter *iter, const char **key,
308 const void **value)
309{
310 const struct hash_bucket *b = iter->hash->buckets + iter->bucket;
311 const struct hash_entry *e;
312
313 iter->entry++;
314
315 if (iter->entry >= b->used) {
316 iter->entry = 0;
317
318 for (iter->bucket++; iter->bucket < iter->hash->n_buckets;
319 iter->bucket++) {
320 b = iter->hash->buckets + iter->bucket;
321
322 if (b->used > 0)
323 break;
324 }
325
326 if (iter->bucket >= iter->hash->n_buckets)
327 return false;
328 }
329
330 e = b->entries + iter->entry;
331
332 if (value != NULL)
333 *value = e->value;
334 if (key != NULL)
335 *key = e->key;
336
337 return true;
338}