another special case for loopback
[oweals/gnunet.git] / src / datastore / plugin_datastore_heap.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2012 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 datastore/plugin_datastore_heap.c
23  * @brief heap-based datastore backend; usually we want the datastore
24  *        to be persistent, and storing data in the heap is obviously
25  *        NOT going to be persistent; still, this plugin is useful for
26  *        testing/benchmarking --- but never for production!
27  * @author Christian Grothoff
28  */
29
30 #include "platform.h"
31 #include "gnunet_datastore_plugin.h"
32
33
34 /**
35  * A value that we are storing.
36  */
37 struct Value
38 {
39
40   /**
41    * Key for the value.
42    */
43   struct GNUNET_HashCode key;
44
45   /**
46    * Pointer to the value's data (allocated at the end of this struct).
47    */
48   const void *data;
49
50   /**
51    * Entry for this value in the 'expire' heap.
52    */
53   struct GNUNET_CONTAINER_HeapNode *expire_heap;
54
55   /**
56    * Entry for this value in the 'replication' heap.
57    */
58   struct GNUNET_CONTAINER_HeapNode *replication_heap;
59
60   /**
61    * Expiration time for this value.
62    */
63   struct GNUNET_TIME_Absolute expiration;
64
65   /**
66    * Offset of this value in the array of the 'struct ZeroAnonByType';
67    * only used if anonymity is zero.
68    */
69   unsigned int zero_anon_offset;
70
71   /**
72    * Number of bytes in 'data'.
73    */
74   uint32_t size;
75
76   /**
77    * Priority of the value.
78    */
79   uint32_t priority;
80
81   /**
82    * Anonymity level for the value.
83    */
84   uint32_t anonymity;
85
86   /**
87    * Replication level for the value.
88    */
89   uint32_t replication;
90
91   /**
92    * Type of 'data'.
93    */
94   enum GNUNET_BLOCK_Type type;
95
96 };
97
98
99 /**
100  * We organize 0-anonymity values in arrays "by type".
101  */
102 struct ZeroAnonByType
103 {
104
105   /**
106    * We keep these in a DLL.
107    */
108   struct ZeroAnonByType *next;
109
110   /**
111    * We keep these in a DLL.
112    */
113   struct ZeroAnonByType *prev;
114
115   /**
116    * Array of 0-anonymity items of the given type.
117    */
118   struct Value **array;
119
120   /**
121    * Allocated size of the array.
122    */
123   unsigned int array_size;
124
125   /**
126    * First unused offset in 'array'.
127    */
128   unsigned int array_pos;
129
130   /**
131    * Type of all of the values in 'array'.
132    */
133   enum GNUNET_BLOCK_Type type;
134 };
135
136
137 /**
138  * Context for all functions in this plugin.
139  */
140 struct Plugin
141 {
142   /**
143    * Our execution environment.
144    */
145   struct GNUNET_DATASTORE_PluginEnvironment *env;
146
147   /**
148    * Mapping from keys to 'struct Value's.
149    */
150   struct GNUNET_CONTAINER_MultiHashMap *keyvalue;
151
152   /**
153    * Heap organized by minimum expiration time.
154    */
155   struct GNUNET_CONTAINER_Heap *by_expiration;
156
157   /**
158    * Heap organized by maximum replication value.
159    */
160   struct GNUNET_CONTAINER_Heap *by_replication;
161
162   /**
163    * Head of list of arrays containing zero-anonymity values by type.
164    */
165   struct ZeroAnonByType *zero_head;
166
167   /**
168    * Tail of list of arrays containing zero-anonymity values by type.
169    */
170   struct ZeroAnonByType *zero_tail;
171
172   /**
173    * Size of all values we're storing.
174    */
175   unsigned long long size;
176
177 };
178
179
180 /**
181  * Get an estimate of how much space the database is
182  * currently using.
183  *
184  * @param cls our "struct Plugin*"
185  * @return number of bytes used on disk
186  */
187 static void
188 heap_plugin_estimate_size (void *cls, unsigned long long *estimate)
189 {
190   struct Plugin *plugin = cls;
191
192   if (NULL != estimate)
193     *estimate = plugin->size;
194 }
195
196
197 /**
198  * Store an item in the datastore.
199  *
200  * @param cls closure
201  * @param key key for the item
202  * @param size number of bytes in data
203  * @param data content stored
204  * @param type type of the content
205  * @param priority priority of the content
206  * @param anonymity anonymity-level for the content
207  * @param replication replication-level for the content
208  * @param expiration expiration time for the content
209  * @param cont continuation called with success or failure status
210  * @param cont_cls continuation closure
211  */
212 static void
213 heap_plugin_put (void *cls,
214                  const struct GNUNET_HashCode * key,
215                  uint32_t size,
216                  const void *data,
217                  enum GNUNET_BLOCK_Type type,
218                  uint32_t priority, uint32_t anonymity,
219                  uint32_t replication,
220                  struct GNUNET_TIME_Absolute expiration,
221                  PluginPutCont cont,
222                  void *cont_cls)
223 {
224   struct Plugin *plugin = cls;
225   struct Value *value;
226
227   value = GNUNET_malloc (sizeof (struct Value) + size);
228   value->key = *key;
229   value->data = &value[1];
230   value->expire_heap = GNUNET_CONTAINER_heap_insert (plugin->by_expiration,
231                                                      value,
232                                                      expiration.abs_value_us);
233   value->replication_heap = GNUNET_CONTAINER_heap_insert (plugin->by_replication,
234                                                           value,
235                                                           replication);
236   value->expiration = expiration;
237   if (0 == anonymity)
238   {
239     struct ZeroAnonByType *zabt;
240
241     for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
242       if (zabt->type == type)
243         break;
244     if (NULL == zabt)
245     {
246       zabt = GNUNET_new (struct ZeroAnonByType);
247       zabt->type = type;
248       GNUNET_CONTAINER_DLL_insert (plugin->zero_head,
249                                    plugin->zero_tail,
250                                    zabt);
251     }
252     if (zabt->array_size == zabt->array_pos)
253     {
254       GNUNET_array_grow (zabt->array,
255                          zabt->array_size,
256                          zabt->array_size * 2 + 4);
257     }
258     value->zero_anon_offset = zabt->array_pos;
259     zabt->array[zabt->array_pos++] = value;
260   }
261   value->size = size;
262   value->priority = priority;
263   value->anonymity = anonymity;
264   value->replication = replication;
265   value->type = type;
266   GNUNET_memcpy (&value[1], data, size);
267   GNUNET_CONTAINER_multihashmap_put (plugin->keyvalue,
268                                      &value->key,
269                                      value,
270                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
271   plugin->size += size;
272   cont (cont_cls, key, size, GNUNET_OK, NULL);
273 }
274
275
276 /**
277  * Delete the given value, removing it from the plugin's data
278  * structures.
279  *
280  * @param plugin the plugin
281  * @param value value to delete
282  */
283 static void
284 delete_value (struct Plugin *plugin,
285               struct Value *value)
286 {
287   GNUNET_assert (GNUNET_YES ==
288                  GNUNET_CONTAINER_multihashmap_remove (plugin->keyvalue,
289                                                        &value->key,
290                                                        value));
291   GNUNET_assert (value == GNUNET_CONTAINER_heap_remove_node (value->expire_heap));
292   GNUNET_assert (value == GNUNET_CONTAINER_heap_remove_node (value->replication_heap));
293   if (0 == value->anonymity)
294   {
295     struct ZeroAnonByType *zabt;
296
297     for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
298       if (zabt->type == value->type)
299         break;
300     GNUNET_assert (NULL != zabt);
301     zabt->array[value->zero_anon_offset] = zabt->array[--zabt->array_pos];
302     zabt->array[value->zero_anon_offset]->zero_anon_offset = value->zero_anon_offset;
303     if (0 == zabt->array_pos)
304     {
305       GNUNET_array_grow (zabt->array,
306                          zabt->array_size,
307                          0);
308       GNUNET_CONTAINER_DLL_remove (plugin->zero_head,
309                                    plugin->zero_tail,
310                                    zabt);
311       GNUNET_free (zabt);
312     }
313   }
314   plugin->size -= value->size;
315   GNUNET_free (value);
316 }
317
318
319 /**
320  * Closure for iterator called during 'get_key'.
321  */
322 struct GetContext
323 {
324
325   /**
326    * Desired result offset / number of results.
327    */
328   uint64_t offset;
329
330   /**
331    * The plugin.
332    */
333   struct Plugin *plugin;
334
335   /**
336    * Requested value hash.
337    */
338   const struct GNUNET_HashCode * vhash;
339
340   /**
341    * Requested type.
342    */
343   enum GNUNET_BLOCK_Type type;
344
345   /**
346    * Function to call with the result.
347    */
348   PluginDatumProcessor proc;
349
350   /**
351    * Closure for 'proc'.
352    */
353   void *proc_cls;
354 };
355
356
357 /**
358  * Test if a value matches the specification from the 'get' context
359  *
360  * @param gc query
361  * @param value the value to check against the query
362  * @return GNUNET_YES if the value matches
363  */
364 static int
365 match (const struct GetContext *gc,
366        struct Value *value)
367 {
368   struct GNUNET_HashCode vh;
369
370   if ( (gc->type != GNUNET_BLOCK_TYPE_ANY) &&
371        (gc->type != value->type) )
372     return GNUNET_NO;
373   if (NULL != gc->vhash)
374   {
375     GNUNET_CRYPTO_hash (&value[1], value->size, &vh);
376     if (0 != memcmp (&vh, gc->vhash, sizeof (struct GNUNET_HashCode)))
377       return GNUNET_NO;
378   }
379   return GNUNET_YES;
380 }
381
382
383 /**
384  * Count number of matching values.
385  *
386  * @param cls the 'struct GetContext'
387  * @param key unused
388  * @param val the 'struct Value'
389  * @return GNUNET_YES (continue iteration)
390  */
391 static int
392 count_iterator (void *cls,
393                 const struct GNUNET_HashCode *key,
394                 void *val)
395 {
396   struct GetContext *gc = cls;
397   struct Value *value = val;
398
399   if (GNUNET_NO == match (gc, value))
400     return GNUNET_OK;
401   gc->offset++;
402   return GNUNET_OK;
403 }
404
405
406 /**
407  * Obtain matching value at 'offset'.
408  *
409  * @param cls the 'struct GetContext'
410  * @param key unused
411  * @param val the 'struct Value'
412  * @return GNUNET_YES (continue iteration), GNUNET_NO if result was found
413  */
414 static int
415 get_iterator (void *cls,
416               const struct GNUNET_HashCode *key,
417               void *val)
418 {
419   struct GetContext *gc = cls;
420   struct Value *value = val;
421
422   if (GNUNET_NO == match (gc, value))
423     return GNUNET_OK;
424   if (0 != gc->offset--)
425     return GNUNET_OK;
426   if (GNUNET_NO ==
427       gc->proc (gc->proc_cls,
428                 key,
429                 value->size,
430                 &value[1],
431                 value->type,
432                 value->priority,
433                 value->anonymity,
434             value->expiration,
435                 (uint64_t) (long) value))
436     delete_value (gc->plugin, value);
437   return GNUNET_NO;
438 }
439
440
441 /**
442  * Get one of the results for a particular key in the datastore.
443  *
444  * @param cls closure
445  * @param offset offset of the result (modulo num-results);
446  *               specific ordering does not matter for the offset
447  * @param key maybe NULL (to match all entries)
448  * @param vhash hash of the value, maybe NULL (to
449  *        match all values that have the right key).
450  *        Note that for DBlocks there is no difference
451  *        betwen key and vhash, but for other blocks
452  *        there may be!
453  * @param type entries of which type are relevant?
454  *     Use 0 for any type.
455  * @param proc function to call on each matching value;
456  *        will be called with NULL if nothing matches
457  * @param proc_cls closure for proc
458  */
459 static void
460 heap_plugin_get_key (void *cls, uint64_t offset,
461                      const struct GNUNET_HashCode *key,
462                      const struct GNUNET_HashCode *vhash,
463                      enum GNUNET_BLOCK_Type type, PluginDatumProcessor proc,
464                      void *proc_cls)
465 {
466   struct Plugin *plugin = cls;
467   struct GetContext gc;
468
469   gc.plugin = plugin;
470   gc.offset = 0;
471   gc.vhash = vhash;
472   gc.type = type;
473   gc.proc = proc;
474   gc.proc_cls = proc_cls;
475   if (NULL == key)
476   {
477     GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
478                                            &count_iterator,
479                                            &gc);
480     if (0 == gc.offset)
481     {
482       proc (proc_cls,
483             NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
484       return;
485     }
486     gc.offset = offset % gc.offset;
487     GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
488                                            &get_iterator,
489                                            &gc);
490   }
491   else
492   {
493     GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
494                                                 key,
495                                                 &count_iterator,
496                                                 &gc);
497     if (0 == gc.offset)
498     {
499       proc (proc_cls,
500             NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
501       return;
502     }
503     gc.offset = offset % gc.offset;
504     GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
505                                                 key,
506                                                 &get_iterator,
507                                                 &gc);
508   }
509 }
510
511
512 /**
513  * Get a random item for replication.  Returns a single, not expired,
514  * random item from those with the highest replication counters.  The
515  * item's replication counter is decremented by one IF it was positive
516  * before.  Call 'proc' with all values ZERO or NULL if the datastore
517  * is empty.
518  *
519  * @param cls closure
520  * @param proc function to call the value (once only).
521  * @param proc_cls closure for proc
522  */
523 static void
524 heap_plugin_get_replication (void *cls,
525                              PluginDatumProcessor proc,
526                              void *proc_cls)
527 {
528   struct Plugin *plugin = cls;
529   struct Value *value;
530
531   value = GNUNET_CONTAINER_heap_remove_root (plugin->by_replication);
532   if (NULL == value)
533   {
534     proc (proc_cls,
535           NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
536     return;
537   }
538   if (value->replication > 0)
539   {
540     value->replication--;
541     value->replication_heap = GNUNET_CONTAINER_heap_insert (plugin->by_replication,
542                                                             value,
543                                                             value->replication);
544   }
545   else
546   {
547     /* need a better way to pick a random item, replication level is always 0 */
548     value->replication_heap = GNUNET_CONTAINER_heap_insert (plugin->by_replication,
549                                                             value,
550                                                             value->replication);
551     value = GNUNET_CONTAINER_heap_walk_get_next (plugin->by_replication);
552   }
553   if (GNUNET_NO ==
554       proc (proc_cls,
555             &value->key,
556             value->size,
557             &value[1],
558             value->type,
559             value->priority,
560             value->anonymity,
561             value->expiration,
562             (uint64_t) (long) value))
563     delete_value (plugin, value);
564 }
565
566
567 /**
568  * Get a random item for expiration.  Call 'proc' with all values ZERO
569  * or NULL if the datastore is empty.
570  *
571  * @param cls closure
572  * @param proc function to call the value (once only).
573  * @param proc_cls closure for proc
574  */
575 static void
576 heap_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
577                             void *proc_cls)
578 {
579   struct Plugin *plugin = cls;
580   struct Value *value;
581
582   value = GNUNET_CONTAINER_heap_peek (plugin->by_expiration);
583   if (NULL == value)
584   {
585     proc (proc_cls,
586           NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
587     return;
588   }
589   if (GNUNET_NO ==
590       proc (proc_cls,
591             &value->key,
592             value->size,
593             &value[1],
594             value->type,
595             value->priority,
596             value->anonymity,
597             value->expiration,
598             (uint64_t) (long) value))
599     delete_value (plugin, value);
600 }
601
602
603 /**
604  * Update the priority for a particular key in the datastore.  If
605  * the expiration time in value is different than the time found in
606  * the datastore, the higher value should be kept.  For the
607  * anonymity level, the lower value is to be used.  The specified
608  * priority should be added to the existing priority, ignoring the
609  * priority in value.
610  *
611  * @param cls our `struct Plugin *`
612  * @param uid unique identifier of the datum
613  * @param delta by how much should the priority
614  *     change?  If priority + delta < 0 the
615  *     priority should be set to 0 (never go
616  *     negative).
617  * @param expire new expiration time should be the
618  *     MAX of any existing expiration time and
619  *     this value
620  * @param cont continuation called with success or failure status
621  * @param cons_cls continuation closure
622  */
623 static void
624 heap_plugin_update (void *cls,
625                     uint64_t uid,
626                     int delta,
627                     struct GNUNET_TIME_Absolute expire,
628                     PluginUpdateCont cont,
629                     void *cont_cls)
630 {
631   struct Value *value;
632
633   value = (struct Value*) (long) uid;
634   GNUNET_assert (NULL != value);
635   if (value->expiration.abs_value_us != expire.abs_value_us)
636   {
637     value->expiration = expire;
638     GNUNET_CONTAINER_heap_update_cost (value->expire_heap,
639                                        expire.abs_value_us);
640   }
641   if ( (delta < 0) && (value->priority < - delta) )
642     value->priority = 0;
643   else
644     value->priority += delta;
645   cont (cont_cls, GNUNET_OK, NULL);
646 }
647
648
649 /**
650  * Call the given processor on an item with zero anonymity.
651  *
652  * @param cls our "struct Plugin*"
653  * @param offset offset of the result (modulo num-results);
654  *               specific ordering does not matter for the offset
655  * @param type entries of which type should be considered?
656  *        Use 0 for any type.
657  * @param proc function to call on each matching value;
658  *        will be called  with NULL if no value matches
659  * @param proc_cls closure for proc
660  */
661 static void
662 heap_plugin_get_zero_anonymity (void *cls, uint64_t offset,
663                                 enum GNUNET_BLOCK_Type type,
664                                 PluginDatumProcessor proc, void *proc_cls)
665 {
666   struct Plugin *plugin = cls;
667   struct ZeroAnonByType *zabt;
668   struct Value *value;
669   uint64_t count;
670
671   count = 0;
672   for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
673   {
674     if ( (type != GNUNET_BLOCK_TYPE_ANY) &&
675          (type != zabt->type) )
676       continue;
677     count += zabt->array_pos;
678   }
679   if (0 == count)
680   {
681     proc (proc_cls,
682           NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
683     return;
684   }
685   offset = offset % count;
686   for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
687   {
688     if ( (type != GNUNET_BLOCK_TYPE_ANY) &&
689          (type != zabt->type) )
690       continue;
691     if (offset >= zabt->array_pos)
692     {
693       offset -= zabt->array_pos;
694       continue;
695     }
696     break;
697   }
698   GNUNET_assert (NULL != zabt);
699   value = zabt->array[offset];
700   if (GNUNET_NO ==
701       proc (proc_cls,
702             &value->key,
703             value->size,
704             &value[1],
705             value->type,
706             value->priority,
707             value->anonymity,
708             value->expiration,
709             (uint64_t) (long) value))
710     delete_value (plugin, value);
711 }
712
713
714 /**
715  * Drop database.
716  */
717 static void
718 heap_plugin_drop (void *cls)
719 {
720   /* nothing needs to be done */
721 }
722
723
724 /**
725  * Closure for the 'return_value' function.
726  */
727 struct GetAllContext
728 {
729   /**
730    * Function to call.
731    */
732   PluginKeyProcessor proc;
733
734   /**
735    * Closure for 'proc'.
736    */
737   void *proc_cls;
738 };
739
740
741 /**
742  * Callback invoked to call callback on each value.
743  *
744  * @param cls the plugin
745  * @param key unused
746  * @param val the value
747  * @return GNUNET_OK (continue to iterate)
748  */
749 static int
750 return_value (void *cls,
751               const struct GNUNET_HashCode *key,
752               void *val)
753 {
754   struct GetAllContext *gac = cls;
755
756   gac->proc (gac->proc_cls,
757              key,
758              1);
759   return GNUNET_OK;
760 }
761
762
763 /**
764  * Get all of the keys in the datastore.
765  *
766  * @param cls closure
767  * @param proc function to call on each key
768  * @param proc_cls closure for proc
769  */
770 static void
771 heap_get_keys (void *cls,
772                PluginKeyProcessor proc,
773                void *proc_cls)
774 {
775   struct Plugin *plugin = cls;
776   struct GetAllContext gac;
777
778   gac.proc = proc;
779   gac.proc_cls = proc_cls;
780   GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
781                                          &return_value,
782                                          &gac);
783   proc (proc_cls, NULL, 0);
784 }
785
786
787 /**
788  * Entry point for the plugin.
789  *
790  * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
791  * @return our "struct Plugin*"
792  */
793 void *
794 libgnunet_plugin_datastore_heap_init (void *cls)
795 {
796   struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
797   struct GNUNET_DATASTORE_PluginFunctions *api;
798   struct Plugin *plugin;
799   unsigned long long esize;
800
801   if (GNUNET_OK !=
802       GNUNET_CONFIGURATION_get_value_number (env->cfg,
803                                              "datastore-heap",
804                                              "HASHMAPSIZE",
805                                              &esize))
806     esize = 128 * 1024;
807   plugin = GNUNET_new (struct Plugin);
808   plugin->env = env;
809   plugin->keyvalue = GNUNET_CONTAINER_multihashmap_create (esize, GNUNET_YES);
810   plugin->by_expiration = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
811   plugin->by_replication = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
812   api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
813   api->cls = plugin;
814   api->estimate_size = &heap_plugin_estimate_size;
815   api->put = &heap_plugin_put;
816   api->update = &heap_plugin_update;
817   api->get_key = &heap_plugin_get_key;
818   api->get_replication = &heap_plugin_get_replication;
819   api->get_expiration = &heap_plugin_get_expiration;
820   api->get_zero_anonymity = &heap_plugin_get_zero_anonymity;
821   api->drop = &heap_plugin_drop;
822   api->get_keys = &heap_get_keys;
823   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "heap",
824                    _("Heap database running\n"));
825   return api;
826 }
827
828
829 /**
830  * Callback invoked to free all value.
831  *
832  * @param cls the plugin
833  * @param key unused
834  * @param val the value
835  * @return GNUNET_OK (continue to iterate)
836  */
837 static int
838 free_value (void *cls,
839             const struct GNUNET_HashCode *key,
840             void *val)
841 {
842   struct Plugin *plugin = cls;
843   struct Value *value = val;
844
845   delete_value (plugin, value);
846   return GNUNET_OK;
847 }
848
849
850 /**
851  * Exit point from the plugin.
852  * @param cls our "struct Plugin*"
853  * @return always NULL
854  */
855 void *
856 libgnunet_plugin_datastore_heap_done (void *cls)
857 {
858   struct GNUNET_DATASTORE_PluginFunctions *api = cls;
859   struct Plugin *plugin = api->cls;
860
861   GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
862                                          &free_value,
863                                          plugin);
864   GNUNET_CONTAINER_multihashmap_destroy (plugin->keyvalue);
865   GNUNET_CONTAINER_heap_destroy (plugin->by_expiration);
866   GNUNET_CONTAINER_heap_destroy (plugin->by_replication);
867   GNUNET_free (plugin);
868   GNUNET_free (api);
869   return NULL;
870 }
871
872 /* end of plugin_datastore_heap.c */