(no commit message)
[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
203   for (i = 0; i < map->map_length; i++)
204     {
205       n = map->map[i];
206       while (NULL != (e = n))
207         {
208           n = e->next;
209           if (NULL != it)
210             {
211               kc = e->key;
212               if (GNUNET_OK != it (it_cls, &kc, e->value))
213                 return GNUNET_SYSERR;
214             }
215           count++;
216         }
217     }
218   return count;
219 }
220
221
222 /**
223  * Remove the given key-value pair from the map.  Note that if the
224  * key-value pair is in the map multiple times, only one of the pairs
225  * will be removed.
226  *
227  * @param map the map
228  * @param key key of the key-value pair
229  * @param value value of the key-value pair
230  * @return GNUNET_YES on success, GNUNET_NO if the key-value pair
231  *  is not in the map
232  */
233 int
234 GNUNET_CONTAINER_multihashmap_remove (struct GNUNET_CONTAINER_MultiHashMap
235                                       *map, const GNUNET_HashCode * key,
236                                       void *value)
237 {
238   struct MapEntry *e;
239   struct MapEntry *p;
240   unsigned int i;
241
242   i = idx_of (map, key);
243   p = NULL;
244   e = map->map[i];
245   while (e != NULL)
246     {
247       if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) &&
248           (value == e->value))
249         {
250           if (p == NULL)
251             map->map[i] = e->next;
252           else
253             p->next = e->next;
254           GNUNET_free (e);
255           map->size--;
256           return GNUNET_YES;
257         }
258       p = e;
259       e = e->next;
260     }
261   return GNUNET_NO;
262 }
263
264
265 /**
266  * Remove all entries for the given key from the map.
267  * Note that the values would not be "freed".
268  *
269  * @param map the map
270  * @param key identifies values to be removed
271  * @return number of values removed
272  */
273 int
274 GNUNET_CONTAINER_multihashmap_remove_all (struct GNUNET_CONTAINER_MultiHashMap
275                                           *map, const GNUNET_HashCode * key)
276 {
277   struct MapEntry *e;
278   struct MapEntry *p;
279   unsigned int i;
280   int ret;
281
282   ret = 0;
283   i = idx_of (map, key);
284   p = NULL;
285   e = map->map[i];
286   while (e != NULL)
287     {
288       if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
289         {
290           if (p == NULL)
291             map->map[i] = e->next;
292           else
293             p->next = e->next;
294           GNUNET_free (e);
295           map->size--;
296           if (p == NULL)
297             e = map->map[i];
298           else
299             e = p->next;
300           ret++;
301         }
302       else
303         {
304           p = e;
305           e = e->next;
306         }
307     }
308   return ret;
309 }
310
311
312 /**
313  * Check if the map contains any value under the given
314  * key (including values that are NULL).
315  *
316  * @param map the map
317  * @param key the key to test if a value exists for it
318  * @return GNUNET_YES if such a value exists,
319  *         GNUNET_NO if not
320  */
321 int
322 GNUNET_CONTAINER_multihashmap_contains (const struct
323                                         GNUNET_CONTAINER_MultiHashMap *map,
324                                         const GNUNET_HashCode * key)
325 {
326   struct MapEntry *e;
327
328   e = map->map[idx_of (map, key)];
329   while (e != NULL)
330     {
331       if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
332         return GNUNET_YES;
333       e = e->next;
334     }
335   return GNUNET_NO;
336 }
337
338
339 /**
340  * Grow the given map to a more appropriate size.
341  *
342  * @param map the hash map to grow
343  */
344 static void
345 grow (struct GNUNET_CONTAINER_MultiHashMap *map)
346 {
347   struct MapEntry **old_map;
348   struct MapEntry **new_map;
349   struct MapEntry *e;
350   unsigned int old_len;
351   unsigned int new_len;
352   unsigned int idx;
353   unsigned int i;
354
355   old_map = map->map;
356   old_len = map->map_length;
357   new_len = old_len * 2;
358   new_map = GNUNET_malloc (sizeof (struct MapEntry *) * new_len);
359   map->map_length = new_len;
360   map->map = new_map;
361   for (i = 0; i < old_len; i++)
362     {
363       while (NULL != (e = old_map[i]))
364         {
365           old_map[i] = e->next;
366           idx = idx_of (map, &e->key);
367           e->next = new_map[idx];
368           new_map[idx] = e;
369         }
370     }
371   GNUNET_free (old_map);
372 }
373
374
375 /**
376  * Store a key-value pair in the map.
377  *
378  * @param map the map
379  * @param key key to use
380  * @param value value to use
381  * @param opt options for put
382  * @return GNUNET_OK on success,
383  *         GNUNET_NO if a value was replaced (with REPLACE)
384  *         GNUNET_SYSERR if UNIQUE_ONLY was the option and the
385  *                       value already exists
386  */
387 int
388 GNUNET_CONTAINER_multihashmap_put (struct GNUNET_CONTAINER_MultiHashMap *map,
389                                    const GNUNET_HashCode * key,
390                                    void *value,
391                                    enum GNUNET_CONTAINER_MultiHashMapOption
392                                    opt)
393 {
394   struct MapEntry *e;
395   unsigned int i;
396
397   i = idx_of (map, key);
398   if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
399       (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
400     {
401       e = map->map[i];
402       while (e != NULL)
403         {
404           if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
405             {
406               if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
407                 return GNUNET_SYSERR;
408               e->value = value;
409               return GNUNET_NO;
410             }
411           e = e->next;
412         }
413     }
414   if (map->size / 3 >= map->map_length / 4)
415     {
416       grow (map);
417       i = idx_of (map, key);
418     }
419   e = GNUNET_malloc (sizeof (struct MapEntry));
420   e->key = *key;
421   e->value = value;
422   e->next = map->map[i];
423   map->map[i] = e;
424   map->size++;
425   return GNUNET_OK;
426 }
427
428
429 /**
430  * Iterate over all entries in the map that match a particular key.
431  *
432  * @param map the map
433  * @param key key that the entries must correspond to
434  * @param it function to call on each entry
435  * @param it_cls extra argument to it
436  * @return the number of key value pairs processed,
437  *         GNUNET_SYSERR if it aborted iteration
438  */
439 int
440 GNUNET_CONTAINER_multihashmap_get_multiple (const struct
441                                             GNUNET_CONTAINER_MultiHashMap
442                                             *map, const GNUNET_HashCode * key,
443                                             GNUNET_CONTAINER_HashMapIterator
444                                             it, void *it_cls)
445 {
446   int count;
447   struct MapEntry *e;
448   struct MapEntry *n;
449
450   count = 0;
451   n = map->map[idx_of (map, key)];
452   while (NULL != (e = n))
453     {
454       n = e->next;
455       if (0 != memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
456         continue;
457       if ((it != NULL) && (GNUNET_OK != it (it_cls, key, e->value)))
458         return GNUNET_SYSERR;
459       count++;
460     }
461   return count;
462 }
463
464
465 /* end of container_multihashmap.c */