plugin_datacache_heap.c 14 KB

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