2 This file is part of GNUnet
3 Copyright (C) 2012, 2015 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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file datacache/plugin_datacache_heap.c
23 * @brief heap-only implementation of a database backend for the datacache
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_datacache_plugin.h"
30 #define LOG(kind,...) GNUNET_log_from (kind, "datacache-heap", __VA_ARGS__)
32 #define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache-heap", op, fn)
37 * Context for all functions in this plugin.
42 * Our execution environment.
44 struct GNUNET_DATACACHE_PluginEnvironment *env;
49 struct GNUNET_CONTAINER_MultiHashMap *map;
52 * Heaps sorted by distance.
54 struct GNUNET_CONTAINER_Heap *heaps[NUM_HEAPS];
60 * Entry in the hash map.
67 struct GNUNET_HashCode key;
72 struct GNUNET_TIME_Absolute discard_time;
75 * Corresponding node in the heap.
77 struct GNUNET_CONTAINER_HeapNode *hn;
82 struct GNUNET_PeerIdentity *path_info;
85 * Payload (actual payload follows this struct)
90 * Number of entries in @e path_info.
92 unsigned int path_info_len;
95 * How close is the hash to us? Determines which heap we are in!
102 enum GNUNET_BLOCK_Type type;
107 #define OVERHEAD (sizeof (struct Value) + 64)
111 * Closure for #put_cb().
116 * Expiration time for the new value.
118 struct GNUNET_TIME_Absolute discard_time;
121 * Data for the new value.
128 const struct GNUNET_PeerIdentity *path_info;
131 * Number of bytes in @e data.
138 enum GNUNET_BLOCK_Type type;
141 * Number of entries in @e path_info.
143 unsigned int path_info_len;
146 * Value to set to #GNUNET_YES if an equivalent block was found.
153 * Function called during PUT to detect if an equivalent block
156 * @param cls the `struct PutContext`
157 * @param key the key for the value(s)
158 * @param value an existing value
159 * @return #GNUNET_YES if not found (to continue to iterate)
163 const struct GNUNET_HashCode *key,
166 struct PutContext *put_ctx = cls;
167 struct Value *val = value;
169 if ( (val->size == put_ctx->size) &&
170 (val->type == put_ctx->type) &&
171 (0 == memcmp (&val[1],
175 put_ctx->found = GNUNET_YES;
176 val->discard_time = GNUNET_TIME_absolute_max (val->discard_time,
177 put_ctx->discard_time);
178 /* replace old path with new path */
179 GNUNET_array_grow (val->path_info,
181 put_ctx->path_info_len);
182 GNUNET_memcpy (val->path_info,
184 put_ctx->path_info_len * sizeof (struct GNUNET_PeerIdentity));
185 GNUNET_CONTAINER_heap_update_cost (val->hn,
186 val->discard_time.abs_value_us);
187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188 "Got same value for key %s and type %d (size %u vs %u)\n",
191 (unsigned int) val->size,
192 (unsigned int) put_ctx->size);
200 * Store an item in the datastore.
202 * @param cls closure (our `struct Plugin`)
203 * @param key key to store data under
204 * @param xor_distance how close is @a key to our PID?
205 * @param size number of bytes in @a data
206 * @param data data to store
207 * @param type type of the value
208 * @param discard_time when to discard the value in any case
209 * @param path_info_len number of entries in @a path_info
210 * @param path_info a path through the network
211 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
214 heap_plugin_put (void *cls,
215 const struct GNUNET_HashCode *key,
216 uint32_t xor_distance,
219 enum GNUNET_BLOCK_Type type,
220 struct GNUNET_TIME_Absolute discard_time,
221 unsigned int path_info_len,
222 const struct GNUNET_PeerIdentity *path_info)
224 struct Plugin *plugin = cls;
226 struct PutContext put_ctx;
228 put_ctx.found = GNUNET_NO;
231 put_ctx.path_info = path_info;
232 put_ctx.path_info_len = path_info_len;
233 put_ctx.discard_time = discard_time;
235 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
239 if (GNUNET_YES == put_ctx.found)
241 val = GNUNET_malloc (sizeof (struct Value) + size);
242 GNUNET_memcpy (&val[1],
247 val->discard_time = discard_time;
249 if (xor_distance >= NUM_HEAPS)
250 val->distance = NUM_HEAPS - 1;
252 val->distance = xor_distance;
253 GNUNET_array_grow (val->path_info,
256 GNUNET_memcpy (val->path_info,
258 path_info_len * sizeof (struct GNUNET_PeerIdentity));
259 (void) GNUNET_CONTAINER_multihashmap_put (plugin->map,
262 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
263 val->hn = GNUNET_CONTAINER_heap_insert (plugin->heaps[val->distance],
265 val->discard_time.abs_value_us);
266 return size + OVERHEAD;
271 * Closure for #get_cb().
276 * Function to call for each result.
278 GNUNET_DATACACHE_Iterator iter;
281 * Closure for @e iter.
286 * Number of results found.
291 * Block type requested.
293 enum GNUNET_BLOCK_Type type;
299 * Function called during GET to find matching blocks.
300 * Only matches by type.
302 * @param cls the `struct GetContext`
303 * @param key the key for the value(s)
304 * @param value an existing value
305 * @return #GNUNET_YES to continue to iterate
309 const struct GNUNET_HashCode *key,
312 struct GetContext *get_ctx = cls;
313 struct Value *val = value;
316 if ( (get_ctx->type != val->type) &&
317 (GNUNET_BLOCK_TYPE_ANY != get_ctx->type) )
320 GNUNET_TIME_absolute_get_remaining (val->discard_time).rel_value_us)
322 if (NULL != get_ctx->iter)
323 ret = get_ctx->iter (get_ctx->iter_cls,
326 (const char *) &val[1],
339 * Iterate over the results for a particular key
342 * @param cls closure (our `struct Plugin`)
344 * @param type entries of which type are relevant?
345 * @param iter maybe NULL (to just count)
346 * @param iter_cls closure for @a iter
347 * @return the number of results found
350 heap_plugin_get (void *cls,
351 const struct GNUNET_HashCode *key,
352 enum GNUNET_BLOCK_Type type,
353 GNUNET_DATACACHE_Iterator iter,
356 struct Plugin *plugin = cls;
357 struct GetContext get_ctx;
361 get_ctx.iter_cls = iter_cls;
363 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
372 * Delete the entry with the lowest expiration value
373 * from the datacache right now.
375 * @param cls closure (our `struct Plugin`)
376 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
379 heap_plugin_del (void *cls)
381 struct Plugin *plugin = cls;
384 for (unsigned int i=0;i<NUM_HEAPS;i++)
386 val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i]);
391 return GNUNET_SYSERR;
392 GNUNET_assert (GNUNET_YES ==
393 GNUNET_CONTAINER_multihashmap_remove (plugin->map,
396 plugin->env->delete_notify (plugin->env->cls,
398 val->size + OVERHEAD);
399 GNUNET_free_non_null (val->path_info);
406 * Return a random value from the datastore.
408 * @param cls closure (our `struct Plugin`)
409 * @param iter maybe NULL (to just count)
410 * @param iter_cls closure for @a iter
411 * @return the number of results found
414 heap_plugin_get_random (void *cls,
415 GNUNET_DATACACHE_Iterator iter,
418 struct Plugin *plugin = cls;
419 struct GetContext get_ctx;
421 get_ctx.type = GNUNET_BLOCK_TYPE_ANY;
423 get_ctx.iter_cls = iter_cls;
425 GNUNET_CONTAINER_multihashmap_get_random (plugin->map,
433 * Closure for #find_closest().
435 struct GetClosestContext
437 struct Value **values;
439 unsigned int num_results;
441 const struct GNUNET_HashCode *key;
446 find_closest (void *cls,
447 const struct GNUNET_HashCode *key,
450 struct GetClosestContext *gcc = cls;
451 struct Value *val = value;
454 if (1 != GNUNET_CRYPTO_hash_cmp (key,
456 return GNUNET_OK; /* useless */
457 j = gcc->num_results;
458 for (unsigned int i=0;i<gcc->num_results;i++)
460 if (NULL == gcc->values[i])
465 if (1 == GNUNET_CRYPTO_hash_cmp (&gcc->values[i]->key,
472 if (j == gcc->num_results)
474 gcc->values[j] = val;
480 * Iterate over the results that are "close" to a particular key in
481 * the datacache. "close" is defined as numerically larger than @a
482 * key (when interpreted as a circular address space), with small
485 * @param cls closure (internal context for the plugin)
486 * @param key area of the keyspace to look into
487 * @param num_results number of results that should be returned to @a iter
488 * @param iter maybe NULL (to just count)
489 * @param iter_cls closure for @a iter
490 * @return the number of results found
493 heap_plugin_get_closest (void *cls,
494 const struct GNUNET_HashCode *key,
495 unsigned int num_results,
496 GNUNET_DATACACHE_Iterator iter,
499 struct Plugin *plugin = cls;
500 struct Value *values[num_results];
501 struct GetClosestContext gcc = {
503 .num_results = num_results,
506 GNUNET_CONTAINER_multihashmap_iterate (plugin->map,
509 for (unsigned int i=0;i<num_results;i++)
511 if (NULL == values[i])
516 (void *) &values[i][1],
518 values[i]->discard_time,
519 values[i]->path_info_len,
520 values[i]->path_info);
527 * Entry point for the plugin.
529 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
530 * @return the plugin's closure (our `struct Plugin`)
533 libgnunet_plugin_datacache_heap_init (void *cls)
535 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
536 struct GNUNET_DATACACHE_PluginFunctions *api;
537 struct Plugin *plugin;
539 plugin = GNUNET_new (struct Plugin);
540 plugin->map = GNUNET_CONTAINER_multihashmap_create (1024, /* FIXME: base on quota! */
542 for (unsigned int i=0;i<NUM_HEAPS;i++)
543 plugin->heaps[i] = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
545 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
547 api->get = &heap_plugin_get;
548 api->put = &heap_plugin_put;
549 api->del = &heap_plugin_del;
550 api->get_random = &heap_plugin_get_random;
551 api->get_closest = &heap_plugin_get_closest;
552 LOG (GNUNET_ERROR_TYPE_INFO,
553 _("Heap datacache running\n"));
559 * Exit point from the plugin.
561 * @param cls closure (our "struct Plugin")
565 libgnunet_plugin_datacache_heap_done (void *cls)
567 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
568 struct Plugin *plugin = api->cls;
571 for (unsigned int i=0;i<NUM_HEAPS;i++)
573 while (NULL != (val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i])))
575 GNUNET_assert (GNUNET_YES ==
576 GNUNET_CONTAINER_multihashmap_remove (plugin->map,
579 GNUNET_free_non_null (val->path_info);
582 GNUNET_CONTAINER_heap_destroy (plugin->heaps[i]);
584 GNUNET_CONTAINER_multihashmap_destroy (plugin->map);
585 GNUNET_free (plugin);
592 /* end of plugin_datacache_heap.c */