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