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