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