aboutsummaryrefslogtreecommitdiff
path: root/ctr-std/src/collections
diff options
context:
space:
mode:
authorValentin <[email protected]>2018-06-15 18:57:24 +0200
committerFenrirWolf <[email protected]>2018-06-15 10:57:24 -0600
commitf2a90174bb36b9ad528e863ab34c02ebce002b02 (patch)
tree959e8d67883d3a89e179b3549b1f30d28e51a87c /ctr-std/src/collections
parentMerge pull request #68 from linouxis9/master (diff)
downloadctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.tar.xz
ctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.zip
Update for latest nightly 2018-06-09 (#70)
* Update for latest nightly 2018-06-09 * We now have a proper horizon os and sys modules in libstd
Diffstat (limited to 'ctr-std/src/collections')
-rw-r--r--ctr-std/src/collections/hash/map.rs60
-rw-r--r--ctr-std/src/collections/hash/table.rs166
-rw-r--r--ctr-std/src/collections/mod.rs8
3 files changed, 98 insertions, 136 deletions
diff --git a/ctr-std/src/collections/hash/map.rs b/ctr-std/src/collections/hash/map.rs
index a7eb002..9c77acb 100644
--- a/ctr-std/src/collections/hash/map.rs
+++ b/ctr-std/src/collections/hash/map.rs
@@ -11,7 +11,7 @@
use self::Entry::*;
use self::VacantEntryState::*;
-use alloc::{CollectionAllocErr, oom};
+use alloc::CollectionAllocErr;
use cell::Cell;
use borrow::Borrow;
use cmp::max;
@@ -23,8 +23,10 @@ use mem::{self, replace};
use ops::{Deref, Index};
use sys;
-use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash};
+use super::table::{self, Bucket, EmptyBucket, Fallibility, FullBucket, FullBucketMut, RawTable,
+ SafeHash};
use super::table::BucketState::{Empty, Full};
+use super::table::Fallibility::{Fallible, Infallible};
const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two
@@ -783,11 +785,11 @@ impl<K, V, S> HashMap<K, V, S>
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn reserve(&mut self, additional: usize) {
- match self.try_reserve(additional) {
+ match self.reserve_internal(additional, Infallible) {
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
- Err(CollectionAllocErr::AllocErr) => oom(),
+ Err(CollectionAllocErr::AllocErr) => unreachable!(),
Ok(()) => { /* yay */ }
- }
+ }
}
/// Tries to reserve capacity for at least `additional` more elements to be inserted
@@ -809,17 +811,24 @@ impl<K, V, S> HashMap<K, V, S>
/// ```
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> {
+ self.reserve_internal(additional, Fallible)
+ }
+
+ fn reserve_internal(&mut self, additional: usize, fallibility: Fallibility)
+ -> Result<(), CollectionAllocErr> {
+
let remaining = self.capacity() - self.len(); // this can't overflow
if remaining < additional {
- let min_cap = self.len().checked_add(additional)
+ let min_cap = self.len()
+ .checked_add(additional)
.ok_or(CollectionAllocErr::CapacityOverflow)?;
let raw_cap = self.resize_policy.try_raw_capacity(min_cap)?;
- self.try_resize(raw_cap)?;
+ self.try_resize(raw_cap, fallibility)?;
} else if self.table.tag() && remaining <= self.len() {
// Probe sequence is too long and table is half full,
// resize early to reduce probing length.
let new_capacity = self.table.capacity() * 2;
- self.try_resize(new_capacity)?;
+ self.try_resize(new_capacity, fallibility)?;
}
Ok(())
}
@@ -831,11 +840,21 @@ impl<K, V, S> HashMap<K, V, S>
/// 2) Ensure `new_raw_cap` is a power of two or zero.
#[inline(never)]
#[cold]
- fn try_resize(&mut self, new_raw_cap: usize) -> Result<(), CollectionAllocErr> {
+ fn try_resize(
+ &mut self,
+ new_raw_cap: usize,
+ fallibility: Fallibility,
+ ) -> Result<(), CollectionAllocErr> {
assert!(self.table.size() <= new_raw_cap);
assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0);
- let mut old_table = replace(&mut self.table, RawTable::try_new(new_raw_cap)?);
+ let mut old_table = replace(
+ &mut self.table,
+ match fallibility {
+ Infallible => RawTable::new(new_raw_cap),
+ Fallible => RawTable::try_new(new_raw_cap)?,
+ }
+ );
let old_size = old_table.size();
if old_table.size() == 0 {
@@ -2142,14 +2161,13 @@ impl<'a, K, V> Entry<'a, K, V> {
}
impl<'a, K, V: Default> Entry<'a, K, V> {
- #[unstable(feature = "entry_or_default", issue = "44324")]
+ #[stable(feature = "entry_or_default", since = "1.28.0")]
/// Ensures a value is in the entry by inserting the default value if empty,
/// and returns a mutable reference to the value in the entry.
///
/// # Examples
///
/// ```
- /// #![feature(entry_or_default)]
/// # fn main() {
/// use std::collections::HashMap;
///
@@ -2165,7 +2183,6 @@ impl<'a, K, V: Default> Entry<'a, K, V> {
Vacant(entry) => entry.insert(Default::default()),
}
}
-
}
impl<'a, K, V> OccupiedEntry<'a, K, V> {
@@ -2231,6 +2248,11 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
/// Gets a mutable reference to the value in the entry.
///
+ /// If you need a reference to the `OccupiedEntry` which may outlive the
+ /// destruction of the `Entry` value, see [`into_mut`].
+ ///
+ /// [`into_mut`]: #method.into_mut
+ ///
/// # Examples
///
/// ```
@@ -2242,10 +2264,14 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
///
/// assert_eq!(map["poneyland"], 12);
/// if let Entry::Occupied(mut o) = map.entry("poneyland") {
- /// *o.get_mut() += 10;
+ /// *o.get_mut() += 10;
+ /// assert_eq!(*o.get(), 22);
+ ///
+ /// // We can use the same Entry multiple times.
+ /// *o.get_mut() += 2;
/// }
///
- /// assert_eq!(map["poneyland"], 22);
+ /// assert_eq!(map["poneyland"], 24);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn get_mut(&mut self) -> &mut V {
@@ -2255,6 +2281,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
/// Converts the OccupiedEntry into a mutable reference to the value in the entry
/// with a lifetime bound to the map itself.
///
+ /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`].
+ ///
+ /// [`get_mut`]: #method.get_mut
+ ///
/// # Examples
///
/// ```
diff --git a/ctr-std/src/collections/hash/table.rs b/ctr-std/src/collections/hash/table.rs
index b50652e..d997fb2 100644
--- a/ctr-std/src/collections/hash/table.rs
+++ b/ctr-std/src/collections/hash/table.rs
@@ -8,14 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use alloc::{Global, Alloc, Layout, CollectionAllocErr, oom};
-use cmp;
+use alloc::{Global, Alloc, Layout, LayoutErr, CollectionAllocErr, oom};
use hash::{BuildHasher, Hash, Hasher};
use marker;
-use mem::{align_of, size_of, needs_drop};
+use mem::{size_of, needs_drop};
use mem;
use ops::{Deref, DerefMut};
use ptr::{self, Unique, NonNull};
+use hint;
use self::BucketState::*;
@@ -651,71 +651,39 @@ impl<K, V, M> GapThenFull<K, V, M>
}
}
-
-/// Rounds up to a multiple of a power of two. Returns the closest multiple
-/// of `target_alignment` that is higher or equal to `unrounded`.
-///
-/// # Panics
-///
-/// Panics if `target_alignment` is not a power of two.
-#[inline]
-fn round_up_to_next(unrounded: usize, target_alignment: usize) -> usize {
- assert!(target_alignment.is_power_of_two());
- (unrounded + target_alignment - 1) & !(target_alignment - 1)
-}
-
-#[test]
-fn test_rounding() {
- assert_eq!(round_up_to_next(0, 4), 0);
- assert_eq!(round_up_to_next(1, 4), 4);
- assert_eq!(round_up_to_next(2, 4), 4);
- assert_eq!(round_up_to_next(3, 4), 4);
- assert_eq!(round_up_to_next(4, 4), 4);
- assert_eq!(round_up_to_next(5, 4), 8);
-}
-
-// Returns a tuple of (pairs_offset, end_of_pairs_offset),
-// from the start of a mallocated array.
-#[inline]
-fn calculate_offsets(hashes_size: usize,
- pairs_size: usize,
- pairs_align: usize)
- -> (usize, usize, bool) {
- let pairs_offset = round_up_to_next(hashes_size, pairs_align);
- let (end_of_pairs, oflo) = pairs_offset.overflowing_add(pairs_size);
-
- (pairs_offset, end_of_pairs, oflo)
+// Returns a Layout which describes the allocation required for a hash table,
+// and the offset of the array of (key, value) pairs in the allocation.
+fn calculate_layout<K, V>(capacity: usize) -> Result<(Layout, usize), LayoutErr> {
+ let hashes = Layout::array::<HashUint>(capacity)?;
+ let pairs = Layout::array::<(K, V)>(capacity)?;
+ hashes.extend(pairs).map(|(layout, _)| {
+ // LLVM seems to have trouble properly const-propagating pairs.align(),
+ // possibly due to the use of NonZeroUsize. This little hack allows it
+ // to generate optimal code.
+ //
+ // See https://github.com/rust-lang/rust/issues/51346 for more details.
+ (
+ layout,
+ hashes.size() + hashes.padding_needed_for(mem::align_of::<(K, V)>()),
+ )
+ })
}
-// Returns a tuple of (minimum required malloc alignment,
-// array_size), from the start of a mallocated array.
-fn calculate_allocation(hash_size: usize,
- hash_align: usize,
- pairs_size: usize,
- pairs_align: usize)
- -> (usize, usize, bool) {
- let (_, end_of_pairs, oflo) = calculate_offsets(hash_size, pairs_size, pairs_align);
-
- let align = cmp::max(hash_align, pairs_align);
-
- (align, end_of_pairs, oflo)
+pub(crate) enum Fallibility {
+ Fallible,
+ Infallible,
}
-#[test]
-fn test_offset_calculation() {
- assert_eq!(calculate_allocation(128, 8, 16, 8), (8, 144, false));
- assert_eq!(calculate_allocation(3, 1, 2, 1), (1, 5, false));
- assert_eq!(calculate_allocation(6, 2, 12, 4), (4, 20, false));
- assert_eq!(calculate_offsets(128, 15, 4), (128, 143, false));
- assert_eq!(calculate_offsets(3, 2, 4), (4, 6, false));
- assert_eq!(calculate_offsets(6, 12, 4), (8, 20, false));
-}
+use self::Fallibility::*;
impl<K, V> RawTable<K, V> {
/// Does not initialize the buckets. The caller should ensure they,
/// at the very least, set every hash to EMPTY_BUCKET.
/// Returns an error if it cannot allocate or capacity overflows.
- unsafe fn try_new_uninitialized(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> {
+ unsafe fn new_uninitialized_internal(
+ capacity: usize,
+ fallibility: Fallibility,
+ ) -> Result<RawTable<K, V>, CollectionAllocErr> {
if capacity == 0 {
return Ok(RawTable {
size: 0,
@@ -725,37 +693,15 @@ impl<K, V> RawTable<K, V> {
});
}
- // No need for `checked_mul` before a more restrictive check performed
- // later in this method.
- let hashes_size = capacity.wrapping_mul(size_of::<HashUint>());
- let pairs_size = capacity.wrapping_mul(size_of::<(K, V)>());
-
// Allocating hashmaps is a little tricky. We need to allocate two
// arrays, but since we know their sizes and alignments up front,
// we just allocate a single array, and then have the subarrays
// point into it.
- //
- // This is great in theory, but in practice getting the alignment
- // right is a little subtle. Therefore, calculating offsets has been
- // factored out into a different function.
- let (alignment, size, oflo) = calculate_allocation(hashes_size,
- align_of::<HashUint>(),
- pairs_size,
- align_of::<(K, V)>());
- if oflo {
- return Err(CollectionAllocErr::CapacityOverflow);
- }
-
- // One check for overflow that covers calculation and rounding of size.
- let size_of_bucket = size_of::<HashUint>().checked_add(size_of::<(K, V)>())
- .ok_or(CollectionAllocErr::CapacityOverflow)?;
- let capacity_mul_size_of_bucket = capacity.checked_mul(size_of_bucket);
- if capacity_mul_size_of_bucket.is_none() || size < capacity_mul_size_of_bucket.unwrap() {
- return Err(CollectionAllocErr::CapacityOverflow);
- }
-
- let buffer = Global.alloc(Layout::from_size_align(size, alignment)
- .map_err(|_| CollectionAllocErr::CapacityOverflow)?)?;
+ let (layout, _) = calculate_layout::<K, V>(capacity)?;
+ let buffer = Global.alloc(layout).map_err(|e| match fallibility {
+ Infallible => oom(layout),
+ Fallible => e,
+ })?;
Ok(RawTable {
capacity_mask: capacity.wrapping_sub(1),
@@ -768,48 +714,50 @@ impl<K, V> RawTable<K, V> {
/// Does not initialize the buckets. The caller should ensure they,
/// at the very least, set every hash to EMPTY_BUCKET.
unsafe fn new_uninitialized(capacity: usize) -> RawTable<K, V> {
- match Self::try_new_uninitialized(capacity) {
+ match Self::new_uninitialized_internal(capacity, Infallible) {
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
- Err(CollectionAllocErr::AllocErr) => oom(),
+ Err(CollectionAllocErr::AllocErr) => unreachable!(),
Ok(table) => { table }
}
}
fn raw_bucket_at(&self, index: usize) -> RawBucket<K, V> {
- let hashes_size = self.capacity() * size_of::<HashUint>();
- let pairs_size = self.capacity() * size_of::<(K, V)>();
-
- let (pairs_offset, _, oflo) =
- calculate_offsets(hashes_size, pairs_size, align_of::<(K, V)>());
- debug_assert!(!oflo, "capacity overflow");
-
+ let (_, pairs_offset) = calculate_layout::<K, V>(self.capacity())
+ .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() });
let buffer = self.hashes.ptr() as *mut u8;
unsafe {
RawBucket {
hash_start: buffer as *mut HashUint,
- pair_start: buffer.offset(pairs_offset as isize) as *const (K, V),
+ pair_start: buffer.add(pairs_offset) as *const (K, V),
idx: index,
_marker: marker::PhantomData,
}
}
}
- /// Tries to create a new raw table from a given capacity. If it cannot allocate,
- /// it returns with AllocErr.
- pub fn try_new(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> {
+ fn new_internal(
+ capacity: usize,
+ fallibility: Fallibility,
+ ) -> Result<RawTable<K, V>, CollectionAllocErr> {
unsafe {
- let ret = RawTable::try_new_uninitialized(capacity)?;
+ let ret = RawTable::new_uninitialized_internal(capacity, fallibility)?;
ptr::write_bytes(ret.hashes.ptr(), 0, capacity);
Ok(ret)
}
}
+ /// Tries to create a new raw table from a given capacity. If it cannot allocate,
+ /// it returns with AllocErr.
+ pub fn try_new(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> {
+ Self::new_internal(capacity, Fallible)
+ }
+
/// Creates a new raw table from a given capacity. All buckets are
/// initially empty.
pub fn new(capacity: usize) -> RawTable<K, V> {
- match Self::try_new(capacity) {
+ match Self::new_internal(capacity, Infallible) {
Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"),
- Err(CollectionAllocErr::AllocErr) => oom(),
+ Err(CollectionAllocErr::AllocErr) => unreachable!(),
Ok(table) => { table }
}
}
@@ -1173,18 +1121,10 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
}
}
- let hashes_size = self.capacity() * size_of::<HashUint>();
- let pairs_size = self.capacity() * size_of::<(K, V)>();
- let (align, size, oflo) = calculate_allocation(hashes_size,
- align_of::<HashUint>(),
- pairs_size,
- align_of::<(K, V)>());
-
- debug_assert!(!oflo, "should be impossible");
-
+ let (layout, _) = calculate_layout::<K, V>(self.capacity())
+ .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() });
unsafe {
- Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(),
- Layout::from_size_align(size, align).unwrap());
+ Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(), layout);
// Remember how everything was allocated out of one buffer
// during initialization? We only need one call to free here.
}
diff --git a/ctr-std/src/collections/mod.rs b/ctr-std/src/collections/mod.rs
index 9cf7382..d8e79b9 100644
--- a/ctr-std/src/collections/mod.rs
+++ b/ctr-std/src/collections/mod.rs
@@ -437,14 +437,6 @@ pub use self::hash_map::HashMap;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::hash_set::HashSet;
-#[unstable(feature = "collections_range", issue = "30877")]
-#[rustc_deprecated(reason = "renamed and moved to `std::ops::RangeBounds`", since = "1.26.0")]
-#[doc(hidden)]
-/// Range syntax
-pub mod range {
- pub use ops::RangeBounds as RangeArgument;
-}
-
#[unstable(feature = "try_reserve", reason = "new API", issue="48043")]
pub use heap::CollectionAllocErr;