RPS: Forgot to add header
[oweals/gnunet.git] / src / datacache / plugin_datacache_heap.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2012, 2015 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 datacache/plugin_datacache_heap.c
23  * @brief heap-only implementation of a database backend for the datacache
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_datacache_plugin.h"
29
30 #define LOG(kind,...) GNUNET_log_from (kind, "datacache-heap", __VA_ARGS__)
31
32 #define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache-heap", op, fn)
33
34 #define NUM_HEAPS 24
35
36 /**
37  * Context for all functions in this plugin.
38  */
39 struct Plugin
40 {
41   /**
42    * Our execution environment.
43    */
44   struct GNUNET_DATACACHE_PluginEnvironment *env;
45
46   /**
47    * Our hash map.
48    */
49   struct GNUNET_CONTAINER_MultiHashMap *map;
50
51   /**
52    * Heaps sorted by distance.
53    */
54   struct GNUNET_CONTAINER_Heap *heaps[NUM_HEAPS];
55
56 };
57
58
59 /**
60  * Entry in the hash map.
61  */
62 struct Value
63 {
64   /**
65    * Key for the entry.
66    */
67   struct GNUNET_HashCode key;
68
69   /**
70    * Expiration time.
71    */
72   struct GNUNET_TIME_Absolute discard_time;
73
74   /**
75    * Corresponding node in the heap.
76    */
77   struct GNUNET_CONTAINER_HeapNode *hn;
78
79   /**
80    * Path information.
81    */
82   struct GNUNET_PeerIdentity *path_info;
83
84   /**
85    * Payload (actual payload follows this struct)
86    */
87   size_t size;
88
89   /**
90    * Number of entries in @e path_info.
91    */
92   unsigned int path_info_len;
93
94   /**
95    * How close is the hash to us? Determines which heap we are in!
96    */
97   uint32_t distance;
98
99   /**
100    * Type of the block.
101    */
102   enum GNUNET_BLOCK_Type type;
103
104 };
105
106
107 #define OVERHEAD (sizeof (struct Value) + 64)
108
109
110 /**
111  * Closure for #put_cb().
112  */
113 struct PutContext
114 {
115   /**
116    * Expiration time for the new value.
117    */
118   struct GNUNET_TIME_Absolute discard_time;
119
120   /**
121    * Data for the new value.
122    */
123   const char *data;
124
125   /**
126    * Path information.
127    */
128   const struct GNUNET_PeerIdentity *path_info;
129
130   /**
131    * Number of bytes in @e data.
132    */
133   size_t size;
134
135   /**
136    * Type of the node.
137    */
138   enum GNUNET_BLOCK_Type type;
139
140   /**
141    * Number of entries in @e path_info.
142    */
143   unsigned int path_info_len;
144
145   /**
146    * Value to set to #GNUNET_YES if an equivalent block was found.
147    */
148   int found;
149 };
150
151
152 /**
153  * Function called during PUT to detect if an equivalent block
154  * already exists.
155  *
156  * @param cls the `struct PutContext`
157  * @param key the key for the value(s)
158  * @param value an existing value
159  * @return #GNUNET_YES if not found (to continue to iterate)
160  */
161 static int
162 put_cb (void *cls,
163         const struct GNUNET_HashCode *key,
164         void *value)
165 {
166   struct PutContext *put_ctx = cls;
167   struct Value *val = value;
168
169   if ( (val->size == put_ctx->size) &&
170        (val->type == put_ctx->type) &&
171        (0 == memcmp (&val[1],
172                      put_ctx->data,
173                      put_ctx->size)) )
174   {
175     put_ctx->found = GNUNET_YES;
176     val->discard_time = GNUNET_TIME_absolute_max (val->discard_time,
177                                                   put_ctx->discard_time);
178     /* replace old path with new path */
179     GNUNET_array_grow (val->path_info,
180                        val->path_info_len,
181                        put_ctx->path_info_len);
182     GNUNET_memcpy (val->path_info,
183                    put_ctx->path_info,
184                    put_ctx->path_info_len * sizeof (struct GNUNET_PeerIdentity));
185     GNUNET_CONTAINER_heap_update_cost (val->hn,
186                                        val->discard_time.abs_value_us);
187     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188                 "Got same value for key %s and type %d (size %u vs %u)\n",
189                 GNUNET_h2s (key),
190                 val->type,
191                 (unsigned int) val->size,
192                 (unsigned int) put_ctx->size);
193     return GNUNET_NO;
194   }
195   return GNUNET_YES;
196 }
197
198
199 /**
200  * Store an item in the datastore.
201  *
202  * @param cls closure (our `struct Plugin`)
203  * @param key key to store data under
204  * @param xor_distance how close is @a key to our PID?
205  * @param size number of bytes in @a data
206  * @param data data to store
207  * @param type type of the value
208  * @param discard_time when to discard the value in any case
209  * @param path_info_len number of entries in @a path_info
210  * @param path_info a path through the network
211  * @return 0 if duplicate, -1 on error, number of bytes used otherwise
212  */
213 static ssize_t
214 heap_plugin_put (void *cls,
215                  const struct GNUNET_HashCode *key,
216                  uint32_t xor_distance,
217                  size_t size,
218                  const char *data,
219                  enum GNUNET_BLOCK_Type type,
220                  struct GNUNET_TIME_Absolute discard_time,
221                  unsigned int path_info_len,
222                  const struct GNUNET_PeerIdentity *path_info)
223 {
224   struct Plugin *plugin = cls;
225   struct Value *val;
226   struct PutContext put_ctx;
227
228   put_ctx.found = GNUNET_NO;
229   put_ctx.data = data;
230   put_ctx.size = size;
231   put_ctx.path_info = path_info;
232   put_ctx.path_info_len = path_info_len;
233   put_ctx.discard_time = discard_time;
234   put_ctx.type = type;
235   GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
236                                               key,
237                                               &put_cb,
238                                               &put_ctx);
239   if (GNUNET_YES == put_ctx.found)
240     return 0;
241   val = GNUNET_malloc (sizeof (struct Value) + size);
242   GNUNET_memcpy (&val[1],
243                  data,
244                  size);
245   val->key = *key;
246   val->type = type;
247   val->discard_time = discard_time;
248   val->size = size;
249   if (xor_distance >= NUM_HEAPS)
250     val->distance = NUM_HEAPS - 1;
251   else
252     val->distance = xor_distance;
253   GNUNET_array_grow (val->path_info,
254                      val->path_info_len,
255                      path_info_len);
256   GNUNET_memcpy (val->path_info,
257                  path_info,
258                  path_info_len * sizeof (struct GNUNET_PeerIdentity));
259   (void) GNUNET_CONTAINER_multihashmap_put (plugin->map,
260                                             &val->key,
261                                             val,
262                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
263   val->hn = GNUNET_CONTAINER_heap_insert (plugin->heaps[val->distance],
264                                           val,
265                                           val->discard_time.abs_value_us);
266   return size + OVERHEAD;
267 }
268
269
270 /**
271  * Closure for #get_cb().
272  */
273 struct GetContext
274 {
275   /**
276    * Function to call for each result.
277    */
278   GNUNET_DATACACHE_Iterator iter;
279
280   /**
281    * Closure for @e iter.
282    */
283   void *iter_cls;
284
285   /**
286    * Number of results found.
287    */
288   unsigned int cnt;
289
290   /**
291    * Block type requested.
292    */
293   enum GNUNET_BLOCK_Type type;
294 };
295
296
297
298 /**
299  * Function called during GET to find matching blocks.
300  * Only matches by type.
301  *
302  * @param cls the `struct GetContext`
303  * @param key the key for the value(s)
304  * @param value an existing value
305  * @return #GNUNET_YES to continue to iterate
306  */
307 static int
308 get_cb (void *cls,
309         const struct GNUNET_HashCode *key,
310         void *value)
311 {
312   struct GetContext *get_ctx = cls;
313   struct Value *val = value;
314   int ret;
315
316   if ( (get_ctx->type != val->type) &&
317        (GNUNET_BLOCK_TYPE_ANY != get_ctx->type) )
318     return GNUNET_OK;
319   if (0 ==
320       GNUNET_TIME_absolute_get_remaining (val->discard_time).rel_value_us)
321     return GNUNET_OK;
322   if (NULL != get_ctx->iter)
323     ret = get_ctx->iter (get_ctx->iter_cls,
324                          key,
325                          val->size,
326                          (const char *) &val[1],
327                          val->type,
328                          val->discard_time,
329                          val->path_info_len,
330                          val->path_info);
331   else
332     ret = GNUNET_YES;
333   get_ctx->cnt++;
334   return ret;
335 }
336
337
338 /**
339  * Iterate over the results for a particular key
340  * in the datastore.
341  *
342  * @param cls closure (our `struct Plugin`)
343  * @param key
344  * @param type entries of which type are relevant?
345  * @param iter maybe NULL (to just count)
346  * @param iter_cls closure for @a iter
347  * @return the number of results found
348  */
349 static unsigned int
350 heap_plugin_get (void *cls,
351                  const struct GNUNET_HashCode *key,
352                  enum GNUNET_BLOCK_Type type,
353                  GNUNET_DATACACHE_Iterator iter,
354                  void *iter_cls)
355 {
356   struct Plugin *plugin = cls;
357   struct GetContext get_ctx;
358
359   get_ctx.type = type;
360   get_ctx.iter = iter;
361   get_ctx.iter_cls = iter_cls;
362   get_ctx.cnt = 0;
363   GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
364                                               key,
365                                               &get_cb,
366                                               &get_ctx);
367   return get_ctx.cnt;
368 }
369
370
371 /**
372  * Delete the entry with the lowest expiration value
373  * from the datacache right now.
374  *
375  * @param cls closure (our `struct Plugin`)
376  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
377  */
378 static int
379 heap_plugin_del (void *cls)
380 {
381   struct Plugin *plugin = cls;
382   struct Value *val;
383
384   for (unsigned int i=0;i<NUM_HEAPS;i++)
385   {
386     val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i]);
387     if (NULL != val)
388       break;
389   }
390   if (NULL == val)
391     return GNUNET_SYSERR;
392   GNUNET_assert (GNUNET_YES ==
393                  GNUNET_CONTAINER_multihashmap_remove (plugin->map,
394                                                        &val->key,
395                                                        val));
396   plugin->env->delete_notify (plugin->env->cls,
397                               &val->key,
398                               val->size + OVERHEAD);
399   GNUNET_free_non_null (val->path_info);
400   GNUNET_free (val);
401   return GNUNET_OK;
402 }
403
404
405 /**
406  * Return a random value from the datastore.
407  *
408  * @param cls closure (our `struct Plugin`)
409  * @param iter maybe NULL (to just count)
410  * @param iter_cls closure for @a iter
411  * @return the number of results found
412  */
413 static unsigned int
414 heap_plugin_get_random (void *cls,
415                         GNUNET_DATACACHE_Iterator iter,
416                         void *iter_cls)
417 {
418   struct Plugin *plugin = cls;
419   struct GetContext get_ctx;
420
421   get_ctx.type = GNUNET_BLOCK_TYPE_ANY;
422   get_ctx.iter = iter;
423   get_ctx.iter_cls = iter_cls;
424   get_ctx.cnt = 0;
425   GNUNET_CONTAINER_multihashmap_get_random (plugin->map,
426                                             &get_cb,
427                                             &get_ctx);
428   return get_ctx.cnt;
429 }
430
431
432 /**
433  * Closure for #find_closest().
434  */
435 struct GetClosestContext
436 {
437   struct Value **values;
438
439   unsigned int num_results;
440
441   const struct GNUNET_HashCode *key;
442 };
443
444
445 static int
446 find_closest (void *cls,
447               const struct GNUNET_HashCode *key,
448               void *value)
449 {
450   struct GetClosestContext *gcc = cls;
451   struct Value *val = value;
452   unsigned int j;
453
454   if (1 != GNUNET_CRYPTO_hash_cmp (key,
455                                    gcc->key))
456     return GNUNET_OK; /* useless */
457   j = gcc->num_results;
458   for (unsigned int i=0;i<gcc->num_results;i++)
459   {
460     if (NULL == gcc->values[i])
461     {
462       j = i;
463       break;
464     }
465     if (1 == GNUNET_CRYPTO_hash_cmp (&gcc->values[i]->key,
466                                      key))
467     {
468       j = i;
469       break;
470     }
471   }
472   if (j == gcc->num_results)
473     return GNUNET_OK;
474   gcc->values[j] = val;
475   return GNUNET_OK;
476 }
477
478
479 /**
480  * Iterate over the results that are "close" to a particular key in
481  * the datacache.  "close" is defined as numerically larger than @a
482  * key (when interpreted as a circular address space), with small
483  * distance.
484  *
485  * @param cls closure (internal context for the plugin)
486  * @param key area of the keyspace to look into
487  * @param num_results number of results that should be returned to @a iter
488  * @param iter maybe NULL (to just count)
489  * @param iter_cls closure for @a iter
490  * @return the number of results found
491  */
492 static unsigned int
493 heap_plugin_get_closest (void *cls,
494                          const struct GNUNET_HashCode *key,
495                          unsigned int num_results,
496                          GNUNET_DATACACHE_Iterator iter,
497                          void *iter_cls)
498 {
499   struct Plugin *plugin = cls;
500   struct Value *values[num_results];
501   struct GetClosestContext gcc = {
502     .values = values,
503     .num_results = num_results,
504     .key = key
505   };
506   GNUNET_CONTAINER_multihashmap_iterate (plugin->map,
507                                          &find_closest,
508                                          &gcc);
509   for (unsigned int i=0;i<num_results;i++)
510   {
511     if (NULL == values[i])
512       return i;
513     iter (iter_cls,
514           &values[i]->key,
515           values[i]->size,
516           (void *) &values[i][1],
517           values[i]->type,
518           values[i]->discard_time,
519           values[i]->path_info_len,
520           values[i]->path_info);
521   }
522   return num_results;
523 }
524
525
526 /**
527  * Entry point for the plugin.
528  *
529  * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
530  * @return the plugin's closure (our `struct Plugin`)
531  */
532 void *
533 libgnunet_plugin_datacache_heap_init (void *cls)
534 {
535   struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
536   struct GNUNET_DATACACHE_PluginFunctions *api;
537   struct Plugin *plugin;
538
539   plugin = GNUNET_new (struct Plugin);
540   plugin->map = GNUNET_CONTAINER_multihashmap_create (1024,  /* FIXME: base on quota! */
541                                                       GNUNET_YES);
542   for (unsigned int i=0;i<NUM_HEAPS;i++)
543     plugin->heaps[i] = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
544   plugin->env = env;
545   api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
546   api->cls = plugin;
547   api->get = &heap_plugin_get;
548   api->put = &heap_plugin_put;
549   api->del = &heap_plugin_del;
550   api->get_random = &heap_plugin_get_random;
551   api->get_closest = &heap_plugin_get_closest;
552   LOG (GNUNET_ERROR_TYPE_INFO,
553        _("Heap datacache running\n"));
554   return api;
555 }
556
557
558 /**
559  * Exit point from the plugin.
560  *
561  * @param cls closure (our "struct Plugin")
562  * @return NULL
563  */
564 void *
565 libgnunet_plugin_datacache_heap_done (void *cls)
566 {
567   struct GNUNET_DATACACHE_PluginFunctions *api = cls;
568   struct Plugin *plugin = api->cls;
569   struct Value *val;
570
571   for (unsigned int i=0;i<NUM_HEAPS;i++)
572   {
573     while (NULL != (val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i])))
574     {
575       GNUNET_assert (GNUNET_YES ==
576                      GNUNET_CONTAINER_multihashmap_remove (plugin->map,
577                                                            &val->key,
578                                                            val));
579       GNUNET_free_non_null (val->path_info);
580       GNUNET_free (val);
581     }
582     GNUNET_CONTAINER_heap_destroy (plugin->heaps[i]);
583   }
584   GNUNET_CONTAINER_multihashmap_destroy (plugin->map);
585   GNUNET_free (plugin);
586   GNUNET_free (api);
587   return NULL;
588 }
589
590
591
592 /* end of plugin_datacache_heap.c */