Reform power_of_two methods for perf increase & semantic change to consider 0 not a power of 2.

Vec panics when attempting to reserve capacity > int::MAX (uint::MAX / 2).
This commit is contained in:
Aaron Liblong
2014-12-08 01:03:35 -05:00
parent 99d6956c3b
commit f6328b60da
4 changed files with 43 additions and 37 deletions

View File

@@ -710,20 +710,10 @@ impl<T> Vec<T> {
#[unstable = "matches collection reform specification, waiting for dust to settle"] #[unstable = "matches collection reform specification, waiting for dust to settle"]
pub fn reserve(&mut self, additional: uint) { pub fn reserve(&mut self, additional: uint) {
if self.cap - self.len < additional { if self.cap - self.len < additional {
match self.len.checked_add(additional) { let err_msg = "Vec::reserve: `uint` overflow";
None => panic!("Vec::reserve: `uint` overflow"), let new_cap = self.len.checked_add(additional).expect(err_msg)
// if the checked_add .checked_next_power_of_two().expect(err_msg);
Some(new_cap) => { self.grow_capacity(new_cap);
let amort_cap = new_cap.next_power_of_two();
// next_power_of_two will overflow to exactly 0 for really big capacities
let cap = if amort_cap == 0 {
new_cap
} else {
amort_cap
};
self.grow_capacity(cap)
}
}
} }
} }

View File

@@ -673,35 +673,30 @@ signed_int_impl! { int }
#[unstable = "recently settled as part of numerics reform"] #[unstable = "recently settled as part of numerics reform"]
pub trait UnsignedInt: Int { pub trait UnsignedInt: Int {
/// Returns `true` iff `self == 2^k` for some `k`. /// Returns `true` iff `self == 2^k` for some `k`.
#[inline]
fn is_power_of_two(self) -> bool { fn is_power_of_two(self) -> bool {
(self - Int::one()) & self == Int::zero() (self - Int::one()) & self == Int::zero() && !(self == Int::zero())
} }
/// Returns the smallest power of two greater than or equal to `self`. /// Returns the smallest power of two greater than or equal to `self`.
/// Unspecified behavior on overflow.
#[inline] #[inline]
fn next_power_of_two(self) -> Self { fn next_power_of_two(self) -> Self {
let halfbits = size_of::<Self>() * 4; let bits = size_of::<Self>() * 8;
let mut tmp = self - Int::one(); let one: Self = Int::one();
let mut shift = 1u; one << ((bits - (self - one).leading_zeros()) % bits)
while shift <= halfbits {
tmp = tmp | (tmp >> shift);
shift = shift << 1u;
}
tmp + Int::one()
} }
/// Returns the smallest power of two greater than or equal to `n`. If the /// Returns the smallest power of two greater than or equal to `n`. If the
/// next power of two is greater than the type's maximum value, `None` is /// next power of two is greater than the type's maximum value, `None` is
/// returned, otherwise the power of two is wrapped in `Some`. /// returned, otherwise the power of two is wrapped in `Some`.
fn checked_next_power_of_two(self) -> Option<Self> { fn checked_next_power_of_two(self) -> Option<Self> {
let halfbits = size_of::<Self>() * 4; let npot = self.next_power_of_two();
let mut tmp = self - Int::one(); if npot >= self {
let mut shift = 1u; Some(npot)
while shift <= halfbits { } else {
tmp = tmp | (tmp >> shift); None
shift = shift << 1u;
} }
tmp.checked_add(Int::one())
} }
} }

View File

@@ -623,10 +623,10 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
/// Resizes the internal vectors to a new capacity. It's your responsibility to: /// Resizes the internal vectors to a new capacity. It's your responsibility to:
/// 1) Make sure the new capacity is enough for all the elements, accounting /// 1) Make sure the new capacity is enough for all the elements, accounting
/// for the load factor. /// for the load factor.
/// 2) Ensure new_capacity is a power of two. /// 2) Ensure new_capacity is a power of two or zero.
fn resize(&mut self, new_capacity: uint) { fn resize(&mut self, new_capacity: uint) {
assert!(self.table.size() <= new_capacity); assert!(self.table.size() <= new_capacity);
assert!(new_capacity.is_power_of_two()); assert!(new_capacity.is_power_of_two() || new_capacity == 0);
let mut old_table = replace(&mut self.table, RawTable::new(new_capacity)); let mut old_table = replace(&mut self.table, RawTable::new(new_capacity));
let old_size = old_table.size(); let old_size = old_table.size();

View File

@@ -664,11 +664,32 @@ mod tests {
assert_eq!(third.checked_mul(4), None); assert_eq!(third.checked_mul(4), None);
} }
macro_rules! test_is_power_of_two {
($test_name:ident, $T:ident) => (
fn $test_name() {
#![test]
assert_eq!((0 as $T).is_power_of_two(), false);
assert_eq!((1 as $T).is_power_of_two(), true);
assert_eq!((2 as $T).is_power_of_two(), true);
assert_eq!((3 as $T).is_power_of_two(), false);
assert_eq!((4 as $T).is_power_of_two(), true);
assert_eq!((5 as $T).is_power_of_two(), false);
assert!(($T::MAX / 2 + 1).is_power_of_two(), true);
}
)
}
test_is_power_of_two!{ test_is_power_of_two_u8, u8 }
test_is_power_of_two!{ test_is_power_of_two_u16, u16 }
test_is_power_of_two!{ test_is_power_of_two_u32, u32 }
test_is_power_of_two!{ test_is_power_of_two_u64, u64 }
test_is_power_of_two!{ test_is_power_of_two_uint, uint }
macro_rules! test_next_power_of_two { macro_rules! test_next_power_of_two {
($test_name:ident, $T:ident) => ( ($test_name:ident, $T:ident) => (
fn $test_name() { fn $test_name() {
#![test] #![test]
assert_eq!((0 as $T).next_power_of_two(), 0); assert_eq!((0 as $T).next_power_of_two(), 1);
let mut next_power = 1; let mut next_power = 1;
for i in range::<$T>(1, 40) { for i in range::<$T>(1, 40) {
assert_eq!(i.next_power_of_two(), next_power); assert_eq!(i.next_power_of_two(), next_power);
@@ -688,15 +709,15 @@ mod tests {
($test_name:ident, $T:ident) => ( ($test_name:ident, $T:ident) => (
fn $test_name() { fn $test_name() {
#![test] #![test]
assert_eq!((0 as $T).checked_next_power_of_two(), None); assert_eq!((0 as $T).checked_next_power_of_two(), Some(1));
assert!(($T::MAX / 2).checked_next_power_of_two().is_some());
assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None);
assert_eq!($T::MAX.checked_next_power_of_two(), None);
let mut next_power = 1; let mut next_power = 1;
for i in range::<$T>(1, 40) { for i in range::<$T>(1, 40) {
assert_eq!(i.checked_next_power_of_two(), Some(next_power)); assert_eq!(i.checked_next_power_of_two(), Some(next_power));
if i == next_power { next_power *= 2 } if i == next_power { next_power *= 2 }
} }
assert!(($T::MAX / 2).checked_next_power_of_two().is_some());
assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None);
assert_eq!($T::MAX.checked_next_power_of_two(), None);
} }
) )
} }