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