- gnunet-consensus now profiling tool
[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_log_from(GNUNET_ERROR_TYPE_INFO, "ibf", "inserting in bucket %d \n", bucket);
115       
116       GNUNET_CRYPTO_hash_xor (&key_copy, &ibf->id_sum[bucket],
117                               &ibf->id_sum[bucket]);
118       GNUNET_CRYPTO_hash_xor (&key_hash, &ibf->hash_sum[bucket],
119                               &ibf->hash_sum[bucket]);
120     }
121   }
122 }
123
124 /**
125  * Insert an element into an IBF.
126  *
127  * @param ibf the IBF
128  * @param id the element's hash code
129  */
130 void
131 ibf_insert (struct InvertibleBloomFilter *ibf, const struct GNUNET_HashCode *key)
132 {
133   ibf_insert_on_side (ibf, key, 1);
134 }
135
136 static int
137 ibf_is_empty (struct InvertibleBloomFilter *ibf)
138 {
139   int i;
140   for (i = 0; i < ibf->size; i++)
141   {
142     int j;
143     if (0 != ibf->count[i])
144       return GNUNET_NO;
145     for (j = 0; j < 16; ++j)
146     {
147       if (0 != ibf->hash_sum[i].bits[j])
148         return GNUNET_NO;
149       if (0 != ibf->id_sum[i].bits[j])
150         return GNUNET_NO;
151     }
152   }
153   return GNUNET_YES;
154 }
155
156
157 /**
158  * Decode and remove an element from the IBF, if possible.
159  *
160  * @param ibf the invertible bloom filter to decode
161  * @param side sign of the cell's count where the decoded element came from.
162  *             A negative sign indicates that the element was recovered
163  *             resides in an IBF that was previously subtracted from.
164  * @param ret_id the hash code of the decoded element, if successful
165  * @return GNUNET_YES if decoding an element was successful,
166  *         GNUNET_NO if the IBF is empty,
167  *         GNUNET_SYSERR if the decoding has failed
168  */
169 int
170 ibf_decode (struct InvertibleBloomFilter *ibf,
171             int *ret_side, struct GNUNET_HashCode *ret_id)
172 {
173   struct GNUNET_HashCode hash;
174   int i;
175
176   GNUNET_assert (NULL != ibf);
177
178   for (i = 0; i < ibf->size; i++)
179   {
180     if ((1 != ibf->count[i]) && (-1 != ibf->count[i]))
181       continue;
182
183     GNUNET_CRYPTO_hash (&ibf->id_sum[i], sizeof (struct GNUNET_HashCode), &hash);
184
185     if (0 != memcmp (&hash, &ibf->hash_sum[i], sizeof (struct GNUNET_HashCode)))
186       continue;
187
188     if (NULL != ret_side)
189       *ret_side = ibf->count[i];
190     if (NULL != ret_id)
191       *ret_id = ibf->id_sum[i];
192
193     /* insert on the opposite side, effectively removing the element */
194     ibf_insert_on_side (ibf, &ibf->id_sum[i], -ibf->count[i]);
195
196     return GNUNET_YES;
197   }
198
199   if (GNUNET_YES == ibf_is_empty (ibf))
200     return GNUNET_NO;
201   return GNUNET_SYSERR;
202 }
203
204
205
206 /**
207  * Subtract ibf2 from ibf1, storing the result in ibf1.
208  * The two IBF's must have the same parameters size and hash_num.
209  *
210  * @param ibf1 IBF that is subtracted from
211  * @param ibf2 IBF that will be subtracted from ibf1
212  */
213 void
214 ibf_subtract (struct InvertibleBloomFilter *ibf1, struct InvertibleBloomFilter *ibf2)
215 {
216   int i;
217
218   GNUNET_assert (ibf1->size == ibf2->size);
219   GNUNET_assert (ibf1->hash_num == ibf2->hash_num);
220   GNUNET_assert (ibf1->salt == ibf2->salt);
221
222   for (i = 0; i < ibf1->size; i++)
223   {
224     ibf1->count[i] -= ibf2->count[i];
225     GNUNET_CRYPTO_hash_xor (&ibf1->id_sum[i], &ibf2->id_sum[i],
226                             &ibf1->id_sum[i]);
227     GNUNET_CRYPTO_hash_xor (&ibf1->hash_sum[i], &ibf2->hash_sum[i], 
228                             &ibf1->hash_sum[i]);
229   }
230 }
231
232 /**
233  * Create a copy of an IBF, the copy has to be destroyed properly.
234  *
235  * @param ibf the IBF to copy
236  */
237 struct InvertibleBloomFilter *
238 ibf_dup (struct InvertibleBloomFilter *ibf)
239 {
240   struct InvertibleBloomFilter *copy;
241   copy = GNUNET_malloc (sizeof *copy);
242   copy->hash_num = ibf->hash_num;
243   copy->salt = ibf->salt;
244   copy->size = ibf->size;
245   copy->hash_sum = GNUNET_memdup (ibf->hash_sum, ibf->size * sizeof (struct GNUNET_HashCode));
246   copy->id_sum = GNUNET_memdup (ibf->id_sum, ibf->size * sizeof (struct GNUNET_HashCode));
247   copy->count = GNUNET_memdup (ibf->count, ibf->size * sizeof (uint8_t));
248   return copy;
249 }
250
251 /**
252  * Destroy all resources associated with the invertible bloom filter.
253  * No more ibf_*-functions may be called on ibf after calling destroy.
254  *
255  * @param ibf the intertible bloom filter to destroy
256  */
257 void
258 ibf_destroy (struct InvertibleBloomFilter *ibf)
259 {
260   GNUNET_free (ibf->hash_sum);
261   GNUNET_free (ibf->id_sum);
262   GNUNET_free (ibf->count);
263   GNUNET_free (ibf);
264 }