style fix
[oweals/gnunet.git] / src / util / container_multihashmap.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2008, 2012 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_multihashmap.c
22  * @brief hash map where the same key may be present multiple times
23  * @author Christian Grothoff
24  */
25
26 #include "platform.h"
27 #include "gnunet_container_lib.h"
28
29 #define LOG(kind, ...) \
30   GNUNET_log_from(kind, "util-container-multihashmap", __VA_ARGS__)
31
32 /**
33  * Maximum recursion depth for callbacks of
34  * #GNUNET_CONTAINER_multihashmap_get_multiple() themselve s
35  * again calling #GNUNET_CONTAINER_multihashmap_get_multiple().
36  * Should be totally excessive, but if violated we die.
37  */
38 #define NEXT_CACHE_SIZE 16
39
40
41 /**
42  * An entry in the hash map with the full key.
43  */
44 struct BigMapEntry {
45   /**
46    * Value of the entry.
47    */
48   void *value;
49
50   /**
51    * If there is a hash collision, we create a linked list.
52    */
53   struct BigMapEntry *next;
54
55   /**
56    * Key for the entry.
57    */
58   struct GNUNET_HashCode key;
59 };
60
61
62 /**
63  * An entry in the hash map with just a pointer to the key.
64  */
65 struct SmallMapEntry {
66   /**
67    * Value of the entry.
68    */
69   void *value;
70
71   /**
72    * If there is a hash collision, we create a linked list.
73    */
74   struct SmallMapEntry *next;
75
76   /**
77    * Key for the entry.
78    */
79   const struct GNUNET_HashCode *key;
80 };
81
82
83 /**
84  * Entry in the map.
85  */
86 union MapEntry {
87   /**
88    * Variant used if map entries only contain a pointer to the key.
89    */
90   struct SmallMapEntry *sme;
91
92   /**
93    * Variant used if map entries contain the full key.
94    */
95   struct BigMapEntry *bme;
96 };
97
98
99 /**
100  * Internal representation of the hash map.
101  */
102 struct GNUNET_CONTAINER_MultiHashMap {
103   /**
104    * All of our buckets.
105    */
106   union MapEntry *map;
107
108   /**
109    * Number of entries in the map.
110    */
111   unsigned int size;
112
113   /**
114    * Length of the "map" array.
115    */
116   unsigned int map_length;
117
118   /**
119    * #GNUNET_NO if the map entries are of type 'struct BigMapEntry',
120    * #GNUNET_YES if the map entries are of type 'struct SmallMapEntry'.
121    */
122   int use_small_entries;
123
124   /**
125    * Counts the destructive modifications (grow, remove)
126    * to the map, so that iterators can check if they are still valid.
127    */
128   unsigned int modification_counter;
129
130   /**
131    * Map entries indicating iteration positions currently
132    * in use by #GNUNET_CONTAINER_multihashmap_get_multiple().
133    * Only used up to @e next_cache_off.
134    */
135   union MapEntry next_cache[NEXT_CACHE_SIZE];
136
137   /**
138    * Offset of @e next_cache entries in use, must be smaller
139    * than #NEXT_CACHE_SIZE.
140    */
141   unsigned int next_cache_off;
142 };
143
144
145 /**
146  * Cursor into a multihashmap.
147  * Allows to enumerate elements asynchronously.
148  */
149 struct GNUNET_CONTAINER_MultiHashMapIterator {
150   /**
151    * Position in the bucket @e idx
152    */
153   union MapEntry me;
154
155   /**
156    * Current bucket index.
157    */
158   unsigned int idx;
159
160   /**
161    * Modification counter as observed on the map when the iterator
162    * was created.
163    */
164   unsigned int modification_counter;
165
166   /**
167    * Map that we are iterating over.
168    */
169   const struct GNUNET_CONTAINER_MultiHashMap *map;
170 };
171
172
173 /**
174  * Create a multi hash map.
175  *
176  * @param len initial size (map will grow as needed)
177  * @param do_not_copy_keys #GNUNET_NO is always safe and should be used by default;
178  *                         #GNUNET_YES means that on 'put', the 'key' does not have
179  *                         to be copied as the destination of the pointer is
180  *                         guaranteed to be life as long as the value is stored in
181  *                         the hashmap.  This can significantly reduce memory
182  *                         consumption, but of course is also a recipie for
183  *                         heap corruption if the assumption is not true.  Only
184  *                         use this if (1) memory use is important in this case and
185  *                         (2) you have triple-checked that the invariant holds
186  * @return NULL on error
187  */
188 struct GNUNET_CONTAINER_MultiHashMap *
189 GNUNET_CONTAINER_multihashmap_create(unsigned int len, int do_not_copy_keys)
190 {
191   struct GNUNET_CONTAINER_MultiHashMap *hm;
192
193   GNUNET_assert(len > 0);
194   hm = GNUNET_new(struct GNUNET_CONTAINER_MultiHashMap);
195   if (len * sizeof(union MapEntry) > GNUNET_MAX_MALLOC_CHECKED)
196     {
197       size_t s;
198       /* application *explicitly* requested very large map, hopefully
199          it checks the return value... */
200       s = len * sizeof(union MapEntry);
201       if ((s / sizeof(union MapEntry)) != len)
202         return NULL; /* integer overflow on multiplication */
203       if (NULL == (hm->map = GNUNET_malloc_large(s)))
204         {
205           /* out of memory */
206           GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
207                      "Out of memory allocating large hash map (%u entries)\n",
208                      len);
209           GNUNET_free(hm);
210           return NULL;
211         }
212     }
213   else
214     {
215       hm->map = GNUNET_new_array(len, union MapEntry);
216     }
217   hm->map_length = len;
218   hm->use_small_entries = do_not_copy_keys;
219   return hm;
220 }
221
222
223 /**
224  * Destroy a hash map.  Will not free any values stored in the hash
225  * map!
226  *
227  * @param map the map
228  */
229 void
230 GNUNET_CONTAINER_multihashmap_destroy(
231   struct GNUNET_CONTAINER_MultiHashMap *map)
232 {
233   GNUNET_assert(0 == map->next_cache_off);
234   for (unsigned int i = 0; i < map->map_length; i++)
235     {
236       union MapEntry me;
237
238       me = map->map[i];
239       if (map->use_small_entries)
240         {
241           struct SmallMapEntry *sme;
242           struct SmallMapEntry *nxt;
243
244           nxt = me.sme;
245           while (NULL != (sme = nxt))
246             {
247               nxt = sme->next;
248               GNUNET_free(sme);
249             }
250           me.sme = NULL;
251         }
252       else
253         {
254           struct BigMapEntry *bme;
255           struct BigMapEntry *nxt;
256
257           nxt = me.bme;
258           while (NULL != (bme = nxt))
259             {
260               nxt = bme->next;
261               GNUNET_free(bme);
262             }
263           me.bme = NULL;
264         }
265     }
266   GNUNET_free(map->map);
267   GNUNET_free(map);
268 }
269
270
271 /**
272  * Compute the index of the bucket for the given key.
273  *
274  * @param map hash map for which to compute the index
275  * @param key what key should the index be computed for
276  * @return offset into the "map" array of "map"
277  */
278 static unsigned int
279 idx_of(const struct GNUNET_CONTAINER_MultiHashMap *map,
280        const struct GNUNET_HashCode *key)
281 {
282   GNUNET_assert(map != NULL);
283   return (*(unsigned int *)key) % map->map_length;
284 }
285
286
287 /**
288  * Get the number of key-value pairs in the map.
289  *
290  * @param map the map
291  * @return the number of key value pairs
292  */
293 unsigned int
294 GNUNET_CONTAINER_multihashmap_size(
295   const struct GNUNET_CONTAINER_MultiHashMap *map)
296 {
297   return map->size;
298 }
299
300
301 /**
302  * Given a key find a value in the map matching the key.
303  *
304  * @param map the map
305  * @param key what to look for
306  * @return NULL if no value was found; note that
307  *   this is indistinguishable from values that just
308  *   happen to be NULL; use "contains" to test for
309  *   key-value pairs with value NULL
310  */
311 void *
312 GNUNET_CONTAINER_multihashmap_get(
313   const struct GNUNET_CONTAINER_MultiHashMap *map,
314   const struct GNUNET_HashCode *key)
315 {
316   union MapEntry me;
317
318   me = map->map[idx_of(map, key)];
319   if (map->use_small_entries)
320     {
321       struct SmallMapEntry *sme;
322
323       for (sme = me.sme; NULL != sme; sme = sme->next)
324         if (0 == GNUNET_memcmp(key, sme->key))
325           return sme->value;
326     }
327   else
328     {
329       struct BigMapEntry *bme;
330
331       for (bme = me.bme; NULL != bme; bme = bme->next)
332         if (0 == GNUNET_memcmp(key, &bme->key))
333           return bme->value;
334     }
335   return NULL;
336 }
337
338
339 /**
340  * Iterate over all entries in the map.
341  *
342  * @param map the map
343  * @param it function to call on each entry
344  * @param it_cls extra argument to @a it
345  * @return the number of key value pairs processed,
346  *         #GNUNET_SYSERR if it aborted iteration
347  */
348 int
349 GNUNET_CONTAINER_multihashmap_iterate(
350   struct GNUNET_CONTAINER_MultiHashMap *map,
351   GNUNET_CONTAINER_MulitHashMapIteratorCallback it,
352   void *it_cls)
353 {
354   int count;
355   union MapEntry me;
356   union MapEntry *ce;
357   struct GNUNET_HashCode kc;
358
359   GNUNET_assert(NULL != map);
360   ce = &map->next_cache[map->next_cache_off];
361   GNUNET_assert(++map->next_cache_off < NEXT_CACHE_SIZE);
362   count = 0;
363   for (unsigned i = 0; i < map->map_length; i++)
364     {
365       me = map->map[i];
366       if (map->use_small_entries)
367         {
368           struct SmallMapEntry *sme;
369
370           ce->sme = me.sme;
371           while (NULL != (sme = ce->sme))
372             {
373               ce->sme = sme->next;
374               if (NULL != it)
375                 {
376                   if (GNUNET_OK != it(it_cls, sme->key, sme->value))
377                     {
378                       GNUNET_assert(--map->next_cache_off < NEXT_CACHE_SIZE);
379                       return GNUNET_SYSERR;
380                     }
381                 }
382               count++;
383             }
384         }
385       else
386         {
387           struct BigMapEntry *bme;
388
389           ce->bme = me.bme;
390           while (NULL != (bme = ce->bme))
391             {
392               ce->bme = bme->next;
393               if (NULL != it)
394                 {
395                   kc = bme->key;
396                   if (GNUNET_OK != it(it_cls, &kc, bme->value))
397                     {
398                       GNUNET_assert(--map->next_cache_off < NEXT_CACHE_SIZE);
399                       return GNUNET_SYSERR;
400                     }
401                 }
402               count++;
403             }
404         }
405     }
406   GNUNET_assert(--map->next_cache_off < NEXT_CACHE_SIZE);
407   return count;
408 }
409
410
411 /**
412  * We are about to free() the @a bme, make sure it is not in
413  * the list of next values for any iterator in the @a map's next_cache.
414  *
415  * @param map the map to check
416  * @param bme the entry that is about to be free'd
417  */
418 static void
419 update_next_cache_bme(struct GNUNET_CONTAINER_MultiHashMap *map,
420                       const struct BigMapEntry *bme)
421 {
422   for (unsigned int i = 0; i < map->next_cache_off; i++)
423     if (map->next_cache[i].bme == bme)
424       map->next_cache[i].bme = bme->next;
425 }
426
427
428 /**
429  * We are about to free() the @a sme, make sure it is not in
430  * the list of next values for any iterator in the @a map's next_cache.
431  *
432  * @param map the map to check
433  * @param sme the entry that is about to be free'd
434  */
435 static void
436 update_next_cache_sme(struct GNUNET_CONTAINER_MultiHashMap *map,
437                       const struct SmallMapEntry *sme)
438 {
439   for (unsigned int i = 0; i < map->next_cache_off; i++)
440     if (map->next_cache[i].sme == sme)
441       map->next_cache[i].sme = sme->next;
442 }
443
444
445 /**
446  * Remove the given key-value pair from the map.  Note that if the
447  * key-value pair is in the map multiple times, only one of the pairs
448  * will be removed.
449  *
450  * @param map the map
451  * @param key key of the key-value pair
452  * @param value value of the key-value pair
453  * @return #GNUNET_YES on success, #GNUNET_NO if the key-value pair
454  *  is not in the map
455  */
456 int
457 GNUNET_CONTAINER_multihashmap_remove(struct GNUNET_CONTAINER_MultiHashMap *map,
458                                      const struct GNUNET_HashCode *key,
459                                      const void *value)
460 {
461   union MapEntry me;
462   unsigned int i;
463
464   map->modification_counter++;
465
466   i = idx_of(map, key);
467   me = map->map[i];
468   if (map->use_small_entries)
469     {
470       struct SmallMapEntry *p;
471
472       p = NULL;
473       for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
474         {
475           if ((0 == GNUNET_memcmp(key, sme->key)) && (value == sme->value))
476             {
477               if (NULL == p)
478                 map->map[i].sme = sme->next;
479               else
480                 p->next = sme->next;
481               update_next_cache_sme(map, sme);
482               GNUNET_free(sme);
483               map->size--;
484               return GNUNET_YES;
485             }
486           p = sme;
487         }
488     }
489   else
490     {
491       struct BigMapEntry *p;
492
493       p = NULL;
494       for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
495         {
496           if ((0 == GNUNET_memcmp(key, &bme->key)) && (value == bme->value))
497             {
498               if (NULL == p)
499                 map->map[i].bme = bme->next;
500               else
501                 p->next = bme->next;
502               update_next_cache_bme(map, bme);
503               GNUNET_free(bme);
504               map->size--;
505               return GNUNET_YES;
506             }
507           p = bme;
508         }
509     }
510   return GNUNET_NO;
511 }
512
513
514 /**
515  * Remove all entries for the given key from the map.
516  * Note that the values would not be "freed".
517  *
518  * @param map the map
519  * @param key identifies values to be removed
520  * @return number of values removed
521  */
522 int
523 GNUNET_CONTAINER_multihashmap_remove_all(
524   struct GNUNET_CONTAINER_MultiHashMap *map,
525   const struct GNUNET_HashCode *key)
526 {
527   union MapEntry me;
528   unsigned int i;
529   int ret;
530
531   map->modification_counter++;
532
533   ret = 0;
534   i = idx_of(map, key);
535   me = map->map[i];
536   if (map->use_small_entries)
537     {
538       struct SmallMapEntry *sme;
539       struct SmallMapEntry *p;
540
541       p = NULL;
542       sme = me.sme;
543       while (NULL != sme)
544         {
545           if (0 == GNUNET_memcmp(key, sme->key))
546             {
547               if (NULL == p)
548                 map->map[i].sme = sme->next;
549               else
550                 p->next = sme->next;
551               update_next_cache_sme(map, sme);
552               GNUNET_free(sme);
553               map->size--;
554               if (NULL == p)
555                 sme = map->map[i].sme;
556               else
557                 sme = p->next;
558               ret++;
559             }
560           else
561             {
562               p = sme;
563               sme = sme->next;
564             }
565         }
566     }
567   else
568     {
569       struct BigMapEntry *bme;
570       struct BigMapEntry *p;
571
572       p = NULL;
573       bme = me.bme;
574       while (NULL != bme)
575         {
576           if (0 == GNUNET_memcmp(key, &bme->key))
577             {
578               if (NULL == p)
579                 map->map[i].bme = bme->next;
580               else
581                 p->next = bme->next;
582               update_next_cache_bme(map, bme);
583               GNUNET_free(bme);
584               map->size--;
585               if (NULL == p)
586                 bme = map->map[i].bme;
587               else
588                 bme = p->next;
589               ret++;
590             }
591           else
592             {
593               p = bme;
594               bme = bme->next;
595             }
596         }
597     }
598   return ret;
599 }
600
601
602 /**
603  * Callback used to remove all entries from the map.
604  *
605  * @param cls the `struct GNUNET_CONTAINER_MultiHashMap`
606  * @param key the key
607  * @param value the value
608  * @return #GNUNET_OK (continue to iterate)
609  */
610 static int
611 remove_all(void *cls, const struct GNUNET_HashCode *key, void *value)
612 {
613   struct GNUNET_CONTAINER_MultiHashMap *map = cls;
614
615   GNUNET_assert(GNUNET_YES ==
616                 GNUNET_CONTAINER_multihashmap_remove(map, key, value));
617   return GNUNET_OK;
618 }
619
620
621 /**
622  * @ingroup hashmap
623  * Remove all entries from the map.
624  * Note that the values would not be "freed".
625  *
626  * @param map the map
627  * @return number of values removed
628  */
629 unsigned int
630 GNUNET_CONTAINER_multihashmap_clear(struct GNUNET_CONTAINER_MultiHashMap *map)
631 {
632   unsigned int ret;
633
634   ret = map->size;
635   GNUNET_CONTAINER_multihashmap_iterate(map, &remove_all, map);
636   return ret;
637 }
638
639
640 /**
641  * Check if the map contains any value under the given
642  * key (including values that are NULL).
643  *
644  * @param map the map
645  * @param key the key to test if a value exists for it
646  * @return #GNUNET_YES if such a value exists,
647  *         #GNUNET_NO if not
648  */
649 int
650 GNUNET_CONTAINER_multihashmap_contains(
651   const struct GNUNET_CONTAINER_MultiHashMap *map,
652   const struct GNUNET_HashCode *key)
653 {
654   union MapEntry me;
655
656   me = map->map[idx_of(map, key)];
657   if (map->use_small_entries)
658     {
659       struct SmallMapEntry *sme;
660
661       for (sme = me.sme; NULL != sme; sme = sme->next)
662         if (0 == GNUNET_memcmp(key, sme->key))
663           return GNUNET_YES;
664     }
665   else
666     {
667       struct BigMapEntry *bme;
668
669       for (bme = me.bme; NULL != bme; bme = bme->next)
670         if (0 == GNUNET_memcmp(key, &bme->key))
671           return GNUNET_YES;
672     }
673   return GNUNET_NO;
674 }
675
676
677 /**
678  * Check if the map contains the given value under the given
679  * key.
680  *
681  * @param map the map
682  * @param key the key to test if a value exists for it
683  * @param value value to test for
684  * @return #GNUNET_YES if such a value exists,
685  *         #GNUNET_NO if not
686  */
687 int
688 GNUNET_CONTAINER_multihashmap_contains_value(
689   const struct GNUNET_CONTAINER_MultiHashMap *map,
690   const struct GNUNET_HashCode *key,
691   const void *value)
692 {
693   union MapEntry me;
694
695   me = map->map[idx_of(map, key)];
696   if (map->use_small_entries)
697     {
698       struct SmallMapEntry *sme;
699
700       for (sme = me.sme; NULL != sme; sme = sme->next)
701         if ((0 == GNUNET_memcmp(key, sme->key)) && (sme->value == value))
702           return GNUNET_YES;
703     }
704   else
705     {
706       struct BigMapEntry *bme;
707
708       for (bme = me.bme; NULL != bme; bme = bme->next)
709         if ((0 == GNUNET_memcmp(key, &bme->key)) && (bme->value == value))
710           return GNUNET_YES;
711     }
712   return GNUNET_NO;
713 }
714
715
716 /**
717  * Grow the given map to a more appropriate size.
718  *
719  * @param map the hash map to grow
720  */
721 static void
722 grow(struct GNUNET_CONTAINER_MultiHashMap *map)
723 {
724   union MapEntry *old_map;
725   union MapEntry *new_map;
726   unsigned int old_len;
727   unsigned int new_len;
728   unsigned int idx;
729
730   old_map = map->map;
731   old_len = map->map_length;
732   GNUNET_assert(0 != old_len);
733   new_len = old_len * 2;
734   if (0 == new_len) /* 2^31 * 2 == 0 */
735     new_len = old_len; /* never use 0 */
736   if (new_len == old_len)
737     return; /* nothing changed */
738   new_map = GNUNET_malloc_large(new_len * sizeof(union MapEntry));
739   if (NULL == new_map)
740     return; /* grow not possible */
741   map->modification_counter++;
742   map->map_length = new_len;
743   map->map = new_map;
744   for (unsigned int i = 0; i < old_len; i++)
745     {
746       if (map->use_small_entries)
747         {
748           struct SmallMapEntry *sme;
749
750           while (NULL != (sme = old_map[i].sme))
751             {
752               old_map[i].sme = sme->next;
753               idx = idx_of(map, sme->key);
754               sme->next = new_map[idx].sme;
755               new_map[idx].sme = sme;
756             }
757         }
758       else
759         {
760           struct BigMapEntry *bme;
761
762           while (NULL != (bme = old_map[i].bme))
763             {
764               old_map[i].bme = bme->next;
765               idx = idx_of(map, &bme->key);
766               bme->next = new_map[idx].bme;
767               new_map[idx].bme = bme;
768             }
769         }
770     }
771   GNUNET_free(old_map);
772 }
773
774
775 /**
776  * Store a key-value pair in the map.
777  *
778  * @param map the map
779  * @param key key to use
780  * @param value value to use
781  * @param opt options for put
782  * @return #GNUNET_OK on success,
783  *         #GNUNET_NO if a value was replaced (with REPLACE)
784  *         #GNUNET_SYSERR if UNIQUE_ONLY was the option and the
785  *                       value already exists
786  */
787 int
788 GNUNET_CONTAINER_multihashmap_put(struct GNUNET_CONTAINER_MultiHashMap *map,
789                                   const struct GNUNET_HashCode *key,
790                                   void *value,
791                                   enum GNUNET_CONTAINER_MultiHashMapOption opt)
792 {
793   union MapEntry me;
794   unsigned int i;
795
796   i = idx_of(map, key);
797   if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
798       (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
799     {
800       me = map->map[i];
801       if (map->use_small_entries)
802         {
803           struct SmallMapEntry *sme;
804
805           for (sme = me.sme; NULL != sme; sme = sme->next)
806             if (0 == GNUNET_memcmp(key, sme->key))
807               {
808                 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
809                   return GNUNET_SYSERR;
810                 sme->value = value;
811                 return GNUNET_NO;
812               }
813         }
814       else
815         {
816           struct BigMapEntry *bme;
817
818           for (bme = me.bme; NULL != bme; bme = bme->next)
819             if (0 == GNUNET_memcmp(key, &bme->key))
820               {
821                 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
822                   return GNUNET_SYSERR;
823                 bme->value = value;
824                 return GNUNET_NO;
825               }
826         }
827     }
828   if (map->size / 3 >= map->map_length / 4)
829     {
830       grow(map);
831       i = idx_of(map, key);
832     }
833   if (map->use_small_entries)
834     {
835       struct SmallMapEntry *sme;
836
837       sme = GNUNET_new(struct SmallMapEntry);
838       sme->key = key;
839       sme->value = value;
840       sme->next = map->map[i].sme;
841       map->map[i].sme = sme;
842     }
843   else
844     {
845       struct BigMapEntry *bme;
846
847       bme = GNUNET_new(struct BigMapEntry);
848       bme->key = *key;
849       bme->value = value;
850       bme->next = map->map[i].bme;
851       map->map[i].bme = bme;
852     }
853   map->size++;
854   return GNUNET_OK;
855 }
856
857
858 /**
859  * Iterate over all entries in the map that match a particular key.
860  *
861  * @param map the map
862  * @param key key that the entries must correspond to
863  * @param it function to call on each entry
864  * @param it_cls extra argument to it
865  * @return the number of key value pairs processed,
866  *         #GNUNET_SYSERR if it aborted iteration
867  */
868 int
869 GNUNET_CONTAINER_multihashmap_get_multiple(
870   struct GNUNET_CONTAINER_MultiHashMap *map,
871   const struct GNUNET_HashCode *key,
872   GNUNET_CONTAINER_MulitHashMapIteratorCallback it,
873   void *it_cls)
874 {
875   int count;
876   union MapEntry *me;
877   union MapEntry *ce;
878
879   ce = &map->next_cache[map->next_cache_off];
880   GNUNET_assert(++map->next_cache_off < NEXT_CACHE_SIZE);
881   count = 0;
882   me = &map->map[idx_of(map, key)];
883   if (map->use_small_entries)
884     {
885       struct SmallMapEntry *sme;
886
887       ce->sme = me->sme;
888       while (NULL != (sme = ce->sme))
889         {
890           ce->sme = sme->next;
891           if (0 != GNUNET_memcmp(key, sme->key))
892             continue;
893           if ((NULL != it) && (GNUNET_OK != it(it_cls, key, sme->value)))
894             {
895               GNUNET_assert(--map->next_cache_off < NEXT_CACHE_SIZE);
896               return GNUNET_SYSERR;
897             }
898           count++;
899         }
900     }
901   else
902     {
903       struct BigMapEntry *bme;
904
905       ce->bme = me->bme;
906       while (NULL != (bme = ce->bme))
907         {
908           ce->bme = bme->next;
909           if (0 != GNUNET_memcmp(key, &bme->key))
910             continue;
911           if ((NULL != it) && (GNUNET_OK != it(it_cls, key, bme->value)))
912             {
913               GNUNET_assert(--map->next_cache_off < NEXT_CACHE_SIZE);
914               return GNUNET_SYSERR;
915             }
916           count++;
917         }
918     }
919   GNUNET_assert(--map->next_cache_off < NEXT_CACHE_SIZE);
920   return count;
921 }
922
923
924 /**
925  * @ingroup hashmap
926  * Call @a it on a random value from the map, or not at all
927  * if the map is empty. Note that this function has linear
928  * complexity (in the size of the map).
929  *
930  * @param map the map
931  * @param it function to call on a random entry
932  * @param it_cls extra argument to @a it
933  * @return the number of key value pairs processed, zero or one.
934  */
935 unsigned int
936 GNUNET_CONTAINER_multihashmap_get_random(
937   const struct GNUNET_CONTAINER_MultiHashMap *map,
938   GNUNET_CONTAINER_MulitHashMapIteratorCallback it,
939   void *it_cls)
940 {
941   unsigned int off;
942   unsigned int idx;
943   union MapEntry me;
944
945   if (0 == map->size)
946     return 0;
947   if (NULL == it)
948     return 1;
949   off = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_NONCE, map->size);
950   for (idx = 0; idx < map->map_length; idx++)
951     {
952       me = map->map[idx];
953       if (map->use_small_entries)
954         {
955           struct SmallMapEntry *sme;
956           struct SmallMapEntry *nxt;
957
958           nxt = me.sme;
959           while (NULL != (sme = nxt))
960             {
961               nxt = sme->next;
962               if (0 == off)
963                 {
964                   if (GNUNET_OK != it(it_cls, sme->key, sme->value))
965                     return GNUNET_SYSERR;
966                   return 1;
967                 }
968               off--;
969             }
970         }
971       else
972         {
973           struct BigMapEntry *bme;
974           struct BigMapEntry *nxt;
975
976           nxt = me.bme;
977           while (NULL != (bme = nxt))
978             {
979               nxt = bme->next;
980               if (0 == off)
981                 {
982                   if (GNUNET_OK != it(it_cls, &bme->key, bme->value))
983                     return GNUNET_SYSERR;
984                   return 1;
985                 }
986               off--;
987             }
988         }
989     }
990   GNUNET_break(0);
991   return GNUNET_SYSERR;
992 }
993
994
995 /**
996  * Create an iterator for a multihashmap.
997  * The iterator can be used to retrieve all the elements in the multihashmap
998  * one by one, without having to handle all elements at once (in contrast to
999  * GNUNET_CONTAINER_multihashmap_iterate()).  Note that the iterator can not be
1000  * used anymore if elements have been removed from 'map' after the creation of
1001  * the iterator, or 'map' has been destroyed.  Adding elements to 'map' may
1002  * result in skipped or repeated elements.
1003  *
1004  * @param map the map to create an iterator for
1005  * @return an iterator over the given multihashmap 'map'
1006  */
1007 struct GNUNET_CONTAINER_MultiHashMapIterator *
1008 GNUNET_CONTAINER_multihashmap_iterator_create(
1009   const struct GNUNET_CONTAINER_MultiHashMap *map)
1010 {
1011   struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
1012
1013   iter = GNUNET_new(struct GNUNET_CONTAINER_MultiHashMapIterator);
1014   iter->map = map;
1015   iter->modification_counter = map->modification_counter;
1016   iter->me = map->map[0];
1017   return iter;
1018 }
1019
1020
1021 /**
1022  * Retrieve the next element from the hash map at the iterator's position.
1023  * If there are no elements left, GNUNET_NO is returned, and 'key' and 'value'
1024  * are not modified.
1025  * This operation is only allowed if no elements have been removed from the
1026  * multihashmap since the creation of 'iter', and the map has not been destroyed.
1027  * Adding elements may result in repeating or skipping elements.
1028  *
1029  * @param iter the iterator to get the next element from
1030  * @param key pointer to store the key in, can be NULL
1031  * @param value pointer to store the value in, can be NULL
1032  * @return #GNUNET_YES we returned an element,
1033  *         #GNUNET_NO if we are out of elements
1034  */
1035 int
1036 GNUNET_CONTAINER_multihashmap_iterator_next(
1037   struct GNUNET_CONTAINER_MultiHashMapIterator *iter,
1038   struct GNUNET_HashCode *key,
1039   const void **value)
1040 {
1041   /* make sure the map has not been modified */
1042   GNUNET_assert(iter->modification_counter == iter->map->modification_counter);
1043
1044   /* look for the next entry, skipping empty buckets */
1045   while (1)
1046     {
1047       if (iter->idx >= iter->map->map_length)
1048         return GNUNET_NO;
1049       if (GNUNET_YES == iter->map->use_small_entries)
1050         {
1051           if (NULL != iter->me.sme)
1052             {
1053               if (NULL != key)
1054                 *key = *iter->me.sme->key;
1055               if (NULL != value)
1056                 *value = iter->me.sme->value;
1057               iter->me.sme = iter->me.sme->next;
1058               return GNUNET_YES;
1059             }
1060         }
1061       else
1062         {
1063           if (NULL != iter->me.bme)
1064             {
1065               if (NULL != key)
1066                 *key = iter->me.bme->key;
1067               if (NULL != value)
1068                 *value = iter->me.bme->value;
1069               iter->me.bme = iter->me.bme->next;
1070               return GNUNET_YES;
1071             }
1072         }
1073       iter->idx += 1;
1074       if (iter->idx < iter->map->map_length)
1075         iter->me = iter->map->map[iter->idx];
1076     }
1077 }
1078
1079
1080 /**
1081  * Destroy a multihashmap iterator.
1082  *
1083  * @param iter the iterator to destroy
1084  */
1085 void
1086 GNUNET_CONTAINER_multihashmap_iterator_destroy(
1087   struct GNUNET_CONTAINER_MultiHashMapIterator *iter)
1088 {
1089   GNUNET_free(iter);
1090 }
1091
1092
1093 /* end of container_multihashmap.c */