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 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.
17 * @file datacache/plugin_datacache_heap.c
18 * @brief heap-only implementation of a database backend for the datacache
19 * @author Christian Grothoff
22 #include "gnunet_util_lib.h"
23 #include "gnunet_datacache_plugin.h"
25 #define LOG(kind,...) GNUNET_log_from (kind, "datacache-heap", __VA_ARGS__)
27 #define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache-heap", op, fn)
32 * Context for all functions in this plugin.
37 * Our execution environment.
39 struct GNUNET_DATACACHE_PluginEnvironment *env;
44 struct GNUNET_CONTAINER_MultiHashMap *map;
47 * Heaps sorted by distance.
49 struct GNUNET_CONTAINER_Heap *heaps[NUM_HEAPS];
55 * Entry in the hash map.
62 struct GNUNET_HashCode key;
67 struct GNUNET_TIME_Absolute discard_time;
70 * Corresponding node in the heap.
72 struct GNUNET_CONTAINER_HeapNode *hn;
77 struct GNUNET_PeerIdentity *path_info;
80 * Payload (actual payload follows this struct)
85 * Number of entries in @e path_info.
87 unsigned int path_info_len;
90 * How close is the hash to us? Determines which heap we are in!
97 enum GNUNET_BLOCK_Type type;
102 #define OVERHEAD (sizeof (struct Value) + 64)
106 * Closure for #put_cb().
111 * Expiration time for the new value.
113 struct GNUNET_TIME_Absolute discard_time;
116 * Data for the new value.
123 const struct GNUNET_PeerIdentity *path_info;
126 * Number of bytes in @e data.
133 enum GNUNET_BLOCK_Type type;
136 * Number of entries in @e path_info.
138 unsigned int path_info_len;
141 * Value to set to #GNUNET_YES if an equivalent block was found.
148 * Function called during PUT to detect if an equivalent block
151 * @param cls the `struct PutContext`
152 * @param key the key for the value(s)
153 * @param value an existing value
154 * @return #GNUNET_YES if not found (to continue to iterate)
158 const struct GNUNET_HashCode *key,
161 struct PutContext *put_ctx = cls;
162 struct Value *val = value;
164 if ( (val->size == put_ctx->size) &&
165 (val->type == put_ctx->type) &&
166 (0 == memcmp (&val[1],
170 put_ctx->found = GNUNET_YES;
171 val->discard_time = GNUNET_TIME_absolute_max (val->discard_time,
172 put_ctx->discard_time);
173 /* replace old path with new path */
174 GNUNET_array_grow (val->path_info,
176 put_ctx->path_info_len);
177 GNUNET_memcpy (val->path_info,
179 put_ctx->path_info_len * sizeof (struct GNUNET_PeerIdentity));
180 GNUNET_CONTAINER_heap_update_cost (val->hn,
181 val->discard_time.abs_value_us);
182 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
183 "Got same value for key %s and type %d (size %u vs %u)\n",
186 (unsigned int) val->size,
187 (unsigned int) put_ctx->size);
195 * Store an item in the datastore.
197 * @param cls closure (our `struct Plugin`)
198 * @param key key to store data under
199 * @param xor_distance how close is @a key to our PID?
200 * @param size number of bytes in @a data
201 * @param data data to store
202 * @param type type of the value
203 * @param discard_time when to discard the value in any case
204 * @param path_info_len number of entries in @a path_info
205 * @param path_info a path through the network
206 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
209 heap_plugin_put (void *cls,
210 const struct GNUNET_HashCode *key,
211 uint32_t xor_distance,
214 enum GNUNET_BLOCK_Type type,
215 struct GNUNET_TIME_Absolute discard_time,
216 unsigned int path_info_len,
217 const struct GNUNET_PeerIdentity *path_info)
219 struct Plugin *plugin = cls;
221 struct PutContext put_ctx;
223 put_ctx.found = GNUNET_NO;
226 put_ctx.path_info = path_info;
227 put_ctx.path_info_len = path_info_len;
228 put_ctx.discard_time = discard_time;
230 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
234 if (GNUNET_YES == put_ctx.found)
236 val = GNUNET_malloc (sizeof (struct Value) + size);
237 GNUNET_memcpy (&val[1],
242 val->discard_time = discard_time;
244 if (xor_distance >= NUM_HEAPS)
245 val->distance = NUM_HEAPS - 1;
247 val->distance = xor_distance;
248 GNUNET_array_grow (val->path_info,
251 GNUNET_memcpy (val->path_info,
253 path_info_len * sizeof (struct GNUNET_PeerIdentity));
254 (void) GNUNET_CONTAINER_multihashmap_put (plugin->map,
257 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
258 val->hn = GNUNET_CONTAINER_heap_insert (plugin->heaps[val->distance],
260 val->discard_time.abs_value_us);
261 return size + OVERHEAD;
266 * Closure for #get_cb().
271 * Function to call for each result.
273 GNUNET_DATACACHE_Iterator iter;
276 * Closure for @e iter.
281 * Number of results found.
286 * Block type requested.
288 enum GNUNET_BLOCK_Type type;
294 * Function called during GET to find matching blocks.
295 * Only matches by type.
297 * @param cls the `struct GetContext`
298 * @param key the key for the value(s)
299 * @param value an existing value
300 * @return #GNUNET_YES to continue to iterate
304 const struct GNUNET_HashCode *key,
307 struct GetContext *get_ctx = cls;
308 struct Value *val = value;
311 if ( (get_ctx->type != val->type) &&
312 (GNUNET_BLOCK_TYPE_ANY != get_ctx->type) )
314 if (NULL != get_ctx->iter)
315 ret = get_ctx->iter (get_ctx->iter_cls,
318 (const char *) &val[1],
331 * Iterate over the results for a particular key
334 * @param cls closure (our `struct Plugin`)
336 * @param type entries of which type are relevant?
337 * @param iter maybe NULL (to just count)
338 * @param iter_cls closure for @a iter
339 * @return the number of results found
342 heap_plugin_get (void *cls,
343 const struct GNUNET_HashCode *key,
344 enum GNUNET_BLOCK_Type type,
345 GNUNET_DATACACHE_Iterator iter,
348 struct Plugin *plugin = cls;
349 struct GetContext get_ctx;
353 get_ctx.iter_cls = iter_cls;
355 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
364 * Delete the entry with the lowest expiration value
365 * from the datacache right now.
367 * @param cls closure (our `struct Plugin`)
368 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
371 heap_plugin_del (void *cls)
373 struct Plugin *plugin = cls;
376 for (unsigned int i=0;i<NUM_HEAPS;i++)
378 val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i]);
383 return GNUNET_SYSERR;
384 GNUNET_assert (GNUNET_YES ==
385 GNUNET_CONTAINER_multihashmap_remove (plugin->map,
388 plugin->env->delete_notify (plugin->env->cls,
390 val->size + OVERHEAD);
391 GNUNET_free_non_null (val->path_info);
398 * Return a random value from the datastore.
400 * @param cls closure (our `struct Plugin`)
401 * @param iter maybe NULL (to just count)
402 * @param iter_cls closure for @a iter
403 * @return the number of results found
406 heap_plugin_get_random (void *cls,
407 GNUNET_DATACACHE_Iterator iter,
410 struct Plugin *plugin = cls;
411 struct GetContext get_ctx;
413 get_ctx.type = GNUNET_BLOCK_TYPE_ANY;
415 get_ctx.iter_cls = iter_cls;
417 GNUNET_CONTAINER_multihashmap_get_random (plugin->map,
425 * Closure for #find_closest().
427 struct GetClosestContext
429 struct Value **values;
431 unsigned int num_results;
433 const struct GNUNET_HashCode *key;
438 find_closest (void *cls,
439 const struct GNUNET_HashCode *key,
442 struct GetClosestContext *gcc = cls;
443 struct Value *val = value;
446 if (1 != GNUNET_CRYPTO_hash_cmp (key,
448 return GNUNET_OK; /* useless */
449 j = gcc->num_results;
450 for (unsigned int i=0;i<gcc->num_results;i++)
452 if (NULL == gcc->values[i])
457 if (1 == GNUNET_CRYPTO_hash_cmp (&gcc->values[i]->key,
464 if (j == gcc->num_results)
466 gcc->values[j] = val;
472 * Iterate over the results that are "close" to a particular key in
473 * the datacache. "close" is defined as numerically larger than @a
474 * key (when interpreted as a circular address space), with small
477 * @param cls closure (internal context for the plugin)
478 * @param key area of the keyspace to look into
479 * @param num_results number of results that should be returned to @a iter
480 * @param iter maybe NULL (to just count)
481 * @param iter_cls closure for @a iter
482 * @return the number of results found
485 heap_plugin_get_closest (void *cls,
486 const struct GNUNET_HashCode *key,
487 unsigned int num_results,
488 GNUNET_DATACACHE_Iterator iter,
491 struct Plugin *plugin = cls;
492 struct Value *values[num_results];
493 struct GetClosestContext gcc = {
495 .num_results = num_results,
498 GNUNET_CONTAINER_multihashmap_iterate (plugin->map,
501 for (unsigned int i=0;i<num_results;i++)
503 if (NULL == values[i])
508 (void *) &values[i][1],
510 values[i]->discard_time,
511 values[i]->path_info_len,
512 values[i]->path_info);
519 * Entry point for the plugin.
521 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
522 * @return the plugin's closure (our `struct Plugin`)
525 libgnunet_plugin_datacache_heap_init (void *cls)
527 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
528 struct GNUNET_DATACACHE_PluginFunctions *api;
529 struct Plugin *plugin;
531 plugin = GNUNET_new (struct Plugin);
532 plugin->map = GNUNET_CONTAINER_multihashmap_create (1024, /* FIXME: base on quota! */
534 for (unsigned int i=0;i<NUM_HEAPS;i++)
535 plugin->heaps[i] = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
537 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
539 api->get = &heap_plugin_get;
540 api->put = &heap_plugin_put;
541 api->del = &heap_plugin_del;
542 api->get_random = &heap_plugin_get_random;
543 api->get_closest = &heap_plugin_get_closest;
544 LOG (GNUNET_ERROR_TYPE_INFO,
545 _("Heap datacache running\n"));
551 * Exit point from the plugin.
553 * @param cls closure (our "struct Plugin")
557 libgnunet_plugin_datacache_heap_done (void *cls)
559 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
560 struct Plugin *plugin = api->cls;
563 for (unsigned int i=0;i<NUM_HEAPS;i++)
565 while (NULL != (val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i])))
567 GNUNET_assert (GNUNET_YES ==
568 GNUNET_CONTAINER_multihashmap_remove (plugin->map,
571 GNUNET_free_non_null (val->path_info);
574 GNUNET_CONTAINER_heap_destroy (plugin->heaps[i]);
576 GNUNET_CONTAINER_multihashmap_destroy (plugin->map);
577 GNUNET_free (plugin);
584 /* end of plugin_datacache_heap.c */