diff options
Diffstat (limited to 'src/consensus/ibf.c')
-rw-r--r-- | src/consensus/ibf.c | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/src/consensus/ibf.c b/src/consensus/ibf.c new file mode 100644 index 0000000..629fde3 --- /dev/null +++ b/src/consensus/ibf.c @@ -0,0 +1,262 @@ +/* + This file is part of GNUnet + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + + +/** + * @file consensus/ibf.c + * @brief implementation of the invertible bloom filter + * @author Florian Dold + */ + + +#include "ibf.h" + + +/** + * Create an invertible bloom filter. + * + * @param size number of IBF buckets + * @param hash_num number of buckets one element is hashed in + * @param salt salt for mingling hashes, different salt may + * result in less (or more) collisions + * @return the newly created invertible bloom filter + */ +struct InvertibleBloomFilter * +ibf_create (unsigned int size, unsigned int hash_num, uint32_t salt) +{ + struct InvertibleBloomFilter *ibf; + + ibf = GNUNET_malloc (sizeof (struct InvertibleBloomFilter)); + ibf->count = GNUNET_malloc (size * sizeof (uint8_t)); + ibf->id_sum = GNUNET_malloc (size * sizeof (struct GNUNET_HashCode)); + ibf->hash_sum = GNUNET_malloc (size * sizeof (struct GNUNET_HashCode)); + ibf->size = size; + ibf->hash_num = hash_num; + + return ibf; +} + + + +/** + * Insert an element into an IBF, with either positive or negative sign. + * + * @param ibf the IBF + * @param id the element's hash code + * @param side the sign of side determines the sign of the + * inserted element. + */ +void +ibf_insert_on_side (struct InvertibleBloomFilter *ibf, + const struct GNUNET_HashCode *key, + int side) +{ + struct GNUNET_HashCode bucket_indices; + struct GNUNET_HashCode key_copy; + struct GNUNET_HashCode key_hash; + unsigned int i; + + + GNUNET_assert ((1 == side) || (-1 == side)); + GNUNET_assert (NULL != ibf); + + { + int used_buckets[ibf->hash_num]; + + /* copy the key, if key and an entry in the IBF alias */ + key_copy = *key; + + bucket_indices = key_copy; + GNUNET_CRYPTO_hash (key, sizeof (struct GNUNET_HashCode), &key_hash); + + for (i = 0; i < ibf->hash_num; i++) + { + unsigned int bucket; + unsigned int j; + int collided; + + if ( (0 != i) && + (0 == (i % 16)) ) + GNUNET_CRYPTO_hash (&bucket_indices, sizeof (struct GNUNET_HashCode), + &bucket_indices); + + bucket = bucket_indices.bits[i%16] % ibf->size; + collided = GNUNET_NO; + for (j = 0; j < i; j++) + if (used_buckets[j] == bucket) + collided = GNUNET_YES; + if (GNUNET_YES == collided) + { + used_buckets[i] = -1; + continue; + } + used_buckets[i] = bucket; + + ibf->count[bucket] += side; + + GNUNET_CRYPTO_hash_xor (&key_copy, &ibf->id_sum[bucket], + &ibf->id_sum[bucket]); + GNUNET_CRYPTO_hash_xor (&key_hash, &ibf->hash_sum[bucket], + &ibf->hash_sum[bucket]); + } + } +} + +/** + * Insert an element into an IBF. + * + * @param ibf the IBF + * @param id the element's hash code + */ +void +ibf_insert (struct InvertibleBloomFilter *ibf, const struct GNUNET_HashCode *key) +{ + ibf_insert_on_side (ibf, key, 1); +} + +static int +ibf_is_empty (struct InvertibleBloomFilter *ibf) +{ + int i; + for (i = 0; i < ibf->size; i++) + { + int j; + if (0 != ibf->count[i]) + return GNUNET_NO; + for (j = 0; j < 16; ++j) + { + if (0 != ibf->hash_sum[i].bits[j]) + return GNUNET_NO; + if (0 != ibf->id_sum[i].bits[j]) + return GNUNET_NO; + } + } + return GNUNET_YES; +} + + +/** + * Decode and remove an element from the IBF, if possible. + * + * @param ibf the invertible bloom filter to decode + * @param side sign of the cell's count where the decoded element came from. + * A negative sign indicates that the element was recovered + * resides in an IBF that was previously subtracted from. + * @param ret_id the hash code of the decoded element, if successful + * @return GNUNET_YES if decoding an element was successful, + * GNUNET_NO if the IBF is empty, + * GNUNET_SYSERR if the decoding has failed + */ +int +ibf_decode (struct InvertibleBloomFilter *ibf, + int *ret_side, struct GNUNET_HashCode *ret_id) +{ + struct GNUNET_HashCode hash; + int i; + + GNUNET_assert (NULL != ibf); + + for (i = 0; i < ibf->size; i++) + { + if ((1 != ibf->count[i]) && (-1 != ibf->count[i])) + continue; + + GNUNET_CRYPTO_hash (&ibf->id_sum[i], sizeof (struct GNUNET_HashCode), &hash); + + if (0 != memcmp (&hash, &ibf->hash_sum[i], sizeof (struct GNUNET_HashCode))) + continue; + + if (NULL != ret_side) + *ret_side = ibf->count[i]; + if (NULL != ret_id) + *ret_id = ibf->id_sum[i]; + + /* insert on the opposite side, effectively removing the element */ + ibf_insert_on_side (ibf, &ibf->id_sum[i], -ibf->count[i]); + + return GNUNET_YES; + } + + if (GNUNET_YES == ibf_is_empty (ibf)) + return GNUNET_NO; + return GNUNET_SYSERR; +} + + + +/** + * Subtract ibf2 from ibf1, storing the result in ibf1. + * The two IBF's must have the same parameters size and hash_num. + * + * @param ibf1 IBF that is subtracted from + * @param ibf2 IBF that will be subtracted from ibf1 + */ +void +ibf_subtract (struct InvertibleBloomFilter *ibf1, struct InvertibleBloomFilter *ibf2) +{ + int i; + + GNUNET_assert (ibf1->size == ibf2->size); + GNUNET_assert (ibf1->hash_num == ibf2->hash_num); + GNUNET_assert (ibf1->salt == ibf2->salt); + + for (i = 0; i < ibf1->size; i++) + { + ibf1->count[i] -= ibf2->count[i]; + GNUNET_CRYPTO_hash_xor (&ibf1->id_sum[i], &ibf2->id_sum[i], + &ibf1->id_sum[i]); + GNUNET_CRYPTO_hash_xor (&ibf1->hash_sum[i], &ibf2->hash_sum[i], + &ibf1->hash_sum[i]); + } +} + +/** + * Create a copy of an IBF, the copy has to be destroyed properly. + * + * @param ibf the IBF to copy + */ +struct InvertibleBloomFilter * +ibf_dup (struct InvertibleBloomFilter *ibf) +{ + struct InvertibleBloomFilter *copy; + copy = GNUNET_malloc (sizeof *copy); + copy->hash_num = ibf->hash_num; + copy->salt = ibf->salt; + copy->size = ibf->size; + copy->hash_sum = GNUNET_memdup (ibf->hash_sum, ibf->size * sizeof (struct GNUNET_HashCode)); + copy->id_sum = GNUNET_memdup (ibf->id_sum, ibf->size * sizeof (struct GNUNET_HashCode)); + copy->count = GNUNET_memdup (ibf->count, ibf->size * sizeof (uint8_t)); + return copy; +} + +/** + * Destroy all resources associated with the invertible bloom filter. + * No more ibf_*-functions may be called on ibf after calling destroy. + * + * @param ibf the intertible bloom filter to destroy + */ +void +ibf_destroy (struct InvertibleBloomFilter *ibf) +{ + GNUNET_free (ibf->hash_sum); + GNUNET_free (ibf->id_sum); + GNUNET_free (ibf->count); + GNUNET_free (ibf); +} |