Rollup merge of #76492 - fusion-engineering-forks:int-bits, r=dtolnay

Add associated constant `BITS` to all integer types

Recently I've regularly come across this snippet (in a few different crates, including `core` and `std`):
```rust
std::mem::size_of<usize>() * 8
```

I think it's time for a `usize::BITS`.
This commit is contained in:
Ralf Jung 2020-09-19 11:47:45 +02:00 committed by GitHub
commit fef3324043
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 72 additions and 64 deletions

View File

@ -14,6 +14,7 @@
#![feature(generators)] #![feature(generators)]
#![feature(generator_trait)] #![feature(generator_trait)]
#![feature(fn_traits)] #![feature(fn_traits)]
#![feature(int_bits_const)]
#![feature(min_specialization)] #![feature(min_specialization)]
#![feature(optin_builtin_traits)] #![feature(optin_builtin_traits)]
#![feature(nll)] #![feature(nll)]

View File

@ -48,7 +48,7 @@ where
P: Pointer, P: Pointer,
T: Tag, T: Tag,
{ {
const TAG_BIT_SHIFT: usize = (8 * std::mem::size_of::<usize>()) - T::BITS; const TAG_BIT_SHIFT: usize = usize::BITS as usize - T::BITS;
const ASSERTION: () = { const ASSERTION: () = {
assert!(T::BITS <= P::BITS); assert!(T::BITS <= P::BITS);
// Used for the transmute_copy's below // Used for the transmute_copy's below

View File

@ -146,7 +146,7 @@
use core::fmt; use core::fmt;
use core::iter::{FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen}; use core::iter::{FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen};
use core::mem::{self, size_of, swap, ManuallyDrop}; use core::mem::{self, swap, ManuallyDrop};
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use core::ptr; use core::ptr;
@ -617,7 +617,7 @@ impl<T: Ord> BinaryHeap<T> {
#[inline(always)] #[inline(always)]
fn log2_fast(x: usize) -> usize { fn log2_fast(x: usize) -> usize {
8 * size_of::<usize>() - (x.leading_zeros() as usize) - 1 (usize::BITS - x.leading_zeros() - 1) as usize
} }
// `rebuild` takes O(len1 + len2) operations // `rebuild` takes O(len1 + len2) operations

View File

@ -101,6 +101,7 @@
#![feature(fn_traits)] #![feature(fn_traits)]
#![feature(fundamental)] #![feature(fundamental)]
#![feature(inplace_iteration)] #![feature(inplace_iteration)]
#![feature(int_bits_const)]
#![feature(lang_items)] #![feature(lang_items)]
#![feature(layout_for_ptr)] #![feature(layout_for_ptr)]
#![feature(libc)] #![feature(libc)]

View File

@ -528,7 +528,7 @@ unsafe impl<#[may_dangle] T, A: AllocRef> Drop for RawVec<T, A> {
#[inline] #[inline]
fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> {
if mem::size_of::<usize>() < 8 && alloc_size > isize::MAX as usize { if usize::BITS < 64 && alloc_size > isize::MAX as usize {
Err(CapacityOverflow) Err(CapacityOverflow)
} else { } else {
Ok(()) Ok(())

View File

@ -18,6 +18,7 @@
#![feature(deque_range)] #![feature(deque_range)]
#![feature(inplace_iteration)] #![feature(inplace_iteration)]
#![feature(iter_map_while)] #![feature(iter_map_while)]
#![feature(int_bits_const)]
use std::collections::hash_map::DefaultHasher; use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};

View File

@ -1,6 +1,5 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::TryReserveError::*; use std::collections::TryReserveError::*;
use std::mem::size_of;
use std::ops::Bound::*; use std::ops::Bound::*;
pub trait IntoCow<'a, B: ?Sized> pub trait IntoCow<'a, B: ?Sized>
@ -605,7 +604,7 @@ fn test_try_reserve() {
// on 64-bit, we assume the OS will give an OOM for such a ridiculous size. // on 64-bit, we assume the OS will give an OOM for such a ridiculous size.
// Any platform that succeeds for these requests is technically broken with // Any platform that succeeds for these requests is technically broken with
// ptr::offset because LLVM is the worst. // ptr::offset because LLVM is the worst.
let guards_against_isize = size_of::<usize>() < 8; let guards_against_isize = usize::BITS < 64;
{ {
// Note: basic stuff is checked by test_reserve // Note: basic stuff is checked by test_reserve
@ -686,7 +685,7 @@ fn test_try_reserve_exact() {
const MAX_CAP: usize = isize::MAX as usize; const MAX_CAP: usize = isize::MAX as usize;
const MAX_USIZE: usize = usize::MAX; const MAX_USIZE: usize = usize::MAX;
let guards_against_isize = size_of::<usize>() < 8; let guards_against_isize = usize::BITS < 64;
{ {
let mut empty_string: String = String::new(); let mut empty_string: String = String::new();

View File

@ -1341,7 +1341,7 @@ fn test_try_reserve() {
// on 64-bit, we assume the OS will give an OOM for such a ridiculous size. // on 64-bit, we assume the OS will give an OOM for such a ridiculous size.
// Any platform that succeeds for these requests is technically broken with // Any platform that succeeds for these requests is technically broken with
// ptr::offset because LLVM is the worst. // ptr::offset because LLVM is the worst.
let guards_against_isize = size_of::<usize>() < 8; let guards_against_isize = usize::BITS < 64;
{ {
// Note: basic stuff is checked by test_reserve // Note: basic stuff is checked by test_reserve

View File

@ -2086,7 +2086,7 @@ impl<T: ?Sized> Pointer for *const T {
f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32); f.flags |= 1 << (FlagV1::SignAwareZeroPad as u32);
if f.width.is_none() { if f.width.is_none() {
f.width = Some(((mem::size_of::<usize>() * 8) / 4) + 2); f.width = Some((usize::BITS / 4) as usize + 2);
} }
} }
f.flags |= 1 << (FlagV1::Alternate as u32); f.flags |= 1 << (FlagV1::Alternate as u32);

View File

@ -20,7 +20,6 @@
#![macro_use] #![macro_use]
use crate::intrinsics; use crate::intrinsics;
use crate::mem;
/// Arithmetic operations required by bignums. /// Arithmetic operations required by bignums.
pub trait FullOps: Sized { pub trait FullOps: Sized {
@ -58,25 +57,22 @@ macro_rules! impl_full_ops {
// This cannot overflow; // This cannot overflow;
// the output is between `0` and `2^nbits * (2^nbits - 1)`. // the output is between `0` and `2^nbits * (2^nbits - 1)`.
// FIXME: will LLVM optimize this into ADC or similar? // FIXME: will LLVM optimize this into ADC or similar?
let nbits = mem::size_of::<$ty>() * 8;
let v = (self as $bigty) * (other as $bigty) + (carry as $bigty); let v = (self as $bigty) * (other as $bigty) + (carry as $bigty);
((v >> nbits) as $ty, v as $ty) ((v >> <$ty>::BITS) as $ty, v as $ty)
} }
fn full_mul_add(self, other: $ty, other2: $ty, carry: $ty) -> ($ty, $ty) { fn full_mul_add(self, other: $ty, other2: $ty, carry: $ty) -> ($ty, $ty) {
// This cannot overflow; // This cannot overflow;
// the output is between `0` and `2^nbits * (2^nbits - 1)`. // the output is between `0` and `2^nbits * (2^nbits - 1)`.
let nbits = mem::size_of::<$ty>() * 8;
let v = (self as $bigty) * (other as $bigty) + (other2 as $bigty) + let v = (self as $bigty) * (other as $bigty) + (other2 as $bigty) +
(carry as $bigty); (carry as $bigty);
((v >> nbits) as $ty, v as $ty) ((v >> <$ty>::BITS) as $ty, v as $ty)
} }
fn full_div_rem(self, other: $ty, borrow: $ty) -> ($ty, $ty) { fn full_div_rem(self, other: $ty, borrow: $ty) -> ($ty, $ty) {
debug_assert!(borrow < other); debug_assert!(borrow < other);
// This cannot overflow; the output is between `0` and `other * (2^nbits - 1)`. // This cannot overflow; the output is between `0` and `other * (2^nbits - 1)`.
let nbits = mem::size_of::<$ty>() * 8; let lhs = ((borrow as $bigty) << <$ty>::BITS) | (self as $bigty);
let lhs = ((borrow as $bigty) << nbits) | (self as $bigty);
let rhs = other as $bigty; let rhs = other as $bigty;
((lhs / rhs) as $ty, (lhs % rhs) as $ty) ((lhs / rhs) as $ty, (lhs % rhs) as $ty)
} }
@ -128,13 +124,11 @@ macro_rules! define_bignum {
/// Makes a bignum from `u64` value. /// Makes a bignum from `u64` value.
pub fn from_u64(mut v: u64) -> $name { pub fn from_u64(mut v: u64) -> $name {
use crate::mem;
let mut base = [0; $n]; let mut base = [0; $n];
let mut sz = 0; let mut sz = 0;
while v > 0 { while v > 0 {
base[sz] = v as $ty; base[sz] = v as $ty;
v >>= mem::size_of::<$ty>() * 8; v >>= <$ty>::BITS;
sz += 1; sz += 1;
} }
$name { size: sz, base: base } $name { size: sz, base: base }
@ -150,9 +144,7 @@ macro_rules! define_bignum {
/// Returns the `i`-th bit where bit 0 is the least significant one. /// Returns the `i`-th bit where bit 0 is the least significant one.
/// In other words, the bit with weight `2^i`. /// In other words, the bit with weight `2^i`.
pub fn get_bit(&self, i: usize) -> u8 { pub fn get_bit(&self, i: usize) -> u8 {
use crate::mem; let digitbits = <$ty>::BITS as usize;
let digitbits = mem::size_of::<$ty>() * 8;
let d = i / digitbits; let d = i / digitbits;
let b = i % digitbits; let b = i % digitbits;
((self.base[d] >> b) & 1) as u8 ((self.base[d] >> b) & 1) as u8
@ -166,8 +158,6 @@ macro_rules! define_bignum {
/// Returns the number of bits necessary to represent this value. Note that zero /// Returns the number of bits necessary to represent this value. Note that zero
/// is considered to need 0 bits. /// is considered to need 0 bits.
pub fn bit_length(&self) -> usize { pub fn bit_length(&self) -> usize {
use crate::mem;
// Skip over the most significant digits which are zero. // Skip over the most significant digits which are zero.
let digits = self.digits(); let digits = self.digits();
let zeros = digits.iter().rev().take_while(|&&x| x == 0).count(); let zeros = digits.iter().rev().take_while(|&&x| x == 0).count();
@ -180,7 +170,7 @@ macro_rules! define_bignum {
} }
// This could be optimized with leading_zeros() and bit shifts, but that's // This could be optimized with leading_zeros() and bit shifts, but that's
// probably not worth the hassle. // probably not worth the hassle.
let digitbits = mem::size_of::<$ty>() * 8; let digitbits = <$ty>::BITS as usize;
let mut i = nonzero.len() * digitbits - 1; let mut i = nonzero.len() * digitbits - 1;
while self.get_bit(i) == 0 { while self.get_bit(i) == 0 {
i -= 1; i -= 1;
@ -265,9 +255,7 @@ macro_rules! define_bignum {
/// Multiplies itself by `2^bits` and returns its own mutable reference. /// Multiplies itself by `2^bits` and returns its own mutable reference.
pub fn mul_pow2(&mut self, bits: usize) -> &mut $name { pub fn mul_pow2(&mut self, bits: usize) -> &mut $name {
use crate::mem; let digitbits = <$ty>::BITS as usize;
let digitbits = mem::size_of::<$ty>() * 8;
let digits = bits / digitbits; let digits = bits / digitbits;
let bits = bits % digitbits; let bits = bits % digitbits;
@ -393,13 +381,11 @@ macro_rules! define_bignum {
/// Divide self by another bignum, overwriting `q` with the quotient and `r` with the /// Divide self by another bignum, overwriting `q` with the quotient and `r` with the
/// remainder. /// remainder.
pub fn div_rem(&self, d: &$name, q: &mut $name, r: &mut $name) { pub fn div_rem(&self, d: &$name, q: &mut $name, r: &mut $name) {
use crate::mem;
// Stupid slow base-2 long division taken from // Stupid slow base-2 long division taken from
// https://en.wikipedia.org/wiki/Division_algorithm // https://en.wikipedia.org/wiki/Division_algorithm
// FIXME use a greater base ($ty) for the long division. // FIXME use a greater base ($ty) for the long division.
assert!(!d.is_zero()); assert!(!d.is_zero());
let digitbits = mem::size_of::<$ty>() * 8; let digitbits = <$ty>::BITS as usize;
for digit in &mut q.base[..] { for digit in &mut q.base[..] {
*digit = 0; *digit = 0;
} }
@ -462,10 +448,8 @@ macro_rules! define_bignum {
impl crate::fmt::Debug for $name { impl crate::fmt::Debug for $name {
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
use crate::mem;
let sz = if self.size < 1 { 1 } else { self.size }; let sz = if self.size < 1 { 1 } else { self.size };
let digitlen = mem::size_of::<$ty>() * 2; let digitlen = <$ty>::BITS as usize / 4;
write!(f, "{:#x}", self.base[sz - 1])?; write!(f, "{:#x}", self.base[sz - 1])?;
for &v in self.base[..sz - 1].iter().rev() { for &v in self.base[..sz - 1].iter().rev() {

View File

@ -348,6 +348,20 @@ $EndFeature, "
pub const MAX: Self = !Self::MIN; pub const MAX: Self = !Self::MIN;
} }
doc_comment! {
concat!("The size of this integer type in bits.
# Examples
```
", $Feature, "#![feature(int_bits_const)]
assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");",
$EndFeature, "
```"),
#[unstable(feature = "int_bits_const", issue = "76904")]
pub const BITS: u32 = $BITS;
}
doc_comment! { doc_comment! {
concat!("Converts a string slice in a given base to an integer. concat!("Converts a string slice in a given base to an integer.
@ -2601,6 +2615,20 @@ $EndFeature, "
pub const MAX: Self = !0; pub const MAX: Self = !0;
} }
doc_comment! {
concat!("The size of this integer type in bits.
# Examples
```
", $Feature, "#![feature(int_bits_const)]
assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");",
$EndFeature, "
```"),
#[unstable(feature = "int_bits_const", issue = "76904")]
pub const BITS: u32 = $BITS;
}
doc_comment! { doc_comment! {
concat!("Converts a string slice in a given base to an integer. concat!("Converts a string slice in a given base to an integer.

View File

@ -565,7 +565,7 @@ fn break_patterns<T>(v: &mut [T]) {
random random
}; };
let mut gen_usize = || { let mut gen_usize = || {
if mem::size_of::<usize>() <= 4 { if usize::BITS <= 32 {
gen_u32() as usize gen_u32() as usize
} else { } else {
(((gen_u32() as u64) << 32) | (gen_u32() as u64)) as usize (((gen_u32() as u64) << 32) | (gen_u32() as u64)) as usize
@ -667,7 +667,7 @@ where
/// ///
/// `limit` is the number of allowed imbalanced partitions before switching to `heapsort`. If zero, /// `limit` is the number of allowed imbalanced partitions before switching to `heapsort`. If zero,
/// this function will immediately switch to heapsort. /// this function will immediately switch to heapsort.
fn recurse<'a, T, F>(mut v: &'a mut [T], is_less: &mut F, mut pred: Option<&'a T>, mut limit: usize) fn recurse<'a, T, F>(mut v: &'a mut [T], is_less: &mut F, mut pred: Option<&'a T>, mut limit: u32)
where where
F: FnMut(&T, &T) -> bool, F: FnMut(&T, &T) -> bool,
{ {
@ -763,7 +763,7 @@ where
} }
// Limit the number of imbalanced partitions to `floor(log2(len)) + 1`. // Limit the number of imbalanced partitions to `floor(log2(len)) + 1`.
let limit = mem::size_of::<usize>() * 8 - v.len().leading_zeros() as usize; let limit = usize::BITS - v.len().leading_zeros();
recurse(v, &mut is_less, None, limit); recurse(v, &mut is_less, None, limit);
} }

View File

@ -474,7 +474,7 @@ fn test_iterator_step_by_nth_overflow() {
} }
let mut it = Test(0); let mut it = Test(0);
let root = usize::MAX >> (::std::mem::size_of::<usize>() * 8 / 2); let root = usize::MAX >> (usize::BITS / 2);
let n = root + 20; let n = root + 20;
(&mut it).step_by(n).nth(n); (&mut it).step_by(n).nth(n);
assert_eq!(it.0, n as Bigger * n as Bigger); assert_eq!(it.0, n as Bigger * n as Bigger);

View File

@ -52,6 +52,7 @@
#![feature(partition_point)] #![feature(partition_point)]
#![feature(once_cell)] #![feature(once_cell)]
#![feature(unsafe_block_in_unsafe_fn)] #![feature(unsafe_block_in_unsafe_fn)]
#![feature(int_bits_const)]
#![deny(unsafe_op_in_unsafe_fn)] #![deny(unsafe_op_in_unsafe_fn)]
extern crate test; extern crate test;

View File

@ -2,7 +2,6 @@ macro_rules! int_module {
($T:ident, $T_i:ident) => { ($T:ident, $T_i:ident) => {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::mem;
use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
use core::$T_i::*; use core::$T_i::*;
@ -82,30 +81,27 @@ macro_rules! int_module {
#[test] #[test]
fn test_count_zeros() { fn test_count_zeros() {
let bits = mem::size_of::<$T>() * 8; assert_eq!(A.count_zeros(), $T::BITS - 3);
assert_eq!(A.count_zeros(), bits as u32 - 3); assert_eq!(B.count_zeros(), $T::BITS - 2);
assert_eq!(B.count_zeros(), bits as u32 - 2); assert_eq!(C.count_zeros(), $T::BITS - 5);
assert_eq!(C.count_zeros(), bits as u32 - 5);
} }
#[test] #[test]
fn test_leading_trailing_ones() { fn test_leading_trailing_ones() {
let bits = (mem::size_of::<$T>() * 8) as u32;
let a: $T = 0b0101_1111; let a: $T = 0b0101_1111;
assert_eq!(a.trailing_ones(), 5); assert_eq!(a.trailing_ones(), 5);
assert_eq!((!a).leading_ones(), bits - 7); assert_eq!((!a).leading_ones(), $T::BITS - 7);
assert_eq!(a.reverse_bits().leading_ones(), 5); assert_eq!(a.reverse_bits().leading_ones(), 5);
assert_eq!(_1.leading_ones(), bits); assert_eq!(_1.leading_ones(), $T::BITS);
assert_eq!(_1.trailing_ones(), bits); assert_eq!(_1.trailing_ones(), $T::BITS);
assert_eq!((_1 << 1).trailing_ones(), 0); assert_eq!((_1 << 1).trailing_ones(), 0);
assert_eq!(MAX.leading_ones(), 0); assert_eq!(MAX.leading_ones(), 0);
assert_eq!((_1 << 1).leading_ones(), bits - 1); assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1);
assert_eq!(MAX.trailing_ones(), bits - 1); assert_eq!(MAX.trailing_ones(), $T::BITS - 1);
assert_eq!(_0.leading_ones(), 0); assert_eq!(_0.leading_ones(), 0);
assert_eq!(_0.trailing_ones(), 0); assert_eq!(_0.trailing_ones(), 0);

View File

@ -4,7 +4,6 @@ macro_rules! uint_module {
mod tests { mod tests {
use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
use core::$T_i::*; use core::$T_i::*;
use std::mem;
use std::str::FromStr; use std::str::FromStr;
use crate::num; use crate::num;
@ -47,30 +46,27 @@ macro_rules! uint_module {
#[test] #[test]
fn test_count_zeros() { fn test_count_zeros() {
let bits = mem::size_of::<$T>() * 8; assert!(A.count_zeros() == $T::BITS - 3);
assert!(A.count_zeros() == bits as u32 - 3); assert!(B.count_zeros() == $T::BITS - 2);
assert!(B.count_zeros() == bits as u32 - 2); assert!(C.count_zeros() == $T::BITS - 5);
assert!(C.count_zeros() == bits as u32 - 5);
} }
#[test] #[test]
fn test_leading_trailing_ones() { fn test_leading_trailing_ones() {
let bits = (mem::size_of::<$T>() * 8) as u32;
let a: $T = 0b0101_1111; let a: $T = 0b0101_1111;
assert_eq!(a.trailing_ones(), 5); assert_eq!(a.trailing_ones(), 5);
assert_eq!((!a).leading_ones(), bits - 7); assert_eq!((!a).leading_ones(), $T::BITS - 7);
assert_eq!(a.reverse_bits().leading_ones(), 5); assert_eq!(a.reverse_bits().leading_ones(), 5);
assert_eq!(_1.leading_ones(), bits); assert_eq!(_1.leading_ones(), $T::BITS);
assert_eq!(_1.trailing_ones(), bits); assert_eq!(_1.trailing_ones(), $T::BITS);
assert_eq!((_1 << 1).trailing_ones(), 0); assert_eq!((_1 << 1).trailing_ones(), 0);
assert_eq!((_1 >> 1).leading_ones(), 0); assert_eq!((_1 >> 1).leading_ones(), 0);
assert_eq!((_1 << 1).leading_ones(), bits - 1); assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1);
assert_eq!((_1 >> 1).trailing_ones(), bits - 1); assert_eq!((_1 >> 1).trailing_ones(), $T::BITS - 1);
assert_eq!(_0.leading_ones(), 0); assert_eq!(_0.leading_ones(), 0);
assert_eq!(_0.trailing_ones(), 0); assert_eq!(_0.trailing_ones(), 0);

View File

@ -53,7 +53,7 @@ impl DwarfReader {
} }
pub unsafe fn read_sleb128(&mut self) -> i64 { pub unsafe fn read_sleb128(&mut self) -> i64 {
let mut shift: usize = 0; let mut shift: u32 = 0;
let mut result: u64 = 0; let mut result: u64 = 0;
let mut byte: u8; let mut byte: u8;
loop { loop {
@ -65,7 +65,7 @@ impl DwarfReader {
} }
} }
// sign-extend // sign-extend
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 { if shift < u64::BITS && (byte & 0x40) != 0 {
result |= (!0 as u64) << shift; result |= (!0 as u64) << shift;
} }
result as i64 result as i64

View File

@ -18,6 +18,7 @@
issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/" issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/"
)] )]
#![feature(core_intrinsics)] #![feature(core_intrinsics)]
#![feature(int_bits_const)]
#![feature(lang_items)] #![feature(lang_items)]
#![feature(libc)] #![feature(libc)]
#![feature(nll)] #![feature(nll)]