about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2025-02-22 11:36:42 +0100
committerGitHub <noreply@github.com>2025-02-22 11:36:42 +0100
commit1df3a35bca9ff2c23073fabc2d7e6e336d7790f6 (patch)
tree4f7afff7051cbc35afd3087b8b53bd19f276fc53
parentb6d3be4948e92fce0236cbbe22b55c55f6950269 (diff)
parent97bc99a18f8175bf251a902fe5956e4d42fecebb (diff)
downloadrust-1df3a35bca9ff2c23073fabc2d7e6e336d7790f6.tar.gz
rust-1df3a35bca9ff2c23073fabc2d7e6e336d7790f6.zip
Rollup merge of #136910 - okaneco:sig_ones, r=thomcc
Implement feature `isolate_most_least_significant_one` for integer types

Accepted ACP - https://github.com/rust-lang/libs-team/issues/467
Tracking issue - #136909

Implement ACP for functions that isolate the most significant set bit and least significant set bit on unsigned, signed, and `NonZero` integers.

Add function `isolate_most_significant_one`
Add function `isolate_least_significant_one`

---

This PR adds the following impls
```rust
impl {u8, u16, u32, u64, u128, usize} {
    const fn isolate_most_significant_one(self) -> Self;
    const fn isolate_least_significant_one(self) -> Self;
}
impl {i8, i16, i32, i64, i128, isize} {
    const fn isolate_most_significant_one(self) -> Self;
    const fn isolate_least_significant_one(self) -> Self;
}
impl NonZeroT {
    const fn isolate_most_significant_one(self) -> Self;
    const fn isolate_least_significant_one(self) -> Self;
}
```
Example behavior
```rust
assert_eq!(u8::isolate_most_significant_one(0b01100100), 0b01000000);
assert_eq!(u8::isolate_least_significant_one(0b01100100), 0b00000100);
```
-rw-r--r--library/core/src/num/int_macros.rs46
-rw-r--r--library/core/src/num/nonzero.rs64
-rw-r--r--library/core/src/num/uint_macros.rs46
-rw-r--r--library/coretests/tests/lib.rs1
-rw-r--r--library/coretests/tests/nonzero.rs100
-rw-r--r--library/coretests/tests/num/int_macros.rs34
-rw-r--r--library/coretests/tests/num/uint_macros.rs34
7 files changed, 325 insertions, 0 deletions
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 296b5ebdfaf..74221e21687 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -183,6 +183,52 @@ macro_rules! int_impl {
             (self as $UnsignedT).trailing_ones()
         }
 
+        /// Returns `self` with only the most significant bit set, or `0` if
+        /// the input is `0`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// #![feature(isolate_most_least_significant_one)]
+        ///
+        #[doc = concat!("let n: ", stringify!($SelfT), " = 0b_01100100;")]
+        ///
+        /// assert_eq!(n.isolate_most_significant_one(), 0b_01000000);
+        #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".isolate_most_significant_one(), 0);")]
+        /// ```
+        #[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline(always)]
+        pub const fn isolate_most_significant_one(self) -> Self {
+            self & (((1 as $SelfT) << (<$SelfT>::BITS - 1)).wrapping_shr(self.leading_zeros()))
+        }
+
+        /// Returns `self` with only the least significant bit set, or `0` if
+        /// the input is `0`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// #![feature(isolate_most_least_significant_one)]
+        ///
+        #[doc = concat!("let n: ", stringify!($SelfT), " = 0b_01100100;")]
+        ///
+        /// assert_eq!(n.isolate_least_significant_one(), 0b_00000100);
+        #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".isolate_least_significant_one(), 0);")]
+        /// ```
+        #[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline(always)]
+        pub const fn isolate_least_significant_one(self) -> Self {
+            self & self.wrapping_neg()
+        }
+
         /// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size.
         ///
         /// This produces the same result as an `as` cast, but ensures that the bit-width remains
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index b59451d7c75..b94b492e8e1 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -605,6 +605,70 @@ macro_rules! nonzero_integer {
                 }
             }
 
