2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @file datastore/plugin_datastore_heap.c
21 * @brief heap-based datastore backend; usually we want the datastore
22 * to be persistent, and storing data in the heap is obviously
23 * NOT going to be persistent; still, this plugin is useful for
24 * testing/benchmarking --- but never for production!
25 * @author Christian Grothoff
29 #include "gnunet_datastore_plugin.h"
33 * A value that we are storing.
41 struct GNUNET_HashCode key;
44 * Pointer to the value's data (allocated at the end of this struct).
49 * Entry for this value in the 'expire' heap.
51 struct GNUNET_CONTAINER_HeapNode *expire_heap;
54 * Entry for this value in the 'replication' heap.
56 struct GNUNET_CONTAINER_HeapNode *replication_heap;
59 * Expiration time for this value.
61 struct GNUNET_TIME_Absolute expiration;
64 * Offset of this value in the array of the 'struct ZeroAnonByType';
65 * only used if anonymity is zero.
67 unsigned int zero_anon_offset;
70 * Number of bytes in 'data'.
75 * Priority of the value.
80 * Anonymity level for the value.
85 * Replication level for the value.
92 enum GNUNET_BLOCK_Type type;
98 * We organize 0-anonymity values in arrays "by type".
100 struct ZeroAnonByType
104 * We keep these in a DLL.
106 struct ZeroAnonByType *next;
109 * We keep these in a DLL.
111 struct ZeroAnonByType *prev;
114 * Array of 0-anonymity items of the given type.
116 struct Value **array;
119 * Allocated size of the array.
121 unsigned int array_size;
124 * First unused offset in 'array'.
126 unsigned int array_pos;
129 * Type of all of the values in 'array'.
131 enum GNUNET_BLOCK_Type type;
136 * Context for all functions in this plugin.
141 * Our execution environment.
143 struct GNUNET_DATASTORE_PluginEnvironment *env;
146 * Mapping from keys to 'struct Value's.
148 struct GNUNET_CONTAINER_MultiHashMap *keyvalue;
151 * Heap organized by minimum expiration time.
153 struct GNUNET_CONTAINER_Heap *by_expiration;
156 * Heap organized by maximum replication value.
158 struct GNUNET_CONTAINER_Heap *by_replication;
161 * Head of list of arrays containing zero-anonymity values by type.
163 struct ZeroAnonByType *zero_head;
166 * Tail of list of arrays containing zero-anonymity values by type.
168 struct ZeroAnonByType *zero_tail;
171 * Size of all values we're storing.
173 unsigned long long size;
179 * Get an estimate of how much space the database is
182 * @param cls our "struct Plugin*"
183 * @return number of bytes used on disk
186 heap_plugin_estimate_size (void *cls, unsigned long long *estimate)
188 struct Plugin *plugin = cls;
190 if (NULL != estimate)
191 *estimate = plugin->size;
196 * Closure for iterator for updating.
201 * Number of bytes in 'data'.
206 * Pointer to the data.
211 * Priority of the value.
216 * Replication level for the value.
218 uint32_t replication;
221 * Expiration time for this value.
223 struct GNUNET_TIME_Absolute expiration;
226 * True if the value was found and updated.
233 * Update the matching value.
235 * @param cls the 'struct UpdateContext'
237 * @param val the 'struct Value'
238 * @return GNUNET_YES (continue iteration), GNUNET_NO if value was found
241 update_iterator (void *cls,
242 const struct GNUNET_HashCode *key,
245 struct UpdateContext *uc = cls;
246 struct Value *value = val;
248 if (value->size != uc->size)
250 if (0 != memcmp (value->data, uc->data, uc->size))
252 uc->expiration = GNUNET_TIME_absolute_max (value->expiration,
254 if (value->expiration.abs_value_us != uc->expiration.abs_value_us)
256 value->expiration = uc->expiration;
257 GNUNET_CONTAINER_heap_update_cost (value->expire_heap,
258 value->expiration.abs_value_us);
260 /* Saturating adds, don't overflow */
261 if (value->priority > UINT32_MAX - uc->priority)
262 value->priority = UINT32_MAX;
264 value->priority += uc->priority;
265 if (value->replication > UINT32_MAX - uc->replication)
266 value->replication = UINT32_MAX;
268 value->replication += uc->replication;
274 * Store an item in the datastore.
277 * @param key key for the item
278 * @param absent true if the key was not found in the bloom filter
279 * @param size number of bytes in data
280 * @param data content stored
281 * @param type type of the content
282 * @param priority priority of the content
283 * @param anonymity anonymity-level for the content
284 * @param replication replication-level for the content
285 * @param expiration expiration time for the content
286 * @param cont continuation called with success or failure status
287 * @param cont_cls continuation closure
290 heap_plugin_put (void *cls,
291 const struct GNUNET_HashCode *key,
295 enum GNUNET_BLOCK_Type type,
298 uint32_t replication,
299 struct GNUNET_TIME_Absolute expiration,
303 struct Plugin *plugin = cls;
307 struct UpdateContext uc;
311 uc.priority = priority;
312 uc.replication = replication;
313 uc.expiration = expiration;
315 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
321 cont (cont_cls, key, size, GNUNET_NO, NULL);
325 value = GNUNET_malloc (sizeof (struct Value) + size);
327 value->data = &value[1];
328 value->expire_heap = GNUNET_CONTAINER_heap_insert (plugin->by_expiration,
330 expiration.abs_value_us);
331 value->replication_heap = GNUNET_CONTAINER_heap_insert (plugin->by_replication,
334 value->expiration = expiration;
337 struct ZeroAnonByType *zabt;
339 for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
340 if (zabt->type == type)
344 zabt = GNUNET_new (struct ZeroAnonByType);
346 GNUNET_CONTAINER_DLL_insert (plugin->zero_head,
350 if (zabt->array_size == zabt->array_pos)
352 GNUNET_array_grow (zabt->array,
354 zabt->array_size * 2 + 4);
356 value->zero_anon_offset = zabt->array_pos;
357 zabt->array[zabt->array_pos++] = value;
360 value->priority = priority;
361 value->anonymity = anonymity;
362 value->replication = replication;
364 GNUNET_memcpy (&value[1], data, size);
365 GNUNET_CONTAINER_multihashmap_put (plugin->keyvalue,
368 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
369 plugin->size += size;
370 cont (cont_cls, key, size, GNUNET_OK, NULL);
375 * Delete the given value, removing it from the plugin's data
378 * @param plugin the plugin
379 * @param value value to delete
382 delete_value (struct Plugin *plugin,
385 GNUNET_assert (GNUNET_YES ==
386 GNUNET_CONTAINER_multihashmap_remove (plugin->keyvalue,
389 GNUNET_assert (value == GNUNET_CONTAINER_heap_remove_node (value->expire_heap));
390 GNUNET_assert (value == GNUNET_CONTAINER_heap_remove_node (value->replication_heap));
391 if (0 == value->anonymity)
393 struct ZeroAnonByType *zabt;
395 for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
396 if (zabt->type == value->type)
398 GNUNET_assert (NULL != zabt);
399 zabt->array[value->zero_anon_offset] = zabt->array[--zabt->array_pos];
400 zabt->array[value->zero_anon_offset]->zero_anon_offset = value->zero_anon_offset;
401 if (0 == zabt->array_pos)
403 GNUNET_array_grow (zabt->array,
406 GNUNET_CONTAINER_DLL_remove (plugin->zero_head,
412 plugin->size -= value->size;
418 * Closure for iterator called during 'get_key'.
424 * Lowest uid to consider.
429 * Value with lowest uid >= next_uid found so far.
436 enum GNUNET_BLOCK_Type type;
439 * If true, return a random value
447 * Obtain the matching value with the lowest uid >= next_uid.
449 * @param cls the 'struct GetContext'
451 * @param val the 'struct Value'
452 * @return GNUNET_YES (continue iteration), GNUNET_NO if result was found
455 get_iterator (void *cls,
456 const struct GNUNET_HashCode *key,
459 struct GetContext *gc = cls;
460 struct Value *value = val;
462 if ( (gc->type != GNUNET_BLOCK_TYPE_ANY) &&
463 (gc->type != value->type) )
470 if ( (uint64_t) (intptr_t) value < gc->next_uid)
472 if ( (NULL != gc->value) &&
473 (value > gc->value) )
481 * Get one of the results for a particular key in the datastore.
484 * @param next_uid return the result with lowest uid >= next_uid
485 * @param random if true, return a random result instead of using next_uid
486 * @param key maybe NULL (to match all entries)
487 * @param type entries of which type are relevant?
488 * Use 0 for any type.
489 * @param proc function to call on the matching value;
490 * will be called with NULL if nothing matches
491 * @param proc_cls closure for @a proc
494 heap_plugin_get_key (void *cls,
497 const struct GNUNET_HashCode *key,
498 enum GNUNET_BLOCK_Type type,
499 PluginDatumProcessor proc,
502 struct Plugin *plugin = cls;
503 struct GetContext gc;
506 gc.next_uid = next_uid;
511 GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
517 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
522 if (NULL == gc.value)
524 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
527 GNUNET_assert (GNUNET_OK ==
535 gc.value->replication,
536 gc.value->expiration,
537 (uint64_t) (intptr_t) gc.value));
542 * Get a random item for replication. Returns a single, not expired,
543 * random item from those with the highest replication counters. The
544 * item's replication counter is decremented by one IF it was positive
545 * before. Call 'proc' with all values ZERO or NULL if the datastore
549 * @param proc function to call the value (once only).
550 * @param proc_cls closure for proc
553 heap_plugin_get_replication (void *cls,
554 PluginDatumProcessor proc,
557 struct Plugin *plugin = cls;
560 value = GNUNET_CONTAINER_heap_remove_root (plugin->by_replication);
563 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
566 if (value->replication > 0)
568 value->replication--;
569 value->replication_heap = GNUNET_CONTAINER_heap_insert (plugin->by_replication,
575 /* need a better way to pick a random item, replication level is always 0 */
576 value->replication_heap = GNUNET_CONTAINER_heap_insert (plugin->by_replication,
579 value = GNUNET_CONTAINER_heap_walk_get_next (plugin->by_replication);
581 GNUNET_assert (GNUNET_OK ==
591 (uint64_t) (intptr_t) value));
596 * Get a random item for expiration. Call 'proc' with all values ZERO
597 * or NULL if the datastore is empty.
600 * @param proc function to call the value (once only).
601 * @param proc_cls closure for proc
604 heap_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
607 struct Plugin *plugin = cls;
610 value = GNUNET_CONTAINER_heap_peek (plugin->by_expiration);
613 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
626 (uint64_t) (intptr_t) value))
627 delete_value (plugin, value);
632 * Call the given processor on an item with zero anonymity.
634 * @param cls our "struct Plugin*"
635 * @param next_uid return the result with lowest uid >= next_uid
636 * @param type entries of which type should be considered?
637 * Must not be zero (ANY).
638 * @param proc function to call on each matching value;
639 * will be called with NULL if no value matches
640 * @param proc_cls closure for proc
643 heap_plugin_get_zero_anonymity (void *cls, uint64_t next_uid,
644 enum GNUNET_BLOCK_Type type,
645 PluginDatumProcessor proc, void *proc_cls)
647 struct Plugin *plugin = cls;
648 struct ZeroAnonByType *zabt;
649 struct Value *value = NULL;
651 for (zabt = plugin->zero_head; NULL != zabt; zabt = zabt->next)
653 if ( (type != GNUNET_BLOCK_TYPE_ANY) &&
654 (type != zabt->type) )
656 for (int i = 0; i < zabt->array_pos; ++i)
658 if ( (uint64_t) (intptr_t) zabt->array[i] < next_uid)
660 if ( (NULL != value) &&
661 (zabt->array[i] > value) )
663 value = zabt->array[i];
668 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
671 GNUNET_assert (GNUNET_OK ==
681 (uint64_t) (intptr_t) value));
689 heap_plugin_drop (void *cls)
691 /* nothing needs to be done */
696 * Closure for the 'return_value' function.
703 PluginKeyProcessor proc;
706 * Closure for 'proc'.
713 * Callback invoked to call callback on each value.
715 * @param cls the plugin
717 * @param val the value
718 * @return GNUNET_OK (continue to iterate)
721 return_value (void *cls,
722 const struct GNUNET_HashCode *key,
725 struct GetAllContext *gac = cls;
727 gac->proc (gac->proc_cls,
735 * Get all of the keys in the datastore.
738 * @param proc function to call on each key
739 * @param proc_cls closure for proc
742 heap_get_keys (void *cls,
743 PluginKeyProcessor proc,
746 struct Plugin *plugin = cls;
747 struct GetAllContext gac;
750 gac.proc_cls = proc_cls;
751 GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
754 proc (proc_cls, NULL, 0);
759 * Closure for iterator called during 'remove_key'.
783 * Obtain the matching value with the lowest uid >= next_uid.
785 * @param cls the 'struct GetContext'
787 * @param val the 'struct Value'
788 * @return GNUNET_YES (continue iteration), GNUNET_NO if result was found
791 remove_iterator (void *cls,
792 const struct GNUNET_HashCode *key,
795 struct RemoveContext *rc = cls;
796 struct Value *value = val;
798 if (value->size != rc->size)
800 if (0 != memcmp (value->data, rc->data, rc->size))
808 * Remove a particular key in the datastore.
811 * @param key key for the content
812 * @param size number of bytes in data
813 * @param data content stored
814 * @param cont continuation called with success or failure status
815 * @param cont_cls continuation closure for @a cont
818 heap_plugin_remove_key (void *cls,
819 const struct GNUNET_HashCode *key,
822 PluginRemoveCont cont,
825 struct Plugin *plugin = cls;
826 struct RemoveContext rc;
831 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->keyvalue,
835 if (NULL == rc.value)
844 delete_value (plugin,
855 * Entry point for the plugin.
857 * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
858 * @return our "struct Plugin*"
861 libgnunet_plugin_datastore_heap_init (void *cls)
863 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
864 struct GNUNET_DATASTORE_PluginFunctions *api;
865 struct Plugin *plugin;
866 unsigned long long esize;
869 GNUNET_CONFIGURATION_get_value_number (env->cfg,
874 plugin = GNUNET_new (struct Plugin);
876 plugin->keyvalue = GNUNET_CONTAINER_multihashmap_create (esize, GNUNET_YES);
877 plugin->by_expiration = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
878 plugin->by_replication = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
879 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
881 api->estimate_size = &heap_plugin_estimate_size;
882 api->put = &heap_plugin_put;
883 api->get_key = &heap_plugin_get_key;
884 api->get_replication = &heap_plugin_get_replication;
885 api->get_expiration = &heap_plugin_get_expiration;
886 api->get_zero_anonymity = &heap_plugin_get_zero_anonymity;
887 api->drop = &heap_plugin_drop;
888 api->get_keys = &heap_get_keys;
889 api->remove_key = &heap_plugin_remove_key;
890 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "heap",
891 _("Heap database running\n"));
897 * Callback invoked to free all value.
899 * @param cls the plugin
901 * @param val the value
902 * @return GNUNET_OK (continue to iterate)
905 free_value (void *cls,
906 const struct GNUNET_HashCode *key,
909 struct Plugin *plugin = cls;
910 struct Value *value = val;
912 delete_value (plugin, value);
918 * Exit point from the plugin.
919 * @param cls our "struct Plugin*"
920 * @return always NULL
923 libgnunet_plugin_datastore_heap_done (void *cls)
925 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
926 struct Plugin *plugin = api->cls;
928 GNUNET_CONTAINER_multihashmap_iterate (plugin->keyvalue,
931 GNUNET_CONTAINER_multihashmap_destroy (plugin->keyvalue);
932 GNUNET_CONTAINER_heap_destroy (plugin->by_expiration);
933 GNUNET_CONTAINER_heap_destroy (plugin->by_replication);
934 GNUNET_free (plugin);
939 /* end of plugin_datastore_heap.c */