Skip to main content

crypto_bigint/uint/ref_type/
bits.rs

1use super::UintRef;
2use crate::{Choice, Limb, traits::BitOps, word};
3
4impl UintRef {
5    /// Get the precision of this number in bits.
6    #[allow(clippy::cast_possible_truncation)]
7    #[must_use]
8    pub const fn bits_precision(&self) -> u32 {
9        self.limbs.len() as u32 * Limb::BITS
10    }
11
12    /// Get the value of the bit at position `index`, as a truthy or falsy [`Choice`].
13    /// Returns the falsy value for indices out of range.
14    #[inline(always)]
15    #[allow(clippy::cast_possible_truncation)]
16    #[must_use]
17    pub const fn bit(&self, index: u32) -> Choice {
18        let limb_num = index >> Limb::LOG2_BITS;
19        let index_in_limb = index & (Limb::BITS - 1);
20        let index_mask = 1 << index_in_limb;
21
22        let mut result = 0;
23        let mut i = 0;
24        while i < self.limbs.len() {
25            let is_right_limb = Choice::from_u32_eq(i as u32, limb_num);
26            result |= word::select(0, self.limbs[i].0, is_right_limb);
27            i += 1;
28        }
29
30        word::choice_from_lsb((result & index_mask) >> index_in_limb)
31    }
32
33    /// Returns `true` if the bit at position `index` is set, `false` for an unset bit
34    /// or for indices out of range.
35    ///
36    /// # Remarks
37    /// This operation is variable time with respect to `index` only.
38    #[inline(always)]
39    #[must_use]
40    #[allow(clippy::integer_division_remainder_used, reason = "vartime")]
41    pub const fn bit_vartime(&self, index: u32) -> bool {
42        let limb_num = (index / Limb::BITS) as usize;
43        let index_in_limb = index % Limb::BITS;
44        if limb_num >= self.limbs.len() {
45            false
46        } else {
47            (self.limbs[limb_num].0 >> index_in_limb) & 1 == 1
48        }
49    }
50
51    /// Calculate the number of bits needed to represent this number, i.e. the index of the highest
52    /// set bit.
53    ///
54    /// Use [`UintRef::bits_precision`] to get the total capacity of this integer.
55    #[inline(always)]
56    #[must_use]
57    pub const fn bits(&self) -> u32 {
58        self.bits_precision() - self.leading_zeros()
59    }
60
61    /// Calculate the number of bits needed to represent this number in variable-time with respect
62    /// to `self`.
63    #[inline(always)]
64    #[allow(clippy::cast_possible_truncation)]
65    #[must_use]
66    pub const fn bits_vartime(&self) -> u32 {
67        let mut i = self.limbs.len() - 1;
68        while i > 0 && self.limbs[i].0 == 0 {
69            i -= 1;
70        }
71
72        let limb = self.limbs[i];
73        Limb::BITS * (i as u32 + 1) - limb.leading_zeros()
74    }
75
76    /// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`.
77    #[inline(always)]
78    #[allow(clippy::cast_possible_truncation)]
79    pub const fn set_bit(&mut self, index: u32, bit_value: Choice) {
80        let limb_num = index >> Limb::LOG2_BITS;
81        let index_in_limb = index & (Limb::BITS - 1);
82        let index_mask = 1 << index_in_limb;
83
84        let mut i = 0;
85        while i < self.limbs.len() {
86            let is_right_limb = Choice::from_u32_eq(i as u32, limb_num);
87            let old_limb = self.limbs[i].0;
88            let new_limb = word::select(old_limb & !index_mask, old_limb | index_mask, bit_value);
89            self.limbs[i] = Limb(word::select(old_limb, new_limb, is_right_limb));
90            i += 1;
91        }
92    }
93
94    /// Sets the bit at `index` to 0 or 1 depending on the value of `bit_value`, in variable-time
95    /// with respect to `index`.
96    #[inline(always)]
97    #[track_caller]
98    #[allow(clippy::integer_division_remainder_used, reason = "vartime")]
99    pub const fn set_bit_vartime(&mut self, index: u32, bit_value: bool) {
100        let limb_num = (index / Limb::BITS) as usize;
101        let index_in_limb = index % Limb::BITS;
102        if bit_value {
103            self.limbs[limb_num].0 |= 1 << index_in_limb;
104        } else {
105            self.limbs[limb_num].0 &= !(1 << index_in_limb);
106        }
107    }
108
109    /// Calculate the number of leading zeros in the binary representation of this number.
110    #[must_use]
111    pub const fn leading_zeros(&self) -> u32 {
112        let mut count = 0;
113        let mut i = self.limbs.len();
114        let mut nonzero_limb_not_encountered = Choice::TRUE;
115        while i > 0 {
116            i -= 1;
117            let l = self.limbs[i];
118            let z = l.leading_zeros();
119            count += nonzero_limb_not_encountered.select_u32(0, z);
120            nonzero_limb_not_encountered =
121                nonzero_limb_not_encountered.and(word::choice_from_nz(l.0).not());
122        }
123
124        count
125    }
126
127    /// Calculate the number of trailing zeros in the binary representation of this number.
128    #[inline(always)]
129    #[must_use]
130    pub const fn trailing_zeros(&self) -> u32 {
131        let mut count = 0;
132        let mut i = 0;
133        let mut nonzero_limb_not_encountered = Choice::TRUE;
134        while i < self.limbs.len() {
135            let l = self.limbs[i];
136            let z = l.trailing_zeros();
137            count += nonzero_limb_not_encountered.select_u32(0, z);
138            nonzero_limb_not_encountered =
139                nonzero_limb_not_encountered.and(word::choice_from_nz(l.0).not());
140            i += 1;
141        }
142
143        count
144    }
145
146    /// Calculate the number of trailing zeros in the binary representation of this number, in
147    /// variable-time with respect to `self`.
148    #[inline(always)]
149    #[must_use]
150    pub const fn trailing_zeros_vartime(&self) -> u32 {
151        let mut count = 0;
152        let mut i = 0;
153        while i < self.limbs.len() {
154            let l = self.limbs[i];
155            let z = l.trailing_zeros();
156            count += z;
157            if z != Limb::BITS {
158                break;
159            }
160            i += 1;
161        }
162
163        count
164    }
165
166    /// Calculate the number of trailing ones in the binary representation of this number.
167    #[inline(always)]
168    #[must_use]
169    pub const fn trailing_ones(&self) -> u32 {
170        let mut count = 0;
171        let mut i = 0;
172        let mut nonmax_limb_not_encountered = Choice::TRUE;
173        while i < self.limbs.len() {
174            let l = self.limbs[i];
175            let z = l.trailing_ones();
176            count += nonmax_limb_not_encountered.select_u32(0, z);
177            nonmax_limb_not_encountered =
178                nonmax_limb_not_encountered.and(word::choice_from_eq(l.0, Limb::MAX.0));
179            i += 1;
180        }
181
182        count
183    }
184
185    /// Calculate the number of trailing ones in the binary representation of this number, in
186    /// variable-time with respect to `self`.
187    #[inline(always)]
188    #[must_use]
189    pub const fn trailing_ones_vartime(&self) -> u32 {
190        let mut count = 0;
191        let mut i = 0;
192        while i < self.limbs.len() {
193            let l = self.limbs[i];
194            let z = l.trailing_ones();
195            count += z;
196            if z != Limb::BITS {
197                break;
198            }
199            i += 1;
200        }
201
202        count
203    }
204
205    /// Clear all bits at or above a given bit position.
206    #[allow(clippy::cast_possible_truncation)]
207    pub const fn restrict_bits(&mut self, len: u32) {
208        let limb = len >> Limb::LOG2_BITS;
209        let limb_mask = Limb((1 << (len & (Limb::BITS - 1))) - 1);
210        let mut i = 0;
211        let mut clear = Choice::FALSE;
212        while i < self.nlimbs() {
213            let apply = Choice::from_u32_eq(i as u32, limb);
214            self.limbs[i] = self.limbs[i].bitand(Limb::select(
215                Limb(word::choice_to_mask(clear.not())),
216                limb_mask,
217                apply,
218            ));
219            clear = clear.or(apply);
220            i += 1;
221        }
222    }
223}
224
225impl BitOps for UintRef {
226    #[inline(always)]
227    fn bits_precision(&self) -> u32 {
228        self.bits_precision()
229    }
230
231    #[inline(always)]
232    fn bytes_precision(&self) -> usize {
233        self.limbs.len()
234    }
235
236    fn leading_zeros(&self) -> u32 {
237        self.leading_zeros()
238    }
239
240    fn bit(&self, index: u32) -> Choice {
241        self.bit(index)
242    }
243
244    fn set_bit(&mut self, index: u32, bit_value: Choice) {
245        self.set_bit(index, bit_value);
246    }
247
248    fn trailing_zeros(&self) -> u32 {
249        self.trailing_zeros()
250    }
251
252    fn trailing_ones(&self) -> u32 {
253        self.trailing_ones()
254    }
255
256    fn bit_vartime(&self, index: u32) -> bool {
257        self.bit_vartime(index)
258    }
259
260    fn bits_vartime(&self) -> u32 {
261        self.bits_vartime()
262    }
263
264    fn set_bit_vartime(&mut self, index: u32, bit_value: bool) {
265        self.set_bit_vartime(index, bit_value);
266    }
267
268    fn trailing_zeros_vartime(&self) -> u32 {
269        self.trailing_zeros_vartime()
270    }
271
272    fn trailing_ones_vartime(&self) -> u32 {
273        self.trailing_ones_vartime()
274    }
275}