1use crate::Choice;
12use core::hint::black_box;
13
14macro_rules! bitle {
18 ($x:expr, $y:expr, $bits:expr) => {
19 (((!$x) | $y) & (($x ^ $y) | !($y.wrapping_sub($x)))) >> ($bits - 1)
20 };
21}
22
23macro_rules! bitborrow {
27 ($x:expr, $y:expr, $result:expr, $bits:expr) => {
28 (((!$x) & $y) | (((!$x) | $y) & $result)) >> ($bits - 1)
29 };
30}
31
32macro_rules! bitlt {
36 ($x:expr, $y:expr, $bits:expr) => {
37 $crate::primitives::bitborrow!($x, $y, ($x).wrapping_sub($y), $bits)
38 };
39}
40
41macro_rules! bitnz {
43 ($value:expr, $bits:expr) => {
44 ($value | $value.wrapping_neg()) >> ($bits - 1)
45 };
46}
47
48pub(crate) use {bitborrow, bitle, bitlt, bitnz};
49
50macro_rules! comparison_primitives {
54 ($ty:ty, $le:ident, $lt:ident, $nz:ident, $from_lsb:ident) => {
55 #[inline(always)]
56 pub(crate) const fn $le(lhs: $ty, rhs: $ty) -> Choice {
57 let bit = bitle!(lhs, rhs, <$ty>::BITS);
58 #[cfg(all(target_arch = "arm", not(target_has_atomic = "ptr")))]
59 let bit = black_box(bit);
60 Choice::$from_lsb(bit)
61 }
62
63 #[cfg_attr(
64 all(target_arch = "arm", not(target_has_atomic = "ptr")),
65 inline(never)
66 )]
67 #[cfg_attr(
68 not(all(target_arch = "arm", not(target_has_atomic = "ptr"))),
69 inline(always)
70 )]
71 pub(crate) const fn $lt(lhs: $ty, rhs: $ty) -> Choice {
72 let bit = $crate::primitives::bitlt!(lhs, rhs, <$ty>::BITS);
73 #[cfg(all(target_arch = "arm", not(target_has_atomic = "ptr")))]
74 let bit = black_box(bit);
75 Choice::$from_lsb(bit)
76 }
77
78 #[inline(always)]
79 pub(crate) const fn $nz(value: $ty) -> Choice {
80 let bit = bitnz!(value, <$ty>::BITS);
81 #[cfg(all(target_arch = "arm", not(target_has_atomic = "ptr")))]
82 let bit = black_box(bit);
83 Choice::$from_lsb(bit)
84 }
85 };
86}
87
88comparison_primitives!(u32, le_u32, lt_u32, nz_u32, from_u32_lsb);
89comparison_primitives!(u64, le_u64, lt_u64, nz_u64, from_u64_lsb);
90
91macro_rules! borrowing_sub_primitives {
92 ($ty:ty, $sub:ident, $sub_mask:ident, $sub_inner:ident, $from_lsb:ident) => {
93 #[inline]
98 #[must_use]
99 pub const fn $sub(lhs: $ty, rhs: $ty, borrow: Choice) -> ($ty, Choice) {
100 let (difference, borrow) = $sub_inner(lhs, rhs, borrow.to_u8() as $ty);
101 (difference, Choice::$from_lsb(borrow))
102 }
103
104 #[inline]
113 #[must_use]
114 pub const fn $sub_mask(lhs: $ty, rhs: $ty, borrow: $ty) -> ($ty, $ty) {
115 let (difference, borrow) = $sub_inner(lhs, rhs, borrow >> (<$ty>::BITS - 1));
116 (difference, borrow.wrapping_neg())
117 }
118
119 #[inline(always)]
121 const fn $sub_inner(lhs: $ty, rhs: $ty, borrow: $ty) -> ($ty, $ty) {
122 let difference = lhs.wrapping_sub(rhs).wrapping_sub(borrow);
123 let borrow = black_box(bitborrow!(lhs, rhs, difference, <$ty>::BITS));
128 (difference, borrow)
129 }
130 };
131}
132
133borrowing_sub_primitives!(
134 u32,
135 borrowing_sub_u32,
136 borrowing_sub_mask_u32,
137 borrowing_sub_inner_u32,
138 from_u32_lsb
139);
140borrowing_sub_primitives!(
141 u64,
142 borrowing_sub_u64,
143 borrowing_sub_mask_u64,
144 borrowing_sub_inner_u64,
145 from_u64_lsb
146);
147
148#[inline]
150#[must_use]
151pub const fn leading_zeros_u32(value: u32) -> u32 {
152 #[cfg(any(
153 target_arch = "aarch64",
154 target_arch = "x86",
155 target_arch = "x86_64",
156 target_feature = "thumb2",
157 target_feature = "zbb"
158 ))]
159 {
160 value.leading_zeros()
161 }
162 #[cfg(not(any(
163 target_arch = "aarch64",
164 target_arch = "x86",
165 target_arch = "x86_64",
166 target_feature = "thumb2",
167 target_feature = "zbb"
168 )))]
169 {
170 leading_zeros_u32_soft(value)
171 }
172}
173
174#[inline]
176#[must_use]
177pub const fn trailing_zeros_u32(value: u32) -> u32 {
178 #[cfg(any(
179 target_arch = "aarch64",
180 target_arch = "x86",
181 target_arch = "x86_64",
182 target_feature = "thumb2",
183 target_feature = "zbb"
184 ))]
185 {
186 value.trailing_zeros()
187 }
188 #[cfg(not(any(
189 target_arch = "aarch64",
190 target_arch = "x86",
191 target_arch = "x86_64",
192 target_feature = "thumb2",
193 target_feature = "zbb"
194 )))]
195 {
196 leading_zeros_u32_soft(value.reverse_bits())
198 }
199}
200
201#[inline]
203#[must_use]
204#[allow(clippy::cast_possible_truncation)]
205pub const fn leading_zeros_u64(value: u64) -> u32 {
206 #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
207 {
208 value.leading_zeros()
209 }
210 #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
211 {
212 let hi = (value >> 32) as u32;
213 let lo = value as u32;
214 let mask = Choice::from_u32_nz(hi).not().to_u32_mask();
215 leading_zeros_u32(hi).wrapping_add(mask & leading_zeros_u32(lo))
216 }
217}
218
219#[inline]
221#[must_use]
222#[allow(clippy::cast_possible_truncation)]
223pub const fn trailing_zeros_u64(value: u64) -> u32 {
224 #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
225 {
226 value.trailing_zeros()
227 }
228 #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
229 {
230 let hi = (value >> 32) as u32;
231 let lo = value as u32;
232 let mask = Choice::from_u32_nz(lo).not().to_u32_mask();
233 trailing_zeros_u32(lo).wrapping_add(mask & trailing_zeros_u32(hi))
234 }
235}
236
237#[cfg(any(
239 test,
240 not(any(
241 target_arch = "aarch64",
242 target_arch = "x86",
243 target_arch = "x86_64",
244 target_feature = "thumb2",
245 target_feature = "zbb"
246 ))
247))]
248#[inline]
249#[allow(clippy::arithmetic_side_effects, reason = "shift is between 1 and 16")]
250const fn leading_zeros_u32_soft(mut value: u32) -> u32 {
251 let mut count = 0u32;
252 let mut shift = 16;
253 while shift != 0 {
254 let mask = Choice::from_u32_nz(value >> (u32::BITS - shift))
255 .not()
256 .to_u32_mask();
257 value ^= mask & (value ^ (value << shift));
258 count = count.wrapping_add(mask & shift);
259 shift >>= 1;
260 }
261 count.wrapping_add(Choice::from_u32_nz(value).not().to_u32_mask() & 1)
262}
263
264#[inline]
266#[must_use]
267pub const fn overflowing_add_u64(lhs: u64, rhs: u64) -> (u64, Choice) {
268 let sum = lhs.wrapping_add(rhs);
269 let carry = ((lhs & rhs) | ((lhs | rhs) & !sum)) >> (u64::BITS - 1);
270 (sum, Choice::from_u64_lsb(carry))
271}
272
273#[inline]
275#[must_use]
276pub const fn overflowing_add_u128(lhs: u128, rhs: u128) -> (u128, Choice) {
277 #[cfg(target_arch = "riscv32")]
278 {
279 overflowing_add_u128_halves(lhs, rhs)
280 }
281 #[cfg(not(target_arch = "riscv32"))]
282 {
283 let sum = lhs.wrapping_add(rhs);
284 let carry = ((lhs & rhs) | ((lhs | rhs) & !sum)) >> (u128::BITS - 1);
285 (sum, Choice::from_u128_lsb(carry))
286 }
287}
288
289#[cfg(any(target_arch = "riscv32", test))]
292#[inline]
293#[allow(clippy::cast_possible_truncation)]
294const fn overflowing_add_u128_halves(lhs: u128, rhs: u128) -> (u128, Choice) {
295 let (lo, lo_carry) = overflowing_add_u64(lhs as u64, rhs as u64);
296 let (hi, hi_carry) = overflowing_add_u64((lhs >> 64) as u64, (rhs >> 64) as u64);
297 let (hi, carry) = overflowing_add_u64(hi, lo_carry.to_u8() as u64);
298 (((hi as u128) << 64) | lo as u128, hi_carry.or(carry))
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 const _: () = {
307 assert!(borrowing_sub_u32(0, 0, Choice::TRUE).0 == u32::MAX);
308 assert!(borrowing_sub_u32(0, 0, Choice::TRUE).1.to_bool());
309 assert!(borrowing_sub_u64(0, 0, Choice::TRUE).0 == u64::MAX);
310 assert!(borrowing_sub_u64(0, 0, Choice::TRUE).1.to_bool());
311 assert!(!borrowing_sub_u32(1, 0, Choice::TRUE).1.to_bool());
312 assert!(!borrowing_sub_u64(1, 0, Choice::TRUE).1.to_bool());
313 assert!(borrowing_sub_mask_u32(0, 0, u32::MAX).0 == u32::MAX);
314 assert!(borrowing_sub_mask_u32(0, 0, u32::MAX).1 == u32::MAX);
315 assert!(borrowing_sub_mask_u64(0, 0, u64::MAX).0 == u64::MAX);
316 assert!(borrowing_sub_mask_u64(0, 0, u64::MAX).1 == u64::MAX);
317 assert!(borrowing_sub_mask_u32(1, 0, u32::MAX).1 == 0);
318 assert!(borrowing_sub_mask_u64(1, 0, u64::MAX).1 == 0);
319 assert!(leading_zeros_u32(0) == 32);
320 assert!(trailing_zeros_u32(0) == 32);
321 assert!(leading_zeros_u64(0) == 64);
322 assert!(trailing_zeros_u64(0) == 64);
323 assert!(overflowing_add_u64(u64::MAX, 1).1.to_bool());
324 assert!(overflowing_add_u128(u128::MAX, 1).1.to_bool());
325 };
326
327 #[test]
328 fn zero_counts_u32() {
329 for bit in 0..u32::BITS {
330 for value in [0, 1 << bit, !(1 << bit), u32::MAX >> bit] {
331 assert_eq!(leading_zeros_u32(value), value.leading_zeros());
332 assert_eq!(trailing_zeros_u32(value), value.trailing_zeros());
333 assert_eq!(leading_zeros_u32_soft(value), value.leading_zeros());
335 assert_eq!(
336 leading_zeros_u32_soft(value.reverse_bits()),
337 value.trailing_zeros()
338 );
339 }
340 }
341 }
342
343 #[test]
344 fn zero_counts_u64() {
345 for bit in 0..u64::BITS {
346 for value in [0, 1 << bit, !(1 << bit), u64::MAX >> bit] {
347 assert_eq!(leading_zeros_u64(value), value.leading_zeros());
348 assert_eq!(trailing_zeros_u64(value), value.trailing_zeros());
349 }
350 }
351 }
352
353 macro_rules! subtraction_tests {
354 ($test:ident, $ty:ty, $sub:ident, $sub_mask:ident) => {
355 #[test]
356 fn $test() {
357 let half = 1 << (<$ty>::BITS >> 1);
358 let high = 1 << (<$ty>::BITS - 1);
359 let values = [0, 1, half - 1, half, half + 1, high - 1, high, <$ty>::MAX];
360 for lhs in values {
361 for rhs in values {
362 for mask in [0, 1, high - 1, high, <$ty>::MAX - 1, <$ty>::MAX] {
364 let incoming = mask >> (<$ty>::BITS - 1) != 0;
365 let (expected, first_borrow) = lhs.overflowing_sub(rhs);
366 let (expected, second_borrow) =
367 expected.overflowing_sub(<$ty>::from(incoming));
368 let (difference, borrow) =
369 $sub(lhs, rhs, Choice::from_u8_lsb(u8::from(incoming)));
370 assert_eq!(
371 (difference, borrow.to_bool()),
372 (expected, first_borrow | second_borrow)
373 );
374 assert_eq!(
375 $sub_mask(lhs, rhs, mask),
376 (
377 expected,
378 <$ty>::from(first_borrow | second_borrow).wrapping_neg()
379 )
380 );
381 }
382 }
383 }
384 }
385 };
386 }
387
388 subtraction_tests!(sub_u32, u32, borrowing_sub_u32, borrowing_sub_mask_u32);
389 subtraction_tests!(sub_u64, u64, borrowing_sub_u64, borrowing_sub_mask_u64);
390
391 #[test]
392 fn sub_mask_propagates_borrow() {
393 let (lo, borrow) = borrowing_sub_mask_u32(0, 1, 0);
394 let (mid, borrow) = borrowing_sub_mask_u32(0, 0, borrow);
395 assert_eq!((lo, mid, borrow), (u32::MAX, u32::MAX, u32::MAX));
396 assert_eq!(borrowing_sub_mask_u32(1, 0, borrow), (0, 0));
397
398 let (lo, borrow) = borrowing_sub_mask_u64(0, 1, 0);
399 let (mid, borrow) = borrowing_sub_mask_u64(0, 0, borrow);
400 assert_eq!((lo, mid, borrow), (u64::MAX, u64::MAX, u64::MAX));
401 assert_eq!(borrowing_sub_mask_u64(1, 0, borrow), (0, 0));
402 }
403
404 macro_rules! addition_tests {
405 ($test:ident, $ty:ty, $add:ident) => {
406 #[test]
407 fn $test() {
408 let half = 1 << (<$ty>::BITS >> 1);
410 let high = 1 << (<$ty>::BITS - 1);
411 let values = [0, 1, half - 1, half, half + 1, high - 1, high, <$ty>::MAX];
412 for lhs in values {
413 for rhs in values {
414 let (sum, carry) = $add(lhs, rhs);
415 assert_eq!((sum, carry.to_bool()), lhs.overflowing_add(rhs));
416 }
417 }
418 }
419 };
420 }
421
422 addition_tests!(add_u64, u64, overflowing_add_u64);
423 addition_tests!(add_u128, u128, overflowing_add_u128);
424 addition_tests!(add_u128_halves, u128, overflowing_add_u128_halves);
425}