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