aboutsummaryrefslogtreecommitdiff
path: root/src/lib/util/container_multihashmap32.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/container_multihashmap32.c')
-rw-r--r--src/lib/util/container_multihashmap32.c606
1 files changed, 606 insertions, 0 deletions
diff --git a/src/lib/util/container_multihashmap32.c b/src/lib/util/container_multihashmap32.c
new file mode 100644
index 000000000..3b4c92426
--- /dev/null
+++ b/src/lib/util/container_multihashmap32.c
@@ -0,0 +1,606 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file util/container_multihashmap32.c
22 * @brief a version of hash map implemented in container_multihashmap.c but with
23 * uint32_t as keys
24 * @author Christian Grothoff
25 * @author Sree Harsha Totakura
26 */
27
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32#define LOG(kind, ...) \
33 GNUNET_log_from (kind, "util-container-multihashmap32", __VA_ARGS__)
34
35
36/**
37 * Maximum recursion depth for callbacks of
38 * #GNUNET_CONTAINER_multihashmap_get_multiple() themselves
39 * again calling #GNUNET_CONTAINER_multihashmap_get_multiple().
40 * Should be totally excessive, but if violated we die.
41 */
42#define NEXT_CACHE_SIZE 16
43
44/**
45 * An entry in the hash map.
46 */
47struct MapEntry
48{
49 /**
50 * Key for the entry.
51 */
52 uint32_t key;
53
54 /**
55 * Value of the entry.
56 */
57 void *value;
58
59 /**
60 * If there is a hash collision, we create a linked list.
61 */
62 struct MapEntry *next;
63};
64
65/**
66 * Internal representation of the hash map.
67 */
68struct GNUNET_CONTAINER_MultiHashMap32
69{
70 /**
71 * All of our buckets.
72 */
73 struct MapEntry **map;
74
75 /**
76 * Number of entries in the map.
77 */
78 unsigned int size;
79
80 /**
81 * Length of the @e map array.
82 */
83 unsigned int map_length;
84
85 /**
86 * Counts the destructive modifications (grow, remove)
87 * to the map, so that iterators can check if they are still valid.
88 */
89 unsigned int modification_counter;
90
91 /**
92 * Map entries indicating iteration positions currently
93 * in use by #GNUNET_CONTAINER_multihashmap_get_multiple().
94 * Only used up to @e next_cache_off.
95 */
96 struct MapEntry *next_cache[NEXT_CACHE_SIZE];
97
98 /**
99 * Offset of @e next_cache entries in use, must be smaller
100 * than #NEXT_CACHE_SIZE.
101 */
102 unsigned int next_cache_off;
103};
104
105
106/**
107 * Cursor into a multihashmap.
108 * Allows to enumerate elements asynchronously.
109 */
110struct GNUNET_CONTAINER_MultiHashMap32Iterator
111{
112 /**
113 * Position in the bucket @e idx
114 */
115 struct MapEntry *me;
116
117 /**
118 * Current bucket index.
119 */
120 unsigned int idx;
121
122 /**
123 * Modification counter as observed on the map when the iterator
124 * was created.
125 */
126 unsigned int modification_counter;
127
128 /**
129 * Map that we are iterating over.
130 */
131 const struct GNUNET_CONTAINER_MultiHashMap32 *map;
132};
133
134
135/**
136 * Create a multi hash map.
137 *
138 * @param len initial size (map will grow as needed)
139 * @return NULL on error
140 */
141struct GNUNET_CONTAINER_MultiHashMap32 *
142GNUNET_CONTAINER_multihashmap32_create (unsigned int len)
143{
144 struct GNUNET_CONTAINER_MultiHashMap32 *ret;
145
146 GNUNET_assert (len > 0);
147 ret = GNUNET_new (struct GNUNET_CONTAINER_MultiHashMap32);
148 ret->map = GNUNET_malloc_large (len * sizeof(struct MapEntry *));
149 if (NULL == ret->map)
150 {
151 GNUNET_free (ret);
152 return NULL;
153 }
154 ret->map_length = len;
155 return ret;
156}
157
158
159/**
160 * Destroy a hash map. Will not free any values
161 * stored in the hash map!
162 *
163 * @param map the map
164 */
165void
166GNUNET_CONTAINER_multihashmap32_destroy (
167 struct GNUNET_CONTAINER_MultiHashMap32 *map)
168{
169 struct MapEntry *e;
170
171 for (unsigned int i = 0; i < map->map_length; i++)
172 {
173 while (NULL != (e = map->map[i]))
174 {
175 map->map[i] = e->next;
176 GNUNET_free (e);
177 }
178 }
179 GNUNET_free (map->map);
180 GNUNET_free (map);
181}
182
183
184/**
185 * Compute the index of the bucket for the given key.
186 *
187 * @param m hash map for which to compute the index
188 * @param key what key should the index be computed for
189 * @return offset into the "map" array of "m"
190 */
191static unsigned int
192idx_of (const struct GNUNET_CONTAINER_MultiHashMap32 *m, const uint32_t key)
193{
194 GNUNET_assert (NULL != m);
195 return ((unsigned int) key) % m->map_length;
196}
197
198
199unsigned int
200GNUNET_CONTAINER_multihashmap32_size (
201 const struct GNUNET_CONTAINER_MultiHashMap32 *map)
202{
203 return map->size;
204}
205
206
207void *
208GNUNET_CONTAINER_multihashmap32_get (
209 const struct GNUNET_CONTAINER_MultiHashMap32 *map,
210 uint32_t key)
211{
212 struct MapEntry *e;
213
214 e = map->map[idx_of (map, key)];
215 while (NULL != e)
216 {
217 if (key == e->key)
218 return e->value;
219 e = e->next;
220 }
221 return NULL;
222}
223
224
225int
226GNUNET_CONTAINER_multihashmap32_iterate (
227 struct GNUNET_CONTAINER_MultiHashMap32 *map,
228 GNUNET_CONTAINER_MultiHashMapIterator32Callback it,
229 void *it_cls)
230{
231 int count;
232 struct MapEntry **ce;
233
234 count = 0;
235 GNUNET_assert (NULL != map);
236 ce = &map->next_cache[map->next_cache_off];
237 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
238 for (unsigned int i = 0; i < map->map_length; i++)
239 {
240 struct MapEntry *e;
241
242 *ce = map->map[i];
243 while (NULL != (e = *ce))
244 {
245 *ce = e->next;
246 if (NULL != it)
247 {
248 if (GNUNET_OK != it (it_cls, e->key, e->value))
249 {
250 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
251 return GNUNET_SYSERR;
252 }
253 }
254 count++;
255 }
256 }
257 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
258 return count;
259}
260
261
262/**
263 * We are about to free() the @a me, make sure it is not in
264 * the list of next values for any iterator in the @a map's next_cache.
265 *
266 * @param map the map to check
267 * @param me the entry that is about to be free'd
268 */
269static void
270update_next_cache (struct GNUNET_CONTAINER_MultiHashMap32 *map,
271 const struct MapEntry *me)
272{
273 for (unsigned int i = 0; i < map->next_cache_off; i++)
274 if (map->next_cache[i] == me)
275 map->next_cache[i] = me->next;
276}
277
278
279enum GNUNET_GenericReturnValue
280GNUNET_CONTAINER_multihashmap32_remove (
281 struct GNUNET_CONTAINER_MultiHashMap32 *map,
282 uint32_t key,
283 const void *value)
284{
285 struct MapEntry *e;
286 struct MapEntry *p;
287 unsigned int i;
288
289 map->modification_counter++;
290
291 i = idx_of (map, key);
292 p = NULL;
293 e = map->map[i];
294 while (e != NULL)
295 {
296 if ((key == e->key) && (value == e->value))
297 {
298 if (p == NULL)
299 map->map[i] = e->next;
300 else
301 p->next = e->next;
302 update_next_cache (map, e);
303 GNUNET_free (e);
304 map->size--;
305 return GNUNET_YES;
306 }
307 p = e;
308 e = e->next;
309 }
310 return GNUNET_NO;
311}
312
313
314int
315GNUNET_CONTAINER_multihashmap32_remove_all (
316 struct GNUNET_CONTAINER_MultiHashMap32 *map,
317 uint32_t key)
318{
319 struct MapEntry *e;
320 struct MapEntry *p;
321 unsigned int i;
322 int ret;
323
324 map->modification_counter++;
325
326 ret = 0;
327 i = idx_of (map, key);
328 p = NULL;
329 e = map->map[i];
330 while (e != NULL)
331 {
332 if (key == e->key)
333 {
334 if (p == NULL)
335 map->map[i] = e->next;
336 else
337 p->next = e->next;
338 update_next_cache (map, e);
339 GNUNET_free (e);
340 map->size--;
341 if (p == NULL)
342 e = map->map[i];
343 else
344 e = p->next;
345 ret++;
346 }
347 else
348 {
349 p = e;
350 e = e->next;
351 }
352 }
353 return ret;
354}
355
356
357enum GNUNET_GenericReturnValue
358GNUNET_CONTAINER_multihashmap32_contains (
359 const struct GNUNET_CONTAINER_MultiHashMap32 *map,
360 uint32_t key)
361{
362 struct MapEntry *e;
363
364 e = map->map[idx_of (map, key)];
365 while (e != NULL)
366 {
367 if (key == e->key)
368 return GNUNET_YES;
369 e = e->next;
370 }
371 return GNUNET_NO;
372}
373
374
375enum GNUNET_GenericReturnValue
376GNUNET_CONTAINER_multihashmap32_contains_value (
377 const struct GNUNET_CONTAINER_MultiHashMap32 *map,
378 uint32_t key,
379 const void *value)
380{
381 struct MapEntry *e;
382
383 e = map->map[idx_of (map, key)];
384 while (e != NULL)
385 {
386 if ((key == e->key) && (e->value == value))
387 return GNUNET_YES;
388 e = e->next;
389 }
390 return GNUNET_NO;
391}
392
393
394/**
395 * Grow the given map to a more appropriate size.
396 *
397 * @param map the hash map to grow
398 */
399static void
400grow (struct GNUNET_CONTAINER_MultiHashMap32 *map)
401{
402 struct MapEntry **old_map;
403 struct MapEntry **new_map;
404 struct MapEntry *e;
405 unsigned int old_len;
406 unsigned int new_len;
407 unsigned int idx;
408
409 old_map = map->map;
410 old_len = map->map_length;
411 new_len = old_len * 2;
412 if (0 == new_len) /* 2^31 * 2 == 0 */
413 new_len = old_len; /* never use 0 */
414 if (new_len == old_len)
415 return; /* nothing changed */
416 new_map = GNUNET_malloc_large (new_len * sizeof(struct MapEntry *));
417 if (NULL == new_map)
418 return; /* grow not possible */
419 map->modification_counter++;
420 map->map_length = new_len;
421 map->map = new_map;
422 for (unsigned int i = 0; i < old_len; i++)
423 {
424 while (NULL != (e = old_map[i]))
425 {
426 old_map[i] = e->next;
427 idx = idx_of (map, e->key);
428 e->next = new_map[idx];
429 new_map[idx] = e;
430 }
431 }
432 GNUNET_free (old_map);
433}
434
435
436/**
437 * Store a key-value pair in the map.
438 *
439 * @param map the map
440 * @param key key to use
441 * @param value value to use
442 * @param opt options for put
443 * @return #GNUNET_OK on success,
444 * #GNUNET_NO if a value was replaced (with REPLACE)
445 * #GNUNET_SYSERR if UNIQUE_ONLY was the option and the
446 * value already exists
447 */
448enum GNUNET_GenericReturnValue
449GNUNET_CONTAINER_multihashmap32_put (
450 struct GNUNET_CONTAINER_MultiHashMap32 *map,
451 uint32_t key,
452 void *value,
453 enum GNUNET_CONTAINER_MultiHashMapOption opt)
454{
455 struct MapEntry *e;
456 unsigned int i;
457
458 i = idx_of (map, key);
459 if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
460 (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
461 {
462 e = map->map[i];
463 while (e != NULL)
464 {
465 if (key == e->key)
466 {
467 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
468 return GNUNET_SYSERR;
469 e->value = value;
470 return GNUNET_NO;
471 }
472 e = e->next;
473 }
474 }
475 if (map->size / 3 >= map->map_length / 4)
476 {
477 grow (map);
478 i = idx_of (map, key);
479 }
480 e = GNUNET_new (struct MapEntry);
481 e->key = key;
482 e->value = value;
483 e->next = map->map[i];
484 map->map[i] = e;
485 map->size++;
486 return GNUNET_OK;
487}
488
489
490int
491GNUNET_CONTAINER_multihashmap32_get_multiple (
492 struct GNUNET_CONTAINER_MultiHashMap32 *map,
493 uint32_t key,
494 GNUNET_CONTAINER_MultiHashMapIterator32Callback it,
495 void *it_cls)
496{
497 int count;
498 struct MapEntry *e;
499 struct MapEntry **ce;
500
501 count = 0;
502 ce = &map->next_cache[map->next_cache_off];
503 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
504
505 *ce = map->map[idx_of (map, key)];
506 while (NULL != (e = *ce))
507 {
508 *ce = e->next;
509 if (key != e->key)
510 continue;
511 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, e->value)))
512 {
513 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
514 return GNUNET_SYSERR;
515 }
516 count++;
517 }
518 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
519 return count;
520}
521
522
523/**
524 * Create an iterator for a multihashmap.
525 * The iterator can be used to retrieve all the elements in the multihashmap
526 * one by one, without having to handle all elements at once (in contrast to
527 * GNUNET_CONTAINER_multihashmap_iterate()). Note that the iterator can not be
528 * used anymore if elements have been removed from 'map' after the creation of
529 * the iterator, or 'map' has been destroyed. Adding elements to 'map' may
530 * result in skipped or repeated elements.
531 *
532 * @param map the map to create an iterator for
533 * @return an iterator over the given multihashmap @a map
534 */
535struct GNUNET_CONTAINER_MultiHashMap32Iterator *
536GNUNET_CONTAINER_multihashmap32_iterator_create (
537 const struct GNUNET_CONTAINER_MultiHashMap32 *map)
538{
539 struct GNUNET_CONTAINER_MultiHashMap32Iterator *iter;
540
541 iter = GNUNET_new (struct GNUNET_CONTAINER_MultiHashMap32Iterator);
542 iter->map = map;
543 iter->modification_counter = map->modification_counter;
544 iter->me = map->map[0];
545 return iter;
546}
547
548
549/**
550 * Retrieve the next element from the hash map at the iterator's position.
551 * If there are no elements left, GNUNET_NO is returned, and 'key' and 'value'
552 * are not modified.
553 * This operation is only allowed if no elements have been removed from the
554 * multihashmap since the creation of 'iter', and the map has not been destroyed.
555 * Adding elements may result in repeating or skipping elements.
556 *
557 * @param iter the iterator to get the next element from
558 * @param key pointer to store the key in, can be NULL
559 * @param value pointer to store the value in, can be NULL
560 * @return #GNUNET_YES we returned an element,
561 * #GNUNET_NO if we are out of elements
562 */
563enum GNUNET_GenericReturnValue
564GNUNET_CONTAINER_multihashmap32_iterator_next (
565 struct GNUNET_CONTAINER_MultiHashMap32Iterator *iter,
566 uint32_t *key,
567 const void **value)
568{
569 /* make sure the map has not been modified */
570 GNUNET_assert (iter->modification_counter == iter->map->modification_counter);
571
572 /* look for the next entry, skipping empty buckets */
573 while (1)
574 {
575 if (iter->idx >= iter->map->map_length)
576 return GNUNET_NO;
577 if (NULL != iter->me)
578 {
579 if (NULL != key)
580 *key = iter->me->key;
581 if (NULL != value)
582 *value = iter->me->value;
583 iter->me = iter->me->next;
584 return GNUNET_YES;
585 }
586 iter->idx += 1;
587 if (iter->idx < iter->map->map_length)
588 iter->me = iter->map->map[iter->idx];
589 }
590}
591
592
593/**
594 * Destroy a multihashmap iterator.
595 *
596 * @param iter the iterator to destroy
597 */
598void
599GNUNET_CONTAINER_multihashmap32_iterator_destroy (
600 struct GNUNET_CONTAINER_MultiHashMapIterator *iter)
601{
602 GNUNET_free (iter);
603}
604
605
606/* end of container_multihashmap.c */