fixed bug https://www.gnunet.org/bugs/view.php?id=1762
[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   GNUNET_assert (len > 0);
89   ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MultiHashMap));
90   ret->map = GNUNET_malloc (len * sizeof (struct MapEntry *));
91   ret->map_length = len;
92   return ret;
93 }
94
95
96 /**
97  * Destroy a hash map.  Will not free any values
98  * stored in the hash map!
99  *
100  * @param map the map
101  */
102 void
103 GNUNET_CONTAINER_multihashmap_destroy (struct GNUNET_CONTAINER_MultiHashMap
104                                        *map)
105 {
106   unsigned int i;
107   struct MapEntry *e;
108
109   for (i = 0; i < map->map_length; i++)
110   {
111     while (NULL != (e = map->map[i]))
112     {
113       map->map[i] = e->next;
114       GNUNET_free (e);
115     }
116   }
117   GNUNET_free (map->map);
118   GNUNET_free (map);
119 }
120
121
122 /**
123  * Compute the index of the bucket for the given key.
124  *
125  * @param m hash map for which to compute the index
126  * @param key what key should the index be computed for
127  * @return offset into the "map" array of "m"
128  */
129 static unsigned int
130 idx_of (const struct GNUNET_CONTAINER_MultiHashMap *m,
131         const GNUNET_HashCode * key)
132 {
133   GNUNET_assert (m != NULL);
134   return (*(unsigned int *) key) % m->map_length;
135 }
136
137
138 /**
139  * Get the number of key-value pairs in the map.
140  *
141  * @param map the map
142  * @return the number of key value pairs
143  */
144 unsigned int
145 GNUNET_CONTAINER_multihashmap_size (const struct GNUNET_CONTAINER_MultiHashMap
146                                     *map)
147 {
148   return map->size;
149 }
150
151
152 /**
153  * Given a key find a value in the map matching the key.
154  *
155  * @param map the map
156  * @param key what to look for
157  * @return NULL if no value was found; note that
158  *   this is indistinguishable from values that just
159  *   happen to be NULL; use "contains" to test for
160  *   key-value pairs with value NULL
161  */
162 void *
163 GNUNET_CONTAINER_multihashmap_get (const struct GNUNET_CONTAINER_MultiHashMap
164                                    *map, const GNUNET_HashCode * key)
165 {
166   struct MapEntry *e;
167
168   e = map->map[idx_of (map, key)];
169   while (e != NULL)
170   {
171     if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
172       return e->value;
173     e = e->next;
174   }
175   return NULL;
176 }
177
178
179 /**
180  * Iterate over all entries in the map.
181  *
182  * @param map the map
183  * @param it function to call on each entry
184  * @param it_cls extra argument to it
185  * @return the number of key value pairs processed,
186  *         GNUNET_SYSERR if it aborted iteration
187  */
188 int
189 GNUNET_CONTAINER_multihashmap_iterate (const struct
190                                        GNUNET_CONTAINER_MultiHashMap *map,
191                                        GNUNET_CONTAINER_HashMapIterator it,
192                                        void *it_cls)
193 {
194   int count;
195   unsigned int i;
196   struct MapEntry *e;
197   struct MapEntry *n;
198   GNUNET_HashCode kc;
199
200   count = 0;
201   GNUNET_assert (map != NULL);
202   for (i = 0; i < map->map_length; i++)
203   {
204     n = map->map[i];
205     while (NULL != (e = n))
206     {
207       n = e->next;
208       if (NULL != it)
209       {
210         kc = e->key;
211         if (GNUNET_OK != it (it_cls, &kc, e->value))
212           return GNUNET_SYSERR;
213       }
214       count++;
215     }
216   }
217   return count;
218 }
219
220
221 /**
222  * Remove the given key-value pair from the map.  Note that if the
223  * key-value pair is in the map multiple times, only one of the pairs
224  * will be removed.
225  *
226  * @param map the map
227  * @param key key of the key-value pair
228  * @param value value of the key-value pair
229  * @return GNUNET_YES on success, GNUNET_NO if the key-value pair
230  *  is not in the map
231  */
232 int
233 GNUNET_CONTAINER_multihashmap_remove (struct GNUNET_CONTAINER_MultiHashMap *map,
234                                       const GNUNET_HashCode * key, void *value)
235 {
236   struct MapEntry *e;
237   struct MapEntry *p;
238   unsigned int i;
239
240   i = idx_of (map, key);
241   p = NULL;
242   e = map->map[i];
243   while (e != NULL)
244   {
245     if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) &&
246         (value == e->value))
247     {
248       if (p == NULL)
249         map->map[i] = e->next;
250       else
251         p->next = e->next;
252       GNUNET_free (e);
253       map->size--;
254       return GNUNET_YES;
255     }
256     p = e;
257     e = e->next;
258   }
259   return GNUNET_NO;
260 }
261
262
263 /**
264  * Remove all entries for the given key from the map.
265  * Note that the values would not be "freed".
266  *
267  * @param map the map
268  * @param key identifies values to be removed
269  * @return number of values removed
270  */
271 int
272 GNUNET_CONTAINER_multihashmap_remove_all (struct GNUNET_CONTAINER_MultiHashMap
273                                           *map, const GNUNET_HashCode * key)
274 {
275   struct MapEntry *e;
276   struct MapEntry *p;
277   unsigned int i;
278   int ret;
279
280   ret = 0;
281   i = idx_of (map, key);
282   p = NULL;
283   e = map->map[i];
284   while (e != NULL)
285   {
286     if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
287     {
288       if (p == NULL)
289         map->map[i] = e->next;
290       else
291         p->next = e->next;
292       GNUNET_free (e);
293       map->size--;
294       if (p == NULL)
295         e = map->map[i];
296       else
297         e = p->next;
298       ret++;
299     }
300     else
301     {
302       p = e;
303       e = e->next;
304     }
305   }
306   return ret;
307 }
308
309
310 /**
311  * Check if the map contains any value under the given
312  * key (including values that are NULL).
313  *
314  * @param map the map
315  * @param key the key to test if a value exists for it
316  * @return GNUNET_YES if such a value exists,
317  *         GNUNET_NO if not
318  */
319 int
320 GNUNET_CONTAINER_multihashmap_contains (const struct
321                                         GNUNET_CONTAINER_MultiHashMap *map,
322                                         const GNUNET_HashCode * key)
323 {
324   struct MapEntry *e;
325
326   e = map->map[idx_of (map, key)];
327   while (e != NULL)
328   {
329     if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
330       return GNUNET_YES;
331     e = e->next;
332   }
333   return GNUNET_NO;
334 }
335
336
337 /**
338  * Check if the map contains the given value under the given
339  * key.
340  *
341  * @param map the map
342  * @param key the key to test if a value exists for it
343  * @param value value to test for
344  * @return GNUNET_YES if such a value exists,
345  *         GNUNET_NO if not
346  */
347 int
348 GNUNET_CONTAINER_multihashmap_contains_value (const struct
349                                               GNUNET_CONTAINER_MultiHashMap
350                                               *map, const GNUNET_HashCode * key,
351                                               const void *value)
352 {
353   struct MapEntry *e;
354
355   e = map->map[idx_of (map, key)];
356   while (e != NULL)
357   {
358     if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) &&
359         (e->value == value))
360       return GNUNET_YES;
361     e = e->next;
362   }
363   return GNUNET_NO;
364 }
365
366
367 /**
368  * Grow the given map to a more appropriate size.
369  *
370  * @param map the hash map to grow
371  */
372 static void
373 grow (struct GNUNET_CONTAINER_MultiHashMap *map)
374 {
375   struct MapEntry **old_map;
376   struct MapEntry **new_map;
377   struct MapEntry *e;
378   unsigned int old_len;
379   unsigned int new_len;
380   unsigned int idx;
381   unsigned int i;
382
383   old_map = map->map;
384   old_len = map->map_length;
385   new_len = old_len * 2;
386   new_map = GNUNET_malloc (sizeof (struct MapEntry *) * new_len);
387   map->map_length = new_len;
388   map->map = new_map;
389   for (i = 0; i < old_len; i++)
390   {
391     while (NULL != (e = old_map[i]))
392     {
393       old_map[i] = e->next;
394       idx = idx_of (map, &e->key);
395       e->next = new_map[idx];
396       new_map[idx] = e;
397     }
398   }
399   GNUNET_free (old_map);
400 }
401
402
403 /**
404  * Store a key-value pair in the map.
405  *
406  * @param map the map
407  * @param key key to use
408  * @param value value to use
409  * @param opt options for put
410  * @return GNUNET_OK on success,
411  *         GNUNET_NO if a value was replaced (with REPLACE)
412  *         GNUNET_SYSERR if UNIQUE_ONLY was the option and the
413  *                       value already exists
414  */
415 int
416 GNUNET_CONTAINER_multihashmap_put (struct GNUNET_CONTAINER_MultiHashMap *map,
417                                    const GNUNET_HashCode * key, void *value,
418                                    enum GNUNET_CONTAINER_MultiHashMapOption opt)
419 {
420   struct MapEntry *e;
421   unsigned int i;
422
423   i = idx_of (map, key);
424   if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
425       (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
426   {
427     e = map->map[i];
428     while (e != NULL)
429     {
430       if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
431       {
432         if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
433           return GNUNET_SYSERR;
434         e->value = value;
435         return GNUNET_NO;
436       }
437       e = e->next;
438     }
439   }
440   if (map->size / 3 >= map->map_length / 4)
441   {
442     grow (map);
443     i = idx_of (map, key);
444   }
445   e = GNUNET_malloc (sizeof (struct MapEntry));
446   e->key = *key;
447   e->value = value;
448   e->next = map->map[i];
449   map->map[i] = e;
450   map->size++;
451   return GNUNET_OK;
452 }
453
454
455 /**
456  * Iterate over all entries in the map that match a particular key.
457  *
458  * @param map the map
459  * @param key key that the entries must correspond to
460  * @param it function to call on each entry
461  * @param it_cls extra argument to it
462  * @return the number of key value pairs processed,
463  *         GNUNET_SYSERR if it aborted iteration
464  */
465 int
466 GNUNET_CONTAINER_multihashmap_get_multiple (const struct
467                                             GNUNET_CONTAINER_MultiHashMap *map,
468                                             const GNUNET_HashCode * key,
469                                             GNUNET_CONTAINER_HashMapIterator it,
470                                             void *it_cls)
471 {
472   int count;
473   struct MapEntry *e;
474   struct MapEntry *n;
475
476   count = 0;
477   n = map->map[idx_of (map, key)];
478   while (NULL != (e = n))
479   {
480     n = e->next;
481     if (0 != memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
482       continue;
483     if ((it != NULL) && (GNUNET_OK != it (it_cls, key, e->value)))
484       return GNUNET_SYSERR;
485     count++;
486   }
487   return count;
488 }
489
490
491 /* end of container_multihashmap.c */