fix use-after-free on exit
[oweals/gnunet.git] / src / datastore / plugin_datastore_heap.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2012 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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 msg set to error message
210  * @return GNUNET_OK on success
211  */
212 static int
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, char **msg)
221 {
222   struct Plugin *plugin = cls;
223   struct Value *value;
224
225   value = GNUNET_malloc (sizeof (struct Value) + size);
226   value->key = *key;
227   value->data = &value[1];
228   value->expire_heap = GNUNET_CONTAINER_heap_insert (plugin->by_expiration,
229                                                      value,
230                                                      expiration.abs_value_us);
231   value->replication_heap = GNUNET_CONTAINER_heap_insert (plugin->by_replication,
232                                                           value,
233                                                           replication);
234   value->expiration = expiration;
235   if (0 == anonymity)
236   {
237     struct ZeroAnonByType *zabt;
238
239     for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
240       if (zabt->type == type)
241         break;
242     if (NULL == zabt)
243     {
244       zabt = GNUNET_new (struct ZeroAnonByType);
245       zabt->type = type;
246       GNUNET_CONTAINER_DLL_insert (plugin->zero_head,
247                                    plugin->zero_tail,
248                                    zabt);
249     }
250     if (zabt->array_size == zabt->array_pos)
251     {
252       GNUNET_array_grow (zabt->array,
253                          zabt->array_size,
254                          zabt->array_size * 2 + 4);
255     }
256     value->zero_anon_offset = zabt->array_pos;
257     zabt->array[zabt->array_pos++] = value;
258   }
259   value->size = size;
260   value->priority = priority;
261   value->anonymity = anonymity;
262   value->replication = replication;
263   value->type = type;
264   memcpy (&value[1], data, size);
265   GNUNET_CONTAINER_multihashmap_put (plugin->keyvalue,
266                                      &value->key,
267                                      value,
268                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
269   plugin->size += size;
270   return GNUNET_OK;
271 }
272
273
274 /**
275  * Delete the given value, removing it from the plugin's data
276  * structures.
277  *
278  * @param plugin the plugin
279  * @param value value to delete
280  */
281 static void
282 delete_value (struct Plugin *plugin,
283               struct Value *value)
284 {
285   GNUNET_assert (GNUNET_YES ==
286                  GNUNET_CONTAINER_multihashmap_remove (plugin->keyvalue,
287                                                        &value->key,
288                                                        value));
289   GNUNET_assert (value == GNUNET_CONTAINER_heap_remove_node (value->expire_heap));
290   GNUNET_assert (value == GNUNET_CONTAINER_heap_remove_node (value->replication_heap));
291   if (0 == value->anonymity)
292   {
293     struct ZeroAnonByType *zabt;
294
295     for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
296       if (zabt->type == value->type)
297         break;
298     GNUNET_assert (NULL != zabt);
299     zabt->array[value->zero_anon_offset] = zabt->array[--zabt->array_pos];
300     zabt->array[value->zero_anon_offset]->zero_anon_offset = value->zero_anon_offset;
301     if (0 == zabt->array_pos)
302     {
303       GNUNET_array_grow (zabt->array,
304                          zabt->array_size,
305                          0);
306       GNUNET_CONTAINER_DLL_remove (plugin->zero_head,
307                                    plugin->zero_tail,
308                                    zabt);
309       GNUNET_free (zabt);
310     }
311   }
312   plugin->size -= value->size;
313   GNUNET_free (value);
314 }
315
316
317 /**
318  * Closure for iterator called during 'get_key'.
319  */
320 struct GetContext
321 {
322
323   /**
324    * Desired result offset / number of results.
325    */
326   uint64_t offset;
327
328   /**
329    * The plugin.
330    */
331   struct Plugin *plugin;
332                 
333   /**
334    * Requested value hash.
335    */
336   const struct GNUNET_HashCode * vhash;
337
338   /**
339    * Requested type.
340    */
341   enum GNUNET_BLOCK_Type type;
342
343   /**
344    * Function to call with the result.
345    */
346   PluginDatumProcessor proc;
347
348   /**
349    * Closure for 'proc'.
350    */
351   void *proc_cls;
352 };
353
354
355 /**
356  * Test if a value matches the specification from the 'get' context
357  *
358  * @param gc query
359  * @param value the value to check against the query
360  * @return GNUNET_YES if the value matches
361  */
362 static int
363 match (const struct GetContext *gc,
364        struct Value *value)
365 {
366   struct GNUNET_HashCode vh;
367
368   if ( (gc->type != GNUNET_BLOCK_TYPE_ANY) &&
369        (gc->type != value->type) )
370     return GNUNET_NO;
371   if (NULL != gc->vhash)
372   {
373     GNUNET_CRYPTO_hash (&value[1], value->size, &vh);
374     if (0 != memcmp (&vh, gc->vhash, sizeof (struct GNUNET_HashCode)))
375       return GNUNET_NO;
376   }
377   return GNUNET_YES;
378 }
379
380
381 /**
382  * Count number of matching values.
383  *
384  * @param cls the 'struct GetContext'
385  * @param key unused
386  * @param val the 'struct Value'
387  * @return GNUNET_YES (continue iteration)
388  */
389 static int
390 count_iterator (void *cls,
391                 const struct GNUNET_HashCode *key,
392                 void *val)
393 {
394   struct GetContext *gc = cls;
395   struct Value *value = val;
396
397   if (GNUNET_NO == match (gc, value))
398     return GNUNET_OK;
399   gc->offset++;
400   return GNUNET_OK;
401 }
402
403
404 /**
405  * Obtain matching value at 'offset'.
406  *
407  * @param cls the 'struct GetContext'
408  * @param key unused
409  * @param val the 'struct Value'
410  * @return GNUNET_YES (continue iteration), GNUNET_NO if result was found
411  */
412 static int
413 get_iterator (void *cls,
414               const struct GNUNET_HashCode *key,
415               void *val)
416 {
417   struct GetContext *gc = cls;
418   struct Value *value = val;
419
420   if (GNUNET_NO == match (gc, value))
421     return GNUNET_OK;
422   if (0 != gc->offset--)
423     return GNUNET_OK;
424   if (GNUNET_NO ==
425       gc->proc (gc->proc_cls,
426                 key,
427                 value->size,
428                 &value[1],
429                 value->type,
430                 value->priority,
431                 value->anonymity,
432             value->expiration,
433                 (uint64_t) (long) value))
434     delete_value (gc->plugin, value);
435   return GNUNET_NO;
436 }
437
438
439 /**
440  * Get one of the results for a particular key in the datastore.
441  *
442  * @param cls closure
443  * @param offset offset of the result (modulo num-results);
444  *               specific ordering does not matter for the offset
445  * @param key maybe NULL (to match all entries)
446  * @param vhash hash of the value, maybe NULL (to
447  *        match all values that have the right key).
448  *        Note that for DBlocks there is no difference
449  *        betwen key and vhash, but for other blocks
450  *        there may be!
451  * @param type entries of which type are relevant?
452  *     Use 0 for any type.
453  * @param proc function to call on each matching value;
454  *        will be called with NULL if nothing matches
455  * @param proc_cls closure for proc
456  */
457 static void
458 heap_plugin_get_key (void *cls, uint64_t offset,
459                      const struct GNUNET_HashCode *key,
460                      const struct GNUNET_HashCode *vhash,
461                      enum GNUNET_BLOCK_Type type, PluginDatumProcessor proc,
462                      void *proc_cls)
463 {
464   struct Plugin *plugin = cls;
465   struct GetContext gc;
466
467   gc.plugin = plugin;
468   gc.offset = 0;
469   gc.vhash = vhash;
470   gc.type = type;
471   gc.proc = proc;
472   gc.proc_cls = proc_cls;
473   if (NULL == key)
474   {
475     GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
476                                            &count_iterator,
477                                            &gc);
478     if (0 == gc.offset)
479     {
480       proc (proc_cls,
481             NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
482       return;
483     }
484     gc.offset = offset % gc.offset;
485     GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
486                                            &get_iterator,
487                                            &gc);
488   }
489   else
490   {
491     GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
492                                                 key,
493                                                 &count_iterator,
494                                                 &gc);
495     if (0 == gc.offset)
496     {
497       proc (proc_cls,
498             NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
499       return;
500     }
501     gc.offset = offset % gc.offset;
502     GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
503                                                 key,
504                                                 &get_iterator,
505                                                 &gc);
506   }
507 }
508
509
510 /**
511  * Get a random item for replication.  Returns a single, not expired,
512  * random item from those with the highest replication counters.  The
513  * item's replication counter is decremented by one IF it was positive
514  * before.  Call 'proc' with all values ZERO or NULL if the datastore
515  * is empty.
516  *
517  * @param cls closure
518  * @param proc function to call the value (once only).
519  * @param proc_cls closure for proc
520  */
521 static void
522 heap_plugin_get_replication (void *cls,
523                              PluginDatumProcessor proc,
524                              void *proc_cls)
525 {
526   struct Plugin *plugin = cls;
527   struct Value *value;
528
529   value = GNUNET_CONTAINER_heap_remove_root (plugin->by_replication);
530   if (NULL == value)
531   {
532     proc (proc_cls,
533           NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
534     return;
535   }
536   if (value->replication > 0)
537   {
538     value->replication--;
539     value->replication_heap = GNUNET_CONTAINER_heap_insert (plugin->by_replication,
540                                                             value,
541                                                             value->replication);
542   }
543   else
544   {
545     /* need a better way to pick a random item, replication level is always 0 */
546     value->replication_heap = GNUNET_CONTAINER_heap_insert (plugin->by_replication,
547                                                             value,
548                                                             value->replication);
549     value = GNUNET_CONTAINER_heap_walk_get_next (plugin->by_replication);
550   }
551   if (GNUNET_NO ==
552       proc (proc_cls,
553             &value->key,
554             value->size,
555             &value[1],
556             value->type,
557             value->priority,
558             value->anonymity,
559             value->expiration,
560             (uint64_t) (long) value))
561     delete_value (plugin, value);
562 }
563
564
565 /**
566  * Get a random item for expiration.  Call 'proc' with all values ZERO
567  * or NULL if the datastore is empty.
568  *
569  * @param cls closure
570  * @param proc function to call the value (once only).
571  * @param proc_cls closure for proc
572  */
573 static void
574 heap_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
575                             void *proc_cls)
576 {
577   struct Plugin *plugin = cls;
578   struct Value *value;
579
580   value = GNUNET_CONTAINER_heap_peek (plugin->by_expiration);
581   if (NULL == value)
582   {
583     proc (proc_cls,
584           NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
585     return;
586   }
587   if (GNUNET_NO ==
588       proc (proc_cls,
589             &value->key,
590             value->size,
591             &value[1],
592             value->type,
593             value->priority,
594             value->anonymity,
595             value->expiration,
596             (uint64_t) (long) value))
597     delete_value (plugin, value);
598 }
599
600
601 /**
602  * Update the priority for a particular key in the datastore.  If
603  * the expiration time in value is different than the time found in
604  * the datastore, the higher value should be kept.  For the
605  * anonymity level, the lower value is to be used.  The specified
606  * priority should be added to the existing priority, ignoring the
607  * priority in value.
608  *
609  * @param cls our "struct Plugin*"
610  * @param uid unique identifier of the datum
611  * @param delta by how much should the priority
612  *     change?  If priority + delta < 0 the
613  *     priority should be set to 0 (never go
614  *     negative).
615  * @param expire new expiration time should be the
616  *     MAX of any existing expiration time and
617  *     this value
618  * @param msg set to error message
619  * @return GNUNET_OK on success
620  */
621 static int
622 heap_plugin_update (void *cls,
623                     uint64_t uid,
624                     int delta,
625                     struct GNUNET_TIME_Absolute expire, char **msg)
626 {
627   struct Plugin *plugin = cls;
628   struct Value *value;
629
630   value = (struct Value*) (long) uid;
631   GNUNET_assert (NULL != value);
632   if (value->expiration.abs_value_us != expire.abs_value_us)
633   {
634     value->expiration = expire;
635     GNUNET_CONTAINER_heap_update_cost (plugin->by_expiration,
636                                        value->expire_heap,
637                                        expire.abs_value_us);
638   }
639   if ( (delta < 0) && (value->priority < - delta) )
640     value->priority = 0;
641   else
642     value->priority += delta;
643   return GNUNET_OK;
644 }
645
646
647 /**
648  * Call the given processor on an item with zero anonymity.
649  *
650  * @param cls our "struct Plugin*"
651  * @param offset offset of the result (modulo num-results);
652  *               specific ordering does not matter for the offset
653  * @param type entries of which type should be considered?
654  *        Use 0 for any type.
655  * @param proc function to call on each matching value;
656  *        will be called  with NULL if no value matches
657  * @param proc_cls closure for proc
658  */
659 static void
660 heap_plugin_get_zero_anonymity (void *cls, uint64_t offset,
661                                 enum GNUNET_BLOCK_Type type,
662                                 PluginDatumProcessor proc, void *proc_cls)
663 {
664   struct Plugin *plugin = cls;
665   struct ZeroAnonByType *zabt;
666   struct Value *value;
667   uint64_t count;
668
669   count = 0;
670   for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
671   {
672     if ( (type != GNUNET_BLOCK_TYPE_ANY) &&
673          (type != zabt->type) )
674       continue;
675     count += zabt->array_pos;
676   }
677   if (0 == count)
678   {
679     proc (proc_cls,
680           NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
681     return;
682   }
683   offset = offset % count;
684   for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
685   {
686     if ( (type != GNUNET_BLOCK_TYPE_ANY) &&
687          (type != zabt->type) )
688       continue;
689     if (offset >= zabt->array_pos)
690     {
691       offset -= zabt->array_pos;
692       continue;
693     }
694     break;
695   }
696   GNUNET_assert (NULL != zabt);
697   value = zabt->array[offset];
698   if (GNUNET_NO ==
699       proc (proc_cls,
700             &value->key,
701             value->size,
702             &value[1],
703             value->type,
704             value->priority,
705             value->anonymity,
706             value->expiration,
707             (uint64_t) (long) value))
708     delete_value (plugin, value);
709 }
710
711
712 /**
713  * Drop database.
714  */
715 static void
716 heap_plugin_drop (void *cls)
717 {
718   /* nothing needs to be done */
719 }
720
721
722 /**
723  * Closure for the 'return_value' function.
724  */
725 struct GetAllContext
726 {
727   /**
728    * Function to call.
729    */
730   PluginKeyProcessor proc;
731
732   /**
733    * Closure for 'proc'.
734    */
735   void *proc_cls;
736 };
737
738
739 /**
740  * Callback invoked to call callback on each value.
741  *
742  * @param cls the plugin
743  * @param key unused
744  * @param val the value
745  * @return GNUNET_OK (continue to iterate)
746  */
747 static int
748 return_value (void *cls,
749               const struct GNUNET_HashCode *key,
750               void *val)
751 {
752   struct GetAllContext *gac = cls;
753
754   gac->proc (gac->proc_cls,
755              key,
756              1);
757   return GNUNET_OK;
758 }
759
760
761 /**
762  * Get all of the keys in the datastore.
763  *
764  * @param cls closure
765  * @param proc function to call on each key
766  * @param proc_cls closure for proc
767  */
768 static void
769 heap_get_keys (void *cls,
770                PluginKeyProcessor proc,
771                void *proc_cls)
772 {
773   struct Plugin *plugin = cls;
774   struct GetAllContext gac;
775
776   gac.proc = proc;
777   gac.proc_cls = proc_cls;
778   GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
779                                          &return_value,
780                                          &gac);
781 }
782
783
784 /**
785  * Entry point for the plugin.
786  *
787  * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
788  * @return our "struct Plugin*"
789  */
790 void *
791 libgnunet_plugin_datastore_heap_init (void *cls)
792 {
793   struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
794   struct GNUNET_DATASTORE_PluginFunctions *api;
795   struct Plugin *plugin;
796   unsigned long long esize;
797
798   if (GNUNET_OK !=
799       GNUNET_CONFIGURATION_get_value_number (env->cfg,
800                                              "datastore-heap",
801                                              "HASHMAPSIZE",
802                                              &esize))
803     esize = 128 * 1024;
804   plugin = GNUNET_new (struct Plugin);
805   plugin->env = env;
806   plugin->keyvalue = GNUNET_CONTAINER_multihashmap_create (esize, GNUNET_YES);
807   plugin->by_expiration = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
808   plugin->by_replication = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
809   api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
810   api->cls = plugin;
811   api->estimate_size = &heap_plugin_estimate_size;
812   api->put = &heap_plugin_put;
813   api->update = &heap_plugin_update;
814   api->get_key = &heap_plugin_get_key;
815   api->get_replication = &heap_plugin_get_replication;
816   api->get_expiration = &heap_plugin_get_expiration;
817   api->get_zero_anonymity = &heap_plugin_get_zero_anonymity;
818   api->drop = &heap_plugin_drop;
819   api->get_keys = &heap_get_keys;
820   GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "heap",
821                    _("Heap database running\n"));
822   return api;
823 }
824
825
826 /**
827  * Callback invoked to free all value.
828  *
829  * @param cls the plugin
830  * @param key unused
831  * @param val the value
832  * @return GNUNET_OK (continue to iterate)
833  */
834 static int
835 free_value (void *cls,
836             const struct GNUNET_HashCode *key,
837             void *val)
838 {
839   struct Plugin *plugin = cls;
840   struct Value *value = val;
841
842   delete_value (plugin, value);
843   return GNUNET_OK;
844 }
845
846
847 /**
848  * Exit point from the plugin.
849  * @param cls our "struct Plugin*"
850  * @return always NULL
851  */
852 void *
853 libgnunet_plugin_datastore_heap_done (void *cls)
854 {
855   struct GNUNET_DATASTORE_PluginFunctions *api = cls;
856   struct Plugin *plugin = api->cls;
857
858   GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
859                                          &free_value,
860                                          plugin);
861   GNUNET_CONTAINER_multihashmap_destroy (plugin->keyvalue);
862   GNUNET_CONTAINER_heap_destroy (plugin->by_expiration);
863   GNUNET_CONTAINER_heap_destroy (plugin->by_replication);
864   GNUNET_free (plugin);
865   GNUNET_free (api);
866   return NULL;
867 }
868
869 /* end of plugin_datastore_heap.c */