fix socket cmp, fix compiler warnings about unused args
[oweals/gnunet.git] / src / util / container_multishortmap.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_multishortmap.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_util_lib.h"
28
29 #define LOG(kind, ...) \
30   GNUNET_log_from (kind, "util-container-multishortmap", __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_ShortHashCode 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_ShortHashCode *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_MultiShortmap
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 multishortmap.
153  * Allows to enumerate elements asynchronously.
154  */
155 struct GNUNET_CONTAINER_MultiShortmapIterator
156 {
157   /**
158    * Position in the bucket '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_MultiShortmap *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_MultiShortmap *
196 GNUNET_CONTAINER_multishortmap_create (unsigned int len, int do_not_copy_keys)
197 {
198   struct GNUNET_CONTAINER_MultiShortmap *map;
199
200   GNUNET_assert (len > 0);
201   map = GNUNET_new (struct GNUNET_CONTAINER_MultiShortmap);
202   map->map = GNUNET_malloc_large (len * sizeof (union MapEntry));
203   if (NULL == map->map)
204   {
205     GNUNET_free (map);
206     return NULL;
207   }
208   map->map_length = len;
209   map->use_small_entries = do_not_copy_keys;
210   return map;
211 }
212
213
214 /**
215  * Destroy a hash map.  Will not free any values
216  * stored in the hash map!
217  *
218  * @param map the map
219  */
220 void
221 GNUNET_CONTAINER_multishortmap_destroy (
222   struct GNUNET_CONTAINER_MultiShortmap *map)
223 {
224   GNUNET_assert (0 == map->next_cache_off);
225   for (unsigned int i = 0; i < map->map_length; i++)
226   {
227     union MapEntry me;
228
229     me = map->map[i];
230     if (map->use_small_entries)
231     {
232       struct SmallMapEntry *sme;
233       struct SmallMapEntry *nxt;
234
235       nxt = me.sme;
236       while (NULL != (sme = nxt))
237       {
238         nxt = sme->next;
239         GNUNET_free (sme);
240       }
241       me.sme = NULL;
242     }
243     else
244     {
245       struct BigMapEntry *bme;
246       struct BigMapEntry *nxt;
247
248       nxt = me.bme;
249       while (NULL != (bme = nxt))
250       {
251         nxt = bme->next;
252         GNUNET_free (bme);
253       }
254       me.bme = NULL;
255     }
256   }
257   GNUNET_free (map->map);
258   GNUNET_free (map);
259 }
260
261
262 /**
263  * Compute the index of the bucket for the given key.
264  *
265  * @param map hash map for which to compute the index
266  * @param key what key should the index be computed for
267  * @return offset into the "map" array of "map"
268  */
269 static unsigned int
270 idx_of (const struct GNUNET_CONTAINER_MultiShortmap *map,
271         const struct GNUNET_ShortHashCode *key)
272 {
273   unsigned int kx;
274
275   GNUNET_assert (NULL != map);
276   GNUNET_memcpy (&kx, key, sizeof (kx));
277   return kx % map->map_length;
278 }
279
280
281 /**
282  * Get the number of key-value pairs in the map.
283  *
284  * @param map the map
285  * @return the number of key value pairs
286  */
287 unsigned int
288 GNUNET_CONTAINER_multishortmap_size (
289   const struct GNUNET_CONTAINER_MultiShortmap *map)
290 {
291   return map->size;
292 }
293
294
295 /**
296  * Given a key find a value in the map matching the key.
297  *
298  * @param map the map
299  * @param key what to look for
300  * @return NULL if no value was found; note that
301  *   this is indistinguishable from values that just
302  *   happen to be NULL; use "contains" to test for
303  *   key-value pairs with value NULL
304  */
305 void *
306 GNUNET_CONTAINER_multishortmap_get (
307   const struct GNUNET_CONTAINER_MultiShortmap *map,
308   const struct GNUNET_ShortHashCode *key)
309 {
310   union MapEntry me;
311
312   me = map->map[idx_of (map, key)];
313   if (map->use_small_entries)
314   {
315     for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
316       if (0 == GNUNET_memcmp (key, sme->key))
317         return sme->value;
318   }
319   else
320   {
321     for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
322       if (0 == GNUNET_memcmp (key, &bme->key))
323         return bme->value;
324   }
325   return NULL;
326 }
327
328
329 /**
330  * Iterate over all entries in the map.
331  *
332  * @param map the map
333  * @param it function to call on each entry
334  * @param it_cls extra argument to @a it
335  * @return the number of key value pairs processed,
336  *         #GNUNET_SYSERR if it aborted iteration
337  */
338 int
339 GNUNET_CONTAINER_multishortmap_iterate (
340   struct GNUNET_CONTAINER_MultiShortmap *map,
341   GNUNET_CONTAINER_ShortmapIterator it,
342   void *it_cls)
343 {
344   int count;
345   union MapEntry me;
346   union MapEntry *ce;
347   struct GNUNET_ShortHashCode kc;
348
349   count = 0;
350   GNUNET_assert (NULL != map);
351   ce = &map->next_cache[map->next_cache_off];
352   GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
353   for (unsigned int i = 0; i < map->map_length; i++)
354   {
355     me = map->map[i];
356     if (map->use_small_entries)
357     {
358       struct SmallMapEntry *sme;
359
360       ce->sme = me.sme;
361       while (NULL != (sme = ce->sme))
362       {
363         ce->sme = sme->next;
364         if ((NULL != it) && (GNUNET_OK != it (it_cls, sme->key, sme->value)))
365         {
366           GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
367           return GNUNET_SYSERR;
368         }
369         count++;
370       }
371     }
372     else
373     {
374       struct BigMapEntry *bme;
375
376       ce->bme = me.bme;
377       while (NULL != (bme = ce->bme))
378       {
379         ce->bme = bme->next;
380         if (NULL != it)
381         {
382           kc = bme->key;
383           if (GNUNET_OK != it (it_cls, &kc, bme->value))
384           {
385             GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
386             return GNUNET_SYSERR;
387           }
388         }
389         count++;
390       }
391     }
392   }
393   GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
394   return count;
395 }
396
397
398 /**
399  * We are about to free() the @a bme, make sure it is not in
400  * the list of next values for any iterator in the @a map's next_cache.
401  *
402  * @param map the map to check
403  * @param bme the entry that is about to be free'd
404  */
405 static void
406 update_next_cache_bme (struct GNUNET_CONTAINER_MultiShortmap *map,
407                        const struct BigMapEntry *bme)
408 {
409   for (unsigned int i = 0; i < map->next_cache_off; i++)
410     if (map->next_cache[i].bme == bme)
411       map->next_cache[i].bme = bme->next;
412 }
413
414
415 /**
416  * We are about to free() the @a sme, make sure it is not in
417  * the list of next values for any iterator in the @a map's next_cache.
418  *
419  * @param map the map to check
420  * @param sme the entry that is about to be free'd
421  */
422 static void
423 update_next_cache_sme (struct GNUNET_CONTAINER_MultiShortmap *map,
424                        const struct SmallMapEntry *sme)
425 {
426   for (unsigned int i = 0; i < map->next_cache_off; i++)
427     if (map->next_cache[i].sme == sme)
428       map->next_cache[i].sme = sme->next;
429 }
430
431
432 /**
433  * Remove the given key-value pair from the map.  Note that if the
434  * key-value pair is in the map multiple times, only one of the pairs
435  * will be removed.
436  *
437  * @param map the map
438  * @param key key of the key-value pair
439  * @param value value of the key-value pair
440  * @return #GNUNET_YES on success, #GNUNET_NO if the key-value pair
441  *  is not in the map
442  */
443 int
444 GNUNET_CONTAINER_multishortmap_remove (
445   struct GNUNET_CONTAINER_MultiShortmap *map,
446   const struct GNUNET_ShortHashCode *key,
447   const void *value)
448 {
449   union MapEntry me;
450   unsigned int i;
451
452   map->modification_counter++;
453   i = idx_of (map, key);
454   me = map->map[i];
455   if (map->use_small_entries)
456   {
457     struct SmallMapEntry *p = NULL;
458
459     for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
460     {
461       if ((0 == GNUNET_memcmp (key, sme->key)) && (value == sme->value))
462       {
463         if (NULL == p)
464           map->map[i].sme = sme->next;
465         else
466           p->next = sme->next;
467         update_next_cache_sme (map, sme);
468         GNUNET_free (sme);
469         map->size--;
470         return GNUNET_YES;
471       }
472       p = sme;
473     }
474   }
475   else
476   {
477     struct BigMapEntry *p = NULL;
478
479     for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
480     {
481       if ((0 == GNUNET_memcmp (key, &bme->key)) && (value == bme->value))
482       {
483         if (NULL == p)
484           map->map[i].bme = bme->next;
485         else
486           p->next = bme->next;
487         update_next_cache_bme (map, bme);
488         GNUNET_free (bme);
489         map->size--;
490         return GNUNET_YES;
491       }
492       p = bme;
493     }
494   }
495   return GNUNET_NO;
496 }
497
498
499 /**
500  * Remove all entries for the given key from the map.
501  * Note that the values would not be "freed".
502  *
503  * @param map the map
504  * @param key identifies values to be removed
505  * @return number of values removed
506  */
507 int
508 GNUNET_CONTAINER_multishortmap_remove_all (
509   struct GNUNET_CONTAINER_MultiShortmap *map,
510   const struct GNUNET_ShortHashCode *key)
511 {
512   union MapEntry me;
513   unsigned int i;
514   int ret;
515
516   map->modification_counter++;
517
518   ret = 0;
519   i = idx_of (map, key);
520   me = map->map[i];
521   if (map->use_small_entries)
522   {
523     struct SmallMapEntry *sme;
524     struct SmallMapEntry *p;
525
526     p = NULL;
527     sme = me.sme;
528     while (NULL != sme)
529     {
530       if (0 == GNUNET_memcmp (key, sme->key))
531       {
532         if (NULL == p)
533           map->map[i].sme = sme->next;
534         else
535           p->next = sme->next;
536         update_next_cache_sme (map, sme);
537         GNUNET_free (sme);
538         map->size--;
539         if (NULL == p)
540           sme = map->map[i].sme;
541         else
542           sme = p->next;
543         ret++;
544       }
545       else
546       {
547         p = sme;
548         sme = sme->next;
549       }
550     }
551   }
552   else
553   {
554     struct BigMapEntry *bme;
555     struct BigMapEntry *p;
556
557     p = NULL;
558     bme = me.bme;
559     while (NULL != bme)
560     {
561       if (0 == GNUNET_memcmp (key, &bme->key))
562       {
563         if (NULL == p)
564           map->map[i].bme = bme->next;
565         else
566           p->next = bme->next;
567         update_next_cache_bme (map, bme);
568         GNUNET_free (bme);
569         map->size--;
570         if (NULL == p)
571           bme = map->map[i].bme;
572         else
573           bme = p->next;
574         ret++;
575       }
576       else
577       {
578         p = bme;
579         bme = bme->next;
580       }
581     }
582   }
583   return ret;
584 }
585
586
587 /**
588  * Check if the map contains any value under the given
589  * key (including values that are NULL).
590  *
591  * @param map the map
592  * @param key the key to test if a value exists for it
593  * @return #GNUNET_YES if such a value exists,
594  *         #GNUNET_NO if not
595  */
596 int
597 GNUNET_CONTAINER_multishortmap_contains (
598   const struct GNUNET_CONTAINER_MultiShortmap *map,
599   const struct GNUNET_ShortHashCode *key)
600 {
601   union MapEntry me;
602
603   me = map->map[idx_of (map, key)];
604   if (map->use_small_entries)
605   {
606     for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
607       if (0 == GNUNET_memcmp (key, sme->key))
608         return GNUNET_YES;
609   }
610   else
611   {
612     for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
613       if (0 == GNUNET_memcmp (key, &bme->key))
614         return GNUNET_YES;
615   }
616   return GNUNET_NO;
617 }
618
619
620 /**
621  * Check if the map contains the given value under the given
622  * key.
623  *
624  * @param map the map
625  * @param key the key to test if a value exists for it
626  * @param value value to test for
627  * @return #GNUNET_YES if such a value exists,
628  *         #GNUNET_NO if not
629  */
630 int
631 GNUNET_CONTAINER_multishortmap_contains_value (
632   const struct GNUNET_CONTAINER_MultiShortmap *map,
633   const struct GNUNET_ShortHashCode *key,
634   const void *value)
635 {
636   union MapEntry me;
637
638   me = map->map[idx_of (map, key)];
639   if (map->use_small_entries)
640   {
641     for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
642       if ((0 == GNUNET_memcmp (key, sme->key)) && (sme->value == value))
643         return GNUNET_YES;
644   }
645   else
646   {
647     for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
648       if ((0 == GNUNET_memcmp (key, &bme->key)) && (bme->value == value))
649         return GNUNET_YES;
650   }
651   return GNUNET_NO;
652 }
653
654
655 /**
656  * Grow the given map to a more appropriate size.
657  *
658  * @param map the hash map to grow
659  */
660 static void
661 grow (struct GNUNET_CONTAINER_MultiShortmap *map)
662 {
663   union MapEntry *old_map;
664   union MapEntry *new_map;
665   unsigned int old_len;
666   unsigned int new_len;
667   unsigned int idx;
668
669   old_map = map->map;
670   old_len = map->map_length;
671   new_len = old_len * 2;
672   if (0 == new_len) /* 2^31 * 2 == 0 */
673     new_len = old_len; /* never use 0 */
674   if (new_len == old_len)
675     return; /* nothing changed */
676   new_map = GNUNET_malloc_large (new_len * sizeof (union MapEntry));
677   if (NULL == new_map)
678     return; /* grow not possible */
679   map->modification_counter++;
680   map->map_length = new_len;
681   map->map = new_map;
682   for (unsigned int i = 0; i < old_len; i++)
683   {
684     if (map->use_small_entries)
685     {
686       struct SmallMapEntry *sme;
687
688       while (NULL != (sme = old_map[i].sme))
689       {
690         old_map[i].sme = sme->next;
691         idx = idx_of (map, sme->key);
692         sme->next = new_map[idx].sme;
693         new_map[idx].sme = sme;
694       }
695     }
696     else
697     {
698       struct BigMapEntry *bme;
699
700       while (NULL != (bme = old_map[i].bme))
701       {
702         old_map[i].bme = bme->next;
703         idx = idx_of (map, &bme->key);
704         bme->next = new_map[idx].bme;
705         new_map[idx].bme = bme;
706       }
707     }
708   }
709   GNUNET_free (old_map);
710 }
711
712
713 /**
714  * Store a key-value pair in the map.
715  *
716  * @param map the map
717  * @param key key to use
718  * @param value value to use
719  * @param opt options for put
720  * @return #GNUNET_OK on success,
721  *         #GNUNET_NO if a value was replaced (with REPLACE)
722  *         #GNUNET_SYSERR if #GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY was the option and the
723  *                       value already exists
724  */
725 int
726 GNUNET_CONTAINER_multishortmap_put (
727   struct GNUNET_CONTAINER_MultiShortmap *map,
728   const struct GNUNET_ShortHashCode *key,
729   void *value,
730   enum GNUNET_CONTAINER_MultiHashMapOption opt)
731 {
732   union MapEntry me;
733   unsigned int i;
734
735   i = idx_of (map, key);
736   if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
737       (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
738   {
739     me = map->map[i];
740     if (map->use_small_entries)
741     {
742       for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
743         if (0 == GNUNET_memcmp (key, sme->key))
744         {
745           if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
746             return GNUNET_SYSERR;
747           sme->value = value;
748           return GNUNET_NO;
749         }
750     }
751     else
752     {
753       for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
754         if (0 == GNUNET_memcmp (key, &bme->key))
755         {
756           if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
757             return GNUNET_SYSERR;
758           bme->value = value;
759           return GNUNET_NO;
760         }
761     }
762   }
763   if (map->size / 3 >= map->map_length / 4)
764   {
765     grow (map);
766     i = idx_of (map, key);
767   }
768   if (map->use_small_entries)
769   {
770     struct SmallMapEntry *sme;
771
772     sme = GNUNET_new (struct SmallMapEntry);
773     sme->key = key;
774     sme->value = value;
775     sme->next = map->map[i].sme;
776     map->map[i].sme = sme;
777   }
778   else
779   {
780     struct BigMapEntry *bme;
781
782     bme = GNUNET_new (struct BigMapEntry);
783     bme->key = *key;
784     bme->value = value;
785     bme->next = map->map[i].bme;
786     map->map[i].bme = bme;
787   }
788   map->size++;
789   return GNUNET_OK;
790 }
791
792
793 /**
794  * Iterate over all entries in the map that match a particular key.
795  *
796  * @param map the map
797  * @param key key that the entries must correspond to
798  * @param it function to call on each entry
799  * @param it_cls extra argument to @a it
800  * @return the number of key value pairs processed,
801  *         #GNUNET_SYSERR if it aborted iteration
802  */
803 int
804 GNUNET_CONTAINER_multishortmap_get_multiple (
805   struct GNUNET_CONTAINER_MultiShortmap *map,
806   const struct GNUNET_ShortHashCode *key,
807   GNUNET_CONTAINER_ShortmapIterator it,
808   void *it_cls)
809 {
810   int count;
811   union MapEntry me;
812   union MapEntry *ce;
813
814   ce = &map->next_cache[map->next_cache_off];
815   GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
816   count = 0;
817   me = map->map[idx_of (map, key)];
818   if (map->use_small_entries)
819   {
820     struct SmallMapEntry *sme;
821
822     ce->sme = me.sme;
823     while (NULL != (sme = ce->sme))
824     {
825       ce->sme = sme->next;
826       if (0 != GNUNET_memcmp (key, sme->key))
827         continue;
828       if ((NULL != it) && (GNUNET_OK != it (it_cls, key, sme->value)))
829       {
830         GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
831         return GNUNET_SYSERR;
832       }
833       count++;
834     }
835   }
836   else
837   {
838     struct BigMapEntry *bme;
839
840     ce->bme = me.bme;
841     while (NULL != (bme = ce->bme))
842     {
843       ce->bme = bme->next;
844       if (0 != GNUNET_memcmp (key, &bme->key))
845         continue;
846       if ((NULL != it) && (GNUNET_OK != it (it_cls, key, bme->value)))
847       {
848         GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
849         return GNUNET_SYSERR;
850       }
851       count++;
852     }
853   }
854   GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
855   return count;
856 }
857
858
859 /**
860  * @ingroup hashmap
861  * Call @a it on a random value from the map, or not at all
862  * if the map is empty.  Note that this function has linear
863  * complexity (in the size of the map).
864  *
865  * @param map the map
866  * @param it function to call on a random entry
867  * @param it_cls extra argument to @a it
868  * @return the number of key value pairs processed, zero or one.
869  */
870 unsigned int
871 GNUNET_CONTAINER_multishortmap_get_random (
872   const struct GNUNET_CONTAINER_MultiShortmap *map,
873   GNUNET_CONTAINER_ShortmapIterator it,
874   void *it_cls)
875 {
876   unsigned int off;
877   union MapEntry me;
878
879   if (0 == map->size)
880     return 0;
881   if (NULL == it)
882     return 1;
883   off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, map->size);
884   for (unsigned int idx = 0; idx < map->map_length; idx++)
885   {
886     me = map->map[idx];
887     if (map->use_small_entries)
888     {
889       for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
890       {
891         if (0 == off)
892         {
893           if (GNUNET_OK != it (it_cls, sme->key, sme->value))
894             return GNUNET_SYSERR;
895           return 1;
896         }
897         off--;
898       }
899     }
900     else
901     {
902       for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
903       {
904         if (0 == off)
905         {
906           if (GNUNET_OK != it (it_cls, &bme->key, bme->value))
907             return GNUNET_SYSERR;
908           return 1;
909         }
910         off--;
911       }
912     }
913   }
914   GNUNET_break (0);
915   return GNUNET_SYSERR;
916 }
917
918
919 /**
920  * Create an iterator for a multishortmap.
921  * The iterator can be used to retrieve all the elements in the multishortmap
922  * one by one, without having to handle all elements at once (in contrast to
923  * #GNUNET_CONTAINER_multishortmap_iterate).  Note that the iterator can not be
924  * used anymore if elements have been removed from 'map' after the creation of
925  * the iterator, or 'map' has been destroyed.  Adding elements to 'map' may
926  * result in skipped or repeated elements.
927  *
928  * @param map the map to create an iterator for
929  * @return an iterator over the given multishortmap 'map'
930  */
931 struct GNUNET_CONTAINER_MultiShortmapIterator *
932 GNUNET_CONTAINER_multishortmap_iterator_create (
933   const struct GNUNET_CONTAINER_MultiShortmap *map)
934 {
935   struct GNUNET_CONTAINER_MultiShortmapIterator *iter;
936
937   iter = GNUNET_new (struct GNUNET_CONTAINER_MultiShortmapIterator);
938   iter->map = map;
939   iter->modification_counter = map->modification_counter;
940   iter->me = map->map[0];
941   return iter;
942 }
943
944
945 /**
946  * Retrieve the next element from the hash map at the iterator's position.
947  * If there are no elements left, GNUNET_NO is returned, and 'key' and 'value'
948  * are not modified.
949  * This operation is only allowed if no elements have been removed from the
950  * multishortmap since the creation of 'iter', and the map has not been destroyed.
951  * Adding elements may result in repeating or skipping elements.
952  *
953  * @param iter the iterator to get the next element from
954  * @param key pointer to store the key in, can be NULL
955  * @param value pointer to store the value in, can be NULL
956  * @return #GNUNET_YES we returned an element,
957  *         #GNUNET_NO if we are out of elements
958  */
959 int
960 GNUNET_CONTAINER_multishortmap_iterator_next (
961   struct GNUNET_CONTAINER_MultiShortmapIterator *iter,
962   struct GNUNET_ShortHashCode *key,
963   const void **value)
964 {
965   /* make sure the map has not been modified */
966   GNUNET_assert (iter->modification_counter == iter->map->modification_counter);
967
968   /* look for the next entry, skipping empty buckets */
969   while (1)
970   {
971     if (iter->idx >= iter->map->map_length)
972       return GNUNET_NO;
973     if (GNUNET_YES == iter->map->use_small_entries)
974     {
975       if (NULL != iter->me.sme)
976       {
977         if (NULL != key)
978           *key = *iter->me.sme->key;
979         if (NULL != value)
980           *value = iter->me.sme->value;
981         iter->me.sme = iter->me.sme->next;
982         return GNUNET_YES;
983       }
984     }
985     else
986     {
987       if (NULL != iter->me.bme)
988       {
989         if (NULL != key)
990           *key = iter->me.bme->key;
991         if (NULL != value)
992           *value = iter->me.bme->value;
993         iter->me.bme = iter->me.bme->next;
994         return GNUNET_YES;
995       }
996     }
997     iter->idx += 1;
998     if (iter->idx < iter->map->map_length)
999       iter->me = iter->map->map[iter->idx];
1000   }
1001 }
1002
1003
1004 /**
1005  * Destroy a multishortmap iterator.
1006  *
1007  * @param iter the iterator to destroy
1008  */
1009 void
1010 GNUNET_CONTAINER_multishortmap_iterator_destroy (
1011   struct GNUNET_CONTAINER_MultiShortmapIterator *iter)
1012 {
1013   GNUNET_free (iter);
1014 }
1015
1016
1017 /* end of container_multishortmap.c */