-fix (C) notices
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_cache.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2008--2013 GNUnet e.V.
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 3, 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., 51 Franklin Street, Fifth Floor,
18   Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file testbed/gnunet-service-testbed_cache.c
23  * @brief testbed cache implementation
24  * @author Sree Harsha Totakura
25  */
26 #include "gnunet-service-testbed.h"
27
28 /**
29  * Redefine LOG with a changed log component string
30  */
31 #ifdef LOG
32 #undef LOG
33 #endif
34 #define LOG(kind,...)                                   \
35   GNUNET_log_from (kind, "testbed-cache", __VA_ARGS__)
36
37
38 /**
39  * Cache entry
40  */
41 struct CacheEntry
42 {
43   /**
44    * DLL next ptr for least recently used cache entries
45    */
46   struct CacheEntry *next;
47
48   /**
49    * DLL prev ptr for least recently used cache entries
50    */
51   struct CacheEntry *prev;
52
53   /**
54    * The HELLO message
55    */
56   struct GNUNET_MessageHeader *hello;
57
58   /**
59    * The id of the peer this entry corresponds to
60    */
61   unsigned int peer_id;
62 };
63
64
65 /**
66  * Hashmap to maintain cache
67  */
68 static struct GNUNET_CONTAINER_MultiHashMap32 *cache;
69
70 /**
71  * DLL head for least recently used cache entries; least recently used
72  * cache items are at the head. The cache enties are added to this queue when
73  * their demand becomes zero. They are removed from the queue when they are
74  * needed by any operation.
75  */
76 static struct CacheEntry *cache_head;
77
78 /**
79  * DLL tail for least recently used cache entries; recently used cache
80  * items are at the tail.The cache enties are added to this queue when
81  * their demand becomes zero. They are removed from the queue when they are
82  * needed by any operation.
83  */
84 static struct CacheEntry *cache_tail;
85
86 /**
87  * Maximum number of elements to cache
88  */
89 static unsigned int cache_size;
90
91
92 /**
93  * Looks up in the cache and returns the entry
94  *
95  * @param peer_id the peer identity of the peer whose corresponding entry has to
96  *          be looked up
97  * @return the HELLO message; NULL if not found
98  */
99 static struct CacheEntry *
100 cache_lookup (unsigned int peer_id)
101 {
102   struct CacheEntry *entry;
103
104   GNUNET_assert (NULL != cache);
105   entry = GNUNET_CONTAINER_multihashmap32_get (cache, peer_id);
106   if (NULL == entry)
107     return NULL;
108   GNUNET_CONTAINER_DLL_remove (cache_head, cache_tail, entry);
109   GNUNET_CONTAINER_DLL_insert_tail (cache_head, cache_tail, entry);
110   return entry;
111 }
112
113
114 /**
115  * Free the resources occupied by a cache entry
116  *
117  * @param entry the cache entry to free
118  */
119 static void
120 free_entry (struct CacheEntry *entry)
121 {
122   GNUNET_CONTAINER_DLL_remove (cache_head, cache_tail, entry);
123   GNUNET_free_non_null (entry->hello);
124   GNUNET_free (entry);
125 }
126
127
128 /**
129  * Creates a new cache entry and then puts it into the cache's hashtable.
130  *
131  * @param peer_id the index of the peer to tag the newly created entry
132  * @return the newly created entry
133  */
134 static struct CacheEntry *
135 add_entry (unsigned int peer_id)
136 {
137   struct CacheEntry *entry;
138
139   GNUNET_assert (NULL != cache);
140   if (cache_size == GNUNET_CONTAINER_multihashmap32_size (cache))
141   {
142     /* remove the LRU head */
143     entry = cache_head;
144     GNUNET_assert (GNUNET_OK ==
145                    GNUNET_CONTAINER_multihashmap32_remove (cache, (uint32_t)
146                                                            entry->peer_id,
147                                                            entry));
148     free_entry (entry);
149   }
150   entry = GNUNET_new (struct CacheEntry);
151   entry->peer_id = peer_id;
152   GNUNET_assert (GNUNET_OK ==
153                  GNUNET_CONTAINER_multihashmap32_put (cache,
154                                                       (uint32_t) peer_id,
155                                                       entry,
156                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
157   GNUNET_CONTAINER_DLL_insert_tail (cache_head, cache_tail, entry);
158   return entry;
159 }
160
161
162 /**
163  * Iterator over hash map entries.
164  *
165  * @param cls closure
166  * @param key current key
167  * @param value value in the hash map
168  * @return GNUNET_YES if we should continue to
169  *         iterate,
170  *         GNUNET_NO if not.
171  */
172 static int
173 cache_clear_iterator (void *cls, uint32_t key, void *value)
174 {
175   struct CacheEntry *entry = value;
176
177   GNUNET_assert (NULL != entry);
178   GNUNET_assert (GNUNET_YES ==
179                  GNUNET_CONTAINER_multihashmap32_remove (cache, key, value));
180   free_entry (entry);
181   return GNUNET_YES;
182 }
183
184
185 /**
186  * Clear cache
187  */
188 void
189 GST_cache_clear ()
190 {
191   if (NULL != cache)
192   {
193     GNUNET_CONTAINER_multihashmap32_iterate (cache, &cache_clear_iterator, NULL);
194     GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (cache));
195     GNUNET_CONTAINER_multihashmap32_destroy (cache);
196     cache = NULL;
197   }
198   cache_size = 0;
199   cache_head = NULL;
200   cache_tail = NULL;
201 }
202
203
204 /**
205  * Initializes the cache
206  *
207  * @param size the size of the cache
208  */
209 void
210 GST_cache_init (unsigned int size)
211 {
212   if (0 == size)
213     return;
214   cache_size = size;
215   cache = GNUNET_CONTAINER_multihashmap32_create (cache_size);
216 }
217
218
219 /**
220  * Looks up in the hello cache and returns the HELLO of the given peer
221  *
222  * @param peer_id the index of the peer whose HELLO has to be looked up
223  * @return the HELLO message; NULL if not found
224  */
225 const struct GNUNET_MessageHeader *
226 GST_cache_lookup_hello (const unsigned int peer_id)
227 {
228   struct CacheEntry *entry;
229
230   LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id);
231   if (NULL == cache)
232   {
233     LOG_DEBUG ("Caching disabled\n");
234     return NULL;
235   }
236   entry = cache_lookup (peer_id);
237   if (NULL == entry)
238     return NULL;
239   if (NULL != entry->hello)
240     LOG_DEBUG ("HELLO found for peer %u\n", peer_id);
241   return entry->hello;
242 }
243
244
245 /**
246  * Caches the HELLO of the given peer. Updates the HELLO if it was already
247  * cached before
248  *
249  * @param peer_id the peer identity of the peer whose HELLO has to be cached
250  * @param hello the HELLO message
251  */
252 void
253 GST_cache_add_hello (const unsigned int peer_id,
254                      const struct GNUNET_MessageHeader *hello)
255 {
256   struct CacheEntry *entry;
257
258   if (NULL == cache)
259     return;
260   entry = cache_lookup (peer_id);
261   if (NULL == entry)
262     entry = add_entry (peer_id);
263   GNUNET_free_non_null (entry->hello);
264   entry->hello = GNUNET_copy_message (hello);
265 }
266
267 /* end of gnunet-service-testbed_hc.c */