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