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