add $(GN_LIBINTL) to Makefile.am (fixes 0005902)
[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 it
6    under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License,
8    or (at your 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    Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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,
194                                              NULL);
195     GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (cache));
196     GNUNET_CONTAINER_multihashmap32_destroy (cache);
197     cache = NULL;
198   }
199   cache_size = 0;
200   cache_head = NULL;
201   cache_tail = NULL;
202 }
203
204
205 /**
206  * Initializes the cache
207  *
208  * @param size the size of the cache
209  */
210 void
211 GST_cache_init (unsigned int size)
212 {
213   if (0 == size)
214     return;
215   cache_size = size;
216   cache = GNUNET_CONTAINER_multihashmap32_create (cache_size);
217 }
218
219
220 /**
221  * Looks up in the hello cache and returns the HELLO of the given peer
222  *
223  * @param peer_id the index of the peer whose HELLO has to be looked up
224  * @return the HELLO message; NULL if not found
225  */
226 const struct GNUNET_MessageHeader *
227 GST_cache_lookup_hello (const unsigned int peer_id)
228 {
229   struct CacheEntry *entry;
230
231   LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id);
232   if (NULL == cache)
233   {
234     LOG_DEBUG ("Caching disabled\n");
235     return NULL;
236   }
237   entry = cache_lookup (peer_id);
238   if (NULL == entry)
239     return NULL;
240   if (NULL != entry->hello)
241     LOG_DEBUG ("HELLO found for peer %u\n", peer_id);
242   return entry->hello;
243 }
244
245
246 /**
247  * Caches the HELLO of the given peer. Updates the HELLO if it was already
248  * cached before
249  *
250  * @param peer_id the peer identity of the peer whose HELLO has to be cached
251  * @param hello the HELLO message
252  */
253 void
254 GST_cache_add_hello (const unsigned int peer_id,
255                      const struct GNUNET_MessageHeader *hello)
256 {
257   struct CacheEntry *entry;
258
259   if (NULL == cache)
260     return;
261   entry = cache_lookup (peer_id);
262   if (NULL == entry)
263     entry = add_entry (peer_id);
264   GNUNET_free_non_null (entry->hello);
265   entry->hello = GNUNET_copy_message (hello);
266 }
267
268
269 /* end of gnunet-service-testbed_hc.c */