]> git.ipfire.org Git - thirdparty/systemd.git/blame - hashmap.c
license: add GPLv2+ license blurbs everwhere
[thirdparty/systemd.git] / hashmap.c
CommitLineData
60918275
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
2
a7334b09
LP
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
60918275
LP
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <assert.h>
27#include <stdlib.h>
28#include <string.h>
29#include <errno.h>
30
31#include "util.h"
32#include "hashmap.h"
33#include "macro.h"
34
35#define NBUCKETS 127
36
37struct hashmap_entry {
38 const void *key;
39 void *value;
60918275
LP
40 struct hashmap_entry *bucket_next, *bucket_previous;
41 struct hashmap_entry *iterate_next, *iterate_previous;
42};
43
44struct Hashmap {
45 hash_func_t hash_func;
46 compare_func_t compare_func;
47
48 struct hashmap_entry *iterate_list_head, *iterate_list_tail;
49 unsigned n_entries;
50};
51
52#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
53
54unsigned string_hash_func(const void *p) {
55 unsigned hash = 0;
56 const char *c;
57
58 for (c = p; *c; c++)
59 hash = 31 * hash + (unsigned) *c;
60
61 return hash;
62}
63
64int string_compare_func(const void *a, const void *b) {
65 return strcmp(a, b);
66}
67
68unsigned trivial_hash_func(const void *p) {
69 return PTR_TO_UINT(p);
70}
71
72int trivial_compare_func(const void *a, const void *b) {
73 return a < b ? -1 : (a > b ? 1 : 0);
74}
75
76Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
77 Hashmap *h;
78
79 if (!(h = malloc0(ALIGN(sizeof(Hashmap)) + NBUCKETS * ALIGN(sizeof(struct hashmap_entry*)))))
80 return NULL;
81
82 h->hash_func = hash_func ? hash_func : trivial_hash_func;
83 h->compare_func = compare_func ? compare_func : trivial_compare_func;
84
85 h->n_entries = 0;
86 h->iterate_list_head = h->iterate_list_tail = NULL;
87
88 return h;
89}
90
034c6ed7
LP
91int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) {
92 assert(h);
93
94 if (*h)
95 return 0;
96
97 if (!(*h = hashmap_new(hash_func, compare_func)))
98 return -ENOMEM;
99
100 return 0;
101}
102
60918275
LP
103static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
104 assert(h);
105 assert(e);
106
107 /* Remove from iteration list */
108 if (e->iterate_next)
109 e->iterate_next->iterate_previous = e->iterate_previous;
110 else
111 h->iterate_list_tail = e->iterate_previous;
112
113 if (e->iterate_previous)
114 e->iterate_previous->iterate_next = e->iterate_next;
115 else
116 h->iterate_list_head = e->iterate_next;
117
118 /* Remove from hash table bucket list */
119 if (e->bucket_next)
120 e->bucket_next->bucket_previous = e->bucket_previous;
121
122 if (e->bucket_previous)
123 e->bucket_previous->bucket_next = e->bucket_next;
124 else {
125 unsigned hash = h->hash_func(e->key) % NBUCKETS;
126 BY_HASH(h)[hash] = e->bucket_next;
127 }
128
129 free(e);
130
131 assert(h->n_entries >= 1);
132 h->n_entries--;
133}
134
135void hashmap_free(Hashmap*h) {
136
137 if (!h)
138 return;
139
11dd41ce 140 hashmap_clear(h);
60918275
LP
141
142 free(h);
143}
144
11dd41ce
LP
145void hashmap_clear(Hashmap *h) {
146 if (!h)
147 return;
148
149 while (h->iterate_list_head)
150 remove_entry(h, h->iterate_list_head);
151}
152
60918275
LP
153static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
154 struct hashmap_entry *e;
155 assert(h);
156 assert(hash < NBUCKETS);
157
158 for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
159 if (h->compare_func(e->key, key) == 0)
160 return e;
161
162 return NULL;
163}
164
165int hashmap_put(Hashmap *h, const void *key, void *value) {
166 struct hashmap_entry *e;
167 unsigned hash;
168
169 assert(h);
170
171 hash = h->hash_func(key) % NBUCKETS;
172
3158713e
LP
173 if ((e = hash_scan(h, hash, key))) {
174
175 if (e->value == value)
176 return 0;
177
60918275 178 return -EEXIST;
3158713e 179 }
60918275
LP
180
181 if (!(e = new(struct hashmap_entry, 1)))
182 return -ENOMEM;
183
184 e->key = key;
185 e->value = value;
186
187 /* Insert into hash table */
188 e->bucket_next = BY_HASH(h)[hash];
189 e->bucket_previous = NULL;
190 if (BY_HASH(h)[hash])
191 BY_HASH(h)[hash]->bucket_previous = e;
192 BY_HASH(h)[hash] = e;
193
194 /* Insert into iteration list */
195 e->iterate_previous = h->iterate_list_tail;
196 e->iterate_next = NULL;
197 if (h->iterate_list_tail) {
198 assert(h->iterate_list_head);
199 h->iterate_list_tail->iterate_next = e;
200 } else {
201 assert(!h->iterate_list_head);
202 h->iterate_list_head = e;
203 }
204 h->iterate_list_tail = e;
205
206 h->n_entries++;
207 assert(h->n_entries >= 1);
208
209 return 0;
210}
211
3158713e
LP
212int hashmap_replace(Hashmap *h, const void *key, void *value) {
213 struct hashmap_entry *e;
214 unsigned hash;
215
216 assert(h);
217
218 hash = h->hash_func(key) % NBUCKETS;
219
220 if ((e = hash_scan(h, hash, key))) {
221 e->value = value;
222 return 0;
223 }
224
225 return hashmap_put(h, key, value);
226}
227
60918275
LP
228void* hashmap_get(Hashmap *h, const void *key) {
229 unsigned hash;
230 struct hashmap_entry *e;
231
232 if (!h)
233 return NULL;
234
235 hash = h->hash_func(key) % NBUCKETS;
236
237 if (!(e = hash_scan(h, hash, key)))
238 return NULL;
239
240 return e->value;
241}
242
243void* hashmap_remove(Hashmap *h, const void *key) {
244 struct hashmap_entry *e;
245 unsigned hash;
246 void *data;
247
248 if (!h)
249 return NULL;
250
251 hash = h->hash_func(key) % NBUCKETS;
252
253 if (!(e = hash_scan(h, hash, key)))
254 return NULL;
255
256 data = e->value;
257 remove_entry(h, e);
258
259 return data;
260}
261
3158713e
LP
262void* hashmap_remove_value(Hashmap *h, const void *key, void *value) {
263 struct hashmap_entry *e;
264 unsigned hash;
265
266 if (!h)
267 return NULL;
268
269 hash = h->hash_func(key) % NBUCKETS;
270
271 if (!(e = hash_scan(h, hash, key)))
272 return NULL;
273
274 if (e->value != value)
275 return NULL;
276
277 remove_entry(h, e);
278
279 return value;
280}
281
034c6ed7 282void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
60918275
LP
283 struct hashmap_entry *e;
284
034c6ed7 285 assert(i);
60918275
LP
286
287 if (!h)
288 goto at_end;
289
034c6ed7 290 if (*i == ITERATOR_LAST)
60918275
LP
291 goto at_end;
292
034c6ed7 293 if (*i == ITERATOR_FIRST && !h->iterate_list_head)
60918275
LP
294 goto at_end;
295
034c6ed7 296 e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
60918275
LP
297
298 if (e->iterate_next)
034c6ed7 299 *i = (Iterator) e->iterate_next;
60918275 300 else
034c6ed7 301 *i = ITERATOR_LAST;
60918275
LP
302
303 if (key)
304 *key = e->key;
305
306 return e->value;
307
308at_end:
034c6ed7 309 *i = ITERATOR_LAST;
60918275
LP
310
311 if (key)
312 *key = NULL;
313
314 return NULL;
315}
316
034c6ed7 317void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
60918275
LP
318 struct hashmap_entry *e;
319
034c6ed7 320 assert(i);
60918275
LP
321
322 if (!h)
323 goto at_beginning;
324
034c6ed7 325 if (*i == ITERATOR_FIRST)
60918275
LP
326 goto at_beginning;
327
034c6ed7 328 if (*i == ITERATOR_LAST && !h->iterate_list_tail)
60918275
LP
329 goto at_beginning;
330
034c6ed7 331 e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
60918275
LP
332
333 if (e->iterate_previous)
034c6ed7 334 *i = (Iterator) e->iterate_previous;
60918275 335 else
034c6ed7 336 *i = ITERATOR_FIRST;
60918275
LP
337
338 if (key)
339 *key = e->key;
340
341 return e->value;
342
343at_beginning:
034c6ed7 344 *i = ITERATOR_FIRST;
60918275
LP
345
346 if (key)
347 *key = NULL;
348
349 return NULL;
350}
351
034c6ed7
LP
352void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) {
353 unsigned hash;
354 struct hashmap_entry *e;
355
356 if (!h)
357 return NULL;
358
359 hash = h->hash_func(key) % NBUCKETS;
360
361 if (!(e = hash_scan(h, hash, key)))
362 return NULL;
363
364 *i = (Iterator) e;
365
366 return e->value;
367}
368
60918275
LP
369void* hashmap_first(Hashmap *h) {
370
371 if (!h)
372 return NULL;
373
374 if (!h->iterate_list_head)
375 return NULL;
376
377 return h->iterate_list_head->value;
378}
379
380void* hashmap_last(Hashmap *h) {
381
382 if (!h)
383 return NULL;
384
385 if (!h->iterate_list_tail)
386 return NULL;
387
388 return h->iterate_list_tail->value;
389}
390
391void* hashmap_steal_first(Hashmap *h) {
392 void *data;
393
394 if (!h)
395 return NULL;
396
397 if (!h->iterate_list_head)
398 return NULL;
399
400 data = h->iterate_list_head->value;
401 remove_entry(h, h->iterate_list_head);
402
403 return data;
404}
405
406unsigned hashmap_size(Hashmap *h) {
407
408 if (!h)
409 return 0;
410
411 return h->n_entries;
412}
413
414bool hashmap_isempty(Hashmap *h) {
415
416 if (!h)
417 return true;
418
419 return h->n_entries == 0;
420}
91cdde8a
LP
421
422int hashmap_merge(Hashmap *h, Hashmap *other) {
423 struct hashmap_entry *e;
424
425 assert(h);
426
427 if (!other)
428 return 0;
429
430 for (e = other->iterate_list_head; e; e = e->iterate_next) {
431 int r;
432
433 if ((r = hashmap_put(h, e->key, e->value)) < 0)
434 if (r != -EEXIST)
435 return r;
436 }
437
438 return 0;
439}
440
441Hashmap *hashmap_copy(Hashmap *h) {
442 Hashmap *copy;
443
444 assert(h);
445
446 if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
447 return NULL;
448
449 if (hashmap_merge(copy, h) < 0) {
450 hashmap_free(copy);
451 return NULL;
452 }
453
454 return copy;
455}