crypto_bigint/int/
shl.rs

1//! [`Int`] bitwise left shift operations.
2
3use core::ops::{Shl, ShlAssign};
4
5use subtle::CtOption;
6
7use crate::{ConstCtOption, Int, ShlVartime, Uint, WrappingShl};
8
9impl<const LIMBS: usize> Int<LIMBS> {
10    /// Computes `self << shift`.
11    ///
12    /// Panics if `shift >= Self::BITS`.
13    pub const fn shl(&self, shift: u32) -> Self {
14        Self(Uint::shl(&self.0, shift))
15    }
16
17    /// Computes `self << shift` in variable time.
18    ///
19    /// Panics if `shift >= Self::BITS`.
20    pub const fn shl_vartime(&self, shift: u32) -> Self {
21        Self(Uint::shl_vartime(&self.0, shift))
22    }
23
24    /// Computes `self << shift`.
25    ///
26    /// Returns `None` if `shift >= Self::BITS`.
27    pub const fn overflowing_shl(&self, shift: u32) -> ConstCtOption<Self> {
28        self.0.overflowing_shl(shift).as_int()
29    }
30
31    /// Computes `self << shift`.
32    ///
33    /// Returns `None` if `shift >= Self::BITS`.
34    ///
35    /// NOTE: this operation is variable time with respect to `shift` *ONLY*.
36    ///
37    /// When used with a fixed `shift`, this function is constant-time with respect
38    /// to `self`.
39    #[inline(always)]
40    pub const fn overflowing_shl_vartime(&self, shift: u32) -> ConstCtOption<Self> {
41        self.0.overflowing_shl_vartime(shift).as_int()
42    }
43
44    /// Computes `self << shift` in a panic-free manner, returning zero if the shift exceeds the
45    /// precision.
46    pub const fn wrapping_shl(&self, shift: u32) -> Self {
47        Self(self.0.wrapping_shl(shift))
48    }
49
50    /// Computes `self << shift` in variable-time in a panic-free manner, returning zero if the
51    /// shift exceeds the precision.
52    pub const fn wrapping_shl_vartime(&self, shift: u32) -> Self {
53        Self(self.0.wrapping_shl_vartime(shift))
54    }
55}
56
57macro_rules! impl_shl {
58    ($($shift:ty),+) => {
59        $(
60            impl<const LIMBS: usize> Shl<$shift> for Int<LIMBS> {
61                type Output = Int<LIMBS>;
62
63                #[inline]
64                fn shl(self, shift: $shift) -> Int<LIMBS> {
65                    <&Self>::shl(&self, shift)
66                }
67            }
68
69            impl<const LIMBS: usize> Shl<$shift> for &Int<LIMBS> {
70                type Output = Int<LIMBS>;
71
72                #[inline]
73                fn shl(self, shift: $shift) -> Int<LIMBS> {
74                    Int::<LIMBS>::shl(self, u32::try_from(shift).expect("invalid shift"))
75                }
76            }
77
78            impl<const LIMBS: usize> ShlAssign<$shift> for Int<LIMBS> {
79                fn shl_assign(&mut self, shift: $shift) {
80                    *self = self.shl(shift)
81                }
82            }
83        )+
84    };
85}
86
87impl_shl!(i32, u32, usize);
88
89impl<const LIMBS: usize> WrappingShl for Int<LIMBS> {
90    fn wrapping_shl(&self, shift: u32) -> Int<LIMBS> {
91        self.wrapping_shl(shift)
92    }
93}
94
95impl<const LIMBS: usize> ShlVartime for Int<LIMBS> {
96    fn overflowing_shl_vartime(&self, shift: u32) -> CtOption<Self> {
97        self.overflowing_shl(shift).into()
98    }
99    fn wrapping_shl_vartime(&self, shift: u32) -> Self {
100        self.wrapping_shl(shift)
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use crate::I256;
107
108    const N: I256 =
109        I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
110
111    const TWO_N: I256 =
112        I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD755DB9CD5E9140777FA4BD19A06C8282");
113
114    const FOUR_N: I256 =
115        I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAEABB739ABD2280EEFF497A3340D90504");
116
117    const SIXTY_FIVE: I256 =
118        I256::from_be_hex("FFFFFFFFFFFFFFFD755DB9CD5E9140777FA4BD19A06C82820000000000000000");
119
120    const EIGHTY_EIGHT: I256 =
121        I256::from_be_hex("FFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD03641410000000000000000000000");
122
123    const SIXTY_FOUR: I256 =
124        I256::from_be_hex("FFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD03641410000000000000000");
125
126    #[test]
127    fn shl_simple() {
128        let mut t = I256::from(1i8);
129        assert_eq!(t << 1, I256::from(2i8));
130        t = I256::from(3i8);
131        assert_eq!(t << 8, I256::from(0x300i16));
132    }
133
134    #[test]
135    fn shl1() {
136        assert_eq!(N << 1, TWO_N);
137    }
138
139    #[test]
140    fn shl2() {
141        assert_eq!(N << 2, FOUR_N);
142    }
143
144    #[test]
145    fn shl65() {
146        assert_eq!(N << 65, SIXTY_FIVE);
147    }
148
149    #[test]
150    fn shl88() {
151        assert_eq!(N << 88, EIGHTY_EIGHT);
152    }
153
154    #[test]
155    fn shl256_const() {
156        assert!(N.overflowing_shl(256).is_none().is_true_vartime());
157        assert!(N.overflowing_shl_vartime(256).is_none().is_true_vartime());
158    }
159
160    #[test]
161    #[should_panic(expected = "`shift` within the bit size of the integer")]
162    fn shl256() {
163        let _ = N << 256;
164    }
165
166    #[test]
167    fn shl64() {
168        assert_eq!(N << 64, SIXTY_FOUR);
169    }
170}