aboutsummaryrefslogtreecommitdiff
path: root/src/service/setu/gnunet-service-setu_strata_estimator.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/setu/gnunet-service-setu_strata_estimator.c')
-rw-r--r--src/service/setu/gnunet-service-setu_strata_estimator.c468
1 files changed, 468 insertions, 0 deletions
diff --git a/src/service/setu/gnunet-service-setu_strata_estimator.c b/src/service/setu/gnunet-service-setu_strata_estimator.c
new file mode 100644
index 000000000..43ccf3afd
--- /dev/null
+++ b/src/service/setu/gnunet-service-setu_strata_estimator.c
@@ -0,0 +1,468 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012 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 * @file set/gnunet-service-setu_strata_estimator.c
22 * @brief invertible bloom filter
23 * @author Florian Dold
24 * @author Christian Grothoff
25 * @author Elias Summermatter
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "ibf.h"
30#include "gnunet-service-setu_strata_estimator.h"
31
32
33/**
34 * Should we try compressing the strata estimator? This will
35 * break compatibility with the 0.10.1-network.
36 */
37#define FAIL_10_1_COMPATIBILTIY 1
38
39/**
40 * Number of strata estimators in memory NOT transmitted
41 */
42
43#define MULTI_SE_BASE_COUNT 8
44
45/**
46 * The avg size of 1 se
47 * Based on the bsc thesis of Elias Summermatter (2021)
48 */
49
50#define AVG_BYTE_SIZE_SE 4221
51
52/**
53 * Calculates the optimal number of strata Estimators to send
54 * @param avg_element_size
55 * @param element_count
56 * @return
57 */
58uint8_t
59determine_strata_count (uint64_t avg_element_size, uint64_t element_count)
60{
61 uint64_t base_size = avg_element_size * element_count;
62 /* >67kb total size of elements in set */
63 if (base_size < AVG_BYTE_SIZE_SE * 16)
64 return 1;
65 /* >270kb total size of elements in set */
66 if (base_size < AVG_BYTE_SIZE_SE * 64)
67 return 2;
68 /* >1mb total size of elements in set */
69 if (base_size < AVG_BYTE_SIZE_SE * 256)
70 return 4;
71 return 8;
72}
73
74
75/**
76 * Modify an IBF key @a k_in based on the @a salt, returning a
77 * salted key in @a k_out.
78 */
79static void
80salt_key (const struct IBF_Key *k_in,
81 uint32_t salt,
82 struct IBF_Key *k_out)
83{
84 int s = (salt * 7) % 64;
85 uint64_t x = k_in->key_val;
86
87 /* rotate ibf key */
88 if (s > 0)
89 x = (x >> s) | (x << (64 - s));
90 k_out->key_val = x;
91}
92
93
94/**
95 * Reverse modification done in the salt_key function
96 */
97static void
98unsalt_key (const struct IBF_Key *k_in,
99 uint32_t salt,
100 struct IBF_Key *k_out)
101{
102 int s = (salt * 7) % 64;
103 uint64_t x = k_in->key_val;
104
105 x = (x << s) | (x >> (64 - s));
106 k_out->key_val = x;
107}
108
109
110/**
111 * Write the given strata estimator to the buffer.
112 *
113 * @param se strata estimator to serialize
114 * @param[out] buf buffer to write to, must be of appropriate size
115 * @return number of bytes written to @a buf
116 */
117size_t
118strata_estimator_write (struct MultiStrataEstimator *se,
119 uint16_t se_ibf_total_size,
120 uint8_t number_se_send,
121 void *buf)
122{
123 char *sbuf = buf;
124 unsigned int i;
125 size_t osize;
126 uint64_t sbuf_offset = 0;
127 se->size = number_se_send;
128
129 GNUNET_assert (NULL != se);
130 for (uint8_t strata_ctr = 0; strata_ctr < number_se_send; strata_ctr++)
131 {
132 for (i = 0; i < se->stratas[strata_ctr]->strata_count; i++)
133 {
134 ibf_write_slice (se->stratas[strata_ctr]->strata[i],
135 0,
136 se->stratas[strata_ctr]->ibf_size,
137 &sbuf[sbuf_offset],
138 8);
139 sbuf_offset += se->stratas[strata_ctr]->ibf_size * IBF_BUCKET_SIZE;
140 }
141 }
142 osize = ((se_ibf_total_size / 8) * number_se_send) * IBF_BUCKET_SIZE
143 * se->stratas[0]->strata_count;
144#if FAIL_10_1_COMPATIBILTIY
145 {
146 char *cbuf;
147 size_t nsize;
148
149 if (GNUNET_YES ==
150 GNUNET_try_compression (buf,
151 osize,
152 &cbuf,
153 &nsize))
154 {
155 GNUNET_memcpy (buf, cbuf, nsize);
156 osize = nsize;
157 GNUNET_free (cbuf);
158 }
159 }
160#endif
161 return osize;
162}
163
164
165/**
166 * Read strata from the buffer into the given strata
167 * estimator. The strata estimator must already be allocated.
168 *
169 * @param buf buffer to read from
170 * @param buf_len number of bytes in @a buf
171 * @param is_compressed is the data compressed?
172 * @param[out] se strata estimator to write to
173 * @return #GNUNET_OK on success
174 */
175int
176strata_estimator_read (const void *buf,
177 size_t buf_len,
178 int is_compressed,
179 uint8_t number_se_received,
180 uint16_t se_ibf_total_size,
181 struct MultiStrataEstimator *se)
182{
183 unsigned int i;
184 size_t osize;
185 char *dbuf;
186
187 dbuf = NULL;
188 if (GNUNET_YES == is_compressed)
189 {
190 osize = ((se_ibf_total_size / 8) * number_se_received) * IBF_BUCKET_SIZE
191 * se->stratas[0]->strata_count;
192 dbuf = GNUNET_decompress (buf,
193 buf_len,
194 osize);
195 if (NULL == dbuf)
196 {
197 GNUNET_break_op (0); /* bad compressed input data */
198 return GNUNET_SYSERR;
199 }
200 buf = dbuf;
201 buf_len = osize;
202 }
203
204 if (buf_len != se->stratas[0]->strata_count * ((se_ibf_total_size / 8)
205 * number_se_received)
206 * IBF_BUCKET_SIZE)
207 {
208 GNUNET_break (0); /* very odd error */
209 GNUNET_free (dbuf);
210 return GNUNET_SYSERR;
211 }
212
213 for (uint8_t strata_ctr = 0; strata_ctr < number_se_received; strata_ctr++)
214 {
215 for (i = 0; i < se->stratas[strata_ctr]->strata_count; i++)
216 {
217 ibf_read_slice (buf, 0, se->stratas[strata_ctr]->ibf_size,
218 se->stratas[strata_ctr]->strata[i], 8);
219 buf += se->stratas[strata_ctr]->ibf_size * IBF_BUCKET_SIZE;
220 }
221 }
222 se->size = number_se_received;
223 GNUNET_free (dbuf);
224 return GNUNET_OK;
225}
226
227
228/**
229 * Add a key to the strata estimator.
230 *
231 * @param se strata estimator to add the key to
232 * @param key key to add
233 */
234void
235strata_estimator_insert (struct MultiStrataEstimator *se,
236 struct IBF_Key key)
237{
238
239
240 /* count trailing '1'-bits of v */
241 for (int strata_ctr = 0; strata_ctr < MULTI_SE_BASE_COUNT; strata_ctr++)
242 {
243 unsigned int i;
244 uint64_t v;
245
246 struct IBF_Key salted_key;
247 salt_key (&key,
248 strata_ctr * (64 / MULTI_SE_BASE_COUNT),
249 &salted_key);
250 v = salted_key.key_val;
251 for (i = 0; v & 1; v >>= 1, i++)
252 {
253 ibf_insert (se->stratas[strata_ctr]->strata[i], salted_key);
254 }
255 }
256 /* empty */;
257
258}
259
260
261/**
262 * Remove a key from the strata estimator. (NOT USED)
263 *
264 * @param se strata estimator to remove the key from
265 * @param key key to remove
266 */
267void
268strata_estimator_remove (struct MultiStrataEstimator *se,
269 struct IBF_Key key)
270{
271
272 /* count trailing '1'-bits of v */
273 for (int strata_ctr = 0; strata_ctr < se->size; strata_ctr++)
274 {
275 uint64_t v;
276 unsigned int i;
277
278 struct IBF_Key unsalted_key;
279 unsalt_key (&key,
280 strata_ctr * (64 / MULTI_SE_BASE_COUNT),
281 &unsalted_key);
282
283 v = unsalted_key.key_val;
284 for (i = 0; v & 1; v >>= 1, i++)
285 {
286 /* empty */;
287 ibf_remove (se->stratas[strata_ctr]->strata[i], unsalted_key);
288 }
289 }
290}
291
292
293/**
294 * Create a new strata estimator with the given parameters.
295 *
296 * @param strata_count number of stratas, that is, number of ibfs in the estimator
297 * @param ibf_size size of each ibf stratum
298 * @param ibf_hashnum hashnum parameter of each ibf
299 * @return a freshly allocated, empty strata estimator, NULL on error
300 */
301struct MultiStrataEstimator *
302strata_estimator_create (unsigned int strata_count,
303 uint32_t ibf_size,
304 uint8_t ibf_hashnum)
305{
306 struct MultiStrataEstimator *se;
307 unsigned int i;
308 unsigned int j;
309 se = GNUNET_new (struct MultiStrataEstimator);
310
311 se->size = MULTI_SE_BASE_COUNT;
312 se->stratas = GNUNET_new_array (MULTI_SE_BASE_COUNT,struct StrataEstimator *);
313
314 uint8_t ibf_prime_sizes[] = {79,79,79,79,79,79,79,79};
315
316 for (uint8_t strata_ctr = 0; strata_ctr < MULTI_SE_BASE_COUNT; strata_ctr++)
317 {
318 se->stratas[strata_ctr] = GNUNET_new (struct StrataEstimator);
319 se->stratas[strata_ctr]->strata_count = strata_count;
320 se->stratas[strata_ctr]->ibf_size = ibf_prime_sizes[strata_ctr];
321 se->stratas[strata_ctr]->strata = GNUNET_new_array (strata_count * 4,
322 struct
323 InvertibleBloomFilter *);
324 for (i = 0; i < strata_count; i++)
325 {
326 se->stratas[strata_ctr]->strata[i] = ibf_create (
327 ibf_prime_sizes[strata_ctr], ibf_hashnum);
328 if (NULL == se->stratas[strata_ctr]->strata[i])
329 {
330 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
331 "Failed to allocate memory for strata estimator\n");
332 for (j = 0; j < i; j++)
333 ibf_destroy (se->stratas[strata_ctr]->strata[i]);
334 GNUNET_free (se);
335 return NULL;
336 }
337 }
338 }
339 return se;
340}
341
342
343/**
344 * Estimate set difference with two strata estimators,
345 * i.e. arrays of IBFs.
346 * Does not not modify its arguments.
347 *
348 * @param se1 first strata estimator
349 * @param se2 second strata estimator
350 * @return the estimated difference
351 */
352void
353strata_estimator_difference (const struct MultiStrataEstimator *se1,
354 const struct MultiStrataEstimator *se2)
355{
356 int avg_local_diff = 0;
357 int avg_remote_diff = 0;
358 uint8_t number_of_estimators = se1->size;
359
360 for (uint8_t strata_ctr = 0; strata_ctr < number_of_estimators; strata_ctr++)
361 {
362 GNUNET_assert (se1->stratas[strata_ctr]->strata_count ==
363 se2->stratas[strata_ctr]->strata_count);
364
365
366 for (int i = se1->stratas[strata_ctr]->strata_count - 1; i >= 0; i--)
367 {
368 struct InvertibleBloomFilter *diff;
369 /* number of keys decoded from the ibf */
370
371 /* FIXME: implement this without always allocating new IBFs */
372 diff = ibf_dup (se1->stratas[strata_ctr]->strata[i]);
373 diff->local_decoded_count = 0;
374 diff->remote_decoded_count = 0;
375
376 ibf_subtract (diff, se2->stratas[strata_ctr]->strata[i]);
377
378 for (int ibf_count = 0; GNUNET_YES; ibf_count++)
379 {
380 int more;
381
382 more = ibf_decode (diff, NULL, NULL);
383 if (GNUNET_NO == more)
384 {
385 se1->stratas[strata_ctr]->strata[0]->local_decoded_count +=
386 diff->local_decoded_count;
387 se1->stratas[strata_ctr]->strata[0]->remote_decoded_count +=
388 diff->remote_decoded_count;
389 break;
390 }
391 /* Estimate if decoding fails or would not terminate */
392 if ((GNUNET_SYSERR == more) || (ibf_count > diff->size))
393 {
394 se1->stratas[strata_ctr]->strata[0]->local_decoded_count =
395 se1->stratas[strata_ctr]->strata[0]->local_decoded_count * (1 << (i
396 +
397 1));
398 se1->stratas[strata_ctr]->strata[0]->remote_decoded_count =
399 se1->stratas[strata_ctr]->strata[0]->remote_decoded_count * (1 << (i
400 +
401 1));
402 ibf_destroy (diff);
403 goto break_all_counting_loops;
404 }
405 }
406 ibf_destroy (diff);
407 }
408break_all_counting_loops:;
409 avg_local_diff += se1->stratas[strata_ctr]->strata[0]->local_decoded_count;
410 avg_remote_diff +=
411 se1->stratas[strata_ctr]->strata[0]->remote_decoded_count;
412 }
413 se1->stratas[0]->strata[0]->local_decoded_count = avg_local_diff
414 / number_of_estimators;
415 se1->stratas[0]->strata[0]->remote_decoded_count = avg_remote_diff
416 / number_of_estimators;
417}
418
419
420/**
421 * Make a copy of a strata estimator.
422 *
423 * @param se the strata estimator to copy
424 * @return the copy
425 */
426struct MultiStrataEstimator *
427strata_estimator_dup (struct MultiStrataEstimator *se)
428{
429 struct MultiStrataEstimator *c;
430 unsigned int i;
431
432 c = GNUNET_new (struct MultiStrataEstimator);
433 c->stratas = GNUNET_new_array (MULTI_SE_BASE_COUNT,struct StrataEstimator *);
434 for (uint8_t strata_ctr = 0; strata_ctr < MULTI_SE_BASE_COUNT; strata_ctr++)
435 {
436 c->stratas[strata_ctr] = GNUNET_new (struct StrataEstimator);
437 c->stratas[strata_ctr]->strata_count =
438 se->stratas[strata_ctr]->strata_count;
439 c->stratas[strata_ctr]->ibf_size = se->stratas[strata_ctr]->ibf_size;
440 c->stratas[strata_ctr]->strata = GNUNET_new_array (
441 se->stratas[strata_ctr]->strata_count,
442 struct
443 InvertibleBloomFilter *);
444 for (i = 0; i < se->stratas[strata_ctr]->strata_count; i++)
445 c->stratas[strata_ctr]->strata[i] = ibf_dup (
446 se->stratas[strata_ctr]->strata[i]);
447 }
448 return c;
449}
450
451
452/**
453 * Destroy a strata estimator, free all of its resources.
454 *
455 * @param se strata estimator to destroy.
456 */
457void
458strata_estimator_destroy (struct MultiStrataEstimator *se)
459{
460 unsigned int i;
461 for (uint8_t strata_ctr = 0; strata_ctr < MULTI_SE_BASE_COUNT; strata_ctr++)
462 {
463 for (i = 0; i < se->stratas[strata_ctr]->strata_count; i++)
464 ibf_destroy (se->stratas[strata_ctr]->strata[i]);
465 GNUNET_free (se->stratas[strata_ctr]->strata);
466 }
467 GNUNET_free (se);
468}