indentation, comment and style fixes, no semantic changes
[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?
615  * @param expire new expiration time should be the
616  *     MAX of any existing expiration time and
617  *     this value
618  * @param cont continuation called with success or failure status
619  * @param cons_cls continuation closure
620  */
621 static void
622 heap_plugin_update (void *cls,
623                     uint64_t uid,
624                     uint32_t delta,
625                     struct GNUNET_TIME_Absolute expire,
626                     PluginUpdateCont cont,
627                     void *cont_cls)
628 {
629   struct Value *value;
630
631   value = (struct Value*) (long) uid;
632   GNUNET_assert (NULL != value);
633   if (value->expiration.abs_value_us != expire.abs_value_us)
634   {
635     value->expiration = expire;
636     GNUNET_CONTAINER_heap_update_cost (value->expire_heap,
637                                        expire.abs_value_us);
638   }
639   /* Saturating add, don't overflow */
640   if (value->priority > UINT32_MAX - delta)
641     value->priority = UINT32_MAX;
642   else
643     value->priority += delta;
644   cont (cont_cls, GNUNET_OK, NULL);
645 }
646
647
648 /**
649  * Call the given processor on an item with zero anonymity.
650  *
651  * @param cls our "struct Plugin*"
652  * @param offset offset of the result (modulo num-results);
653  *               specific ordering does not matter for the offset
654  * @param type entries of which type should be considered?
655  *        Use 0 for any type.
656  * @param proc function to call on each matching value;
657  *        will be called  with NULL if no value matches
658  * @param proc_cls closure for proc
659  */
660 static void
661 heap_plugin_get_zero_anonymity (void *cls, uint64_t offset,
662                                 enum GNUNET_BLOCK_Type type,
663                                 PluginDatumProcessor proc, void *proc_cls)
664 {
665   struct Plugin *plugin = cls;
666   struct ZeroAnonByType *zabt;
667   struct Value *value;
668   uint64_t count;
669
670   count = 0;
671   for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
672   {
673     if ( (type != GNUNET_BLOCK_TYPE_ANY) &&
674          (type != zabt->type) )
675       continue;
676     count += zabt->array_pos;
677   }
678   if (0 == count)
679   {
680     proc (proc_cls,
681           NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
682     return;
683   }
684   offset = offset % count;
685   for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
686   {
687     if ( (type != GNUNET_BLOCK_TYPE_ANY) &&
688          (type != zabt->type) )
689       continue;
690     if (offset >= zabt->array_pos)
691     {
692       offset -= zabt->array_pos;
693       continue;
694     }
695     break;
696   }
697   GNUNET_assert (NULL != zabt);
698   value = zabt->array[offset];
699   if (GNUNET_NO ==
700       proc (proc_cls,
701             &value->key,
702             value->size,
703             &value[1],
704             value->type,
705             value->priority,
706             value->anonymity,
707             value->expiration,
708             (uint64_t) (long) value))
709     delete_value (plugin, value);
710 }
711
712
713 /**
714  * Drop database.
715  */
716 static void
717 heap_plugin_drop (void *cls)
718 {
719   /* nothing needs to be done */
720 }
721
722
723 /**
724  * Closure for the 'return_value' function.
725  */
726 struct GetAllContext
727 {
728   /**
729    * Function to call.
730    */
731   PluginKeyProcessor proc;
732
733   /**
734    * Closure for 'proc'.
735    */
736   void *proc_cls;
737 };
738
739
740 /**
741  * Callback invoked to call callback on each value.
742  *
743  * @param cls the plugin
744  * @param key unused
745  * @param val the value
746  * @return GNUNET_OK (continue to iterate)
747  */
748 static int
749 return_value (void *cls,
750               const struct GNUNET_HashCode *key,
751               void *val)
752 {
753   struct GetAllContext *gac = cls;
754
755   gac->proc (gac->proc_cls,
756              key,
757              1);
758   return GNUNET_OK;
759 }
760
761
762 /**
763  * Get all of the keys in the datastore.
764  *
765  * @param cls closure
766  * @param proc function to call on each key
767  * @param proc_cls closure for proc
768  */
769 static void
770 heap_get_keys (void *cls,
771                PluginKeyProcessor proc,
772                void *proc_cls)
773 {
774   struct Plugin *plugin = cls;
775   struct GetAllContext gac;
776
777   gac.proc = proc;
778   gac.proc_cls = proc_cls;
779   GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
780                                          &return_value,
781                                          &gac);
782   proc (proc_cls, NULL, 0);
783 }
784
785
786 /**
787  * Entry point for the plugin.
788  *
789  * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
790  * @return our "struct Plugin*"
791  */
792 void *
793 libgnunet_plugin_datastore_heap_init (void *cls)
794 {
795   struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
796   struct GNUNET_DATASTORE_PluginFunctions *api;
797   struct Plugin *plugin;
798   unsigned long long esize;
799
800   if (GNUNET_OK !=
801       GNUNET_CONFIGURATION_get_value_number (env->cfg,
802                                              "datastore-heap",
803                                              "HASHMAPSIZE",
804                                              &esize))
805     esize = 128 * 1024;
806   plugin = GNUNET_new (struct Plugin);
807   plugin->env = env;
808   plugin->keyvalue = GNUNET_CONTAINER_multihashmap_create (esize, GNUNET_YES);
809   plugin->by_expiration = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
810   plugin->by_replication = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
811   api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
812   api->cls = plugin;
813   api->estimate_size = &heap_plugin_estimate_size;
814   api->put = &heap_plugin_put;
815   api->update = &heap_plugin_update;
816   api->get_key = &heap_plugin_get_key;
817   api->get_replication = &heap_plugin_get_replication;
818   api->get_expiration = &heap_plugin_get_expiration;
819   api->get_zero_anonymity = &heap_plugin_get_zero_anonymity;
820   api->drop = &heap_plugin_drop;
821   api->get_keys = &heap_get_keys;
822   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "heap",
823                    _("Heap database running\n"));
824   return api;
825 }
826
827
828 /**
829  * Callback invoked to free all value.
830  *
831  * @param cls the plugin
832  * @param key unused
833  * @param val the value
834  * @return GNUNET_OK (continue to iterate)
835  */
836 static int
837 free_value (void *cls,
838             const struct GNUNET_HashCode *key,
839             void *val)
840 {
841   struct Plugin *plugin = cls;
842   struct Value *value = val;
843
844   delete_value (plugin, value);
845   return GNUNET_OK;
846 }
847
848
849 /**
850  * Exit point from the plugin.
851  * @param cls our "struct Plugin*"
852  * @return always NULL
853  */
854 void *
855 libgnunet_plugin_datastore_heap_done (void *cls)
856 {
857   struct GNUNET_DATASTORE_PluginFunctions *api = cls;
858   struct Plugin *plugin = api->cls;
859
860   GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
861                                          &free_value,
862                                          plugin);
863   GNUNET_CONTAINER_multihashmap_destroy (plugin->keyvalue);
864   GNUNET_CONTAINER_heap_destroy (plugin->by_expiration);
865   GNUNET_CONTAINER_heap_destroy (plugin->by_replication);
866   GNUNET_free (plugin);
867   GNUNET_free (api);
868   return NULL;
869 }
870
871 /* end of plugin_datastore_heap.c */