Skip to content

Commit

Permalink
Rollup merge of rust-lang#79114 - andjo403:nonzero_leading_trailing_z…
Browse files Browse the repository at this point in the history
…eros, r=m-ou-se

add trailing_zeros and leading_zeros to non zero types

as a way towards being able to use the optimized intrinsics ctlz_nonzero and cttz_nonzero from stable.

have not crated any tracking issue if this is not a solution that is wanted
  • Loading branch information
m-ou-se committed Nov 18, 2020
2 parents 1bd70a2 + 9bbc4c1 commit 6a6607a
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 1 deletion.
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
#![feature(const_mut_refs)]
#![feature(const_int_pow)]
#![feature(constctlz)]
#![feature(const_cttz)]
#![feature(const_panic)]
#![feature(const_pin)]
#![feature(const_fn)]
Expand Down
74 changes: 74 additions & 0 deletions library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::str::FromStr;

use super::from_str_radix;
use super::{IntErrorKind, ParseIntError};
use crate::intrinsics;

macro_rules! doc_comment {
($x:expr, $($tt:tt)*) => {
Expand Down Expand Up @@ -189,3 +190,76 @@ macro_rules! from_str_radix_nzint_impl {

from_str_radix_nzint_impl! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize
NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize }

macro_rules! nonzero_leading_trailing_zeros {
( $( $Ty: ident($Uint: ty) , $LeadingTestExpr:expr ;)+ ) => {
$(
impl $Ty {
doc_comment! {
concat!("Returns the number of leading zeros in the binary representation of `self`.
On many architectures, this function can perform better than `leading_zeros()` on the underlying integer type, as special handling of zero can be avoided.
# Examples
Basic usage:
```
#![feature(nonzero_leading_trailing_zeros)]
let n = std::num::", stringify!($Ty), "::new(", stringify!($LeadingTestExpr), ").unwrap();
assert_eq!(n.leading_zeros(), 0);
```"),
#[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")]
#[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")]
#[inline]
pub const fn leading_zeros(self) -> u32 {
// SAFETY: since `self` can not be zero it is safe to call ctlz_nonzero
unsafe { intrinsics::ctlz_nonzero(self.0 as $Uint) as u32 }
}
}

doc_comment! {
concat!("Returns the number of trailing zeros in the binary representation
of `self`.
On many architectures, this function can perform better than `trailing_zeros()` on the underlying integer type, as special handling of zero can be avoided.
# Examples
Basic usage:
```
#![feature(nonzero_leading_trailing_zeros)]
let n = std::num::", stringify!($Ty), "::new(0b0101000).unwrap();
assert_eq!(n.trailing_zeros(), 3);
```"),
#[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")]
#[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")]
#[inline]
pub const fn trailing_zeros(self) -> u32 {
// SAFETY: since `self` can not be zero it is safe to call cttz_nonzero
unsafe { intrinsics::cttz_nonzero(self.0 as $Uint) as u32 }
}
}

}
)+
}
}

nonzero_leading_trailing_zeros! {
NonZeroU8(u8), u8::MAX;
NonZeroU16(u16), u16::MAX;
NonZeroU32(u32), u32::MAX;
NonZeroU64(u64), u64::MAX;
NonZeroU128(u128), u128::MAX;
NonZeroUsize(usize), usize::MAX;
NonZeroI8(u8), -1i8;
NonZeroI16(u16), -1i16;
NonZeroI32(u32), -1i32;
NonZeroI64(u64), -1i64;
NonZeroI128(u128), -1i128;
NonZeroIsize(usize), -1isize;
}
2 changes: 2 additions & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
#![feature(once_cell)]
#![feature(unsafe_block_in_unsafe_fn)]
#![feature(int_bits_const)]
#![feature(nonzero_leading_trailing_zeros)]
#![feature(const_option)]
#![deny(unsafe_op_in_unsafe_fn)]

extern crate test;
Expand Down
102 changes: 101 additions & 1 deletion library/core/tests/nonzero.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use core::convert::TryFrom;
use core::num::{IntErrorKind, NonZeroI32, NonZeroI8, NonZeroU32, NonZeroU8};
use core::num::{
IntErrorKind, NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize,
NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
};
use core::option::Option::{self, None, Some};
use std::mem::size_of;

Expand Down Expand Up @@ -212,3 +215,100 @@ fn nonzero_const() {
const ONE: Option<NonZeroU8> = NonZeroU8::new(1);
assert!(ONE.is_some());
}

#[test]
fn nonzero_leading_zeros() {
assert_eq!(NonZeroU8::new(1).unwrap().leading_zeros(), 7);
assert_eq!(NonZeroI8::new(1).unwrap().leading_zeros(), 7);
assert_eq!(NonZeroU16::new(1).unwrap().leading_zeros(), 15);
assert_eq!(NonZeroI16::new(1).unwrap().leading_zeros(), 15);
assert_eq!(NonZeroU32::new(1).unwrap().leading_zeros(), 31);
assert_eq!(NonZeroI32::new(1).unwrap().leading_zeros(), 31);
assert_eq!(NonZeroU64::new(1).unwrap().leading_zeros(), 63);
assert_eq!(NonZeroI64::new(1).unwrap().leading_zeros(), 63);
assert_eq!(NonZeroU128::new(1).unwrap().leading_zeros(), 127);
assert_eq!(NonZeroI128::new(1).unwrap().leading_zeros(), 127);
assert_eq!(NonZeroUsize::new(1).unwrap().leading_zeros(), usize::BITS - 1);
assert_eq!(NonZeroIsize::new(1).unwrap().leading_zeros(), usize::BITS - 1);

assert_eq!(NonZeroU8::new(u8::MAX >> 2).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroI8::new((u8::MAX >> 2) as i8).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroU16::new(u16::MAX >> 2).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroI16::new((u16::MAX >> 2) as i16).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroU32::new(u32::MAX >> 2).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroI32::new((u32::MAX >> 2) as i32).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroU64::new(u64::MAX >> 2).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroI64::new((u64::MAX >> 2) as i64).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroU128::new(u128::MAX >> 2).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroI128::new((u128::MAX >> 2) as i128).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroUsize::new(usize::MAX >> 2).unwrap().leading_zeros(), 2);
assert_eq!(NonZeroIsize::new((usize::MAX >> 2) as isize).unwrap().leading_zeros(), 2);

assert_eq!(NonZeroU8::new(u8::MAX).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroI8::new(-1i8).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroU16::new(u16::MAX).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroI16::new(-1i16).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroU32::new(u32::MAX).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroI32::new(-1i32).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroU64::new(u64::MAX).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroI64::new(-1i64).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroU128::new(u128::MAX).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroI128::new(-1i128).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroUsize::new(usize::MAX).unwrap().leading_zeros(), 0);
assert_eq!(NonZeroIsize::new(-1isize).unwrap().leading_zeros(), 0);

const LEADING_ZEROS: u32 = NonZeroU16::new(1).unwrap().leading_zeros();
assert_eq!(LEADING_ZEROS, 15);
}

#[test]
fn nonzero_trailing_zeros() {
assert_eq!(NonZeroU8::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroI8::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroU16::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroI16::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroU32::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroI32::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroU64::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroI64::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroU128::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroI128::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroUsize::new(1).unwrap().trailing_zeros(), 0);
assert_eq!(NonZeroIsize::new(1).unwrap().trailing_zeros(), 0);

assert_eq!(NonZeroU8::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroI8::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroU16::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroI16::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroU32::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroI32::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroU64::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroI64::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroU128::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroI128::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroUsize::new(1 << 2).unwrap().trailing_zeros(), 2);
assert_eq!(NonZeroIsize::new(1 << 2).unwrap().trailing_zeros(), 2);

assert_eq!(NonZeroU8::new(1 << 7).unwrap().trailing_zeros(), 7);
assert_eq!(NonZeroI8::new(1 << 7).unwrap().trailing_zeros(), 7);
assert_eq!(NonZeroU16::new(1 << 15).unwrap().trailing_zeros(), 15);
assert_eq!(NonZeroI16::new(1 << 15).unwrap().trailing_zeros(), 15);
assert_eq!(NonZeroU32::new(1 << 31).unwrap().trailing_zeros(), 31);
assert_eq!(NonZeroI32::new(1 << 31).unwrap().trailing_zeros(), 31);
assert_eq!(NonZeroU64::new(1 << 63).unwrap().trailing_zeros(), 63);
assert_eq!(NonZeroI64::new(1 << 63).unwrap().trailing_zeros(), 63);
assert_eq!(NonZeroU128::new(1 << 127).unwrap().trailing_zeros(), 127);
assert_eq!(NonZeroI128::new(1 << 127).unwrap().trailing_zeros(), 127);

assert_eq!(
NonZeroUsize::new(1 << (usize::BITS - 1)).unwrap().trailing_zeros(),
usize::BITS - 1
);
assert_eq!(
NonZeroIsize::new(1 << (usize::BITS - 1)).unwrap().trailing_zeros(),
usize::BITS - 1
);

const TRAILING_ZEROS: u32 = NonZeroU16::new(1 << 2).unwrap().trailing_zeros();
assert_eq!(TRAILING_ZEROS, 2);
}

0 comments on commit 6a6607a

Please sign in to comment.