From e001d505a7cf2306caf002febad107d299f4a22f Mon Sep 17 00:00:00 2001 From: Nics Date: Wed, 21 Jan 2026 00:18:55 +0100 Subject: [PATCH 1/2] cmov: (NonZero){u,i}size improvements Implements `Cmov` and `CmovEq` for `NonZeroUsize` and `NonZeroIsize`. Implements slice for size integer types (and their `NonZero*` variants. --- cmov/src/lib.rs | 8 +++++--- cmov/src/slice.rs | 14 ++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/cmov/src/lib.rs b/cmov/src/lib.rs index a56a7be7..d67e54d0 100644 --- a/cmov/src/lib.rs +++ b/cmov/src/lib.rs @@ -41,8 +41,8 @@ mod slice; use core::{ cmp, num::{ - NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, - NonZeroU32, NonZeroU64, NonZeroU128, + NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, }, }; @@ -316,11 +316,13 @@ impl_cmov_traits_for_nonzero_integers!( NonZeroI32, NonZeroI64, NonZeroI128, + NonZeroIsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, - NonZeroU128 + NonZeroU128, + NonZeroUsize ); impl Cmov for cmp::Ordering { diff --git a/cmov/src/slice.rs b/cmov/src/slice.rs index dd46f277..3a214ac4 100644 --- a/cmov/src/slice.rs +++ b/cmov/src/slice.rs @@ -4,8 +4,8 @@ use crate::{Cmov, CmovEq, Condition}; use core::{ cmp, num::{ - NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, - NonZeroU32, NonZeroU64, NonZeroU128, + NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, }, ops::{BitOrAssign, Shl}, ptr, slice, @@ -134,7 +134,7 @@ macro_rules! impl_cmov_with_loop { } // These types are large enough we don't need to use anything more complex than a simple loop -impl_cmov_with_loop!(u32, u64, u128); +impl_cmov_with_loop!(u32, u64, u128, usize); /// Ensure the two provided types have the same size and alignment. macro_rules! assert_size_and_alignment_eq { @@ -183,16 +183,19 @@ impl_cmov_with_cast!( i32 => u32, i64 => u64, i128 => u128, + isize => usize, NonZeroI8 => i8, NonZeroI16 => i16, NonZeroI32 => i32, NonZeroI64 => i64, NonZeroI128 => i128, + NonZeroIsize => isize, NonZeroU8 => u8, NonZeroU16 => u16, NonZeroU32 => u32, NonZeroU64 => u64, NonZeroU128 => u128, + NonZeroUsize => usize, cmp::Ordering => i8 // #[repr(i8)] ); @@ -249,7 +252,7 @@ macro_rules! impl_cmoveq_with_loop { } // TODO(tarcieri): investigate word-coalescing impls -impl_cmoveq_with_loop!(u16, u32, u64, u128); +impl_cmoveq_with_loop!(u16, u32, u64, u128, usize); /// Implement [`CmovEq`] traits by casting to a different type that impls the traits. macro_rules! impl_cmoveq_with_cast { @@ -280,16 +283,19 @@ impl_cmoveq_with_cast!( i32 => u32, i64 => u64, i128 => u128, + isize => usize, NonZeroI8 => i8, NonZeroI16 => i16, NonZeroI32 => i32, NonZeroI64 => i64, NonZeroI128 => i128, + NonZeroIsize => isize, NonZeroU8 => u8, NonZeroU16 => u16, NonZeroU32 => u32, NonZeroU64 => u64, NonZeroU128 => u128, + NonZeroUsize => usize, cmp::Ordering => i8 // #[repr(i8)] ); From 499f166bd7a49a789c1716ed85783993245363f3 Mon Sep 17 00:00:00 2001 From: Nics Date: Wed, 21 Jan 2026 00:27:40 +0100 Subject: [PATCH 2/2] ctutils: Add size integers implementations - Add `CtGt` and `CtLt` for usize - Add `CtAssign` and `CtSelect` for `NonZero{U,I}size` - Add `CtNeg` for `NonZero{U,I}size` and `{u,i}size` - Add some tests --- ctutils/src/traits/ct_assign.rs | 10 ++++---- ctutils/src/traits/ct_eq.rs | 39 ++++++++++++++++++++++-------- ctutils/src/traits/ct_gt.rs | 41 ++++++++++++++++++++++++-------- ctutils/src/traits/ct_lt.rs | 36 +++++++++++++++++++++++++--- ctutils/src/traits/ct_neg.rs | 27 ++++++++++++++------- ctutils/src/traits/ct_select.rs | 42 +++++++++++++++++++++++++-------- 6 files changed, 149 insertions(+), 46 deletions(-) diff --git a/ctutils/src/traits/ct_assign.rs b/ctutils/src/traits/ct_assign.rs index 3f558621..58699224 100644 --- a/ctutils/src/traits/ct_assign.rs +++ b/ctutils/src/traits/ct_assign.rs @@ -3,8 +3,8 @@ use cmov::Cmov; use core::{ cmp, num::{ - NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, - NonZeroU32, NonZeroU64, NonZeroU128, + NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, }, }; @@ -19,8 +19,8 @@ use core::num::NonZero; /// This crate provides built-in implementations for the following types: /// - [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], [`isize`] /// - [`u8`], [`u16`], [`u32`], [`u64`], [`u128`], [`usize`] -/// - [`NonZeroI8`], [`NonZeroI16`], [`NonZeroI32`], [`NonZeroI64`], [`NonZeroI128`] -/// - [`NonZeroU8`], [`NonZeroU16`], [`NonZeroU32`], [`NonZeroU64`], [`NonZeroU128`] +/// - [`NonZeroI8`], [`NonZeroI16`], [`NonZeroI32`], [`NonZeroI64`], [`NonZeroI128`], [`NonZeroI128`] +/// - [`NonZeroU8`], [`NonZeroU16`], [`NonZeroU32`], [`NonZeroU64`], [`NonZeroU128`],, [`NonZeroUsize`] /// - [`cmp::Ordering`] /// - [`Choice`] /// - `[T]` and `[T; N]` where `T` impls [`CtAssignSlice`], which the previously mentioned @@ -106,11 +106,13 @@ impl_ct_assign_slice_with_cmov!( NonZeroI32, NonZeroI64, NonZeroI128, + NonZeroIsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, + NonZeroUsize, cmp::Ordering ); diff --git a/ctutils/src/traits/ct_eq.rs b/ctutils/src/traits/ct_eq.rs index e786c1c5..f30b8f06 100644 --- a/ctutils/src/traits/ct_eq.rs +++ b/ctutils/src/traits/ct_eq.rs @@ -259,24 +259,43 @@ mod tests { }; } - macro_rules! ct_eq_test { + macro_rules! ct_eq_test_unsigned { ($ty:ty, $name:ident) => { #[test] fn $name() { - let a: $ty = 42; - let b: $ty = 42; - let c: $ty = 1; + let a = <$ty>::MAX; + let b = <$ty>::MAX; + let c = <$ty>::MIN; truth_table!(a, b, c); } }; } - ct_eq_test!(u8, u8_ct_eq); - ct_eq_test!(u16, u16_ct_eq); - ct_eq_test!(u32, u32_ct_eq); - ct_eq_test!(u64, u64_ct_eq); - ct_eq_test!(u128, u128_ct_eq); - ct_eq_test!(usize, usize_ct_eq); + macro_rules! ct_eq_test_signed { + ($ty:ty, $name:ident) => { + #[test] + fn $name() { + let a = <$ty>::MAX; + let b = <$ty>::MAX; + let c = <$ty>::MIN; + truth_table!(a, b, c); + } + }; + } + + ct_eq_test_unsigned!(u8, u8_ct_eq); + ct_eq_test_unsigned!(u16, u16_ct_eq); + ct_eq_test_unsigned!(u32, u32_ct_eq); + ct_eq_test_unsigned!(u64, u64_ct_eq); + ct_eq_test_unsigned!(u128, u128_ct_eq); + ct_eq_test_unsigned!(usize, usize_ct_eq); + + ct_eq_test_signed!(i8, i8_ct_eq); + ct_eq_test_signed!(i16, i16_ct_eq); + ct_eq_test_signed!(i32, i32_ct_eq); + ct_eq_test_signed!(i64, i64_ct_eq); + ct_eq_test_signed!(i128, i128_ct_eq); + ct_eq_test_signed!(isize, isize_ct_eq); #[test] fn array_ct_eq() { diff --git a/ctutils/src/traits/ct_gt.rs b/ctutils/src/traits/ct_gt.rs index fcca6033..dd8b67d0 100644 --- a/ctutils/src/traits/ct_gt.rs +++ b/ctutils/src/traits/ct_gt.rs @@ -1,7 +1,7 @@ use crate::Choice; use core::{ cmp, - num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128}, + num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}, }; /// Constant time greater than. @@ -26,7 +26,7 @@ macro_rules! impl_unsigned_ct_gt { }; } -impl_unsigned_ct_gt!(u8, u16, u32, u64, u128); +impl_unsigned_ct_gt!(u8, u16, u32, u64, u128, usize); /// Impl `CtGt` for `NonZero` by calling `NonZero::get`. macro_rules! impl_ct_gt_for_nonzero_integer { @@ -42,7 +42,14 @@ macro_rules! impl_ct_gt_for_nonzero_integer { }; } -impl_ct_gt_for_nonzero_integer!(NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128); +impl_ct_gt_for_nonzero_integer!( + NonZeroU8, + NonZeroU16, + NonZeroU32, + NonZeroU64, + NonZeroU128, + NonZeroUsize +); impl CtGt for cmp::Ordering { #[inline] @@ -59,15 +66,29 @@ mod tests { use super::CtGt; use core::cmp::Ordering; - #[test] - fn ct_gt() { - let a = 42u64; - let b = 43u64; - assert!(!a.ct_gt(&a).to_bool()); - assert!(!a.ct_gt(&b).to_bool()); - assert!(b.ct_gt(&a).to_bool()); + /// Test `CtGt` + macro_rules! ct_gt_tests { + ( $($int:ident),+ ) => { + $( + mod $int { + use super::CtGt; + + #[test] + fn ct_gt() { + let a = <$int>::MIN; + let b = <$int>::MAX; + assert!(!a.ct_gt(&a).to_bool()); + assert!(!a.ct_gt(&b).to_bool()); + assert!(b.ct_gt(&a).to_bool()); + } + + } + )+ + }; } + ct_gt_tests!(u8, u16, u32, u64, u128, usize); + #[test] fn ordering() { assert!(!Ordering::Equal.ct_gt(&Ordering::Equal).to_bool()); diff --git a/ctutils/src/traits/ct_lt.rs b/ctutils/src/traits/ct_lt.rs index 75ea7d4f..f89d71ea 100644 --- a/ctutils/src/traits/ct_lt.rs +++ b/ctutils/src/traits/ct_lt.rs @@ -1,7 +1,7 @@ use crate::Choice; use core::{ cmp, - num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128}, + num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}, }; /// Constant time less than. @@ -26,7 +26,7 @@ macro_rules! impl_unsigned_ct_lt { }; } -impl_unsigned_ct_lt!(u8, u16, u32, u64, u128); +impl_unsigned_ct_lt!(u8, u16, u32, u64, u128, usize); /// Impl `CtLt` for `NonZero` by calling `NonZero::get`. macro_rules! impl_ct_lt_for_nonzero_integer { @@ -42,7 +42,14 @@ macro_rules! impl_ct_lt_for_nonzero_integer { }; } -impl_ct_lt_for_nonzero_integer!(NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128); +impl_ct_lt_for_nonzero_integer!( + NonZeroU8, + NonZeroU16, + NonZeroU32, + NonZeroU64, + NonZeroU128, + NonZeroUsize +); impl CtLt for cmp::Ordering { #[inline] @@ -68,6 +75,29 @@ mod tests { assert!(!b.ct_lt(&a).to_bool()); } + /// Test `CtLt` + macro_rules! ct_lt_tests { + ( $($int:ident),+ ) => { + $( + mod $int { + use super::CtLt; + + #[test] + fn ct_gt() { + let a = <$int>::MIN; + let b = <$int>::MAX; + assert!(!a.ct_lt(&a).to_bool()); + assert!(a.ct_lt(&b).to_bool()); + assert!(!b.ct_lt(&a).to_bool()); + } + + } + )+ + }; + } + + ct_lt_tests!(u8, u16, u32, u64, u128, usize); + #[test] fn ordering() { assert!(!Ordering::Equal.ct_lt(&Ordering::Equal).to_bool()); diff --git a/ctutils/src/traits/ct_neg.rs b/ctutils/src/traits/ct_neg.rs index 76d5d2bc..b23ca4cc 100644 --- a/ctutils/src/traits/ct_neg.rs +++ b/ctutils/src/traits/ct_neg.rs @@ -1,7 +1,7 @@ use crate::{Choice, CtAssign, CtSelect}; use core::num::{ - NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, NonZeroU32, - NonZeroU64, NonZeroU128, + NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, }; /// Constant-time conditional negation: negates a value when `choice` is [`Choice::TRUE`]. @@ -62,13 +62,15 @@ impl_signed_ct_neg!( i32, i64, i128, + isize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, - NonZeroI128 + NonZeroI128, + NonZeroIsize ); -impl_unsigned_ct_neg!(u8, u16, u32, u64, u128); +impl_unsigned_ct_neg!(u8, u16, u32, u64, u128, usize); /// Unfortunately `NonZeroU*` doesn't support `wrapping_neg` for some reason (but `NonZeroI*` does), /// even though the wrapping negation of any non-zero integer should also be non-zero. @@ -89,7 +91,14 @@ macro_rules! impl_ct_neg_for_unsigned_nonzero { }; } -impl_ct_neg_for_unsigned_nonzero!(NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128); +impl_ct_neg_for_unsigned_nonzero!( + NonZeroU8, + NonZeroU16, + NonZeroU32, + NonZeroU64, + NonZeroU128, + NonZeroUsize +); #[cfg(test)] mod tests { @@ -130,14 +139,14 @@ mod tests { use crate::{Choice, CtNeg}; #[test] - fn u32_ct_neg() { + fn ct_neg() { let n: $uint = 42; assert_eq!(n, n.ct_neg(Choice::FALSE)); assert_eq!(<$uint>::MAX - n + 1, n.ct_neg(Choice::TRUE)); } #[test] - fn u32_ct_neg_assign() { + fn ct_neg_assign() { let n: $uint = 42; let mut x = n; x.ct_neg_assign(Choice::FALSE); @@ -151,6 +160,6 @@ mod tests { }; } - signed_ct_neg_tests!(i8, i16, i32, i64, i128); - unsigned_ct_neg_tests!(u8, u16, u32, u64, u128); + signed_ct_neg_tests!(i8, i16, i32, i64, i128, isize); + unsigned_ct_neg_tests!(u8, u16, u32, u64, u128, usize); } diff --git a/ctutils/src/traits/ct_select.rs b/ctutils/src/traits/ct_select.rs index 5a7fe3d9..be8bcc22 100644 --- a/ctutils/src/traits/ct_select.rs +++ b/ctutils/src/traits/ct_select.rs @@ -2,8 +2,8 @@ use crate::{Choice, CtAssign, CtAssignSlice}; use core::{ cmp, num::{ - NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, - NonZeroU32, NonZeroU64, NonZeroU128, + NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, }, }; @@ -15,8 +15,8 @@ use crate::CtOption; /// This crate provides built-in implementations for the following types: /// - [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], [`isize`] /// - [`u8`], [`u16`], [`u32`], [`u64`], [`u128`], [`usize`] -/// - [`NonZeroI8`], [`NonZeroI16`], [`NonZeroI32`], [`NonZeroI64`], [`NonZeroI128`] -/// - [`NonZeroU8`], [`NonZeroU16`], [`NonZeroU32`], [`NonZeroU64`], [`NonZeroU128`] +/// - [`NonZeroI8`], [`NonZeroI16`], [`NonZeroI32`], [`NonZeroI64`], [`NonZeroI128`], [`NonZeroI128`] +/// - [`NonZeroU8`], [`NonZeroU16`], [`NonZeroU32`], [`NonZeroU64`], [`NonZeroU128`],, [`NonZeroUsize`] /// - [`cmp::Ordering`] /// - [`Choice`] /// - `[T; N]` where `T` impls [`CtSelectArray`], which the previously mentioned types all do, @@ -109,11 +109,13 @@ impl_ct_select_with_ct_assign!( NonZeroI32, NonZeroI64, NonZeroI128, + NonZeroIsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, + NonZeroUsize, cmp::Ordering ); @@ -159,7 +161,7 @@ mod alloc { mod tests { use super::{Choice, CtSelect, cmp}; - macro_rules! ct_select_test { + macro_rules! ct_select_test_unsigned { ($ty:ty, $name:ident) => { #[test] fn $name() { @@ -171,11 +173,31 @@ mod tests { }; } - ct_select_test!(u8, u8_ct_select); - ct_select_test!(u16, u16_ct_select); - ct_select_test!(u32, u32_ct_select); - ct_select_test!(u64, u64_ct_select); - ct_select_test!(u128, u128_ct_select); + macro_rules! ct_select_test_signed { + ($ty:ty, $name:ident) => { + #[test] + fn $name() { + let a: $ty = 1; + let b: $ty = -2; + assert_eq!(a.ct_select(&b, Choice::FALSE), a); + assert_eq!(a.ct_select(&b, Choice::TRUE), b); + } + }; + } + + ct_select_test_unsigned!(u8, u8_ct_select); + ct_select_test_unsigned!(u16, u16_ct_select); + ct_select_test_unsigned!(u32, u32_ct_select); + ct_select_test_unsigned!(u64, u64_ct_select); + ct_select_test_unsigned!(u128, u128_ct_select); + ct_select_test_unsigned!(usize, usize_ct_select); + + ct_select_test_signed!(i8, i8_ct_select); + ct_select_test_signed!(i16, i16_ct_select); + ct_select_test_signed!(i32, i32_ct_select); + ct_select_test_signed!(i64, i64_ct_select); + ct_select_test_signed!(i128, i128_ct_select); + ct_select_test_signed!(isize, isize_ct_select); #[test] fn ordering_ct_select() {