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