diff options
| author | Marcin Fatyga <marcinf@google.com> | 2016-11-01 15:26:22 +0100 |
|---|---|---|
| committer | Marcin Fatyga <marcinf@google.com> | 2016-11-01 15:26:22 +0100 |
| commit | 655effedf25e2039d283b839429bf2f42b7012a4 (patch) | |
| tree | 34fd087d891556c70a14b26a90d1bdccd0a7ccb2 /src/libstd | |
| parent | 4e2822c5c28bb342e5862ba7cc0b90b865c68be1 (diff) | |
| parent | ac968c466451cb9aafd9e8598ddb396ed0e6fe31 (diff) | |
| download | rust-655effedf25e2039d283b839429bf2f42b7012a4.tar.gz rust-655effedf25e2039d283b839429bf2f42b7012a4.zip | |
Merge branch 'master' of https://github.com/rust-lang/rust
Conflicts: src/libcoretest/lib.rs
Diffstat (limited to 'src/libstd')
95 files changed, 3703 insertions, 1730 deletions
diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 2d540c6b59a..72cd6e4830b 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -23,10 +23,10 @@ fn main() { println!("cargo:rustc-cfg=cargobuild"); println!("cargo:rerun-if-changed=build.rs"); - let target = env::var("TARGET").unwrap(); - let host = env::var("HOST").unwrap(); + let target = env::var("TARGET").expect("TARGET was not set"); + let host = env::var("HOST").expect("HOST was not set"); if cfg!(feature = "backtrace") && !target.contains("apple") && !target.contains("msvc") && - !target.contains("emscripten") { + !target.contains("emscripten") && !target.contains("fuchsia") { build_libbacktrace(&host, &target); } @@ -58,6 +58,8 @@ fn main() { println!("cargo:rustc-link-lib=ws2_32"); println!("cargo:rustc-link-lib=userenv"); println!("cargo:rustc-link-lib=shell32"); + } else if target.contains("fuchsia") { + println!("cargo:rustc-link-lib=magenta"); } } @@ -103,5 +105,5 @@ fn build_libbacktrace(host: &str, target: &str) { run(Command::new("make") .current_dir(&build_dir) .arg(format!("INCDIR={}", src_dir.display())) - .arg("-j").arg(env::var("NUM_JOBS").unwrap())); + .arg("-j").arg(env::var("NUM_JOBS").expect("NUM_JOBS was not set"))); } diff --git a/src/libstd/collections/hash/bench.rs b/src/libstd/collections/hash/bench.rs index a1275d23d57..ff6cb7985a4 100644 --- a/src/libstd/collections/hash/bench.rs +++ b/src/libstd/collections/hash/bench.rs @@ -15,17 +15,17 @@ extern crate test; use self::test::Bencher; #[bench] -fn new_drop(b : &mut Bencher) { +fn new_drop(b: &mut Bencher) { use super::map::HashMap; b.iter(|| { - let m : HashMap<i32, i32> = HashMap::new(); + let m: HashMap<i32, i32> = HashMap::new(); assert_eq!(m.len(), 0); }) } #[bench] -fn new_insert_drop(b : &mut Bencher) { +fn new_insert_drop(b: &mut Bencher) { use super::map::HashMap; b.iter(|| { diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index eb1653f18cb..fb8a0c3c265 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -14,33 +14,19 @@ use self::VacantEntryState::*; use borrow::Borrow; use cmp::max; use fmt::{self, Debug}; +#[allow(deprecated)] use hash::{Hash, Hasher, BuildHasher, SipHasher13}; use iter::{FromIterator, FusedIterator}; use mem::{self, replace}; use ops::{Deref, Index}; use rand::{self, Rng}; -use super::table::{ - self, - Bucket, - EmptyBucket, - FullBucket, - FullBucketMut, - RawTable, - SafeHash -}; -use super::table::BucketState::{ - Empty, - Full, -}; - -const INITIAL_LOG2_CAP: usize = 5; -const INITIAL_CAPACITY: usize = 1 << INITIAL_LOG2_CAP; // 2^5 - -/// The default behavior of HashMap implements a load factor of 90.9%. -/// This behavior is characterized by the following condition: -/// -/// - if size > 0.909 * capacity: grow the map +use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash}; +use super::table::BucketState::{Empty, Full}; + +const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two + +/// The default behavior of HashMap implements a maximum load factor of 90.9%. #[derive(Clone)] struct DefaultResizePolicy; @@ -49,40 +35,35 @@ impl DefaultResizePolicy { DefaultResizePolicy } + /// A hash map's "capacity" is the number of elements it can hold without + /// being resized. Its "raw capacity" is the number of slots required to + /// provide that capacity, accounting for maximum loading. The raw capacity + /// is always zero or a power of two. #[inline] - fn min_capacity(&self, usable_size: usize) -> usize { - // Here, we are rephrasing the logic by specifying the lower limit - // on capacity: - // - // - if `cap < size * 1.1`: grow the map - usable_size * 11 / 10 + fn raw_capacity(&self, len: usize) -> usize { + if len == 0 { + 0 + } else { + // 1. Account for loading: `raw_capacity >= len * 1.1`. + // 2. Ensure it is a power of two. + // 3. Ensure it is at least the minimum size. + let mut raw_cap = len * 11 / 10; + assert!(raw_cap >= len, "raw_cap overflow"); + raw_cap = raw_cap.checked_next_power_of_two().expect("raw_capacity overflow"); + raw_cap = max(MIN_NONZERO_RAW_CAPACITY, raw_cap); + raw_cap + } } - /// An inverse of `min_capacity`, approximately. + /// The capacity of the given raw capacity. #[inline] - fn usable_capacity(&self, cap: usize) -> usize { - // As the number of entries approaches usable capacity, - // min_capacity(size) must be smaller than the internal capacity, - // so that the map is not resized: - // `min_capacity(usable_capacity(x)) <= x`. - // The left-hand side can only be smaller due to flooring by integer - // division. - // + fn capacity(&self, raw_cap: usize) -> usize { // This doesn't have to be checked for overflow since allocation size // in bytes will overflow earlier than multiplication by 10. // // As per https://github.com/rust-lang/rust/pull/30991 this is updated - // to be: (cap * den + den - 1) / num - (cap * 10 + 10 - 1) / 11 - } -} - -#[test] -fn test_resize_policy() { - let rp = DefaultResizePolicy; - for n in 0..1000 { - assert!(rp.min_capacity(rp.usable_capacity(n)) <= n); - assert!(rp.usable_capacity(rp.min_capacity(n)) <= n); + // to be: (raw_cap * den + den - 1) / num + (raw_cap * 10 + 10 - 1) / 11 } } @@ -196,15 +177,29 @@ fn test_resize_policy() { // // FIXME(Gankro, pczarn): review the proof and put it all in a separate README.md -/// A hash map implementation which uses linear probing with Robin -/// Hood bucket stealing. +/// A hash map implementation which uses linear probing with Robin Hood bucket +/// stealing. /// -/// By default, HashMap uses a somewhat slow hashing algorithm which can provide resistance -/// to DoS attacks. Rust makes a best attempt at acquiring random numbers without IO -/// blocking from your system. Because of this HashMap is not guaranteed to provide -/// DoS resistance since the numbers generated might not be truly random. If you do -/// require this behavior you can create your own hashing function using -/// [BuildHasherDefault](../hash/struct.BuildHasherDefault.html). +/// By default, `HashMap` uses a hashing algorithm selected to provide +/// resistance against HashDoS attacks. The algorithm is randomly seeded, and a +/// reasonable best-effort is made to generate this seed from a high quality, +/// secure source of randomness provided by the host without blocking the +/// program. Because of this, the randomness of the seed is dependant on the +/// quality of the system's random number generator at the time it is created. +/// In particular, seeds generated when the system's entropy pool is abnormally +/// low such as during system boot may be of a lower quality. +/// +/// The default hashing algorithm is currently SipHash 1-3, though this is +/// subject to change at any point in the future. While its performance is very +/// competitive for medium sized keys, other hashing algorithms will outperform +/// it for small keys such as integers as well as large keys such as long +/// strings, though those algorithms will typically *not* protect against +/// attacks such as HashDoS. +/// +/// The hashing algorithm can be replaced on a per-`HashMap` basis using the +/// `HashMap::default`, `HashMap::with_hasher`, and +/// `HashMap::with_capacity_and_hasher` methods. Many alternative algorithms +/// are available on crates.io, such as the `fnv` crate. /// /// It is required that the keys implement the [`Eq`] and [`Hash`] traits, although /// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`. @@ -335,6 +330,22 @@ fn test_resize_policy() { /// println!("{:?} has {} hp", viking, health); /// } /// ``` +/// +/// A HashMap with fixed list of elements can be initialized from an array: +/// +/// ``` +/// use std::collections::HashMap; +/// +/// fn main() { +/// let timber_resources: HashMap<&str, i32> = +/// [("Norway", 100), +/// ("Denmark", 50), +/// ("Iceland", 10)] +/// .iter().cloned().collect(); +/// // use the values stored in map +/// } +/// ``` + #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashMap<K, V, S = RandomState> { @@ -348,12 +359,9 @@ pub struct HashMap<K, V, S = RandomState> { /// Search for a pre-hashed key. #[inline] -fn search_hashed<K, V, M, F>(table: M, - hash: SafeHash, - mut is_match: F) - -> InternalEntry<K, V, M> where - M: Deref<Target=RawTable<K, V>>, - F: FnMut(&K) -> bool, +fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> InternalEntry<K, V, M> + where M: Deref<Target = RawTable<K, V>>, + F: FnMut(&K) -> bool { // This is the only function where capacity can be zero. To avoid // undefined behavior when Bucket::new gets the raw bucket in this @@ -375,7 +383,7 @@ fn search_hashed<K, V, M, F>(table: M, elem: NoElem(bucket), }; } - Full(bucket) => bucket + Full(bucket) => bucket, }; let robin_ib = full.index() as isize - full.displacement() as isize; @@ -394,9 +402,7 @@ fn search_hashed<K, V, M, F>(table: M, if hash == full.hash() { // If the key doesn't match, it can't be this one.. if is_match(full.read().0) { - return InternalEntry::Occupied { - elem: full - }; + return InternalEntry::Occupied { elem: full }; } } @@ -409,13 +415,13 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) { let (empty, retkey, retval) = starting_bucket.take(); let mut gap = match empty.gap_peek() { Some(b) => b, - None => return (retkey, retval) + None => return (retkey, retval), }; while gap.full().displacement() != 0 { gap = match gap.shift() { Some(b) => b, - None => break + None => break, }; } @@ -429,11 +435,11 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) { /// /// `hash`, `k`, and `v` are the elements to "robin hood" into the hashtable. fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, - mut ib: usize, - mut hash: SafeHash, - mut key: K, - mut val: V) - -> &'a mut V { + mut ib: usize, + mut hash: SafeHash, + mut key: K, + mut val: V) + -> &'a mut V { let starting_index = bucket.index(); let size = bucket.table().size(); // Save the *starting point*. @@ -465,8 +471,8 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, // FullBucketMut, into just one FullBucketMut. The "table" // refers to the inner FullBucketMut in this context. return bucket.into_table().into_mut_refs().1; - }, - Full(bucket) => bucket + } + Full(bucket) => bucket, }; let probe_ib = full_bucket.index() - full_bucket.displacement(); @@ -483,9 +489,12 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, } impl<K, V, S> HashMap<K, V, S> - where K: Eq + Hash, S: BuildHasher + where K: Eq + Hash, + S: BuildHasher { - fn make_hash<X: ?Sized>(&self, x: &X) -> SafeHash where X: Hash { + fn make_hash<X: ?Sized>(&self, x: &X) -> SafeHash + where X: Hash + { table::make_hash(&self.hash_builder, x) } @@ -494,7 +503,8 @@ impl<K, V, S> HashMap<K, V, S> /// search_hashed. #[inline] fn search<'a, Q: ?Sized>(&'a self, q: &Q) -> InternalEntry<K, V, &'a RawTable<K, V>> - where K: Borrow<Q>, Q: Eq + Hash + where K: Borrow<Q>, + Q: Eq + Hash { let hash = self.make_hash(q); search_hashed(&self.table, hash, |k| q.eq(k.borrow())) @@ -502,7 +512,8 @@ impl<K, V, S> HashMap<K, V, S> #[inline] fn search_mut<'a, Q: ?Sized>(&'a mut self, q: &Q) -> InternalEntry<K, V, &'a mut RawTable<K, V>> - where K: Borrow<Q>, Q: Eq + Hash + where K: Borrow<Q>, + Q: Eq + Hash { let hash = self.make_hash(q); search_hashed(&mut self.table, hash, |k| q.eq(k.borrow())) @@ -510,11 +521,11 @@ impl<K, V, S> HashMap<K, V, S> // The caller should ensure that invariants by Robin Hood Hashing hold. fn insert_hashed_ordered(&mut self, hash: SafeHash, k: K, v: V) { - let cap = self.table.capacity(); + let raw_cap = self.raw_capacity(); let mut buckets = Bucket::new(&mut self.table, hash); let ib = buckets.index(); - while buckets.index() != ib + cap { + while buckets.index() != ib + raw_cap { // We don't need to compare hashes for value swap. // Not even DIBs for Robin Hood. buckets = match buckets.peek() { @@ -522,7 +533,7 @@ impl<K, V, S> HashMap<K, V, S> empty.put(hash, k, v); return; } - Full(b) => b.into_bucket() + Full(b) => b.into_bucket(), }; buckets.next(); } @@ -545,7 +556,10 @@ impl<K: Hash + Eq, V> HashMap<K, V, RandomState> { Default::default() } - /// Creates an empty `HashMap` with the given initial capacity. + /// Creates an empty `HashMap` with the specified capacity. + /// + /// The hash map will be able to hold at least `capacity` elements without + /// reallocating. If `capacity` is 0, the hash map will not allocate. /// /// # Examples /// @@ -561,7 +575,8 @@ impl<K: Hash + Eq, V> HashMap<K, V, RandomState> { } impl<K, V, S> HashMap<K, V, S> - where K: Eq + Hash, S: BuildHasher + where K: Eq + Hash, + S: BuildHasher { /// Creates an empty `HashMap` which will use the given hash builder to hash /// keys. @@ -593,9 +608,11 @@ impl<K, V, S> HashMap<K, V, S> } } - /// Creates an empty `HashMap` with space for at least `capacity` - /// elements, using `hasher` to hash the keys. + /// Creates an empty `HashMap` with the specified capacity, using `hasher` + /// to hash the keys. /// + /// The hash map will be able to hold at least `capacity` elements without + /// reallocating. If `capacity` is 0, the hash map will not allocate. /// Warning: `hasher` is normally randomly generated, and /// is designed to allow HashMaps to be resistant to attacks that /// cause many collisions and very poor performance. Setting it @@ -613,16 +630,13 @@ impl<K, V, S> HashMap<K, V, S> /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) - -> HashMap<K, V, S> { + pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashMap<K, V, S> { let resize_policy = DefaultResizePolicy::new(); - let min_cap = max(INITIAL_CAPACITY, resize_policy.min_capacity(capacity)); - let internal_cap = min_cap.checked_next_power_of_two().expect("capacity overflow"); - assert!(internal_cap >= capacity, "capacity overflow"); + let raw_cap = resize_policy.raw_capacity(capacity); HashMap { hash_builder: hash_builder, resize_policy: resize_policy, - table: RawTable::new(internal_cap), + table: RawTable::new(raw_cap), } } @@ -647,7 +661,13 @@ impl<K, V, S> HashMap<K, V, S> #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn capacity(&self) -> usize { - self.resize_policy.usable_capacity(self.table.capacity()) + self.resize_policy.capacity(self.raw_capacity()) + } + + /// Returns the hash map's raw capacity. + #[inline] + fn raw_capacity(&self) -> usize { + self.table.capacity() } /// Reserves capacity for at least `additional` more elements to be inserted @@ -667,28 +687,24 @@ impl<K, V, S> HashMap<K, V, S> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { - let new_size = self.len().checked_add(additional).expect("capacity overflow"); - let min_cap = self.resize_policy.min_capacity(new_size); - - // An invalid value shouldn't make us run out of space. This includes - // an overflow check. - assert!(new_size <= min_cap); - - if self.table.capacity() < min_cap { - let new_capacity = max(min_cap.next_power_of_two(), INITIAL_CAPACITY); - self.resize(new_capacity); + let remaining = self.capacity() - self.len(); // this can't overflow + if remaining < additional { + let min_cap = self.len().checked_add(additional).expect("reserve overflow"); + let raw_cap = self.resize_policy.raw_capacity(min_cap); + self.resize(raw_cap); } } - /// 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 + /// Resizes the internal vectors to a new capacity. It's your + /// responsibility to: + /// 1) Ensure `new_raw_cap` is enough for all the elements, accounting /// for the load factor. - /// 2) Ensure `new_capacity` is a power of two or zero. - fn resize(&mut self, new_capacity: usize) { - assert!(self.table.size() <= new_capacity); - assert!(new_capacity.is_power_of_two() || new_capacity == 0); + /// 2) Ensure `new_raw_cap` is a power of two or zero. + fn resize(&mut self, new_raw_cap: usize) { + 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::new(new_capacity)); + let mut old_table = replace(&mut self.table, RawTable::new(new_raw_cap)); let old_size = old_table.size(); if old_table.capacity() == 0 || old_table.size() == 0 { @@ -752,7 +768,7 @@ impl<K, V, S> HashMap<K, V, S> } b.into_bucket() } - Empty(b) => b.into_bucket() + Empty(b) => b.into_bucket(), }; bucket.next(); } @@ -778,14 +794,9 @@ impl<K, V, S> HashMap<K, V, S> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn shrink_to_fit(&mut self) { - let min_capacity = self.resize_policy.min_capacity(self.len()); - let min_capacity = max(min_capacity.next_power_of_two(), INITIAL_CAPACITY); - - // An invalid value shouldn't make us run out of space. - debug_assert!(self.len() <= min_capacity); - - if self.table.capacity() != min_capacity { - let old_table = replace(&mut self.table, RawTable::new(min_capacity)); + let new_raw_cap = self.resize_policy.raw_capacity(self.len()); + if self.raw_capacity() != new_raw_cap { + let old_table = replace(&mut self.table, RawTable::new(new_raw_cap)); let old_size = old_table.size(); // Shrink the table. Naive algorithm for resizing: @@ -806,16 +817,12 @@ impl<K, V, S> HashMap<K, V, S> fn insert_hashed_nocheck(&mut self, hash: SafeHash, k: K, v: V) -> Option<V> { let entry = search_hashed(&mut self.table, hash, |key| *key == k).into_entry(k); match entry { - Some(Occupied(mut elem)) => { - Some(elem.insert(v)) - } + Some(Occupied(mut elem)) => Some(elem.insert(v)), Some(Vacant(elem)) => { elem.insert(v); None } - None => { - unreachable!() - } + None => unreachable!(), } } @@ -979,7 +986,9 @@ impl<K, V, S> HashMap<K, V, S> /// assert_eq!(a.len(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { self.table.size() } + pub fn len(&self) -> usize { + self.table.size() + } /// Returns true if the map contains no elements. /// @@ -995,7 +1004,9 @@ impl<K, V, S> HashMap<K, V, S> /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { self.len() == 0 } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } /// Clears the map, returning all key-value pairs as an iterator. Keeps the /// allocated memory for reuse. @@ -1019,9 +1030,7 @@ impl<K, V, S> HashMap<K, V, S> #[inline] #[stable(feature = "drain", since = "1.6.0")] pub fn drain(&mut self) -> Drain<K, V> { - Drain { - inner: self.table.drain(), - } + Drain { inner: self.table.drain() } } /// Clears the map, removing all key-value pairs. Keeps the allocated memory @@ -1064,7 +1073,8 @@ impl<K, V, S> HashMap<K, V, S> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> - where K: Borrow<Q>, Q: Hash + Eq + where K: Borrow<Q>, + Q: Hash + Eq { self.search(k).into_occupied_bucket().map(|bucket| bucket.into_refs().1) } @@ -1090,7 +1100,8 @@ impl<K, V, S> HashMap<K, V, S> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool - where K: Borrow<Q>, Q: Hash + Eq + where K: Borrow<Q>, + Q: Hash + Eq { self.search(k).into_occupied_bucket().is_some() } @@ -1118,7 +1129,8 @@ impl<K, V, S> HashMap<K, V, S> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> - where K: Borrow<Q>, Q: Hash + Eq + where K: Borrow<Q>, + Q: Hash + Eq { self.search_mut(k).into_occupied_bucket().map(|bucket| bucket.into_mut_refs().1) } @@ -1176,10 +1188,11 @@ impl<K, V, S> HashMap<K, V, S> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V> - where K: Borrow<Q>, Q: Hash + Eq + where K: Borrow<Q>, + Q: Hash + Eq { if self.table.size() == 0 { - return None + return None; } self.search_mut(k).into_occupied_bucket().map(|bucket| pop_internal(bucket).1) @@ -1188,25 +1201,32 @@ impl<K, V, S> HashMap<K, V, S> #[stable(feature = "rust1", since = "1.0.0")] impl<K, V, S> PartialEq for HashMap<K, V, S> - where K: Eq + Hash, V: PartialEq, S: BuildHasher + where K: Eq + Hash, + V: PartialEq, + S: BuildHasher { fn eq(&self, other: &HashMap<K, V, S>) -> bool { - if self.len() != other.len() { return false; } + if self.len() != other.len() { + return false; + } - self.iter().all(|(key, value)| - other.get(key).map_or(false, |v| *value == *v) - ) + self.iter().all(|(key, value)| other.get(key).map_or(false, |v| *value == *v)) } } #[stable(feature = "rust1", since = "1.0.0")] impl<K, V, S> Eq for HashMap<K, V, S> - where K: Eq + Hash, V: Eq, S: BuildHasher -{} + where K: Eq + Hash, + V: Eq, + S: BuildHasher +{ +} #[stable(feature = "rust1", since = "1.0.0")] impl<K, V, S> Debug for HashMap<K, V, S> - where K: Eq + Hash + Debug, V: Debug, S: BuildHasher + where K: Eq + Hash + Debug, + V: Debug, + S: BuildHasher { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_map().entries(self.iter()).finish() @@ -1216,7 +1236,7 @@ impl<K, V, S> Debug for HashMap<K, V, S> #[stable(feature = "rust1", since = "1.0.0")] impl<K, V, S> Default for HashMap<K, V, S> where K: Eq + Hash, - S: BuildHasher + Default, + S: BuildHasher + Default { /// Creates an empty `HashMap<K, V, S>`, with the `Default` value for the hasher. fn default() -> HashMap<K, V, S> { @@ -1228,7 +1248,7 @@ impl<K, V, S> Default for HashMap<K, V, S> impl<'a, K, Q: ?Sized, V, S> Index<&'a Q> for HashMap<K, V, S> where K: Eq + Hash + Borrow<Q>, Q: Eq + Hash, - S: BuildHasher, + S: BuildHasher { type Output = V; @@ -1241,79 +1261,71 @@ impl<'a, K, Q: ?Sized, V, S> Index<&'a Q> for HashMap<K, V, S> /// HashMap iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, K: 'a, V: 'a> { - inner: table::Iter<'a, K, V> + inner: table::Iter<'a, K, V>, } // FIXME(#19839) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Clone for Iter<'a, K, V> { fn clone(&self) -> Iter<'a, K, V> { - Iter { - inner: self.inner.clone() - } + Iter { inner: self.inner.clone() } } } /// HashMap mutable values iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct IterMut<'a, K: 'a, V: 'a> { - inner: table::IterMut<'a, K, V> + inner: table::IterMut<'a, K, V>, } /// HashMap move iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter<K, V> { - inner: table::IntoIter<K, V> + inner: table::IntoIter<K, V>, } /// HashMap keys iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct Keys<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V> + inner: Iter<'a, K, V>, } // FIXME(#19839) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Clone for Keys<'a, K, V> { fn clone(&self) -> Keys<'a, K, V> { - Keys { - inner: self.inner.clone() - } + Keys { inner: self.inner.clone() } } } /// HashMap values iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct Values<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V> + inner: Iter<'a, K, V>, } // FIXME(#19839) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> Clone for Values<'a, K, V> { fn clone(&self) -> Values<'a, K, V> { - Values { - inner: self.inner.clone() - } + Values { inner: self.inner.clone() } } } /// HashMap drain iterator. #[stable(feature = "drain", since = "1.6.0")] pub struct Drain<'a, K: 'a, V: 'a> { - inner: table::Drain<'a, K, V> + inner: table::Drain<'a, K, V>, } /// Mutable HashMap values iterator. #[stable(feature = "map_values_mut", since = "1.10.0")] pub struct ValuesMut<'a, K: 'a, V: 'a> { - inner: IterMut<'a, K, V> + inner: IterMut<'a, K, V>, } enum InternalEntry<K, V, M> { - Occupied { - elem: FullBucket<K, V, M>, - }, + Occupied { elem: FullBucket<K, V, M> }, Vacant { hash: SafeHash, elem: VacantEntryState<K, V, M>, @@ -1338,7 +1350,7 @@ impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> { InternalEntry::Occupied { elem } => { Some(Occupied(OccupiedEntry { key: Some(key), - elem: elem + elem: elem, })) } InternalEntry::Vacant { hash, elem } => { @@ -1348,7 +1360,7 @@ impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> { elem: elem, })) } - InternalEntry::TableIsEmpty => None + InternalEntry::TableIsEmpty => None, } } } @@ -1362,27 +1374,29 @@ impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> { pub enum Entry<'a, K: 'a, V: 'a> { /// An occupied Entry. #[stable(feature = "rust1", since = "1.0.0")] - Occupied( - #[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V> - ), + Occupied(#[stable(feature = "rust1", since = "1.0.0")] + OccupiedEntry<'a, K, V>), /// A vacant Entry. #[stable(feature = "rust1", since = "1.0.0")] - Vacant( - #[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V> - ), + Vacant(#[stable(feature = "rust1", since = "1.0.0")] + VacantEntry<'a, K, V>), } #[stable(feature= "debug_hash_map", since = "1.12.0")] impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for Entry<'a, K, V> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Vacant(ref v) => f.debug_tuple("Entry") - .field(v) - .finish(), - Occupied(ref o) => f.debug_tuple("Entry") - .field(o) - .finish(), + Vacant(ref v) => { + f.debug_tuple("Entry") + .field(v) + .finish() + } + Occupied(ref o) => { + f.debug_tuple("Entry") + .field(o) + .finish() + } } } } @@ -1401,9 +1415,9 @@ pub struct OccupiedEntry<'a, K: 'a, V: 'a> { impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for OccupiedEntry<'a, K, V> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("OccupiedEntry") - .field("key", self.key()) - .field("value", self.get()) - .finish() + .field("key", self.key()) + .field("value", self.get()) + .finish() } } @@ -1422,8 +1436,8 @@ pub struct VacantEntry<'a, K: 'a, V: 'a> { impl<'a, K: 'a + Debug, V: 'a> Debug for VacantEntry<'a, K, V> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_tuple("VacantEntry") - .field(self.key()) - .finish() + .field(self.key()) + .finish() } } @@ -1438,7 +1452,8 @@ enum VacantEntryState<K, V, M> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S> - where K: Eq + Hash, S: BuildHasher + where K: Eq + Hash, + S: BuildHasher { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V>; @@ -1450,7 +1465,8 @@ impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S> #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S> - where K: Eq + Hash, S: BuildHasher + where K: Eq + Hash, + S: BuildHasher { type Item = (&'a K, &'a mut V); type IntoIter = IterMut<'a, K, V>; @@ -1462,7 +1478,8 @@ impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S> #[stable(feature = "rust1", since = "1.0.0")] impl<K, V, S> IntoIterator for HashMap<K, V, S> - where K: Eq + Hash, S: BuildHasher + where K: Eq + Hash, + S: BuildHasher { type Item = (K, V); type IntoIter = IntoIter<K, V>; @@ -1485,9 +1502,7 @@ impl<K, V, S> IntoIterator for HashMap<K, V, S> /// let vec: Vec<(&str, isize)> = map.into_iter().collect(); /// ``` fn into_iter(self) -> IntoIter<K, V> { - IntoIter { - inner: self.table.into_iter() - } + IntoIter { inner: self.table.into_iter() } } } @@ -1495,12 +1510,21 @@ impl<K, V, S> IntoIterator for HashMap<K, V, S> impl<'a, K, V> Iterator for Iter<'a, K, V> { type Item = (&'a K, &'a V); - #[inline] fn next(&mut self) -> Option<(&'a K, &'a V)> { self.inner.next() } - #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + #[inline] + fn next(&mut self) -> Option<(&'a K, &'a V)> { + self.inner.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { - #[inline] fn len(&self) -> usize { self.inner.len() } + #[inline] + fn len(&self) -> usize { + self.inner.len() + } } #[unstable(feature = "fused", issue = "35602")] @@ -1510,12 +1534,21 @@ impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} impl<'a, K, V> Iterator for IterMut<'a, K, V> { type Item = (&'a K, &'a mut V); - #[inline] fn next(&mut self) -> Option<(&'a K, &'a mut V)> { self.inner.next() } - #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + #[inline] + fn next(&mut self) -> Option<(&'a K, &'a mut V)> { + self.inner.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { - #[inline] fn len(&self) -> usize { self.inner.len() } + #[inline] + fn len(&self) -> usize { + self.inner.len() + } } #[unstable(feature = "fused", issue = "35602")] impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} @@ -1524,12 +1557,21 @@ impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} impl<K, V> Iterator for IntoIter<K, V> { type Item = (K, V); - #[inline] fn next(&mut self) -> Option<(K, V)> { self.inner.next().map(|(_, k, v)| (k, v)) } - #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + #[inline] + fn next(&mut self) -> Option<(K, V)> { + self.inner.next().map(|(_, k, v)| (k, v)) + } + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() + } } #[stable(feature = "rust1", since = "1.0.0")] impl<K, V> ExactSizeIterator for IntoIter<K, V> { - #[inline] fn len(&self) -> usize { self.inner.len() } + #[inline] + fn len(&self) -> usize { + self.inner.len() + } } #[unstable(feature = "fused", issue = "35602")] impl<K, V> FusedIterator for IntoIter<K, V> {} @@ -1538,12 +1580,21 @@ impl<K, V> FusedIterator for IntoIter<K, V> {} impl<'a, K, V> Iterator for Keys<'a, K, V> { type Item = &'a K; - #[inline] fn next(&mut self) -> Option<(&'a K)> { self.inner.next().map(|(k, _)| k) } - #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + #[inline] + fn next(&mut self) -> Option<(&'a K)> { + self.inner.next().map(|(k, _)| k) + } + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { - #[inline] fn len(&self) -> usize { self.inner.len() } + #[inline] + fn len(&self) -> usize { + self.inner.len() + } } #[unstable(feature = "fused", issue = "35602")] impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} @@ -1552,12 +1603,21 @@ impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} impl<'a, K, V> Iterator for Values<'a, K, V> { type Item = &'a V; - #[inline] fn next(&mut self) -> Option<(&'a V)> { self.inner.next().map(|(_, v)| v) } - #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + #[inline] + fn next(&mut self) -> Option<(&'a V)> { + self.inner.next().map(|(_, v)| v) + } + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { - #[inline] fn len(&self) -> usize { self.inner.len() } + #[inline] + fn len(&self) -> usize { + self.inner.len() + } } #[unstable(feature = "fused", issue = "35602")] impl<'a, K, V> FusedIterator for Values<'a, K, V> {} @@ -1566,26 +1626,44 @@ impl<'a, K, V> FusedIterator for Values<'a, K, V> {} impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { type Item = &'a mut V; - #[inline] fn next(&mut self) -> Option<(&'a mut V)> { self.inner.next().map(|(_, v)| v) } - #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + #[inline] + fn next(&mut self) -> Option<(&'a mut V)> { + self.inner.next().map(|(_, v)| v) + } + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() + } } #[stable(feature = "map_values_mut", since = "1.10.0")] impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { - #[inline] fn len(&self) -> usize { self.inner.len() } + #[inline] + fn len(&self) -> usize { + self.inner.len() + } } #[unstable(feature = "fused", issue = "35602")] impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "drain", since = "1.6.0")] impl<'a, K, V> Iterator for Drain<'a, K, V> { type Item = (K, V); - #[inline] fn next(&mut self) -> Option<(K, V)> { self.inner.next().map(|(_, k, v)| (k, v)) } - #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } + #[inline] + fn next(&mut self) -> Option<(K, V)> { + self.inner.next().map(|(_, k, v)| (k, v)) + } + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() + } } -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "drain", since = "1.6.0")] impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> { - #[inline] fn len(&self) -> usize { self.inner.len() } + #[inline] + fn len(&self) -> usize { + self.inner.len() + } } #[unstable(feature = "fused", issue = "35602")] impl<'a, K, V> FusedIterator for Drain<'a, K, V> {} @@ -1880,21 +1958,18 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(self, value: V) -> &'a mut V { match self.elem { - NeqElem(bucket, ib) => { - robin_hood(bucket, ib, self.hash, self.key, value) - } - NoElem(bucket) => { - bucket.put(self.hash, self.key, value).into_mut_refs().1 - } + NeqElem(bucket, ib) => robin_hood(bucket, ib, self.hash, self.key, value), + NoElem(bucket) => bucket.put(self.hash, self.key, value).into_mut_refs().1, } } } #[stable(feature = "rust1", since = "1.0.0")] impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S> - where K: Eq + Hash, S: BuildHasher + Default + where K: Eq + Hash, + S: BuildHasher + Default { - fn from_iter<T: IntoIterator<Item=(K, V)>>(iter: T) -> HashMap<K, V, S> { + fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> HashMap<K, V, S> { let iterator = iter.into_iter(); let lower = iterator.size_hint().0; let mut map = HashMap::with_capacity_and_hasher(lower, Default::default()); @@ -1905,9 +1980,10 @@ impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S> #[stable(feature = "rust1", since = "1.0.0")] impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S> - where K: Eq + Hash, S: BuildHasher + where K: Eq + Hash, + S: BuildHasher { - fn extend<T: IntoIterator<Item=(K, V)>>(&mut self, iter: T) { + fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) { for (k, v) in iter { self.insert(k, v); } @@ -1916,9 +1992,11 @@ impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S> #[stable(feature = "hash_extend_copy", since = "1.4.0")] impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S> - where K: Eq + Hash + Copy, V: Copy, S: BuildHasher + where K: Eq + Hash + Copy, + V: Copy, + S: BuildHasher { - fn extend<T: IntoIterator<Item=(&'a K, &'a V)>>(&mut self, iter: T) { + fn extend<T: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: T) { self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); } } @@ -1960,7 +2038,8 @@ impl RandomState { /// let s = RandomState::new(); /// ``` #[inline] - #[allow(deprecated)] // rand + #[allow(deprecated)] + // rand #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn new() -> RandomState { // Historically this function did not cache keys from the OS and instead @@ -1987,9 +2066,7 @@ impl RandomState { (r.gen(), r.gen()) }); - KEYS.with(|&(k0, k1)| { - RandomState { k0: k0, k1: k1 } - }) + KEYS.with(|&(k0, k1)| RandomState { k0: k0, k1: k1 }) } } @@ -1997,6 +2074,7 @@ impl RandomState { impl BuildHasher for RandomState { type Hasher = DefaultHasher; #[inline] + #[allow(deprecated)] fn build_hasher(&self) -> DefaultHasher { DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1)) } @@ -2009,10 +2087,32 @@ impl BuildHasher for RandomState { /// /// [`RandomState`]: struct.RandomState.html /// [`Hasher`]: ../../hash/trait.Hasher.html -#[unstable(feature = "hashmap_default_hasher", issue = "0")] +#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] +#[allow(deprecated)] +#[derive(Debug)] pub struct DefaultHasher(SipHasher13); -#[unstable(feature = "hashmap_default_hasher", issue = "0")] +impl DefaultHasher { + /// Creates a new `DefaultHasher`. + /// + /// This hasher is not guaranteed to be the same as all other + /// `DefaultHasher` instances, but is the same as all other `DefaultHasher` + /// instances created through `new` or `default`. + #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] + #[allow(deprecated)] + pub fn new() -> DefaultHasher { + DefaultHasher(SipHasher13::new_with_keys(0, 0)) + } +} + +#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] +impl Default for DefaultHasher { + fn default() -> DefaultHasher { + DefaultHasher::new() + } +} + +#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] impl Hasher for DefaultHasher { #[inline] fn write(&mut self, msg: &[u8]) { @@ -2025,7 +2125,7 @@ impl Hasher for DefaultHasher { } } -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] impl Default for RandomState { /// Constructs a new `RandomState`. #[inline] @@ -2035,7 +2135,9 @@ impl Default for RandomState { } impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> - where K: Eq + Hash + Borrow<Q>, S: BuildHasher, Q: Eq + Hash + where K: Eq + Hash + Borrow<Q>, + S: BuildHasher, + Q: Eq + Hash { type Key = K; @@ -2045,7 +2147,7 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> fn take(&mut self, key: &Q) -> Option<K> { if self.table.size() == 0 { - return None + return None; } self.search_mut(key).into_occupied_bucket().map(|bucket| pop_internal(bucket).0) @@ -2069,28 +2171,83 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> #[allow(dead_code)] fn assert_covariance() { - fn map_key<'new>(v: HashMap<&'static str, u8>) -> HashMap<&'new str, u8> { v } - fn map_val<'new>(v: HashMap<u8, &'static str>) -> HashMap<u8, &'new str> { v } - fn iter_key<'a, 'new>(v: Iter<'a, &'static str, u8>) -> Iter<'a, &'new str, u8> { v } - fn iter_val<'a, 'new>(v: Iter<'a, u8, &'static str>) -> Iter<'a, u8, &'new str> { v } - fn into_iter_key<'new>(v: IntoIter<&'static str, u8>) -> IntoIter<&'new str, u8> { v } - fn into_iter_val<'new>(v: IntoIter<u8, &'static str>) -> IntoIter<u8, &'new str> { v } - fn keys_key<'a, 'new>(v: Keys<'a, &'static str, u8>) -> Keys<'a, &'new str, u8> { v } - fn keys_val<'a, 'new>(v: Keys<'a, u8, &'static str>) -> Keys<'a, u8, &'new str> { v } - fn values_key<'a, 'new>(v: Values<'a, &'static str, u8>) -> Values<'a, &'new str, u8> { v } - fn values_val<'a, 'new>(v: Values<'a, u8, &'static str>) -> Values<'a, u8, &'new str> { v } + fn map_key<'new>(v: HashMap<&'static str, u8>) -> HashMap<&'new str, u8> { + v + } + fn map_val<'new>(v: HashMap<u8, &'static str>) -> HashMap<u8, &'new str> { + v + } + fn iter_key<'a, 'new>(v: Iter<'a, &'static str, u8>) -> Iter<'a, &'new str, u8> { + v + } + fn iter_val<'a, 'new>(v: Iter<'a, u8, &'static str>) -> Iter<'a, u8, &'new str> { + v + } + fn into_iter_key<'new>(v: IntoIter<&'static str, u8>) -> IntoIter<&'new str, u8> { + v + } + fn into_iter_val<'new>(v: IntoIter<u8, &'static str>) -> IntoIter<u8, &'new str> { + v + } + fn keys_key<'a, 'new>(v: Keys<'a, &'static str, u8>) -> Keys<'a, &'new str, u8> { + v + } + fn keys_val<'a, 'new>(v: Keys<'a, u8, &'static str>) -> Keys<'a, u8, &'new str> { + v + } + fn values_key<'a, 'new>(v: Values<'a, &'static str, u8>) -> Values<'a, &'new str, u8> { + v + } + fn values_val<'a, 'new>(v: Values<'a, u8, &'static str>) -> Values<'a, u8, &'new str> { + v + } fn drain<'new>(d: Drain<'static, &'static str, &'static str>) - -> Drain<'new, &'new str, &'new str> { d } + -> Drain<'new, &'new str, &'new str> { + d + } } #[cfg(test)] mod test_map { use super::HashMap; use super::Entry::{Occupied, Vacant}; + use super::RandomState; use cell::RefCell; use rand::{thread_rng, Rng}; #[test] + fn test_zero_capacities() { + type HM = HashMap<i32, i32>; + + let m = HM::new(); + assert_eq!(m.capacity(), 0); + + let m = HM::default(); + assert_eq!(m.capacity(), 0); + + let m = HM::with_hasher(RandomState::new()); + assert_eq!(m.capacity(), 0); + + let m = HM::with_capacity(0); + assert_eq!(m.capacity(), 0); + + let m = HM::with_capacity_and_hasher(0, RandomState::new()); + assert_eq!(m.capacity(), 0); + + let mut m = HM::new(); + m.insert(1, 1); + m.insert(2, 2); + m.remove(&1); + m.remove(&2); + m.shrink_to_fit(); + assert_eq!(m.capacity(), 0); + + let mut m = HM::new(); + m.reserve(0); + assert_eq!(m.capacity(), 0); + } + + #[test] fn test_create_capacity_zero() { let mut m = HashMap::with_capacity(0); @@ -2130,7 +2287,7 @@ mod test_map { #[derive(Hash, PartialEq, Eq)] struct Dropable { - k: usize + k: usize, } impl Dropable { @@ -2174,7 +2331,7 @@ mod test_map { for i in 0..100 { let d1 = Dropable::new(i); - let d2 = Dropable::new(i+100); + let d2 = Dropable::new(i + 100); m.insert(d1, d2); } @@ -2233,7 +2390,7 @@ mod test_map { for i in 0..100 { let d1 = Dropable::new(i); - let d2 = Dropable::new(i+100); + let d2 = Dropable::new(i + 100); hm.insert(d1, d2); } @@ -2261,13 +2418,13 @@ mod test_map { for _ in half.by_ref() {} DROP_VECTOR.with(|v| { - let nk = (0..100).filter(|&i| { - v.borrow()[i] == 1 - }).count(); + let nk = (0..100) + .filter(|&i| v.borrow()[i] == 1) + .count(); - let nv = (0..100).filter(|&i| { - v.borrow()[i+100] == 1 - }).count(); + let nv = (0..100) + .filter(|&i| v.borrow()[i + 100] == 1) + .count(); assert_eq!(nk, 50); assert_eq!(nv, 50); @@ -2324,12 +2481,12 @@ mod test_map { for i in 1..1001 { assert!(m.insert(i, i).is_none()); - for j in 1..i+1 { + for j in 1..i + 1 { let r = m.get(&j); assert_eq!(r, Some(&j)); } - for j in i+1..1001 { + for j in i + 1..1001 { let r = m.get(&j); assert_eq!(r, None); } @@ -2343,11 +2500,11 @@ mod test_map { for i in 1..1001 { assert!(m.remove(&i).is_some()); - for j in 1..i+1 { + for j in 1..i + 1 { assert!(!m.contains_key(&j)); } - for j in i+1..1001 { + for j in i + 1..1001 { assert!(m.contains_key(&j)); } } @@ -2383,7 +2540,8 @@ mod test_map { assert!(m.insert(5, 14).is_none()); let new = 100; match m.get_mut(&5) { - None => panic!(), Some(x) => *x = new + None => panic!(), + Some(x) => *x = new, } assert_eq!(m.get(&5), Some(&new)); } @@ -2502,7 +2660,7 @@ mod test_map { m.insert(1, 2); match m.get(&1) { None => panic!(), - Some(v) => assert_eq!(*v, 2) + Some(v) => assert_eq!(*v, 2), } } @@ -2547,8 +2705,8 @@ mod test_map { assert!(m.is_empty()); let mut i = 0; - let old_cap = m.table.capacity(); - while old_cap == m.table.capacity() { + let old_raw_cap = m.raw_capacity(); + while old_raw_cap == m.raw_capacity() { m.insert(i, i); i += 1; } @@ -2562,47 +2720,47 @@ mod test_map { let mut m = HashMap::new(); assert_eq!(m.len(), 0); - assert_eq!(m.table.capacity(), 0); + assert_eq!(m.raw_capacity(), 0); assert!(m.is_empty()); m.insert(0, 0); m.remove(&0); assert!(m.is_empty()); - let initial_cap = m.table.capacity(); - m.reserve(initial_cap); - let cap = m.table.capacity(); + let initial_raw_cap = m.raw_capacity(); + m.reserve(initial_raw_cap); + let raw_cap = m.raw_capacity(); - assert_eq!(cap, initial_cap * 2); + assert_eq!(raw_cap, initial_raw_cap * 2); let mut i = 0; - for _ in 0..cap * 3 / 4 { + for _ in 0..raw_cap * 3 / 4 { m.insert(i, i); i += 1; } // three quarters full assert_eq!(m.len(), i); - assert_eq!(m.table.capacity(), cap); + assert_eq!(m.raw_capacity(), raw_cap); - for _ in 0..cap / 4 { + for _ in 0..raw_cap / 4 { m.insert(i, i); i += 1; } // half full - let new_cap = m.table.capacity(); - assert_eq!(new_cap, cap * 2); + let new_raw_cap = m.raw_capacity(); + assert_eq!(new_raw_cap, raw_cap * 2); - for _ in 0..cap / 2 - 1 { + for _ in 0..raw_cap / 2 - 1 { i -= 1; m.remove(&i); - assert_eq!(m.table.capacity(), new_cap); + assert_eq!(m.raw_capacity(), new_raw_cap); } // A little more than one quarter full. m.shrink_to_fit(); - assert_eq!(m.table.capacity(), cap); + assert_eq!(m.raw_capacity(), raw_cap); // again, a little more than half full - for _ in 0..cap / 2 - 1 { + for _ in 0..raw_cap / 2 - 1 { i -= 1; m.remove(&i); } @@ -2610,7 +2768,7 @@ mod test_map { assert_eq!(m.len(), i); assert!(!m.is_empty()); - assert_eq!(m.table.capacity(), initial_cap); + assert_eq!(m.raw_capacity(), initial_raw_cap); } #[test] @@ -2665,7 +2823,7 @@ mod test_map { fn test_size_hint() { let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - let map: HashMap<_, _> = xs.iter().cloned().collect(); + let map: HashMap<_, _> = xs.iter().cloned().collect(); let mut iter = map.iter(); @@ -2678,7 +2836,7 @@ mod test_map { fn test_iter_len() { let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - let map: HashMap<_, _> = xs.iter().cloned().collect(); + let map: HashMap<_, _> = xs.iter().cloned().collect(); let mut iter = map.iter(); @@ -2691,7 +2849,7 @@ mod test_map { fn test_mut_size_hint() { let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - let mut map: HashMap<_, _> = xs.iter().cloned().collect(); + let mut map: HashMap<_, _> = xs.iter().cloned().collect(); let mut iter = map.iter_mut(); @@ -2704,7 +2862,7 @@ mod test_map { fn test_iter_mut_len() { let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; - let mut map: HashMap<_, _> = xs.iter().cloned().collect(); + let mut map: HashMap<_, _> = xs.iter().cloned().collect(); let mut iter = map.iter_mut(); @@ -2737,7 +2895,7 @@ mod test_map { } #[test] - fn test_entry(){ + fn test_entry() { let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)]; let mut map: HashMap<_, _> = xs.iter().cloned().collect(); @@ -2811,11 +2969,11 @@ mod test_map { for i in 0..1000 { let x = rng.gen_range(-10, 10); match m.entry(x) { - Vacant(_) => {}, + Vacant(_) => {} Occupied(e) => { println!("{}: remove {}", i, x); e.remove(); - }, + } } check(&m); @@ -2893,7 +3051,7 @@ mod test_map { Vacant(e) => { assert_eq!(key, *e.key()); e.insert(value.clone()); - }, + } } assert_eq!(a.len(), 1); assert_eq!(a[key], value); diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index ff56747fee6..1ec7a4a7b63 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -17,8 +17,6 @@ use ops::{BitOr, BitAnd, BitXor, Sub}; use super::Recover; use super::map::{self, HashMap, Keys, RandomState}; -const INITIAL_CAPACITY: usize = 32; - // Future Optimization (FIXME!) // ============================= // @@ -100,10 +98,24 @@ const INITIAL_CAPACITY: usize = 32; /// println!("{:?}", x); /// } /// ``` +/// +/// HashSet with fixed list of elements can be initialized from an array: +/// +/// ``` +/// use std::collections::HashSet; +/// +/// fn main() { +/// let viking_names: HashSet<&str> = +/// [ "Einar", "Olaf", "Harald" ].iter().cloned().collect(); +/// // use the values stored in the set +/// } +/// ``` + + #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashSet<T, S = RandomState> { - map: HashMap<T, (), S> + map: HashMap<T, (), S>, } impl<T: Hash + Eq> HashSet<T, RandomState> { @@ -118,11 +130,13 @@ impl<T: Hash + Eq> HashSet<T, RandomState> { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn new() -> HashSet<T, RandomState> { - HashSet::with_capacity(INITIAL_CAPACITY) + HashSet { map: HashMap::new() } } - /// Creates an empty HashSet with space for at least `n` elements in - /// the hash table. + /// Creates an empty `HashSet` with the specified capacity. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. If `capacity` is 0, the hash set will not allocate. /// /// # Examples /// @@ -138,7 +152,8 @@ impl<T: Hash + Eq> HashSet<T, RandomState> { } impl<T, S> HashSet<T, S> - where T: Eq + Hash, S: BuildHasher + where T: Eq + Hash, + S: BuildHasher { /// Creates a new empty hash set which will use the given hasher to hash /// keys. @@ -163,11 +178,14 @@ impl<T, S> HashSet<T, S> #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_hasher(hasher: S) -> HashSet<T, S> { - HashSet::with_capacity_and_hasher(INITIAL_CAPACITY, hasher) + HashSet { map: HashMap::with_hasher(hasher) } } - /// Creates an empty HashSet with space for at least `capacity` - /// elements in the hash table, using `hasher` to hash the keys. + /// Creates an empty HashSet with with the specified capacity, using + /// `hasher` to hash the keys. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. If `capacity` is 0, the hash set will not allocate. /// /// Warning: `hasher` is normally randomly generated, and /// is designed to allow `HashSet`s to be resistant to attacks that @@ -186,11 +204,8 @@ impl<T, S> HashSet<T, S> /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_capacity_and_hasher(capacity: usize, hasher: S) - -> HashSet<T, S> { - HashSet { - map: HashMap::with_capacity_and_hasher(capacity, hasher), - } + pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet<T, S> { + HashSet { map: HashMap::with_capacity_and_hasher(capacity, hasher) } } /// Returns a reference to the set's hasher. @@ -327,8 +342,9 @@ impl<T, S> HashSet<T, S> /// assert_eq!(diff1, [1, 4].iter().cloned().collect()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn symmetric_difference<'a>(&'a self, other: &'a HashSet<T, S>) - -> SymmetricDifference<'a, T, S> { + pub fn symmetric_difference<'a>(&'a self, + other: &'a HashSet<T, S>) + -> SymmetricDifference<'a, T, S> { SymmetricDifference { iter: self.difference(other).chain(other.difference(self)) } } @@ -392,7 +408,9 @@ impl<T, S> HashSet<T, S> /// assert_eq!(v.len(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { self.map.len() } + pub fn len(&self) -> usize { + self.map.len() + } /// Returns true if the set contains no elements. /// @@ -407,7 +425,9 @@ impl<T, S> HashSet<T, S> /// assert!(!v.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { self.map.is_empty() } + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } /// Clears the set, returning all elements in an iterator. #[inline] @@ -429,7 +449,9 @@ impl<T, S> HashSet<T, S> /// assert!(v.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { self.map.clear() } + pub fn clear(&mut self) { + self.map.clear() + } /// Returns `true` if the set contains a value. /// @@ -448,7 +470,8 @@ impl<T, S> HashSet<T, S> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool - where T: Borrow<Q>, Q: Hash + Eq + where T: Borrow<Q>, + Q: Hash + Eq { self.map.contains_key(value) } @@ -460,7 +483,8 @@ impl<T, S> HashSet<T, S> /// the value type. #[stable(feature = "set_recovery", since = "1.9.0")] pub fn get<Q: ?Sized>(&self, value: &Q) -> Option<&T> - where T: Borrow<Q>, Q: Hash + Eq + where T: Borrow<Q>, + Q: Hash + Eq { Recover::get(&self.map, value) } @@ -551,7 +575,9 @@ impl<T, S> HashSet<T, S> /// assert_eq!(set.len(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: T) -> bool { self.map.insert(value, ()).is_none() } + pub fn insert(&mut self, value: T) -> bool { + self.map.insert(value, ()).is_none() + } /// Adds a value to the set, replacing the existing value, if any, that is equal to the given /// one. Returns the replaced value. @@ -580,7 +606,8 @@ impl<T, S> HashSet<T, S> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool - where T: Borrow<Q>, Q: Hash + Eq + where T: Borrow<Q>, + Q: Hash + Eq { self.map.remove(value).is_some() } @@ -592,7 +619,8 @@ impl<T, S> HashSet<T, S> /// the value type. #[stable(feature = "set_recovery", since = "1.9.0")] pub fn take<Q: ?Sized>(&mut self, value: &Q) -> Option<T> - where T: Borrow<Q>, Q: Hash + Eq + where T: Borrow<Q>, + Q: Hash + Eq { Recover::take(&mut self.map, value) } @@ -600,10 +628,13 @@ impl<T, S> HashSet<T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> PartialEq for HashSet<T, S> - where T: Eq + Hash, S: BuildHasher + where T: Eq + Hash, + S: BuildHasher { fn eq(&self, other: &HashSet<T, S>) -> bool { - if self.len() != other.len() { return false; } + if self.len() != other.len() { + return false; + } self.iter().all(|key| other.contains(key)) } @@ -611,8 +642,10 @@ impl<T, S> PartialEq for HashSet<T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> Eq for HashSet<T, S> - where T: Eq + Hash, S: BuildHasher -{} + where T: Eq + Hash, + S: BuildHasher +{ +} #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> fmt::Debug for HashSet<T, S> @@ -627,9 +660,9 @@ impl<T, S> fmt::Debug for HashSet<T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> FromIterator<T> for HashSet<T, S> where T: Eq + Hash, - S: BuildHasher + Default, + S: BuildHasher + Default { - fn from_iter<I: IntoIterator<Item=T>>(iter: I) -> HashSet<T, S> { + fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> HashSet<T, S> { let iterator = iter.into_iter(); let lower = iterator.size_hint().0; let mut set = HashSet::with_capacity_and_hasher(lower, Default::default()); @@ -641,9 +674,9 @@ impl<T, S> FromIterator<T> for HashSet<T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> Extend<T> for HashSet<T, S> where T: Eq + Hash, - S: BuildHasher, + S: BuildHasher { - fn extend<I: IntoIterator<Item=T>>(&mut self, iter: I) { + fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { for k in iter { self.insert(k); } @@ -653,9 +686,9 @@ impl<T, S> Extend<T> for HashSet<T, S> #[stable(feature = "hash_extend_copy", since = "1.4.0")] impl<'a, T, S> Extend<&'a T> for HashSet<T, S> where T: 'a + Eq + Hash + Copy, - S: BuildHasher, + S: BuildHasher { - fn extend<I: IntoIterator<Item=&'a T>>(&mut self, iter: I) { + fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } } @@ -663,18 +696,18 @@ impl<'a, T, S> Extend<&'a T> for HashSet<T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> Default for HashSet<T, S> where T: Eq + Hash, - S: BuildHasher + Default, + S: BuildHasher + Default { /// Creates an empty `HashSet<T, S>` with the `Default` value for the hasher. fn default() -> HashSet<T, S> { - HashSet::with_hasher(Default::default()) + HashSet { map: HashMap::default() } } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, 'b, T, S> BitOr<&'b HashSet<T, S>> for &'a HashSet<T, S> where T: Eq + Hash + Clone, - S: BuildHasher + Default, + S: BuildHasher + Default { type Output = HashSet<T, S>; @@ -706,7 +739,7 @@ impl<'a, 'b, T, S> BitOr<&'b HashSet<T, S>> for &'a HashSet<T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<'a, 'b, T, S> BitAnd<&'b HashSet<T, S>> for &'a HashSet<T, S> where T: Eq + Hash + Clone, - S: BuildHasher + Default, + S: BuildHasher + Default { type Output = HashSet<T, S>; @@ -738,7 +771,7 @@ impl<'a, 'b, T, S> BitAnd<&'b HashSet<T, S>> for &'a HashSet<T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<'a, 'b, T, S> BitXor<&'b HashSet<T, S>> for &'a HashSet<T, S> where T: Eq + Hash + Clone, - S: BuildHasher + Default, + S: BuildHasher + Default { type Output = HashSet<T, S>; @@ -770,7 +803,7 @@ impl<'a, 'b, T, S> BitXor<&'b HashSet<T, S>> for &'a HashSet<T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<'a, 'b, T, S> Sub<&'b HashSet<T, S>> for &'a HashSet<T, S> where T: Eq + Hash + Clone, - S: BuildHasher + Default, + S: BuildHasher + Default { type Output = HashSet<T, S>; @@ -802,13 +835,13 @@ impl<'a, 'b, T, S> Sub<&'b HashSet<T, S>> for &'a HashSet<T, S> /// HashSet iterator #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, K: 'a> { - iter: Keys<'a, K, ()> + iter: Keys<'a, K, ()>, } /// HashSet move iterator #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter<K> { - iter: map::IntoIter<K, ()> + iter: map::IntoIter<K, ()>, } /// HashSet drain iterator @@ -838,18 +871,19 @@ pub struct Difference<'a, T: 'a, S: 'a> { /// Symmetric difference iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct SymmetricDifference<'a, T: 'a, S: 'a> { - iter: Chain<Difference<'a, T, S>, Difference<'a, T, S>> + iter: Chain<Difference<'a, T, S>, Difference<'a, T, S>>, } /// Set union iterator. #[stable(feature = "rust1", since = "1.0.0")] pub struct Union<'a, T: 'a, S: 'a> { - iter: Chain<Iter<'a, T>, Difference<'a, T, S>> + iter: Chain<Iter<'a, T>, Difference<'a, T, S>>, } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> IntoIterator for &'a HashSet<T, S> - where T: Eq + Hash, S: BuildHasher + where T: Eq + Hash, + S: BuildHasher { type Item = &'a T; type IntoIter = Iter<'a, T>; @@ -894,18 +928,26 @@ impl<T, S> IntoIterator for HashSet<T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K> Clone for Iter<'a, K> { - fn clone(&self) -> Iter<'a, K> { Iter { iter: self.iter.clone() } } + fn clone(&self) -> Iter<'a, K> { + Iter { iter: self.iter.clone() } + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K> Iterator for Iter<'a, K> { type Item = &'a K; - fn next(&mut self) -> Option<&'a K> { self.iter.next() } - fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } + fn next(&mut self) -> Option<&'a K> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K> ExactSizeIterator for Iter<'a, K> { - fn len(&self) -> usize { self.iter.len() } + fn len(&self) -> usize { + self.iter.len() + } } #[unstable(feature = "fused", issue = "35602")] impl<'a, K> FusedIterator for Iter<'a, K> {} @@ -914,12 +956,18 @@ impl<'a, K> FusedIterator for Iter<'a, K> {} impl<K> Iterator for IntoIter<K> { type Item = K; - fn next(&mut self) -> Option<K> { self.iter.next().map(|(k, _)| k) } - fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } + fn next(&mut self) -> Option<K> { + self.iter.next().map(|(k, _)| k) + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } } #[stable(feature = "rust1", since = "1.0.0")] impl<K> ExactSizeIterator for IntoIter<K> { - fn len(&self) -> usize { self.iter.len() } + fn len(&self) -> usize { + self.iter.len() + } } #[unstable(feature = "fused", issue = "35602")] impl<K> FusedIterator for IntoIter<K> {} @@ -928,12 +976,18 @@ impl<K> FusedIterator for IntoIter<K> {} impl<'a, K> Iterator for Drain<'a, K> { type Item = K; - fn next(&mut self) -> Option<K> { self.iter.next().map(|(k, _)| k) } - fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } + fn next(&mut self) -> Option<K> { + self.iter.next().map(|(k, _)| k) + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K> ExactSizeIterator for Drain<'a, K> { - fn len(&self) -> usize { self.iter.len() } + fn len(&self) -> usize { + self.iter.len() + } } #[unstable(feature = "fused", issue = "35602")] impl<'a, K> FusedIterator for Drain<'a, K> {} @@ -947,7 +1001,8 @@ impl<'a, T, S> Clone for Intersection<'a, T, S> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Iterator for Intersection<'a, T, S> - where T: Eq + Hash, S: BuildHasher + where T: Eq + Hash, + S: BuildHasher { type Item = &'a T; @@ -955,9 +1010,11 @@ impl<'a, T, S> Iterator for Intersection<'a, T, S> loop { match self.iter.next() { None => return None, - Some(elt) => if self.other.contains(elt) { - return Some(elt) - }, + Some(elt) => { + if self.other.contains(elt) { + return Some(elt); + } + } } } } @@ -970,8 +1027,10 @@ impl<'a, T, S> Iterator for Intersection<'a, T, S> #[unstable(feature = "fused", issue = "35602")] impl<'a, T, S> FusedIterator for Intersection<'a, T, S> - where T: Eq + Hash, S: BuildHasher -{} + where T: Eq + Hash, + S: BuildHasher +{ +} #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Clone for Difference<'a, T, S> { @@ -982,7 +1041,8 @@ impl<'a, T, S> Clone for Difference<'a, T, S> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Iterator for Difference<'a, T, S> - where T: Eq + Hash, S: BuildHasher + where T: Eq + Hash, + S: BuildHasher { type Item = &'a T; @@ -990,9 +1050,11 @@ impl<'a, T, S> Iterator for Difference<'a, T, S> loop { match self.iter.next() { None => return None, - Some(elt) => if !self.other.contains(elt) { - return Some(elt) - }, + Some(elt) => { + if !self.other.contains(elt) { + return Some(elt); + } + } } } } @@ -1005,8 +1067,10 @@ impl<'a, T, S> Iterator for Difference<'a, T, S> #[unstable(feature = "fused", issue = "35602")] impl<'a, T, S> FusedIterator for Difference<'a, T, S> - where T: Eq + Hash, S: BuildHasher -{} + where T: Eq + Hash, + S: BuildHasher +{ +} #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Clone for SymmetricDifference<'a, T, S> { @@ -1017,58 +1081,123 @@ impl<'a, T, S> Clone for SymmetricDifference<'a, T, S> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S> - where T: Eq + Hash, S: BuildHasher + where T: Eq + Hash, + S: BuildHasher { type Item = &'a T; - fn next(&mut self) -> Option<&'a T> { self.iter.next() } - fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } + fn next(&mut self) -> Option<&'a T> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } } #[unstable(feature = "fused", issue = "35602")] impl<'a, T, S> FusedIterator for SymmetricDifference<'a, T, S> - where T: Eq + Hash, S: BuildHasher -{} + where T: Eq + Hash, + S: BuildHasher +{ +} #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Clone for Union<'a, T, S> { - fn clone(&self) -> Union<'a, T, S> { Union { iter: self.iter.clone() } } + fn clone(&self) -> Union<'a, T, S> { + Union { iter: self.iter.clone() } + } } #[unstable(feature = "fused", issue = "35602")] impl<'a, T, S> FusedIterator for Union<'a, T, S> - where T: Eq + Hash, S: BuildHasher -{} + where T: Eq + Hash, + S: BuildHasher +{ +} #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T, S> Iterator for Union<'a, T, S> - where T: Eq + Hash, S: BuildHasher + where T: Eq + Hash, + S: BuildHasher { type Item = &'a T; - fn next(&mut self) -> Option<&'a T> { self.iter.next() } - fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } + fn next(&mut self) -> Option<&'a T> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } } #[allow(dead_code)] fn assert_covariance() { - fn set<'new>(v: HashSet<&'static str>) -> HashSet<&'new str> { v } - fn iter<'a, 'new>(v: Iter<'a, &'static str>) -> Iter<'a, &'new str> { v } - fn into_iter<'new>(v: IntoIter<&'static str>) -> IntoIter<&'new str> { v } + fn set<'new>(v: HashSet<&'static str>) -> HashSet<&'new str> { + v + } + fn iter<'a, 'new>(v: Iter<'a, &'static str>) -> Iter<'a, &'new str> { + v + } + fn into_iter<'new>(v: IntoIter<&'static str>) -> IntoIter<&'new str> { + v + } fn difference<'a, 'new>(v: Difference<'a, &'static str, RandomState>) - -> Difference<'a, &'new str, RandomState> { v } + -> Difference<'a, &'new str, RandomState> { + v + } fn symmetric_difference<'a, 'new>(v: SymmetricDifference<'a, &'static str, RandomState>) - -> SymmetricDifference<'a, &'new str, RandomState> { v } + -> SymmetricDifference<'a, &'new str, RandomState> { + v + } fn intersection<'a, 'new>(v: Intersection<'a, &'static str, RandomState>) - -> Intersection<'a, &'new str, RandomState> { v } + -> Intersection<'a, &'new str, RandomState> { + v + } fn union<'a, 'new>(v: Union<'a, &'static str, RandomState>) - -> Union<'a, &'new str, RandomState> { v } - fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { d } + -> Union<'a, &'new str, RandomState> { + v + } + fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { + d + } } #[cfg(test)] mod test_set { use super::HashSet; + use super::super::map::RandomState; + + #[test] + fn test_zero_capacities() { + type HS = HashSet<i32>; + + let s = HS::new(); + assert_eq!(s.capacity(), 0); + + let s = HS::default(); + assert_eq!(s.capacity(), 0); + + let s = HS::with_hasher(RandomState::new()); + assert_eq!(s.capacity(), 0); + + let s = HS::with_capacity(0); + assert_eq!(s.capacity(), 0); + + let s = HS::with_capacity_and_hasher(0, RandomState::new()); + assert_eq!(s.capacity(), 0); + + let mut s = HS::new(); + s.insert(1); + s.insert(2); + s.remove(&1); + s.remove(&2); + s.shrink_to_fit(); + assert_eq!(s.capacity(), 0); + + let mut s = HS::new(); + s.reserve(0); + assert_eq!(s.capacity(), 0); + } #[test] fn test_disjoint() { @@ -1335,7 +1464,9 @@ mod test_set { assert_eq!(last_i, 49); } - for _ in &s { panic!("s should be empty!"); } + for _ in &s { + panic!("s should be empty!"); + } // reset to try again. s.extend(1..100); diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 8f02c9c7d3d..ec0e457dc6a 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -21,13 +21,24 @@ use ptr::{self, Unique, Shared}; use self::BucketState::*; -const EMPTY_BUCKET: u64 = 0; +/// Integer type used for stored hash values. +/// +/// No more than bit_width(usize) bits are needed to select a bucket. +/// +/// The most significant bit is ours to use for tagging `SafeHash`. +/// +/// (Even if we could have usize::MAX bytes allocated for buckets, +/// each bucket stores at least a `HashUint`, so there can be no more than +/// usize::MAX / size_of(usize) buckets.) +type HashUint = usize; + +const EMPTY_BUCKET: HashUint = 0; /// The raw hashtable, providing safe-ish access to the unzipped and highly -/// optimized arrays of hashes, keys, and values. +/// optimized arrays of hashes, and key-value pairs. /// -/// This design uses less memory and is a lot faster than the naive -/// `Vec<Option<u64, K, V>>`, because we don't pay for the overhead of an +/// This design is a lot faster than the naive +/// `Vec<Option<(u64, K, V)>>`, because we don't pay for the overhead of an /// option on every element, and we get a generally more cache-aware design. /// /// Essential invariants of this structure: @@ -48,22 +59,23 @@ const EMPTY_BUCKET: u64 = 0; /// which will likely map to the same bucket, while not being confused /// with "empty". /// -/// - All three "arrays represented by pointers" are the same length: +/// - Both "arrays represented by pointers" are the same length: /// `capacity`. This is set at creation and never changes. The arrays -/// are unzipped to save space (we don't have to pay for the padding -/// between odd sized elements, such as in a map from u64 to u8), and -/// be more cache aware (scanning through 8 hashes brings in at most -/// 2 cache lines, since they're all right beside each other). +/// are unzipped and are more cache aware (scanning through 8 hashes +/// brings in at most 2 cache lines, since they're all right beside each +/// other). This layout may waste space in padding such as in a map from +/// u64 to u8, but is a more cache conscious layout as the key-value pairs +/// are only very shortly probed and the desired value will be in the same +/// or next cache line. /// /// You can kind of think of this module/data structure as a safe wrapper /// around just the "table" part of the hashtable. It enforces some /// invariants at the type level and employs some performance trickery, -/// but in general is just a tricked out `Vec<Option<u64, K, V>>`. -#[cfg_attr(stage0, unsafe_no_drop_flag)] +/// but in general is just a tricked out `Vec<Option<(u64, K, V)>>`. pub struct RawTable<K, V> { capacity: usize, size: usize, - hashes: Unique<u64>, + hashes: Unique<HashUint>, // Because K/V do not appear directly in any of the types in the struct, // inform rustc that in fact instances of K and V are reachable from here. @@ -74,11 +86,9 @@ unsafe impl<K: Send, V: Send> Send for RawTable<K, V> {} unsafe impl<K: Sync, V: Sync> Sync for RawTable<K, V> {} struct RawBucket<K, V> { - hash: *mut u64, - + hash: *mut HashUint, // We use *const to ensure covariance with respect to K and V - key: *const K, - val: *const V, + pair: *const (K, V), _marker: marker::PhantomData<(K, V)>, } @@ -137,15 +147,27 @@ pub struct GapThenFull<K, V, M> { /// buckets. #[derive(PartialEq, Copy, Clone)] pub struct SafeHash { - hash: u64, + hash: HashUint, } impl SafeHash { /// Peek at the hash value, which is guaranteed to be non-zero. #[inline(always)] - pub fn inspect(&self) -> u64 { + pub fn inspect(&self) -> HashUint { self.hash } + + #[inline(always)] + pub fn new(hash: u64) -> Self { + // We need to avoid 0 in order to prevent collisions with + // EMPTY_HASH. We can maintain our precious uniform distribution + // of initial indexes by unconditionally setting the MSB, + // effectively reducing the hashes by one bit. + // + // Truncate hash to fit in `HashUint`. + let hash_bits = size_of::<HashUint>() * 8; + SafeHash { hash: (1 << (hash_bits - 1)) | (hash as HashUint) } + } } /// We need to remove hashes of 0. That's reserved for empty buckets. @@ -157,33 +179,28 @@ pub fn make_hash<T: ?Sized, S>(hash_state: &S, t: &T) -> SafeHash { let mut state = hash_state.build_hasher(); t.hash(&mut state); - // We need to avoid 0 in order to prevent collisions with - // EMPTY_HASH. We can maintain our precious uniform distribution - // of initial indexes by unconditionally setting the MSB, - // effectively reducing 64-bits hashes to 63 bits. - SafeHash { hash: 0x8000_0000_0000_0000 | state.finish() } + SafeHash::new(state.finish()) } -// `replace` casts a `*u64` to a `*SafeHash`. Since we statically +// `replace` casts a `*HashUint` to a `*SafeHash`. Since we statically // ensure that a `FullBucket` points to an index with a non-zero hash, -// and a `SafeHash` is just a `u64` with a different name, this is +// and a `SafeHash` is just a `HashUint` with a different name, this is // safe. // // This test ensures that a `SafeHash` really IS the same size as a -// `u64`. If you need to change the size of `SafeHash` (and +// `HashUint`. If you need to change the size of `SafeHash` (and // consequently made this test fail), `replace` needs to be // modified to no longer assume this. #[test] -fn can_alias_safehash_as_u64() { - assert_eq!(size_of::<SafeHash>(), size_of::<u64>()) +fn can_alias_safehash_as_hash() { + assert_eq!(size_of::<SafeHash>(), size_of::<HashUint>()) } impl<K, V> RawBucket<K, V> { unsafe fn offset(self, count: isize) -> RawBucket<K, V> { RawBucket { hash: self.hash.offset(count), - key: self.key.offset(count), - val: self.val.offset(count), + pair: self.pair.offset(count), _marker: marker::PhantomData, } } @@ -371,8 +388,7 @@ impl<K, V, M> EmptyBucket<K, V, M> pub fn put(mut self, hash: SafeHash, key: K, value: V) -> FullBucket<K, V, M> { unsafe { *self.raw.hash = hash.inspect(); - ptr::write(self.raw.key as *mut K, key); - ptr::write(self.raw.val as *mut V, value); + ptr::write(self.raw.pair as *mut (K, V), (key, value)); self.table.borrow_table_mut().size += 1; } @@ -431,7 +447,7 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> FullBucket<K, V, M> { /// Gets references to the key and value at a given index. pub fn read(&self) -> (&K, &V) { - unsafe { (&*self.raw.key, &*self.raw.val) } + unsafe { (&(*self.raw.pair).0, &(*self.raw.pair).1) } } } @@ -448,13 +464,14 @@ impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> { unsafe { *self.raw.hash = EMPTY_BUCKET; + let (k, v) = ptr::read(self.raw.pair); (EmptyBucket { - raw: self.raw, - idx: self.idx, - table: self.table, - }, - ptr::read(self.raw.key), - ptr::read(self.raw.val)) + raw: self.raw, + idx: self.idx, + table: self.table, + }, + k, + v) } } } @@ -467,8 +484,7 @@ impl<K, V, M> FullBucket<K, V, M> pub fn replace(&mut self, h: SafeHash, k: K, v: V) -> (SafeHash, K, V) { unsafe { let old_hash = ptr::replace(self.raw.hash as *mut SafeHash, h); - let old_key = ptr::replace(self.raw.key as *mut K, k); - let old_val = ptr::replace(self.raw.val as *mut V, v); + let (old_key, old_val) = ptr::replace(self.raw.pair as *mut (K, V), (k, v)); (old_hash, old_key, old_val) } @@ -480,7 +496,8 @@ impl<K, V, M> FullBucket<K, V, M> { /// Gets mutable references to the key and value at a given index. pub fn read_mut(&mut self) -> (&mut K, &mut V) { - unsafe { (&mut *(self.raw.key as *mut K), &mut *(self.raw.val as *mut V)) } + let pair_mut = self.raw.pair as *mut (K, V); + unsafe { (&mut (*pair_mut).0, &mut (*pair_mut).1) } } } @@ -493,7 +510,7 @@ impl<'t, K, V, M> FullBucket<K, V, M> /// in exchange for this, the returned references have a longer lifetime /// than the references returned by `read()`. pub fn into_refs(self) -> (&'t K, &'t V) { - unsafe { (&*self.raw.key, &*self.raw.val) } + unsafe { (&(*self.raw.pair).0, &(*self.raw.pair).1) } } } @@ -503,7 +520,8 @@ impl<'t, K, V, M> FullBucket<K, V, M> /// This works similarly to `into_refs`, exchanging a bucket state /// for mutable references into the table. pub fn into_mut_refs(self) -> (&'t mut K, &'t mut V) { - unsafe { (&mut *(self.raw.key as *mut K), &mut *(self.raw.val as *mut V)) } + let pair_mut = self.raw.pair as *mut (K, V); + unsafe { (&mut (*pair_mut).0, &mut (*pair_mut).1) } } } @@ -518,8 +536,7 @@ impl<K, V, M> GapThenFull<K, V, M> pub fn shift(mut self) -> Option<GapThenFull<K, V, M>> { unsafe { *self.gap.raw.hash = mem::replace(&mut *self.full.raw.hash, EMPTY_BUCKET); - ptr::copy_nonoverlapping(self.full.raw.key, self.gap.raw.key as *mut K, 1); - ptr::copy_nonoverlapping(self.full.raw.val, self.gap.raw.val as *mut V, 1); + ptr::copy_nonoverlapping(self.full.raw.pair, self.gap.raw.pair as *mut (K, V), 1); } let FullBucket { raw: prev_raw, idx: prev_idx, .. } = self.full; @@ -561,49 +578,42 @@ fn test_rounding() { assert_eq!(round_up_to_next(5, 4), 8); } -// Returns a tuple of (key_offset, val_offset), +// Returns a tuple of (pairs_offset, end_of_pairs_offset), // from the start of a mallocated array. #[inline] fn calculate_offsets(hashes_size: usize, - keys_size: usize, - keys_align: usize, - vals_align: usize) + pairs_size: usize, + pairs_align: usize) -> (usize, usize, bool) { - let keys_offset = round_up_to_next(hashes_size, keys_align); - let (end_of_keys, oflo) = keys_offset.overflowing_add(keys_size); + let pairs_offset = round_up_to_next(hashes_size, pairs_align); + let (end_of_pairs, oflo) = pairs_offset.overflowing_add(pairs_size); - let vals_offset = round_up_to_next(end_of_keys, vals_align); - - (keys_offset, vals_offset, oflo) + (pairs_offset, end_of_pairs, oflo) } // Returns a tuple of (minimum required malloc alignment, hash_offset, // array_size), from the start of a mallocated array. fn calculate_allocation(hash_size: usize, hash_align: usize, - keys_size: usize, - keys_align: usize, - vals_size: usize, - vals_align: usize) + pairs_size: usize, + pairs_align: usize) -> (usize, usize, usize, bool) { let hash_offset = 0; - let (_, vals_offset, oflo) = calculate_offsets(hash_size, keys_size, keys_align, vals_align); - let (end_of_vals, oflo2) = vals_offset.overflowing_add(vals_size); + let (_, end_of_pairs, oflo) = calculate_offsets(hash_size, pairs_size, pairs_align); - let align = cmp::max(hash_align, cmp::max(keys_align, vals_align)); + let align = cmp::max(hash_align, pairs_align); - (align, hash_offset, end_of_vals, oflo || oflo2) + (align, hash_offset, end_of_pairs, oflo) } #[test] fn test_offset_calculation() { - assert_eq!(calculate_allocation(128, 8, 15, 1, 4, 4), - (8, 0, 148, false)); - assert_eq!(calculate_allocation(3, 1, 2, 1, 1, 1), (1, 0, 6, false)); - assert_eq!(calculate_allocation(6, 2, 12, 4, 24, 8), (8, 0, 48, false)); - assert_eq!(calculate_offsets(128, 15, 1, 4), (128, 144, false)); - assert_eq!(calculate_offsets(3, 2, 1, 1), (3, 5, false)); - assert_eq!(calculate_offsets(6, 12, 4, 8), (8, 24, false)); + assert_eq!(calculate_allocation(128, 8, 16, 8), (8, 0, 144, false)); + assert_eq!(calculate_allocation(3, 1, 2, 1), (1, 0, 5, false)); + assert_eq!(calculate_allocation(6, 2, 12, 4), (4, 0, 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)); } impl<K, V> RawTable<K, V> { @@ -614,18 +624,17 @@ impl<K, V> RawTable<K, V> { return RawTable { size: 0, capacity: 0, - hashes: Unique::new(EMPTY as *mut u64), + hashes: Unique::new(EMPTY as *mut HashUint), marker: marker::PhantomData, }; } // No need for `checked_mul` before a more restrictive check performed // later in this method. - let hashes_size = capacity * size_of::<u64>(); - let keys_size = capacity * size_of::<K>(); - let vals_size = capacity * size_of::<V>(); + 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 three + // 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. @@ -633,32 +642,25 @@ impl<K, V> RawTable<K, V> { // 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 (malloc_alignment, hash_offset, size, oflo) = calculate_allocation(hashes_size, - align_of::<u64>(), - keys_size, - align_of::<K>(), - vals_size, - align_of::<V>()); - + let (alignment, hash_offset, size, oflo) = calculate_allocation(hashes_size, + align_of::<HashUint>(), + pairs_size, + align_of::<(K, V)>()); assert!(!oflo, "capacity overflow"); // One check for overflow that covers calculation and rounding of size. - let size_of_bucket = size_of::<u64>() - .checked_add(size_of::<K>()) - .unwrap() - .checked_add(size_of::<V>()) - .unwrap(); + let size_of_bucket = size_of::<HashUint>().checked_add(size_of::<(K, V)>()).unwrap(); assert!(size >= capacity.checked_mul(size_of_bucket) - .expect("capacity overflow"), + .expect("capacity overflow"), "capacity overflow"); - let buffer = allocate(size, malloc_alignment); + let buffer = allocate(size, alignment); if buffer.is_null() { ::alloc::oom() } - let hashes = buffer.offset(hash_offset as isize) as *mut u64; + let hashes = buffer.offset(hash_offset as isize) as *mut HashUint; RawTable { capacity: capacity, @@ -669,20 +671,17 @@ impl<K, V> RawTable<K, V> { } fn first_bucket_raw(&self) -> RawBucket<K, V> { - let hashes_size = self.capacity * size_of::<u64>(); - let keys_size = self.capacity * size_of::<K>(); - - let buffer = *self.hashes as *const u8; - let (keys_offset, vals_offset, oflo) = calculate_offsets(hashes_size, - keys_size, - align_of::<K>(), - align_of::<V>()); + let hashes_size = self.capacity * size_of::<HashUint>(); + let pairs_size = self.capacity * size_of::<(K, V)>(); + + let buffer = *self.hashes as *mut u8; + let (pairs_offset, _, oflo) = + calculate_offsets(hashes_size, pairs_size, align_of::<(K, V)>()); debug_assert!(!oflo, "capacity overflow"); unsafe { RawBucket { hash: *self.hashes, - key: buffer.offset(keys_offset as isize) as *const K, - val: buffer.offset(vals_offset as isize) as *const V, + pair: buffer.offset(pairs_offset as isize) as *const _, _marker: marker::PhantomData, } } @@ -776,7 +775,7 @@ impl<K, V> RawTable<K, V> { /// this interface is safe, it's not used outside this module. struct RawBuckets<'a, K, V> { raw: RawBucket<K, V>, - hashes_end: *mut u64, + hashes_end: *mut HashUint, // Strictly speaking, this should be &'a (K,V), but that would // require that K:'a, and we often use RawBuckets<'static...> for @@ -822,7 +821,7 @@ impl<'a, K, V> Iterator for RawBuckets<'a, K, V> { /// the table's remaining entries. It's used in the implementation of Drop. struct RevMoveBuckets<'a, K, V> { raw: RawBucket<K, V>, - hashes_end: *mut u64, + hashes_end: *mut HashUint, elems_left: usize, // As above, `&'a (K,V)` would seem better, but we often use @@ -847,7 +846,7 @@ impl<'a, K, V> Iterator for RevMoveBuckets<'a, K, V> { if *self.raw.hash != EMPTY_BUCKET { self.elems_left -= 1; - return Some((ptr::read(self.raw.key), ptr::read(self.raw.val))); + return Some(ptr::read(self.raw.pair)); } } } @@ -912,7 +911,7 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> { fn next(&mut self) -> Option<(&'a K, &'a V)> { self.iter.next().map(|bucket| { self.elems_left -= 1; - unsafe { (&*bucket.key, &*bucket.val) } + unsafe { (&(*bucket.pair).0, &(*bucket.pair).1) } }) } @@ -932,7 +931,8 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> { fn next(&mut self) -> Option<(&'a K, &'a mut V)> { self.iter.next().map(|bucket| { self.elems_left -= 1; - unsafe { (&*bucket.key, &mut *(bucket.val as *mut V)) } + let pair_mut = bucket.pair as *mut (K, V); + unsafe { (&(*pair_mut).0, &mut (*pair_mut).1) } }) } @@ -953,7 +953,8 @@ impl<K, V> Iterator for IntoIter<K, V> { self.iter.next().map(|bucket| { self.table.size -= 1; unsafe { - (SafeHash { hash: *bucket.hash }, ptr::read(bucket.key), ptr::read(bucket.val)) + let (k, v) = ptr::read(bucket.pair); + (SafeHash { hash: *bucket.hash }, k, v) } }) } @@ -977,9 +978,8 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { self.iter.next().map(|bucket| { unsafe { (**self.table).size -= 1; - (SafeHash { hash: ptr::replace(bucket.hash, EMPTY_BUCKET) }, - ptr::read(bucket.key), - ptr::read(bucket.val)) + let (k, v) = ptr::read(bucket.pair); + (SafeHash { hash: ptr::replace(bucket.hash, EMPTY_BUCKET) }, k, v) } }) } @@ -991,9 +991,7 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { } impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> { fn len(&self) -> usize { - unsafe { - (**self.table).size() - } + unsafe { (**self.table).size() } } } @@ -1020,8 +1018,7 @@ impl<K: Clone, V: Clone> Clone for RawTable<K, V> { (full.hash(), k.clone(), v.clone()) }; *new_buckets.raw.hash = h.inspect(); - ptr::write(new_buckets.raw.key as *mut K, k); - ptr::write(new_buckets.raw.val as *mut V, v); + ptr::write(new_buckets.raw.pair as *mut (K, V), (k, v)); } Empty(..) => { *new_buckets.raw.hash = EMPTY_BUCKET; @@ -1058,15 +1055,12 @@ impl<K, V> Drop for RawTable<K, V> { } } - let hashes_size = self.capacity * size_of::<u64>(); - let keys_size = self.capacity * size_of::<K>(); - let vals_size = self.capacity * size_of::<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::<u64>(), - keys_size, - align_of::<K>(), - vals_size, - align_of::<V>()); + align_of::<HashUint>(), + pairs_size, + align_of::<(K, V)>()); debug_assert!(!oflo, "should be impossible"); diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index 9b13d542300..b9e92a01b2f 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -15,7 +15,7 @@ //! standard implementations, it should be possible for two libraries to //! communicate without significant data conversion. //! -//! To get this out of the way: you should probably just use `Vec` or `HashMap`. +//! To get this out of the way: you should probably just use [`Vec`] or [`HashMap`]. //! These two collections cover most use cases for generic data storage and //! processing. They are exceptionally good at doing what they do. All the other //! collections in the standard library have specific use cases where they are @@ -25,10 +25,10 @@ //! //! Rust's collections can be grouped into four major categories: //! -//! * Sequences: `Vec`, `VecDeque`, `LinkedList` -//! * Maps: `HashMap`, `BTreeMap` -//! * Sets: `HashSet`, `BTreeSet` -//! * Misc: `BinaryHeap` +//! * Sequences: [`Vec`], [`VecDeque`], [`LinkedList`] +//! * Maps: [`HashMap`], [`BTreeMap`] +//! * Sets: [`HashSet`], [`BTreeSet`] +//! * Misc: [`BinaryHeap`] //! //! # When Should You Use Which Collection? //! @@ -46,13 +46,13 @@ //! * You want a heap-allocated array. //! //! ### Use a `VecDeque` when: -//! * You want a `Vec` that supports efficient insertion at both ends of the +//! * You want a [`Vec`] that supports efficient insertion at both ends of the //! sequence. //! * You want a queue. //! * You want a double-ended queue (deque). //! //! ### Use a `LinkedList` when: -//! * You want a `Vec` or `VecDeque` of unknown size, and can't tolerate +//! * You want a [`Vec`] or [`VecDeque`] of unknown size, and can't tolerate //! amortization. //! * You want to efficiently split and append lists. //! * You are *absolutely* certain you *really*, *truly*, want a doubly linked @@ -92,38 +92,38 @@ //! Throughout the documentation, we will follow a few conventions. For all //! operations, the collection's size is denoted by n. If another collection is //! involved in the operation, it contains m elements. Operations which have an -//! *amortized* cost are suffixed with a `*`. Operations with an *expected* +//! *amortized* cost are suffixed with a `*`. Operations with an *expected* //! cost are suffixed with a `~`. //! //! All amortized costs are for the potential need to resize when capacity is -//! exhausted. If a resize occurs it will take O(n) time. Our collections never +//! exhausted. If a resize occurs it will take O(n) time. Our collections never //! automatically shrink, so removal operations aren't amortized. Over a //! sufficiently large series of operations, the average cost per operation will //! deterministically equal the given cost. //! -//! Only HashMap has expected costs, due to the probabilistic nature of hashing. -//! It is theoretically possible, though very unlikely, for HashMap to +//! Only [`HashMap`] has expected costs, due to the probabilistic nature of hashing. +//! It is theoretically possible, though very unlikely, for [`HashMap`] to //! experience worse performance. //! //! ## Sequences //! -//! | | get(i) | insert(i) | remove(i) | append | split_off(i) | -//! |--------------|----------------|-----------------|----------------|--------|----------------| -//! | Vec | O(1) | O(n-i)* | O(n-i) | O(m)* | O(n-i) | -//! | VecDeque | O(1) | O(min(i, n-i))* | O(min(i, n-i)) | O(m)* | O(min(i, n-i)) | -//! | LinkedList | O(min(i, n-i)) | O(min(i, n-i)) | O(min(i, n-i)) | O(1) | O(min(i, n-i)) | +//! | | get(i) | insert(i) | remove(i) | append | split_off(i) | +//! |----------------|----------------|-----------------|----------------|--------|----------------| +//! | [`Vec`] | O(1) | O(n-i)* | O(n-i) | O(m)* | O(n-i) | +//! | [`VecDeque`] | O(1) | O(min(i, n-i))* | O(min(i, n-i)) | O(m)* | O(min(i, n-i)) | +//! | [`LinkedList`] | O(min(i, n-i)) | O(min(i, n-i)) | O(min(i, n-i)) | O(1) | O(min(i, n-i)) | //! -//! Note that where ties occur, Vec is generally going to be faster than VecDeque, and VecDeque -//! is generally going to be faster than LinkedList. +//! Note that where ties occur, [`Vec`] is generally going to be faster than [`VecDeque`], and +//! [`VecDeque`] is generally going to be faster than [`LinkedList`]. //! //! ## Maps //! //! For Sets, all operations have the cost of the equivalent Map operation. //! -//! | | get | insert | remove | predecessor | append | -//! |----------|-----------|----------|----------|-------------|--------| -//! | HashMap | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | -//! | BTreeMap | O(log n) | O(log n) | O(log n) | O(log n) | O(n+m) | +//! | | get | insert | remove | predecessor | append | +//! |--------------|-----------|----------|----------|-------------|--------| +//! | [`HashMap`] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | +//! | [`BTreeMap`] | O(log n) | O(log n) | O(log n) | O(log n) | O(n+m) | //! //! # Correct and Efficient Usage of Collections //! @@ -136,7 +136,7 @@ //! ## Capacity Management //! //! Many collections provide several constructors and methods that refer to -//! "capacity". These collections are generally built on top of an array. +//! "capacity". These collections are generally built on top of an array. //! Optimally, this array would be exactly the right size to fit only the //! elements stored in the collection, but for the collection to do this would //! be very inefficient. If the backing array was exactly the right size at all @@ -157,29 +157,29 @@ //! information to do this itself. Therefore, it is up to us programmers to give //! it hints. //! -//! Any `with_capacity` constructor will instruct the collection to allocate +//! Any `with_capacity()` constructor will instruct the collection to allocate //! enough space for the specified number of elements. Ideally this will be for //! exactly that many elements, but some implementation details may prevent -//! this. `Vec` and `VecDeque` can be relied on to allocate exactly the -//! requested amount, though. Use `with_capacity` when you know exactly how many +//! this. [`Vec`] and [`VecDeque`] can be relied on to allocate exactly the +//! requested amount, though. Use `with_capacity()` when you know exactly how many //! elements will be inserted, or at least have a reasonable upper-bound on that //! number. //! -//! When anticipating a large influx of elements, the `reserve` family of +//! When anticipating a large influx of elements, the `reserve()` family of //! methods can be used to hint to the collection how much room it should make -//! for the coming items. As with `with_capacity`, the precise behavior of +//! for the coming items. As with `with_capacity()`, the precise behavior of //! these methods will be specific to the collection of interest. //! //! For optimal performance, collections will generally avoid shrinking -//! themselves. If you believe that a collection will not soon contain any more -//! elements, or just really need the memory, the `shrink_to_fit` method prompts +//! themselves. If you believe that a collection will not soon contain any more +//! elements, or just really need the memory, the `shrink_to_fit()` method prompts //! the collection to shrink the backing array to the minimum size capable of //! holding its elements. //! //! Finally, if ever you're interested in what the actual capacity of the -//! collection is, most collections provide a `capacity` method to query this -//! information on demand. This can be useful for debugging purposes, or for -//! use with the `reserve` methods. +//! collection is, most collections provide a `capacity()` method to query this +//! information on demand. This can be useful for debugging purposes, or for +//! use with the `reserve()` methods. //! //! ## Iterators //! @@ -194,15 +194,15 @@ //! //! All of the standard collections provide several iterators for performing //! bulk manipulation of their contents. The three primary iterators almost -//! every collection should provide are `iter`, `iter_mut`, and `into_iter`. +//! every collection should provide are `iter()`, `iter_mut()`, and `into_iter()`. //! Some of these are not provided on collections where it would be unsound or //! unreasonable to provide them. //! -//! `iter` provides an iterator of immutable references to all the contents of a -//! collection in the most "natural" order. For sequence collections like `Vec`, +//! `iter()` provides an iterator of immutable references to all the contents of a +//! collection in the most "natural" order. For sequence collections like [`Vec`], //! this means the items will be yielded in increasing order of index starting -//! at 0. For ordered collections like `BTreeMap`, this means that the items -//! will be yielded in sorted order. For unordered collections like `HashMap`, +//! at 0. For ordered collections like [`BTreeMap`], this means that the items +//! will be yielded in sorted order. For unordered collections like [`HashMap`], //! the items will be yielded in whatever order the internal representation made //! most convenient. This is great for reading through all the contents of the //! collection. @@ -214,8 +214,8 @@ //! } //! ``` //! -//! `iter_mut` provides an iterator of *mutable* references in the same order as -//! `iter`. This is great for mutating all the contents of the collection. +//! `iter_mut()` provides an iterator of *mutable* references in the same order as +//! `iter()`. This is great for mutating all the contents of the collection. //! //! ``` //! let mut vec = vec![1, 2, 3, 4]; @@ -224,12 +224,12 @@ //! } //! ``` //! -//! `into_iter` transforms the actual collection into an iterator over its +//! `into_iter()` transforms the actual collection into an iterator over its //! contents by-value. This is great when the collection itself is no longer -//! needed, and the values are needed elsewhere. Using `extend` with `into_iter` +//! needed, and the values are needed elsewhere. Using `extend()` with `into_iter()` //! is the main way that contents of one collection are moved into another. -//! `extend` automatically calls `into_iter`, and takes any `T: IntoIterator`. -//! Calling `collect` on an iterator itself is also a great way to convert one +//! `extend()` automatically calls `into_iter()`, and takes any `T: `[`IntoIterator`]. +//! Calling `collect()` on an iterator itself is also a great way to convert one //! collection into another. Both of these methods should internally use the //! capacity management tools discussed in the previous section to do this as //! efficiently as possible. @@ -248,9 +248,9 @@ //! ``` //! //! Iterators also provide a series of *adapter* methods for performing common -//! threads to sequences. Among the adapters are functional favorites like `map`, -//! `fold`, `skip`, and `take`. Of particular interest to collections is the -//! `rev` adapter, that reverses any iterator that supports this operation. Most +//! threads to sequences. Among the adapters are functional favorites like `map()`, +//! `fold()`, `skip()` and `take()`. Of particular interest to collections is the +//! `rev()` adapter, that reverses any iterator that supports this operation. Most //! collections provide reversible iterators as the way to iterate over them in //! reverse order. //! @@ -263,27 +263,27 @@ //! //! Several other collection methods also return iterators to yield a sequence //! of results but avoid allocating an entire collection to store the result in. -//! This provides maximum flexibility as `collect` or `extend` can be called to +//! This provides maximum flexibility as `collect()` or `extend()` can be called to //! "pipe" the sequence into any collection if desired. Otherwise, the sequence //! can be looped over with a `for` loop. The iterator can also be discarded //! after partial use, preventing the computation of the unused items. //! //! ## Entries //! -//! The `entry` API is intended to provide an efficient mechanism for +//! The `entry()` API is intended to provide an efficient mechanism for //! manipulating the contents of a map conditionally on the presence of a key or //! not. The primary motivating use case for this is to provide efficient //! accumulator maps. For instance, if one wishes to maintain a count of the //! number of times each key has been seen, they will have to perform some //! conditional logic on whether this is the first time the key has been seen or -//! not. Normally, this would require a `find` followed by an `insert`, +//! not. Normally, this would require a `find()` followed by an `insert()`, //! effectively duplicating the search effort on each insertion. //! //! When a user calls `map.entry(&key)`, the map will search for the key and //! then yield a variant of the `Entry` enum. //! //! If a `Vacant(entry)` is yielded, then the key *was not* found. In this case -//! the only valid operation is to `insert` a value into the entry. When this is +//! the only valid operation is to `insert()` a value into the entry. When this is //! done, the vacant entry is consumed and converted into a mutable reference to //! the value that was inserted. This allows for further manipulation of the //! value beyond the lifetime of the search itself. This is useful if complex @@ -291,14 +291,14 @@ //! just inserted. //! //! If an `Occupied(entry)` is yielded, then the key *was* found. In this case, -//! the user has several options: they can `get`, `insert`, or `remove` the +//! the user has several options: they can `get()`, `insert()` or `remove()` the //! value of the occupied entry. Additionally, they can convert the occupied //! entry into a mutable reference to its value, providing symmetry to the -//! vacant `insert` case. +//! vacant `insert()` case. //! //! ### Examples //! -//! Here are the two primary ways in which `entry` is used. First, a simple +//! Here are the two primary ways in which `entry()` is used. First, a simple //! example where the logic performed on the values is trivial. //! //! #### Counting the number of times each character in a string occurs @@ -322,7 +322,7 @@ //! ``` //! //! When the logic to be performed on the value is more complex, we may simply -//! use the `entry` API to ensure that the value is initialized, and perform the +//! use the `entry()` API to ensure that the value is initialized and perform the //! logic afterwards. //! //! #### Tracking the inebriation of customers at a bar @@ -406,6 +406,16 @@ //! // ...but the key hasn't changed. b is still "baz", not "xyz". //! assert_eq!(map.keys().next().unwrap().b, "baz"); //! ``` +//! +//! [`Vec`]: ../../std/vec/struct.Vec.html +//! [`HashMap`]: ../../std/collections/struct.HashMap.html +//! [`VecDeque`]: ../../std/collections/struct.VecDeque.html +//! [`LinkedList`]: ../../std/collections/struct.LinkedList.html +//! [`BTreeMap`]: ../../std/collections/struct.BTreeMap.html +//! [`HashSet`]: ../../std/collections/struct.HashSet.html +//! [`BTreeSet`]: ../../std/collections/struct.BTreeSet.html +//! [`BinaryHeap`]: ../../std/collections/struct.BinaryHeap.html +//! [`IntoIterator`]: ../../std/iter/trait.IntoIterator.html #![stable(feature = "rust1", since = "1.0.0")] @@ -425,6 +435,9 @@ pub use self::hash_map::HashMap; #[stable(feature = "rust1", since = "1.0.0")] pub use self::hash_set::HashSet; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core_collections::range; + mod hash; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 63bf051c9bc..e29dbe35c5a 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -21,6 +21,7 @@ use ffi::{OsStr, OsString}; use fmt; use io; use path::{Path, PathBuf}; +use sys; use sys::os as os_imp; /// Returns the current working directory as a `PathBuf`. @@ -83,7 +84,7 @@ pub struct VarsOs { inner: os_imp::Env } /// environment variables of the current process. /// /// The returned iterator contains a snapshot of the process's environment -/// variables at the time of this invocation, modifications to environment +/// variables at the time of this invocation. Modifications to environment /// variables afterwards will not be reflected in the returned iterator. /// /// # Panics @@ -112,7 +113,7 @@ pub fn vars() -> Vars { /// environment variables of the current process. /// /// The returned iterator contains a snapshot of the process's environment -/// variables at the time of this invocation, modifications to environment +/// variables at the time of this invocation. Modifications to environment /// variables afterwards will not be reflected in the returned iterator. /// /// # Examples @@ -557,7 +558,7 @@ pub struct Args { inner: ArgsOs } /// /// This structure is created through the `std::env::args_os` method. #[stable(feature = "env", since = "1.0.0")] -pub struct ArgsOs { inner: os_imp::Args } +pub struct ArgsOs { inner: sys::args::Args } /// Returns the arguments which this program was started with (normally passed /// via the command line). @@ -606,7 +607,7 @@ pub fn args() -> Args { /// ``` #[stable(feature = "env", since = "1.0.0")] pub fn args_os() -> ArgsOs { - ArgsOs { inner: os_imp::args() } + ArgsOs { inner: sys::args::args() } } #[stable(feature = "env", since = "1.0.0")] @@ -649,6 +650,8 @@ impl DoubleEndedIterator for ArgsOs { /// Constants associated with the current target #[stable(feature = "env", since = "1.0.0")] pub mod consts { + use sys::env::os; + /// A string describing the architecture of the CPU that is currently /// in use. /// @@ -673,7 +676,7 @@ pub mod consts { /// - unix /// - windows #[stable(feature = "env", since = "1.0.0")] - pub const FAMILY: &'static str = super::os::FAMILY; + pub const FAMILY: &'static str = os::FAMILY; /// A string describing the specific operating system in use. /// Example value is `linux`. @@ -692,7 +695,7 @@ pub mod consts { /// - android /// - windows #[stable(feature = "env", since = "1.0.0")] - pub const OS: &'static str = super::os::OS; + pub const OS: &'static str = os::OS; /// Specifies the filename prefix used for shared libraries on this /// platform. Example value is `lib`. @@ -702,7 +705,7 @@ pub mod consts { /// - lib /// - `""` (an empty string) #[stable(feature = "env", since = "1.0.0")] - pub const DLL_PREFIX: &'static str = super::os::DLL_PREFIX; + pub const DLL_PREFIX: &'static str = os::DLL_PREFIX; /// Specifies the filename suffix used for shared libraries on this /// platform. Example value is `.so`. @@ -713,7 +716,7 @@ pub mod consts { /// - .dylib /// - .dll #[stable(feature = "env", since = "1.0.0")] - pub const DLL_SUFFIX: &'static str = super::os::DLL_SUFFIX; + pub const DLL_SUFFIX: &'static str = os::DLL_SUFFIX; /// Specifies the file extension used for shared libraries on this /// platform that goes after the dot. Example value is `so`. @@ -724,7 +727,7 @@ pub mod consts { /// - dylib /// - dll #[stable(feature = "env", since = "1.0.0")] - pub const DLL_EXTENSION: &'static str = super::os::DLL_EXTENSION; + pub const DLL_EXTENSION: &'static str = os::DLL_EXTENSION; /// Specifies the filename suffix used for executable binaries on this /// platform. Example value is `.exe`. @@ -736,7 +739,7 @@ pub mod consts { /// - .pexe /// - `""` (an empty string) #[stable(feature = "env", since = "1.0.0")] - pub const EXE_SUFFIX: &'static str = super::os::EXE_SUFFIX; + pub const EXE_SUFFIX: &'static str = os::EXE_SUFFIX; /// Specifies the file extension, if any, used for executable binaries /// on this platform. Example value is `exe`. @@ -746,161 +749,7 @@ pub mod consts { /// - exe /// - `""` (an empty string) #[stable(feature = "env", since = "1.0.0")] - pub const EXE_EXTENSION: &'static str = super::os::EXE_EXTENSION; - -} - -#[cfg(target_os = "linux")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "linux"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "macos")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "macos"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".dylib"; - pub const DLL_EXTENSION: &'static str = "dylib"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "ios")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "ios"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".dylib"; - pub const DLL_EXTENSION: &'static str = "dylib"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "freebsd")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "freebsd"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "dragonfly")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "dragonfly"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "bitrig")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "bitrig"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "netbsd")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "netbsd"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "openbsd")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "openbsd"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "android")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "android"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "solaris")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "solaris"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; -} - -#[cfg(target_os = "windows")] -mod os { - pub const FAMILY: &'static str = "windows"; - pub const OS: &'static str = "windows"; - pub const DLL_PREFIX: &'static str = ""; - pub const DLL_SUFFIX: &'static str = ".dll"; - pub const DLL_EXTENSION: &'static str = "dll"; - pub const EXE_SUFFIX: &'static str = ".exe"; - pub const EXE_EXTENSION: &'static str = "exe"; -} - -#[cfg(all(target_os = "nacl", not(target_arch = "le32")))] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "nacl"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ".nexe"; - pub const EXE_EXTENSION: &'static str = "nexe"; -} -#[cfg(all(target_os = "nacl", target_arch = "le32"))] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "pnacl"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".pso"; - pub const DLL_EXTENSION: &'static str = "pso"; - pub const EXE_SUFFIX: &'static str = ".pexe"; - pub const EXE_EXTENSION: &'static str = "pexe"; -} - -#[cfg(target_os = "emscripten")] -mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "emscripten"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ".js"; - pub const EXE_EXTENSION: &'static str = "js"; + pub const EXE_EXTENSION: &'static str = os::EXE_EXTENSION; } #[cfg(target_arch = "x86")] @@ -958,6 +807,11 @@ mod arch { pub const ARCH: &'static str = "asmjs"; } +#[cfg(target_arch = "wasm32")] +mod arch { + pub const ARCH: &'static str = "wasm32"; +} + #[cfg(test)] mod tests { use super::*; @@ -1006,6 +860,7 @@ mod tests { } #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn test_var_big() { let mut s = "".to_string(); let mut i = 0; @@ -1019,6 +874,7 @@ mod tests { } #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn test_self_exe_path() { let path = current_exe(); assert!(path.is_ok()); @@ -1029,6 +885,7 @@ mod tests { } #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn test_env_set_get_huge() { let n = make_rand_name(); let s = repeat("x").take(10000).collect::<String>(); diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 16290620010..a1909b0f957 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -13,9 +13,9 @@ //! # The `Error` trait //! //! `Error` is a trait representing the basic expectations for error values, -//! i.e. values of type `E` in `Result<T, E>`. At a minimum, errors must provide +//! i.e. values of type `E` in [`Result<T, E>`]. At a minimum, errors must provide //! a description, but they may optionally provide additional detail (via -//! `Display`) and cause chain information: +//! [`Display`]) and cause chain information: //! //! ``` //! use std::fmt::Display; @@ -27,12 +27,16 @@ //! } //! ``` //! -//! The `cause` method is generally used when errors cross "abstraction +//! The [`cause`] method is generally used when errors cross "abstraction //! boundaries", i.e. when a one module must report an error that is "caused" //! by an error from a lower-level module. This setup makes it possible for the //! high-level module to provide its own errors that do not commit to any //! particular implementation, but also reveal some of its implementation for -//! debugging via `cause` chains. +//! debugging via [`cause`] chains. +//! +//! [`Result<T, E>`]: ../result/enum.Result.html +//! [`Display`]: ../fmt/trait.Display.html +//! [`cause`]: trait.Error.html#method.cause #![stable(feature = "rust1", since = "1.0.0")] @@ -51,7 +55,6 @@ use any::TypeId; use cell; use char; use fmt::{self, Debug, Display}; -use marker::Reflect; use mem::transmute; use num; use str; @@ -59,12 +62,14 @@ use string; /// Base functionality for all errors in Rust. #[stable(feature = "rust1", since = "1.0.0")] -pub trait Error: Debug + Display + Reflect { +pub trait Error: Debug + Display { /// A short description of the error. /// - /// The description should not contain newlines or sentence-ending - /// punctuation, to facilitate embedding in larger user-facing - /// strings. + /// The description should only be used for a simple message. + /// It should not contain newlines or sentence-ending punctuation, + /// to facilitate embedding in larger user-facing strings. + /// For showing formatted error messages with more information see + /// [Display](https://doc.rust-lang.org/std/fmt/trait.Display.html). /// /// # Examples /// @@ -288,15 +293,15 @@ impl Error for fmt::Error { } } -#[unstable(feature = "try_borrow", issue = "35070")] -impl<'a, T: ?Sized + Reflect> Error for cell::BorrowError<'a, T> { +#[stable(feature = "try_borrow", since = "1.13.0")] +impl Error for cell::BorrowError { fn description(&self) -> &str { "already mutably borrowed" } } -#[unstable(feature = "try_borrow", issue = "35070")] -impl<'a, T: ?Sized + Reflect> Error for cell::BorrowMutError<'a, T> { +#[stable(feature = "try_borrow", since = "1.13.0")] +impl Error for cell::BorrowMutError { fn description(&self) -> &str { "already borrowed" } diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 1c449712e1f..3ad5b5627d3 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -146,19 +146,19 @@ pub struct CStr { /// An error returned from `CString::new` to indicate that a nul byte was found /// in the vector provided. -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct NulError(usize, Vec<u8>); /// An error returned from `CStr::from_bytes_with_nul` to indicate that a nul /// byte was found too early in the slice provided or one wasn't found at all. -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "cstr_from_bytes", since = "1.10.0")] pub struct FromBytesWithNulError { _a: () } /// An error returned from `CString::into_string` to indicate that a UTF-8 error /// was encountered during the conversion. -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "cstring_into", since = "1.7.0")] pub struct IntoStringError { inner: CString, @@ -228,9 +228,14 @@ impl CString { /// Retakes ownership of a `CString` that was transferred to C. /// + /// Additionally, the length of the string will be recalculated from the pointer. + /// + /// # Safety + /// /// This should only ever be called with a pointer that was earlier - /// obtained by calling `into_raw` on a `CString`. Additionally, the length - /// of the string will be recalculated from the pointer. + /// obtained by calling `into_raw` on a `CString`. Other usage (e.g. trying to take + /// ownership of a string that was allocated by foreign code) is likely to lead + /// to undefined behavior or allocator corruption. #[stable(feature = "cstr_memory", since = "1.4.0")] pub unsafe fn from_raw(ptr: *mut c_char) -> CString { let len = libc::strlen(ptr) + 1; // Including the NUL byte @@ -309,9 +314,11 @@ impl CString { } // Turns this `CString` into an empty string to prevent -// memory unsafe code from working by accident. +// memory unsafe code from working by accident. Inline +// to prevent LLVM from optimizing it away in debug builds. #[stable(feature = "cstring_drop", since = "1.13.0")] impl Drop for CString { + #[inline] fn drop(&mut self) { unsafe { *self.inner.get_unchecked_mut(0) = 0; } } @@ -719,7 +726,8 @@ mod tests { use super::*; use os::raw::c_char; use borrow::Cow::{Borrowed, Owned}; - use hash::{SipHasher, Hash, Hasher}; + use hash::{Hash, Hasher}; + use collections::hash_map::DefaultHasher; #[test] fn c_to_rust() { @@ -801,10 +809,10 @@ mod tests { let ptr = data.as_ptr() as *const c_char; let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) }; - let mut s = SipHasher::new_with_keys(0, 0); + let mut s = DefaultHasher::new(); cstr.hash(&mut s); let cstr_hash = s.finish(); - let mut s = SipHasher::new_with_keys(0, 0); + let mut s = DefaultHasher::new(); CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s); let cstring_hash = s.finish(); diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index d93d3c73622..84b50f04463 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -53,17 +53,6 @@ impl OsString { OsString { inner: Buf::from_string(String::new()) } } - #[cfg(unix)] - fn _from_bytes(vec: Vec<u8>) -> Option<OsString> { - use os::unix::ffi::OsStringExt; - Some(OsString::from_vec(vec)) - } - - #[cfg(windows)] - fn _from_bytes(vec: Vec<u8>) -> Option<OsString> { - String::from_utf8(vec).ok().map(OsString::from) - } - /// Converts to an `OsStr` slice. #[stable(feature = "rust1", since = "1.0.0")] pub fn as_os_str(&self) -> &OsStr { diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 698ec4f1b73..df5741d00a2 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -83,6 +83,7 @@ pub struct Metadata(fs_imp::FileAttr); /// /// [`io::Result`]: ../io/type.Result.html #[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] pub struct ReadDir(fs_imp::ReadDir); /// Entries returned by the [`ReadDir`] iterator. @@ -1055,6 +1056,15 @@ impl DirEntry { } } +#[stable(feature = "dir_entry_debug", since = "1.13.0")] +impl fmt::Debug for DirEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("DirEntry") + .field(&self.path()) + .finish() + } +} + impl AsInner<fs_imp::DirEntry> for DirEntry { fn as_inner(&self) -> &fs_imp::DirEntry { &self.0 } } @@ -1677,7 +1687,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use io::prelude::*; @@ -1895,6 +1905,130 @@ mod tests { } #[test] + fn file_test_io_eof() { + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_eof.txt"); + let mut buf = [0; 256]; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + assert_eq!(check!(rw.read(&mut buf)), 0); + assert_eq!(check!(rw.read(&mut buf)), 0); + } + check!(fs::remove_file(&filename)); + } + + #[test] + #[cfg(unix)] + fn file_test_io_read_write_at() { + use os::unix::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt"); + let mut buf = [0; 256]; + let write1 = "asdf"; + let write2 = "qwer-"; + let write3 = "-zxcv"; + let content = "qwer-asdf-zxcv"; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0")); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.read(&mut buf)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + } + { + let mut read = check!(File::open(&filename)); + assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(read.read(&mut buf)), write3.len()); + assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.read_at(&mut buf, 14)), 0); + assert_eq!(check!(read.read_at(&mut buf, 15)), 0); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + } + check!(fs::remove_file(&filename)); + } + + #[test] + #[cfg(windows)] + fn file_test_io_seek_read_write() { + use os::windows::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt"); + let mut buf = [0; 256]; + let write1 = "asdf"; + let write2 = "qwer-"; + let write3 = "-zxcv"; + let content = "qwer-asdf-zxcv"; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); + assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.read(&mut buf)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len()); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14); + } + { + let mut read = check!(File::open(&filename)); + assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.read(&mut buf)), write3.len()); + assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek_read(&mut buf, 14)), 0); + assert_eq!(check!(read.seek_read(&mut buf, 15)), 0); + } + check!(fs::remove_file(&filename)); + } + + #[test] fn file_test_stat_is_correct_on_is_file() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); @@ -2212,8 +2346,8 @@ mod tests { check!(fs::set_permissions(&out, attr.permissions())); } - #[cfg(windows)] #[test] + #[cfg(windows)] fn copy_file_preserves_streams() { let tmp = tmpdir(); check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); @@ -2642,6 +2776,17 @@ mod tests { } #[test] + fn dir_entry_debug() { + let tmpdir = tmpdir(); + File::create(&tmpdir.join("b")).unwrap(); + let mut read_dir = tmpdir.path().read_dir().unwrap(); + let dir_entry = read_dir.next().unwrap().unwrap(); + let actual = format!("{:?}", dir_entry); + let expected = format!("DirEntry({:?})", dir_entry.0.path()); + assert_eq!(actual, expected); + } + + #[test] fn read_dir_not_found() { let res = fs::read_dir("/path/that/does/not/exist"); assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound); diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index dbb45d54f38..44dd4e9874a 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -12,7 +12,6 @@ use io::prelude::*; -use marker::Reflect; use cmp; use error; use fmt; @@ -21,11 +20,15 @@ use memchr; /// The `BufReader` struct adds buffering to any reader. /// -/// It can be excessively inefficient to work directly with a `Read` instance. -/// For example, every call to `read` on `TcpStream` results in a system call. -/// A `BufReader` performs large, infrequent reads on the underlying `Read` +/// It can be excessively inefficient to work directly with a [`Read`] instance. +/// For example, every call to [`read`] on [`TcpStream`] results in a system call. +/// A `BufReader` performs large, infrequent reads on the underlying [`Read`] /// and maintains an in-memory buffer of the results. /// +/// [`Read`]: ../../std/io/trait.Read.html +/// [`read`]: ../../std/net/struct.TcpStream.html#method.read +/// [`TcpStream`]: ../../std/net/struct.TcpStream.html +/// /// # Examples /// /// ``` @@ -216,8 +219,8 @@ impl<R: Seek> Seek for BufReader<R> { /// /// Seeking always discards the internal buffer, even if the seek position /// would otherwise fall within it. This guarantees that calling - /// `.unwrap()` immediately after a seek yields the underlying reader at - /// the same position. + /// `.into_inner()` immediately after a seek yields the underlying reader + /// at the same position. /// /// See `std::io::Seek` for more details. /// @@ -231,7 +234,7 @@ impl<R: Seek> Seek for BufReader<R> { if let SeekFrom::Current(n) = pos { let remainder = (self.cap - self.pos) as i64; // it should be safe to assume that remainder fits within an i64 as the alternative - // means we managed to allocate 8 ebibytes and that's absurd. + // means we managed to allocate 8 exbibytes and that's absurd. // But it's not out of the realm of possibility for some weird underlying reader to // support seeking by i64::min_value() so we need to handle underflow when subtracting // remainder. @@ -255,7 +258,7 @@ impl<R: Seek> Seek for BufReader<R> { /// Wraps a writer and buffers its output. /// /// It can be excessively inefficient to work directly with something that -/// implements `Write`. For example, every call to `write` on `TcpStream` +/// implements [`Write`]. For example, every call to [`write`] on [`TcpStream`] /// results in a system call. A `BufWriter` keeps an in-memory buffer of data /// and writes it to an underlying writer in large, infrequent batches. /// @@ -263,7 +266,7 @@ impl<R: Seek> Seek for BufReader<R> { /// /// # Examples /// -/// Let's write the numbers one through ten to a `TcpStream`: +/// Let's write the numbers one through ten to a [`TcpStream`]: /// /// ```no_run /// use std::io::prelude::*; @@ -295,6 +298,10 @@ impl<R: Seek> Seek for BufReader<R> { /// By wrapping the stream with a `BufWriter`, these ten writes are all grouped /// together by the buffer, and will all be written out in one system call when /// the `stream` is dropped. +/// +/// [`Write`]: ../../std/io/trait.Write.html +/// [`write`]: ../../std/net/struct.TcpStream.html#method.write +/// [`TcpStream`]: ../../std/net/struct.TcpStream.html #[stable(feature = "rust1", since = "1.0.0")] pub struct BufWriter<W: Write> { inner: Option<W>, @@ -578,7 +585,7 @@ impl<W> From<IntoInnerError<W>> for Error { } #[stable(feature = "rust1", since = "1.0.0")] -impl<W: Reflect + Send + fmt::Debug> error::Error for IntoInnerError<W> { +impl<W: Send + fmt::Debug> error::Error for IntoInnerError<W> { fn description(&self) -> &str { error::Error::description(self.error()) } @@ -1107,6 +1114,7 @@ mod tests { } #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn panic_in_write_doesnt_flush_in_drop() { static WRITES: AtomicUsize = AtomicUsize::new(0); diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 1b836b74537..cb9e7bd3270 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -10,26 +10,33 @@ use io::prelude::*; +use core::convert::TryInto; use cmp; use io::{self, SeekFrom, Error, ErrorKind}; /// A `Cursor` wraps another type and provides it with a -/// [`Seek`](trait.Seek.html) implementation. +/// [`Seek`] implementation. /// -/// Cursors are typically used with in-memory buffers to allow them to -/// implement `Read` and/or `Write`, allowing these buffers to be used +/// `Cursor`s are typically used with in-memory buffers to allow them to +/// implement [`Read`] and/or [`Write`], allowing these buffers to be used /// anywhere you might use a reader or writer that does actual I/O. /// /// The standard library implements some I/O traits on various types which -/// are commonly used as a buffer, like `Cursor<Vec<u8>>` and `Cursor<&[u8]>`. +/// are commonly used as a buffer, like `Cursor<`[`Vec`]`<u8>>` and +/// `Cursor<`[`&[u8]`]`>`. /// /// # Examples /// -/// We may want to write bytes to a [`File`][file] in our production +/// We may want to write bytes to a [`File`] in our production /// code, but use an in-memory buffer in our tests. We can do this with /// `Cursor`: /// -/// [file]: ../fs/struct.File.html +/// [`Seek`]: trait.Seek.html +/// [`Read`]: ../../std/io/trait.Read.html +/// [`Write`]: ../../std/io/trait.Write.html +/// [`Vec`]: ../../std/vec/struct.Vec.html +/// [`&[u8]`]: ../../std/primitive.slice.html +/// [`File`]: ../fs/struct.File.html /// /// ```no_run /// use std::io::prelude::*; @@ -242,18 +249,20 @@ impl<'a> Write for Cursor<&'a mut [u8]> { #[stable(feature = "rust1", since = "1.0.0")] impl Write for Cursor<Vec<u8>> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + let pos: usize = self.position().try_into().map_err(|_| { + Error::new(ErrorKind::InvalidInput, + "cursor position exceeds maximum possible vector length") + })?; // Make sure the internal buffer is as least as big as where we // currently are - let pos = self.position(); - let amt = pos.saturating_sub(self.inner.len() as u64); - // use `resize` so that the zero filling is as efficient as possible let len = self.inner.len(); - self.inner.resize(len + amt as usize, 0); - + if len < pos { + // use `resize` so that the zero filling is as efficient as possible + self.inner.resize(pos, 0); + } // Figure out what bytes will be used to overwrite what's currently // there (left), and what will be appended on the end (right) { - let pos = pos as usize; let space = self.inner.len() - pos; let (left, right) = buf.split_at(cmp::min(space, buf.len())); self.inner[pos..pos + left.len()].copy_from_slice(left); @@ -261,7 +270,7 @@ impl Write for Cursor<Vec<u8>> { } // Bump us forward - self.set_position(pos + buf.len() as u64); + self.set_position((pos + buf.len()) as u64); Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -383,7 +392,7 @@ mod tests { #[test] fn test_mem_reader() { - let mut reader = Cursor::new(vec!(0, 1, 2, 3, 4, 5, 6, 7)); + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); let mut buf = []; assert_eq!(reader.read(&mut buf).unwrap(), 0); assert_eq!(reader.position(), 0); @@ -405,7 +414,7 @@ mod tests { #[test] fn test_boxed_slice_reader() { - let mut reader = Cursor::new(vec!(0, 1, 2, 3, 4, 5, 6, 7).into_boxed_slice()); + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); let mut buf = []; assert_eq!(reader.read(&mut buf).unwrap(), 0); assert_eq!(reader.position(), 0); @@ -427,7 +436,7 @@ mod tests { #[test] fn read_to_end() { - let mut reader = Cursor::new(vec!(0, 1, 2, 3, 4, 5, 6, 7)); + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); let mut v = Vec::new(); reader.read_to_end(&mut v).unwrap(); assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]); @@ -503,7 +512,7 @@ mod tests { assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); assert_eq!(r.read(&mut [0]).unwrap(), 0); - let mut r = Cursor::new(vec!(10)); + let mut r = Cursor::new(vec![10]); assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); assert_eq!(r.read(&mut [0]).unwrap(), 0); @@ -523,14 +532,14 @@ mod tests { let mut r = Cursor::new(&buf[..]); assert!(r.seek(SeekFrom::End(-2)).is_err()); - let mut r = Cursor::new(vec!(10)); + let mut r = Cursor::new(vec![10]); assert!(r.seek(SeekFrom::End(-2)).is_err()); let mut buf = [0]; let mut r = Cursor::new(&mut buf[..]); assert!(r.seek(SeekFrom::End(-2)).is_err()); - let mut r = Cursor::new(vec!(10).into_boxed_slice()); + let mut r = Cursor::new(vec![10].into_boxed_slice()); assert!(r.seek(SeekFrom::End(-2)).is_err()); } @@ -580,4 +589,12 @@ mod tests { let mut r = Cursor::new(Vec::new()); assert!(r.seek(SeekFrom::End(-2)).is_err()); } + + #[test] + #[cfg(target_pointer_width = "32")] + fn vec_seek_and_write_past_usize_max() { + let mut c = Cursor::new(Vec::new()); + c.set_position(<usize>::max_value() as u64 + 1); + assert!(c.write_all(&[1, 2, 3]).is_err()); + } } diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs index cd05e6b5de9..6b26c016638 100644 --- a/src/libstd/io/impls.rs +++ b/src/libstd/io/impls.rs @@ -147,6 +147,10 @@ impl<B: BufRead + ?Sized> BufRead for Box<B> { // ============================================================================= // In-memory buffer implementations +/// Read is implemented for `&[u8]` by copying from the slice. +/// +/// Note that reading updates the slice to point to the yet unread part. +/// The slice will be empty when EOF is reached. #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Read for &'a [u8] { #[inline] @@ -180,6 +184,11 @@ impl<'a> BufRead for &'a [u8] { fn consume(&mut self, amt: usize) { *self = &self[amt..]; } } +/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting +/// its data. +/// +/// Note that writing updates the slice to point to the yet unwritten part. +/// The slice will be empty when it has been completely overwritten. #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Write for &'a mut [u8] { #[inline] @@ -204,6 +213,8 @@ impl<'a> Write for &'a mut [u8] { fn flush(&mut self) -> io::Result<()> { Ok(()) } } +/// Write is implemented for `Vec<u8>` by appending to the vector. +/// The vector will grow as needed. #[stable(feature = "rust1", since = "1.0.0")] impl Write for Vec<u8> { #[inline] diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 06609cfad15..37c3f70d54d 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -12,18 +12,15 @@ //! //! The `std::io` module contains a number of common things you'll need //! when doing input and output. The most core part of this module is -//! the [`Read`][read] and [`Write`][write] traits, which provide the +//! the [`Read`] and [`Write`] traits, which provide the //! most general interface for reading and writing input and output. //! -//! [read]: trait.Read.html -//! [write]: trait.Write.html -//! //! # Read and Write //! -//! Because they are traits, `Read` and `Write` are implemented by a number +//! Because they are traits, [`Read`] and [`Write`] are implemented by a number //! of other types, and you can implement them for your types too. As such, //! you'll see a few different types of I/O throughout the documentation in -//! this module: `File`s, `TcpStream`s, and sometimes even `Vec<T>`s. For +//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec<T>`]s. For //! example, `Read` adds a `read()` method, which we can use on `File`s: //! //! ``` @@ -43,15 +40,15 @@ //! # } //! ``` //! -//! `Read` and `Write` are so important, implementors of the two traits have a +//! [`Read`] and [`Write`] are so important, implementors of the two traits have a //! nickname: readers and writers. So you'll sometimes see 'a reader' instead -//! of 'a type that implements the `Read` trait'. Much easier! +//! of 'a type that implements the [`Read`] trait'. Much easier! //! //! ## Seek and BufRead //! -//! Beyond that, there are two important traits that are provided: [`Seek`][seek] -//! and [`BufRead`][bufread]. Both of these build on top of a reader to control -//! how the reading happens. `Seek` lets you control where the next byte is +//! Beyond that, there are two important traits that are provided: [`Seek`] +//! and [`BufRead`]. Both of these build on top of a reader to control +//! how the reading happens. [`Seek`] lets you control where the next byte is //! coming from: //! //! ``` @@ -75,21 +72,18 @@ //! # } //! ``` //! -//! [seek]: trait.Seek.html -//! [bufread]: trait.BufRead.html -//! -//! `BufRead` uses an internal buffer to provide a number of other ways to read, but +//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but //! to show it off, we'll need to talk about buffers in general. Keep reading! //! //! ## BufReader and BufWriter //! //! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be //! making near-constant calls to the operating system. To help with this, -//! `std::io` comes with two structs, `BufReader` and `BufWriter`, which wrap +//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap //! readers and writers. The wrapper uses a buffer, reducing the number of //! calls and providing nicer methods for accessing exactly what you want. //! -//! For example, `BufReader` works with the `BufRead` trait to add extra +//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra //! methods to any reader: //! //! ``` @@ -111,8 +105,8 @@ //! # } //! ``` //! -//! `BufWriter` doesn't add any new ways of writing; it just buffers every call -//! to [`write()`][write()]: +//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call +//! to [`write()`]: //! //! ``` //! use std::io; @@ -134,8 +128,6 @@ //! # } //! ``` //! -//! [write()]: trait.Write.html#tymethod.write -//! //! ## Standard input and output //! //! A very common source of input is standard input: @@ -165,13 +157,13 @@ //! # } //! ``` //! -//! Of course, using `io::stdout()` directly is less common than something like -//! `println!`. +//! Of course, using [`io::stdout()`] directly is less common than something like +//! [`println!`]. //! //! ## Iterator types //! //! A large number of the structures provided by `std::io` are for various -//! ways of iterating over I/O. For example, `Lines` is used to split over +//! ways of iterating over I/O. For example, [`Lines`] is used to split over //! lines: //! //! ``` @@ -211,10 +203,10 @@ //! //! ## io::Result //! -//! Last, but certainly not least, is [`io::Result`][result]. This type is used +//! Last, but certainly not least, is [`io::Result`]. This type is used //! as the return type of many `std::io` functions that can cause an error, and //! can be returned from your own functions as well. Many of the examples in this -//! module use the [`try!`][try] macro: +//! module use the [`try!`] macro: //! //! ``` //! use std::io; @@ -230,14 +222,11 @@ //! } //! ``` //! -//! The return type of `read_input()`, `io::Result<()>`, is a very common type -//! for functions which don't have a 'real' return value, but do want to return -//! errors if they happen. In this case, the only purpose of this function is +//! The return type of `read_input()`, [`io::Result<()>`][`io::Result`], is a very +//! common type for functions which don't have a 'real' return value, but do want to +//! return errors if they happen. In this case, the only purpose of this function is //! to read the line and print it, so we use `()`. //! -//! [result]: type.Result.html -//! [try]: ../macro.try.html -//! //! ## Platform-specific behavior //! //! Many I/O functions throughout the standard library are documented to indicate @@ -246,6 +235,22 @@ //! any possibly unclear semantics. Note, however, that this is informative, not a binding //! contract. The implementation of many of these functions are subject to change over //! time and may call fewer or more syscalls/library functions. +//! +//! [`Read`]: trait.Read.html +//! [`Write`]: trait.Write.html +//! [`Seek`]: trait.Seek.html +//! [`BufRead`]: trait.BufRead.html +//! [`File`]: ../fs/struct.File.html +//! [`TcpStream`]: ../net/struct.TcpStream.html +//! [`Vec<T>`]: ../vec/struct.Vec.html +//! [`BufReader`]: struct.BufReader.html +//! [`BufWriter`]: struct.BufWriter.html +//! [`write()`]: trait.Write.html#tymethod.write +//! [`io::stdout()`]: fn.stdout.html +//! [`println!`]: ../macro.println.html +//! [`Lines`]: struct.Lines.html +//! [`io::Result`]: type.Result.html +//! [`try!`]: ../macro.try.html #![stable(feature = "rust1", since = "1.0.0")] @@ -1149,10 +1154,7 @@ fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>) /// /// For example, reading line-by-line is inefficient without using a buffer, so /// if you want to read by line, you'll need `BufRead`, which includes a -/// [`read_line()`][readline] method as well as a [`lines()`][lines] iterator. -/// -/// [readline]: #method.read_line -/// [lines]: #method.lines +/// [`read_line()`] method as well as a [`lines()`] iterator. /// /// # Examples /// @@ -1168,14 +1170,17 @@ fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>) /// } /// ``` /// -/// If you have something that implements `Read`, you can use the [`BufReader` -/// type][bufreader] to turn it into a `BufRead`. +/// If you have something that implements [`Read`], you can use the [`BufReader` +/// type][`BufReader`] to turn it into a `BufRead`. /// -/// For example, [`File`][file] implements `Read`, but not `BufRead`. -/// `BufReader` to the rescue! +/// For example, [`File`] implements [`Read`], but not `BufRead`. +/// [`BufReader`] to the rescue! /// -/// [bufreader]: struct.BufReader.html -/// [file]: ../fs/struct.File.html +/// [`BufReader`]: struct.BufReader.html +/// [`File`]: ../fs/struct.File.html +/// [`read_line()`]: #method.read_line +/// [`lines()`]: #method.lines +/// [`Read`]: trait.Read.html /// /// ``` /// use std::io::{self, BufReader}; @@ -1199,13 +1204,13 @@ pub trait BufRead: Read { /// Fills the internal buffer of this object, returning the buffer contents. /// /// This function is a lower-level call. It needs to be paired with the - /// [`consume`][consume] method to function properly. When calling this + /// [`consume()`] method to function properly. When calling this /// method, none of the contents will be "read" in the sense that later - /// calling `read` may return the same contents. As such, `consume` must be - /// called with the number of bytes that are consumed from this buffer to + /// calling `read` may return the same contents. As such, [`consume()`] must + /// be called with the number of bytes that are consumed from this buffer to /// ensure that the bytes are never returned twice. /// - /// [consume]: #tymethod.consume + /// [`consume()`]: #tymethod.consume /// /// An empty buffer returned indicates that the stream has reached EOF. /// @@ -1246,38 +1251,36 @@ pub trait BufRead: Read { /// so they should no longer be returned in calls to `read`. /// /// This function is a lower-level call. It needs to be paired with the - /// [`fill_buf`][fillbuf] method to function properly. This function does + /// [`fill_buf()`] method to function properly. This function does /// not perform any I/O, it simply informs this object that some amount of - /// its buffer, returned from `fill_buf`, has been consumed and should no - /// longer be returned. As such, this function may do odd things if - /// `fill_buf` isn't called before calling it. - /// - /// [fillbuf]: #tymethod.fill_buf + /// its buffer, returned from [`fill_buf()`], has been consumed and should + /// no longer be returned. As such, this function may do odd things if + /// [`fill_buf()`] isn't called before calling it. /// /// The `amt` must be `<=` the number of bytes in the buffer returned by - /// `fill_buf`. + /// [`fill_buf()`]. /// /// # Examples /// - /// Since `consume()` is meant to be used with [`fill_buf()`][fillbuf], + /// Since `consume()` is meant to be used with [`fill_buf()`], /// that method's example includes an example of `consume()`. + /// + /// [`fill_buf()`]: #tymethod.fill_buf #[stable(feature = "rust1", since = "1.0.0")] fn consume(&mut self, amt: usize); - /// Read all bytes into `buf` until the delimiter `byte` is reached. + /// Read all bytes into `buf` until the delimiter `byte` or EOF is reached. /// /// This function will read bytes from the underlying stream until the /// delimiter or EOF is found. Once found, all bytes up to, and including, /// the delimiter (if found) will be appended to `buf`. /// - /// If this reader is currently at EOF then this function will not modify - /// `buf` and will return `Ok(n)` where `n` is the number of bytes which - /// were read. + /// If successful, this function will return the total number of bytes read. /// /// # Errors /// - /// This function will ignore all instances of `ErrorKind::Interrupted` and - /// will otherwise return any errors returned by `fill_buf`. + /// This function will ignore all instances of [`ErrorKind::Interrupted`] and + /// will otherwise return any errors returned by [`fill_buf()`]. /// /// If an I/O error is encountered then all bytes read so far will be /// present in `buf` and its length will have been adjusted appropriately. @@ -1287,6 +1290,9 @@ pub trait BufRead: Read { /// A locked standard input implements `BufRead`. In this example, we'll /// read from standard input until we see an `a` byte. /// + /// [`fill_buf()`]: #tymethod.fill_buf + /// [`ErrorKind::Interrupted`]: enum.ErrorKind.html#variant.Interrupted + /// /// ``` /// use std::io; /// use std::io::prelude::*; @@ -1315,25 +1321,24 @@ pub trait BufRead: Read { /// up to, and including, the delimiter (if found) will be appended to /// `buf`. /// - /// If this reader is currently at EOF then this function will not modify - /// `buf` and will return `Ok(n)` where `n` is the number of bytes which - /// were read. + /// If successful, this function will return the total number of bytes read. /// /// # Errors /// - /// This function has the same error semantics as `read_until` and will also - /// return an error if the read bytes are not valid UTF-8. If an I/O error - /// is encountered then `buf` may contain some bytes already read in the - /// event that all data read so far was valid UTF-8. + /// This function has the same error semantics as [`read_until()`] and will + /// also return an error if the read bytes are not valid UTF-8. If an I/O + /// error is encountered then `buf` may contain some bytes already read in + /// the event that all data read so far was valid UTF-8. /// /// # Examples /// /// A locked standard input implements `BufRead`. In this example, we'll /// read all of the lines from standard input. If we were to do this in - /// an actual project, the [`lines()`][lines] method would be easier, of + /// an actual project, the [`lines()`] method would be easier, of /// course. /// - /// [lines]: #method.lines + /// [`lines()`]: #method.lines + /// [`read_until()`]: #method.read_until /// /// ``` /// use std::io; @@ -1362,17 +1367,21 @@ pub trait BufRead: Read { /// `byte`. /// /// The iterator returned from this function will return instances of - /// `io::Result<Vec<u8>>`. Each vector returned will *not* have the - /// delimiter byte at the end. + /// [`io::Result`]`<`[`Vec<u8>`]`>`. Each vector returned will *not* have + /// the delimiter byte at the end. /// - /// This function will yield errors whenever `read_until` would have also - /// yielded an error. + /// This function will yield errors whenever [`read_until()`] would have + /// also yielded an error. /// /// # Examples /// /// A locked standard input implements `BufRead`. In this example, we'll /// read some input from standard input, splitting on commas. /// + /// [`io::Result`]: type.Result.html + /// [`Vec<u8>`]: ../vec/struct.Vec.html + /// [`read_until()`]: #method.read_until + /// /// ``` /// use std::io; /// use std::io::prelude::*; @@ -1391,9 +1400,12 @@ pub trait BufRead: Read { /// Returns an iterator over the lines of this reader. /// /// The iterator returned from this function will yield instances of - /// `io::Result<String>`. Each string returned will *not* have a newline + /// [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline /// byte (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end. /// + /// [`io::Result`]: type.Result.html + /// [`String`]: ../string/struct.String.html + /// /// # Examples /// /// A locked standard input implements `BufRead`: @@ -1416,10 +1428,10 @@ pub trait BufRead: Read { /// Adaptor to chain together two readers. /// -/// This struct is generally created by calling [`chain()`][chain] on a reader. -/// Please see the documentation of `chain()` for more details. +/// This struct is generally created by calling [`chain()`] on a reader. +/// Please see the documentation of [`chain()`] for more details. /// -/// [chain]: trait.Read.html#method.chain +/// [`chain()`]: trait.Read.html#method.chain #[stable(feature = "rust1", since = "1.0.0")] pub struct Chain<T, U> { first: T, @@ -1432,7 +1444,7 @@ impl<T: Read, U: Read> Read for Chain<T, U> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> { if !self.done_first { match self.first.read(buf)? { - 0 => { self.done_first = true; } + 0 if buf.len() != 0 => { self.done_first = true; } n => return Ok(n), } } @@ -1524,7 +1536,7 @@ impl<T> Take<T> { /// # Ok(()) /// # } /// ``` - #[unstable(feature = "io_take_into_inner", issue = "0")] + #[unstable(feature = "io_take_into_inner", issue = "23755")] pub fn into_inner(self) -> T { self.inner } @@ -1580,10 +1592,10 @@ fn read_one_byte(reader: &mut Read) -> Option<Result<u8>> { /// An iterator over `u8` values of a reader. /// -/// This struct is generally created by calling [`bytes()`][bytes] on a reader. -/// Please see the documentation of `bytes()` for more details. +/// This struct is generally created by calling [`bytes()`] on a reader. +/// Please see the documentation of [`bytes()`] for more details. /// -/// [bytes]: trait.Read.html#method.bytes +/// [`bytes()`]: trait.Read.html#method.bytes #[stable(feature = "rust1", since = "1.0.0")] pub struct Bytes<R> { inner: R, @@ -1761,6 +1773,7 @@ mod tests { use super::repeat; #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn read_until() { let mut buf = Cursor::new(&b"12"[..]); let mut v = Vec::new(); @@ -1959,7 +1972,19 @@ mod tests { cmp_bufread(chain1, chain2, &testdata[..]); } + #[test] + fn chain_zero_length_read_is_not_eof() { + let a = b"A"; + let b = b"B"; + let mut s = String::new(); + let mut chain = (&a[..]).chain(&b[..]); + chain.read(&mut []).unwrap(); + chain.read_to_string(&mut s).unwrap(); + assert_eq!("AB", s); + } + #[bench] + #[cfg_attr(target_os = "emscripten", ignore)] fn bench_read_to_end(b: &mut test::Bencher) { b.iter(|| { let mut lr = repeat(1).take(10000000); diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 9a782e95f6e..c24ee8ff303 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -125,13 +125,10 @@ impl<R: io::Read> io::Read for Maybe<R> { } fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> { - #[cfg(windows)] - const ERR: i32 = ::sys::c::ERROR_INVALID_HANDLE as i32; - #[cfg(not(windows))] - const ERR: i32 = ::libc::EBADF as i32; + use sys::stdio::EBADF_ERR; match r { - Err(ref e) if e.raw_os_error() == Some(ERR) => Ok(default), + Err(ref e) if e.raw_os_error() == Some(EBADF_ERR) => Ok(default), r => r } } @@ -593,11 +590,11 @@ impl<'a> Write for StderrLock<'a> { with a more general mechanism", issue = "0")] #[doc(hidden)] -pub fn set_panic(sink: Box<Write + Send>) -> Option<Box<Write + Send>> { +pub fn set_panic(sink: Option<Box<Write + Send>>) -> Option<Box<Write + Send>> { use panicking::LOCAL_STDERR; use mem; LOCAL_STDERR.with(move |slot| { - mem::replace(&mut *slot.borrow_mut(), Some(sink)) + mem::replace(&mut *slot.borrow_mut(), sink) }).and_then(|mut s| { let _ = s.flush(); Some(s) @@ -617,10 +614,10 @@ pub fn set_panic(sink: Box<Write + Send>) -> Option<Box<Write + Send>> { with a more general mechanism", issue = "0")] #[doc(hidden)] -pub fn set_print(sink: Box<Write + Send>) -> Option<Box<Write + Send>> { +pub fn set_print(sink: Option<Box<Write + Send>>) -> Option<Box<Write + Send>> { use mem; LOCAL_STDOUT.with(move |slot| { - mem::replace(&mut *slot.borrow_mut(), Some(sink)) + mem::replace(&mut *slot.borrow_mut(), sink) }).and_then(|mut s| { let _ = s.flush(); Some(s) @@ -668,6 +665,7 @@ mod tests { use super::*; #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn panic_doesnt_poison() { thread::spawn(|| { let _a = stdin(); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 115a24fc83c..e470690b7c6 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -218,12 +218,14 @@ #![feature(associated_consts)] #![feature(borrow_state)] #![feature(box_syntax)] +#![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(cfg_target_vendor)] #![feature(char_escape_debug)] #![feature(char_internals)] #![feature(collections)] #![feature(collections_bound)] +#![feature(collections_range)] #![feature(compiler_builtins_lib)] #![feature(const_fn)] #![feature(core_float)] @@ -239,6 +241,7 @@ #![feature(heap_api)] #![feature(inclusive_range)] #![feature(int_error_internals)] +#![feature(integer_atomics)] #![feature(into_cow)] #![feature(lang_items)] #![feature(libc)] @@ -255,10 +258,9 @@ #![feature(panic_unwind)] #![feature(placement_in_syntax)] #![feature(prelude_import)] -#![feature(question_mark)] +#![cfg_attr(stage0, feature(question_mark))] #![feature(rand)] #![feature(raw)] -#![feature(reflect_marker)] #![feature(repr_simd)] #![feature(rustc_attrs)] #![feature(shared)] @@ -273,12 +275,10 @@ #![feature(str_utf16)] #![feature(test, rustc_private)] #![feature(thread_local)] -#![feature(try_borrow)] #![feature(try_from)] #![feature(unboxed_closures)] #![feature(unicode)] #![feature(unique)] -#![cfg_attr(stage0, feature(unsafe_no_drop_flag))] #![feature(unwind_attributes)] #![feature(vec_push_all)] #![feature(zero_one)] @@ -304,8 +304,8 @@ use prelude::v1::*; // We want to reexport a few macros from core but libcore has already been // imported by the compiler (via our #[no_std] attribute) In this case we just // add a new crate name so we can attach the reexports to it. -#[macro_reexport(assert, assert_eq, debug_assert, debug_assert_eq, - unreachable, unimplemented, write, writeln, try)] +#[macro_reexport(assert, assert_eq, assert_ne, debug_assert, debug_assert_eq, + debug_assert_ne, unreachable, unimplemented, write, writeln, try)] extern crate core as __core; #[macro_use] diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 6f0f6ecab5b..0ce6b0a9431 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -112,12 +112,14 @@ macro_rules! print { /// # Examples /// /// ``` +/// println!(); /// println!("hello there!"); /// println!("format {} arguments", "some"); /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] macro_rules! println { + () => (print!("\n")); ($fmt:expr) => (print!(concat!($fmt, "\n"))); ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); } @@ -193,12 +195,18 @@ macro_rules! assert_approx_eq { pub mod builtin { /// The core macro for formatted string creation & output. /// - /// This macro produces a value of type `fmt::Arguments`. This value can be - /// passed to the functions in `std::fmt` for performing useful functions. - /// All other formatting macros (`format!`, `write!`, `println!`, etc) are + /// This macro produces a value of type [`fmt::Arguments`]. This value can be + /// passed to the functions in [`std::fmt`] for performing useful functions. + /// All other formatting macros ([`format!`], [`write!`], [`println!`], etc) are /// proxied through this one. /// - /// For more information, see the documentation in `std::fmt`. + /// For more information, see the documentation in [`std::fmt`]. + /// + /// [`fmt::Arguments`]: ../std/fmt/struct.Arguments.html + /// [`std::fmt`]: ../std/fmt/index.html + /// [`format!`]: ../std/macro.format.html + /// [`write!`]: ../std/macro.write.html + /// [`println!`]: ../std/macro.println.html /// /// # Examples /// @@ -278,7 +286,7 @@ pub mod builtin { /// // fn concat_idents!(new, fun, name) { } // not usable in this way! /// # } /// ``` - #[unstable(feature = "concat_idents", issue = "29599")] + #[unstable(feature = "concat_idents_macro", issue = "29599")] #[macro_export] macro_rules! concat_idents { ($($e:ident),*) => ({ /* compiler built-in */ }) @@ -373,9 +381,11 @@ pub mod builtin { /// Includes a utf8-encoded file as a string. /// + /// The file is located relative to the current file. (similarly to how + /// modules are found) + /// /// This macro will yield an expression of type `&'static str` which is the - /// contents of the filename specified. The file is located relative to the - /// current file (similarly to how modules are found), + /// contents of the file. /// /// # Examples /// @@ -388,9 +398,11 @@ pub mod builtin { /// Includes a file as a reference to a byte array. /// + /// The file is located relative to the current file. (similarly to how + /// modules are found) + /// /// This macro will yield an expression of type `&'static [u8; N]` which is - /// the contents of the filename specified. The file is located relative to - /// the current file (similarly to how modules are found), + /// the contents of the file. /// /// # Examples /// @@ -444,9 +456,17 @@ pub mod builtin { #[macro_export] macro_rules! cfg { ($($cfg:tt)*) => ({ /* compiler built-in */ }) } - /// Parse the current given file as an expression. + /// Parse a file as an expression or an item according to the context. + /// + /// The file is located relative to the current file. (similarly to how + /// modules are found) /// - /// This is generally a bad idea, because it's going to behave unhygienically. + /// Using this macro is often a bad idea, because if the file is + /// parsed as an expression, it is going to be placed in the + /// surrounding code unhygenically. This could result in variables + /// or functions being different from what the file expected if + /// there are variables or functions that have the same name in + /// the current file. /// /// # Examples /// diff --git a/src/libstd/memchr.rs b/src/libstd/memchr.rs index 03f55f7ad61..7c8c97a6caf 100644 --- a/src/libstd/memchr.rs +++ b/src/libstd/memchr.rs @@ -11,8 +11,6 @@ // Original implementation taken from rust-memchr // Copyright 2015 Andrew Gallant, bluss and Nicolas Koch - - /// A safe interface to `memchr`. /// /// Returns the index corresponding to the first occurrence of `needle` in @@ -32,32 +30,9 @@ /// let haystack = b"the quick brown fox"; /// assert_eq!(memchr(b'k', haystack), Some(8)); /// ``` +#[inline] pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> { - // libc memchr - #[cfg(not(target_os = "windows"))] - fn memchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> { - use libc; - - let p = unsafe { - libc::memchr( - haystack.as_ptr() as *const libc::c_void, - needle as libc::c_int, - haystack.len() as libc::size_t) - }; - if p.is_null() { - None - } else { - Some(p as usize - (haystack.as_ptr() as usize)) - } - } - - // use fallback on windows, since it's faster - #[cfg(target_os = "windows")] - fn memchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> { - fallback::memchr(needle, haystack) - } - - memchr_specific(needle, haystack) + ::sys::memchr::memchr(needle, haystack) } /// A safe interface to `memrchr`. @@ -75,251 +50,9 @@ pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> { /// let haystack = b"the quick brown fox"; /// assert_eq!(memrchr(b'o', haystack), Some(17)); /// ``` +#[inline] pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> { - - #[cfg(target_os = "linux")] - fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> { - use libc; - - // GNU's memrchr() will - unlike memchr() - error if haystack is empty. - if haystack.is_empty() {return None} - let p = unsafe { - libc::memrchr( - haystack.as_ptr() as *const libc::c_void, - needle as libc::c_int, - haystack.len() as libc::size_t) - }; - if p.is_null() { - None - } else { - Some(p as usize - (haystack.as_ptr() as usize)) - } - } - - #[cfg(not(target_os = "linux"))] - fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> { - fallback::memrchr(needle, haystack) - } - - memrchr_specific(needle, haystack) -} - -#[allow(dead_code)] -mod fallback { - use cmp; - use mem; - - const LO_U64: u64 = 0x0101010101010101; - const HI_U64: u64 = 0x8080808080808080; - - // use truncation - const LO_USIZE: usize = LO_U64 as usize; - const HI_USIZE: usize = HI_U64 as usize; - - /// Return `true` if `x` contains any zero byte. - /// - /// From *Matters Computational*, J. Arndt - /// - /// "The idea is to subtract one from each of the bytes and then look for - /// bytes where the borrow propagated all the way to the most significant - /// bit." - #[inline] - fn contains_zero_byte(x: usize) -> bool { - x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0 - } - - #[cfg(target_pointer_width = "32")] - #[inline] - fn repeat_byte(b: u8) -> usize { - let mut rep = (b as usize) << 8 | b as usize; - rep = rep << 16 | rep; - rep - } - - #[cfg(target_pointer_width = "64")] - #[inline] - fn repeat_byte(b: u8) -> usize { - let mut rep = (b as usize) << 8 | b as usize; - rep = rep << 16 | rep; - rep = rep << 32 | rep; - rep - } - - /// Return the first index matching the byte `a` in `text`. - pub fn memchr(x: u8, text: &[u8]) -> Option<usize> { - // Scan for a single byte value by reading two `usize` words at a time. - // - // Split `text` in three parts - // - unaligned initial part, before the first word aligned address in text - // - body, scan by 2 words at a time - // - the last remaining part, < 2 word size - let len = text.len(); - let ptr = text.as_ptr(); - let usize_bytes = mem::size_of::<usize>(); - - // search up to an aligned boundary - let align = (ptr as usize) & (usize_bytes- 1); - let mut offset; - if align > 0 { - offset = cmp::min(usize_bytes - align, len); - if let Some(index) = text[..offset].iter().position(|elt| *elt == x) { - return Some(index); - } - } else { - offset = 0; - } - - // search the body of the text - let repeated_x = repeat_byte(x); - - if len >= 2 * usize_bytes { - while offset <= len - 2 * usize_bytes { - unsafe { - let u = *(ptr.offset(offset as isize) as *const usize); - let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize); - - // break if there is a matching byte - let zu = contains_zero_byte(u ^ repeated_x); - let zv = contains_zero_byte(v ^ repeated_x); - if zu || zv { - break; - } - } - offset += usize_bytes * 2; - } - } - - // find the byte after the point the body loop stopped - text[offset..].iter().position(|elt| *elt == x).map(|i| offset + i) - } - - /// Return the last index matching the byte `a` in `text`. - pub fn memrchr(x: u8, text: &[u8]) -> Option<usize> { - // Scan for a single byte value by reading two `usize` words at a time. - // - // Split `text` in three parts - // - unaligned tail, after the last word aligned address in text - // - body, scan by 2 words at a time - // - the first remaining bytes, < 2 word size - let len = text.len(); - let ptr = text.as_ptr(); - let usize_bytes = mem::size_of::<usize>(); - - // search to an aligned boundary - let end_align = (ptr as usize + len) & (usize_bytes - 1); - let mut offset; - if end_align > 0 { - offset = if end_align >= len { 0 } else { len - end_align }; - if let Some(index) = text[offset..].iter().rposition(|elt| *elt == x) { - return Some(offset + index); - } - } else { - offset = len; - } - - // search the body of the text - let repeated_x = repeat_byte(x); - - while offset >= 2 * usize_bytes { - unsafe { - let u = *(ptr.offset(offset as isize - 2 * usize_bytes as isize) as *const usize); - let v = *(ptr.offset(offset as isize - usize_bytes as isize) as *const usize); - - // break if there is a matching byte - let zu = contains_zero_byte(u ^ repeated_x); - let zv = contains_zero_byte(v ^ repeated_x); - if zu || zv { - break; - } - } - offset -= 2 * usize_bytes; - } - - // find the byte before the point the body loop stopped - text[..offset].iter().rposition(|elt| *elt == x) - } - - // test fallback implementations on all platforms - #[test] - fn matches_one() { - assert_eq!(Some(0), memchr(b'a', b"a")); - } - - #[test] - fn matches_begin() { - assert_eq!(Some(0), memchr(b'a', b"aaaa")); - } - - #[test] - fn matches_end() { - assert_eq!(Some(4), memchr(b'z', b"aaaaz")); - } - - #[test] - fn matches_nul() { - assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00")); - } - - #[test] - fn matches_past_nul() { - assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z")); - } - - #[test] - fn no_match_empty() { - assert_eq!(None, memchr(b'a', b"")); - } - - #[test] - fn no_match() { - assert_eq!(None, memchr(b'a', b"xyz")); - } - - #[test] - fn matches_one_reversed() { - assert_eq!(Some(0), memrchr(b'a', b"a")); - } - - #[test] - fn matches_begin_reversed() { - assert_eq!(Some(3), memrchr(b'a', b"aaaa")); - } - - #[test] - fn matches_end_reversed() { - assert_eq!(Some(0), memrchr(b'z', b"zaaaa")); - } - - #[test] - fn matches_nul_reversed() { - assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00")); - } - - #[test] - fn matches_past_nul_reversed() { - assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa")); - } - - #[test] - fn no_match_empty_reversed() { - assert_eq!(None, memrchr(b'a', b"")); - } - - #[test] - fn no_match_reversed() { - assert_eq!(None, memrchr(b'a', b"xyz")); - } - - #[test] - fn each_alignment_reversed() { - let mut data = [1u8; 64]; - let needle = 2; - let pos = 40; - data[pos] = needle; - for start in 0..16 { - assert_eq!(Some(pos - start), memrchr(needle, &data[start..])); - } - } + ::sys::memchr::memrchr(needle, haystack) } #[cfg(test)] diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index d0b59b42c17..20dc5b3801b 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -93,6 +93,26 @@ impl SocketAddr { SocketAddr::V6(ref mut a) => a.set_port(new_port), } } + + /// Returns true if the IP in this `SocketAddr` is a valid IPv4 address, + /// false if it's a valid IPv6 address. + #[unstable(feature = "sockaddr_checker", issue = "36949")] + pub fn is_ipv4(&self) -> bool { + match *self { + SocketAddr::V4(_) => true, + SocketAddr::V6(_) => false, + } + } + + /// Returns true if the IP in this `SocketAddr` is a valid IPv6 address, + /// false if it's a valid IPv4 address. + #[unstable(feature = "sockaddr_checker", issue = "36949")] + pub fn is_ipv6(&self) -> bool { + match *self { + SocketAddr::V4(_) => false, + SocketAddr::V6(_) => true, + } + } } impl SocketAddrV4 { @@ -519,7 +539,7 @@ impl<'a, T: ToSocketAddrs + ?Sized> ToSocketAddrs for &'a T { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use net::*; use net::test::{tsa, sa6, sa4}; @@ -631,4 +651,19 @@ mod tests { v6.set_scope_id(20); assert_eq!(v6.scope_id(), 20); } + + #[test] + fn is_v4() { + let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)); + assert!(v4.is_ipv4()); + assert!(!v4.is_ipv6()); + } + + #[test] + fn is_v6() { + let v6 = SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0)); + assert!(!v6.is_ipv4()); + assert!(v6.is_ipv6()); + } } diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 05ef559422f..49080680fac 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -130,6 +130,24 @@ impl IpAddr { IpAddr::V6(ref a) => a.is_documentation(), } } + + /// Returns true if this address is a valid IPv4 address, false if it's a valid IPv6 address. + #[unstable(feature = "ipaddr_checker", issue = "36949")] + pub fn is_ipv4(&self) -> bool { + match *self { + IpAddr::V4(_) => true, + IpAddr::V6(_) => false, + } + } + + /// Returns true if this address is a valid IPv6 address, false if it's a valid IPv4 address. + #[unstable(feature = "ipaddr_checker", issue = "36949")] + pub fn is_ipv6(&self) -> bool { + match *self { + IpAddr::V4(_) => false, + IpAddr::V6(_) => true, + } + } } impl Ipv4Addr { @@ -277,8 +295,7 @@ impl Ipv4Addr { } } -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] +#[stable(feature = "ip_addr", since = "1.7.0")] impl fmt::Display for IpAddr { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -669,7 +686,7 @@ impl From<[u8; 16]> for Ipv6Addr { } // Tests for this module -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use net::*; use net::Ipv6MulticastScope::*; @@ -1023,4 +1040,18 @@ mod tests { assert!("2001:db8:f00::1002".parse::<Ipv6Addr>().unwrap() < "2001:db8:f00::2001".parse::<Ipv6Addr>().unwrap()); } + + #[test] + fn is_v4() { + let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3)); + assert!(ip.is_ipv4()); + assert!(!ip.is_ipv6()); + } + + #[test] + fn is_v6() { + let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678)); + assert!(!ip.is_ipv4()); + assert!(ip.is_ipv6()); + } } diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index 2a78afa85f7..56286fbe253 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -31,14 +31,15 @@ mod addr; mod tcp; mod udp; mod parser; -#[cfg(test)] mod test; +#[cfg(test)] +mod test; /// Possible values which can be passed to the [`shutdown`] method of /// [`TcpStream`]. /// /// [`shutdown`]: struct.TcpStream.html#method.shutdown /// [`TcpStream`]: struct.TcpStream.html -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub enum Shutdown { /// Indicates that the reading portion of this stream/socket should be shut diff --git a/src/libstd/net/parser.rs b/src/libstd/net/parser.rs index 854d87c4cbe..d86711c10ac 100644 --- a/src/libstd/net/parser.rs +++ b/src/libstd/net/parser.rs @@ -302,7 +302,7 @@ impl<'a> Parser<'a> { } } -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "ip_addr", since = "1.7.0")] impl FromStr for IpAddr { type Err = AddrParseError; fn from_str(s: &str) -> Result<IpAddr, AddrParseError> { @@ -370,7 +370,7 @@ impl FromStr for SocketAddr { /// An error returned when parsing an IP address or a socket address. #[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct AddrParseError(()); #[stable(feature = "addr_parse_error_error", since = "1.4.0")] diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 3c5f07c3e33..0e7c5b06713 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -428,7 +428,7 @@ impl fmt::Debug for TcpListener { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use io::ErrorKind; use io::prelude::*; diff --git a/src/libstd/net/test.rs b/src/libstd/net/test.rs index 98ac61f6461..3f2eacda7d6 100644 --- a/src/libstd/net/test.rs +++ b/src/libstd/net/test.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[allow(dead_code)] // not used on emscripten + use env; use net::{SocketAddr, SocketAddrV4, SocketAddrV6, Ipv4Addr, Ipv6Addr, ToSocketAddrs}; use sync::atomic::{AtomicUsize, Ordering}; diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 781f026c12c..c03ac496adb 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -353,7 +353,7 @@ impl fmt::Debug for UdpSocket { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use io::ErrorKind; use net::*; diff --git a/src/libstd/os/fuchsia/fs.rs b/src/libstd/os/fuchsia/fs.rs new file mode 100644 index 00000000000..d22f9a628bd --- /dev/null +++ b/src/libstd/os/fuchsia/fs.rs @@ -0,0 +1,103 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use fs::Metadata; +use sys_common::AsInner; + +/// OS-specific extension methods for `fs::Metadata` +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/src/libstd/os/fuchsia/mod.rs b/src/libstd/os/fuchsia/mod.rs new file mode 100644 index 00000000000..1ebcbba9147 --- /dev/null +++ b/src/libstd/os/fuchsia/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Fuchsia-specific definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod raw; +pub mod fs; diff --git a/src/libstd/os/fuchsia/raw.rs b/src/libstd/os/fuchsia/raw.rs new file mode 100644 index 00000000000..5d017351492 --- /dev/null +++ b/src/libstd/os/fuchsia/raw.rs @@ -0,0 +1,270 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Fuchsia-specific raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![rustc_deprecated(since = "1.8.0", + reason = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions")] +#![allow(deprecated)] + +use os::raw::c_ulong; + +#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_ulong; + +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use self::arch::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t}; + +#[cfg(any(target_arch = "x86", + target_arch = "le32", + target_arch = "powerpc", + target_arch = "arm"))] +mod arch { + use os::raw::{c_long, c_short, c_uint}; + + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad1: c_short, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __st_ino: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + } +} + +#[cfg(target_arch = "mips")] +mod arch { + use os::raw::{c_long, c_ulong}; + + #[cfg(target_env = "musl")] + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i64; + #[cfg(not(target_env = "musl"))] + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64; + #[cfg(target_env = "musl")] + #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; + #[cfg(not(target_env = "musl"))] + #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64; + #[cfg(target_env = "musl")] + #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; + #[cfg(not(target_env = "musl"))] + #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_pad1: [c_long; 3], + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_pad2: [c_long; 2], + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_pad5: [c_long; 14], + } +} + +#[cfg(target_arch = "mips64")] +mod arch { + pub use libc::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t}; +} + +#[cfg(target_arch = "aarch64")] +mod arch { + use os::raw::{c_long, c_int}; + + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad1: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: c_int, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __unused: [c_int; 2], + } +} + +#[cfg(target_arch = "x86_64")] +mod arch { + use os::raw::{c_long, c_int}; + + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad0: c_int, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __unused: [c_long; 3], + } +} diff --git a/src/libstd/os/haiku/fs.rs b/src/libstd/os/haiku/fs.rs new file mode 100644 index 00000000000..54f8ea1b71b --- /dev/null +++ b/src/libstd/os/haiku/fs.rs @@ -0,0 +1,138 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use libc; + +use fs::Metadata; +use sys_common::AsInner; + +#[allow(deprecated)] +use os::haiku::raw; + +/// OS-specific extension methods for `fs::Metadata` +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated(since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait")] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_crtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_crtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { + &*(self.as_inner().as_inner() as *const libc::stat + as *const raw::stat) + } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_crtime(&self) -> i64 { + self.as_inner().as_inner().st_crtime as i64 + } + fn st_crtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_crtime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/src/libstd/os/haiku/mod.rs b/src/libstd/os/haiku/mod.rs new file mode 100644 index 00000000000..dd1675cc9b5 --- /dev/null +++ b/src/libstd/os/haiku/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Haiku-specific definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod raw; +pub mod fs; diff --git a/src/libstd/os/haiku/raw.rs b/src/libstd/os/haiku/raw.rs new file mode 100644 index 00000000000..95353d999f9 --- /dev/null +++ b/src/libstd/os/haiku/raw.rs @@ -0,0 +1,74 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Haiku-specific raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +#![allow(deprecated)] + +use os::raw::{c_long}; +use os::unix::raw::{uid_t, gid_t}; + +// Use the direct definition of usize, instead of uintptr_t like in libc +#[stable(feature = "pthread_t", since = "1.8.0")] pub type pthread_t = usize; + +#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = i64; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = i32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = i32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = i64; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = i32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = i64; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i32; + +#[repr(C)] +#[derive(Clone)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: ino_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: mode_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: nlink_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: uid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: gid_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: off_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: dev_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: blksize_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_crtime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_crtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_type: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: blkcnt_t, +} diff --git a/src/libstd/os/linux/raw.rs b/src/libstd/os/linux/raw.rs index 1c19e58818d..e6a95bc831f 100644 --- a/src/libstd/os/linux/raw.rs +++ b/src/libstd/os/linux/raw.rs @@ -34,7 +34,8 @@ pub use self::arch::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t}; target_arch = "le32", target_arch = "powerpc", target_arch = "arm", - target_arch = "asmjs"))] + target_arch = "asmjs", + target_arch = "wasm32"))] mod arch { use os::raw::{c_long, c_short, c_uint}; diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs index a91d251fc12..366a1674156 100644 --- a/src/libstd/os/mod.rs +++ b/src/libstd/os/mod.rs @@ -24,6 +24,7 @@ pub use sys::ext as windows; #[cfg(target_os = "bitrig")] pub mod bitrig; #[cfg(target_os = "dragonfly")] pub mod dragonfly; #[cfg(target_os = "freebsd")] pub mod freebsd; +#[cfg(target_os = "haiku")] pub mod haiku; #[cfg(target_os = "ios")] pub mod ios; #[cfg(target_os = "linux")] pub mod linux; #[cfg(target_os = "macos")] pub mod macos; @@ -32,5 +33,6 @@ pub use sys::ext as windows; #[cfg(target_os = "openbsd")] pub mod openbsd; #[cfg(target_os = "solaris")] pub mod solaris; #[cfg(target_os = "emscripten")] pub mod emscripten; +#[cfg(target_os = "fuchsia")] pub mod fuchsia; pub mod raw; diff --git a/src/libstd/os/raw.rs b/src/libstd/os/raw.rs index 6c5c1b90a4a..2a918d8aeb7 100644 --- a/src/libstd/os/raw.rs +++ b/src/libstd/os/raw.rs @@ -18,7 +18,8 @@ target_arch = "arm", target_arch = "powerpc", target_arch = "powerpc64", - target_arch = "s390x"))))] + target_arch = "s390x")), + all(target_os = "fuchsia", target_arch = "aarch64")))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = u8; #[cfg(not(any(target_os = "android", target_os = "emscripten", @@ -26,7 +27,8 @@ target_arch = "arm", target_arch = "powerpc", target_arch = "powerpc64", - target_arch = "s390x")))))] + target_arch = "s390x")), + all(target_os = "fuchsia", target_arch = "aarch64"))))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = i8; #[stable(feature = "raw_os", since = "1.1.0")] pub type c_schar = i8; #[stable(feature = "raw_os", since = "1.1.0")] pub type c_uchar = u8; diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 47f594a9b0c..a7e8c4fab37 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -18,7 +18,7 @@ use ops::{Deref, DerefMut}; use panicking; use ptr::{Unique, Shared}; use rc::Rc; -use sync::{Arc, Mutex, RwLock}; +use sync::{Arc, Mutex, RwLock, atomic}; use thread::Result; #[stable(feature = "panic_hooks", since = "1.10.0")] @@ -196,9 +196,9 @@ impl<'a, T: RefUnwindSafe + ?Sized> UnwindSafe for &'a T {} impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *const T {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *mut T {} -#[stable(feature = "catch_unwind", since = "1.9.0")] +#[unstable(feature = "unique", issue = "27730")] impl<T: UnwindSafe> UnwindSafe for Unique<T> {} -#[stable(feature = "catch_unwind", since = "1.9.0")] +#[unstable(feature = "shared", issue = "27730")] impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Shared<T> {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl<T: ?Sized> UnwindSafe for Mutex<T> {} @@ -231,6 +231,46 @@ impl<T: ?Sized> RefUnwindSafe for Mutex<T> {} #[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] impl<T: ?Sized> RefUnwindSafe for RwLock<T> {} +#[cfg(target_has_atomic = "ptr")] +#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +impl RefUnwindSafe for atomic::AtomicIsize {} +#[cfg(target_has_atomic = "8")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicI8 {} +#[cfg(target_has_atomic = "16")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicI16 {} +#[cfg(target_has_atomic = "32")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicI32 {} +#[cfg(target_has_atomic = "64")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicI64 {} + +#[cfg(target_has_atomic = "ptr")] +#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +impl RefUnwindSafe for atomic::AtomicUsize {} +#[cfg(target_has_atomic = "8")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicU8 {} +#[cfg(target_has_atomic = "16")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicU16 {} +#[cfg(target_has_atomic = "32")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicU32 {} +#[cfg(target_has_atomic = "64")] +#[unstable(feature = "integer_atomics", issue = "32976")] +impl RefUnwindSafe for atomic::AtomicU64 {} + +#[cfg(target_has_atomic = "8")] +#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +impl RefUnwindSafe for atomic::AtomicBool {} + +#[cfg(target_has_atomic = "ptr")] +#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +impl<T> RefUnwindSafe for atomic::AtomicPtr<T> {} + #[stable(feature = "catch_unwind", since = "1.9.0")] impl<T> Deref for AssertUnwindSafe<T> { type Target = T; diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 0c10dcbdad6..1f5b3437b61 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -84,6 +84,20 @@ static mut HOOK: Hook = Hook::Default; /// # Panics /// /// Panics if called from a panicking thread. +/// +/// # Examples +/// +/// The following will print "Custom panic hook": +/// +/// ```should_panic +/// use std::panic; +/// +/// panic::set_hook(Box::new(|_| { +/// println!("Custom panic hook"); +/// })); +/// +/// panic!("Normal panic"); +/// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) { if thread::panicking() { @@ -109,6 +123,22 @@ pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) { /// # Panics /// /// Panics if called from a panicking thread. +/// +/// # Examples +/// +/// The following will print "Normal panic": +/// +/// ```should_panic +/// use std::panic; +/// +/// panic::set_hook(Box::new(|_| { +/// println!("Custom panic hook"); +/// })); +/// +/// let _ = panic::take_hook(); +/// +/// panic!("Normal panic"); +/// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> { if thread::panicking() { diff --git a/src/libstd/path.rs b/src/libstd/path.rs index bd27bcf48a0..a55318d3883 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -113,7 +113,7 @@ use ops::{self, Deref}; use ffi::{OsStr, OsString}; -use self::platform::{is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix}; +use sys::path::{is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix}; //////////////////////////////////////////////////////////////////////////////// // GENERAL NOTES @@ -126,130 +126,6 @@ use self::platform::{is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix}; // available. //////////////////////////////////////////////////////////////////////////////// -// Platform-specific definitions -//////////////////////////////////////////////////////////////////////////////// - -// The following modules give the most basic tools for parsing paths on various -// platforms. The bulk of the code is devoted to parsing prefixes on Windows. - -#[cfg(unix)] -mod platform { - use super::Prefix; - use ffi::OsStr; - - #[inline] - pub fn is_sep_byte(b: u8) -> bool { - b == b'/' - } - - #[inline] - pub fn is_verbatim_sep(b: u8) -> bool { - b == b'/' - } - - pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { - None - } - - pub const MAIN_SEP_STR: &'static str = "/"; - pub const MAIN_SEP: char = '/'; -} - -#[cfg(windows)] -mod platform { - use ascii::*; - - use super::{os_str_as_u8_slice, u8_slice_as_os_str, Prefix}; - use ffi::OsStr; - - #[inline] - pub fn is_sep_byte(b: u8) -> bool { - b == b'/' || b == b'\\' - } - - #[inline] - pub fn is_verbatim_sep(b: u8) -> bool { - b == b'\\' - } - - pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> { - use super::Prefix::*; - unsafe { - // The unsafety here stems from converting between &OsStr and &[u8] - // and back. This is safe to do because (1) we only look at ASCII - // contents of the encoding and (2) new &OsStr values are produced - // only from ASCII-bounded slices of existing &OsStr values. - let mut path = os_str_as_u8_slice(path); - - if path.starts_with(br"\\") { - // \\ - path = &path[2..]; - if path.starts_with(br"?\") { - // \\?\ - path = &path[2..]; - if path.starts_with(br"UNC\") { - // \\?\UNC\server\share - path = &path[4..]; - let (server, share) = match parse_two_comps(path, is_verbatim_sep) { - Some((server, share)) => - (u8_slice_as_os_str(server), u8_slice_as_os_str(share)), - None => (u8_slice_as_os_str(path), u8_slice_as_os_str(&[])), - }; - return Some(VerbatimUNC(server, share)); - } else { - // \\?\path - let idx = path.iter().position(|&b| b == b'\\'); - if idx == Some(2) && path[1] == b':' { - let c = path[0]; - if c.is_ascii() && (c as char).is_alphabetic() { - // \\?\C:\ path - return Some(VerbatimDisk(c.to_ascii_uppercase())); - } - } - let slice = &path[..idx.unwrap_or(path.len())]; - return Some(Verbatim(u8_slice_as_os_str(slice))); - } - } else if path.starts_with(b".\\") { - // \\.\path - path = &path[2..]; - let pos = path.iter().position(|&b| b == b'\\'); - let slice = &path[..pos.unwrap_or(path.len())]; - return Some(DeviceNS(u8_slice_as_os_str(slice))); - } - match parse_two_comps(path, is_sep_byte) { - Some((server, share)) if !server.is_empty() && !share.is_empty() => { - // \\server\share - return Some(UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share))); - } - _ => (), - } - } else if path.get(1) == Some(& b':') { - // C: - let c = path[0]; - if c.is_ascii() && (c as char).is_alphabetic() { - return Some(Disk(c.to_ascii_uppercase())); - } - } - return None; - } - - fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> { - let first = match path.iter().position(|x| f(*x)) { - None => return None, - Some(x) => &path[..x], - }; - path = &path[(first.len() + 1)..]; - let idx = path.iter().position(|x| f(*x)); - let second = &path[..idx.unwrap_or(path.len())]; - Some((first, second)) - } - } - - pub const MAIN_SEP_STR: &'static str = "\\"; - pub const MAIN_SEP: char = '\\'; -} - -//////////////////////////////////////////////////////////////////////////////// // Windows Prefixes //////////////////////////////////////////////////////////////////////////////// @@ -373,7 +249,7 @@ pub fn is_separator(c: char) -> bool { /// The primary separator for the current platform #[stable(feature = "rust1", since = "1.0.0")] -pub const MAIN_SEPARATOR: char = platform::MAIN_SEP; +pub const MAIN_SEPARATOR: char = ::sys::path::MAIN_SEP; //////////////////////////////////////////////////////////////////////////////// // Misc helpers @@ -1418,14 +1294,18 @@ impl Into<OsString> for PathBuf { /// This type supports a number of operations for inspecting a path, including /// breaking the path into its components (separated by `/` or `\`, depending on /// the platform), extracting the file name, determining whether the path is -/// absolute, and so on. More details about the overall approach can be found in -/// the module documentation. +/// absolute, and so on. /// /// This is an *unsized* type, meaning that it must always be used behind a -/// pointer like `&` or [`Box`]. +/// pointer like `&` or [`Box`]. For an owned version of this type, +/// see [`PathBuf`]. /// /// [`str`]: ../primitive.str.html /// [`Box`]: ../boxed/struct.Box.html +/// [`PathBuf`]: struct.PathBuf.html +/// +/// More details about the overall approach can be found in +/// the module documentation. /// /// # Examples /// @@ -3511,10 +3391,11 @@ mod tests { #[test] pub fn test_compare() { - use hash::{Hash, Hasher, SipHasher}; + use hash::{Hash, Hasher}; + use collections::hash_map::DefaultHasher; fn hash<T: Hash>(t: T) -> u64 { - let mut s = SipHasher::new_with_keys(0, 0); + let mut s = DefaultHasher::new(); t.hash(&mut s); s.finish() } diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index fdc84467015..54dde6681e1 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -229,7 +229,7 @@ mod prim_unit { } /// /// fn main() { /// unsafe { -/// let my_num: *mut i32 = libc::malloc(mem::size_of::<i32>() as libc::size_t) as *mut i32; +/// let my_num: *mut i32 = libc::malloc(mem::size_of::<i32>()) as *mut i32; /// if my_num.is_null() { /// panic!("failed to allocate memory"); /// } diff --git a/src/libstd/process.rs b/src/libstd/process.rs index f0c44430700..9d21a76e81b 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -8,7 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Working with processes. +//! A module for working with processes. +//! +//! # Examples +//! +//! Basic usage where we try to execute the `cat` shell command: +//! +//! ```should_panic +//! use std::process::Command; +//! +//! let mut child = Command::new("/bin/cat") +//! .arg("file.txt") +//! .spawn() +//! .expect("failed to execute child"); +//! +//! let ecode = child.wait() +//! .expect("failed to wait on child"); +//! +//! assert!(ecode.success()); +//! ``` #![stable(feature = "process", since = "1.0.0")] @@ -807,7 +825,7 @@ pub fn exit(code: i32) -> ! { ::sys::os::exit(code) } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use io::prelude::*; diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 3f14fcd239f..f48325218fb 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -242,9 +242,10 @@ mod tests { } #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn test_os_rng_tasks() { - let mut txs = vec!(); + let mut txs = vec![]; for _ in 0..20 { let (tx, rx) = channel(); txs.push(tx); diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs index e3de1efaa31..78d5aa597ba 100644 --- a/src/libstd/rt.rs +++ b/src/libstd/rt.rs @@ -51,7 +51,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { thread_info::set(main_guard, thread); // Store our args if necessary in a squirreled away location - sys_common::args::init(argc, argv); + sys::args::init(argc, argv); // Let's run some code! let res = panic::catch_unwind(mem::transmute::<_, fn()>(main)); diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index a11200873d5..5dc6ee2bc8c 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -62,3 +62,7 @@ extern {} #[cfg(target_os = "ios")] #[link(name = "System")] extern {} + +#[cfg(target_os = "haiku")] +#[link(name = "network")] +extern {} diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs index ac0f400379e..f46eab68484 100644 --- a/src/libstd/sync/barrier.rs +++ b/src/libstd/sync/barrier.rs @@ -118,6 +118,7 @@ mod tests { use thread; #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn test_barrier() { const N: usize = 10; diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 3db8b05b954..a983ae716a4 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -270,6 +270,7 @@ mod tests { } #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn notify_one() { let m = Arc::new(Mutex::new(())); let m2 = m.clone(); @@ -286,6 +287,7 @@ mod tests { } #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn notify_all() { const N: usize = 10; @@ -322,6 +324,7 @@ mod tests { } #[test] + #[cfg_attr(target_os = "emscripten", ignore)] fn wait_timeout_ms() { let m = Arc::new(Mutex::new(())); let m2 = m.clone(); @@ -343,6 +346,7 @@ mod tests { #[test] #[should_panic] + #[cfg_attr(target_os = "emscripten", ignore)] fn two_mutexes() { let m = Arc::new(Mutex::new(())); let m2 = m.clone(); diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index 3d9f81413dc..fce640e7c7a 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -270,7 +270,6 @@ use error; use fmt; use mem; use cell::UnsafeCell; -use marker::Reflect; use time::{Duration, Instant}; #[unstable(feature = "mpsc_select", issue = "27800")] @@ -1163,7 +1162,7 @@ impl<T> fmt::Display for SendError<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: Send + Reflect> error::Error for SendError<T> { +impl<T: Send> error::Error for SendError<T> { fn description(&self) -> &str { "sending on a closed channel" } @@ -1198,7 +1197,7 @@ impl<T> fmt::Display for TrySendError<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: Send + Reflect> error::Error for TrySendError<T> { +impl<T: Send> error::Error for TrySendError<T> { fn description(&self) -> &str { match *self { @@ -1268,7 +1267,7 @@ impl error::Error for TryRecvError { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use env; use super::*; @@ -1940,9 +1939,16 @@ mod tests { // wait for the child thread to exit before we exit rx2.recv().unwrap(); } + + #[test] + fn issue_32114() { + let (tx, _) = channel(); + let _ = tx.send(123); + assert_eq!(tx.send(123), Err(SendError(123))); + } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod sync_tests { use env; use thread; diff --git a/src/libstd/sync/mpsc/mpsc_queue.rs b/src/libstd/sync/mpsc/mpsc_queue.rs index d926043fbbc..8d80f942ff7 100644 --- a/src/libstd/sync/mpsc/mpsc_queue.rs +++ b/src/libstd/sync/mpsc/mpsc_queue.rs @@ -146,7 +146,7 @@ impl<T> Drop for Queue<T> { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use sync::mpsc::channel; use super::{Queue, Data, Empty, Inconsistent}; diff --git a/src/libstd/sync/mpsc/oneshot.rs b/src/libstd/sync/mpsc/oneshot.rs index 7389280b853..767e9f96ac8 100644 --- a/src/libstd/sync/mpsc/oneshot.rs +++ b/src/libstd/sync/mpsc/oneshot.rs @@ -113,6 +113,8 @@ impl<T> Packet<T> { // Couldn't send the data, the port hung up first. Return the data // back up the stack. DISCONNECTED => { + self.state.swap(DISCONNECTED, Ordering::SeqCst); + self.upgrade = NothingSent; Err(self.data.take().unwrap()) } diff --git a/src/libstd/sync/mpsc/select.rs b/src/libstd/sync/mpsc/select.rs index 677544d335e..8b4da532af6 100644 --- a/src/libstd/sync/mpsc/select.rs +++ b/src/libstd/sync/mpsc/select.rs @@ -103,7 +103,7 @@ pub struct Handle<'rx, T:Send+'rx> { struct Packets { cur: *mut Handle<'static, ()> } #[doc(hidden)] -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub enum StartResult { Installed, Abort, @@ -352,22 +352,20 @@ impl Iterator for Packets { } } -#[stable(feature = "mpsc_debug", since = "1.7.0")] impl fmt::Debug for Select { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Select {{ .. }}") } } -#[stable(feature = "mpsc_debug", since = "1.7.0")] impl<'rx, T:Send+'rx> fmt::Debug for Handle<'rx, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Handle {{ .. }}") } } -#[cfg(test)] #[allow(unused_imports)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use thread; use sync::mpsc::*; diff --git a/src/libstd/sync/mpsc/spsc_queue.rs b/src/libstd/sync/mpsc/spsc_queue.rs index 724d7b1be73..5858e4b6ddb 100644 --- a/src/libstd/sync/mpsc/spsc_queue.rs +++ b/src/libstd/sync/mpsc/spsc_queue.rs @@ -231,7 +231,7 @@ impl<T> Drop for Queue<T> { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use sync::Arc; use super::Queue; diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 098a3e44258..812724c7a16 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -352,7 +352,7 @@ pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Fla &guard.__lock.poison } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use sync::mpsc::channel; use sync::{Arc, Mutex, Condvar}; diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 86d2986959c..71e163321ae 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -367,7 +367,7 @@ impl OnceState { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use panic; use sync::mpsc::channel; @@ -387,7 +387,7 @@ mod tests { #[test] fn stampede_once() { static O: Once = Once::new(); - static mut run: bool = false; + static mut RUN: bool = false; let (tx, rx) = channel(); for _ in 0..10 { @@ -396,10 +396,10 @@ mod tests { for _ in 0..4 { thread::yield_now() } unsafe { O.call_once(|| { - assert!(!run); - run = true; + assert!(!RUN); + RUN = true; }); - assert!(run); + assert!(RUN); } tx.send(()).unwrap(); }); @@ -407,10 +407,10 @@ mod tests { unsafe { O.call_once(|| { - assert!(!run); - run = true; + assert!(!RUN); + RUN = true; }); - assert!(run); + assert!(RUN); } for _ in 0..10 { diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index 7f053c6704b..f08b7641521 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -136,6 +136,10 @@ impl<T: ?Sized> RwLock<T> { /// This function will return an error if the RwLock is poisoned. An RwLock /// is poisoned whenever a writer panics while holding an exclusive lock. /// The failure will occur immediately after the lock has been acquired. + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by the current thread. #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn read(&self) -> LockResult<RwLockReadGuard<T>> { @@ -188,6 +192,10 @@ impl<T: ?Sized> RwLock<T> { /// This function will return an error if the RwLock is poisoned. An RwLock /// is poisoned whenever a writer panics while holding an exclusive lock. /// An error will be returned when the lock is acquired. + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by the current thread. #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> { @@ -380,7 +388,7 @@ impl<'a, T: ?Sized> Drop for RwLockWriteGuard<'a, T> { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { #![allow(deprecated)] // rand diff --git a/src/libstd/sys/common/args.rs b/src/libstd/sys/common/args.rs deleted file mode 100644 index fad2c277da4..00000000000 --- a/src/libstd/sys/common/args.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Global storage for command line arguments -//! -//! The current incarnation of the Rust runtime expects for -//! the processes `argc` and `argv` arguments to be stored -//! in a globally-accessible location for use by the `os` module. -//! -//! Only valid to call on Linux. Mac and Windows use syscalls to -//! discover the command line arguments. -//! -//! FIXME #7756: Would be nice for this to not exist. - -#![allow(dead_code)] // different code on OSX/linux/etc - -/// One-time global initialization. -pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) } - -/// One-time global cleanup. -pub unsafe fn cleanup() { imp::cleanup() } - -/// Make a clone of the global arguments. -pub fn clone() -> Option<Vec<Vec<u8>>> { imp::clone() } - -#[cfg(any(target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "emscripten"))] -mod imp { - use libc::c_char; - use mem; - use ffi::CStr; - - use sys_common::mutex::Mutex; - - static mut GLOBAL_ARGS_PTR: usize = 0; - static LOCK: Mutex = Mutex::new(); - - pub unsafe fn init(argc: isize, argv: *const *const u8) { - let args = (0..argc).map(|i| { - CStr::from_ptr(*argv.offset(i) as *const c_char).to_bytes().to_vec() - }).collect(); - - LOCK.lock(); - let ptr = get_global_ptr(); - assert!((*ptr).is_none()); - (*ptr) = Some(box args); - LOCK.unlock(); - } - - pub unsafe fn cleanup() { - LOCK.lock(); - *get_global_ptr() = None; - LOCK.unlock(); - } - - pub fn clone() -> Option<Vec<Vec<u8>>> { - unsafe { - LOCK.lock(); - let ptr = get_global_ptr(); - let ret = (*ptr).as_ref().map(|s| (**s).clone()); - LOCK.unlock(); - return ret - } - } - - fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> { - unsafe { mem::transmute(&GLOBAL_ARGS_PTR) } - } - -} - -#[cfg(any(target_os = "macos", - target_os = "ios", - target_os = "windows"))] -mod imp { - pub unsafe fn init(_argc: isize, _argv: *const *const u8) { - } - - pub fn cleanup() { - } - - pub fn clone() -> Option<Vec<Vec<u8>>> { - panic!() - } -} diff --git a/src/libstd/sys/common/io.rs b/src/libstd/sys/common/io.rs index 3cd70eddb85..0483725dd83 100644 --- a/src/libstd/sys/common/io.rs +++ b/src/libstd/sys/common/io.rs @@ -51,6 +51,7 @@ pub unsafe fn read_to_end_uninitialized(r: &mut Read, buf: &mut Vec<u8>) -> io:: } #[cfg(test)] +#[allow(dead_code)] // not used on emscripten pub mod test { use path::{Path, PathBuf}; use env; @@ -165,6 +166,7 @@ mod tests { } #[bench] + #[cfg_attr(target_os = "emscripten", ignore)] fn bench_uninitialized(b: &mut ::test::Bencher) { b.iter(|| { let mut lr = repeat(1).take(10000000); diff --git a/src/libstd/sys/common/memchr.rs b/src/libstd/sys/common/memchr.rs new file mode 100644 index 00000000000..3824a5fb528 --- /dev/null +++ b/src/libstd/sys/common/memchr.rs @@ -0,0 +1,230 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Original implementation taken from rust-memchr +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +#[allow(dead_code)] +pub mod fallback { + use cmp; + use mem; + + const LO_U64: u64 = 0x0101010101010101; + const HI_U64: u64 = 0x8080808080808080; + + // use truncation + const LO_USIZE: usize = LO_U64 as usize; + const HI_USIZE: usize = HI_U64 as usize; + + /// Return `true` if `x` contains any zero byte. + /// + /// From *Matters Computational*, J. Arndt + /// + /// "The idea is to subtract one from each of the bytes and then look for + /// bytes where the borrow propagated all the way to the most significant + /// bit." + #[inline] + fn contains_zero_byte(x: usize) -> bool { + x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0 + } + + #[cfg(target_pointer_width = "32")] + #[inline] + fn repeat_byte(b: u8) -> usize { + let mut rep = (b as usize) << 8 | b as usize; + rep = rep << 16 | rep; + rep + } + + #[cfg(target_pointer_width = "64")] + #[inline] + fn repeat_byte(b: u8) -> usize { + let mut rep = (b as usize) << 8 | b as usize; + rep = rep << 16 | rep; + rep = rep << 32 | rep; + rep + } + + /// Return the first index matching the byte `a` in `text`. + pub fn memchr(x: u8, text: &[u8]) -> Option<usize> { + // Scan for a single byte value by reading two `usize` words at a time. + // + // Split `text` in three parts + // - unaligned initial part, before the first word aligned address in text + // - body, scan by 2 words at a time + // - the last remaining part, < 2 word size + let len = text.len(); + let ptr = text.as_ptr(); + let usize_bytes = mem::size_of::<usize>(); + + // search up to an aligned boundary + let align = (ptr as usize) & (usize_bytes- 1); + let mut offset; + if align > 0 { + offset = cmp::min(usize_bytes - align, len); + if let Some(index) = text[..offset].iter().position(|elt| *elt == x) { + return Some(index); + } + } else { + offset = 0; + } + + // search the body of the text + let repeated_x = repeat_byte(x); + + if len >= 2 * usize_bytes { + while offset <= len - 2 * usize_bytes { + unsafe { + let u = *(ptr.offset(offset as isize) as *const usize); + let v = *(ptr.offset((offset + usize_bytes) as isize) as *const usize); + + // break if there is a matching byte + let zu = contains_zero_byte(u ^ repeated_x); + let zv = contains_zero_byte(v ^ repeated_x); + if zu || zv { + break; + } + } + offset += usize_bytes * 2; + } + } + + // find the byte after the point the body loop stopped + text[offset..].iter().position(|elt| *elt == x).map(|i| offset + i) + } + + /// Return the last index matching the byte `a` in `text`. + pub fn memrchr(x: u8, text: &[u8]) -> Option<usize> { + // Scan for a single byte value by reading two `usize` words at a time. + // + // Split `text` in three parts + // - unaligned tail, after the last word aligned address in text + // - body, scan by 2 words at a time + // - the first remaining bytes, < 2 word size + let len = text.len(); + let ptr = text.as_ptr(); + let usize_bytes = mem::size_of::<usize>(); + + // search to an aligned boundary + let end_align = (ptr as usize + len) & (usize_bytes - 1); + let mut offset; + if end_align > 0 { + offset = if end_align >= len { 0 } else { len - end_align }; + if let Some(index) = text[offset..].iter().rposition(|elt| *elt == x) { + return Some(offset + index); + } + } else { + offset = len; + } + + // search the body of the text + let repeated_x = repeat_byte(x); + + while offset >= 2 * usize_bytes { + unsafe { + let u = *(ptr.offset(offset as isize - 2 * usize_bytes as isize) as *const usize); + let v = *(ptr.offset(offset as isize - usize_bytes as isize) as *const usize); + + // break if there is a matching byte + let zu = contains_zero_byte(u ^ repeated_x); + let zv = contains_zero_byte(v ^ repeated_x); + if zu || zv { + break; + } + } + offset -= 2 * usize_bytes; + } + + // find the byte before the point the body loop stopped + text[..offset].iter().rposition(|elt| *elt == x) + } + + // test fallback implementations on all platforms + #[test] + fn matches_one() { + assert_eq!(Some(0), memchr(b'a', b"a")); + } + + #[test] + fn matches_begin() { + assert_eq!(Some(0), memchr(b'a', b"aaaa")); + } + + #[test] + fn matches_end() { + assert_eq!(Some(4), memchr(b'z', b"aaaaz")); + } + + #[test] + fn matches_nul() { + assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00")); + } + + #[test] + fn matches_past_nul() { + assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z")); + } + + #[test] + fn no_match_empty() { + assert_eq!(None, memchr(b'a', b"")); + } + + #[test] + fn no_match() { + assert_eq!(None, memchr(b'a', b"xyz")); + } + + #[test] + fn matches_one_reversed() { + assert_eq!(Some(0), memrchr(b'a', b"a")); + } + + #[test] + fn matches_begin_reversed() { + assert_eq!(Some(3), memrchr(b'a', b"aaaa")); + } + + #[test] + fn matches_end_reversed() { + assert_eq!(Some(0), memrchr(b'z', b"zaaaa")); + } + + #[test] + fn matches_nul_reversed() { + assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00")); + } + + #[test] + fn matches_past_nul_reversed() { + assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa")); + } + + #[test] + fn no_match_empty_reversed() { + assert_eq!(None, memrchr(b'a', b"")); + } + + #[test] + fn no_match_reversed() { + assert_eq!(None, memrchr(b'a', b"xyz")); + } + + #[test] + fn each_alignment_reversed() { + let mut data = [1u8; 64]; + let needle = 2; + let pos = 40; + data[pos] = needle; + for start in 0..16 { + assert_eq!(Some(pos - start), memrchr(needle, &data[start..])); + } + } +} diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index d1ca6765107..2845f895f18 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -25,12 +25,12 @@ macro_rules! rtassert { }) } -pub mod args; pub mod at_exit_imp; #[cfg(any(not(cargobuild), feature = "backtrace"))] pub mod backtrace; pub mod condvar; pub mod io; +pub mod memchr; pub mod mutex; pub mod net; pub mod poison; @@ -91,7 +91,7 @@ pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> { pub fn cleanup() { static CLEANUP: Once = Once::new(); CLEANUP.call_once(|| unsafe { - args::cleanup(); + sys::args::cleanup(); sys::stack_overflow::cleanup(); at_exit_imp::cleanup(); }); diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index a777cfe35e5..10ad61f4c80 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -24,24 +24,35 @@ use time::Duration; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "openbsd", target_os = "netbsd", - target_os = "solaris"))] + target_os = "solaris", target_os = "haiku"))] use sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "openbsd", target_os = "netbsd", - target_os = "solaris")))] + target_os = "solaris", target_os = "haiku")))] use sys::net::netc::IPV6_ADD_MEMBERSHIP; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "openbsd", target_os = "netbsd", - target_os = "solaris"))] + target_os = "solaris", target_os = "haiku"))] use sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "openbsd", target_os = "netbsd", - target_os = "solaris")))] + target_os = "solaris", target_os = "haiku")))] use sys::net::netc::IPV6_DROP_MEMBERSHIP; +#[cfg(any(target_os = "linux", target_os = "android", + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "haiku", target_os = "bitrig"))] +use libc::MSG_NOSIGNAL; +#[cfg(not(any(target_os = "linux", target_os = "android", + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "haiku", target_os = "bitrig")))] +const MSG_NOSIGNAL: c_int = 0x0; + //////////////////////////////////////////////////////////////////////////////// // sockaddr and misc bindings //////////////////////////////////////////////////////////////////////////////// @@ -225,7 +236,7 @@ impl TcpStream { c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, - 0) + MSG_NOSIGNAL) })?; Ok(ret as usize) } @@ -449,7 +460,7 @@ impl UdpSocket { let ret = cvt(unsafe { c::sendto(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, - 0, dstp, dstlen) + MSG_NOSIGNAL, dstp, dstlen) })?; Ok(ret as usize) } @@ -573,7 +584,7 @@ impl UdpSocket { c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, - 0) + MSG_NOSIGNAL) })?; Ok(ret as usize) } diff --git a/src/libstd/sys/common/poison.rs b/src/libstd/sys/common/poison.rs index 55212bf35d6..bdc727f1dfc 100644 --- a/src/libstd/sys/common/poison.rs +++ b/src/libstd/sys/common/poison.rs @@ -10,7 +10,6 @@ use error::{Error}; use fmt; -use marker::Reflect; use sync::atomic::{AtomicBool, Ordering}; use thread; @@ -117,7 +116,7 @@ impl<T> fmt::Display for PoisonError<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: Reflect> Error for PoisonError<T> { +impl<T> Error for PoisonError<T> { fn description(&self) -> &str { "poisoned lock: another task failed inside" } @@ -174,7 +173,7 @@ impl<T> fmt::Display for TryLockError<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: Reflect> Error for TryLockError<T> { +impl<T> Error for TryLockError<T> { fn description(&self) -> &str { match *self { TryLockError::Poisoned(ref p) => p.description(), diff --git a/src/libstd/sys/common/remutex.rs b/src/libstd/sys/common/remutex.rs index cbdeaad7f6b..4d0407ccf6c 100644 --- a/src/libstd/sys/common/remutex.rs +++ b/src/libstd/sys/common/remutex.rs @@ -156,7 +156,7 @@ impl<'a, T> Drop for ReentrantMutexGuard<'a, T> { } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use cell::RefCell; diff --git a/src/libstd/sys/common/wtf8.rs b/src/libstd/sys/common/wtf8.rs index 8d357aa78c9..0a94ff1e958 100644 --- a/src/libstd/sys/common/wtf8.rs +++ b/src/libstd/sys/common/wtf8.rs @@ -206,10 +206,12 @@ impl Wtf8Buf { /// Copied from String::push /// This does **not** include the WTF-8 concatenation check. fn push_code_point_unchecked(&mut self, code_point: CodePoint) { - let bytes = unsafe { - char::from_u32_unchecked(code_point.value).encode_utf8() + let c = unsafe { + char::from_u32_unchecked(code_point.value) }; - self.bytes.extend_from_slice(bytes.as_slice()); + let mut bytes = [0; 4]; + let bytes = c.encode_utf8(&mut bytes).as_bytes(); + self.bytes.extend_from_slice(bytes) } #[inline] @@ -738,15 +740,16 @@ impl<'a> Iterator for EncodeWide<'a> { return Some(tmp); } + let mut buf = [0; 2]; self.code_points.next().map(|code_point| { - let n = unsafe { - char::from_u32_unchecked(code_point.value).encode_utf16() + let c = unsafe { + char::from_u32_unchecked(code_point.value) }; - let n = n.as_slice(); - if n.len() == 2 { - self.extra = n[1]; + let n = c.encode_utf16(&mut buf).len(); + if n == 2 { + self.extra = buf[1]; } - n[0] + buf[0] }) } diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs index abbe3fc1846..10436723a81 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -28,10 +28,11 @@ #![cfg(target_os = "android")] -use libc::{c_int, sighandler_t}; +use libc::{c_int, c_void, sighandler_t, size_t, ssize_t}; +use libc::{ftruncate, pread, pwrite}; use io; -use sys::cvt_r; +use super::{cvt, cvt_r}; // The `log2` and `log2f` functions apparently appeared in android-18, or at // least you can see they're not present in the android-17 header [1] and they @@ -96,13 +97,10 @@ pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t { // // If it doesn't we just fall back to `ftruncate`, generating an error for // too-large values. +#[cfg(target_pointer_width = "32")] pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { weak!(fn ftruncate64(c_int, i64) -> c_int); - extern { - fn ftruncate(fd: c_int, off: i32) -> c_int; - } - unsafe { match ftruncate64.get() { Some(f) => cvt_r(|| f(fd, size as i64)).map(|_| ()), @@ -117,3 +115,56 @@ pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { } } } + +#[cfg(target_pointer_width = "64")] +pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { + unsafe { + cvt_r(|| ftruncate(fd, size as i64)).map(|_| ()) + } +} + +#[cfg(target_pointer_width = "32")] +pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64) + -> io::Result<ssize_t> +{ + use convert::TryInto; + weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t); + pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { + if let Ok(o) = offset.try_into() { + cvt(pread(fd, buf, count, o)) + } else { + Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot pread >2GB")) + } + }) +} + +#[cfg(target_pointer_width = "32")] +pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64) + -> io::Result<ssize_t> +{ + use convert::TryInto; + weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t); + pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { + if let Ok(o) = offset.try_into() { + cvt(pwrite(fd, buf, count, o)) + } else { + Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot pwrite >2GB")) + } + }) +} + +#[cfg(target_pointer_width = "64")] +pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64) + -> io::Result<ssize_t> +{ + cvt(pread(fd, buf, count, offset)) +} + +#[cfg(target_pointer_width = "64")] +pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64) + -> io::Result<ssize_t> +{ + cvt(pwrite(fd, buf, count, offset)) +} diff --git a/src/libstd/sys/unix/args.rs b/src/libstd/sys/unix/args.rs new file mode 100644 index 00000000000..c04fd863674 --- /dev/null +++ b/src/libstd/sys/unix/args.rs @@ -0,0 +1,212 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Global initialization and retreival of command line arguments. +//! +//! On some platforms these are stored during runtime startup, +//! and on some they are retrieved from the system on demand. + +#![allow(dead_code)] // runtime init functions not used during testing + +use ffi::OsString; +use marker::PhantomData; +use vec; + +/// One-time global initialization. +pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) } + +/// One-time global cleanup. +pub unsafe fn cleanup() { imp::cleanup() } + +/// Returns the command line arguments +pub fn args() -> Args { + imp::args() +} + +pub struct Args { + iter: vec::IntoIter<OsString>, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option<OsString> { self.iter.next() } + fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { self.iter.len() } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() } +} + +#[cfg(any(target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "bitrig", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "emscripten", + target_os = "haiku", + target_os = "fuchsia"))] +mod imp { + use os::unix::prelude::*; + use mem; + use ffi::{CStr, OsString}; + use marker::PhantomData; + use libc; + use super::Args; + + use sys_common::mutex::Mutex; + + static mut GLOBAL_ARGS_PTR: usize = 0; + static LOCK: Mutex = Mutex::new(); + + pub unsafe fn init(argc: isize, argv: *const *const u8) { + let args = (0..argc).map(|i| { + CStr::from_ptr(*argv.offset(i) as *const libc::c_char).to_bytes().to_vec() + }).collect(); + + LOCK.lock(); + let ptr = get_global_ptr(); + assert!((*ptr).is_none()); + (*ptr) = Some(box args); + LOCK.unlock(); + } + + pub unsafe fn cleanup() { + LOCK.lock(); + *get_global_ptr() = None; + LOCK.unlock(); + } + + pub fn args() -> Args { + let bytes = clone().unwrap_or(Vec::new()); + let v: Vec<OsString> = bytes.into_iter().map(|v| { + OsStringExt::from_vec(v) + }).collect(); + Args { iter: v.into_iter(), _dont_send_or_sync_me: PhantomData } + } + + fn clone() -> Option<Vec<Vec<u8>>> { + unsafe { + LOCK.lock(); + let ptr = get_global_ptr(); + let ret = (*ptr).as_ref().map(|s| (**s).clone()); + LOCK.unlock(); + return ret + } + } + + fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> { + unsafe { mem::transmute(&GLOBAL_ARGS_PTR) } + } + +} + +#[cfg(any(target_os = "macos", + target_os = "ios"))] +mod imp { + use ffi::CStr; + use marker::PhantomData; + use libc; + use super::Args; + + pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + } + + pub fn cleanup() { + } + + #[cfg(target_os = "macos")] + pub fn args() -> Args { + use os::unix::prelude::*; + extern { + // These functions are in crt_externs.h. + fn _NSGetArgc() -> *mut libc::c_int; + fn _NSGetArgv() -> *mut *mut *mut libc::c_char; + } + + let vec = unsafe { + let (argc, argv) = (*_NSGetArgc() as isize, + *_NSGetArgv() as *const *const libc::c_char); + (0.. argc as isize).map(|i| { + let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec(); + OsStringExt::from_vec(bytes) + }).collect::<Vec<_>>() + }; + Args { + iter: vec.into_iter(), + _dont_send_or_sync_me: PhantomData, + } + } + + // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs + // and use underscores in their names - they're most probably + // are considered private and therefore should be avoided + // Here is another way to get arguments using Objective C + // runtime + // + // In general it looks like: + // res = Vec::new() + // let args = [[NSProcessInfo processInfo] arguments] + // for i in (0..[args count]) + // res.push([args objectAtIndex:i]) + // res + #[cfg(target_os = "ios")] + pub fn args() -> Args { + use ffi::OsString; + use mem; + use str; + + extern { + fn sel_registerName(name: *const libc::c_uchar) -> Sel; + fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; + fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; + } + + #[link(name = "Foundation", kind = "framework")] + #[link(name = "objc")] + #[cfg(not(cargobuild))] + extern {} + + type Sel = *const libc::c_void; + type NsId = *const libc::c_void; + + let mut res = Vec::new(); + + unsafe { + let process_info_sel = sel_registerName("processInfo\0".as_ptr()); + let arguments_sel = sel_registerName("arguments\0".as_ptr()); + let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); + let count_sel = sel_registerName("count\0".as_ptr()); + let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); + + let klass = objc_getClass("NSProcessInfo\0".as_ptr()); + let info = objc_msgSend(klass, process_info_sel); + let args = objc_msgSend(info, arguments_sel); + + let cnt: usize = mem::transmute(objc_msgSend(args, count_sel)); + for i in 0..cnt { + let tmp = objc_msgSend(args, object_at_sel, i); + let utf_c_str: *const libc::c_char = + mem::transmute(objc_msgSend(tmp, utf8_sel)); + let bytes = CStr::from_ptr(utf_c_str).to_bytes(); + res.push(OsString::from(str::from_utf8(bytes).unwrap())) + } + } + + Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData } + } +} diff --git a/src/libstd/sys/unix/env.rs b/src/libstd/sys/unix/env.rs new file mode 100644 index 00000000000..eff3a8c2a34 --- /dev/null +++ b/src/libstd/sys/unix/env.rs @@ -0,0 +1,184 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[cfg(target_os = "linux")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "linux"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(target_os = "macos")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "macos"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".dylib"; + pub const DLL_EXTENSION: &'static str = "dylib"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(target_os = "ios")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "ios"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".dylib"; + pub const DLL_EXTENSION: &'static str = "dylib"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(target_os = "freebsd")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "freebsd"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(target_os = "dragonfly")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "dragonfly"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(target_os = "bitrig")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "bitrig"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(target_os = "netbsd")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "netbsd"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(target_os = "openbsd")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "openbsd"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(target_os = "android")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "android"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(target_os = "solaris")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "solaris"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(all(target_os = "nacl", not(target_arch = "le32")))] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "nacl"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ".nexe"; + pub const EXE_EXTENSION: &'static str = "nexe"; +} +#[cfg(all(target_os = "nacl", target_arch = "le32"))] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "pnacl"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".pso"; + pub const DLL_EXTENSION: &'static str = "pso"; + pub const EXE_SUFFIX: &'static str = ".pexe"; + pub const EXE_EXTENSION: &'static str = "pexe"; +} + +#[cfg(target_os = "haiku")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "haiku"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} + +#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "emscripten"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ".js"; + pub const EXE_EXTENSION: &'static str = "js"; +} + +#[cfg(all(target_os = "emscripten", target_arch = "wasm32"))] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "emscripten"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ".js"; + pub const EXE_EXTENSION: &'static str = "js"; +} + +#[cfg(target_os = "fuchsia")] +pub mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "fuchsia"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 77587918ac9..fcfab051588 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -20,6 +20,51 @@ use sys; use sys_common::{FromInner, AsInner, AsInnerMut}; use sys::platform::fs::MetadataExt as UnixMetadataExt; +/// Unix-specific extensions to `File` +#[unstable(feature = "file_offset", issue = "35918")] +pub trait FileExt { + /// Reads a number of bytes starting from a given offset. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Note that similar to `File::read`, it is not an error to return with a + /// short read. + #[unstable(feature = "file_offset", issue = "35918")] + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; + + /// Writes a number of bytes starting from a given offset. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// When writing beyond the end of the file, the file is appropiately + /// extended and the intermediate bytes are initialized with the value 0. + /// + /// Note that similar to `File::write`, it is not an error to return a + /// short write. + #[unstable(feature = "file_offset", issue = "35918")] + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>; +} + +#[unstable(feature = "file_offset", issue = "35918")] +impl FileExt for fs::File { + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.as_inner().read_at(buf, offset) + } + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + self.as_inner().write_at(buf, offset) + } +} + /// Unix-specific extensions to `Permissions` #[stable(feature = "fs_ext", since = "1.1.0")] pub trait PermissionsExt { diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs index 1be3d75d866..b2483f4e209 100644 --- a/src/libstd/sys/unix/ext/mod.rs +++ b/src/libstd/sys/unix/ext/mod.rs @@ -50,6 +50,8 @@ pub mod prelude { pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::fs::DirEntryExt; + #[doc(no_inline)] #[unstable(feature = "file_offset", issue = "35918")] + pub use super::fs::FileExt; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::thread::JoinHandleExt; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 3f93fce1935..80f53da1cef 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -28,6 +28,17 @@ use sys::cvt; use sys::net::Socket; use sys_common::{AsInner, FromInner, IntoInner}; +#[cfg(any(target_os = "linux", target_os = "android", + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "haiku", target_os = "bitrig"))] +use libc::MSG_NOSIGNAL; +#[cfg(not(any(target_os = "linux", target_os = "android", + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "haiku", target_os = "bitrig")))] +const MSG_NOSIGNAL: libc::c_int = 0x0; + fn sun_path_offset() -> usize { unsafe { // Work with an actual instance of the type since using a null pointer is UB @@ -690,7 +701,7 @@ impl UnixDatagram { let count = cvt(libc::sendto(*d.0.as_inner(), buf.as_ptr() as *const _, buf.len(), - 0, + MSG_NOSIGNAL, &addr as *const _ as *const _, len))?; Ok(count as usize) @@ -786,7 +797,7 @@ impl IntoRawFd for UnixDatagram { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod test { use thread; use io; diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index 5bd92f2eb57..3a7c59d4e6d 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -161,21 +161,21 @@ impl AsRawFd for process::ChildStderr { } } -#[stable(feature = "process_extensions", since = "1.2.0")] +#[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawFd for process::ChildStdin { fn into_raw_fd(self) -> RawFd { self.into_inner().into_fd().into_raw() } } -#[stable(feature = "process_extensions", since = "1.2.0")] +#[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawFd for process::ChildStdout { fn into_raw_fd(self) -> RawFd { self.into_inner().into_fd().into_raw() } } -#[stable(feature = "process_extensions", since = "1.2.0")] +#[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawFd for process::ChildStderr { fn into_raw_fd(self) -> RawFd { self.into_inner().into_fd().into_raw() diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index b2b1f16f20a..41bb96fed16 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -11,7 +11,7 @@ #![unstable(reason = "not public", issue = "0", feature = "fd")] use io::{self, Read}; -use libc::{self, c_int, size_t, c_void}; +use libc::{self, c_int, c_void}; use mem; use sync::atomic::{AtomicBool, Ordering}; use sys::cvt; @@ -40,7 +40,7 @@ impl FileDesc { let ret = cvt(unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, - buf.len() as size_t) + buf.len()) })?; Ok(ret as usize) } @@ -50,23 +50,77 @@ impl FileDesc { (&mut me).read_to_end(buf) } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + #[cfg(target_os = "android")] + use super::android::cvt_pread64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64) + -> io::Result<isize> + { + #[cfg(any(target_os = "linux", target_os = "emscripten"))] + use libc::pread64; + #[cfg(not(any(target_os = "linux", target_os = "emscripten")))] + use libc::pread as pread64; + cvt(pread64(fd, buf, count, offset)) + } + + unsafe { + cvt_pread64(self.fd, + buf.as_mut_ptr() as *mut c_void, + buf.len(), + offset as i64) + .map(|n| n as usize) + } + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let ret = cvt(unsafe { libc::write(self.fd, buf.as_ptr() as *const c_void, - buf.len() as size_t) + buf.len()) })?; Ok(ret as usize) } - #[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten")))] + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + #[cfg(target_os = "android")] + use super::android::cvt_pwrite64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64) + -> io::Result<isize> + { + #[cfg(any(target_os = "linux", target_os = "emscripten"))] + use libc::pwrite64; + #[cfg(not(any(target_os = "linux", target_os = "emscripten")))] + use libc::pwrite as pwrite64; + cvt(pwrite64(fd, buf, count, offset)) + } + + unsafe { + cvt_pwrite64(self.fd, + buf.as_ptr() as *const c_void, + buf.len(), + offset as i64) + .map(|n| n as usize) + } + } + + #[cfg(not(any(target_env = "newlib", + target_os = "solaris", + target_os = "emscripten", + target_os = "haiku")))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; Ok(()) } } - #[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten"))] + #[cfg(any(target_env = "newlib", + target_os = "solaris", + target_os = "emscripten", + target_os = "haiku"))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; @@ -104,9 +158,9 @@ impl FileDesc { // resolve so we at least compile this. // // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963 - #[cfg(target_os = "android")] + #[cfg(any(target_os = "android", target_os = "haiku"))] use libc::F_DUPFD as F_DUPFD_CLOEXEC; - #[cfg(not(target_os = "android"))] + #[cfg(not(any(target_os = "android", target_os="haiku")))] use libc::F_DUPFD_CLOEXEC; let make_filedesc = |fd| { diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index e6fe3eb112a..0b43fd2ac8c 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -193,6 +193,14 @@ impl FromInner<u32> for FilePermissions { } } +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("/home")' + fmt::Debug::fmt(&*self.root, f) + } +} + impl Iterator for ReadDir { type Item = io::Result<DirEntry>; @@ -279,7 +287,12 @@ impl DirEntry { stat(&self.path()).map(|m| m.file_type()) } - #[cfg(not(target_os = "solaris"))] + #[cfg(target_os = "haiku")] + pub fn file_type(&self) -> io::Result<FileType> { + lstat(&self.path()).map(|m| m.file_type()) + } + + #[cfg(not(any(target_os = "solaris", target_os = "haiku")))] pub fn file_type(&self) -> io::Result<FileType> { match self.entry.d_type { libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), @@ -298,7 +311,9 @@ impl DirEntry { target_os = "linux", target_os = "emscripten", target_os = "android", - target_os = "solaris"))] + target_os = "solaris", + target_os = "haiku", + target_os = "fuchsia"))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 } @@ -327,7 +342,9 @@ impl DirEntry { } #[cfg(any(target_os = "android", target_os = "linux", - target_os = "emscripten"))] + target_os = "emscripten", + target_os = "haiku", + target_os = "fuchsia"))] fn name_bytes(&self) -> &[u8] { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() @@ -476,10 +493,18 @@ impl File { self.0.read_to_end(buf) } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.0.read_at(buf, offset) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + self.0.write_at(buf, offset) + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { @@ -662,7 +687,7 @@ pub fn readlink(p: &Path) -> io::Result<PathBuf> { loop { let buf_read = cvt(unsafe { - libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity() as libc::size_t) + libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize; unsafe { buf.set_len(buf_read); } diff --git a/src/libstd/sys/unix/memchr.rs b/src/libstd/sys/unix/memchr.rs new file mode 100644 index 00000000000..aed04703ea1 --- /dev/null +++ b/src/libstd/sys/unix/memchr.rs @@ -0,0 +1,57 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Original implementation taken from rust-memchr +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> { + use libc; + + let p = unsafe { + libc::memchr( + haystack.as_ptr() as *const libc::c_void, + needle as libc::c_int, + haystack.len()) + }; + if p.is_null() { + None + } else { + Some(p as usize - (haystack.as_ptr() as usize)) + } +} + +pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> { + + #[cfg(target_os = "linux")] + fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> { + use libc; + + // GNU's memrchr() will - unlike memchr() - error if haystack is empty. + if haystack.is_empty() {return None} + let p = unsafe { + libc::memrchr( + haystack.as_ptr() as *const libc::c_void, + needle as libc::c_int, + haystack.len()) + }; + if p.is_null() { + None + } else { + Some(p as usize - (haystack.as_ptr() as usize)) + } + } + + #[cfg(not(target_os = "linux"))] + fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> { + ::sys_common::memchr::fallback::memrchr(needle, haystack) + } + + memrchr_specific(needle, haystack) +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 23687e10e47..66bc9d4a491 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -17,6 +17,7 @@ use libc; #[cfg(target_os = "bitrig")] pub use os::bitrig as platform; #[cfg(target_os = "dragonfly")] pub use os::dragonfly as platform; #[cfg(target_os = "freebsd")] pub use os::freebsd as platform; +#[cfg(target_os = "haiku")] pub use os::haiku as platform; #[cfg(target_os = "ios")] pub use os::ios as platform; #[cfg(target_os = "linux")] pub use os::linux as platform; #[cfg(target_os = "macos")] pub use os::macos as platform; @@ -25,21 +26,26 @@ use libc; #[cfg(target_os = "openbsd")] pub use os::openbsd as platform; #[cfg(target_os = "solaris")] pub use os::solaris as platform; #[cfg(target_os = "emscripten")] pub use os::emscripten as platform; +#[cfg(target_os = "fuchsia")] pub use os::fuchsia as platform; #[macro_use] pub mod weak; +pub mod args; pub mod android; #[cfg(any(not(cargobuild), feature = "backtrace"))] pub mod backtrace; pub mod condvar; +pub mod env; pub mod ext; pub mod fd; pub mod fs; +pub mod memchr; pub mod mutex; pub mod net; pub mod os; pub mod os_str; +pub mod path; pub mod pipe; pub mod process; pub mod rand; @@ -78,16 +84,16 @@ pub fn init() { unsafe { libc::write(libc::STDERR_FILENO, msg.as_ptr() as *const libc::c_void, - msg.len() as libc::size_t); + msg.len()); intrinsics::abort(); } } - #[cfg(not(any(target_os = "nacl", target_os = "emscripten")))] + #[cfg(not(any(target_os = "nacl", target_os = "emscripten", target_os="fuchsia")))] unsafe fn reset_sigpipe() { assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0); } - #[cfg(any(target_os = "nacl", target_os = "emscripten"))] + #[cfg(any(target_os = "nacl", target_os = "emscripten", target_os="fuchsia"))] unsafe fn reset_sigpipe() {} } diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index f124ea651ec..ad287bbec38 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -10,7 +10,7 @@ use ffi::CStr; use io; -use libc::{self, c_int, size_t, sockaddr, socklen_t}; +use libc::{self, c_int, size_t, sockaddr, socklen_t, EAI_SYSTEM}; use net::{SocketAddr, Shutdown}; use str; use sys::fd::FileDesc; @@ -33,12 +33,25 @@ use libc::SOCK_CLOEXEC; #[cfg(not(target_os = "linux"))] const SOCK_CLOEXEC: c_int = 0; +// Another conditional contant for name resolution: Macos et iOS use +// SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. +// Other platforms do otherwise. +#[cfg(target_vendor = "apple")] +use libc::SO_NOSIGPIPE; +#[cfg(not(target_vendor = "apple"))] +const SO_NOSIGPIPE: c_int = 0; + pub struct Socket(FileDesc); pub fn init() {} pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { return Ok(()) } + if err == 0 { + return Ok(()) + } + if err == EAI_SYSTEM { + return Err(io::Error::last_os_error()) + } let detail = unsafe { str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap() @@ -76,7 +89,11 @@ impl Socket { let fd = cvt(libc::socket(fam, ty, 0))?; let fd = FileDesc::new(fd); fd.set_cloexec()?; - Ok(Socket(fd)) + let socket = Socket(fd); + if cfg!(target_vendor = "apple") { + setsockopt(&socket, libc::SOL_SOCKET, SO_NOSIGPIPE, 1)?; + } + Ok(socket) } } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 82606d2c728..e591f25cac1 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -38,7 +38,7 @@ static ENV_LOCK: Mutex = Mutex::new(); extern { #[cfg(not(target_os = "dragonfly"))] - #[cfg_attr(any(target_os = "linux", target_os = "emscripten"), + #[cfg_attr(any(target_os = "linux", target_os = "emscripten", target_os = "fuchsia"), link_name = "__errno_location")] #[cfg_attr(any(target_os = "bitrig", target_os = "netbsd", @@ -51,6 +51,7 @@ extern { target_os = "ios", target_os = "freebsd"), link_name = "__error")] + #[cfg_attr(target_os = "haiku", link_name = "_errnop")] fn errno_location() -> *mut c_int; } @@ -93,7 +94,7 @@ pub fn error_string(errno: i32) -> String { let p = buf.as_mut_ptr(); unsafe { - if strerror_r(errno as c_int, p, buf.len() as libc::size_t) < 0 { + if strerror_r(errno as c_int, p, buf.len()) < 0 { panic!("strerror_r failure"); } @@ -107,7 +108,7 @@ pub fn getcwd() -> io::Result<PathBuf> { loop { unsafe { let ptr = buf.as_mut_ptr() as *mut libc::c_char; - if !libc::getcwd(ptr, buf.capacity() as libc::size_t).is_null() { + if !libc::getcwd(ptr, buf.capacity()).is_null() { let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); buf.set_len(len); buf.shrink_to_fit(); @@ -199,21 +200,20 @@ pub fn current_exe() -> io::Result<PathBuf> { libc::KERN_PROC as c_int, libc::KERN_PROC_PATHNAME as c_int, -1 as c_int]; - let mut sz: libc::size_t = 0; + let mut sz = 0; cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, - ptr::null_mut(), &mut sz, ptr::null_mut(), - 0 as libc::size_t))?; + ptr::null_mut(), &mut sz, ptr::null_mut(), 0))?; if sz == 0 { return Err(io::Error::last_os_error()) } - let mut v: Vec<u8> = Vec::with_capacity(sz as usize); + let mut v: Vec<u8> = Vec::with_capacity(sz); cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, v.as_mut_ptr() as *mut libc::c_void, &mut sz, - ptr::null_mut(), 0 as libc::size_t))?; + ptr::null_mut(), 0))?; if sz == 0 { return Err(io::Error::last_os_error()); } - v.set_len(sz as usize - 1); // chop off trailing NUL + v.set_len(sz - 1); // chop off trailing NUL Ok(PathBuf::from(OsString::from_vec(v))) } } @@ -303,123 +303,53 @@ pub fn current_exe() -> io::Result<PathBuf> { } } -pub struct Args { - iter: vec::IntoIter<OsString>, - _dont_send_or_sync_me: PhantomData<*mut ()>, -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option<OsString> { self.iter.next() } - fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { self.iter.len() } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() } -} - -/// Returns the command line arguments -/// -/// Returns a list of the command line arguments. -#[cfg(target_os = "macos")] -pub fn args() -> Args { - extern { - // These functions are in crt_externs.h. - fn _NSGetArgc() -> *mut c_int; - fn _NSGetArgv() -> *mut *mut *mut c_char; - } - - let vec = unsafe { - let (argc, argv) = (*_NSGetArgc() as isize, - *_NSGetArgv() as *const *const c_char); - (0.. argc as isize).map(|i| { - let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec(); - OsStringExt::from_vec(bytes) - }).collect::<Vec<_>>() - }; - Args { - iter: vec.into_iter(), - _dont_send_or_sync_me: PhantomData, - } -} - -// As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs -// and use underscores in their names - they're most probably -// are considered private and therefore should be avoided -// Here is another way to get arguments using Objective C -// runtime -// -// In general it looks like: -// res = Vec::new() -// let args = [[NSProcessInfo processInfo] arguments] -// for i in (0..[args count]) -// res.push([args objectAtIndex:i]) -// res -#[cfg(target_os = "ios")] -pub fn args() -> Args { - use mem; - - extern { - fn sel_registerName(name: *const libc::c_uchar) -> Sel; - fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; - fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; +#[cfg(target_os = "haiku")] +pub fn current_exe() -> io::Result<PathBuf> { + // Use Haiku's image info functions + #[repr(C)] + struct image_info { + id: i32, + type_: i32, + sequence: i32, + init_order: i32, + init_routine: *mut libc::c_void, // function pointer + term_routine: *mut libc::c_void, // function pointer + device: libc::dev_t, + node: libc::ino_t, + name: [libc::c_char; 1024], // MAXPATHLEN + text: *mut libc::c_void, + data: *mut libc::c_void, + text_size: i32, + data_size: i32, + api_version: i32, + abi: i32, } - #[link(name = "Foundation", kind = "framework")] - #[link(name = "objc")] - #[cfg(not(cargobuild))] - extern {} - - type Sel = *const libc::c_void; - type NsId = *const libc::c_void; - - let mut res = Vec::new(); - unsafe { - let process_info_sel = sel_registerName("processInfo\0".as_ptr()); - let arguments_sel = sel_registerName("arguments\0".as_ptr()); - let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); - let count_sel = sel_registerName("count\0".as_ptr()); - let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); - - let klass = objc_getClass("NSProcessInfo\0".as_ptr()); - let info = objc_msgSend(klass, process_info_sel); - let args = objc_msgSend(info, arguments_sel); - - let cnt: usize = mem::transmute(objc_msgSend(args, count_sel)); - for i in 0..cnt { - let tmp = objc_msgSend(args, object_at_sel, i); - let utf_c_str: *const libc::c_char = - mem::transmute(objc_msgSend(tmp, utf8_sel)); - let bytes = CStr::from_ptr(utf_c_str).to_bytes(); - res.push(OsString::from(str::from_utf8(bytes).unwrap())) + extern { + fn _get_next_image_info(team_id: i32, cookie: *mut i32, + info: *mut image_info, size: i32) -> i32; } - } - Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData } + let mut info: image_info = mem::zeroed(); + let mut cookie: i32 = 0; + // the executable can be found at team id 0 + let result = _get_next_image_info(0, &mut cookie, &mut info, + mem::size_of::<image_info>() as i32); + if result != 0 { + use io::ErrorKind; + Err(io::Error::new(ErrorKind::Other, "Error getting executable path")) + } else { + let name = CStr::from_ptr(info.name.as_ptr()).to_bytes(); + Ok(PathBuf::from(OsStr::from_bytes(name))) + } + } } -#[cfg(any(target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "nacl", - target_os = "emscripten"))] -pub fn args() -> Args { - use sys_common; - let bytes = sys_common::args::clone().unwrap_or(Vec::new()); - let v: Vec<OsString> = bytes.into_iter().map(|v| { - OsStringExt::from_vec(v) - }).collect(); - Args { iter: v.into_iter(), _dont_send_or_sync_me: PhantomData } +#[cfg(target_os = "fuchsia")] +pub fn current_exe() -> io::Result<PathBuf> { + use io::ErrorKind; + Err(io::Error::new(ErrorKind::Other, "Not yet implemented on fuchsia")) } pub struct Env { @@ -563,7 +493,7 @@ pub fn home_dir() -> Option<PathBuf> { buf: &mut Vec<c_char>) -> Option<()> { let mut result = ptr::null_mut(); match libc::getpwuid_r(me, passwd, buf.as_mut_ptr(), - buf.capacity() as libc::size_t, + buf.capacity(), &mut result) { 0 if !result.is_null() => Some(()), _ => None @@ -576,7 +506,7 @@ pub fn home_dir() -> Option<PathBuf> { // getpwuid_r semantics is different on Illumos/Solaris: // http://illumos.org/man/3c/getpwuid_r let result = libc::getpwuid_r(me, passwd, buf.as_mut_ptr(), - buf.capacity() as libc::size_t); + buf.capacity()); if result.is_null() { None } else { Some(()) } } diff --git a/src/libstd/sys/unix/path.rs b/src/libstd/sys/unix/path.rs new file mode 100644 index 00000000000..bf9af7a4353 --- /dev/null +++ b/src/libstd/sys/unix/path.rs @@ -0,0 +1,29 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use path::Prefix; +use ffi::OsStr; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' +} + +pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { + None +} + +pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 50014f51f6c..dafc11d9cc8 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -369,7 +369,7 @@ impl Command { } // NaCl has no signal support. - if cfg!(not(target_os = "nacl")) { + if cfg!(not(any(target_os = "nacl", target_os = "emscripten"))) { // Reset signal handling so the child process starts in a // standardized state. libstd ignores SIGPIPE, and signal-handling // libraries often set a mask. Child processes inherit ignored @@ -589,7 +589,7 @@ impl Process { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use super::*; diff --git a/src/libstd/sys/unix/rand.rs b/src/libstd/sys/unix/rand.rs index 6b50ca9bcdf..3aebb8c18ec 100644 --- a/src/libstd/sys/unix/rand.rs +++ b/src/libstd/sys/unix/rand.rs @@ -27,7 +27,8 @@ fn next_u64(mut fill_buf: &mut FnMut(&mut [u8])) -> u64 { #[cfg(all(unix, not(target_os = "ios"), not(target_os = "openbsd"), - not(target_os = "freebsd")))] + not(target_os = "freebsd"), + not(target_os = "fuchsia")))] mod imp { use self::OsRngInner::*; use super::{next_u32, next_u64}; @@ -97,8 +98,8 @@ mod imp { // full entropy pool let reader = File::open("/dev/urandom").expect("Unable to open /dev/urandom"); let mut reader_rng = ReaderRng::new(reader); - reader_rng.fill_bytes(& mut v[read..]); - read += v.len() as usize; + reader_rng.fill_bytes(&mut v[read..]); + read += v.len(); } else { panic!("unexpected getrandom error: {}", err); } @@ -281,7 +282,7 @@ mod imp { } fn fill_bytes(&mut self, v: &mut [u8]) { let ret = unsafe { - SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, + SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) }; if ret == -1 { @@ -339,3 +340,54 @@ mod imp { } } } + +#[cfg(target_os = "fuchsia")] +mod imp { + use super::{next_u32, next_u64}; + + use io; + use rand::Rng; + + #[link(name = "magenta")] + extern { + fn mx_cprng_draw(buffer: *mut u8, len: usize) -> isize; + } + + fn getrandom(buf: &mut [u8]) -> isize { + unsafe { mx_cprng_draw(buf.as_mut_ptr(), buf.len()) } + } + + pub struct OsRng { + // dummy field to ensure that this struct cannot be constructed outside + // of this module + _dummy: (), + } + + impl OsRng { + /// Create a new `OsRng`. + pub fn new() -> io::Result<OsRng> { + Ok(OsRng { _dummy: () }) + } + } + + impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + next_u32(&mut |v| self.fill_bytes(v)) + } + fn next_u64(&mut self) -> u64 { + next_u64(&mut |v| self.fill_bytes(v)) + } + fn fill_bytes(&mut self, v: &mut [u8]) { + let mut buf = v; + while !buf.is_empty() { + let ret = getrandom(buf); + if ret < 0 { + panic!("kernel mx_cprng_draw call failed! (returned {}, buf.len() {})", + ret, buf.len()); + } + let move_buf = buf; + buf = &mut move_buf[(ret as usize)..]; + } + } + } +} diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index 972bdbc3818..947ba2cc752 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -65,3 +65,5 @@ impl io::Write for Stderr { } fn flush(&mut self) -> io::Result<()> { Ok(()) } } + +pub const EBADF_ERR: i32 = ::libc::EBADF as i32; diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index 5db7086e427..1642baa34d6 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -29,6 +29,20 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} +// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc, +// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS. +#[cfg(not(target_os = "emscripten"))] +unsafe fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, + stack_size: libc::size_t) -> libc::c_int { + libc::pthread_attr_setstacksize(attr, stack_size) +} + +#[cfg(target_os = "emscripten")] +unsafe fn pthread_attr_setstacksize(_attr: *mut libc::pthread_attr_t, + _stack_size: libc::size_t) -> libc::c_int { + panic!() +} + impl Thread { pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) -> io::Result<Thread> { @@ -38,8 +52,8 @@ impl Thread { assert_eq!(libc::pthread_attr_init(&mut attr), 0); let stack_size = cmp::max(stack, min_stack_size(&attr)); - match libc::pthread_attr_setstacksize(&mut attr, - stack_size as libc::size_t) { + match pthread_attr_setstacksize(&mut attr, + stack_size) { 0 => {} n => { assert_eq!(n, libc::EINVAL); @@ -50,7 +64,6 @@ impl Thread { let page_size = os::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - let stack_size = stack_size as libc::size_t; assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0); } @@ -115,9 +128,16 @@ impl Thread { name.as_ptr() as *mut libc::c_void); } } - #[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten"))] + #[cfg(any(target_env = "newlib", + target_os = "solaris", + target_os = "haiku", + target_os = "emscripten"))] + pub fn set_name(_name: &CStr) { + // Newlib, Illumos, Haiku, and Emscripten have no way to set a thread name. + } + #[cfg(target_os = "fuchsia")] pub fn set_name(_name: &CStr) { - // Newlib, Illumos and Emscripten have no way to set a thread name. + // FIXME: determine whether Fuchsia has a way to set a thread name. } pub fn sleep(dur: Duration) { @@ -247,12 +267,8 @@ pub mod guard { // Rellocate the last page of the stack. // This ensures SIGBUS will be raised on // stack overflow. - let result = mmap(stackaddr, - psize as libc::size_t, - PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, - 0); + let result = mmap(stackaddr, psize, PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); if result != stackaddr || result == MAP_FAILED { panic!("failed to allocate a guard page"); @@ -276,8 +292,8 @@ pub mod guard { #[cfg(target_os = "macos")] pub unsafe fn current() -> Option<usize> { - Some((libc::pthread_get_stackaddr_np(libc::pthread_self()) as libc::size_t - - libc::pthread_get_stacksize_np(libc::pthread_self())) as usize) + Some((libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize - + libc::pthread_get_stacksize_np(libc::pthread_self()))) } #[cfg(any(target_os = "openbsd", target_os = "bitrig"))] @@ -289,10 +305,10 @@ pub mod guard { let extra = if cfg!(target_os = "bitrig") {3} else {1} * os::page_size(); Some(if libc::pthread_main_np() == 1 { // main thread - current_stack.ss_sp as usize - current_stack.ss_size as usize + extra + current_stack.ss_sp as usize - current_stack.ss_size + extra } else { // new thread - current_stack.ss_sp as usize - current_stack.ss_size as usize + current_stack.ss_sp as usize - current_stack.ss_size }) } @@ -318,11 +334,11 @@ pub mod guard { &mut size), 0); ret = if cfg!(target_os = "freebsd") { - Some(stackaddr as usize - guardsize as usize) + Some(stackaddr as usize - guardsize) } else if cfg!(target_os = "netbsd") { Some(stackaddr as usize) } else { - Some(stackaddr as usize + guardsize as usize) + Some(stackaddr as usize + guardsize) }; } assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); @@ -341,8 +357,8 @@ fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t); match __pthread_get_minstack.get() { - None => libc::PTHREAD_STACK_MIN as usize, - Some(f) => unsafe { f(attr) as usize }, + None => libc::PTHREAD_STACK_MIN, + Some(f) => unsafe { f(attr) }, } } @@ -351,7 +367,7 @@ fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { #[cfg(all(not(target_os = "linux"), not(target_os = "netbsd")))] fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - libc::PTHREAD_STACK_MIN as usize + libc::PTHREAD_STACK_MIN } #[cfg(target_os = "netbsd")] diff --git a/src/libstd/sys/windows/args.rs b/src/libstd/sys/windows/args.rs new file mode 100644 index 00000000000..aa61f9adb82 --- /dev/null +++ b/src/libstd/sys/windows/args.rs @@ -0,0 +1,76 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] // runtime init functions not used during testing + +use os::windows::prelude::*; +use sys::c; +use slice; +use ops::Range; +use ffi::OsString; +use libc::{c_int, c_void}; + +pub unsafe fn init(_argc: isize, _argv: *const *const u8) { } + +pub unsafe fn cleanup() { } + +pub fn args() -> Args { + unsafe { + let mut nArgs: c_int = 0; + let lpCmdLine = c::GetCommandLineW(); + let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs); + + // szArcList can be NULL if CommandLinToArgvW failed, + // but in that case nArgs is 0 so we won't actually + // try to read a null pointer + Args { cur: szArgList, range: 0..(nArgs as isize) } + } +} + +pub struct Args { + range: Range<isize>, + cur: *mut *mut u16, +} + +unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString { + let mut len = 0; + while *ptr.offset(len) != 0 { len += 1; } + + // Push it onto the list. + let ptr = ptr as *const u16; + let buf = slice::from_raw_parts(ptr, len as usize); + OsStringExt::from_wide(buf) +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option<OsString> { + self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) + } + fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { + self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { self.range.len() } +} + +impl Drop for Args { + fn drop(&mut self) { + // self.cur can be null if CommandLineToArgvW previously failed, + // but LocalFree ignores NULL pointers + unsafe { c::LocalFree(self.cur as *mut c_void); } + } +} diff --git a/src/libstd/sys/windows/env.rs b/src/libstd/sys/windows/env.rs new file mode 100644 index 00000000000..e6d74895774 --- /dev/null +++ b/src/libstd/sys/windows/env.rs @@ -0,0 +1,19 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod os { + pub const FAMILY: &'static str = "windows"; + pub const OS: &'static str = "windows"; + pub const DLL_PREFIX: &'static str = ""; + pub const DLL_SUFFIX: &'static str = ".dll"; + pub const DLL_EXTENSION: &'static str = "dll"; + pub const EXE_SUFFIX: &'static str = ".exe"; + pub const EXE_EXTENSION: &'static str = "exe"; +} diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index 4388a0bdff2..1e2b8bf38fa 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -12,12 +12,61 @@ #![stable(feature = "rust1", since = "1.0.0")] -use fs::{OpenOptions, Metadata}; +use fs::{self, OpenOptions, Metadata}; use io; use path::Path; use sys; use sys_common::{AsInnerMut, AsInner}; +/// Windows-specific extensions to `File` +#[unstable(feature = "file_offset", issue = "35918")] +pub trait FileExt { + /// Seeks to a given position and reads a number of bytes. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the read. + /// + /// Reading beyond the end of the file will always return with a length of + /// 0. + /// + /// Note that similar to `File::read`, it is not an error to return with a + /// short read. When returning from such a short read, the file pointer is + /// still updated. + #[unstable(feature = "file_offset", issue = "35918")] + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; + + /// Seeks to a given position and writes a number of bytes. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the write. + /// + /// When writing beyond the end of the file, the file is appropiately + /// extended and the intermediate bytes are left uninitialized. + /// + /// Note that similar to `File::write`, it is not an error to return a + /// short write. When returning from such a short write, the file pointer + /// is still updated. + #[unstable(feature = "file_offset", issue = "35918")] + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>; +} + +#[unstable(feature = "file_offset", issue = "35918")] +impl FileExt for fs::File { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.as_inner().read_at(buf, offset) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + self.as_inner().write_at(buf, offset) + } +} + /// Windows-specific extensions to `OpenOptions` #[stable(feature = "open_options_ext", since = "1.10.0")] pub trait OpenOptionsExt { diff --git a/src/libstd/sys/windows/ext/mod.rs b/src/libstd/sys/windows/ext/mod.rs index c3578fdfdb1..932bb5e9564 100644 --- a/src/libstd/sys/windows/ext/mod.rs +++ b/src/libstd/sys/windows/ext/mod.rs @@ -36,4 +36,6 @@ pub mod prelude { pub use super::ffi::{OsStrExt, OsStringExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::fs::{OpenOptionsExt, MetadataExt}; + #[doc(no_inline)] #[unstable(feature = "file_offset", issue = "35918")] + pub use super::fs::FileExt; } diff --git a/src/libstd/sys/windows/ext/process.rs b/src/libstd/sys/windows/ext/process.rs index 98166bf8cda..bce32959a23 100644 --- a/src/libstd/sys/windows/ext/process.rs +++ b/src/libstd/sys/windows/ext/process.rs @@ -33,7 +33,7 @@ impl AsRawHandle for process::Child { } } -#[stable(feature = "process_extensions", since = "1.2.0")] +#[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::Child { fn into_raw_handle(self) -> RawHandle { self.into_inner().into_handle().into_raw() as *mut _ @@ -61,21 +61,21 @@ impl AsRawHandle for process::ChildStderr { } } -#[stable(feature = "process_extensions", since = "1.2.0")] +#[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::ChildStdin { fn into_raw_handle(self) -> RawHandle { self.into_inner().into_handle().into_raw() as *mut _ } } -#[stable(feature = "process_extensions", since = "1.2.0")] +#[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::ChildStdout { fn into_raw_handle(self) -> RawHandle { self.into_inner().into_handle().into_raw() as *mut _ } } -#[stable(feature = "process_extensions", since = "1.2.0")] +#[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::ChildStderr { fn into_raw_handle(self) -> RawHandle { self.into_inner().into_handle().into_raw() as *mut _ @@ -91,7 +91,7 @@ pub trait ExitStatusExt { fn from_raw(raw: u32) -> Self; } -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "exit_status_from", since = "1.12.0")] impl ExitStatusExt for process::ExitStatus { fn from_raw(raw: u32) -> Self { process::ExitStatus::from_inner(From::from(raw)) diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 90a16853d56..98fd15f863b 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -81,6 +81,14 @@ pub struct FilePermissions { attrs: c::DWORD } pub struct DirBuilder; +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("C:\")' + fmt::Debug::fmt(&*self.root, f) + } +} + impl Iterator for ReadDir { type Item = io::Result<DirEntry>; fn next(&mut self) -> Option<io::Result<DirEntry>> { @@ -311,6 +319,10 @@ impl File { self.handle.read(buf) } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.handle.read_at(buf, offset) + } + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { self.handle.read_to_end(buf) } @@ -319,6 +331,10 @@ impl File { self.handle.write(buf) } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + self.handle.write_at(buf, offset) + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index 97e746ee345..10b86ba44bc 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -104,6 +104,23 @@ impl RawHandle { } } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + let mut read = 0; + let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD; + let res = unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + overlapped.Offset = offset as u32; + overlapped.OffsetHigh = (offset >> 32) as u32; + cvt(c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, + len, &mut read, &mut overlapped)) + }; + match res { + Ok(_) => Ok(read as usize), + Err(ref e) if e.raw_os_error() == Some(c::ERROR_HANDLE_EOF as i32) => Ok(0), + Err(e) => Err(e), + } + } + pub unsafe fn read_overlapped(&self, buf: &mut [u8], overlapped: *mut c::OVERLAPPED) @@ -174,6 +191,19 @@ impl RawHandle { Ok(amt as usize) } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + let mut written = 0; + let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD; + unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + overlapped.Offset = offset as u32; + overlapped.OffsetHigh = (offset >> 32) as u32; + cvt(c::WriteFile(self.0, buf.as_ptr() as c::LPVOID, + len, &mut written, &mut overlapped))?; + } + Ok(written as usize) + } + pub fn duplicate(&self, access: c::DWORD, inherit: bool, options: c::DWORD) -> io::Result<Handle> { let mut ret = 0 as c::HANDLE; diff --git a/src/libstd/sys/windows/memchr.rs b/src/libstd/sys/windows/memchr.rs new file mode 100644 index 00000000000..5a5386acaa5 --- /dev/null +++ b/src/libstd/sys/windows/memchr.rs @@ -0,0 +1,15 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Original implementation taken from rust-memchr +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +// Fallback memchr is fastest on windows +pub use sys_common::memchr::fallback::{memchr, memrchr}; diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 9741a704e8f..9cd6e6ca176 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -18,17 +18,21 @@ use time::Duration; #[macro_use] pub mod compat; +pub mod args; pub mod backtrace; pub mod c; pub mod condvar; pub mod dynamic_lib; +pub mod env; pub mod ext; pub mod fs; pub mod handle; +pub mod memchr; pub mod mutex; pub mod net; pub mod os; pub mod os_str; +pub mod path; pub mod pipe; pub mod process; pub mod rand; diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index 260fc3c4db6..7e28dd1e259 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -18,8 +18,6 @@ use error::Error as StdError; use ffi::{OsString, OsStr}; use fmt; use io; -use libc::{c_int, c_void}; -use ops::Range; use os::windows::ffi::EncodeWide; use path::{self, PathBuf}; use ptr; @@ -272,60 +270,6 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { }).map(|_| ()) } -pub struct Args { - range: Range<isize>, - cur: *mut *mut u16, -} - -unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString { - let mut len = 0; - while *ptr.offset(len) != 0 { len += 1; } - - // Push it onto the list. - let ptr = ptr as *const u16; - let buf = slice::from_raw_parts(ptr, len as usize); - OsStringExt::from_wide(buf) -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option<OsString> { - self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) - } - fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option<OsString> { - self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { self.range.len() } -} - -impl Drop for Args { - fn drop(&mut self) { - // self.cur can be null if CommandLineToArgvW previously failed, - // but LocalFree ignores NULL pointers - unsafe { c::LocalFree(self.cur as *mut c_void); } - } -} - -pub fn args() -> Args { - unsafe { - let mut nArgs: c_int = 0; - let lpCmdLine = c::GetCommandLineW(); - let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs); - - // szArcList can be NULL if CommandLinToArgvW failed, - // but in that case nArgs is 0 so we won't actually - // try to read a null pointer - Args { cur: szArgList, range: 0..(nArgs as isize) } - } -} - pub fn temp_dir() -> PathBuf { super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPathW(sz, buf) diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs new file mode 100644 index 00000000000..2b47808451b --- /dev/null +++ b/src/libstd/sys/windows/path.rs @@ -0,0 +1,108 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ascii::*; + +use path::Prefix; +use ffi::OsStr; +use mem; + +fn os_str_as_u8_slice(s: &OsStr) -> &[u8] { + unsafe { mem::transmute(s) } +} +unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { + mem::transmute(s) +} + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' || b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> { + use path::Prefix::*; + unsafe { + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + let mut path = os_str_as_u8_slice(path); + + if path.starts_with(br"\\") { + // \\ + path = &path[2..]; + if path.starts_with(br"?\") { + // \\?\ + path = &path[2..]; + if path.starts_with(br"UNC\") { + // \\?\UNC\server\share + path = &path[4..]; + let (server, share) = match parse_two_comps(path, is_verbatim_sep) { + Some((server, share)) => + (u8_slice_as_os_str(server), u8_slice_as_os_str(share)), + None => (u8_slice_as_os_str(path), u8_slice_as_os_str(&[])), + }; + return Some(VerbatimUNC(server, share)); + } else { + // \\?\path + let idx = path.iter().position(|&b| b == b'\\'); + if idx == Some(2) && path[1] == b':' { + let c = path[0]; + if c.is_ascii() && (c as char).is_alphabetic() { + // \\?\C:\ path + return Some(VerbatimDisk(c.to_ascii_uppercase())); + } + } + let slice = &path[..idx.unwrap_or(path.len())]; + return Some(Verbatim(u8_slice_as_os_str(slice))); + } + } else if path.starts_with(b".\\") { + // \\.\path + path = &path[2..]; + let pos = path.iter().position(|&b| b == b'\\'); + let slice = &path[..pos.unwrap_or(path.len())]; + return Some(DeviceNS(u8_slice_as_os_str(slice))); + } + match parse_two_comps(path, is_sep_byte) { + Some((server, share)) if !server.is_empty() && !share.is_empty() => { + // \\server\share + return Some(UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share))); + } + _ => (), + } + } else if path.get(1) == Some(& b':') { + // C: + let c = path[0]; + if c.is_ascii() && (c as char).is_alphabetic() { + return Some(Disk(c.to_ascii_uppercase())); + } + } + return None; + } + + fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> { + let first = match path.iter().position(|x| f(*x)) { + None => return None, + Some(x) => &path[..x], + }; + path = &path[(first.len() + 1)..]; + let idx = path.iter().position(|x| f(*x)); + let second = &path[..idx.unwrap_or(path.len())]; + Some((first, second)) + } +} + +pub const MAIN_SEP_STR: &'static str = "\\"; +pub const MAIN_SEP: char = '\\'; diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index 01249f05f62..5f097d2631d 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -205,3 +205,5 @@ impl Output { fn invalid_encoding() -> io::Error { io::Error::new(io::ErrorKind::InvalidData, "text was not valid unicode") } + +pub const EBADF_ERR: i32 = ::sys::c::ERROR_INVALID_HANDLE as i32; diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index c44dee49f14..54d3f793045 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -358,36 +358,8 @@ pub mod elf { } } - // Since what appears to be glibc 2.18 this symbol has been shipped which - // GCC and clang both use to invoke destructors in thread_local globals, so - // let's do the same! - // - // Note, however, that we run on lots older linuxes, as well as cross - // compiling from a newer linux to an older linux, so we also have a - // fallback implementation to use as well. - // - // Due to rust-lang/rust#18804, make sure this is not generic! - #[cfg(target_os = "linux")] - unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { - use mem; - use libc; - use sys_common::thread_local as os; - - extern { - #[linkage = "extern_weak"] - static __dso_handle: *mut u8; - #[linkage = "extern_weak"] - static __cxa_thread_atexit_impl: *const libc::c_void; - } - if !__cxa_thread_atexit_impl.is_null() { - type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8), - arg: *mut u8, - dso_handle: *mut u8) -> libc::c_int; - mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl) - (dtor, t, &__dso_handle as *const _ as *mut _); - return - } - + #[cfg(any(target_os = "linux", target_os = "fuchsia"))] + unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { // The fallback implementation uses a vanilla OS-based TLS key to track // the list of destructors that need to be run for this thread. The key // then has its own destructor which runs all the other destructors. @@ -397,6 +369,8 @@ pub mod elf { // *should* be the case that this loop always terminates because we // provide the guarantee that a TLS key cannot be set after it is // flagged for destruction. + use sys_common::thread_local as os; + static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors)); type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; if DTORS.get().is_null() { @@ -418,6 +392,37 @@ pub mod elf { } } + // Since what appears to be glibc 2.18 this symbol has been shipped which + // GCC and clang both use to invoke destructors in thread_local globals, so + // let's do the same! + // + // Note, however, that we run on lots older linuxes, as well as cross + // compiling from a newer linux to an older linux, so we also have a + // fallback implementation to use as well. + // + // Due to rust-lang/rust#18804, make sure this is not generic! + #[cfg(target_os = "linux")] + unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { + use mem; + use libc; + + extern { + #[linkage = "extern_weak"] + static __dso_handle: *mut u8; + #[linkage = "extern_weak"] + static __cxa_thread_atexit_impl: *const libc::c_void; + } + if !__cxa_thread_atexit_impl.is_null() { + type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8), + arg: *mut u8, + dso_handle: *mut u8) -> libc::c_int; + mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl) + (dtor, t, &__dso_handle as *const _ as *mut _); + return + } + register_dtor_fallback(t, dtor); + } + // OSX's analog of the above linux function is this _tlv_atexit function. // The disassembly of thread_local globals in C++ (at least produced by // clang) will have this show up in the output. @@ -430,6 +435,13 @@ pub mod elf { _tlv_atexit(dtor, t); } + // Just use the thread_local fallback implementation, at least until there's + // a more direct implementation. + #[cfg(target_os = "fuchsia")] + unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { + register_dtor_fallback(t, dtor); + } + pub unsafe extern fn destroy_value<T>(ptr: *mut u8) { let ptr = ptr as *mut Key<T>; // Right before we run the user destructor be sure to flag the @@ -524,7 +536,7 @@ pub mod os { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use sync::mpsc::{channel, Sender}; use cell::{Cell, UnsafeCell}; diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index d8e021bb04f..150482e4af4 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -151,7 +151,7 @@ //! //! [`Cell`]: ../cell/struct.Cell.html //! [`RefCell`]: ../cell/struct.RefCell.html -//! [`thread_local!`]: ../macro.thread_local!.html +//! [`thread_local!`]: ../macro.thread_local.html //! [`with`]: struct.LocalKey.html#method.with #![stable(feature = "rust1", since = "1.0.0")] @@ -166,6 +166,7 @@ use panicking; use str; use sync::{Mutex, Condvar, Arc}; use sys::thread as imp; +use sys_common::mutex; use sys_common::thread_info; use sys_common::util; use sys_common::{AsInner, IntoInner}; @@ -525,12 +526,52 @@ pub fn park_timeout(dur: Duration) { } //////////////////////////////////////////////////////////////////////////////// +// ThreadId +//////////////////////////////////////////////////////////////////////////////// + +/// A unique identifier for a running thread. +/// +/// A `ThreadId` is an opaque object that has a unique value for each thread +/// that creates one. `ThreadId`s do not correspond to a thread's system- +/// designated identifier. +#[unstable(feature = "thread_id", issue = "21507")] +#[derive(Eq, PartialEq, Copy, Clone)] +pub struct ThreadId(u64); + +impl ThreadId { + // Generate a new unique thread ID. + fn new() -> ThreadId { + static GUARD: mutex::Mutex = mutex::Mutex::new(); + static mut COUNTER: u64 = 0; + + unsafe { + GUARD.lock(); + + // If we somehow use up all our bits, panic so that we're not + // covering up subtle bugs of IDs being reused. + if COUNTER == ::u64::MAX { + GUARD.unlock(); + panic!("failed to generate unique thread ID: bitspace exhausted"); + } + + let id = COUNTER; + COUNTER += 1; + + GUARD.unlock(); + + ThreadId(id) + } + } +} + +//////////////////////////////////////////////////////////////////////////////// // Thread //////////////////////////////////////////////////////////////////////////////// /// The internal representation of a `Thread` handle struct Inner { name: Option<CString>, // Guaranteed to be UTF-8 + id: ThreadId, lock: Mutex<bool>, // true when there is a buffered unpark cvar: Condvar, } @@ -551,6 +592,7 @@ impl Thread { Thread { inner: Arc::new(Inner { name: cname, + id: ThreadId::new(), lock: Mutex::new(false), cvar: Condvar::new(), }) @@ -569,6 +611,12 @@ impl Thread { } } + /// Gets the thread's unique identifier. + #[unstable(feature = "thread_id", issue = "21507")] + pub fn id(&self) -> ThreadId { + self.inner.id + } + /// Gets the thread's name. /// /// # Examples @@ -741,7 +789,7 @@ fn _assert_sync_and_send() { // Tests //////////////////////////////////////////////////////////////////////////////// -#[cfg(test)] +#[cfg(all(test, not(target_os = "emscripten")))] mod tests { use any::Any; use sync::mpsc::{channel, Sender}; @@ -977,6 +1025,17 @@ mod tests { thread::sleep(Duration::from_millis(2)); } + #[test] + fn test_thread_id_equal() { + assert!(thread::current().id() == thread::current().id()); + } + + #[test] + fn test_thread_id_not_equal() { + let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap(); + assert!(thread::current().id() != spawned_id); + } + // NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due // to the test harness apparently interfering with stderr configuration. } diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 246c57ab238..10a0c567e14 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -83,7 +83,7 @@ impl Duration { /// Returns the number of whole seconds represented by this duration. /// - /// The extra precision represented by this duration is ignored (e.g. extra + /// The extra precision represented by this duration is ignored (i.e. extra /// nanoseconds are not represented in the returned value). #[stable(feature = "duration", since = "1.3.0")] #[inline] @@ -93,7 +93,7 @@ impl Duration { /// /// This method does **not** return the length of the duration when /// represented by nanoseconds. The returned number always represents a - /// fractional portion of a second (e.g. it is less than one billion). + /// fractional portion of a second (i.e. it is less than one billion). #[stable(feature = "duration", since = "1.3.0")] #[inline] pub fn subsec_nanos(&self) -> u32 { self.nanos } diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs index 154f603c84f..6854f1e14fa 100644 --- a/src/libstd/time/mod.rs +++ b/src/libstd/time/mod.rs @@ -118,7 +118,7 @@ pub struct Instant(time::Instant); pub struct SystemTime(time::SystemTime); /// An error returned from the `duration_since` method on `SystemTime`, -/// used to learn about why how far in the opposite direction a timestamp lies. +/// used to learn how far in the opposite direction a system time lies. #[derive(Clone, Debug)] #[stable(feature = "time2", since = "1.8.0")] pub struct SystemTimeError(Duration); |
