collision detection for IBF, timing for test 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 #include "ibf.h"
29
30
31 /**
32  * Opaque handle to an invertible bloom filter (IBF).
33  *
34  * An IBF is a counting bloom filter that has the ability to restore
35  * the hashes of its stored elements with high probability.
36  */
37 struct InvertibleBloomFilter
38 {
39   /**
40    * How many cells does this IBF have?
41    */
42   unsigned int size;
43
44   /**
45    * In how many cells do we hash one element?
46    * Usually 4 or 3.
47    */
48   unsigned int hash_num;
49
50   /**
51    * Salt for mingling hashes
52    */
53   uint32_t salt;
54
55   /**
56    * How many times has a bucket been hit?
57    * Can be negative, as a result of IBF subtraction.
58    */
59   int8_t *count;
60
61   /**
62    * xor sums of the elements' hash codes, used to identify the elements.
63    */
64   struct GNUNET_HashCode *id_sum;
65
66   /**
67    * xor sums of the "hash of the hash".
68    */
69   struct GNUNET_HashCode *hash_sum;
70
71 };
72
73
74 /**
75  * Create an invertible bloom filter.
76  *
77  * @param size number of IBF buckets
78  * @param hash_num number of buckets one element is hashed in
79  * @param salt salt for mingling hashes, different salt may
80  *        result in less (or more) collisions
81  * @return the newly created invertible bloom filter
82  */
83 struct InvertibleBloomFilter *
84 ibf_create (unsigned int size, unsigned int hash_num, uint32_t salt)
85 {
86   struct InvertibleBloomFilter *ibf;
87
88   ibf = GNUNET_malloc (sizeof (struct InvertibleBloomFilter));
89   ibf->count = GNUNET_malloc (size * sizeof (uint8_t));
90   ibf->id_sum = GNUNET_malloc (size * sizeof (struct GNUNET_HashCode));
91   ibf->hash_sum = GNUNET_malloc (size * sizeof (struct GNUNET_HashCode));
92   ibf->size = size;
93   ibf->hash_num = hash_num;
94
95   return ibf;
96 }
97
98
99
100 /**
101  * Insert an element into an IBF, with either positive or negative sign.
102  *
103  * @param ibf the IBF
104  * @param id the element's hash code
105  * @param side the sign of side determines the sign of the 
106  *        inserted element.
107  */
108 void
109 ibf_insert_on_side (struct InvertibleBloomFilter *ibf,
110                     const struct GNUNET_HashCode *key,
111                     int side)
112 {
113   struct GNUNET_HashCode bucket_indices;
114   struct GNUNET_HashCode key_copy;
115   struct GNUNET_HashCode key_hash;
116   int *used_buckets;
117   unsigned int i;
118
119
120   GNUNET_assert ((1 == side) || (-1 == side));
121   GNUNET_assert (NULL != ibf);
122
123   used_buckets = alloca (ibf->hash_num * sizeof (int));
124
125   /* copy the key, if key and an entry in the IBF alias */
126   key_copy = *key;
127
128   bucket_indices = key_copy;
129   GNUNET_CRYPTO_hash (key, sizeof (struct GNUNET_HashCode), &key_hash);
130
131   for (i = 0; i < ibf->hash_num; i++)
132   {
133     unsigned int bucket;
134     unsigned int j;
135     int collided;
136     
137     if ((i % 16) == 0)
138       GNUNET_CRYPTO_hash (&bucket_indices, sizeof (struct GNUNET_HashCode),
139                           &bucket_indices);
140
141     bucket = bucket_indices.bits[i%16] % ibf->size;
142     collided = GNUNET_NO;
143     for (j = 0; j < i; j++)
144       if (used_buckets[j] == bucket)
145         collided = GNUNET_YES;
146     if (GNUNET_YES == collided)
147     {
148       used_buckets[i] = -1;
149       continue;
150     }
151     used_buckets[i] = bucket;
152
153     ibf->count[bucket] += side;
154     
155     GNUNET_CRYPTO_hash_xor (&key_copy, &ibf->id_sum[bucket],
156                             &ibf->id_sum[bucket]);
157     GNUNET_CRYPTO_hash_xor (&key_hash, &ibf->hash_sum[bucket],
158                             &ibf->hash_sum[bucket]);
159   }
160 }
161
162 /**
163  * Insert an element into an IBF.
164  *
165  * @param ibf the IBF
166  * @param id the element's hash code
167  */
168 void
169 ibf_insert (struct InvertibleBloomFilter *ibf, const struct GNUNET_HashCode *key)
170 {
171   ibf_insert_on_side (ibf, key, 1);
172 }
173
174 static int
175 ibf_is_empty (struct InvertibleBloomFilter *ibf)
176 {
177   int i;
178   for (i = 0; i < ibf->size; i++)
179   {
180     int j;
181     if (0 != ibf->count[i])
182       return GNUNET_NO;
183     for (j = 0; j < 16; ++j)
184     {
185       if (0 != ibf->hash_sum[i].bits[j])
186         return GNUNET_NO;
187       if (0 != ibf->id_sum[i].bits[j])
188         return GNUNET_NO;
189     }
190   }
191   return GNUNET_YES;
192 }
193
194
195 /**
196  * Decode and remove an element from the IBF, if possible.
197  *
198  * @param ibf the invertible bloom filter to decode
199  * @param side sign of the cell's count where the decoded element came from.
200  *             A negative sign indicates that the element was recovered
201  *             resides in an IBF that was previously subtracted from.
202  * @param ret_id the hash code of the decoded element, if successful
203  * @return GNUNET_YES if decoding an element was successful,
204  *         GNUNET_NO if the IBF is empty,
205  *         GNUNET_SYSERR if the decoding has failed
206  */
207 int
208 ibf_decode (struct InvertibleBloomFilter *ibf,
209             int *ret_side, struct GNUNET_HashCode *ret_id)
210 {
211   struct GNUNET_HashCode hash;
212   int i;
213
214   GNUNET_assert (NULL != ibf);
215   GNUNET_assert (NULL != ret_id);
216   GNUNET_assert (NULL != ret_side);
217
218   for (i = 0; i < ibf->size; i++)
219   {
220     if ((1 != ibf->count[i]) && (-1 != ibf->count[i]))
221       continue;
222
223     GNUNET_CRYPTO_hash (&ibf->id_sum[i], sizeof (struct GNUNET_HashCode), &hash);
224
225     if (0 != memcmp (&hash, &ibf->hash_sum[i], sizeof (struct GNUNET_HashCode)))
226       continue;
227
228     *ret_side = ibf->count[i];
229     *ret_id = ibf->id_sum[i];
230
231     /* insert on the opposite side, effectively removing the element */
232     ibf_insert_on_side (ibf, &ibf->id_sum[i], -ibf->count[i]);
233
234     return GNUNET_YES;
235   }
236
237   if (GNUNET_YES == ibf_is_empty (ibf))
238     return GNUNET_NO;
239   return GNUNET_SYSERR;
240 }
241
242
243
244 /**
245  * Subtract ibf2 from ibf1, storing the result in ibf1.
246  * The two IBF's must have the same parameters size and hash_num.
247  *
248  * @param ibf1 IBF that is subtracted from
249  * @param ibf2 IBF that will be subtracted from ibf1
250  */
251 void
252 ibf_subtract (struct InvertibleBloomFilter *ibf1, struct InvertibleBloomFilter *ibf2)
253 {
254   int i;
255
256   GNUNET_assert (ibf1->size == ibf2->size);
257   GNUNET_assert (ibf1->hash_num == ibf2->hash_num);
258   GNUNET_assert (ibf1->salt == ibf2->salt);
259
260   for (i = 0; i < ibf1->size; i++)
261   {
262     ibf1->count[i] -= ibf2->count[i];
263     GNUNET_CRYPTO_hash_xor (&ibf1->id_sum[i], &ibf2->id_sum[i],
264                             &ibf1->id_sum[i]);
265     GNUNET_CRYPTO_hash_xor (&ibf1->hash_sum[i], &ibf2->hash_sum[i], 
266                             &ibf1->hash_sum[i]);
267   }
268 }
269