From 97f9965aa861a0f2b3d25dc0b4a55cf2437a7cca Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 23 Apr 2025 14:55:20 -0600 Subject: [PATCH 1/2] Add `UintRef` Adds a reference newtype for a `Limb` slice which can be used as an abstraction for implementing shared functionality for both `Uint` and `BoxedUint`. The initial implementation supports a limited amount of functionality but supports usage as both `&UintRef` and `&mut UintRef`. This includes a common implementation of `fmt` impls shared between `Uint` and `BoxedUint`, replacing a copy-paste implementation. Closes #756 --- src/uint.rs | 52 +++++++------- src/uint/boxed.rs | 63 +++++------------ src/uint/ref_type.rs | 157 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 69 deletions(-) create mode 100644 src/uint/ref_type.rs diff --git a/src/uint.rs b/src/uint.rs index 5802d7573..d88e9a9e3 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -13,6 +13,8 @@ use zeroize::DefaultIsZeroes; #[cfg(feature = "extra-sizes")] pub use extra_sizes::*; +pub use ref_type::UintRef; + use crate::{ Bounded, ConstCtOption, ConstZero, Constants, Encoding, FixedInteger, Int, Integer, Limb, NonZero, Odd, PrecomputeInverter, PrecomputeInverterWithAdjuster, Word, @@ -186,6 +188,16 @@ impl Uint { self.limbs } + /// Borrow the limbs of this [`Uint`] as a [`UintRef`]. + pub const fn as_uint_ref(&self) -> &UintRef { + UintRef::new(&self.limbs) + } + + /// Mutably borrow the limbs of this [`Uint`] as a [`UintRef`]. + pub const fn as_mut_uint_ref(&mut self) -> &mut UintRef { + UintRef::new_mut(&mut self.limbs) + } + /// Convert to a [`NonZero>`]. /// /// Returns some if the original value is non-zero, and false otherwise. @@ -230,6 +242,18 @@ impl AsMut<[Limb]> for Uint { } } +impl AsRef for Uint { + fn as_ref(&self) -> &UintRef { + self.as_uint_ref() + } +} + +impl AsMut for Uint { + fn as_mut(&mut self) -> &mut UintRef { + self.as_mut_uint_ref() + } +} + impl ConditionallySelectable for Uint { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { let mut limbs = [Limb::ZERO; LIMBS]; @@ -313,20 +337,13 @@ impl num_traits::One for Uint { impl fmt::Debug for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Uint(0x{self:X})") + write!(f, "Uint(0x{:X})", self.as_uint_ref()) } } impl fmt::Binary for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - write!(f, "0b")?; - } - - for limb in self.limbs.iter().rev() { - write!(f, "{:0width$b}", &limb.0, width = Limb::BITS as usize)?; - } - Ok(()) + fmt::Binary::fmt(self.as_uint_ref(), f) } } @@ -338,25 +355,13 @@ impl fmt::Display for Uint { impl fmt::LowerHex for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - write!(f, "0x")?; - } - for limb in self.limbs.iter().rev() { - write!(f, "{:0width$x}", &limb.0, width = Limb::BYTES * 2)?; - } - Ok(()) + fmt::LowerHex::fmt(self.as_uint_ref(), f) } } impl fmt::UpperHex for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - write!(f, "0x")?; - } - for limb in self.limbs.iter().rev() { - write!(f, "{:0width$X}", &limb.0, width = Limb::BYTES * 2)?; - } - Ok(()) + fmt::UpperHex::fmt(self.as_uint_ref(), f) } } @@ -485,6 +490,7 @@ impl_uint_concat_split_mixed! { #[cfg(feature = "extra-sizes")] mod extra_sizes; +mod ref_type; #[cfg(test)] #[allow(clippy::unwrap_used)] diff --git a/src/uint/boxed.rs b/src/uint/boxed.rs index 7379240d5..cae324710 100644 --- a/src/uint/boxed.rs +++ b/src/uint/boxed.rs @@ -28,7 +28,7 @@ mod sub_mod; #[cfg(feature = "rand_core")] mod rand; -use crate::{Integer, Limb, NonZero, Odd, Word, Zero, modular::BoxedMontyForm}; +use crate::{Integer, Limb, NonZero, Odd, UintRef, Word, Zero, modular::BoxedMontyForm}; use alloc::{boxed::Box, vec, vec::Vec}; use core::fmt; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -134,20 +134,12 @@ impl BoxedUint { /// Borrow the inner limbs as a slice of [`Word`]s. pub fn as_words(&self) -> &[Word] { - // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word` - #[allow(trivial_casts, unsafe_code)] - unsafe { - &*((&*self.limbs as *const [Limb]) as *const [Word]) - } + self.as_uint_ref().as_words() } /// Borrow the inner limbs as a mutable slice of [`Word`]s. pub fn as_mut_words(&mut self) -> &mut [Word] { - // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word` - #[allow(trivial_casts, unsafe_code)] - unsafe { - &mut *((&mut *self.limbs as *mut [Limb]) as *mut [Word]) - } + self.as_mut_uint_ref().as_mut_words() } /// Borrow the inner limbs as a mutable slice of [`Word`]s. @@ -182,6 +174,16 @@ impl BoxedUint { self.limbs } + /// Borrow the limbs of this [`BoxedUint`] as a [`UintRef`]. + pub fn as_uint_ref(&self) -> &UintRef { + UintRef::new(&self.limbs) + } + + /// Mutably borrow the limbs of this [`BoxedUint`] as a [`UintRef`]. + pub fn as_mut_uint_ref(&mut self) -> &mut UintRef { + UintRef::new_mut(&mut self.limbs) + } + /// Get the number of limbs in this [`BoxedUint`]. pub fn nlimbs(&self) -> usize { self.limbs.len() @@ -371,7 +373,7 @@ impl Zeroize for BoxedUint { impl fmt::Debug for BoxedUint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "BoxedUint(0x{self:X})") + write!(f, "BoxedUint(0x{:X})", self.as_uint_ref()) } } @@ -383,50 +385,19 @@ impl fmt::Display for BoxedUint { impl fmt::Binary for BoxedUint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.limbs.is_empty() { - return fmt::Binary::fmt(&Limb::ZERO, f); - } - - if f.alternate() { - write!(f, "0b")?; - } - - for limb in self.limbs.iter().rev() { - write!(f, "{:0width$b}", &limb.0, width = Limb::BITS as usize)?; - } - Ok(()) + fmt::Binary::fmt(self.as_uint_ref(), f) } } impl fmt::LowerHex for BoxedUint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.limbs.is_empty() { - return fmt::LowerHex::fmt(&Limb::ZERO, f); - } - - if f.alternate() { - write!(f, "0x")?; - } - for limb in self.limbs.iter().rev() { - write!(f, "{:0width$x}", &limb.0, width = Limb::BYTES * 2)?; - } - Ok(()) + fmt::LowerHex::fmt(self.as_uint_ref(), f) } } impl fmt::UpperHex for BoxedUint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.limbs.is_empty() { - return fmt::LowerHex::fmt(&Limb::ZERO, f); - } - - if f.alternate() { - write!(f, "0x")?; - } - for limb in self.limbs.iter().rev() { - write!(f, "{:0width$X}", &limb.0, width = Limb::BYTES * 2)?; - } - Ok(()) + fmt::UpperHex::fmt(self.as_uint_ref(), f) } } diff --git a/src/uint/ref_type.rs b/src/uint/ref_type.rs new file mode 100644 index 000000000..4b78500fc --- /dev/null +++ b/src/uint/ref_type.rs @@ -0,0 +1,157 @@ +//! Unsigned integer reference type. + +use crate::{Limb, Word}; +use core::{ + fmt, + ops::{Index, IndexMut}, +}; + +/// Unsigned integer reference type. +/// +/// This type contains a limb slice which can be borrowed from either a `Uint` or `BoxedUint` and +/// thus provides an abstraction for writing shared implementations. +#[repr(transparent)] +pub struct UintRef(pub [Limb]); + +impl UintRef { + /// Create a [`UintRef`] reference type from a [`Limb`] slice. + pub const fn new(limbs: &[Limb]) -> &Self { + // SAFETY: `UintRef` is a `repr(transparent)` newtype for `[Limb]`. + #[allow(trivial_casts, unsafe_code)] + unsafe { + &*(limbs as *const [Limb] as *const UintRef) + } + } + + /// Create a mutable [`UintRef`] reference type from a [`Limb`] slice. + pub const fn new_mut(limbs: &mut [Limb]) -> &mut Self { + // SAFETY: `UintRef` is a `repr(transparent)` newtype for `[Limb]`. + #[allow(trivial_casts, unsafe_code)] + unsafe { + &mut *(limbs as *mut [Limb] as *mut UintRef) + } + } + + /// Borrow the inner `&[Limb]` slice. + pub const fn as_slice(&self) -> &[Limb] { + &self.0 + } + + /// Mutably borrow the inner `&mut [Limb]` slice. + pub const fn as_mut_slice(&mut self) -> &mut [Limb] { + &mut self.0 + } + + /// Borrow the inner limbs as a slice of [`Word`]s. + pub const fn as_words(&self) -> &[Word] { + // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word` + #[allow(trivial_casts, unsafe_code)] + unsafe { + &*((&self.0 as *const [Limb]) as *const [Word]) + } + } + + /// Borrow the inner limbs as a mutable slice of [`Word`]s. + pub const fn as_mut_words(&mut self) -> &mut [Word] { + // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word` + #[allow(trivial_casts, unsafe_code)] + unsafe { + &mut *((&mut self.0 as *mut [Limb]) as *mut [Word]) + } + } + + /// Get an iterator over the inner limbs. + pub fn iter(&self) -> impl DoubleEndedIterator { + self.0.iter() + } + + /// Get a mutable iterator over the inner limbs. + pub fn iter_mut(&mut self) -> impl DoubleEndedIterator { + self.0.iter_mut() + } + + /// Is the inner limb slice empty? + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Get the number of limbs. + pub fn len(&self) -> usize { + self.0.len() + } +} + +impl AsRef<[Limb]> for UintRef { + fn as_ref(&self) -> &[Limb] { + self.as_slice() + } +} + +impl AsMut<[Limb]> for UintRef { + fn as_mut(&mut self) -> &mut [Limb] { + self.as_mut_slice() + } +} + +impl Index for UintRef { + type Output = Limb; + + fn index(&self, index: usize) -> &Limb { + self.0.index(index) + } +} + +impl IndexMut for UintRef { + fn index_mut(&mut self, index: usize) -> &mut Limb { + self.0.index_mut(index) + } +} + +impl fmt::Debug for UintRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UintRef(0x{self:X})") + } +} + +impl fmt::Binary for UintRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "0b")?; + } + + for limb in self.iter().rev() { + write!(f, "{:0width$b}", &limb.0, width = Limb::BITS as usize)?; + } + Ok(()) + } +} + +impl fmt::Display for UintRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(self, f) + } +} + +impl fmt::LowerHex for UintRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + for limb in self.iter().rev() { + write!(f, "{:0width$x}", &limb.0, width = Limb::BYTES * 2)?; + } + Ok(()) + } +} + +impl fmt::UpperHex for UintRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + write!(f, "0x")?; + } + for limb in self.iter().rev() { + write!(f, "{:0width$X}", &limb.0, width = Limb::BYTES * 2)?; + } + Ok(()) + } +} From 8353f196b95ef35d9a8315b5ae72a13534c5e19e Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Wed, 23 Apr 2025 15:38:47 -0600 Subject: [PATCH 2/2] AsRef/AsMut for BoxedUint --- src/uint/boxed.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/uint/boxed.rs b/src/uint/boxed.rs index cae324710..026406d43 100644 --- a/src/uint/boxed.rs +++ b/src/uint/boxed.rs @@ -306,6 +306,18 @@ impl AsMut<[Limb]> for BoxedUint { } } +impl AsRef for BoxedUint { + fn as_ref(&self) -> &UintRef { + self.as_uint_ref() + } +} + +impl AsMut for BoxedUint { + fn as_mut(&mut self) -> &mut UintRef { + self.as_mut_uint_ref() + } +} + impl Default for BoxedUint { fn default() -> Self { Self::zero()