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 = 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, $from_lsb:ident) => {
93 #[inline]
98 #[must_use]
99 pub const fn $sub(lhs: $ty, rhs: $ty, borrow: Choice) -> ($ty, Choice) {
100 let difference = lhs.wrapping_sub(rhs).wrapping_sub(borrow.to_u8() as $ty);
101 let borrow = black_box(bitborrow!(lhs, rhs, difference, <$ty>::BITS));
106 (difference, Choice::$from_lsb(borrow))
107 }
108 };
109}
110
111borrowing_sub_primitives!(u32, borrowing_sub_u32, from_u32_lsb);
112borrowing_sub_primitives!(u64, borrowing_sub_u64, from_u64_lsb);
113
114#[inline]
116#[must_use]
117pub const fn leading_zeros_u32(value: u32) -> u32 {
118 #[cfg(any(
119 target_arch = "aarch64",
120 target_arch = "x86",
121 target_arch = "x86_64",
122 target_feature = "thumb2",
123 target_feature = "zbb"
124 ))]
125 {
126 value.leading_zeros()
127 }
128 #[cfg(not(any(
129 target_arch = "aarch64",
130 target_arch = "x86",
131 target_arch = "x86_64",
132 target_feature = "thumb2",
133 target_feature = "zbb"
134 )))]
135 {
136 leading_zeros_u32_soft(value)
137 }
138}
139
140#[inline]
142#[must_use]
143pub const fn trailing_zeros_u32(value: u32) -> u32 {
144 #[cfg(any(
145 target_arch = "aarch64",
146 target_arch = "x86",
147 target_arch = "x86_64",
148 target_feature = "thumb2",
149 target_feature = "zbb"
150 ))]
151 {
152 value.trailing_zeros()
153 }
154 #[cfg(not(any(
155 target_arch = "aarch64",
156 target_arch = "x86",
157 target_arch = "x86_64",
158 target_feature = "thumb2",
159 target_feature = "zbb"
160 )))]
161 {
162 leading_zeros_u32_soft(value.reverse_bits())
164 }
165}
166
167#[inline]
169#[must_use]
170#[allow(clippy::cast_possible_truncation)]
171pub const fn leading_zeros_u64(value: u64) -> u32 {
172 #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
173 {
174 value.leading_zeros()
175 }
176 #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
177 {
178 let hi = (value >> 32) as u32;
179 let lo = value as u32;
180 let mask = Choice::from_u32_nz(hi).not().to_u32_mask();
181 leading_zeros_u32(hi).wrapping_add(mask & leading_zeros_u32(lo))
182 }
183}
184
185#[inline]
187#[must_use]
188#[allow(clippy::cast_possible_truncation)]
189pub const fn trailing_zeros_u64(value: u64) -> u32 {
190 #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
191 {
192 value.trailing_zeros()
193 }
194 #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
195 {
196 let hi = (value >> 32) as u32;
197 let lo = value as u32;
198 let mask = Choice::from_u32_nz(lo).not().to_u32_mask();
199 trailing_zeros_u32(lo).wrapping_add(mask & trailing_zeros_u32(hi))
200 }
201}
202
203#[cfg(any(
205 test,
206 not(any(
207 target_arch = "aarch64",
208 target_arch = "x86",
209 target_arch = "x86_64",
210 target_feature = "thumb2",
211 target_feature = "zbb"
212 ))
213))]
214#[inline]
215#[allow(clippy::arithmetic_side_effects, reason = "shift is between 1 and 16")]
216const fn leading_zeros_u32_soft(mut value: u32) -> u32 {
217 let mut count = 0u32;
218 let mut shift = 16;
219 while shift != 0 {
220 let mask = Choice::from_u32_nz(value >> (u32::BITS - shift))
221 .not()
222 .to_u32_mask();
223 value ^= mask & (value ^ (value << shift));
224 count = count.wrapping_add(mask & shift);
225 shift >>= 1;
226 }
227 count.wrapping_add(Choice::from_u32_nz(value).not().to_u32_mask() & 1)
228}
229
230#[inline]
232#[must_use]
233pub const fn overflowing_add_u64(lhs: u64, rhs: u64) -> (u64, Choice) {
234 let sum = lhs.wrapping_add(rhs);
235 let carry = ((lhs & rhs) | ((lhs | rhs) & !sum)) >> (u64::BITS - 1);
236 (sum, Choice::from_u64_lsb(carry))
237}
238
239#[inline]
241#[must_use]
242pub const fn overflowing_add_u128(lhs: u128, rhs: u128) -> (u128, Choice) {
243 #[cfg(target_arch = "riscv32")]
244 {
245 overflowing_add_u128_halves(lhs, rhs)
246 }
247 #[cfg(not(target_arch = "riscv32"))]
248 {
249 let sum = lhs.wrapping_add(rhs);
250 let carry = ((lhs & rhs) | ((lhs | rhs) & !sum)) >> (u128::BITS - 1);
251 (sum, Choice::from_u128_lsb(carry))
252 }
253}
254
255#[cfg(any(target_arch = "riscv32", test))]
258#[inline]
259#[allow(clippy::cast_possible_truncation)]
260const fn overflowing_add_u128_halves(lhs: u128, rhs: u128) -> (u128, Choice) {
261 let (lo, lo_carry) = overflowing_add_u64(lhs as u64, rhs as u64);
262 let (hi, hi_carry) = overflowing_add_u64((lhs >> 64) as u64, (rhs >> 64) as u64);
263 let (hi, carry) = overflowing_add_u64(hi, lo_carry.to_u8() as u64);
264 (((hi as u128) << 64) | lo as u128, hi_carry.or(carry))
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 const _: () = {
273 assert!(borrowing_sub_u32(0, 0, Choice::TRUE).0 == u32::MAX);
274 assert!(borrowing_sub_u32(0, 0, Choice::TRUE).1.to_bool());
275 assert!(borrowing_sub_u64(0, 0, Choice::TRUE).0 == u64::MAX);
276 assert!(borrowing_sub_u64(0, 0, Choice::TRUE).1.to_bool());
277 assert!(!borrowing_sub_u32(1, 0, Choice::TRUE).1.to_bool());
278 assert!(!borrowing_sub_u64(1, 0, Choice::TRUE).1.to_bool());
279 assert!(leading_zeros_u32(0) == 32);
280 assert!(trailing_zeros_u32(0) == 32);
281 assert!(leading_zeros_u64(0) == 64);
282 assert!(trailing_zeros_u64(0) == 64);
283 assert!(overflowing_add_u64(u64::MAX, 1).1.to_bool());
284 assert!(overflowing_add_u128(u128::MAX, 1).1.to_bool());
285 };
286
287 #[test]
288 fn zero_counts_u32() {
289 for bit in 0..u32::BITS {
290 for value in [0, 1 << bit, !(1 << bit), u32::MAX >> bit] {
291 assert_eq!(leading_zeros_u32(value), value.leading_zeros());
292 assert_eq!(trailing_zeros_u32(value), value.trailing_zeros());
293 assert_eq!(leading_zeros_u32_soft(value), value.leading_zeros());
295 assert_eq!(
296 leading_zeros_u32_soft(value.reverse_bits()),
297 value.trailing_zeros()
298 );
299 }
300 }
301 }
302
303 #[test]
304 fn zero_counts_u64() {
305 for bit in 0..u64::BITS {
306 for value in [0, 1 << bit, !(1 << bit), u64::MAX >> bit] {
307 assert_eq!(leading_zeros_u64(value), value.leading_zeros());
308 assert_eq!(trailing_zeros_u64(value), value.trailing_zeros());
309 }
310 }
311 }
312
313 macro_rules! subtraction_tests {
314 ($test:ident, $ty:ty, $sub:ident) => {
315 #[test]
316 fn $test() {
317 let half = 1 << (<$ty>::BITS >> 1);
318 let high = 1 << (<$ty>::BITS - 1);
319 let values = [0, 1, half - 1, half, half + 1, high - 1, high, <$ty>::MAX];
320 for lhs in values {
321 for rhs in values {
322 for incoming in [false, true] {
323 let (expected, first_borrow) = lhs.overflowing_sub(rhs);
324 let (expected, second_borrow) =
325 expected.overflowing_sub(<$ty>::from(incoming));
326 let (difference, borrow) =
327 $sub(lhs, rhs, Choice::from_u8_lsb(u8::from(incoming)));
328 assert_eq!(
329 (difference, borrow.to_bool()),
330 (expected, first_borrow | second_borrow)
331 );
332 }
333 }
334 }
335 }
336 };
337 }
338
339 subtraction_tests!(sub_u32, u32, borrowing_sub_u32);
340 subtraction_tests!(sub_u64, u64, borrowing_sub_u64);
341
342 macro_rules! addition_tests {
343 ($test:ident, $ty:ty, $add:ident) => {
344 #[test]
345 fn $test() {
346 let half = 1 << (<$ty>::BITS >> 1);
348 let high = 1 << (<$ty>::BITS - 1);
349 let values = [0, 1, half - 1, half, half + 1, high - 1, high, <$ty>::MAX];
350 for lhs in values {
351 for rhs in values {
352 let (sum, carry) = $add(lhs, rhs);
353 assert_eq!((sum, carry.to_bool()), lhs.overflowing_add(rhs));
354 }
355 }
356 }
357 };
358 }
359
360 addition_tests!(add_u64, u64, overflowing_add_u64);
361 addition_tests!(add_u128, u128, overflowing_add_u128);
362 addition_tests!(add_u128_halves, u128, overflowing_add_u128_halves);
363}