remove get_random from API
[oweals/gnunet.git] / src / util / container_multihashmap.c
1 /*
2      This file is part of GNUnet.
3      (C) 2008 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
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_common.h"
28 #include "gnunet_container_lib.h"
29 #include "gnunet_crypto_lib.h"
30
31 /**
32  * An entry in the hash map.
33  */
34 struct MapEntry
35 {
36
37   /**
38    * Key for the entry.
39    */
40   GNUNET_HashCode key;
41
42   /**
43    * Value of the entry.
44    */
45   void *value;
46
47   /**
48    * If there is a hash collision, we create a linked list.
49    */
50   struct MapEntry *next;
51
52 };
53
54 /**
55  * Internal representation of the hash map.
56  */
57 struct GNUNET_CONTAINER_MultiHashMap
58 {
59
60   /**
61    * All of our buckets.
62    */
63   struct MapEntry **map;
64
65   /**
66    * Number of entries in the map.
67    */
68   unsigned int size;
69
70   /**
71    * Length of the "map" array.
72    */
73   unsigned int map_length;
74 };
75
76
77 /**
78  * Create a multi hash map.
79  *
80  * @param len initial size (map will grow as needed)
81  * @return NULL on error
82  */
83 struct GNUNET_CONTAINER_MultiHashMap *
84 GNUNET_CONTAINER_multihashmap_create (unsigned int len)
85 {
86   struct GNUNET_CONTAINER_MultiHashMap *ret;
87
88   ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MultiHashMap));
89   ret->map = GNUNET_malloc (len * sizeof (struct MapEntry *));
90   ret->map_length = len;
91   return ret;
92 }
93
94
95 /**
96  * Destroy a hash map.  Will not free any values
97  * stored in the hash map!
98  *
99  * @param map the map
100  */
101 void
102 GNUNET_CONTAINER_multihashmap_destroy (struct GNUNET_CONTAINER_MultiHashMap
103                                        *map)
104 {
105   unsigned int i;
106   struct MapEntry *e;
107
108   for (i = 0; i < map->map_length; i++)
109     {
110       while (NULL != (e = map->map[i]))
111         {
112           map->map[i] = e->next;
113           GNUNET_free (e);
114         }
115     }
116   GNUNET_free (map->map);
117   GNUNET_free (map);
118 }
119
120
121 /**
122  * Compute the index of the bucket for the given key.
123  *
124  * @param m hash map for which to compute the index
125  * @param key what key should the index be computed for
126  * @return offset into the "map" array of "m"
127  */
128 static unsigned int
129 idx_of (const struct GNUNET_CONTAINER_MultiHashMap *m,
130         const GNUNET_HashCode * key)
131 {
132   return (*(unsigned int *) key) % m->map_length;
133 }
134
135
136 /**
137  * Get the number of key-value pairs in the map.
138  *
139  * @param map the map
140  * @return the number of key value pairs
141  */
142 unsigned int
143 GNUNET_CONTAINER_multihashmap_size (const struct GNUNET_CONTAINER_MultiHashMap
144                                     *map)
145 {
146   return map->size;
147 }
148
149
150 /**
151  * Given a key find a value in the map matching the key.
152  *
153  * @param map the map
154  * @param key what to look for
155  * @return NULL if no value was found; note that
156  *   this is indistinguishable from values that just
157  *   happen to be NULL; use "contains" to test for
158  *   key-value pairs with value NULL
159  */
160 void *
161 GNUNET_CONTAINER_multihashmap_get (const struct GNUNET_CONTAINER_MultiHashMap
162                                    *map, const GNUNET_HashCode * key)
163 {
164   struct MapEntry *e;
165
166   e = map->map[idx_of (map, key)];
167   while (e != NULL)
168     {
169       if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
170         return e->value;
171       e = e->next;
172     }
173   return NULL;
174 }
175
176
177 /**
178  * Iterate over all entries in the map.
179  *
180  * @param map the map
181  * @param it function to call on each entry
182  * @param it_cls extra argument to it
183  * @return the number of key value pairs processed,
184  *         GNUNET_SYSERR if it aborted iteration
185  */
186 int
187 GNUNET_CONTAINER_multihashmap_iterate (const struct
188                                        GNUNET_CONTAINER_MultiHashMap *map,
189                                        GNUNET_CONTAINER_HashMapIterator it,
190                                        void *it_cls)
191 {
192   int count;
193   unsigned int i;
194   struct MapEntry *e;
195
196   count = 0;
197   for (i = 0; i < map->map_length; i++)
198     {
199       e = map->map[i];
200       while (e != NULL)
201         {
202           if ( (NULL != it) && 
203                (GNUNET_OK != it (it_cls, &e->key, e->value)) )
204             return GNUNET_SYSERR;
205           count++;
206           e = e->next;
207         }
208     }
209   return count;
210 }
211
212
213 /**
214  * Remove the given key-value pair from the map.  Note that if the
215  * key-value pair is in the map multiple times, only one of the pairs
216  * will be removed.
217  *
218  * @param map the map
219  * @param key key of the key-value pair
220  * @param value value of the key-value pair
221  * @return GNUNET_YES on success, GNUNET_NO if the key-value pair
222  *  is not in the map
223  */
224 int
225 GNUNET_CONTAINER_multihashmap_remove (struct GNUNET_CONTAINER_MultiHashMap
226                                       *map, const GNUNET_HashCode * key,
227                                       void *value)
228 {
229   struct MapEntry *e;
230   struct MapEntry *p;
231   unsigned int i;
232
233   i = idx_of (map, key);
234   p = NULL;
235   e = map->map[i];
236   while (e != NULL)
237     {
238       if ( (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) &&
239            (value == e->value))
240         {
241           if (p == NULL)
242             map->map[i] = e->next;
243           else
244             p->next = e->next;
245           GNUNET_free (e);
246           map->size--;
247           return GNUNET_YES;
248         }
249       p = e;
250       e = e->next;
251     }
252   return GNUNET_NO;
253 }
254
255
256 /**
257  * Remove all entries for the given key from the map.
258  * Note that the values would not be "freed".
259  *
260  * @param map the map
261  * @param key identifies values to be removed
262  * @return number of values removed
263  */
264 int
265 GNUNET_CONTAINER_multihashmap_remove_all (struct GNUNET_CONTAINER_MultiHashMap
266                                           *map, const GNUNET_HashCode * key)
267 {
268   struct MapEntry *e;
269   struct MapEntry *p;
270   unsigned int i;
271   int ret;
272
273   ret = 0;
274   i = idx_of (map, key);
275   p = NULL;
276   e = map->map[i];
277   while (e != NULL)
278     {
279       if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
280         {
281           if (p == NULL)
282             map->map[i] = e->next;
283           else
284             p->next = e->next;
285           GNUNET_free (e);
286           map->size--;
287           if (p == NULL)
288             e = map->map[i];
289           else
290             e = p->next;
291           ret++;
292         }
293       else
294         {
295           p = e;
296           e = e->next;
297         }
298     }
299   return ret;
300 }
301
302
303 /**
304  * Check if the map contains any value under the given
305  * key (including values that are NULL).
306  *
307  * @param map the map
308  * @param key the key to test if a value exists for it
309  * @return GNUNET_YES if such a value exists,
310  *         GNUNET_NO if not
311  */
312 int
313 GNUNET_CONTAINER_multihashmap_contains (const struct
314                                         GNUNET_CONTAINER_MultiHashMap *map,
315                                         const GNUNET_HashCode * key)
316 {
317   struct MapEntry *e;
318
319   e = map->map[idx_of (map, key)];
320   while (e != NULL)
321     {
322       if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
323         return GNUNET_YES;
324       e = e->next;
325     }
326   return GNUNET_NO;
327 }
328
329
330 /**
331  * Grow the given map to a more appropriate size.
332  *
333  * @param map the hash map to grow
334  */
335 static void
336 grow (struct GNUNET_CONTAINER_MultiHashMap *map)
337 {
338   struct MapEntry **old_map;
339   struct MapEntry **new_map;
340   struct MapEntry *e;
341   unsigned int old_len;
342   unsigned int new_len;
343   unsigned int idx;
344   unsigned int i;
345
346   old_map = map->map;
347   old_len = map->map_length;
348   new_len = old_len * 2;
349   new_map = GNUNET_malloc (sizeof (struct MapEntry *) * new_len);
350   map->map_length = new_len;
351   map->map = new_map;
352   for (i = 0; i < old_len; i++)
353     {
354       while (NULL != (e = old_map[i]))
355         {
356           old_map[i] = e->next;
357           idx = idx_of (map, &e->key);
358           e->next = new_map[idx];
359           new_map[idx] = e;
360         }
361     }
362   GNUNET_free (old_map);
363 }
364
365
366 /**
367  * Store a key-value pair in the map.
368  *
369  * @param map the map
370  * @param key key to use
371  * @param value value to use
372  * @param opt options for put
373  * @return GNUNET_OK on success,
374  *         GNUNET_NO if a value was replaced (with REPLACE)
375  *         GNUNET_SYSERR if UNIQUE_ONLY was the option and the
376  *                       value already exists
377  */
378 int
379 GNUNET_CONTAINER_multihashmap_put (struct GNUNET_CONTAINER_MultiHashMap *map,
380                                    const GNUNET_HashCode * key,
381                                    void *value,
382                                    enum GNUNET_CONTAINER_MultiHashMapOption
383                                    opt)
384 {
385   struct MapEntry *e;
386   unsigned int i;
387
388   i = idx_of (map, key);
389   if ( (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
390        (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST) )
391     {
392       e = map->map[i];
393       while (e != NULL)
394         {
395           if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
396             {
397               if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
398                 return GNUNET_SYSERR;
399               e->value = value;
400               return GNUNET_NO;
401             }
402           e = e->next;
403         }
404     }
405   if (map->size / 3 >= map->map_length / 4)
406     grow (map);
407   e = GNUNET_malloc (sizeof (struct MapEntry));
408   e->key = *key;
409   e->value = value;
410   e->next = map->map[i];
411   map->map[i] = e;
412   map->size++;
413   return GNUNET_OK;
414 }
415
416
417 /**
418  * Iterate over all entries in the map that match a particular key.
419  *
420  * @param map the map
421  * @param key key that the entries must correspond to
422  * @param it function to call on each entry
423  * @param it_cls extra argument to it
424  * @return the number of key value pairs processed,
425  *         GNUNET_SYSERR if it aborted iteration
426  */
427 int
428 GNUNET_CONTAINER_multihashmap_get_multiple (const struct
429                                             GNUNET_CONTAINER_MultiHashMap
430                                             *map, const GNUNET_HashCode * key,
431                                             GNUNET_CONTAINER_HashMapIterator
432                                             it, void *it_cls)
433 {
434   int count;
435   struct MapEntry *e;
436
437   count = 0;
438   e = map->map[idx_of (map, key)];
439   while (e != NULL)
440     {
441       if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
442         {
443           if ( (it != NULL) && 
444                (GNUNET_OK != it (it_cls, &e->key, e->value)) )
445             return GNUNET_SYSERR;
446           count++;
447         }
448       e = e->next;
449     }
450   return count;
451 }
452
453
454 /* end of container_multihashmap.c */