+            /// Returns `self` with only the most significant bit set.
+            ///
+            /// # Example
+            ///
+            /// Basic usage:
+            ///
+            /// ```
+            /// #![feature(isolate_most_least_significant_one)]
+            ///
+            /// # use core::num::NonZero;
+            /// # fn main() { test().unwrap(); }
+            /// # fn test() -> Option<()> {
+            #[doc = concat!("let a = NonZero::<", stringify!($Int), ">::new(0b_01100100)?;")]
+            #[doc = concat!("let b = NonZero::<", stringify!($Int), ">::new(0b_01000000)?;")]
+            ///
+            /// assert_eq!(a.isolate_most_significant_one(), b);
+            /// # Some(())
+            /// # }
+            /// ```
+            #[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
+            #[must_use = "this returns the result of the operation, \
+                        without modifying the original"]
+            #[inline(always)]
+            pub const fn isolate_most_significant_one(self) -> Self {
+                let n = self.get() & (((1 as $Int) << (<$Int>::BITS - 1)).wrapping_shr(self.leading_zeros()));
+
+                // SAFETY:
+                // `self` is non-zero, so masking to preserve only the most
+                // significant set bit will result in a non-zero `n`.
+                unsafe { NonZero::new_unchecked(n) }
+            }
+
+            /// Returns `self` with only the least significant bit set.
+            ///
+            /// # Example
+            ///
+            /// Basic usage:
+            ///
+            /// ```
+            /// #![feature(isolate_most_least_significant_one)]
+            ///
+            /// # use core::num::NonZero;
+            /// # fn main() { test().unwrap(); }
+            /// # fn test() -> Option<()> {
+            #[doc = concat!("let a = NonZero::<", stringify!($Int), ">::new(0b_01100100)?;")]
+            #[doc = concat!("let b = NonZero::<", stringify!($Int), ">::new(0b_00000100)?;")]
+            ///
+            /// assert_eq!(a.isolate_least_significant_one(), b);
+            /// # Some(())
+            /// # }
+            /// ```
+            #[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
+            #[must_use = "this returns the result of the operation, \
+                        without modifying the original"]
+            #[inline(always)]
+            pub const fn isolate_least_significant_one(self) -> Self {
+                let n = self.get();
+                let n = n & n.wrapping_neg();
+
+                // SAFETY: `self` is non-zero, so `self` with only its least
+                // significant set bit will remain non-zero.
+                unsafe { NonZero::new_unchecked(n) }
+            }
+
             /// Returns the number of ones in the binary representation of `self`.
             ///
             /// # Examples
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 74d3ae699f6..289bc48acde 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -213,6 +213,52 @@ macro_rules! uint_impl {
             (!self).trailing_zeros()
         }
 
+        /// Returns `self` with only the most significant bit set, or `0` if
+        /// the input is `0`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// #![feature(isolate_most_least_significant_one)]
+        ///
+        #[doc = concat!("let n: ", stringify!($SelfT), " = 0b_01100100;")]
+        ///
+        /// assert_eq!(n.isolate_most_significant_one(), 0b_01000000);
+        #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".isolate_most_significant_one(), 0);")]
+        /// ```
+        #[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline(always)]
+        pub const fn isolate_most_significant_one(self) -> Self {
+            self & (((1 as $SelfT) << (<$SelfT>::BITS - 1)).wrapping_shr(self.leading_zeros()))
+        }
+
+        /// Returns `self` with only the least significant bit set, or `0` if
+        /// the input is `0`.
+        ///
+        /// # Examples
+        ///
+        /// Basic usage:
+        ///
+        /// ```
+        /// #![feature(isolate_most_least_significant_one)]
+        ///
+        #[doc = concat!("let n: ", stringify!($SelfT), " = 0b_01100100;")]
+        ///
+        /// assert_eq!(n.isolate_least_significant_one(), 0b_00000100);
+        #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".isolate_least_significant_one(), 0);")]
+        /// ```
+        #[unstable(feature = "isolate_most_least_significant_one", issue = "136909")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline(always)]
+        pub const fn isolate_least_significant_one(self) -> Self {
+            self & self.wrapping_neg()
+        }
+
         /// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size.
         ///
         /// This produces the same result as an `as` cast, but ensures that the bit-width remains
diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs
index ca58f41d7dd..eb6a8d85a30 100644
--- a/library/coretests/tests/lib.rs
+++ b/library/coretests/tests/lib.rs
@@ -44,6 +44,7 @@
 #![feature(ip)]
 #![feature(ip_from)]
 #![feature(is_ascii_octdigit)]
+#![feature(isolate_most_least_significant_one)]
 #![feature(iter_advance_by)]
 #![feature(iter_array_chunks)]
 #![feature(iter_chain)]
diff --git a/library/coretests/tests/nonzero.rs b/library/coretests/tests/nonzero.rs
index 43c279053d8..bdc5701d9fd 100644
--- a/library/coretests/tests/nonzero.rs
+++ b/library/coretests/tests/nonzero.rs
@@ -322,6 +322,106 @@ fn nonzero_trailing_zeros() {
 }
 
 #[test]
+fn test_nonzero_isolate_most_significant_one() {
+    // Signed most significant one
+    macro_rules! nonzero_int_impl {
+        ($($T:ty),+) => {
+            $(
+                {
+                    const BITS: $T = -1;
+                    const MOST_SIG_ONE: $T = 1 << (<$T>::BITS - 1);
+
+                    // Right shift the most significant one through each
+                    // bit position, starting with all bits set
+                    let mut i = 0;
+                    while i < <$T>::BITS {
+                        assert_eq!(
+                            NonZero::<$T>::new(BITS >> i).unwrap().isolate_most_significant_one(),
+                            NonZero::<$T>::new(MOST_SIG_ONE >> i).unwrap().isolate_most_significant_one()
+                        );
+                        i += 1;
+                    }
+                }
+            )+
+        };
+    }
+
+    // Unsigned most significant one
+    macro_rules! nonzero_uint_impl {
+        ($($T:ty),+) => {
+            $(
+                {
+                    const BITS: $T = <$T>::MAX;
+                    const MOST_SIG_ONE: $T = 1 << (<$T>::BITS - 1);
+
+                    let mut i = 0;
+                    while i < <$T>::BITS {
+                        assert_eq!(
+                            NonZero::<$T>::new(BITS >> i).unwrap().isolate_most_significant_one(),
+                            NonZero::<$T>::new(MOST_SIG_ONE >> i).unwrap().isolate_most_significant_one(),
+                        );
+                        i += 1;
+                    }
+                }
+            )+
+        };
+    }
+
+    nonzero_int_impl!(i8, i16, i32, i64, i128, isize);
+    nonzero_uint_impl!(u8, u16, u32, u64, u128, usize);
+}
+
+#[test]
+fn test_nonzero_isolate_least_significant_one() {
+    // Signed least significant one
+    macro_rules! nonzero_int_impl {
+        ($($T:ty),+) => {
+            $(
+                {
+                    const BITS: $T = -1;
+                    const LEAST_SIG_ONE: $T = 1;
+
+                    // Left shift the least significant one through each
+                    // bit position, starting with all bits set
+                    let mut i = 0;
+                    while i < <$T>::BITS {
+                        assert_eq!(
+                            NonZero::<$T>::new(BITS << i).unwrap().isolate_least_significant_one(),
+                            NonZero::<$T>::new(LEAST_SIG_ONE << i).unwrap().isolate_least_significant_one()
+                        );
+                        i += 1;
+                    }
+                }
+            )+
+        };
+    }
+
+    // Unsigned least significant one
+    macro_rules! nonzero_uint_impl {
+        ($($T:ty),+) => {
+            $(
+                {
+                    const BITS: $T = <$T>::MAX;
+                    const LEAST_SIG_ONE: $T = 1;
+
+                    let mut i = 0;
+                    while i < <$T>::BITS {
+                        assert_eq!(
+                            NonZero::<$T>::new(BITS << i).unwrap().isolate_least_significant_one(),
+                            NonZero::<$T>::new(LEAST_SIG_ONE << i).unwrap().isolate_least_significant_one(),
+                        );
+                        i += 1;
+                    }
+                }
+            )+
+        };
+    }
+
+    nonzero_int_impl!(i8, i16, i32, i64, i128, isize);
+    nonzero_uint_impl!(u8, u16, u32, u64, u128, usize);
+}
+
+#[test]
 fn test_nonzero_uint_div() {
     let nz = NonZero::new(1).unwrap();
 
diff --git a/library/coretests/tests/num/int_macros.rs b/library/coretests/tests/num/int_macros.rs
index f13b836378b..392704e7509 100644
--- a/library/coretests/tests/num/int_macros.rs
+++ b/library/coretests/tests/num/int_macros.rs
@@ -193,6 +193,40 @@ macro_rules! int_module {
         }
 
         #[test]
+        fn test_isolate_most_significant_one() {
+            const BITS: $T = -1;
+            const MOST_SIG_ONE: $T = 1 << (<$T>::BITS - 1);
+
+            // Right shift the most significant one through each
+            // bit position, starting with all bits set
+            let mut i = 0;
+            while i < <$T>::BITS {
+                assert_eq!(
+                    (BITS >> i).isolate_most_significant_one(),
+                    (MOST_SIG_ONE >> i).isolate_most_significant_one()
+                );
+                i += 1;
+            }
+        }
+
+        #[test]
+        fn test_isolate_least_significant_one() {
+            const BITS: $T = -1;
+            const LEAST_SIG_ONE: $T = 1;
+
+            // Left shift the least significant one through each
+            // bit position, starting with all bits set
+            let mut i = 0;
+            while i < <$T>::BITS {
+                assert_eq!(
+                    (BITS << i).isolate_least_significant_one(),
+                    (LEAST_SIG_ONE << i).isolate_least_significant_one()
+                );
+                i += 1;
+            }
+        }
+
+        #[test]
         fn test_from_str() {
             fn from_str<T: std::str::FromStr>(t: &str) -> Option<T> {
                 std::str::FromStr::from_str(t).ok()
diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs
index 99a2d4cd462..c085ba5a249 100644
--- a/library/coretests/tests/num/uint_macros.rs
+++ b/library/coretests/tests/num/uint_macros.rs
@@ -141,6 +141,40 @@ macro_rules! uint_module {
             }
         }
 
+        #[test]
+        fn test_isolate_most_significant_one() {
+            const BITS: $T = <$T>::MAX;
+            const MOST_SIG_ONE: $T = 1 << (<$T>::BITS - 1);
+
+            // Right shift the most significant one through each
+            // bit position, starting with all bits set
+            let mut i = 0;
+            while i < <$T>::BITS {
+                assert_eq!(
+                    (BITS >> i).isolate_most_significant_one(),
+                    (MOST_SIG_ONE >> i).isolate_most_significant_one(),
+                );
+                i += 1;
+            }
+        }
+
+        #[test]
+        fn test_isolate_least_significant_one() {
+            const BITS: $T = <$T>::MAX;
+            const LEAST_SIG_ONE: $T = 1;
+
+            // Left shift the least significant one through each
+            // bit position, starting with all bits set
+            let mut i = 0;
+            while i < <$T>::BITS {
+                assert_eq!(
+                    (BITS << i).isolate_least_significant_one(),
+                    (LEAST_SIG_ONE << i).isolate_least_significant_one(),
+                );
+                i += 1;
+            }
+        }
+
         fn from_str<T: core::str::FromStr>(t: &str) -> Option<T> {
             core::str::FromStr::from_str(t).ok()
         }