aboutsummaryrefslogtreecommitdiff
path: root/src/setu/ibf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/setu/ibf.c')
-rw-r--r--src/setu/ibf.c585
1 files changed, 0 insertions, 585 deletions
diff --git a/src/setu/ibf.c b/src/setu/ibf.c
deleted file mode 100644
index a55e21764..000000000
--- a/src/setu/ibf.c
+++ /dev/null
@@ -1,585 +0,0 @@
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/**
22 * @file set/ibf.c
23 * @brief implementation of the invertible bloom filter
24 * @author Florian Dold
25 * @author Elias Summermatter
26 */
27
28#include "platform.h"
29#include "ibf.h"
30#include "gnunet_util_lib.h"
31#define LOG(kind, ...) GNUNET_log_from (kind, "setu", __VA_ARGS__)
32
33
34/**
35 * Compute the key's hash from the key.
36 * Redefine to use a different hash function.
37 */
38#define IBF_KEY_HASH_VAL(k) (GNUNET_CRYPTO_crc32_n (&(k), sizeof(struct \
39 IBF_KeyHash)))
40
41/**
42 * Create a key from a hashcode.
43 *
44 * @param hash the hashcode
45 * @return a key
46 */
47struct IBF_Key
48ibf_key_from_hashcode (const struct GNUNET_HashCode *hash)
49{
50 return *(struct IBF_Key *) hash;
51}
52
53
54/**
55 * Create a hashcode from a key, by replicating the key
56 * until the hascode is filled
57 *
58 * @param key the key
59 * @param dst hashcode to store the result in
60 */
61void
62ibf_hashcode_from_key (struct IBF_Key key,
63 struct GNUNET_HashCode *dst)
64{
65 struct IBF_Key *p;
66 unsigned int i;
67 const unsigned int keys_per_hashcode = sizeof(struct GNUNET_HashCode)
68 / sizeof(struct IBF_Key);
69
70 p = (struct IBF_Key *) dst;
71 for (i = 0; i < keys_per_hashcode; i++)
72 *p++ = key;
73}
74
75
76/**
77 * Create an invertible bloom filter.
78 *
79 * @param size number of IBF buckets
80 * @param hash_num number of buckets one element is hashed in
81 * @return the newly created invertible bloom filter, NULL on error
82 */
83struct InvertibleBloomFilter *
84ibf_create (uint32_t size, uint8_t hash_num)
85{
86 struct InvertibleBloomFilter *ibf;
87
88 GNUNET_assert (0 != size);
89
90 ibf = GNUNET_new (struct InvertibleBloomFilter);
91 ibf->count = GNUNET_malloc_large (size * sizeof(uint64_t));
92 if (NULL == ibf->count)
93 {
94 GNUNET_free (ibf);
95 return NULL;
96 }
97 ibf->key_sum = GNUNET_malloc_large (size * sizeof(struct IBF_Key));
98 if (NULL == ibf->key_sum)
99 {
100 GNUNET_free (ibf->count);
101 GNUNET_free (ibf);
102 return NULL;
103 }
104 ibf->key_hash_sum = GNUNET_malloc_large (size * sizeof(struct IBF_KeyHash));
105 if (NULL == ibf->key_hash_sum)
106 {
107 GNUNET_free (ibf->key_sum);
108 GNUNET_free (ibf->count);
109 GNUNET_free (ibf);
110 return NULL;
111 }
112 ibf->size = size;
113 ibf->hash_num = hash_num;
114
115 return ibf;
116}
117
118
119/**
120 * Store unique bucket indices for the specified @a key in @a dst.
121 */
122static void
123ibf_get_indices (const struct InvertibleBloomFilter *ibf,
124 struct IBF_Key key,
125 int *dst)
126{
127 uint32_t filled;
128 uint32_t i;
129 uint32_t bucket;
130
131 bucket = GNUNET_CRYPTO_crc32_n (&key, sizeof key);
132 for (i = 0, filled = 0; filled < ibf->hash_num; i++)
133 {
134 uint64_t x;
135
136 for (unsigned int j = 0; j < filled; j++)
137 if (dst[j] == bucket % ibf->size)
138 goto try_next;
139 dst[filled++] = bucket % ibf->size;
140try_next:
141 x = ((uint64_t) bucket << 32) | i;
142 bucket = GNUNET_CRYPTO_crc32_n (&x, sizeof x);
143 }
144}
145
146
147static void
148ibf_insert_into (struct InvertibleBloomFilter *ibf,
149 struct IBF_Key key,
150 const int *buckets,
151 int side)
152{
153 for (unsigned int i = 0; i < ibf->hash_num; i++)
154 {
155 const int bucket = buckets[i];
156
157 ibf->count[bucket].count_val += side;
158 ibf->key_sum[bucket].key_val ^= key.key_val;
159 ibf->key_hash_sum[bucket].key_hash_val
160 ^= IBF_KEY_HASH_VAL (key);
161 }
162}
163
164
165/**
166 * Insert a key into an IBF.
167 *
168 * @param ibf the IBF
169 * @param key the element's hash code
170 */
171void
172ibf_insert (struct InvertibleBloomFilter *ibf,
173 struct IBF_Key key)
174{
175 int buckets[ibf->hash_num];
176
177 GNUNET_assert (ibf->hash_num <= ibf->size);
178 ibf_get_indices (ibf, key, buckets);
179 ibf_insert_into (ibf, key, buckets, 1);
180}
181
182
183/**
184 * Remove a key from an IBF.
185 *
186 * @param ibf the IBF
187 * @param key the element's hash code
188 */
189void
190ibf_remove (struct InvertibleBloomFilter *ibf,
191 struct IBF_Key key)
192{
193 int buckets[ibf->hash_num];
194
195 GNUNET_assert (ibf->hash_num <= ibf->size);
196 ibf_get_indices (ibf, key, buckets);
197 ibf_insert_into (ibf, key, buckets, -1);
198}
199
200
201/**
202 * Test is the IBF is empty, i.e. all counts, keys and key hashes are zero.
203 */
204static int
205ibf_is_empty (struct InvertibleBloomFilter *ibf)
206{
207 for (uint32_t i = 0; i < ibf->size; i++)
208 {
209 if (0 != ibf->count[i].count_val)
210 return GNUNET_NO;
211 if (0 != ibf->key_hash_sum[i].key_hash_val)
212 return GNUNET_NO;
213 if (0 != ibf->key_sum[i].key_val)
214 return GNUNET_NO;
215 }
216 return GNUNET_YES;
217}
218
219
220int
221ibf_decode (struct InvertibleBloomFilter *ibf,
222 int *ret_side,
223 struct IBF_Key *ret_id)
224{
225 struct IBF_KeyHash hash;
226 int buckets[ibf->hash_num];
227
228 for (uint32_t i = 0; i < ibf->size; i++)
229 {
230 int hit;
231
232 /* we can only decode from pure buckets */
233 if ( (1 != ibf->count[i].count_val) &&
234 (-1 != ibf->count[i].count_val) )
235 continue;
236
237 hash.key_hash_val = IBF_KEY_HASH_VAL (ibf->key_sum[i]);
238
239 /* test if the hash matches the key */
240 if (hash.key_hash_val != ibf->key_hash_sum[i].key_hash_val)
241 continue;
242
243 /* test if key in bucket hits its own location,
244 * if not, the key hash was subject to collision */
245 hit = GNUNET_NO;
246 ibf_get_indices (ibf, ibf->key_sum[i], buckets);
247 for (int j = 0; j < ibf->hash_num; j++)
248 if (buckets[j] == i)
249 hit = GNUNET_YES;
250
251 if (GNUNET_NO == hit)
252 continue;
253
254 if (1 == ibf->count[i].count_val)
255 {
256 ibf->remote_decoded_count++;
257 }
258 else
259 {
260 ibf->local_decoded_count++;
261 }
262
263
264 if (NULL != ret_side)
265 *ret_side = ibf->count[i].count_val;
266 if (NULL != ret_id)
267 *ret_id = ibf->key_sum[i];
268
269 /* insert on the opposite side, effectively removing the element */
270 ibf_insert_into (ibf, ibf->key_sum[i], buckets, -ibf->count[i].count_val);
271
272 return GNUNET_YES;
273 }
274
275 if (GNUNET_YES == ibf_is_empty (ibf))
276 return GNUNET_NO;
277 return GNUNET_SYSERR;
278}
279
280
281/**
282 * Returns the minimal bytes needed to store the counter of the IBF
283 *
284 * @param ibf the IBF
285 */
286uint8_t
287ibf_get_max_counter (struct InvertibleBloomFilter *ibf)
288{
289 long long max_counter = 0;
290 for (uint64_t i = 0; i < ibf->size; i++)
291 {
292 if (ibf->count[i].count_val > max_counter)
293 {
294 max_counter = ibf->count[i].count_val;
295 }
296 }
297 return 64 - __builtin_clzll (max_counter);
298}
299
300
301/**
302 * Write buckets from an ibf to a buffer.
303 * Exactly (IBF_BUCKET_SIZE*ibf->size) bytes are written to buf.
304 *
305 * @param ibf the ibf to write
306 * @param start with which bucket to start
307 * @param count how many buckets to write
308 * @param buf buffer to write the data to
309 * @param max bit length of a counter for unpacking
310 */
311void
312ibf_write_slice (const struct InvertibleBloomFilter *ibf,
313 uint32_t start,
314 uint64_t count,
315 void *buf,
316 uint8_t counter_max_length)
317{
318 struct IBF_Key *key_dst;
319 struct IBF_KeyHash *key_hash_dst;
320
321 GNUNET_assert (start + count <= ibf->size);
322
323 /* copy keys */
324 key_dst = (struct IBF_Key *) buf;
325 GNUNET_memcpy (key_dst,
326 ibf->key_sum + start,
327 count * sizeof(*key_dst));
328 key_dst += count;
329 /* copy key hashes */
330 key_hash_dst = (struct IBF_KeyHash *) key_dst;
331 GNUNET_memcpy (key_hash_dst,
332 ibf->key_hash_sum + start,
333 count * sizeof(*key_hash_dst));
334 key_hash_dst += count;
335
336 /* pack and copy counter */
337 pack_counter (ibf,
338 start,
339 count,
340 (uint8_t *) key_hash_dst,
341 counter_max_length);
342
343
344}
345
346
347
348void
349pack_counter (const struct InvertibleBloomFilter *ibf,
350 uint32_t start,
351 uint64_t count,
352 uint8_t *buf,
353 uint8_t counter_max_length)
354{
355 uint8_t store_size = 0;
356 uint8_t store = 0;
357 uint16_t byte_ctr = 0;
358
359 /**
360 * Iterate over IBF bucket
361 */
362 for (uint64_t i = start; i< (count + start);)
363 {
364 uint64_t count_val_to_write = ibf->count[i].count_val;
365 uint8_t count_len_to_write = counter_max_length;
366
367 /**
368 * Pack and compose counters to byte values
369 */
370 while ((count_len_to_write + store_size) >= 8)
371 {
372 uint8_t bit_shift = 0;
373
374 /**
375 * Shift bits if more than a byte has to be written
376 * or the store size is not empty
377 */
378 if ((store_size > 0) || (count_len_to_write > 8))
379 {
380 uint8_t bit_unused = 8 - store_size;
381 bit_shift = count_len_to_write - bit_unused;
382 store = store << bit_unused;
383 }
384
385 buf[byte_ctr] = ((count_val_to_write >> bit_shift) | store) & 0xFF;
386 byte_ctr++;
387 count_len_to_write -= (8 - store_size);
388 count_val_to_write = count_val_to_write & ((1ULL <<
389 count_len_to_write) - 1);
390 store = 0;
391 store_size = 0;
392 }
393 store = (store << count_len_to_write) | count_val_to_write;
394 store_size = store_size + count_len_to_write;
395 count_len_to_write = 0;
396 i++;
397 }
398
399 /**
400 * Pack data left in story before finishing
401 */
402 if (store_size > 0)
403 {
404 buf[byte_ctr] = store << (8 - store_size);
405 byte_ctr++;
406 }
407
408}
409
410
411
412void
413unpack_counter (const struct InvertibleBloomFilter *ibf,
414 uint32_t start,
415 uint64_t count,
416 uint8_t *buf,
417 uint8_t counter_max_length)
418{
419 uint64_t ibf_counter_ctr = 0;
420 uint64_t store = 0;
421 uint64_t store_bit_ctr = 0;
422 uint64_t byte_ctr = 0;
423
424 /**
425 * Iterate over received bytes
426 */
427 while (true)
428 {
429 uint8_t byte_read = buf[byte_ctr];
430 uint8_t bit_to_read_left = 8;
431 byte_ctr++;
432
433 /**
434 * Pack data left in story before finishing
435 */
436 while (true)
437 {
438 /**
439 * Stop decoding when end is reached
440 */
441 if (ibf_counter_ctr > (count - 1))
442 return;
443
444 /*
445 * Unpack the counter
446 */
447 if ((store_bit_ctr + bit_to_read_left) >= counter_max_length)
448 {
449 uint8_t bytes_used = counter_max_length - store_bit_ctr;
450 if (store_bit_ctr > 0)
451 {
452 store = store << bytes_used;
453 }
454
455 uint8_t bytes_to_shift = bit_to_read_left - bytes_used;
456 uint64_t counter_part = byte_read >> bytes_to_shift;
457 store = store | counter_part;
458 ibf->count[ibf_counter_ctr + start].count_val = store;
459 byte_read = byte_read & ((1 << bytes_to_shift) - 1);
460 bit_to_read_left -= bytes_used;
461 ibf_counter_ctr++;
462 store = 0;
463 store_bit_ctr = 0;
464 }
465 else
466 {
467 store_bit_ctr += bit_to_read_left;
468 if (0 == store)
469 {
470 store = byte_read;
471 }
472 else
473 {
474 store = store << bit_to_read_left;
475 store = store | byte_read;
476 }
477 break;
478 }
479 }
480 }
481}
482
483
484/**
485 * Read buckets from a buffer into an ibf.
486 *
487 * @param buf pointer to the buffer to read from
488 * @param start which bucket to start at
489 * @param count how many buckets to read
490 * @param ibf the ibf to read from
491 * @param max bit length of a counter for unpacking
492 */
493void
494ibf_read_slice (const void *buf,
495 uint32_t start,
496 uint64_t count,
497 struct InvertibleBloomFilter *ibf,
498 uint8_t counter_max_length)
499{
500 struct IBF_Key *key_src;
501 struct IBF_KeyHash *key_hash_src;
502 struct IBF_Count *count_src;
503
504 GNUNET_assert (count > 0);
505 GNUNET_assert (start + count <= ibf->size);
506
507 /* copy keys */
508 key_src = (struct IBF_Key *) buf;
509 GNUNET_memcpy (ibf->key_sum + start,
510 key_src,
511 count * sizeof *key_src);
512 key_src += count;
513 /* copy key hashes */
514 key_hash_src = (struct IBF_KeyHash *) key_src;
515 GNUNET_memcpy (ibf->key_hash_sum + start,
516 key_hash_src,
517 count * sizeof *key_hash_src);
518 key_hash_src += count;
519
520 /* copy and unpack counts */
521 count_src = (struct IBF_Count *) key_hash_src;
522 unpack_counter (ibf,start,count,(uint8_t *) count_src,counter_max_length);
523}
524
525
526/**
527 * Subtract ibf2 from ibf1, storing the result in ibf1.
528 * The two IBF's must have the same parameters size and hash_num.
529 *
530 * @param ibf1 IBF that is subtracted from
531 * @param ibf2 IBF that will be subtracted from ibf1
532 */
533void
534ibf_subtract (struct InvertibleBloomFilter *ibf1,
535 const struct InvertibleBloomFilter *ibf2)
536{
537 GNUNET_assert (ibf1->size == ibf2->size);
538 GNUNET_assert (ibf1->hash_num == ibf2->hash_num);
539
540 for (uint32_t i = 0; i < ibf1->size; i++)
541 {
542 ibf1->count[i].count_val -= ibf2->count[i].count_val;
543 ibf1->key_hash_sum[i].key_hash_val ^= ibf2->key_hash_sum[i].key_hash_val;
544 ibf1->key_sum[i].key_val ^= ibf2->key_sum[i].key_val;
545 }
546}
547
548
549/**
550 * Create a copy of an IBF, the copy has to be destroyed properly.
551 *
552 * @param ibf the IBF to copy
553 */
554struct InvertibleBloomFilter *
555ibf_dup (const struct InvertibleBloomFilter *ibf)
556{
557 struct InvertibleBloomFilter *copy;
558
559 copy = GNUNET_malloc (sizeof *copy);
560 copy->hash_num = ibf->hash_num;
561 copy->size = ibf->size;
562 copy->key_hash_sum = GNUNET_memdup (ibf->key_hash_sum,
563 ibf->size * sizeof(struct IBF_KeyHash));
564 copy->key_sum = GNUNET_memdup (ibf->key_sum,
565 ibf->size * sizeof(struct IBF_Key));
566 copy->count = GNUNET_memdup (ibf->count,
567 ibf->size * sizeof(struct IBF_Count));
568 return copy;
569}
570
571
572/**
573 * Destroy all resources associated with the invertible bloom filter.
574 * No more ibf_*-functions may be called on ibf after calling destroy.
575 *
576 * @param ibf the intertible bloom filter to destroy
577 */
578void
579ibf_destroy (struct InvertibleBloomFilter *ibf)
580{
581 GNUNET_free (ibf->key_sum);
582 GNUNET_free (ibf->key_hash_sum);
583 GNUNET_free (ibf->count);
584 GNUNET_free (ibf);
585}