aboutsummaryrefslogtreecommitdiff
path: root/src/consensus/ibf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/consensus/ibf.c')
-rw-r--r--src/consensus/ibf.c262
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);
+}