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