]> git.ipfire.org Git - thirdparty/kmod.git/blame - libkmod/libkmod-hash.c
Change licenses
[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"
22#include "libkmod-private.h"
23
24#include <stdlib.h>
25#include <string.h>
26#include <errno.h>
27
28struct kmod_hash_entry {
29 const char *key;
30 const void *value;
31};
32
33struct kmod_hash_bucket {
34 struct kmod_hash_entry *entries;
35 unsigned int used;
36 unsigned int total;
37};
38
39struct kmod_hash {
40 unsigned int count;
41 unsigned int step;
42 unsigned int n_buckets;
43 void (*free_value)(void *value);
44 struct kmod_hash_bucket buckets[];
45};
46
c35347f1
LDM
47struct kmod_hash *kmod_hash_new(unsigned int n_buckets,
48 void (*free_value)(void *value))
7db08652
GSB
49{
50 struct kmod_hash *hash = calloc(1, sizeof(struct kmod_hash) +
51 n_buckets * sizeof(struct kmod_hash_bucket));
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
64void kmod_hash_free(struct kmod_hash *hash)
65{
66 struct kmod_hash_bucket *bucket, *bucket_end;
67 bucket = hash->buckets;
68 bucket_end = bucket + hash->n_buckets;
69 for (; bucket < bucket_end; bucket++) {
70 if (hash->free_value) {
71 struct kmod_hash_entry *entry, *entry_end;
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 */
140int kmod_hash_add(struct kmod_hash *hash, const char *key, const void *value)
141{
142 unsigned int keylen = strlen(key);
143 unsigned int hashval = hash_superfast(key, keylen);
144 unsigned int pos = hashval % hash->n_buckets;
145 struct kmod_hash_bucket *bucket = hash->buckets + pos;
146 struct kmod_hash_entry *entry, *entry_end;
147
148 if (bucket->used + 1 >= bucket->total) {
149 unsigned new_total = bucket->total + hash->step;
150 size_t size = new_total * sizeof(struct kmod_hash_entry);
151 struct kmod_hash_entry *tmp = realloc(bucket->entries, size);
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,
168 (entry_end - entry) * sizeof(struct kmod_hash_entry));
169 break;
170 }
171 }
172
173 entry->key = key;
174 entry->value = value;
175 bucket->used++;
176 hash->count++;
177 return 0;
178}
179
180static int kmod_hash_entry_cmp(const void *pa, const void *pb)
181{
182 const struct kmod_hash_entry *a = pa;
183 const struct kmod_hash_entry *b = pb;
184 return strcmp(a->key, b->key);
185}
186
187void *kmod_hash_find(const struct kmod_hash *hash, const char *key)
188{
189 unsigned int keylen = strlen(key);
190 unsigned int hashval = hash_superfast(key, keylen);
191 unsigned int pos = hashval % hash->n_buckets;
192 const struct kmod_hash_bucket *bucket = hash->buckets + pos;
193 const struct kmod_hash_entry se = {
194 .key = key,
195 .value = NULL
196 };
197 const struct kmod_hash_entry *entry = bsearch(
198 &se, bucket->entries, bucket->used,
199 sizeof(struct kmod_hash_entry), kmod_hash_entry_cmp);
200 if (entry == NULL)
201 return NULL;
202 return (void *)entry->value;
203}
204
205int kmod_hash_del(struct kmod_hash *hash, const char *key)
206{
207 unsigned int keylen = strlen(key);
208 unsigned int hashval = hash_superfast(key, keylen);
209 unsigned int pos = hashval % hash->n_buckets;
210 unsigned int steps_used, steps_total;
211 struct kmod_hash_bucket *bucket = hash->buckets + pos;
212 struct kmod_hash_entry *entry, *entry_end;
213 const struct kmod_hash_entry se = {
214 .key = key,
215 .value = NULL
216 };
217
218 entry = bsearch(&se, bucket->entries, bucket->used,
219 sizeof(struct kmod_hash_entry), kmod_hash_entry_cmp);
220 if (entry == NULL)
221 return -ENOENT;
222
223 entry_end = bucket->entries + bucket->used;
224 memmove(entry, entry + 1,
225 (entry_end - entry) * sizeof(struct kmod_hash_entry));
226
227 bucket->used--;
228 hash->count--;
229
230 steps_used = bucket->used / hash->step;
231 steps_total = bucket->total / hash->step;
232 if (steps_used + 1 < steps_total) {
233 size_t size = (steps_used + 1) *
234 hash->step * sizeof(struct kmod_hash_entry);
235 struct kmod_hash_entry *tmp = realloc(bucket->entries, size);
236 if (tmp) {
237 bucket->entries = tmp;
238 bucket->total = (steps_used + 1) * hash->step;
239 }
240 }
241
242 return 0;
243}