-fix
[oweals/gnunet.git] / src / consensus / ibf.c
1 /*
2       This file is part of GNUnet
3       (C) 2012 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 2, or (at your
8       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       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19 */
20
21
22 /**
23  * @file consensus/ibf.c
24  * @brief implementation of the invertible bloom filter
25  * @author Florian Dold
26  */
27
28
29 #include "ibf.h"
30
31
32 /**
33  * Create an invertible bloom filter.
34  *
35  * @param size number of IBF buckets
36  * @param hash_num number of buckets one element is hashed in
37  * @param salt salt for mingling hashes, different salt may
38  *        result in less (or more) collisions
39  * @return the newly created invertible bloom filter
40  */
41 struct InvertibleBloomFilter *
42 ibf_create (unsigned int size, unsigned int hash_num, uint32_t salt)
43 {
44   struct InvertibleBloomFilter *ibf;
45
46   ibf = GNUNET_malloc (sizeof (struct InvertibleBloomFilter));
47   ibf->count = GNUNET_malloc (size * sizeof (uint8_t));
48   ibf->id_sum = GNUNET_malloc (size * sizeof (struct GNUNET_HashCode));
49   ibf->hash_sum = GNUNET_malloc (size * sizeof (struct GNUNET_HashCode));
50   ibf->size = size;
51   ibf->hash_num = hash_num;
52
53   return ibf;
54 }
55
56
57
58 /**
59  * Insert an element into an IBF, with either positive or negative sign.
60  *
61  * @param ibf the IBF
62  * @param id the element's hash code
63  * @param side the sign of side determines the sign of the 
64  *        inserted element.
65  */
66 void
67 ibf_insert_on_side (struct InvertibleBloomFilter *ibf,
68                     const struct GNUNET_HashCode *key,
69                     int side)
70 {
71   struct GNUNET_HashCode bucket_indices;
72   struct GNUNET_HashCode key_copy;
73   struct GNUNET_HashCode key_hash;
74   unsigned int i;
75
76
77   GNUNET_assert ((1 == side) || (-1 == side));
78   GNUNET_assert (NULL != ibf);
79
80   {
81     int used_buckets[ibf->hash_num];
82
83     /* copy the key, if key and an entry in the IBF alias */
84     key_copy = *key;
85
86     bucket_indices = key_copy;
87     GNUNET_CRYPTO_hash (key, sizeof (struct GNUNET_HashCode), &key_hash);
88     
89     for (i = 0; i < ibf->hash_num; i++)
90     {
91       unsigned int bucket;
92       unsigned int j;
93       int collided;
94     
95       if ( (0 != i) &&
96            (0 == (i % 16)) )
97         GNUNET_CRYPTO_hash (&bucket_indices, sizeof (struct GNUNET_HashCode),
98                             &bucket_indices);
99       
100       bucket = bucket_indices.bits[i%16] % ibf->size;
101       collided = GNUNET_NO;
102       for (j = 0; j < i; j++)
103         if (used_buckets[j] == bucket)
104           collided = GNUNET_YES;
105       if (GNUNET_YES == collided)
106         {
107           used_buckets[i] = -1;
108           continue;
109         }
110       used_buckets[i] = bucket;
111       
112       ibf->count[bucket] += side;
113
114       GNUNET_CRYPTO_hash_xor (&key_copy, &ibf->id_sum[bucket],
115                               &ibf->id_sum[bucket]);
116       GNUNET_CRYPTO_hash_xor (&key_hash, &ibf->hash_sum[bucket],
117                               &ibf->hash_sum[bucket]);
118     }
119   }
120 }
121
122 /**
123  * Insert an element into an IBF.
124  *
125  * @param ibf the IBF
126  * @param id the element's hash code
127  */
128 void
129 ibf_insert (struct InvertibleBloomFilter *ibf, const struct GNUNET_HashCode *key)
130 {
131   ibf_insert_on_side (ibf, key, 1);
132 }
133
134 static int
135 ibf_is_empty (struct InvertibleBloomFilter *ibf)
136 {
137   int i;
138   for (i = 0; i < ibf->size; i++)
139   {
140     int j;
141     if (0 != ibf->count[i])
142       return GNUNET_NO;
143     for (j = 0; j < 16; ++j)
144     {
145       if (0 != ibf->hash_sum[i].bits[j])
146         return GNUNET_NO;
147       if (0 != ibf->id_sum[i].bits[j])
148         return GNUNET_NO;
149     }
150   }
151   return GNUNET_YES;
152 }
153
154
155 /**
156  * Decode and remove an element from the IBF, if possible.
157  *
158  * @param ibf the invertible bloom filter to decode
159  * @param side sign of the cell's count where the decoded element came from.
160  *             A negative sign indicates that the element was recovered
161  *             resides in an IBF that was previously subtracted from.
162  * @param ret_id the hash code of the decoded element, if successful
163  * @return GNUNET_YES if decoding an element was successful,
164  *         GNUNET_NO if the IBF is empty,
165  *         GNUNET_SYSERR if the decoding has failed
166  */
167 int
168 ibf_decode (struct InvertibleBloomFilter *ibf,
169             int *ret_side, struct GNUNET_HashCode *ret_id)
170 {
171   struct GNUNET_HashCode hash;
172   int i;
173
174   GNUNET_assert (NULL != ibf);
175
176   for (i = 0; i < ibf->size; i++)
177   {
178     if ((1 != ibf->count[i]) && (-1 != ibf->count[i]))
179       continue;
180
181     GNUNET_CRYPTO_hash (&ibf->id_sum[i], sizeof (struct GNUNET_HashCode), &hash);
182
183     if (0 != memcmp (&hash, &ibf->hash_sum[i], sizeof (struct GNUNET_HashCode)))
184       continue;
185
186     if (NULL != ret_side)
187       *ret_side = ibf->count[i];
188     if (NULL != ret_id)
189       *ret_id = ibf->id_sum[i];
190
191     /* insert on the opposite side, effectively removing the element */
192     ibf_insert_on_side (ibf, &ibf->id_sum[i], -ibf->count[i]);
193
194     return GNUNET_YES;
195   }
196
197   if (GNUNET_YES == ibf_is_empty (ibf))
198     return GNUNET_NO;
199   return GNUNET_SYSERR;
200 }
201
202
203
204 /**
205  * Subtract ibf2 from ibf1, storing the result in ibf1.
206  * The two IBF's must have the same parameters size and hash_num.
207  *
208  * @param ibf1 IBF that is subtracted from
209  * @param ibf2 IBF that will be subtracted from ibf1
210  */
211 void
212 ibf_subtract (struct InvertibleBloomFilter *ibf1, struct InvertibleBloomFilter *ibf2)
213 {
214   int i;
215
216   GNUNET_assert (ibf1->size == ibf2->size);
217   GNUNET_assert (ibf1->hash_num == ibf2->hash_num);
218   GNUNET_assert (ibf1->salt == ibf2->salt);
219
220   for (i = 0; i < ibf1->size; i++)
221   {
222     ibf1->count[i] -= ibf2->count[i];
223     GNUNET_CRYPTO_hash_xor (&ibf1->id_sum[i], &ibf2->id_sum[i],
224                             &ibf1->id_sum[i]);
225     GNUNET_CRYPTO_hash_xor (&ibf1->hash_sum[i], &ibf2->hash_sum[i], 
226                             &ibf1->hash_sum[i]);
227   }
228 }
229
230 /**
231  * Create a copy of an IBF, the copy has to be destroyed properly.
232  *
233  * @param ibf the IBF to copy
234  */
235 struct InvertibleBloomFilter *
236 ibf_dup (struct InvertibleBloomFilter *ibf)
237 {
238   struct InvertibleBloomFilter *copy;
239   copy = GNUNET_malloc (sizeof *copy);
240   copy->hash_num = ibf->hash_num;
241   copy->salt = ibf->salt;
242   copy->size = ibf->size;
243   copy->hash_sum = GNUNET_memdup (ibf->hash_sum, ibf->size * sizeof (struct GNUNET_HashCode));
244   copy->id_sum = GNUNET_memdup (ibf->id_sum, ibf->size * sizeof (struct GNUNET_HashCode));
245   copy->count = GNUNET_memdup (ibf->count, ibf->size * sizeof (uint8_t));
246   return copy;
247 }
248
249 /**
250  * Destroy all resources associated with the invertible bloom filter.
251  * No more ibf_*-functions may be called on ibf after calling destroy.
252  *
253  * @param ibf the intertible bloom filter to destroy
254  */
255 void
256 ibf_destroy (struct InvertibleBloomFilter *ibf)
257 {
258   GNUNET_free (ibf->hash_sum);
259   GNUNET_free (ibf->id_sum);
260   GNUNET_free (ibf->count);
261   GNUNET_free (ibf);
262 }