diff options
Diffstat (limited to 'src/datacache/plugin_datacache_heap.c')
-rw-r--r-- | src/datacache/plugin_datacache_heap.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/src/datacache/plugin_datacache_heap.c b/src/datacache/plugin_datacache_heap.c new file mode 100644 index 0000000..41dd089 --- /dev/null +++ b/src/datacache/plugin_datacache_heap.c @@ -0,0 +1,441 @@ +/* + This file is part of GNUnet + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file datacache/plugin_datacache_heap.c + * @brief heap-only implementation of a database backend for the datacache + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_datacache_plugin.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "datacache-heap", __VA_ARGS__) + +#define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache-heap", op, fn) + + + +/** + * Context for all functions in this plugin. + */ +struct Plugin +{ + /** + * Our execution environment. + */ + struct GNUNET_DATACACHE_PluginEnvironment *env; + + /** + * Our hash map. + */ + struct GNUNET_CONTAINER_MultiHashMap *map; + + /** + * Heap for expirations. + */ + struct GNUNET_CONTAINER_Heap *heap; + +}; + + +/** + * Entry in the hash map. + */ +struct Value +{ + /** + * Key for the entry. + */ + struct GNUNET_HashCode key; + + /** + * Expiration time. + */ + struct GNUNET_TIME_Absolute discard_time; + + /** + * Corresponding node in the heap. + */ + struct GNUNET_CONTAINER_HeapNode *hn; + + /** + * Path information. + */ + struct GNUNET_PeerIdentity *path_info; + + /** + * Payload (actual payload follows this struct) + */ + size_t size; + + /** + * Number of entries in 'path_info'. + */ + unsigned int path_info_len; + + /** + * Type of the block. + */ + enum GNUNET_BLOCK_Type type; + +}; + + +#define OVERHEAD (sizeof (struct Value) + 64) + + +/** + * Closure for 'put_cb'. + */ +struct PutContext +{ + /** + * Expiration time for the new value. + */ + struct GNUNET_TIME_Absolute discard_time; + + /** + * Data for the new value. + */ + const char *data; + + /** + * Heap from the plugin. + */ + struct GNUNET_CONTAINER_Heap *heap; + + /** + * Path information. + */ + const struct GNUNET_PeerIdentity *path_info; + + /** + * Number of bytes in 'data'. + */ + size_t size; + + /** + * Type of the node. + */ + enum GNUNET_BLOCK_Type type; + + /** + * Number of entries in 'path_info'. + */ + unsigned int path_info_len; + + /** + * Value to set to GNUNET_YES if an equivalent block was found. + */ + int found; +}; + + +/** + * Function called during PUT to detect if an equivalent block + * already exists. + * + * @param cls the 'struct PutContext' + * @param key the key for the value(s) + * @param value an existing value + * @return GNUNET_YES if not found (to continue to iterate) + */ +static int +put_cb (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct PutContext *put_ctx = cls; + struct Value *val = value; + + if ( (val->size == put_ctx->size) && + (val->type == put_ctx->type) && + (0 == memcmp (&val[1], put_ctx->data, put_ctx->size)) ) + { + put_ctx->found = GNUNET_YES; + val->discard_time = GNUNET_TIME_absolute_max (val->discard_time, + put_ctx->discard_time); + /* replace old path with new path */ + GNUNET_array_grow (val->path_info, + val->path_info_len, + put_ctx->path_info_len); + memcpy (val->path_info, + put_ctx->path_info, + put_ctx->path_info_len * sizeof (struct GNUNET_PeerIdentity)); + GNUNET_CONTAINER_heap_update_cost (put_ctx->heap, + val->hn, + val->discard_time.abs_value); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got same value for key %s and type %d (size %u vs %u)\n", + GNUNET_h2s (key), + val->type, + (unsigned int) val->size, + (unsigned int) put_ctx->size); + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Store an item in the datastore. + * + * @param cls closure (our "struct Plugin") + * @param key key to store data under + * @param size number of bytes in data + * @param data data to store + * @param type type of the value + * @param discard_time when to discard the value in any case + * @param path_info_len number of entries in 'path_info' + * @param path_info a path through the network + * @return 0 if duplicate, -1 on error, number of bytes used otherwise + */ +static ssize_t +heap_plugin_put (void *cls, const struct GNUNET_HashCode * key, size_t size, + const char *data, enum GNUNET_BLOCK_Type type, + struct GNUNET_TIME_Absolute discard_time, + unsigned int path_info_len, + const struct GNUNET_PeerIdentity *path_info) +{ + struct Plugin *plugin = cls; + struct Value *val; + struct PutContext put_ctx; + + put_ctx.found = GNUNET_NO; + put_ctx.heap = plugin->heap; + put_ctx.data = data; + put_ctx.size = size; + put_ctx.path_info = path_info; + put_ctx.path_info_len = path_info_len; + put_ctx.discard_time = discard_time; + put_ctx.type = type; + GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map, + key, + put_cb, + &put_ctx); + if (GNUNET_YES == put_ctx.found) + return 0; + val = GNUNET_malloc (sizeof (struct Value) + size); + memcpy (&val[1], data, size); + val->key = *key; + val->type = type; + val->discard_time = discard_time; + val->size = size; + GNUNET_array_grow (val->path_info, + val->path_info_len, + path_info_len); + memcpy (val->path_info, path_info, + path_info_len * sizeof (struct GNUNET_PeerIdentity)); + (void) GNUNET_CONTAINER_multihashmap_put (plugin->map, + &val->key, + val, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + val->hn = GNUNET_CONTAINER_heap_insert (plugin->heap, + val, + val->discard_time.abs_value); + return size + OVERHEAD; +} + + +/** + * Closure for 'get_cb'. + */ +struct GetContext +{ + /** + * Function to call for each result. + */ + GNUNET_DATACACHE_Iterator iter; + + /** + * Closure for 'iter'. + */ + void *iter_cls; + + /** + * Number of results found. + */ + unsigned int cnt; + + /** + * Block type requested. + */ + enum GNUNET_BLOCK_Type type; +}; + + + +/** + * Function called during GET to find matching blocks. + * Only matches by type. + * + * @param cls the 'struct GetContext' + * @param key the key for the value(s) + * @param value an existing value + * @return GNUNET_YES to continue to iterate + */ +static int +get_cb (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GetContext *get_ctx = cls; + struct Value *val = value; + int ret; + + if ( (get_ctx->type != val->type) && + (GNUNET_BLOCK_TYPE_ANY != get_ctx->type) ) + return GNUNET_OK; + if (NULL != get_ctx->iter) + ret = get_ctx->iter (get_ctx->iter_cls, + key, + val->size, + (const char *) &val[1], + val->type, + val->discard_time, + val->path_info_len, + val->path_info); + else + ret = GNUNET_YES; + get_ctx->cnt++; + return ret; +} + + +/** + * Iterate over the results for a particular key + * in the datastore. + * + * @param cls closure (our "struct Plugin") + * @param key + * @param type entries of which type are relevant? + * @param iter maybe NULL (to just count) + * @param iter_cls closure for iter + * @return the number of results found + */ +static unsigned int +heap_plugin_get (void *cls, const struct GNUNET_HashCode * key, + enum GNUNET_BLOCK_Type type, GNUNET_DATACACHE_Iterator iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + struct GetContext get_ctx; + + get_ctx.type = type; + get_ctx.iter = iter; + get_ctx.iter_cls = iter_cls; + get_ctx.cnt = 0; + GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map, + key, + get_cb, + &get_ctx); + return get_ctx.cnt; +} + + +/** + * Delete the entry with the lowest expiration value + * from the datacache right now. + * + * @param cls closure (our "struct Plugin") + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +static int +heap_plugin_del (void *cls) +{ + struct Plugin *plugin = cls; + struct Value *val; + + val = GNUNET_CONTAINER_heap_remove_root (plugin->heap); + if (NULL == val) + return GNUNET_SYSERR; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (plugin->map, + &val->key, + val)); + plugin->env->delete_notify (plugin->env->cls, + &val->key, + val->size + OVERHEAD); + GNUNET_free_non_null (val->path_info); + GNUNET_free (val); + return GNUNET_OK; +} + + +/** + * Entry point for the plugin. + * + * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet") + * @return the plugin's closure (our "struct Plugin") + */ +void * +libgnunet_plugin_datacache_heap_init (void *cls) +{ + struct GNUNET_DATACACHE_PluginEnvironment *env = cls; + struct GNUNET_DATACACHE_PluginFunctions *api; + struct Plugin *plugin; + + plugin = GNUNET_malloc (sizeof (struct Plugin)); + plugin->map = GNUNET_CONTAINER_multihashmap_create (1024, /* FIXME: base on quota! */ + GNUNET_YES); + plugin->heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + plugin->env = env; + api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions)); + api->cls = plugin; + api->get = &heap_plugin_get; + api->put = &heap_plugin_put; + api->del = &heap_plugin_del; + LOG (GNUNET_ERROR_TYPE_INFO, _("Heap datacache running\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls closure (our "struct Plugin") + * @return NULL + */ +void * +libgnunet_plugin_datacache_heap_done (void *cls) +{ + struct GNUNET_DATACACHE_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + struct Value *val; + + while (NULL != (val = GNUNET_CONTAINER_heap_remove_root (plugin->heap))) + { + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (plugin->map, + &val->key, + val)); + GNUNET_free (val); + } + GNUNET_CONTAINER_heap_destroy (plugin->heap); + GNUNET_CONTAINER_multihashmap_destroy (plugin->map); + GNUNET_free (plugin); + GNUNET_free (api); + return NULL; +} + + + +/* end of plugin_datacache_heap.c */ |