Skip to content

Commit 63a15e0

Browse files
authored
BoxedUint: optimize shl_assign (#423)
Allows `shl_assign` to operate in-place on `self` (although it still requires a temporary buffer)
1 parent d5c8dad commit 63a15e0

File tree

1 file changed

+18
-8
lines changed

1 file changed

+18
-8
lines changed

src/uint/boxed/shl.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,37 @@ impl BoxedUint {
1010
/// Returns a zero and a truthy `Choice` if `shift >= self.bits_precision()`,
1111
/// or the result and a falsy `Choice` otherwise.
1212
pub fn shl(&self, shift: u32) -> (Self, Choice) {
13+
let mut result = self.clone();
14+
let overflow = result.overflowing_shl_assign(shift);
15+
(result, overflow)
16+
}
17+
18+
/// Computes `self <<= shift`.
19+
///
20+
/// Returns a zero and a truthy `Choice` if `shift >= self.bits_precision()`,
21+
/// or a falsy `Choice` otherwise.
22+
fn overflowing_shl_assign(&mut self, shift: u32) -> Choice {
1323
// `floor(log2(bits_precision - 1))` is the number of bits in the representation of `shift`
1424
// (which lies in range `0 <= shift < bits_precision`).
1525
let shift_bits = u32::BITS - (self.bits_precision() - 1).leading_zeros();
1626
let overflow = !shift.ct_lt(&self.bits_precision());
1727
let shift = shift % self.bits_precision();
18-
let mut result = self.clone();
1928
let mut temp = self.clone();
2029

2130
for i in 0..shift_bits {
2231
let bit = Choice::from(((shift >> i) & 1) as u8);
2332
temp.set_zero();
2433
// Will not overflow by construction
25-
result
26-
.shl_vartime_into(&mut temp, 1 << i)
34+
self.shl_vartime_into(&mut temp, 1 << i)
2735
.expect("shift within range");
28-
result.conditional_assign(&temp, bit);
36+
self.conditional_assign(&temp, bit);
2937
}
3038

31-
result.conditional_set_zero(overflow);
39+
#[cfg(feature = "zeroize")]
40+
zeroize::Zeroize::zeroize(&mut temp);
3241

33-
(result, overflow)
42+
self.conditional_set_zero(overflow);
43+
overflow
3444
}
3545

3646
/// Computes `self << shift` and writes the result into `dest`.
@@ -124,8 +134,8 @@ impl Shl<u32> for &BoxedUint {
124134

125135
impl ShlAssign<u32> for BoxedUint {
126136
fn shl_assign(&mut self, shift: u32) {
127-
// TODO(tarcieri): in-place implementation that avoids clone
128-
*self = self.clone().shl(shift)
137+
let overflow = self.overflowing_shl_assign(shift);
138+
assert!(!bool::from(overflow), "attempt to shift left with overflow");
129139
}
130140
}
131141

0 commit comments

Comments
 (0)