avalanche_types/ids/
bits.rs

1//! Bitwise comparison operations used by avalanche consensus.
2use crate::ids::Id;
3
4pub const NUM_BITS: usize = 256;
5const BITS_PER_BYTES: usize = 8;
6
7/// Returns "true" if two Ids are equal for the range [start, stop).
8/// This does bit-per-bit comparison for the Id type of [u8; ID_LEN].
9/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/ids#EqualSubset>
10pub fn equal_subset(start: usize, stop: usize, id1: &Id, id2: &Id) -> bool {
11    if stop == 0 {
12        return true;
13    }
14
15    let stop = stop - 1; // -1 for end index
16    if start > stop {
17        return true;
18    }
19    if stop >= NUM_BITS {
20        return false;
21    }
22
23    let start_index = start / BITS_PER_BYTES;
24    let stop_index = stop / BITS_PER_BYTES;
25
26    if start_index + 1 < stop_index
27        && id1.0[(start_index + 1)..stop_index] != id2.0[(start_index + 1)..stop_index]
28    {
29        return false;
30    }
31
32    let start_bit = start % BITS_PER_BYTES; // index in the byte that the first bit is at
33    let stop_bit = stop % BITS_PER_BYTES; // index in the byte that the last bit is at
34
35    let start_mask: i32 = -1 << start_bit; // 111...0... The number of 0s is equal to start_bit
36    let stop_mask: i32 = (1 << (stop_bit + 1)) - 1; // 000...1... The number of 1s is equal to stop_bit + 1
37
38    if start_index == stop_index {
39        // if looking at same byte, both masks need to be applied
40        let mask = start_mask & stop_mask;
41
42        let b1 = mask & id1.0[start_index] as i32;
43        let b2 = mask & id2.0[start_index] as i32;
44
45        return b1 == b2;
46    }
47
48    let start1 = start_mask & id1.0[start_index] as i32;
49    let start2 = start_mask & id2.0[start_index] as i32;
50
51    let stop1 = stop_mask & id1.0[stop_index] as i32;
52    let stop2 = stop_mask & id2.0[stop_index] as i32;
53
54    start1 == start2 && stop1 == stop2
55}
56
57/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::bits::test_equal_subset --exact --show-output
58#[test]
59fn test_equal_subset() {
60    // ref. TestEqualSubsetEarlyStop
61    let id1 = Id::from_slice(&[0xf0, 0x0f]);
62    let id2 = Id::from_slice(&[0xf0, 0x1f]);
63
64    // println!("");
65    // for c in &id1.0 {
66    //     print!("{:08b} ", *c);
67    // }
68    // println!("");
69    // for c in &id2.0 {
70    //     print!("{:08b} ", *c);
71    // }
72    //
73    // big endian - most significant byte first, 0x1 == 00000001
74    // 11110000 00001111 00000000 ...
75    // 11110000 00011111 00000000 ...
76
77    assert!(equal_subset(0, 12, &id1, &id2));
78    assert!(!equal_subset(0, 13, &id1, &id2));
79
80    // ref. TestEqualSubsetLateStart
81    let id1 = Id::from_slice(&[0x1f, 0xf8]);
82    let id2 = Id::from_slice(&[0x10, 0x08]);
83
84    // println!("");
85    // for c in &id1.0 {
86    //     print!("{:08b} ", *c);
87    // }
88    // println!("");
89    // for c in &id2.0 {
90    //     print!("{:08b} ", *c);
91    // }
92    //
93    // big endian - most significant byte first, 0x1 == 00000001
94    // 00011111 11111000 00000000 ...
95    // 00010000 00001000 00000000 ...
96
97    assert!(equal_subset(4, 12, &id1, &id2));
98    assert!(!equal_subset(4, 13, &id1, &id2));
99}
100
101/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::bits::test_equal_subset_same_byte --exact --show-output
102/// ref. "TestEqualSubsetSameByte"
103#[test]
104fn test_equal_subset_same_byte() {
105    let id1 = Id::from_slice(&[0x18]);
106    let id2 = Id::from_slice(&[0xfc]);
107
108    // println!("");
109    // for c in &id1.0 {
110    //     print!("{:08b} ", *c);
111    // }
112    // println!("");
113    // for c in &id2.0 {
114    //     print!("{:08b} ", *c);
115    // }
116    //
117    // big endian - most significant byte first, 0x1 == 00000001
118    // 00011000 00000000 ...
119    // 11111100 00000000 ...
120
121    assert!(equal_subset(3, 5, &id1, &id2));
122    assert!(!equal_subset(2, 5, &id1, &id2));
123    assert!(!equal_subset(3, 6, &id1, &id2));
124}
125
126/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::bits::test_equal_subset_bad_middle --exact --show-output
127/// ref. "TestEqualSubsetBadMiddle"
128#[test]
129fn test_equal_subset_bad_middle() {
130    let id1 = Id::from_slice(&[0x18, 0xe8, 0x55]);
131    let id2 = Id::from_slice(&[0x18, 0x8e, 0x55]);
132
133    // println!("");
134    // for c in &id1.0 {
135    //     print!("{:08b} ", *c);
136    // }
137    // println!("");
138    // for c in &id2.0 {
139    //     print!("{:08b} ", *c);
140    // }
141    //
142    // big endian - most significant byte first, 0x1 == 00000001
143    // 00011000 11101000 01010101 00000000 ...
144    // 00011000 10001110 01010101 00000000 ...
145
146    assert!(!equal_subset(0, 8 * 3, &id1, &id2));
147}
148
149/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::bits::test_equal_subset_out_of_bounds --exact --show-output
150/// ref. "TestEqualSubsetOutOfBounds"
151#[test]
152fn test_equal_subset_out_of_bounds() {
153    let id1 = Id::from_slice(&[0x18, 0xe8, 0x55]);
154    let id2 = Id::from_slice(&[0x18, 0x8e, 0x55]);
155    assert!(!equal_subset(0, 500, &id1, &id2));
156}
157
158/// Returns the "id1" index of the first different bit in the range [start, stop).
159/// This does bit-per-bit comparison for the Id type of [u8; ID_LEN].
160/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/ids#FirstDifferenceSubset>
161pub fn first_difference_subset(start: usize, stop: usize, id1: &Id, id2: &Id) -> (usize, bool) {
162    if stop == 0 {
163        return (0, false);
164    }
165
166    let stop = stop - 1; // -1 for end index
167    if start > stop {
168        return (0, false);
169    }
170    if stop >= NUM_BITS {
171        return (0, false);
172    }
173
174    let start_index = start / BITS_PER_BYTES;
175    let stop_index = stop / BITS_PER_BYTES;
176
177    let start_bit = start % BITS_PER_BYTES; // index in the byte that the first bit is at
178    let stop_bit = stop % BITS_PER_BYTES; // index in the byte that the last bit is at
179
180    let start_mask: i32 = -1 << start_bit; // 111...0... The number of 0s is equal to start_bit
181    let stop_mask: i32 = (1 << (stop_bit + 1)) - 1; // 000...1... The number of 1s is equal to stop_bit + 1
182
183    if start_index == stop_index {
184        // if looking at same byte, both masks need to be applied
185        let mask = start_mask & stop_mask;
186
187        let b1 = mask & id1.0[start_index] as i32;
188        let b2 = mask & id2.0[start_index] as i32;
189        if b1 == b2 {
190            return (0, false);
191        }
192
193        let bd = b1 ^ b2;
194        return (
195            bd.trailing_zeros() as usize + start_index * BITS_PER_BYTES,
196            true,
197        );
198    }
199
200    let start1 = start_mask & id1.0[start_index] as i32;
201    let start2 = start_mask & id2.0[start_index] as i32;
202    if start1 != start2 {
203        let bd = start1 ^ start2;
204        return (
205            bd.trailing_zeros() as usize + start_index * BITS_PER_BYTES,
206            true,
207        );
208    }
209
210    // check interior bits
211    for idx in (start_index + 1)..stop_index {
212        let b1 = id1.0[idx];
213        let b2 = id2.0[idx];
214        if b1 != b2 {
215            let bd = b1 ^ b2;
216            return (bd.trailing_zeros() as usize + idx * BITS_PER_BYTES, true);
217        }
218    }
219
220    let stop1 = stop_mask & id1.0[stop_index] as i32;
221    let stop2 = stop_mask & id2.0[stop_index] as i32;
222    if stop1 != stop2 {
223        let bd = stop1 ^ stop2;
224        return (
225            bd.trailing_zeros() as usize + stop_index * BITS_PER_BYTES,
226            true,
227        );
228    }
229
230    (0, false) // no difference found
231}
232
233/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::bits::test_first_difference_subset --exact --show-output
234#[test]
235fn test_first_difference_subset() {
236    // ref. TestFirstDifferenceSubsetEarlyStop
237    let id1 = Id::from_slice(&[0xf0, 0x0f]);
238    let id2 = Id::from_slice(&[0xf0, 0x1f]);
239
240    // println!("");
241    // for c in &id1.0 {
242    //     print!("{:08b} ", *c);
243    // }
244    // println!("");
245    // for c in &id2.0 {
246    //     print!("{:08b} ", *c);
247    // }
248    //
249    // big endian - most significant byte first, 0x1 == 00000001
250    // 11110000 00001111 00000000 ...
251    // 11110000 00011111 00000000 ...
252
253    assert_eq!(first_difference_subset(0, 12, &id1, &id2), (0, false));
254    assert_eq!(first_difference_subset(0, 13, &id1, &id2), (12, true));
255
256    // ref. TestFirstDifferenceEqualByte4
257    let id1 = Id::from_slice(&[0x10]);
258    let id2 = Id::from_slice(&[0x00]);
259
260    // println!("");
261    // for c in &id1.0 {
262    //     print!("{:08b} ", *c);
263    // }
264    // println!("");
265    // for c in &id2.0 {
266    //     print!("{:08b} ", *c);
267    // }
268    //
269    // big endian - most significant byte first, 0x1 == 00000001
270    // 00100000 00000000 ...
271    // 00000000 00000000 ...
272
273    assert_eq!(first_difference_subset(0, 4, &id1, &id2), (0, false));
274    assert_eq!(first_difference_subset(0, 5, &id1, &id2), (4, true));
275}
276
277/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::bits::test_first_difference_equal_byte_5 --exact --show-output
278/// ref. TestFirstDifferenceEqualByte5
279#[test]
280fn test_first_difference_equal_byte_5() {
281    let id1 = Id::from_slice(&[0x20]);
282    let id2 = Id::from_slice(&[0x00]);
283
284    // println!("");
285    // for c in &id1.0 {
286    //     print!("{:08b} ", *c);
287    // }
288    // println!("");
289    // for c in &id2.0 {
290    //     print!("{:08b} ", *c);
291    // }
292    //
293    // big endian - most significant byte first, 0x1 == 00000001
294    // 00100000 00000000 ...
295    // 00000000 00000000 ...
296
297    assert_eq!(first_difference_subset(0, 5, &id1, &id2), (0, false));
298    assert_eq!(first_difference_subset(0, 6, &id1, &id2), (5, true));
299}
300
301/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::bits::test_first_difference_subset_middle --exact --show-output
302/// ref. TestFirstDifferenceSubsetMiddle
303#[test]
304fn test_first_difference_subset_middle() {
305    let id1 = Id::from_slice(&[0xf0, 0x0f, 0x11]);
306    let id2 = Id::from_slice(&[0xf0, 0x1f, 0xff]);
307
308    // println!("");
309    // for c in &id1.0 {
310    //     print!("{:08b} ", *c);
311    // }
312    // println!("");
313    // for c in &id2.0 {
314    //     print!("{:08b} ", *c);
315    // }
316    //
317    // big endian - most significant byte first, 0x1 == 00000001
318    // 11110000 00001111 00010001 00000000 ...
319    // 11110000 00011111 11111111 00000000 ...
320
321    assert_eq!(first_difference_subset(0, 24, &id1, &id2), (12, true));
322    assert_eq!(first_difference_subset(0, 12, &id1, &id2), (0, false));
323}
324
325/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::bits::test_first_difference_vacuous --exact --show-output
326/// ref. TestFirstDifferenceVacuous
327#[test]
328fn test_first_difference_vacuous() {
329    let id1 = Id::from_slice(&[0xf0, 0x0f, 0x11]);
330    let id2 = Id::from_slice(&[0xf0, 0x1f, 0xff]);
331
332    // println!("");
333    // for c in &id1.0 {
334    //     print!("{:08b} ", *c);
335    // }
336    // println!("");
337    // for c in &id2.0 {
338    //     print!("{:08b} ", *c);
339    // }
340    //
341    // big endian - most significant byte first, 0x1 == 00000001
342    // 11110000 00001111 00010001 00000000 ...
343    // 11110000 00011111 11111111 00000000 ...
344
345    assert_eq!(first_difference_subset(0, 0, &id1, &id2), (0, false));
346}
347
348#[derive(
349    std::clone::Clone,
350    std::cmp::Eq,
351    std::cmp::Ord,
352    std::cmp::PartialEq,
353    std::cmp::PartialOrd,
354    std::fmt::Debug,
355    std::hash::Hash,
356)]
357pub enum Bit {
358    Zero,
359    One,
360}
361
362impl std::convert::From<usize> for Bit {
363    fn from(v: usize) -> Self {
364        assert!(v <= 1);
365        match v {
366            0 => Bit::Zero,
367            1 => Bit::One,
368            _ => panic!("unexpected bit value {}", v),
369        }
370    }
371}
372
373impl Bit {
374    /// Returns the `usize` value of the enum member.
375    pub fn as_usize(&self) -> usize {
376        match self {
377            Bit::Zero => 0,
378            Bit::One => 1,
379        }
380    }
381}
382
383/// Set that can contain uints in the range [0, 64).
384/// All functions are O(1). The zero value is the empty set.
385/// ref. <https://pkg.go.dev/github.com/ava-labs/avalanchego/ids#BitSet64>
386#[derive(Debug, Clone, Copy, Eq, PartialEq)]
387pub struct Set64(u64);
388
389impl Set64 {
390    pub fn new() -> Self {
391        Self(0_u64)
392    }
393
394    /// Add \[i\] to the set of ints.
395    pub fn add(&mut self, i: u64) {
396        self.0 |= 1 << i;
397    }
398
399    /// Adds all the elements in \[s\] to this set.
400    pub fn union(&mut self, s: Set64) {
401        self.0 |= s.0;
402    }
403
404    /// Takes the intersection of \[s\] with this set.
405    pub fn intersection(&mut self, s: Set64) {
406        self.0 &= s.0;
407    }
408
409    /// Removes all the elements in \[s\] from this set.
410    pub fn difference(&mut self, s: Set64) {
411        // ref. *bs &^= s
412        self.0 &= !(s.0);
413    }
414
415    /// Removes \[i\] from the set of ints with bitclear (AND NOT) operation.
416    pub fn remove(&mut self, i: u64) {
417        // ref. *bs &^= 1 << i
418        self.0 &= !(1 << i);
419    }
420
421    /// Removes all elements from this set.
422    pub fn clear(&mut self) {
423        self.0 = 0;
424    }
425
426    /// Returns true if \[i\] was previously added to this set.
427    pub fn contains(&self, i: u64) -> bool {
428        (self.0 & (1 << i)) != 0
429    }
430
431    /// Returns the number of elements in the set.
432    pub fn len(&self) -> u32 {
433        // ref. bits.OnesCount64
434        u64::count_ones(self.0)
435    }
436
437    /// Returns true if the set is empty.
438    pub fn is_empty(&self) -> bool {
439        self.len() == 0
440    }
441}
442
443impl Default for Set64 {
444    fn default() -> Self {
445        Self::new()
446    }
447}
448
449/// ref. <https://doc.rust-lang.org/std/string/trait.ToString.html>
450/// ref. <https://doc.rust-lang.org/std/fmt/trait.Display.html>
451/// Use "Self.to_string()" to directly invoke this.
452impl std::fmt::Display for Set64 {
453    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454        write!(f, "{:#16x}", self.0)
455    }
456}
457
458/// RUST_LOG=debug cargo test --package avalanche-types --lib -- ids::bits::test_bit_set --exact --show-output
459#[test]
460fn test_bit_set() {
461    let mut bs1 = Set64::new();
462    assert!(bs1.is_empty());
463
464    bs1.add(5);
465    assert!(bs1.len() == 1);
466    assert!(bs1.contains(5));
467
468    bs1.add(10);
469    assert!(bs1.len() == 2);
470    assert!(bs1.contains(5));
471    assert!(bs1.contains(10));
472
473    bs1.add(10);
474    assert!(bs1.len() == 2);
475    assert!(bs1.contains(5));
476    assert!(bs1.contains(10));
477
478    let mut bs2 = Set64::new();
479    assert!(bs2.is_empty());
480
481    bs2.add(0);
482    assert!(bs2.len() == 1);
483    assert!(bs2.contains(0));
484
485    bs2.union(bs1);
486    assert!(bs1.len() == 2);
487    assert!(bs1.contains(5));
488    assert!(bs1.contains(10));
489    assert!(bs2.len() == 3);
490    assert!(bs2.contains(0));
491    assert!(bs2.contains(5));
492    assert!(bs2.contains(10));
493
494    bs1.clear();
495    assert!(bs1.is_empty());
496    assert!(bs2.len() == 3);
497    assert!(bs2.contains(0));
498    assert!(bs2.contains(5));
499    assert!(bs2.contains(10));
500
501    bs1.add(63);
502    assert!(bs1.len() == 1);
503    assert!(bs1.contains(63));
504
505    bs1.add(1);
506    assert!(bs1.len() == 2);
507    assert!(bs1.contains(1));
508    assert!(bs1.contains(63));
509
510    bs1.remove(63);
511    assert!(bs1.len() == 1);
512    assert!(bs1.contains(1));
513
514    let mut bs3 = Set64::new();
515    bs3.add(0);
516    bs3.add(2);
517    bs3.add(5);
518
519    let mut bs4 = Set64::new();
520    bs4.add(2);
521    bs4.add(5);
522
523    bs3.intersection(bs4);
524
525    assert!(bs3.len() == 2);
526    assert!(bs3.contains(2));
527    assert!(bs3.contains(5));
528    assert!(bs4.len() == 2);
529    assert!(bs4.contains(2));
530    assert!(bs4.contains(5));
531
532    let mut bs5 = Set64::new();
533    bs5.add(7);
534    bs5.add(11);
535    bs5.add(9);
536
537    let mut bs6 = Set64::new();
538    bs6.add(9);
539    bs6.add(11);
540
541    bs5.difference(bs6);
542
543    assert!(bs5.len() == 1);
544    assert!(bs5.contains(7));
545    assert!(bs6.len() == 2);
546    assert!(bs6.contains(9));
547    assert!(bs6.contains(11));
548}