-better time conversion code
[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   struct 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 struct 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 struct 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 (struct 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   struct 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 *map,
236                                       const struct GNUNET_HashCode * key, 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 (struct 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 struct 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 (struct 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 struct 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 (struct GNUNET_HashCode)))
332       return GNUNET_YES;
333     e = e->next;
334   }
335   return GNUNET_NO;
336 }
337
338
339 /**
340  * Check if the map contains the given value under the given
341  * key.
342  *
343  * @param map the map
344  * @param key the key to test if a value exists for it
345  * @param value value to test for
346  * @return GNUNET_YES if such a value exists,
347  *         GNUNET_NO if not
348  */
349 int
350 GNUNET_CONTAINER_multihashmap_contains_value (const struct
351                                               GNUNET_CONTAINER_MultiHashMap
352                                               *map, const struct 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 (struct 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 struct GNUNET_HashCode * key, void *value,
420                                    enum GNUNET_CONTAINER_MultiHashMapOption opt)
421 {
422   struct MapEntry *e;
423   unsigned int i;
424
425   i = idx_of (map, key);
426   if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
427       (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
428   {
429     e = map->map[i];
430     while (e != NULL)
431     {
432       if (0 == memcmp (key, &e->key, sizeof (struct GNUNET_HashCode)))
433       {
434         if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
435           return GNUNET_SYSERR;
436         e->value = value;
437         return GNUNET_NO;
438       }
439       e = e->next;
440     }
441   }
442   if (map->size / 3 >= map->map_length / 4)
443   {
444     grow (map);
445     i = idx_of (map, key);
446   }
447   e = GNUNET_malloc (sizeof (struct MapEntry));
448   e->key = *key;
449   e->value = value;
450   e->next = map->map[i];
451   map->map[i] = e;
452   map->size++;
453   return GNUNET_OK;
454 }
455
456
457 /**
458  * Iterate over all entries in the map that match a particular key.
459  *
460  * @param map the map
461  * @param key key that the entries must correspond to
462  * @param it function to call on each entry
463  * @param it_cls extra argument to it
464  * @return the number of key value pairs processed,
465  *         GNUNET_SYSERR if it aborted iteration
466  */
467 int
468 GNUNET_CONTAINER_multihashmap_get_multiple (const struct
469                                             GNUNET_CONTAINER_MultiHashMap *map,
470                                             const struct GNUNET_HashCode * key,
471                                             GNUNET_CONTAINER_HashMapIterator it,
472                                             void *it_cls)
473 {
474   int count;
475   struct MapEntry *e;
476   struct MapEntry *n;
477
478   count = 0;
479   n = map->map[idx_of (map, key)];
480   while (NULL != (e = n))
481   {
482     n = e->next;
483     if (0 != memcmp (key, &e->key, sizeof (struct GNUNET_HashCode)))
484       continue;
485     if ((it != NULL) && (GNUNET_OK != it (it_cls, key, e->value)))
486       return GNUNET_SYSERR;
487     count++;
488   }
489   return count;
490 }
491
492
493 /* end of container_multihashmap.c */