GNUnet 0.21.1
container_bloomfilter.c
Go to the documentation of this file.
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2004, 2006, 2008, 2011, 2012, 2018 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 */
43#include "platform.h"
44#include "gnunet_util_lib.h"
45
46#define LOG(kind, ...) \
47 GNUNET_log_from (kind, "util-container-bloomfilter", __VA_ARGS__)
48
49#define LOG_STRERROR(kind, syscall) \
50 GNUNET_log_from_strerror (kind, "util-container-bloomfilter", syscall)
51
52#define LOG_STRERROR_FILE(kind, syscall, filename) \
53 GNUNET_log_from_strerror_file (kind, \
54 "util-container-bloomfilter", \
55 syscall, \
56 filename)
57
59{
63 char *bitArray;
64
68 char *filename;
69
74
78 unsigned int addressesPerElement;
79
84};
85
86
87size_t
89 const struct GNUNET_CONTAINER_BloomFilter *bf)
90{
91 if (bf == NULL)
92 return 0;
93 return bf->addressesPerElement;
94}
95
96
97size_t
99 const struct GNUNET_CONTAINER_BloomFilter *bf)
100{
101 if (bf == NULL)
102 return 0;
103 return bf->bitArraySize;
104}
105
106
109 const struct GNUNET_CONTAINER_BloomFilter *bf)
110{
112 bf->bitArraySize,
114}
115
116
124static void
126 unsigned int bitIdx)
127{
128 size_t arraySlot;
129 unsigned int targetBit;
130
131 arraySlot = bitIdx / 8;
132 targetBit = (1L << (bitIdx % 8));
133 bitArray[arraySlot] |= targetBit;
134}
135
136
144static void
145clearBit (char *bitArray, unsigned int bitIdx)
146{
147 size_t slot;
148 unsigned int targetBit;
149
150 slot = bitIdx / 8;
151 targetBit = (1L << (bitIdx % 8));
152 bitArray[slot] = bitArray[slot] & (~targetBit);
153}
154
155
163static bool
165 unsigned int bitIdx)
166{
167 size_t slot;
168 unsigned int targetBit;
169
170 slot = bitIdx / 8;
171 targetBit = (1L << (bitIdx % 8));
172 if (bitArray[slot] & targetBit)
173 return true;
174 return false;
175}
176
177
187static void
189 unsigned int bitIdx,
190 const struct GNUNET_DISK_FileHandle *fh)
191{
192 off_t fileSlot;
193 unsigned char value;
194 unsigned int high;
195 unsigned int low;
196 unsigned int targetLoc;
197
199 bitIdx);
201 return;
202 /* Update the counter file on disk */
203 fileSlot = bitIdx / 2;
204 targetLoc = bitIdx % 2;
205
206 GNUNET_assert (fileSlot ==
208 if (1 != GNUNET_DISK_file_read (fh, &value, 1))
209 value = 0;
210 low = value & 0xF;
211 high = (value & (~0xF)) >> 4;
212
213 if (targetLoc == 0)
214 {
215 if (low < 0xF)
216 low++;
217 }
218 else
219 {
220 if (high < 0xF)
221 high++;
222 }
223 value = ((high << 4) | low);
224 GNUNET_assert (fileSlot ==
227}
228
229
238static void
240 unsigned int bitIdx,
241 const struct GNUNET_DISK_FileHandle *fh)
242{
243 off_t fileslot;
244 unsigned char value;
245 unsigned int high;
246 unsigned int low;
247 unsigned int targetLoc;
248
250 return; /* cannot decrement! */
251 /* Each char slot in the counter file holds two 4 bit counters */
252 fileslot = bitIdx / 2;
253 targetLoc = bitIdx % 2;
254 if (GNUNET_SYSERR ==
256 {
258 return;
259 }
260 if (1 != GNUNET_DISK_file_read (fh, &value, 1))
261 value = 0;
262 low = value & 0xF;
263 high = (value & 0xF0) >> 4;
264
265 /* decrement, but once we have reached the max, never go back! */
266 if (targetLoc == 0)
267 {
268 if ((low > 0) && (low < 0xF))
269 low--;
270 if (low == 0)
271 {
272 clearBit (bitArray, bitIdx);
273 }
274 }
275 else
276 {
277 if ((high > 0) && (high < 0xF))
278 high--;
279 if (high == 0)
280 {
281 clearBit (bitArray, bitIdx);
282 }
283 }
284 value = ((high << 4) | low);
285 if (GNUNET_SYSERR ==
287 {
289 return;
290 }
292}
293
294
295#define BUFFSIZE 65536
296
306 size_t size)
307{
308 char buffer[BUFFSIZE];
309 size_t bytesleft = size;
310 int res = 0;
311
313 return GNUNET_SYSERR;
314 memset (buffer, 0, sizeof(buffer));
316 while (bytesleft > 0)
317 {
318 if (bytesleft > sizeof(buffer))
319 {
320 res = GNUNET_DISK_file_write (fh, buffer, sizeof(buffer));
321 if (res >= 0)
322 bytesleft -= res;
323 }
324 else
325 {
326 res = GNUNET_DISK_file_write (fh, buffer, bytesleft);
327 if (res >= 0)
328 bytesleft -= res;
329 }
330 if (GNUNET_SYSERR == res)
331 return GNUNET_SYSERR;
332 }
333 return GNUNET_OK;
334}
335
336
337/* ************** GNUNET_CONTAINER_BloomFilter iterator ********* */
338
349typedef enum GNUNET_GenericReturnValue
350(*BitIterator)(void *cls,
351 const struct GNUNET_CONTAINER_BloomFilter *bf,
352 unsigned int bit);
353
354
364static void
366 BitIterator callback,
367 void *arg,
368 const struct GNUNET_HashCode *key)
369{
370 struct GNUNET_HashCode tmp = *key;
371 int bitCount;
372 unsigned int slot = 0;
373
374 bitCount = bf->addressesPerElement;
375 GNUNET_assert (bf->bitArraySize > 0);
376 GNUNET_assert (bf->bitArraySize * 8LL > bf->bitArraySize);
377 while (bitCount > 0)
378 {
379 while ( (0 != bitCount) &&
380 (slot < (sizeof(struct GNUNET_HashCode) / sizeof(uint32_t))) )
381 {
382 if (GNUNET_YES !=
383 callback (arg,
384 bf,
385 ntohl ((((uint32_t *) &tmp)[slot]))
386 % ((bf->bitArraySize * 8LL))))
387 return;
388 slot++;
389 bitCount--;
390 }
391 if (0 == bitCount)
392 break;
393 GNUNET_CRYPTO_hash (&tmp,
394 sizeof(tmp),
395 &tmp);
396 slot = 0;
397 }
398}
399
400
411 const struct GNUNET_CONTAINER_BloomFilter *bf,
412 unsigned int bit)
413{
414 struct GNUNET_CONTAINER_BloomFilter *b = cls;
415
417 bit,
418 bf->fh);
419 return GNUNET_YES;
420}
421
422
433 const struct GNUNET_CONTAINER_BloomFilter *bf,
434 unsigned int bit)
435{
436 struct GNUNET_CONTAINER_BloomFilter *b = cls;
437
439 bit,
440 bf->fh);
441 return GNUNET_YES;
442}
443
444
455 const struct GNUNET_CONTAINER_BloomFilter *bf,
456 unsigned int bit)
457{
458 bool *arg = cls;
459
460 if (! testBit (bf->bitArray, bit))
461 {
462 *arg = false;
463 return GNUNET_NO;
464 }
465 return GNUNET_YES;
466}
467
468
469/* *********************** INTERFACE **************** */
470
473 size_t size,
474 unsigned int k)
475{
477 char *rbuff;
478 off_t pos;
479 int i;
480 size_t ui;
481 off_t fsize;
482 int must_read;
483
484 GNUNET_assert (NULL != filename);
485 if ((k == 0) || (size == 0))
486 return NULL;
487 if (size < BUFFSIZE)
488 size = BUFFSIZE;
489 ui = 1;
490 while ((ui < size) && (ui * 2 > ui))
491 ui *= 2;
492 size = ui; /* make sure it's a power of 2 */
493
495 /* Try to open a bloomfilter file */
501 if (NULL != bf->fh)
502 {
503 /* file existed, try to read it! */
504 must_read = GNUNET_YES;
505 if (GNUNET_OK !=
507 &fsize))
508 {
510 GNUNET_free (bf);
511 return NULL;
512 }
513 if (0 == fsize)
514 {
515 /* found existing empty file, just overwrite */
516 if (GNUNET_OK !=
517 make_empty_file (bf->fh,
518 size * 4LL))
519 {
521 "write");
523 GNUNET_free (bf);
524 return NULL;
525 }
526 }
527 else if (fsize != ((off_t) size) * 4LL)
528 {
529 GNUNET_log (
531 _ (
532 "Size of file on disk is incorrect for this Bloom filter (want %llu, have %llu)\n"),
533 (unsigned long long) (size * 4LL),
534 (unsigned long long) fsize);
536 GNUNET_free (bf);
537 return NULL;
538 }
539 }
540 else
541 {
542 /* file did not exist, don't read, just create */
543 must_read = GNUNET_NO;
549 if (NULL == bf->fh)
550 {
551 GNUNET_free (bf);
552 return NULL;
553 }
554 if (GNUNET_OK != make_empty_file (bf->fh, size * 4LL))
555 {
558 GNUNET_free (bf);
559 return NULL;
560 }
561 }
563 /* Alloc block */
565 if (NULL == bf->bitArray)
566 {
567 if (NULL != bf->fh)
569 GNUNET_free (bf->filename);
570 GNUNET_free (bf);
571 return NULL;
572 }
573 bf->bitArraySize = size;
574 bf->addressesPerElement = k;
575 if (GNUNET_YES != must_read)
576 return bf; /* already done! */
577 /* Read from the file what bits we can */
578 rbuff = GNUNET_malloc (BUFFSIZE);
579 pos = 0;
580 while (pos < ((off_t) size) * 8LL)
581 {
582 int res;
583
584 res = GNUNET_DISK_file_read (bf->fh, rbuff, BUFFSIZE);
585 if (res == -1)
586 {
588 GNUNET_free (rbuff);
589 GNUNET_free (bf->filename);
591 GNUNET_free (bf);
592 return NULL;
593 }
594 if (res == 0)
595 break; /* is ok! we just did not use that many bits yet */
596 for (i = 0; i < res; i++)
597 {
598 if ((rbuff[i] & 0x0F) != 0)
599 setBit (bf->bitArray, pos + i * 2);
600 if ((rbuff[i] & 0xF0) != 0)
601 setBit (bf->bitArray, pos + i * 2 + 1);
602 }
603 if (res < BUFFSIZE)
604 break;
605 pos += BUFFSIZE * 2; /* 2 bits per byte in the buffer */
606 }
607 GNUNET_free (rbuff);
608 return bf;
609}
610
611
614 size_t size,
615 unsigned int k)
616{
618
619 if ((0 == k) || (0 == size))
620 return NULL;
622 bf->filename = NULL;
623 bf->fh = NULL;
625 if (NULL == bf->bitArray)
626 {
627 GNUNET_free (bf);
628 return NULL;
629 }
630 bf->bitArraySize = size;
631 bf->addressesPerElement = k;
632 if (NULL != data)
634 return bf;
635}
636
637
640 const struct GNUNET_CONTAINER_BloomFilter *bf,
641 char *data,
642 size_t size)
643{
644 if (NULL == bf)
645 return GNUNET_SYSERR;
646 if (bf->bitArraySize != size)
647 return GNUNET_SYSERR;
649 return GNUNET_OK;
650}
651
652
653void
655{
656 if (NULL == bf)
657 return;
658 if (bf->fh != NULL)
660 GNUNET_free (bf->filename);
661 GNUNET_free (bf->bitArray);
662 GNUNET_free (bf);
663}
664
665
666void
668{
669 if (NULL == bf)
670 return;
671
672 memset (bf->bitArray, 0, bf->bitArraySize);
673 if (bf->filename != NULL)
674 make_empty_file (bf->fh, bf->bitArraySize * 4LL);
675}
676
677
678bool
680 const struct GNUNET_CONTAINER_BloomFilter *bf,
681 const struct GNUNET_HashCode *e)
682{
683 bool res;
684
685 if (NULL == bf)
686 return true;
687 res = true;
688 iterateBits (bf,
690 &res,
691 e);
692 return res;
693}
694
695
696void
698 const struct GNUNET_HashCode *e)
699{
700 if (NULL == bf)
701 return;
702 iterateBits (bf,
704 bf,
705 e);
706}
707
708
711 const char *data,
712 size_t size)
713{
714 unsigned int i;
715 unsigned int n;
716 unsigned long long *fc;
717 const unsigned long long *dc;
718
719 if (NULL == bf)
720 return GNUNET_YES;
721 if (bf->bitArraySize != size)
722 return GNUNET_SYSERR;
723 fc = (unsigned long long *) bf->bitArray;
724 dc = (const unsigned long long *) data;
725 n = size / sizeof(unsigned long long);
726
727 for (i = 0; i < n; i++)
728 fc[i] |= dc[i];
729 for (i = n * sizeof(unsigned long long); i < size; i++)
730 bf->bitArray[i] |= data[i];
731 return GNUNET_OK;
732}
733
734
738 const struct GNUNET_CONTAINER_BloomFilter *to_or)
739{
740 unsigned int i;
741 unsigned int n;
742 unsigned long long *fc;
743 const unsigned long long *dc;
744 size_t size;
745
746 if (NULL == bf)
747 return GNUNET_OK;
748 if (bf->bitArraySize != to_or->bitArraySize)
749 {
750 GNUNET_break (0);
751 return GNUNET_SYSERR;
752 }
753 size = bf->bitArraySize;
754 fc = (unsigned long long *) bf->bitArray;
755 dc = (const unsigned long long *) to_or->bitArray;
756 n = size / sizeof(unsigned long long);
757
758 for (i = 0; i < n; i++)
759 fc[i] |= dc[i];
760 for (i = n * sizeof(unsigned long long); i < size; i++)
761 bf->bitArray[i] |= to_or->bitArray[i];
762 return GNUNET_OK;
763}
764
765
766void
768 const struct GNUNET_HashCode *e)
769{
770 if (NULL == bf)
771 return;
772 if (NULL == bf->filename)
773 return;
774 iterateBits (bf,
776 bf,
777 e);
778}
779
780
781void
784 void *iterator_cls,
785 size_t size,
786 unsigned int k)
787{
788 struct GNUNET_HashCode hc;
789 unsigned int i;
790
791 GNUNET_free (bf->bitArray);
792 i = 1;
793 while (i < size)
794 i *= 2;
795 size = i; /* make sure it's a power of 2 */
796 bf->addressesPerElement = k;
797 bf->bitArraySize = size;
799 if (NULL != bf->filename)
800 make_empty_file (bf->fh, bf->bitArraySize * 4LL);
801 while (GNUNET_YES == iterator (iterator_cls, &hc))
803}
804
805
806/* end of container_bloomfilter.c */
#define BUFFSIZE
static enum GNUNET_GenericReturnValue make_empty_file(const struct GNUNET_DISK_FileHandle *fh, size_t size)
Creates a file filled with zeroes.
static enum GNUNET_GenericReturnValue testBitCallback(void *cls, const struct GNUNET_CONTAINER_BloomFilter *bf, unsigned int bit)
Callback: test if all bits are set.
static void incrementBit(char *bitArray, unsigned int bitIdx, const struct GNUNET_DISK_FileHandle *fh)
Sets a bit active in the bitArray and increments bit-specific usage counter on disk (but only if the ...
static void decrementBit(char *bitArray, unsigned int bitIdx, const struct GNUNET_DISK_FileHandle *fh)
Clears a bit from bitArray if the respective usage counter on the disk hits/is zero.
static enum GNUNET_GenericReturnValue incrementBitCallback(void *cls, const struct GNUNET_CONTAINER_BloomFilter *bf, unsigned int bit)
Callback: increment bit.
static void clearBit(char *bitArray, unsigned int bitIdx)
Clears a bit from bitArray.
#define LOG_STRERROR_FILE(kind, syscall, filename)
static void setBit(char *bitArray, unsigned int bitIdx)
Sets a bit active in the bitArray.
enum GNUNET_GenericReturnValue(* BitIterator)(void *cls, const struct GNUNET_CONTAINER_BloomFilter *bf, unsigned int bit)
Iterator (callback) method to be called by the bloomfilter iterator on each bit that is to be set or ...
static void iterateBits(const struct GNUNET_CONTAINER_BloomFilter *bf, BitIterator callback, void *arg, const struct GNUNET_HashCode *key)
Call an iterator for each bit that the bloomfilter must test or set for this element.
static bool testBit(char *bitArray, unsigned int bitIdx)
Checks if a bit is active in the bitArray.
static enum GNUNET_GenericReturnValue decrementBitCallback(void *cls, const struct GNUNET_CONTAINER_BloomFilter *bf, unsigned int bit)
Callback: decrement bit.
static char * data
The data to insert into the dht.
struct GNUNET_HashCode key
The key used in the DHT.
static struct GNUNET_FS_DownloadContext * dc
static char * filename
static char * res
Currently read line or NULL on EOF.
static char * value
Value of the record to add/remove.
static struct GNUNET_DISK_FileHandle * fh
File handle to STDIN, for reading restart/quit commands.
void GNUNET_CONTAINER_bloomfilter_resize(struct GNUNET_CONTAINER_BloomFilter *bf, GNUNET_CONTAINER_HashCodeIterator iterator, void *iterator_cls, size_t size, unsigned int k)
Resize a bloom filter.
struct GNUNET_CONTAINER_BloomFilter * GNUNET_CONTAINER_bloomfilter_init(const char *data, size_t size, unsigned int k)
Create a Bloom filter from raw bits.
void GNUNET_CONTAINER_bloomfilter_add(struct GNUNET_CONTAINER_BloomFilter *bf, const struct GNUNET_HashCode *e)
Add an element to the filter.
bool GNUNET_CONTAINER_bloomfilter_test(const struct GNUNET_CONTAINER_BloomFilter *bf, const struct GNUNET_HashCode *e)
Test if an element is in the filter.
struct GNUNET_CONTAINER_BloomFilter * GNUNET_CONTAINER_bloomfilter_copy(const struct GNUNET_CONTAINER_BloomFilter *bf)
Create a copy of a bloomfilter.
void GNUNET_CONTAINER_bloomfilter_clear(struct GNUNET_CONTAINER_BloomFilter *bf)
Reset a Bloom filter to empty.
enum GNUNET_GenericReturnValue GNUNET_CONTAINER_bloomfilter_get_raw_data(const struct GNUNET_CONTAINER_BloomFilter *bf, char *data, size_t size)
Copy the raw data of this Bloom filter into the given data array.
int(* GNUNET_CONTAINER_HashCodeIterator)(void *cls, struct GNUNET_HashCode *next)
Iterator over struct GNUNET_HashCode.
enum GNUNET_GenericReturnValue GNUNET_CONTAINER_bloomfilter_or2(struct GNUNET_CONTAINER_BloomFilter *bf, const struct GNUNET_CONTAINER_BloomFilter *to_or)
"or" the entries of the given raw data array with the data of the given Bloom filter.
struct GNUNET_CONTAINER_BloomFilter * GNUNET_CONTAINER_bloomfilter_load(const char *filename, size_t size, unsigned int k)
Load a Bloom filter from a file.
enum GNUNET_GenericReturnValue GNUNET_CONTAINER_bloomfilter_or(struct GNUNET_CONTAINER_BloomFilter *bf, const char *data, size_t size)
"or" the entries of the given raw data array with the data of the given Bloom filter.
size_t GNUNET_CONTAINER_bloomfilter_get_size(const struct GNUNET_CONTAINER_BloomFilter *bf)
Get size of the bloom filter.
void GNUNET_CONTAINER_bloomfilter_remove(struct GNUNET_CONTAINER_BloomFilter *bf, const struct GNUNET_HashCode *e)
Remove an element from the filter.
void GNUNET_CONTAINER_bloomfilter_free(struct GNUNET_CONTAINER_BloomFilter *bf)
Free the space associated with a filter in memory, flush to drive if needed (do not free the space on...
struct GNUNET_DISK_FileHandle * GNUNET_DISK_file_open(const char *fn, enum GNUNET_DISK_OpenFlags flags, enum GNUNET_DISK_AccessPermissions perm)
Open a file.
Definition: disk.c:1237
enum GNUNET_GenericReturnValue GNUNET_DISK_file_test(const char *fil)
Check that fil corresponds to a filename (of a file that exists and that is not a directory).
Definition: disk.c:482
ssize_t GNUNET_DISK_file_write(const struct GNUNET_DISK_FileHandle *h, const void *buffer, size_t n)
Write a buffer to a file.
Definition: disk.c:686
off_t GNUNET_DISK_file_seek(const struct GNUNET_DISK_FileHandle *h, off_t offset, enum GNUNET_DISK_Seek whence)
Move the read/write pointer in a file.
Definition: disk.c:205
enum GNUNET_GenericReturnValue GNUNET_DISK_handle_invalid(const struct GNUNET_DISK_FileHandle *h)
Checks whether a handle is invalid.
Definition: disk.c:185
enum GNUNET_GenericReturnValue GNUNET_DISK_file_close(struct GNUNET_DISK_FileHandle *h)
Close an open file.
Definition: disk.c:1308
ssize_t GNUNET_DISK_file_read(const struct GNUNET_DISK_FileHandle *h, void *result, size_t len)
Read the contents of a binary file into a buffer.
Definition: disk.c:622
enum GNUNET_GenericReturnValue GNUNET_DISK_file_handle_size(struct GNUNET_DISK_FileHandle *fh, off_t *size)
Get the size of an open file.
Definition: disk.c:192
@ GNUNET_DISK_OPEN_CREATE
Create file if it doesn't exist.
@ GNUNET_DISK_OPEN_READWRITE
Open the file for both reading and writing.
@ GNUNET_DISK_PERM_USER_READ
Owner can read.
@ GNUNET_DISK_PERM_USER_WRITE
Owner can write.
@ GNUNET_DISK_SEEK_SET
Seek an absolute position (from the start of the file).
void GNUNET_CRYPTO_hash(const void *block, size_t size, struct GNUNET_HashCode *ret)
Compute hash of a given block.
Definition: crypto_hash.c:41
#define GNUNET_log(kind,...)
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
size_t GNUNET_CONTAINER_bloomfilter_get_element_addresses(const struct GNUNET_CONTAINER_BloomFilter *bf)
Get the number of the addresses set per element in the bloom filter.
GNUNET_GenericReturnValue
Named constants for return values.
@ GNUNET_OK
@ GNUNET_YES
@ GNUNET_NO
@ GNUNET_SYSERR
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
#define GNUNET_log_strerror(level, cmd)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
#define GNUNET_malloc_large(size)
Wrapper around malloc.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_malloc(size)
Wrapper around malloc.
#define GNUNET_free(ptr)
Wrapper around free.
static unsigned int size
Size of the "table".
Definition: peer.c:68
#define _(String)
GNU gettext support macro.
Definition: platform.h:178
size_t bitArraySize
Size of bitArray in bytes.
unsigned int addressesPerElement
How many bits we set for each stored element.
struct GNUNET_DISK_FileHandle * fh
The bit counter file on disk.
char * bitArray
The actual bloomfilter bit array.
char * filename
Filename of the filter.
Handle used to access files (and pipes).
A 512-bit hashcode.