ckb_vm/
bits.rs

1#[inline(always)]
2pub fn roundup(x: u64, round: u64) -> u64 {
3    debug_assert!(round.is_power_of_two());
4    // x + (((!x) + 1) & (round - 1))
5    x.wrapping_add((!x).wrapping_add(1) & (round.wrapping_sub(1)))
6}
7
8#[inline(always)]
9pub fn rounddown(x: u64, round: u64) -> u64 {
10    debug_assert!(round.is_power_of_two());
11    // x & !(round - 1)
12    x & !(round.wrapping_sub(1))
13}
14
15#[cfg(test)]
16mod tests {
17    use super::*;
18    use proptest::prelude::*;
19
20    #[test]
21    fn test_roundup() {
22        assert_eq!(0, roundup(0, 16));
23        assert_eq!(16, roundup(1, 16));
24        assert_eq!(16, roundup(15, 16));
25        assert_eq!(16, roundup(16, 16));
26        assert_eq!(32, roundup(17, 16));
27        assert_eq!(u64::MAX - 15, roundup(u64::MAX - 15, 16));
28        assert_eq!(0, roundup(u64::MAX, 16));
29    }
30
31    #[test]
32    fn test_rounddown() {
33        assert_eq!(0, rounddown(0, 16));
34        assert_eq!(0, rounddown(1, 16));
35        assert_eq!(0, rounddown(15, 16));
36        assert_eq!(16, rounddown(16, 16));
37        assert_eq!(16, rounddown(17, 16));
38        assert_eq!(u64::MAX - 15, rounddown(u64::MAX, 16));
39    }
40
41    proptest! {
42        #[test]
43        #[cfg_attr(all(miri, feature = "miri-ci"), ignore)]
44        fn roundup_proptest(x: u64, round in (0u32..16).prop_map(|d| 2u64.pow(d))) {
45            prop_assume!(x.checked_add(round).is_some(), "avoid integer overflow");
46            let result = roundup(x, round);
47
48            // multiple of round
49            assert_eq!(result % round, 0);
50
51            // lower bound
52            assert!(result >= x);
53
54            // upper bound
55            assert!(result < x + round);
56        }
57
58        #[test]
59        #[cfg_attr(all(miri, feature = "miri-ci"), ignore)]
60        fn rounddown_proptest(x: u64, round in (0u32..16).prop_map(|d| 2u64.pow(d))) {
61            let result = rounddown(x, round);
62
63            // multiple of round
64            assert_eq!(result % round, 0);
65
66            // upper bound
67            assert!(result <= x);
68
69            // lower bound
70            if let Some(lower_bound) = x.checked_sub(round) {
71                assert!(result > lower_bound);
72            } else {
73                assert_eq!(result, 0);
74            }
75        }
76    }
77}