chiark / gitweb /
license: add GPLv2+ license blurbs everwhere
[elogind.git] / hashmap.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
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
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
37 struct hashmap_entry {
38         const void *key;
39         void *value;
40         struct hashmap_entry *bucket_next, *bucket_previous;
41         struct hashmap_entry *iterate_next, *iterate_previous;
42 };
43
44 struct 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
54 unsigned 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
64 int string_compare_func(const void *a, const void *b) {
65         return strcmp(a, b);
66 }
67
68 unsigned trivial_hash_func(const void *p) {
69         return PTR_TO_UINT(p);
70 }
71
72 int trivial_compare_func(const void *a, const void *b) {
73         return a < b ? -1 : (a > b ? 1 : 0);
74 }
75
76 Hashmap *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
91 int 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
103 static 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
135 void hashmap_free(Hashmap*h) {
136
137         if (!h)
138                 return;
139
140         hashmap_clear(h);
141
142         free(h);
143 }
144
145 void 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
153 static 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
165 int 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
173         if ((e = hash_scan(h, hash, key))) {
174
175                 if (e->value == value)
176                         return 0;
177
178                 return -EEXIST;
179         }
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
212 int 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
228 void* 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
243 void* 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
262 void* 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
282 void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
283         struct hashmap_entry *e;
284
285         assert(i);
286
287         if (!h)
288                 goto at_end;
289
290         if (*i == ITERATOR_LAST)
291                 goto at_end;
292
293         if (*i == ITERATOR_FIRST && !h->iterate_list_head)
294                 goto at_end;
295
296         e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i;
297
298         if (e->iterate_next)
299                 *i = (Iterator) e->iterate_next;
300         else
301                 *i = ITERATOR_LAST;
302
303         if (key)
304                 *key = e->key;
305
306         return e->value;
307
308 at_end:
309         *i = ITERATOR_LAST;
310
311         if (key)
312                 *key = NULL;
313
314         return NULL;
315 }
316
317 void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) {
318         struct hashmap_entry *e;
319
320         assert(i);
321
322         if (!h)
323                 goto at_beginning;
324
325         if (*i == ITERATOR_FIRST)
326                 goto at_beginning;
327
328         if (*i == ITERATOR_LAST && !h->iterate_list_tail)
329                 goto at_beginning;
330
331         e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i;
332
333         if (e->iterate_previous)
334                 *i = (Iterator) e->iterate_previous;
335         else
336                 *i = ITERATOR_FIRST;
337
338         if (key)
339                 *key = e->key;
340
341         return e->value;
342
343 at_beginning:
344         *i = ITERATOR_FIRST;
345
346         if (key)
347                 *key = NULL;
348
349         return NULL;
350 }
351
352 void *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
369 void* 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
380 void* 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
391 void* 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
406 unsigned hashmap_size(Hashmap *h) {
407
408         if (!h)
409                 return 0;
410
411         return h->n_entries;
412 }
413
414 bool hashmap_isempty(Hashmap *h) {
415
416         if (!h)
417                 return true;
418
419         return h->n_entries == 0;
420 }
421
422 int 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
441 Hashmap *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 }