aboutsummaryrefslogtreecommitdiff
path: root/src/plugin/datacache/plugin_datacache_heap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugin/datacache/plugin_datacache_heap.c')
-rw-r--r--src/plugin/datacache/plugin_datacache_heap.c530
1 files changed, 530 insertions, 0 deletions
diff --git a/src/plugin/datacache/plugin_datacache_heap.c b/src/plugin/datacache/plugin_datacache_heap.c
new file mode 100644
index 000000000..0dd8e47f8
--- /dev/null
+++ b/src/plugin/datacache/plugin_datacache_heap.c
@@ -0,0 +1,530 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012, 2015, 2022 GNUnet e.V.
4
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.
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 Affero General Public License for more details.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file datacache/plugin_datacache_heap.c
23 * @brief heap-only implementation of a database backend for the datacache
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_datacache_plugin.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "datacache-heap", __VA_ARGS__)
31
32#define LOG_STRERROR_FILE(kind, op, fn) GNUNET_log_from_strerror_file (kind, \
33 "datacache-heap", \
34 op, fn)
35
36#define NUM_HEAPS 24
37
38/**
39 * Context for all functions in this plugin.
40 */
41struct Plugin
42{
43 /**
44 * Our execution environment.
45 */
46 struct GNUNET_DATACACHE_PluginEnvironment *env;
47
48 /**
49 * Our hash map.
50 */
51 struct GNUNET_CONTAINER_MultiHashMap *map;
52
53 /**
54 * Heaps sorted by distance.
55 */
56 struct GNUNET_CONTAINER_Heap *heaps[NUM_HEAPS];
57};
58
59
60/**
61 * Entry in the hash map.
62 */
63struct Value
64{
65 /**
66 * Block data.
67 */
68 struct GNUNET_DATACACHE_Block block;
69
70 /**
71 * Corresponding node in the heap.
72 */
73 struct GNUNET_CONTAINER_HeapNode *hn;
74
75 /**
76 * Put path as a non-const pointer.
77 */
78 struct GNUNET_DHT_PathElement *put_path;
79
80 /**
81 * How close is the hash to us? Determines which heap we are in!
82 */
83 uint32_t distance;
84
85};
86
87
88#define OVERHEAD (sizeof(struct Value) + 64)
89
90
91/**
92 * Closure for #put_cb().
93 */
94struct PutContext
95{
96 /**
97 * Block data.
98 */
99 const struct GNUNET_DATACACHE_Block *block;
100
101 /**
102 * Value to set to true if an equivalent block was found.
103 */
104 bool found;
105};
106
107
108/**
109 * Function called during PUT to detect if an equivalent block
110 * already exists.
111 *
112 * @param cls the `struct PutContext`
113 * @param key the key for the value(s)
114 * @param value an existing value
115 * @return #GNUNET_YES if not found (to continue to iterate)
116 */
117static enum GNUNET_GenericReturnValue
118put_cb (void *cls,
119 const struct GNUNET_HashCode *key,
120 void *value)
121{
122 struct PutContext *put_ctx = cls;
123 struct Value *val = value;
124
125 if ((val->block.data_size == put_ctx->block->data_size) &&
126 (val->block.type == put_ctx->block->type) &&
127 (0 == memcmp (val->block.data,
128 put_ctx->block->data,
129 put_ctx->block->data_size)))
130 {
131 put_ctx->found = true;
132 val->block.expiration_time
133 = GNUNET_TIME_absolute_max (val->block.expiration_time,
134 put_ctx->block->expiration_time);
135 /* replace old path with new path */
136 GNUNET_free (val->put_path);
137 val->put_path = GNUNET_memdup (put_ctx->block->put_path,
138 put_ctx->block->put_path_length
139 * sizeof (struct GNUNET_DHT_PathElement));
140 val->block.put_path = val->put_path;
141 val->block.put_path_length = put_ctx->block->put_path_length;
142 GNUNET_CONTAINER_heap_update_cost (val->hn,
143 val->block.expiration_time.abs_value_us);
144 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145 "Got same value for key %s and type %u (size %u vs %u)\n",
146 GNUNET_h2s (key),
147 (unsigned int) val->block.type,
148 (unsigned int) val->block.data_size,
149 (unsigned int) put_ctx->block->data_size);
150 return GNUNET_NO;
151 }
152 return GNUNET_YES;
153}
154
155
156/**
157 * Store an item in the datastore.
158 *
159 * @param cls closure (our `struct Plugin`)
160 * @param xor_distance how close is @a key to our PID?
161 * @param block data to store
162 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
163 */
164static ssize_t
165heap_plugin_put (void *cls,
166 uint32_t xor_distance,
167 const struct GNUNET_DATACACHE_Block *block)
168{
169 struct Plugin *plugin = cls;
170 struct Value *val;
171 struct PutContext put_ctx = {
172 .block = block,
173 .found = false
174 };
175
176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
177 "Storing %u bytes under key %s with path length %u\n",
178 (unsigned int) block->data_size,
179 GNUNET_h2s (&block->key),
180 block->put_path_length);
181 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
182 &block->key,
183 &put_cb,
184 &put_ctx);
185 if (GNUNET_YES == put_ctx.found)
186 return 0;
187 val = GNUNET_malloc (sizeof(struct Value)
188 + block->data_size);
189 GNUNET_memcpy (&val[1],
190 block->data,
191 block->data_size);
192 val->block = *block;
193 val->block.data = &val[1];
194 if (xor_distance >= NUM_HEAPS)
195 val->distance = NUM_HEAPS - 1;
196 else
197 val->distance = xor_distance;
198 if (0 != block->put_path_length)
199 {
200 val->put_path
201 = GNUNET_memdup (block->put_path,
202 block->put_path_length
203 * sizeof (struct GNUNET_DHT_PathElement));
204 val->block.put_path = val->put_path;
205 }
206 (void) GNUNET_CONTAINER_multihashmap_put (plugin->map,
207 &val->block.key,
208 val,
209 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
210 val->hn = GNUNET_CONTAINER_heap_insert (
211 plugin->heaps[val->distance],
212 val,
213 val->block.expiration_time.abs_value_us);
214 return val->block.data_size + OVERHEAD;
215}
216
217
218/**
219 * Closure for #get_cb().
220 */
221struct GetContext
222{
223 /**
224 * Function to call for each result.
225 */
226 GNUNET_DATACACHE_Iterator iter;
227
228 /**
229 * Closure for @e iter.
230 */
231 void *iter_cls;
232
233 /**
234 * Number of results found.
235 */
236 unsigned int cnt;
237
238 /**
239 * Block type requested.
240 */
241 enum GNUNET_BLOCK_Type type;
242};
243
244
245/**
246 * Function called during GET to find matching blocks.
247 * Only matches by type.
248 *
249 * @param cls the `struct GetContext`
250 * @param key the key for the value(s)
251 * @param value an existing value
252 * @return #GNUNET_YES to continue to iterate
253 */
254static enum GNUNET_GenericReturnValue
255get_cb (void *cls,
256 const struct GNUNET_HashCode *key,
257 void *value)
258{
259 struct GetContext *get_ctx = cls;
260 struct Value *val = value;
261 enum GNUNET_GenericReturnValue ret;
262
263 if ( (get_ctx->type != val->block.type) &&
264 (GNUNET_BLOCK_TYPE_ANY != get_ctx->type) )
265 {
266 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
267 "Result for key %s does not match block type %d\n",
268 GNUNET_h2s (key),
269 get_ctx->type);
270 return GNUNET_OK;
271 }
272 if (GNUNET_TIME_absolute_is_past (val->block.expiration_time))
273 {
274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
275 "Result for key %s is expired\n",
276 GNUNET_h2s (key));
277 return GNUNET_OK;
278 }
279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
280 "Found result for key %s\n",
281 GNUNET_h2s (key));
282 if (NULL != get_ctx->iter)
283 ret = get_ctx->iter (get_ctx->iter_cls,
284 &val->block);
285 else
286 ret = GNUNET_YES;
287 get_ctx->cnt++;
288 return ret;
289}
290
291
292/**
293 * Iterate over the results for a particular key
294 * in the datastore.
295 *
296 * @param cls closure (our `struct Plugin`)
297 * @param key
298 * @param type entries of which type are relevant?
299 * @param iter maybe NULL (to just count)
300 * @param iter_cls closure for @a iter
301 * @return the number of results found
302 */
303static unsigned int
304heap_plugin_get (void *cls,
305 const struct GNUNET_HashCode *key,
306 enum GNUNET_BLOCK_Type type,
307 GNUNET_DATACACHE_Iterator iter,
308 void *iter_cls)
309{
310 struct Plugin *plugin = cls;
311 struct GetContext get_ctx;
312
313 get_ctx.type = type;
314 get_ctx.iter = iter;
315 get_ctx.iter_cls = iter_cls;
316 get_ctx.cnt = 0;
317 GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
318 key,
319 &get_cb,
320 &get_ctx);
321 return get_ctx.cnt;
322}
323
324
325/**
326 * Delete the entry with the lowest expiration value
327 * from the datacache right now.
328 *
329 * @param cls closure (our `struct Plugin`)
330 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
331 */
332static enum GNUNET_GenericReturnValue
333heap_plugin_del (void *cls)
334{
335 struct Plugin *plugin = cls;
336 struct Value *val;
337
338 for (unsigned int i = 0; i < NUM_HEAPS; i++)
339 {
340 val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i]);
341 if (NULL != val)
342 break;
343 }
344 if (NULL == val)
345 return GNUNET_SYSERR;
346 GNUNET_assert (GNUNET_YES ==
347 GNUNET_CONTAINER_multihashmap_remove (plugin->map,
348 &val->block.key,
349 val));
350 plugin->env->delete_notify (plugin->env->cls,
351 &val->block.key,
352 val->block.data_size + OVERHEAD);
353 GNUNET_free (val->put_path);
354 GNUNET_free (val);
355 return GNUNET_OK;
356}
357
358
359/**
360 * Closure for #find_closest().
361 */
362struct GetClosestContext
363{
364 struct Value **values;
365
366 const struct GNUNET_HashCode *key;
367
368 enum GNUNET_BLOCK_Type type;
369
370 unsigned int num_results;
371
372};
373
374
375static enum GNUNET_GenericReturnValue
376find_closest (void *cls,
377 const struct GNUNET_HashCode *key,
378 void *value)
379{
380 struct GetClosestContext *gcc = cls;
381 struct Value *val = value;
382 unsigned int j;
383
384 if (1 != GNUNET_CRYPTO_hash_cmp (key,
385 gcc->key))
386 return GNUNET_OK; /* useless */
387 if ( (val->block.type != gcc->type) &&
388 (GNUNET_BLOCK_TYPE_ANY != gcc->type) )
389 return GNUNET_OK; /* useless */
390 j = gcc->num_results;
391 for (unsigned int i = 0; i < gcc->num_results; i++)
392 {
393 if (NULL == gcc->values[i])
394 {
395 j = i;
396 break;
397 }
398 if (1 ==
399 GNUNET_CRYPTO_hash_cmp (&gcc->values[i]->block.key,
400 key))
401 {
402 j = i;
403 break;
404 }
405 }
406 if (j == gcc->num_results)
407 return GNUNET_OK;
408 gcc->values[j] = val;
409 return GNUNET_OK;
410}
411
412
413/**
414 * Iterate over the results that are "close" to a particular key in
415 * the datacache. "close" is defined as numerically larger than @a
416 * key (when interpreted as a circular address space), with small
417 * distance.
418 *
419 * @param cls closure (internal context for the plugin)
420 * @param key area of the keyspace to look into
421 * @param type desired block type for the replies
422 * @param num_results number of results that should be returned to @a iter
423 * @param iter maybe NULL (to just count)
424 * @param iter_cls closure for @a iter
425 * @return the number of results found
426 */
427static unsigned int
428heap_plugin_get_closest (void *cls,
429 const struct GNUNET_HashCode *key,
430 enum GNUNET_BLOCK_Type type,
431 unsigned int num_results,
432 GNUNET_DATACACHE_Iterator iter,
433 void *iter_cls)
434{
435 struct Plugin *plugin = cls;
436 struct Value *values[num_results];
437 struct GetClosestContext gcc = {
438 .values = values,
439 .type = type,
440 .num_results = num_results * 2,
441 .key = key
442 };
443
444 GNUNET_CONTAINER_multihashmap_iterate (plugin->map,
445 &find_closest,
446 &gcc);
447 for (unsigned int i = 0; i < num_results * 2; i++)
448 {
449 if (NULL == values[i])
450 return i;
451 if ( (NULL != iter) &&
452 (GNUNET_SYSERR ==
453 iter (iter_cls,
454 &values[i]->block)) )
455 {
456 LOG (GNUNET_ERROR_TYPE_DEBUG,
457 "Ending iteration (client error)\n");
458 return i;
459 }
460 }
461 return num_results * 2;
462}
463
464
465/**
466 * Entry point for the plugin.
467 *
468 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
469 * @return the plugin's closure (our `struct Plugin`)
470 */
471void *
472libgnunet_plugin_datacache_heap_init (void *cls)
473{
474 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
475 struct GNUNET_DATACACHE_PluginFunctions *api;
476 struct Plugin *plugin;
477
478 plugin = GNUNET_new (struct Plugin);
479 plugin->map = GNUNET_CONTAINER_multihashmap_create (1024, /* FIXME: base on quota! */
480 GNUNET_YES);
481 for (unsigned int i = 0; i < NUM_HEAPS; i++)
482 plugin->heaps[i] = GNUNET_CONTAINER_heap_create (
483 GNUNET_CONTAINER_HEAP_ORDER_MIN);
484 plugin->env = env;
485 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
486 api->cls = plugin;
487 api->get = &heap_plugin_get;
488 api->put = &heap_plugin_put;
489 api->del = &heap_plugin_del;
490 api->get_closest = &heap_plugin_get_closest;
491 LOG (GNUNET_ERROR_TYPE_INFO,
492 _ ("Heap datacache running\n"));
493 return api;
494}
495
496
497/**
498 * Exit point from the plugin.
499 *
500 * @param cls closure (our "struct Plugin")
501 * @return NULL
502 */
503void *
504libgnunet_plugin_datacache_heap_done (void *cls)
505{
506 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
507 struct Plugin *plugin = api->cls;
508 struct Value *val;
509
510 for (unsigned int i = 0; i < NUM_HEAPS; i++)
511 {
512 while (NULL != (val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i])))
513 {
514 GNUNET_assert (GNUNET_YES ==
515 GNUNET_CONTAINER_multihashmap_remove (plugin->map,
516 &val->block.key,
517 val));
518 GNUNET_free (val->put_path);
519 GNUNET_free (val);
520 }
521 GNUNET_CONTAINER_heap_destroy (plugin->heaps[i]);
522 }
523 GNUNET_CONTAINER_multihashmap_destroy (plugin->map);
524 GNUNET_free (plugin);
525 GNUNET_free (api);
526 return NULL;
527}
528
529
530/* end of plugin_datacache_heap.c */