diff options
| author | Sayan Nandan <17377258+sntdevco@users.noreply.github.com> | 2019-08-09 13:01:05 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-09 13:01:05 +0530 |
| commit | fb3a01354ffecc41d7a189e4dd225d706387a522 (patch) | |
| tree | 41492dfe93f1dccba847dadb56ac6aa079edaaa9 /src/libstd | |
| parent | 33445aea509cadcd715009c79795d289268daa7c (diff) | |
| parent | 5aa3d9a7b5d3a46a7f158e8881146331a6bc9243 (diff) | |
| download | rust-fb3a01354ffecc41d7a189e4dd225d706387a522.tar.gz rust-fb3a01354ffecc41d7a189e4dd225d706387a522.zip | |
Merge pull request #1 from rust-lang/master
Merge recent changes into master
Diffstat (limited to 'src/libstd')
311 files changed, 15709 insertions, 14683 deletions
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 1a6b58f35b3..5334c4dfc68 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -3,7 +3,7 @@ authors = ["The Rust Project Developers"] name = "std" version = "0.0.0" build = "build.rs" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "The Rust Standard Library" edition = "2018" @@ -15,15 +15,27 @@ crate-type = ["dylib", "rlib"] [dependencies] alloc = { path = "../liballoc" } +cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../libpanic_unwind", optional = true } panic_abort = { path = "../libpanic_abort" } core = { path = "../libcore" } -libc = { version = "0.2.44", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.1" } +libc = { version = "0.2.51", default-features = false, features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.16" } profiler_builtins = { path = "../libprofiler_builtins", optional = true } unwind = { path = "../libunwind" } -rustc-demangle = { version = "0.1.10", features = ['rustc-dep-of-std'] } -backtrace-sys = { version = "0.1.24", features = ["rustc-dep-of-std"], optional = true } +hashbrown = { version = "0.4.0", features = ['rustc-dep-of-std'] } + +[dependencies.backtrace] +version = "0.3.34" +default-features = false # don't use coresymbolication on OSX +features = [ + "rustc-dep-of-std", # enable build support for integrating into libstd + "dbghelp", # backtrace/symbolize on MSVC + "libbacktrace", # symbolize on most platforms + "libunwind", # backtrace on most platforms + "dladdr", # symbolize on platforms w/o libbacktrace +] +optional = true [dev-dependencies] rand = "0.6.1" @@ -48,12 +60,12 @@ fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } cc = "1.0" [features] -default = ["compiler_builtins_c", "std_detect_file_io", "std_detect_dlsym_getauxval"] +default = ["std_detect_file_io", "std_detect_dlsym_getauxval"] -backtrace = ["backtrace-sys"] panic-unwind = ["panic_unwind"] profiler = ["profiler_builtins"] -compiler_builtins_c = ["compiler_builtins/c"] +compiler-builtins-c = ["alloc/compiler-builtins-c"] +llvm-libunwind = ["unwind/llvm-libunwind"] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = ["core/panic_immediate_abort"] @@ -63,12 +75,13 @@ panic_immediate_abort = ["core/panic_immediate_abort"] # requires rebuilding the standard library to use it. wasm_syscall = [] -# An off-by-default features to enable libstd to assume that wasm-bindgen is in -# the environment for hooking up some thread-related information like the -# current thread id and accessing/getting the current thread's TCB -wasm-bindgen-threads = [] - -# Enable std_detect default features for stdsimd: -# https://github.com/rust-lang-nursery/stdsimd/blob/master/crates/std_detect/Cargo.toml +# Enable std_detect default features for stdarch/crates/std_detect: +# https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml std_detect_file_io = [] std_detect_dlsym_getauxval = [] + +[package.metadata.fortanix-sgx] +# Maximum possible number of threads when testing +threads = 125 +# Maximum heap size +heap_size = 0x8000000 diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index a13da2901df..ff52974775b 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -130,7 +130,7 @@ pub use alloc_crate::alloc::*; /// program opts in to using jemalloc as the global allocator, `System` will /// still allocate memory using `malloc` and `HeapAlloc`. #[stable(feature = "alloc_system_type", since = "1.28.0")] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone)] pub struct System; // The Alloc impl just forwards to the GlobalAlloc impl, which is in `std::sys::*::alloc`. @@ -173,6 +173,9 @@ static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// about the allocation that failed. /// /// The allocation error hook is a global resource. +/// +/// [`set_alloc_error_hook`]: fn.set_alloc_error_hook.html +/// [`take_alloc_error_hook`]: fn.take_alloc_error_hook.html #[unstable(feature = "alloc_error_hook", issue = "51245")] pub fn set_alloc_error_hook(hook: fn(Layout)) { HOOK.store(hook as *mut (), Ordering::SeqCst); @@ -183,6 +186,8 @@ pub fn set_alloc_error_hook(hook: fn(Layout)) { /// *See also the function [`set_alloc_error_hook`].* /// /// If no custom hook is registered, the default hook will be returned. +/// +/// [`set_alloc_error_hook`]: fn.set_alloc_error_hook.html #[unstable(feature = "alloc_error_hook", issue = "51245")] pub fn take_alloc_error_hook() -> fn(Layout) { let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 726c2732153..8db7bc12cd3 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -1,5 +1,3 @@ -#![deny(warnings)] - use std::env; fn main() { @@ -20,8 +18,7 @@ fn main() { } else if target.contains("netbsd") { println!("cargo:rustc-link-lib=pthread"); println!("cargo:rustc-link-lib=rt"); - } else if target.contains("dragonfly") || target.contains("bitrig") || - target.contains("openbsd") { + } else if target.contains("dragonfly") || target.contains("openbsd") { println!("cargo:rustc-link-lib=pthread"); } else if target.contains("solaris") { println!("cargo:rustc-link-lib=socket"); @@ -40,6 +37,10 @@ fn main() { println!("cargo:rustc-link-lib=framework=Security"); println!("cargo:rustc-link-lib=framework=Foundation"); println!("cargo:rustc-link-lib=resolv"); + } else if target.contains("uwp") { + println!("cargo:rustc-link-lib=ws2_32"); + // For BCryptGenRandom + println!("cargo:rustc-link-lib=bcrypt"); } else if target.contains("windows") { println!("cargo:rustc-link-lib=advapi32"); println!("cargo:rustc-link-lib=ws2_32"); diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 928de29b297..1e28ee8da26 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1,222 +1,20 @@ +// ignore-tidy-filelength + use self::Entry::*; -use self::VacantEntryState::*; -use crate::intrinsics::unlikely; -use crate::collections::CollectionAllocErr; -use crate::cell::Cell; +use hashbrown::hash_map as base; + use crate::borrow::Borrow; -use crate::cmp::max; +use crate::cell::Cell; +use crate::collections::CollectionAllocErr; use crate::fmt::{self, Debug}; #[allow(deprecated)] -use crate::hash::{Hash, Hasher, BuildHasher, SipHasher13}; +use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13}; use crate::iter::{FromIterator, FusedIterator}; -use crate::mem::{self, replace}; -use crate::ops::{Deref, DerefMut, Index}; +use crate::ops::Index; use crate::sys; -use super::table::{self, Bucket, EmptyBucket, Fallibility, FullBucket, FullBucketMut, RawTable, - SafeHash}; -use super::table::BucketState::{Empty, Full}; -use super::table::Fallibility::{Fallible, Infallible}; - -const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two - -/// The default behavior of HashMap implements a maximum load factor of 90.9%. -#[derive(Clone)] -struct DefaultResizePolicy; - -impl DefaultResizePolicy { - #[inline] - fn new() -> 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 try_raw_capacity(&self, len: usize) -> Result<usize, CollectionAllocErr> { - if len == 0 { - Ok(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.checked_mul(11) - .map(|l| l / 10) - .and_then(|l| l.checked_next_power_of_two()) - .ok_or(CollectionAllocErr::CapacityOverflow)?; - - raw_cap = max(MIN_NONZERO_RAW_CAPACITY, raw_cap); - Ok(raw_cap) - } - } - - #[inline] - fn raw_capacity(&self, len: usize) -> usize { - self.try_raw_capacity(len).expect("raw_capacity overflow") - } - - /// The capacity of the given raw capacity. - #[inline] - 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: (raw_cap * den + den - 1) / num - (raw_cap * 10 + 10 - 1) / 11 - } -} - -// The main performance trick in this hashmap is called Robin Hood Hashing. -// It gains its excellent performance from one essential operation: -// -// If an insertion collides with an existing element, and that element's -// "probe distance" (how far away the element is from its ideal location) -// is higher than how far we've already probed, swap the elements. -// -// This massively lowers variance in probe distance, and allows us to get very -// high load factors with good performance. The 90% load factor I use is rather -// conservative. -// -// > Why a load factor of approximately 90%? -// -// In general, all the distances to initial buckets will converge on the mean. -// At a load factor of α, the odds of finding the target bucket after k -// probes is approximately 1-α^k. If we set this equal to 50% (since we converge -// on the mean) and set k=8 (64-byte cache line / 8-byte hash), α=0.92. I round -// this down to make the math easier on the CPU and avoid its FPU. -// Since on average we start the probing in the middle of a cache line, this -// strategy pulls in two cache lines of hashes on every lookup. I think that's -// pretty good, but if you want to trade off some space, it could go down to one -// cache line on average with an α of 0.84. -// -// > Wait, what? Where did you get 1-α^k from? -// -// On the first probe, your odds of a collision with an existing element is α. -// The odds of doing this twice in a row is approximately α^2. For three times, -// α^3, etc. Therefore, the odds of colliding k times is α^k. The odds of NOT -// colliding after k tries is 1-α^k. -// -// The paper from 1986 cited below mentions an implementation which keeps track -// of the distance-to-initial-bucket histogram. This approach is not suitable -// for modern architectures because it requires maintaining an internal data -// structure. This allows very good first guesses, but we are most concerned -// with guessing entire cache lines, not individual indexes. Furthermore, array -// accesses are no longer linear and in one direction, as we have now. There -// is also memory and cache pressure that this would entail that would be very -// difficult to properly see in a microbenchmark. -// -// ## Future Improvements (FIXME!) -// -// Allow the load factor to be changed dynamically and/or at initialization. -// -// Also, would it be possible for us to reuse storage when growing the -// underlying table? This is exactly the use case for 'realloc', and may -// be worth exploring. -// -// ## Future Optimizations (FIXME!) -// -// Another possible design choice that I made without any real reason is -// parameterizing the raw table over keys and values. Technically, all we need -// is the size and alignment of keys and values, and the code should be just as -// efficient (well, we might need one for power-of-two size and one for not...). -// This has the potential to reduce code bloat in rust executables, without -// really losing anything except 4 words (key size, key alignment, val size, -// val alignment) which can be passed in to every call of a `RawTable` function. -// This would definitely be an avenue worth exploring if people start complaining -// about the size of rust executables. -// -// Annotate exceedingly likely branches in `table::make_hash` -// and `search_hashed` to reduce instruction cache pressure -// and mispredictions once it becomes possible (blocked on issue #11092). -// -// Shrinking the table could simply reallocate in place after moving buckets -// to the first half. -// -// The growth algorithm (fragment of the Proof of Correctness) -// -------------------- -// -// The growth algorithm is basically a fast path of the naive reinsertion- -// during-resize algorithm. Other paths should never be taken. -// -// Consider growing a robin hood hashtable of capacity n. Normally, we do this -// by allocating a new table of capacity `2n`, and then individually reinsert -// each element in the old table into the new one. This guarantees that the -// new table is a valid robin hood hashtable with all the desired statistical -// properties. Remark that the order we reinsert the elements in should not -// matter. For simplicity and efficiency, we will consider only linear -// reinsertions, which consist of reinserting all elements in the old table -// into the new one by increasing order of index. However we will not be -// starting our reinsertions from index 0 in general. If we start from index -// i, for the purpose of reinsertion we will consider all elements with real -// index j < i to have virtual index n + j. -// -// Our hash generation scheme consists of generating a 64-bit hash and -// truncating the most significant bits. When moving to the new table, we -// simply introduce a new bit to the front of the hash. Therefore, if an -// element has ideal index i in the old table, it can have one of two ideal -// locations in the new table. If the new bit is 0, then the new ideal index -// is i. If the new bit is 1, then the new ideal index is n + i. Intuitively, -// we are producing two independent tables of size n, and for each element we -// independently choose which table to insert it into with equal probability. -// However, rather than wrapping around themselves on overflowing their -// indexes, the first table overflows into the second, and the second into the -// first. Visually, our new table will look something like: -// -// [yy_xxx_xxxx_xxx|xx_yyy_yyyy_yyy] -// -// Where x's are elements inserted into the first table, y's are elements -// inserted into the second, and _'s are empty sections. We now define a few -// key concepts that we will use later. Note that this is a very abstract -// perspective of the table. A real resized table would be at least half -// empty. -// -// Theorem: A linear robin hood reinsertion from the first ideal element -// produces identical results to a linear naive reinsertion from the same -// element. -// -// FIXME(Gankro, pczarn): review the proof and put it all in a separate README.md -// -// Adaptive early resizing -// ---------------------- -// To protect against degenerate performance scenarios (including DOS attacks), -// the implementation includes an adaptive behavior that can resize the map -// early (before its capacity is exceeded) when suspiciously long probe sequences -// are encountered. -// -// With this algorithm in place it would be possible to turn a CPU attack into -// a memory attack due to the aggressive resizing. To prevent that the -// adaptive behavior only triggers when the map is at least half full. -// This reduces the effectiveness of the algorithm but also makes it completely safe. -// -// The previous safety measure also prevents degenerate interactions with -// really bad quality hash algorithms that can make normal inputs look like a -// DOS attack. -// -const DISPLACEMENT_THRESHOLD: usize = 128; -// -// The threshold of 128 is chosen to minimize the chance of exceeding it. -// In particular, we want that chance to be less than 10^-8 with a load of 90%. -// For displacement, the smallest constant that fits our needs is 90, -// so we round that up to 128. -// -// At a load factor of α, the odds of finding the target bucket after exactly n -// unsuccessful probes[1] are -// -// Pr_α{displacement = n} = -// (1 - α) / α * ∑_{k≥1} e^(-kα) * (kα)^(k+n) / (k + n)! * (1 - kα / (k + n + 1)) -// -// We use this formula to find the probability of triggering the adaptive behavior -// -// Pr_0.909{displacement > 128} = 1.601 * 10^-11 -// -// 1. Alfredo Viola (2005). Distributional analysis of Robin Hood linear probing -// hashing with buckets. - -/// A hash map implemented with linear probing and Robin Hood bucket stealing. +/// A hash map implemented with quadratic probing and SIMD lookup. /// /// By default, `HashMap` uses a hashing algorithm selected to provide /// resistance against HashDoS attacks. The algorithm is randomly seeded, and a @@ -254,13 +52,13 @@ const DISPLACEMENT_THRESHOLD: usize = 128; /// the [`Eq`] trait, changes while it is in the map. This is normally only /// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. /// -/// Relevant papers/articles: +/// The hash table implementation is a Rust port of Google's [SwissTable]. +/// The original C++ version of SwissTable can be found [here], and this +/// [CppCon talk] gives an overview of how the algorithm works. /// -/// 1. Pedro Celis. ["Robin Hood Hashing"](https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf) -/// 2. Emmanuel Goossaert. ["Robin Hood -/// hashing"](http://codecapsule.com/2013/11/11/robin-hood-hashing/) -/// 3. Emmanuel Goossaert. ["Robin Hood hashing: backward shift -/// deletion"](http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/) +/// [SwissTable]: https://abseil.io/blog/20180927-swisstables +/// [here]: https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h +/// [CppCon talk]: https://www.youtube.com/watch?v=ncHmEUmJZf4 /// /// # Examples /// @@ -407,277 +205,7 @@ const DISPLACEMENT_THRESHOLD: usize = 128; #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashMap<K, V, S = RandomState> { - // All hashes are keyed on these values, to prevent hash collision attacks. - hash_builder: S, - - table: RawTable<K, V>, - - resize_policy: DefaultResizePolicy, -} - -/// Search for a pre-hashed key. -/// If you don't already know the hash, use search or search_mut instead -#[inline] -fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, 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 - // case, immediately return the appropriate search result. - if table.capacity() == 0 { - return InternalEntry::TableIsEmpty; - } - - search_hashed_nonempty(table, hash, is_match, true) -} - -/// Search for a pre-hashed key when the hash map is known to be non-empty. -#[inline] -fn search_hashed_nonempty<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F, - compare_hashes: bool) - -> InternalEntry<K, V, M> - where M: Deref<Target = RawTable<K, V>>, - F: FnMut(&K) -> bool -{ - // Do not check the capacity as an extra branch could slow the lookup. - - let size = table.size(); - let mut probe = Bucket::new(table, hash); - let mut displacement = 0; - - loop { - let full = match probe.peek() { - Empty(bucket) => { - // Found a hole! - return InternalEntry::Vacant { - hash, - elem: NoElem(bucket, displacement), - }; - } - Full(bucket) => bucket, - }; - - let probe_displacement = full.displacement(); - - if probe_displacement < displacement { - // Found a luckier bucket than me. - // We can finish the search early if we hit any bucket - // with a lower distance to initial bucket than we've probed. - return InternalEntry::Vacant { - hash, - elem: NeqElem(full, probe_displacement), - }; - } - - // If the hash doesn't match, it can't be this one.. - if !compare_hashes || 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 }; - } - } - displacement += 1; - probe = full.next(); - debug_assert!(displacement <= size); - } -} - -/// Same as `search_hashed_nonempty` but for mutable access. -#[inline] -fn search_hashed_nonempty_mut<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F, - compare_hashes: bool) - -> InternalEntry<K, V, M> - where M: DerefMut<Target = RawTable<K, V>>, - F: FnMut(&K) -> bool -{ - // Do not check the capacity as an extra branch could slow the lookup. - - let size = table.size(); - let mut probe = Bucket::new(table, hash); - let mut displacement = 0; - - loop { - let mut full = match probe.peek() { - Empty(bucket) => { - // Found a hole! - return InternalEntry::Vacant { - hash, - elem: NoElem(bucket, displacement), - }; - } - Full(bucket) => bucket, - }; - - let probe_displacement = full.displacement(); - - if probe_displacement < displacement { - // Found a luckier bucket than me. - // We can finish the search early if we hit any bucket - // with a lower distance to initial bucket than we've probed. - return InternalEntry::Vacant { - hash, - elem: NeqElem(full, probe_displacement), - }; - } - - // If the hash doesn't match, it can't be this one.. - if hash == full.hash() || !compare_hashes { - // If the key doesn't match, it can't be this one.. - if is_match(full.read_mut().0) { - return InternalEntry::Occupied { elem: full }; - } - } - displacement += 1; - probe = full.next(); - debug_assert!(displacement <= size); - } -} - -fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) - -> (K, V, &mut RawTable<K, V>) -{ - let (empty, retkey, retval) = starting_bucket.take(); - let mut gap = match empty.gap_peek() { - Ok(b) => b, - Err(b) => return (retkey, retval, b.into_table()), - }; - - while gap.full().displacement() != 0 { - gap = match gap.shift() { - Ok(b) => b, - Err(b) => { - return (retkey, retval, b.into_table()); - }, - }; - } - - // Now we've done all our shifting. Return the value we grabbed earlier. - (retkey, retval, gap.into_table()) -} - -/// Performs robin hood bucket stealing at the given `bucket`. You must -/// also pass that bucket's displacement so we don't have to recalculate it. -/// -/// `hash`, `key`, and `val` are the elements to "robin hood" into the hashtable. -fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, - mut displacement: usize, - mut hash: SafeHash, - mut key: K, - mut val: V) - -> FullBucketMut<'a, K, V> { - let size = bucket.table().size(); - let raw_capacity = bucket.table().capacity(); - // There can be at most `size - dib` buckets to displace, because - // in the worst case, there are `size` elements and we already are - // `displacement` buckets away from the initial one. - let idx_end = (bucket.index() + size - bucket.displacement()) % raw_capacity; - // Save the *starting point*. - let mut bucket = bucket.stash(); - - loop { - let (old_hash, old_key, old_val) = bucket.replace(hash, key, val); - hash = old_hash; - key = old_key; - val = old_val; - - loop { - displacement += 1; - let probe = bucket.next(); - debug_assert!(probe.index() != idx_end); - - let full_bucket = match probe.peek() { - Empty(bucket) => { - // Found a hole! - let bucket = bucket.put(hash, key, val); - // Now that it's stolen, just read the value's pointer - // right out of the table! Go back to the *starting point*. - // - // This use of `into_table` is misleading. It turns the - // bucket, which is a FullBucket on top of a - // FullBucketMut, into just one FullBucketMut. The "table" - // refers to the inner FullBucketMut in this context. - return bucket.into_table(); - } - Full(bucket) => bucket, - }; - - let probe_displacement = full_bucket.displacement(); - - bucket = full_bucket; - - // Robin hood! Steal the spot. - if probe_displacement < displacement { - displacement = probe_displacement; - break; - } - } - } -} - -impl<K, V, S> HashMap<K, V, S> - where K: Eq + Hash, - S: BuildHasher -{ - fn make_hash<X: ?Sized>(&self, x: &X) -> SafeHash - where X: Hash - { - table::make_hash(&self.hash_builder, x) - } - - /// Search for a key, yielding the index if it's found in the hashtable. - /// If you already have the hash for the key lying around, or if you need an - /// InternalEntry, use search_hashed or search_hashed_nonempty. - #[inline] - fn search<'a, Q: ?Sized>(&'a self, q: &Q) - -> Option<FullBucket<K, V, &'a RawTable<K, V>>> - where K: Borrow<Q>, - Q: Eq + Hash - { - if self.is_empty() { - return None; - } - - let hash = self.make_hash(q); - search_hashed_nonempty(&self.table, hash, |k| q.eq(k.borrow()), true) - .into_occupied_bucket() - } - - #[inline] - fn search_mut<'a, Q: ?Sized>(&'a mut self, q: &Q) - -> Option<FullBucket<K, V, &'a mut RawTable<K, V>>> - where K: Borrow<Q>, - Q: Eq + Hash - { - if self.is_empty() { - return None; - } - - let hash = self.make_hash(q); - search_hashed_nonempty(&mut self.table, hash, |k| q.eq(k.borrow()), true) - .into_occupied_bucket() - } - - // The caller should ensure that invariants by Robin Hood Hashing hold - // and that there's space in the underlying table. - fn insert_hashed_ordered(&mut self, hash: SafeHash, k: K, v: V) { - let mut buckets = Bucket::new(&mut self.table, hash); - let start_index = buckets.index(); - - loop { - // We don't need to compare hashes for value swap. - // Not even DIBs for Robin Hood. - buckets = match buckets.peek() { - Empty(empty) => { - empty.put(hash, k, v); - return; - } - Full(b) => b.into_bucket(), - }; - buckets.next(); - debug_assert!(buckets.index() != start_index); - } - } + base: base::HashMap<K, V, S>, } impl<K: Hash + Eq, V> HashMap<K, V, RandomState> { @@ -732,13 +260,7 @@ impl<K, V, S> HashMap<K, V, S> { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn capacity(&self) -> usize { - self.resize_policy.capacity(self.raw_capacity()) - } - - /// Returns the hash map's raw capacity. - #[inline] - fn raw_capacity(&self) -> usize { - self.table.capacity() + self.base.capacity() } /// An iterator visiting all keys in arbitrary order. @@ -759,7 +281,7 @@ impl<K, V, S> HashMap<K, V, S> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn keys(&self) -> Keys<K, V> { + pub fn keys(&self) -> Keys<'_, K, V> { Keys { inner: self.iter() } } @@ -781,7 +303,7 @@ impl<K, V, S> HashMap<K, V, S> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn values(&self) -> Values<K, V> { + pub fn values(&self) -> Values<'_, K, V> { Values { inner: self.iter() } } @@ -808,7 +330,7 @@ impl<K, V, S> HashMap<K, V, S> { /// } /// ``` #[stable(feature = "map_values_mut", since = "1.10.0")] - pub fn values_mut(&mut self) -> ValuesMut<K, V> { + pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { ValuesMut { inner: self.iter_mut() } } @@ -830,8 +352,8 @@ impl<K, V, S> HashMap<K, V, S> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<K, V> { - Iter { inner: self.table.iter() } + pub fn iter(&self) -> Iter<'_, K, V> { + Iter { base: self.base.iter() } } /// An iterator visiting all key-value pairs in arbitrary order, @@ -858,8 +380,8 @@ impl<K, V, S> HashMap<K, V, S> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter_mut(&mut self) -> IterMut<K, V> { - IterMut { inner: self.table.iter_mut() } + pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { + IterMut { base: self.base.iter_mut() } } /// Returns the number of elements in the map. @@ -876,7 +398,7 @@ impl<K, V, S> HashMap<K, V, S> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { - self.table.size() + self.base.len() } /// Returns `true` if the map contains no elements. @@ -894,7 +416,7 @@ 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 + self.base.is_empty() } /// Clears the map, returning all key-value pairs as an iterator. Keeps the @@ -918,8 +440,8 @@ 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() } + pub fn drain(&mut self) -> Drain<'_, K, V> { + Drain { base: self.base.drain() } } /// Clears the map, removing all key-value pairs. Keeps the allocated memory @@ -938,13 +460,14 @@ impl<K, V, S> HashMap<K, V, S> { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn clear(&mut self) { - self.drain(); + self.base.clear(); } } 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. @@ -970,9 +493,7 @@ impl<K, V, S> HashMap<K, V, S> #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_hasher(hash_builder: S) -> HashMap<K, V, S> { HashMap { - hash_builder, - resize_policy: DefaultResizePolicy::new(), - table: RawTable::new(0), + base: base::HashMap::with_hasher(hash_builder), } } @@ -1000,12 +521,8 @@ 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> { - let resize_policy = DefaultResizePolicy::new(); - let raw_cap = resize_policy.raw_capacity(capacity); HashMap { - hash_builder, - resize_policy, - table: RawTable::new(raw_cap), + base: base::HashMap::with_capacity_and_hasher(capacity, hash_builder), } } @@ -1023,9 +540,10 @@ impl<K, V, S> HashMap<K, V, S> /// let map: HashMap<i32, i32> = HashMap::with_hasher(hasher); /// let hasher: &RandomState = map.hasher(); /// ``` + #[inline] #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] pub fn hasher(&self) -> &S { - &self.hash_builder + self.base.hasher() } /// Reserves capacity for at least `additional` more elements to be inserted @@ -1048,11 +566,7 @@ impl<K, V, S> HashMap<K, V, S> #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { - match self.reserve_internal(additional, Infallible) { - Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => unreachable!(), - Ok(()) => { /* yay */ } - } + self.base.reserve(additional) } /// Tries to reserve capacity for at least `additional` more elements to be inserted @@ -1072,92 +586,12 @@ impl<K, V, S> HashMap<K, V, S> /// let mut map: HashMap<&str, isize> = HashMap::new(); /// map.try_reserve(10).expect("why is the test harness OOMing on 10 bytes?"); /// ``` - #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { - self.reserve_internal(additional, Fallible) - } - #[inline] - fn reserve_internal(&mut self, additional: usize, fallibility: Fallibility) - -> Result<(), CollectionAllocErr> { - - let remaining = self.capacity() - self.len(); // this can't overflow - if remaining < additional { - let min_cap = self.len() - .checked_add(additional) - .ok_or(CollectionAllocErr::CapacityOverflow)?; - let raw_cap = self.resize_policy.try_raw_capacity(min_cap)?; - self.try_resize(raw_cap, fallibility)?; - } else if self.table.tag() && remaining <= self.len() { - // Probe sequence is too long and table is half full, - // resize early to reduce probing length. - let new_capacity = self.table.capacity() * 2; - self.try_resize(new_capacity, fallibility)?; - } - Ok(()) - } - - /// 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_raw_cap` is a power of two or zero. - #[inline(never)] - #[cold] - fn try_resize( - &mut self, - new_raw_cap: usize, - fallibility: Fallibility, - ) -> Result<(), CollectionAllocErr> { - assert!(self.table.size() <= new_raw_cap); - assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0); - - let mut old_table = replace( - &mut self.table, - match fallibility { - Infallible => RawTable::new(new_raw_cap), - Fallible => RawTable::try_new(new_raw_cap)?, - } - ); - let old_size = old_table.size(); - - if old_table.size() == 0 { - return Ok(()); - } - - let mut bucket = Bucket::head_bucket(&mut old_table); - - // This is how the buckets might be laid out in memory: - // ($ marks an initialized bucket) - // ________________ - // |$$$_$$$$$$_$$$$$| - // - // But we've skipped the entire initial cluster of buckets - // and will continue iteration in this order: - // ________________ - // |$$$$$$_$$$$$ - // ^ wrap around once end is reached - // ________________ - // $$$_____________| - // ^ exit once table.size == 0 - loop { - bucket = match bucket.peek() { - Full(bucket) => { - let h = bucket.hash(); - let (b, k, v) = bucket.take(); - self.insert_hashed_ordered(h, k, v); - if b.table().size() == 0 { - break; - } - b.into_bucket() - } - Empty(b) => b.into_bucket(), - }; - bucket.next(); - } - - assert_eq!(self.table.size(), old_size); - Ok(()) + #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.base + .try_reserve(additional) + .map_err(map_collection_alloc_err) } /// Shrinks the capacity of the map as much as possible. It will drop @@ -1176,20 +610,10 @@ impl<K, V, S> HashMap<K, V, S> /// map.shrink_to_fit(); /// assert!(map.capacity() >= 2); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn shrink_to_fit(&mut self) { - 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: - for (h, k, v) in old_table.into_iter() { - self.insert_hashed_nocheck(h, k, v); - } - - debug_assert_eq!(self.table.size(), old_size); - } + self.base.shrink_to_fit(); } /// Shrinks the capacity of the map with a lower limit. It will drop @@ -1214,40 +638,14 @@ impl<K, V, S> HashMap<K, V, S> /// map.shrink_to(0); /// assert!(map.capacity() >= 2); /// ``` - #[unstable(feature = "shrink_to", reason = "new API", issue="56431")] + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] pub fn shrink_to(&mut self, min_capacity: usize) { - assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); - - let new_raw_cap = self.resize_policy.raw_capacity(max(self.len(), min_capacity)); - 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: - for (h, k, v) in old_table.into_iter() { - self.insert_hashed_nocheck(h, k, v); - } - - debug_assert_eq!(self.table.size(), old_size); - } - } - - /// Insert a pre-hashed key-value pair, without first checking - /// that there's enough room in the buckets. Returns a reference to the - /// newly insert value. - /// - /// If the key already exists, the hashtable will be returned untouched - /// and a reference to the existing element will be returned. - 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(Vacant(elem)) => { - elem.insert(v); - None - } - None => unreachable!(), - } + assert!( + self.capacity() >= min_capacity, + "Tried to shrink to a larger capacity" + ); + self.base.shrink_to(min_capacity); } /// Gets the given key's corresponding entry in the map for in-place manipulation. @@ -1269,13 +667,10 @@ impl<K, V, S> HashMap<K, V, S> /// assert_eq!(letters[&'u'], 1); /// assert_eq!(letters.get(&'y'), None); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn entry(&mut self, key: K) -> Entry<K, V> { - // Gotta resize now. - self.reserve(1); - let hash = self.make_hash(&key); - search_hashed(&mut self.table, hash, |q| q.eq(&key)) - .into_entry(key).expect("unreachable") + pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + map_entry(self.base.rustc_entry(key)) } /// Returns a reference to the value corresponding to the key. @@ -1300,10 +695,11 @@ impl<K, V, S> HashMap<K, V, S> #[stable(feature = "rust1", since = "1.0.0")] #[inline] 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).map(|bucket| bucket.into_refs().1) + self.base.get(k) } /// Returns the key-value pair corresponding to the supplied key. @@ -1327,11 +723,13 @@ impl<K, V, S> HashMap<K, V, S> /// assert_eq!(map.get_key_value(&2), None); /// ``` #[unstable(feature = "map_get_key_value", issue = "49347")] + #[inline] pub fn get_key_value<Q: ?Sized>(&self, k: &Q) -> Option<(&K, &V)> - where K: Borrow<Q>, - Q: Hash + Eq + where + K: Borrow<Q>, + Q: Hash + Eq, { - self.search(k).map(|bucket| bucket.into_refs()) + self.base.get_key_value(k) } /// Returns `true` if the map contains a value for the specified key. @@ -1354,11 +752,13 @@ impl<K, V, S> HashMap<K, V, S> /// assert_eq!(map.contains_key(&2), false); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] 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).is_some() + self.base.contains_key(k) } /// Returns a mutable reference to the value corresponding to the key. @@ -1383,11 +783,13 @@ impl<K, V, S> HashMap<K, V, S> /// assert_eq!(map[&1], "b"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] 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).map(|bucket| bucket.into_mut_refs().1) + self.base.get_mut(k) } /// Inserts a key-value pair into the map. @@ -1416,10 +818,9 @@ impl<K, V, S> HashMap<K, V, S> /// assert_eq!(map[&37], "c"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn insert(&mut self, k: K, v: V) -> Option<V> { - let hash = self.make_hash(&k); - self.reserve(1); - self.insert_hashed_nocheck(hash, k, v) + self.base.insert(k, v) } /// Removes a key from the map, returning the value at the key if the key @@ -1443,11 +844,13 @@ impl<K, V, S> HashMap<K, V, S> /// assert_eq!(map.remove(&1), None); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] 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, { - self.search_mut(k).map(|bucket| pop_internal(bucket).1) + self.base.remove(k) } /// Removes a key from the map, returning the stored key and value if the @@ -1473,15 +876,13 @@ impl<K, V, S> HashMap<K, V, S> /// # } /// ``` #[stable(feature = "hash_map_remove_entry", since = "1.27.0")] + #[inline] pub fn remove_entry<Q: ?Sized>(&mut self, k: &Q) -> Option<(K, V)> - where K: Borrow<Q>, - Q: Hash + Eq + where + K: Borrow<Q>, + Q: Hash + Eq, { - self.search_mut(k) - .map(|bucket| { - let (k, v, _) = pop_internal(bucket); - (k, v) - }) + self.base.remove_entry(k) } /// Retains only the elements specified by the predicate. @@ -1498,45 +899,18 @@ impl<K, V, S> HashMap<K, V, S> /// assert_eq!(map.len(), 4); /// ``` #[stable(feature = "retain_hash_collection", since = "1.18.0")] - pub fn retain<F>(&mut self, mut f: F) - where F: FnMut(&K, &mut V) -> bool + #[inline] + pub fn retain<F>(&mut self, f: F) + where + F: FnMut(&K, &mut V) -> bool, { - if self.table.size() == 0 { - return; - } - let mut elems_left = self.table.size(); - let mut bucket = Bucket::head_bucket(&mut self.table); - bucket.prev(); - let start_index = bucket.index(); - while elems_left != 0 { - bucket = match bucket.peek() { - Full(mut full) => { - elems_left -= 1; - let should_remove = { - let (k, v) = full.read_mut(); - !f(k, v) - }; - if should_remove { - let prev_raw = full.raw(); - let (_, _, t) = pop_internal(full); - Bucket::new_from(prev_raw, t) - } else { - full.into_bucket() - } - }, - Empty(b) => { - b.into_bucket() - } - }; - bucket.prev(); // reverse iteration - debug_assert!(elems_left == 0 || bucket.index() != start_index); - } + self.base.retain(f) } } impl<K, V, S> HashMap<K, V, S> - where K: Eq + Hash, - S: BuildHasher +where + S: BuildHasher, { /// Creates a raw entry builder for the HashMap. /// @@ -1569,10 +943,9 @@ impl<K, V, S> HashMap<K, V, S> /// so that the map now contains keys which compare equal, search may start /// acting erratically, with two keys randomly masking each other. Implementations /// are free to assume this doesn't happen (within the limits of memory-safety). - #[inline(always)] + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<K, V, S> { - self.reserve(1); + pub fn raw_entry_mut(&mut self) -> RawEntryBuilderMut<'_, K, V, S> { RawEntryBuilderMut { map: self } } @@ -1591,52 +964,59 @@ impl<K, V, S> HashMap<K, V, S> /// `get` should be preferred. /// /// Immutable raw entries have very limited use; you might instead want `raw_entry_mut`. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn raw_entry(&self) -> RawEntryBuilder<K, V, S> { + pub fn raw_entry(&self) -> RawEntryBuilder<'_, K, V, S> { RawEntryBuilder { map: self } } } #[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; } - 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map().entries(self.iter()).finish() } } #[stable(feature = "rust1", since = "1.0.0")] impl<K, V, S> Default for HashMap<K, V, S> - where K: Eq + Hash, - S: BuildHasher + Default +where + K: Eq + Hash, + S: BuildHasher + Default, { /// Creates an empty `HashMap<K, V, S>`, with the `Default` value for the hasher. + #[inline] fn default() -> HashMap<K, V, S> { HashMap::with_hasher(Default::default()) } @@ -1644,9 +1024,10 @@ impl<K, V, S> Default for HashMap<K, V, S> #[stable(feature = "rust1", since = "1.0.0")] impl<K, Q: ?Sized, V, S> Index<&Q> for HashMap<K, V, S> - where K: Eq + Hash + Borrow<Q>, - Q: Eq + Hash, - S: BuildHasher +where + K: Eq + Hash + Borrow<Q>, + Q: Eq + Hash, + S: BuildHasher, { type Output = V; @@ -1670,23 +1051,24 @@ impl<K, Q: ?Sized, V, S> Index<&Q> for HashMap<K, V, S> /// [`HashMap`]: struct.HashMap.html #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, K: 'a, V: 'a> { - inner: table::Iter<'a, K, V>, + base: base::Iter<'a, K, V>, } // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<K, V> Clone for Iter<'_, K, V> { + #[inline] fn clone(&self) -> Self { - Iter { inner: self.inner.clone() } + Iter { + base: self.base.clone(), + } } } #[stable(feature = "std_debug", since = "1.16.0")] impl<K: Debug, V: Debug> fmt::Debug for Iter<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list() - .entries(self.clone()) - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() } } @@ -1699,7 +1081,17 @@ impl<K: Debug, V: Debug> fmt::Debug for Iter<'_, K, V> { /// [`HashMap`]: struct.HashMap.html #[stable(feature = "rust1", since = "1.0.0")] pub struct IterMut<'a, K: 'a, V: 'a> { - inner: table::IterMut<'a, K, V>, + base: base::IterMut<'a, K, V>, +} + +impl<'a, K, V> IterMut<'a, K, V> { + /// Returns a iterator of references over the remaining items. + #[inline] + pub(super) fn iter(&self) -> Iter<'_, K, V> { + Iter { + base: self.base.rustc_iter(), + } + } } /// An owning iterator over the entries of a `HashMap`. @@ -1711,7 +1103,17 @@ pub struct IterMut<'a, K: 'a, V: 'a> { /// [`HashMap`]: struct.HashMap.html #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter<K, V> { - pub(super) inner: table::IntoIter<K, V>, + base: base::IntoIter<K, V>, +} + +impl<K, V> IntoIter<K, V> { + /// Returns a iterator of references over the remaining items. + #[inline] + pub(super) fn iter(&self) -> Iter<'_, K, V> { + Iter { + base: self.base.rustc_iter(), + } + } } /// An iterator over the keys of a `HashMap`. @@ -1729,17 +1131,18 @@ pub struct Keys<'a, K: 'a, V: 'a> { // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<K, V> Clone for Keys<'_, K, V> { + #[inline] fn clone(&self) -> Self { - Keys { inner: self.inner.clone() } + Keys { + inner: self.inner.clone(), + } } } #[stable(feature = "std_debug", since = "1.16.0")] impl<K: Debug, V> fmt::Debug for Keys<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list() - .entries(self.clone()) - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() } } @@ -1758,17 +1161,18 @@ pub struct Values<'a, K: 'a, V: 'a> { // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl<K, V> Clone for Values<'_, K, V> { + #[inline] fn clone(&self) -> Self { - Values { inner: self.inner.clone() } + Values { + inner: self.inner.clone(), + } } } #[stable(feature = "std_debug", since = "1.16.0")] impl<K, V: Debug> fmt::Debug for Values<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list() - .entries(self.clone()) - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() } } @@ -1781,7 +1185,17 @@ impl<K, V: Debug> fmt::Debug for Values<'_, K, V> { /// [`HashMap`]: struct.HashMap.html #[stable(feature = "drain", since = "1.6.0")] pub struct Drain<'a, K: 'a, V: 'a> { - pub(super) inner: table::Drain<'a, K, V>, + base: base::Drain<'a, K, V>, +} + +impl<'a, K, V> Drain<'a, K, V> { + /// Returns a iterator of references over the remaining items. + #[inline] + pub(super) fn iter(&self) -> Iter<'_, K, V> { + Iter { + base: self.base.rustc_iter(), + } + } } /// A mutable iterator over the values of a `HashMap`. @@ -1796,47 +1210,6 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { inner: IterMut<'a, K, V>, } -enum InternalEntry<K, V, M> { - Occupied { elem: FullBucket<K, V, M> }, - Vacant { - hash: SafeHash, - elem: VacantEntryState<K, V, M>, - }, - TableIsEmpty, -} - -impl<K, V, M> InternalEntry<K, V, M> { - #[inline] - fn into_occupied_bucket(self) -> Option<FullBucket<K, V, M>> { - match self { - InternalEntry::Occupied { elem } => Some(elem), - _ => None, - } - } -} - -impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> { - #[inline] - fn into_entry(self, key: K) -> Option<Entry<'a, K, V>> { - match self { - InternalEntry::Occupied { elem } => { - Some(Occupied(OccupiedEntry { - key: Some(key), - elem, - })) - } - InternalEntry::Vacant { hash, elem } => { - Some(Vacant(VacantEntry { - hash, - key, - elem, - })) - } - InternalEntry::TableIsEmpty => None, - } - } -} - /// A builder for computing where in a HashMap a key-value pair would be stored. /// /// See the [`HashMap::raw_entry_mut`] docs for usage examples. @@ -1852,11 +1225,13 @@ pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { /// /// This is a lower-level version of [`Entry`]. /// -/// This `enum` is constructed from the [`raw_entry`] method on [`HashMap`]. +/// This `enum` is constructed through the [`raw_entry_mut`] method on [`HashMap`], +/// then calling one of the methods of that [`RawEntryBuilderMut`]. /// /// [`HashMap`]: struct.HashMap.html /// [`Entry`]: enum.Entry.html -/// [`raw_entry`]: struct.HashMap.html#method.raw_entry +/// [`raw_entry_mut`]: struct.HashMap.html#method.raw_entry_mut +/// [`RawEntryBuilderMut`]: struct.RawEntryBuilderMut.html #[unstable(feature = "hash_raw_entry", issue = "56167")] pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// An occupied entry. @@ -1871,7 +1246,7 @@ pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// [`RawEntryMut`]: enum.RawEntryMut.html #[unstable(feature = "hash_raw_entry", issue = "56167")] pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a> { - elem: FullBucket<K, V, &'a mut RawTable<K, V>>, + base: base::RawOccupiedEntryMut<'a, K, V>, } /// A view into a vacant entry in a `HashMap`. @@ -1880,8 +1255,7 @@ pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a> { /// [`RawEntryMut`]: enum.RawEntryMut.html #[unstable(feature = "hash_raw_entry", issue = "56167")] pub struct RawVacantEntryMut<'a, K: 'a, V: 'a, S: 'a> { - elem: VacantEntryState<K, V, &'a mut RawTable<K, V>>, - hash_builder: &'a S, + base: base::RawVacantEntryMut<'a, K, V, S>, } /// A builder for computing where in a HashMap a key-value pair would be stored. @@ -1895,128 +1269,81 @@ pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { } impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> - where S: BuildHasher, - K: Eq + Hash, +where + S: BuildHasher, { /// Creates a `RawEntryMut` from the given key. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn from_key<Q: ?Sized>(self, k: &Q) -> RawEntryMut<'a, K, V, S> - where K: Borrow<Q>, - Q: Hash + Eq + where + K: Borrow<Q>, + Q: Hash + Eq, { - let mut hasher = self.map.hash_builder.build_hasher(); - k.hash(&mut hasher); - self.from_key_hashed_nocheck(hasher.finish(), k) + map_raw_entry(self.map.base.raw_entry_mut().from_key(k)) } /// Creates a `RawEntryMut` from the given key and its hash. #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn from_key_hashed_nocheck<Q: ?Sized>(self, hash: u64, k: &Q) -> RawEntryMut<'a, K, V, S> - where K: Borrow<Q>, - Q: Eq + where + K: Borrow<Q>, + Q: Eq, { - self.from_hash(hash, |q| q.borrow().eq(k)) + map_raw_entry( + self.map + .base + .raw_entry_mut() + .from_key_hashed_nocheck(hash, k), + ) } - #[inline] - fn search<F>(self, hash: u64, is_match: F, compare_hashes: bool) -> RawEntryMut<'a, K, V, S> - where for<'b> F: FnMut(&'b K) -> bool, - { - match search_hashed_nonempty_mut(&mut self.map.table, - SafeHash::new(hash), - is_match, - compare_hashes) { - InternalEntry::Occupied { elem } => { - RawEntryMut::Occupied(RawOccupiedEntryMut { elem }) - } - InternalEntry::Vacant { elem, .. } => { - RawEntryMut::Vacant(RawVacantEntryMut { - elem, - hash_builder: &self.map.hash_builder, - }) - } - InternalEntry::TableIsEmpty => { - unreachable!() - } - } - } /// Creates a `RawEntryMut` from the given hash. #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn from_hash<F>(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> - where for<'b> F: FnMut(&'b K) -> bool, - { - self.search(hash, is_match, true) - } - - /// Search possible locations for an element with hash `hash` until `is_match` returns true for - /// one of them. There is no guarantee that all keys passed to `is_match` will have the provided - /// hash. - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn search_bucket<F>(self, hash: u64, is_match: F) -> RawEntryMut<'a, K, V, S> - where for<'b> F: FnMut(&'b K) -> bool, + where + for<'b> F: FnMut(&'b K) -> bool, { - self.search(hash, is_match, false) + map_raw_entry(self.map.base.raw_entry_mut().from_hash(hash, is_match)) } } impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> - where S: BuildHasher, +where + S: BuildHasher, { /// Access an entry by key. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn from_key<Q: ?Sized>(self, k: &Q) -> Option<(&'a K, &'a V)> - where K: Borrow<Q>, - Q: Hash + Eq + where + K: Borrow<Q>, + Q: Hash + Eq, { - let mut hasher = self.map.hash_builder.build_hasher(); - k.hash(&mut hasher); - self.from_key_hashed_nocheck(hasher.finish(), k) + self.map.base.raw_entry().from_key(k) } /// Access an entry by a key and its hash. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn from_key_hashed_nocheck<Q: ?Sized>(self, hash: u64, k: &Q) -> Option<(&'a K, &'a V)> - where K: Borrow<Q>, - Q: Hash + Eq - - { - self.from_hash(hash, |q| q.borrow().eq(k)) - } - - fn search<F>(self, hash: u64, is_match: F, compare_hashes: bool) -> Option<(&'a K, &'a V)> - where F: FnMut(&K) -> bool + where + K: Borrow<Q>, + Q: Hash + Eq, { - if unsafe { unlikely(self.map.table.size() == 0) } { - return None; - } - match search_hashed_nonempty(&self.map.table, - SafeHash::new(hash), - is_match, - compare_hashes) { - InternalEntry::Occupied { elem } => Some(elem.into_refs()), - InternalEntry::Vacant { .. } => None, - InternalEntry::TableIsEmpty => unreachable!(), - } + self.map.base.raw_entry().from_key_hashed_nocheck(hash, k) } /// Access an entry by hash. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn from_hash<F>(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> - where F: FnMut(&K) -> bool - { - self.search(hash, is_match, true) - } - - /// Search possible locations for an element with hash `hash` until `is_match` returns true for - /// one of them. There is no guarantee that all keys passed to `is_match` will have the provided - /// hash. - #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn search_bucket<F>(self, hash: u64, is_match: F) -> Option<(&'a K, &'a V)> - where F: FnMut(&K) -> bool + where + F: FnMut(&K) -> bool, { - self.search(hash, is_match, false) + self.map.base.raw_entry().from_hash(hash, is_match) } } @@ -2038,10 +1365,12 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { /// *map.raw_entry_mut().from_key("poneyland").or_insert("poneyland", 10).1 *= 2; /// assert_eq!(map["poneyland"], 6); /// ``` + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn or_insert(self, default_key: K, default_val: V) -> (&'a mut K, &'a mut V) - where K: Hash, - S: BuildHasher, + where + K: Hash, + S: BuildHasher, { match self { RawEntryMut::Occupied(entry) => entry.into_key_value(), @@ -2066,11 +1395,13 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { /// /// assert_eq!(map["poneyland"], "hoho".to_string()); /// ``` + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn or_insert_with<F>(self, default: F) -> (&'a mut K, &'a mut V) - where F: FnOnce() -> (K, V), - K: Hash, - S: BuildHasher, + where + F: FnOnce() -> (K, V), + K: Hash, + S: BuildHasher, { match self { RawEntryMut::Occupied(entry) => entry.into_key_value(), @@ -2104,9 +1435,11 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { /// .or_insert("poneyland", 0); /// assert_eq!(map["poneyland"], 43); /// ``` + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn and_modify<F>(self, f: F) -> Self - where F: FnOnce(&mut K, &mut V) + where + F: FnOnce(&mut K, &mut V), { match self { RawEntryMut::Occupied(mut entry) => { @@ -2115,7 +1448,7 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { f(k, v); } RawEntryMut::Occupied(entry) - }, + } RawEntryMut::Vacant(entry) => RawEntryMut::Vacant(entry), } } @@ -2123,174 +1456,164 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> { impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> { /// Gets a reference to the key in the entry. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn key(&self) -> &K { - self.elem.read().0 + self.base.key() } /// Gets a mutable reference to the key in the entry. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn key_mut(&mut self) -> &mut K { - self.elem.read_mut().0 + self.base.key_mut() } /// Converts the entry into a mutable reference to the key in the entry /// with a lifetime bound to the map itself. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn into_key(self) -> &'a mut K { - self.elem.into_mut_refs().0 + self.base.into_key() } /// Gets a reference to the value in the entry. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn get(&self) -> &V { - self.elem.read().1 + self.base.get() } /// Converts the OccupiedEntry into a mutable reference to the value in the entry /// with a lifetime bound to the map itself. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn into_mut(self) -> &'a mut V { - self.elem.into_mut_refs().1 + self.base.into_mut() } /// Gets a mutable reference to the value in the entry. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn get_mut(&mut self) -> &mut V { - self.elem.read_mut().1 + self.base.get_mut() } /// Gets a reference to the key and value in the entry. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn get_key_value(&mut self) -> (&K, &V) { - self.elem.read() + self.base.get_key_value() } /// Gets a mutable reference to the key and value in the entry. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn get_key_value_mut(&mut self) -> (&mut K, &mut V) { - self.elem.read_mut() + self.base.get_key_value_mut() } /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry /// with a lifetime bound to the map itself. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn into_key_value(self) -> (&'a mut K, &'a mut V) { - self.elem.into_mut_refs() + self.base.into_key_value() } /// Sets the value of the entry, and returns the entry's old value. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn insert(&mut self, value: V) -> V { - mem::replace(self.get_mut(), value) + self.base.insert(value) } /// Sets the value of the entry, and returns the entry's old value. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn insert_key(&mut self, key: K) -> K { - mem::replace(self.key_mut(), key) + self.base.insert_key(key) } /// Takes the value out of the entry, and returns it. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn remove(self) -> V { - pop_internal(self.elem).1 + self.base.remove() } /// Take the ownership of the key and value from the map. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn remove_entry(self) -> (K, V) { - let (k, v, _) = pop_internal(self.elem); - (k, v) + self.base.remove_entry() } } impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { /// Sets the value of the entry with the VacantEntry's key, /// and returns a mutable reference to it. + #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] pub fn insert(self, key: K, value: V) -> (&'a mut K, &'a mut V) - where K: Hash, - S: BuildHasher, + where + K: Hash, + S: BuildHasher, { - let mut hasher = self.hash_builder.build_hasher(); - key.hash(&mut hasher); - self.insert_hashed_nocheck(hasher.finish(), key, value) + self.base.insert(key, value) } /// Sets the value of the entry with the VacantEntry's key, /// and returns a mutable reference to it. #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] - pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) { - let hash = SafeHash::new(hash); - let b = match self.elem { - NeqElem(mut bucket, disp) => { - if disp >= DISPLACEMENT_THRESHOLD { - bucket.table_mut().set_tag(true); - } - robin_hood(bucket, disp, hash, key, value) - }, - NoElem(mut bucket, disp) => { - if disp >= DISPLACEMENT_THRESHOLD { - bucket.table_mut().set_tag(true); - } - bucket.put(hash, key, value) - }, - }; - b.into_mut_refs() + pub fn insert_hashed_nocheck(self, hash: u64, key: K, value: V) -> (&'a mut K, &'a mut V) + where + K: Hash, + S: BuildHasher, + { + self.base.insert_hashed_nocheck(hash, key, value) } } #[unstable(feature = "hash_raw_entry", issue = "56167")] impl<K, V, S> Debug for RawEntryBuilderMut<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RawEntryBuilder") - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilder").finish() } } #[unstable(feature = "hash_raw_entry", issue = "56167")] impl<K: Debug, V: Debug, S> Debug for RawEntryMut<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - RawEntryMut::Vacant(ref v) => { - f.debug_tuple("RawEntry") - .field(v) - .finish() - } - RawEntryMut::Occupied(ref o) => { - f.debug_tuple("RawEntry") - .field(o) - .finish() - } + RawEntryMut::Vacant(ref v) => f.debug_tuple("RawEntry").field(v).finish(), + RawEntryMut::Occupied(ref o) => f.debug_tuple("RawEntry").field(o).finish(), } } } #[unstable(feature = "hash_raw_entry", issue = "56167")] impl<K: Debug, V: Debug> Debug for RawOccupiedEntryMut<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RawOccupiedEntryMut") - .field("key", self.key()) - .field("value", self.get()) - .finish() + .field("key", self.key()) + .field("value", self.get()) + .finish() } } #[unstable(feature = "hash_raw_entry", issue = "56167")] impl<K, V, S> Debug for RawVacantEntryMut<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RawVacantEntryMut") - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawVacantEntryMut").finish() } } #[unstable(feature = "hash_raw_entry", issue = "56167")] impl<K, V, S> Debug for RawEntryBuilder<'_, K, V, S> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RawEntryBuilder") - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawEntryBuilder").finish() } } @@ -2304,29 +1627,19 @@ impl<K, V, S> Debug for RawEntryBuilder<'_, K, V, S> { 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")] +#[stable(feature = "debug_hash_map", since = "1.12.0")] impl<K: Debug, V: Debug> Debug for Entry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + 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(), } } } @@ -2337,18 +1650,12 @@ impl<K: Debug, V: Debug> Debug for Entry<'_, K, V> { /// [`Entry`]: enum.Entry.html #[stable(feature = "rust1", since = "1.0.0")] pub struct OccupiedEntry<'a, K: 'a, V: 'a> { - key: Option<K>, - elem: FullBucket<K, V, &'a mut RawTable<K, V>>, + base: base::RustcOccupiedEntry<'a, K, V>, } -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<'a, K: 'a + Send, V: 'a + Send> Send for OccupiedEntry<'a, K, V> {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<'a, K: 'a + Sync, V: 'a + Sync> Sync for OccupiedEntry<'a, K, V> {} - -#[stable(feature= "debug_hash_map", since = "1.12.0")] +#[stable(feature = "debug_hash_map", since = "1.12.0")] impl<K: Debug, V: Debug> Debug for OccupiedEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OccupiedEntry") .field("key", self.key()) .field("value", self.get()) @@ -2362,39 +1669,22 @@ impl<K: Debug, V: Debug> Debug for OccupiedEntry<'_, K, V> { /// [`Entry`]: enum.Entry.html #[stable(feature = "rust1", since = "1.0.0")] pub struct VacantEntry<'a, K: 'a, V: 'a> { - hash: SafeHash, - key: K, - elem: VacantEntryState<K, V, &'a mut RawTable<K, V>>, + base: base::RustcVacantEntry<'a, K, V>, } -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<'a, K: 'a + Send, V: 'a + Send> Send for VacantEntry<'a, K, V> {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<'a, K: 'a + Sync, V: 'a + Sync> Sync for VacantEntry<'a, K, V> {} - -#[stable(feature= "debug_hash_map", since = "1.12.0")] +#[stable(feature = "debug_hash_map", since = "1.12.0")] impl<K: Debug, V> Debug for VacantEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("VacantEntry") - .field(self.key()) - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("VacantEntry").field(self.key()).finish() } } -/// Possible states of a VacantEntry. -enum VacantEntryState<K, V, M> { - /// The index is occupied, but the key to insert has precedence, - /// and will kick the current one out on insertion. - NeqElem(FullBucket<K, V, M>, usize), - /// The index is genuinely vacant. - NoElem(EmptyBucket<K, V, M>, usize), -} - #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S> { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V>; + #[inline] fn into_iter(self) -> Iter<'a, K, V> { self.iter() } @@ -2405,6 +1695,7 @@ impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S> { type Item = (&'a K, &'a mut V); type IntoIter = IterMut<'a, K, V>; + #[inline] fn into_iter(self) -> IterMut<'a, K, V> { self.iter_mut() } @@ -2432,8 +1723,11 @@ impl<K, V, S> IntoIterator for HashMap<K, V, S> { /// // Not possible with .iter() /// let vec: Vec<(&str, i32)> = map.into_iter().collect(); /// ``` + #[inline] fn into_iter(self) -> IntoIter<K, V> { - IntoIter { inner: self.table.into_iter() } + IntoIter { + base: self.base.into_iter(), + } } } @@ -2443,18 +1737,18 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> { #[inline] fn next(&mut self) -> Option<(&'a K, &'a V)> { - self.inner.next() + self.base.next() } #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - self.inner.size_hint() + self.base.size_hint() } } #[stable(feature = "rust1", since = "1.0.0")] impl<K, V> ExactSizeIterator for Iter<'_, K, V> { #[inline] fn len(&self) -> usize { - self.inner.len() + self.base.len() } } @@ -2467,18 +1761,18 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> { #[inline] fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - self.inner.next() + self.base.next() } #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - self.inner.size_hint() + self.base.size_hint() } } #[stable(feature = "rust1", since = "1.0.0")] impl<K, V> ExactSizeIterator for IterMut<'_, K, V> { #[inline] fn len(&self) -> usize { - self.inner.len() + self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] @@ -2486,13 +1780,12 @@ impl<K, V> FusedIterator for IterMut<'_, K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] impl<K, V> fmt::Debug for IterMut<'_, K, V> - where K: fmt::Debug, - V: fmt::Debug, +where + K: fmt::Debug, + V: fmt::Debug, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list() - .entries(self.inner.iter()) - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() } } @@ -2502,18 +1795,18 @@ impl<K, V> Iterator for IntoIter<K, V> { #[inline] fn next(&mut self) -> Option<(K, V)> { - self.inner.next().map(|(_, k, v)| (k, v)) + self.base.next() } #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - self.inner.size_hint() + self.base.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() + self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] @@ -2521,10 +1814,8 @@ impl<K, V> FusedIterator for IntoIter<K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] impl<K: Debug, V: Debug> fmt::Debug for IntoIter<K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list() - .entries(self.inner.iter()) - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() } } @@ -2599,13 +1890,12 @@ impl<K, V> FusedIterator for ValuesMut<'_, K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] impl<K, V> fmt::Debug for ValuesMut<'_, K, V> - where K: fmt::Debug, - V: fmt::Debug, +where + K: fmt::Debug, + V: fmt::Debug, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list() - .entries(self.inner.inner.iter()) - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.inner.iter()).finish() } } @@ -2615,18 +1905,18 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { #[inline] fn next(&mut self) -> Option<(K, V)> { - self.inner.next().map(|(_, k, v)| (k, v)) + self.base.next() } #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - self.inner.size_hint() + self.base.size_hint() } } #[stable(feature = "drain", since = "1.6.0")] impl<K, V> ExactSizeIterator for Drain<'_, K, V> { #[inline] fn len(&self) -> usize { - self.inner.len() + self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] @@ -2634,13 +1924,12 @@ impl<K, V> FusedIterator for Drain<'_, K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] impl<K, V> fmt::Debug for Drain<'_, K, V> - where K: fmt::Debug, - V: fmt::Debug, +where + K: fmt::Debug, + V: fmt::Debug, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list() - .entries(self.inner.iter()) - .finish() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() } } @@ -2662,6 +1951,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// *map.entry("poneyland").or_insert(10) *= 2; /// assert_eq!(map["poneyland"], 6); /// ``` + #[inline] pub fn or_insert(self, default: V) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), @@ -2685,6 +1975,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// /// assert_eq!(map["poneyland"], "hoho".to_string()); /// ``` + #[inline] pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), @@ -2702,6 +1993,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// let mut map: HashMap<&str, u32> = HashMap::new(); /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); /// ``` + #[inline] #[stable(feature = "map_entry_keys", since = "1.10.0")] pub fn key(&self) -> &K { match *self { @@ -2730,19 +2022,20 @@ impl<'a, K, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` + #[inline] #[stable(feature = "entry_and_modify", since = "1.26.0")] pub fn and_modify<F>(self, f: F) -> Self - where F: FnOnce(&mut V) + where + F: FnOnce(&mut V), { match self { Occupied(mut entry) => { f(entry.get_mut()); Occupied(entry) - }, + } Vacant(entry) => Vacant(entry), } } - } impl<'a, K, V: Default> Entry<'a, K, V> { @@ -2762,6 +2055,7 @@ impl<'a, K, V: Default> Entry<'a, K, V> { /// assert_eq!(map["poneyland"], None); /// # } /// ``` + #[inline] pub fn or_default(self) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), @@ -2782,9 +2076,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// map.entry("poneyland").or_insert(12); /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); /// ``` + #[inline] #[stable(feature = "map_entry_keys", since = "1.10.0")] pub fn key(&self) -> &K { - self.elem.read().0 + self.base.key() } /// Take the ownership of the key and value from the map. @@ -2805,10 +2100,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// /// assert_eq!(map.contains_key("poneyland"), false); /// ``` + #[inline] #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] pub fn remove_entry(self) -> (K, V) { - let (k, v, _) = pop_internal(self.elem); - (k, v) + self.base.remove_entry() } /// Gets a reference to the value in the entry. @@ -2826,9 +2121,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// assert_eq!(o.get(), &12); /// } /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn get(&self) -> &V { - self.elem.read().1 + self.base.get() } /// Gets a mutable reference to the value in the entry. @@ -2858,9 +2154,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// /// assert_eq!(map["poneyland"], 24); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self) -> &mut V { - self.elem.read_mut().1 + self.base.get_mut() } /// Converts the OccupiedEntry into a mutable reference to the value in the entry @@ -2886,9 +2183,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// /// assert_eq!(map["poneyland"], 22); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn into_mut(self) -> &'a mut V { - self.elem.into_mut_refs().1 + self.base.into_mut() } /// Sets the value of the entry, and returns the entry's old value. @@ -2908,11 +2206,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// /// assert_eq!(map["poneyland"], 15); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, mut value: V) -> V { - let old_value = self.get_mut(); - mem::swap(&mut value, old_value); - value + pub fn insert(&mut self, value: V) -> V { + self.base.insert(value) } /// Takes the value out of the entry, and returns it. @@ -2932,16 +2229,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// /// assert_eq!(map.contains_key("poneyland"), false); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(self) -> V { - pop_internal(self.elem).1 - } - - /// Returns a key that was used for search. - /// - /// The key was retained for further use. - fn take_key(&mut self) -> Option<K> { - self.key.take() + self.base.remove() } /// Replaces the entry, returning the old key and value. The new key in the hash map will be @@ -2965,14 +2256,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// } /// /// ``` + #[inline] #[unstable(feature = "map_entry_replace", issue = "44286")] - pub fn replace_entry(mut self, value: V) -> (K, V) { - let (old_key, old_value) = self.elem.read_mut(); - - let old_key = mem::replace(old_key, self.key.unwrap()); - let old_value = mem::replace(old_value, value); - - (old_key, old_value) + pub fn replace_entry(self, value: V) -> (K, V) { + self.base.replace_entry(value) } /// Replaces the key in the hash map with the key used to create this entry. @@ -3000,10 +2287,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// } /// } /// ``` + #[inline] #[unstable(feature = "map_entry_replace", issue = "44286")] - pub fn replace_key(mut self) -> K { - let (old_key, _) = self.elem.read_mut(); - mem::replace(old_key, self.key.unwrap()) + pub fn replace_key(self) -> K { + self.base.replace_key() } } @@ -3019,9 +2306,10 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// let mut map: HashMap<&str, u32> = HashMap::new(); /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); /// ``` + #[inline] #[stable(feature = "map_entry_keys", since = "1.10.0")] pub fn key(&self) -> &K { - &self.key + self.base.key() } /// Take ownership of the key. @@ -3038,9 +2326,10 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// v.into_key(); /// } /// ``` + #[inline] #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] pub fn into_key(self) -> K { - self.key + self.base.into_key() } /// Sets the value of the entry with the VacantEntry's key, @@ -3059,30 +2348,18 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// } /// assert_eq!(map["poneyland"], 37); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(self, value: V) -> &'a mut V { - let b = match self.elem { - NeqElem(mut bucket, disp) => { - if disp >= DISPLACEMENT_THRESHOLD { - bucket.table_mut().set_tag(true); - } - robin_hood(bucket, disp, self.hash, self.key, value) - }, - NoElem(mut bucket, disp) => { - if disp >= DISPLACEMENT_THRESHOLD { - bucket.table_mut().set_tag(true); - } - bucket.put(self.hash, self.key, value) - }, - }; - b.into_mut_refs().1 + self.base.insert(value) } } #[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> { let mut map = HashMap::with_hasher(Default::default()); @@ -3093,35 +2370,26 @@ 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, { + #[inline] fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) { - // Keys may be already present or show multiple times in the iterator. - // Reserve the entire hint lower bound if the map is empty. - // Otherwise reserve half the hint (rounded up), so the map - // will only resize twice in the worst case. - let iter = iter.into_iter(); - let reserve = if self.is_empty() { - iter.size_hint().0 - } else { - (iter.size_hint().0 + 1) / 2 - }; - self.reserve(reserve); - for (k, v) in iter { - self.insert(k, v); - } + self.base.extend(iter) } } #[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, { + #[inline] fn extend<T: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: T) { - self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); + self.base.extend(iter) } } @@ -3226,7 +2494,10 @@ impl DefaultHasher { #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] impl Default for DefaultHasher { - /// Creates a new `DefaultHasher` using [`new`][DefaultHasher::new]. + // FIXME: here should link `new` to [DefaultHasher::new], but it occurs intra-doc link + // resolution failure when re-exporting libstd items. When #56922 fixed, + // link `new` to [DefaultHasher::new] again. + /// Creates a new `DefaultHasher` using `new`. /// See its documentation for more. fn default() -> DefaultHasher { DefaultHasher::new() @@ -3257,41 +2528,34 @@ impl Default for RandomState { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for RandomState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("RandomState { .. }") } } -impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> - where K: Eq + Hash + Borrow<Q>, - S: BuildHasher, - Q: Eq + Hash -{ - type Key = K; - - #[inline] - fn get(&self, key: &Q) -> Option<&K> { - self.search(key).map(|bucket| bucket.into_refs().0) +#[inline] +fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K, V> { + match raw { + base::RustcEntry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), + base::RustcEntry::Vacant(base) => Entry::Vacant(VacantEntry { base }), } +} - fn take(&mut self, key: &Q) -> Option<K> { - self.search_mut(key).map(|bucket| pop_internal(bucket).0) +#[inline] +fn map_collection_alloc_err(err: hashbrown::CollectionAllocErr) -> CollectionAllocErr { + match err { + hashbrown::CollectionAllocErr::CapacityOverflow => CollectionAllocErr::CapacityOverflow, + hashbrown::CollectionAllocErr::AllocErr => CollectionAllocErr::AllocErr, } +} - #[inline] - fn replace(&mut self, key: K) -> Option<K> { - self.reserve(1); - - match self.entry(key) { - Occupied(mut occupied) => { - let key = occupied.take_key().unwrap(); - Some(mem::replace(occupied.elem.read_mut().0, key)) - } - Vacant(vacant) => { - vacant.insert(()); - None - } - } +#[inline] +fn map_raw_entry<'a, K: 'a, V: 'a, S: 'a>( + raw: base::RawEntryMut<'a, K, V, S>, +) -> RawEntryMut<'a, K, V, S> { + match raw { + base::RawEntryMut::Occupied(base) => RawEntryMut::Occupied(RawOccupiedEntryMut { base }), + base::RawEntryMut::Vacant(base) => RawEntryMut::Vacant(RawVacantEntryMut { base }), } } @@ -3327,23 +2591,29 @@ fn assert_covariance() { 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> { + fn drain<'new>( + d: Drain<'static, &'static str, &'static str>, + ) -> Drain<'new, &'new str, &'new str> { d } } #[cfg(test)] mod test_map { - use super::HashMap; use super::Entry::{Occupied, Vacant}; + use super::HashMap; use super::RandomState; use crate::cell::RefCell; use rand::{thread_rng, Rng}; use realstd::collections::CollectionAllocErr::*; - use realstd::mem::size_of; use realstd::usize; + // https://github.com/rust-lang/rust/issues/62301 + fn _assert_hashmap_is_unwind_safe() { + fn assert_unwind_safe<T: crate::panic::UnwindSafe>() {} + assert_unwind_safe::<HashMap<(), crate::cell::UnsafeCell<()>>>(); + } + #[test] fn test_zero_capacities() { type HM = HashMap<i32, i32>; @@ -3478,19 +2748,19 @@ mod test_map { DROP_VECTOR.with(|v| { assert_eq!(v.borrow()[i], 1); - assert_eq!(v.borrow()[i+100], 1); + assert_eq!(v.borrow()[i + 100], 1); }); } DROP_VECTOR.with(|v| { for i in 0..50 { assert_eq!(v.borrow()[i], 0); - assert_eq!(v.borrow()[i+100], 0); + assert_eq!(v.borrow()[i + 100], 0); } for i in 50..100 { assert_eq!(v.borrow()[i], 1); - assert_eq!(v.borrow()[i+100], 1); + assert_eq!(v.borrow()[i + 100], 1); } }); } @@ -3547,13 +2817,9 @@ 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); @@ -3741,7 +3007,7 @@ mod test_map { fn test_iterate() { let mut m = HashMap::with_capacity(4); for i in 0..32 { - assert!(m.insert(i, i*2).is_none()); + assert!(m.insert(i, i * 2).is_none()); } assert_eq!(m.len(), 32); @@ -3829,86 +3095,11 @@ mod test_map { let map_str = format!("{:?}", map); - assert!(map_str == "{1: 2, 3: 4}" || - map_str == "{3: 4, 1: 2}"); + assert!(map_str == "{1: 2, 3: 4}" || map_str == "{3: 4, 1: 2}"); assert_eq!(format!("{:?}", empty), "{}"); } #[test] - fn test_expand() { - let mut m = HashMap::new(); - - assert_eq!(m.len(), 0); - assert!(m.is_empty()); - - let mut i = 0; - let old_raw_cap = m.raw_capacity(); - while old_raw_cap == m.raw_capacity() { - m.insert(i, i); - i += 1; - } - - assert_eq!(m.len(), i); - assert!(!m.is_empty()); - } - - #[test] - fn test_behavior_resize_policy() { - let mut m = HashMap::new(); - - assert_eq!(m.len(), 0); - assert_eq!(m.raw_capacity(), 0); - assert!(m.is_empty()); - - m.insert(0, 0); - m.remove(&0); - assert!(m.is_empty()); - let initial_raw_cap = m.raw_capacity(); - m.reserve(initial_raw_cap); - let raw_cap = m.raw_capacity(); - - assert_eq!(raw_cap, initial_raw_cap * 2); - - let mut i = 0; - for _ in 0..raw_cap * 3 / 4 { - m.insert(i, i); - i += 1; - } - // three quarters full - - assert_eq!(m.len(), i); - assert_eq!(m.raw_capacity(), raw_cap); - - for _ in 0..raw_cap / 4 { - m.insert(i, i); - i += 1; - } - // half full - - let new_raw_cap = m.raw_capacity(); - assert_eq!(new_raw_cap, raw_cap * 2); - - for _ in 0..raw_cap / 2 - 1 { - i -= 1; - m.remove(&i); - assert_eq!(m.raw_capacity(), new_raw_cap); - } - // A little more than one quarter full. - m.shrink_to_fit(); - assert_eq!(m.raw_capacity(), raw_cap); - // again, a little more than half full - for _ in 0..raw_cap / 2 - 1 { - i -= 1; - m.remove(&i); - } - m.shrink_to_fit(); - - assert_eq!(m.len(), i); - assert!(!m.is_empty()); - assert_eq!(m.raw_capacity(), initial_raw_cap); - } - - #[test] fn test_reserve_shrink_to_fit() { let mut m = HashMap::new(); m.insert(0, 0); @@ -3947,13 +3138,15 @@ mod test_map { #[test] fn test_from_iter() { - let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; + let xs = [(1, 1), (2, 2), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]; let map: HashMap<_, _> = xs.iter().cloned().collect(); for &(k, v) in &xs { assert_eq!(map.get(&k), Some(&v)); } + + assert_eq!(map.iter().len(), xs.len() - 1); } #[test] @@ -4048,7 +3241,6 @@ mod test_map { assert_eq!(map.get(&1).unwrap(), &100); assert_eq!(map.len(), 6); - // Existing key (update) match map.entry(2) { Vacant(_) => unreachable!(), @@ -4071,7 +3263,6 @@ mod test_map { assert_eq!(map.get(&3), None); assert_eq!(map.len(), 5); - // Inexistent key (insert) match map.entry(10) { Occupied(_) => unreachable!(), @@ -4086,11 +3277,10 @@ mod test_map { #[test] fn test_entry_take_doesnt_corrupt() { #![allow(deprecated)] //rand - // Test for #19292 + // Test for #19292 fn check(m: &HashMap<i32, ()>) { for k in m.keys() { - assert!(m.contains_key(k), - "{} is in keys() but not in the map?", k); + assert!(m.contains_key(k), "{} is in keys() but not in the map?", k); } } @@ -4195,7 +3385,7 @@ mod test_map { #[test] fn test_retain() { - let mut map: HashMap<i32, i32> = (0..100).map(|x|(x, x*10)).collect(); + let mut map: HashMap<i32, i32> = (0..100).map(|x| (x, x * 10)).collect(); map.retain(|&k, _| k % 2 == 0); assert_eq!(map.len(), 50); @@ -4205,50 +3395,19 @@ mod test_map { } #[test] - fn test_adaptive() { - const TEST_LEN: usize = 5000; - // by cloning we get maps with the same hasher seed - let mut first = HashMap::new(); - let mut second = first.clone(); - first.extend((0..TEST_LEN).map(|i| (i, i))); - second.extend((TEST_LEN..TEST_LEN * 2).map(|i| (i, i))); - - for (&k, &v) in &second { - let prev_cap = first.capacity(); - let expect_grow = first.len() == prev_cap; - first.insert(k, v); - if !expect_grow && first.capacity() != prev_cap { - return; - } - } - panic!("Adaptive early resize failed"); - } - - #[test] fn test_try_reserve() { - - let mut empty_bytes: HashMap<u8,u8> = HashMap::new(); + let mut empty_bytes: HashMap<u8, u8> = HashMap::new(); const MAX_USIZE: usize = usize::MAX; - // HashMap and RawTables use complicated size calculations - // hashes_size is sizeof(HashUint) * capacity; - // pairs_size is sizeof((K. V)) * capacity; - // alignment_hashes_size is 8 - // alignment_pairs size is 4 - let size_of_multiplier = (size_of::<usize>() + size_of::<(u8, u8)>()).next_power_of_two(); - // The following formula is used to calculate the new capacity - let max_no_ovf = ((MAX_USIZE / 11) * 10) / size_of_multiplier - 1; - if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) { - } else { panic!("usize::MAX should trigger an overflow!"); } + } else { + panic!("usize::MAX should trigger an overflow!"); + } - if size_of::<usize>() < 8 { - if let Err(CapacityOverflow) = empty_bytes.try_reserve(max_no_ovf) { - } else { panic!("isize::MAX + 1 should trigger a CapacityOverflow!") } + if let Err(AllocErr) = empty_bytes.try_reserve(MAX_USIZE / 8) { } else { - if let Err(AllocErr) = empty_bytes.try_reserve(max_no_ovf) { - } else { panic!("isize::MAX + 1 should trigger an OOM!") } + panic!("usize::MAX / 8 should trigger an OOM!") } } @@ -4278,9 +3437,14 @@ mod test_map { } let hash1 = compute_hash(&map, 1); assert_eq!(map.raw_entry().from_key(&1).unwrap(), (&1, &100)); - assert_eq!(map.raw_entry().from_hash(hash1, |k| *k == 1).unwrap(), (&1, &100)); - assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash1, &1).unwrap(), (&1, &100)); - assert_eq!(map.raw_entry().search_bucket(hash1, |k| *k == 1).unwrap(), (&1, &100)); + assert_eq!( + map.raw_entry().from_hash(hash1, |k| *k == 1).unwrap(), + (&1, &100) + ); + assert_eq!( + map.raw_entry().from_key_hashed_nocheck(hash1, &1).unwrap(), + (&1, &100) + ); assert_eq!(map.len(), 6); // Existing key (update) @@ -4294,9 +3458,14 @@ mod test_map { } let hash2 = compute_hash(&map, 2); assert_eq!(map.raw_entry().from_key(&2).unwrap(), (&2, &200)); - assert_eq!(map.raw_entry().from_hash(hash2, |k| *k == 2).unwrap(), (&2, &200)); - assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash2, &2).unwrap(), (&2, &200)); - assert_eq!(map.raw_entry().search_bucket(hash2, |k| *k == 2).unwrap(), (&2, &200)); + assert_eq!( + map.raw_entry().from_hash(hash2, |k| *k == 2).unwrap(), + (&2, &200) + ); + assert_eq!( + map.raw_entry().from_key_hashed_nocheck(hash2, &2).unwrap(), + (&2, &200) + ); assert_eq!(map.len(), 6); // Existing key (take) @@ -4310,10 +3479,8 @@ mod test_map { assert_eq!(map.raw_entry().from_key(&3), None); assert_eq!(map.raw_entry().from_hash(hash3, |k| *k == 3), None); assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash3, &3), None); - assert_eq!(map.raw_entry().search_bucket(hash3, |k| *k == 3), None); assert_eq!(map.len(), 5); - // Nonexistent key (insert) match map.raw_entry_mut().from_key(&10) { Occupied(_) => unreachable!(), @@ -4333,7 +3500,6 @@ mod test_map { assert_eq!(map.raw_entry().from_key(&k), kv); assert_eq!(map.raw_entry().from_hash(hash, |q| *q == k), kv); assert_eq!(map.raw_entry().from_key_hashed_nocheck(hash, &k), kv); - assert_eq!(map.raw_entry().search_bucket(hash, |q| *q == k), kv); match map.raw_entry_mut().from_key(&k) { Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), @@ -4347,10 +3513,6 @@ mod test_map { Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), Vacant(_) => assert_eq!(v, None), } - match map.raw_entry_mut().search_bucket(hash, |q| *q == k) { - Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv), - Vacant(_) => assert_eq!(v, None), - } } } diff --git a/src/libstd/collections/hash/mod.rs b/src/libstd/collections/hash/mod.rs index 0d1bbb590db..a6d89a4d32a 100644 --- a/src/libstd/collections/hash/mod.rs +++ b/src/libstd/collections/hash/mod.rs @@ -1,14 +1,5 @@ //! Unordered containers, implemented as hash-tables mod bench; -mod table; pub mod map; pub mod set; - -trait Recover<Q: ?Sized> { - type Key; - - fn get(&self, key: &Q) -> Option<&Self::Key>; - fn take(&mut self, key: &Q) -> Option<Self::Key>; - fn replace(&mut self, key: Self::Key) -> Option<Self::Key>; -} diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index f2111f2d9e0..d243412405a 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -1,14 +1,14 @@ use crate::borrow::Borrow; +use crate::collections::CollectionAllocErr; use crate::fmt; use crate::hash::{Hash, BuildHasher}; use crate::iter::{Chain, FromIterator, FusedIterator}; use crate::ops::{BitOr, BitAnd, BitXor, Sub}; -use super::Recover; use super::map::{self, HashMap, Keys, RandomState}; // Future Optimization (FIXME!) -// ============================= +// ============================ // // Iteration over zero sized values is a noop. There is no need // for `bucket.val` in the case of HashSet. I suppose we would need HKT @@ -181,8 +181,9 @@ impl<T, S> HashSet<T, S> { /// println!("{}", x); /// } /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<T> { + pub fn iter(&self) -> Iter<'_, T> { Iter { iter: self.map.keys() } } @@ -198,6 +199,7 @@ impl<T, S> HashSet<T, S> { /// v.insert(1); /// assert_eq!(v.len(), 1); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { self.map.len() @@ -215,6 +217,7 @@ impl<T, S> HashSet<T, S> { /// v.insert(1); /// assert!(!v.is_empty()); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn is_empty(&self) -> bool { self.map.is_empty() @@ -239,7 +242,7 @@ impl<T, S> HashSet<T, S> { /// ``` #[inline] #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<T> { + pub fn drain(&mut self) -> Drain<'_, T> { Drain { iter: self.map.drain() } } @@ -255,6 +258,7 @@ impl<T, S> HashSet<T, S> { /// v.clear(); /// assert!(v.is_empty()); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn clear(&mut self) { self.map.clear() @@ -332,6 +336,7 @@ impl<T, S> HashSet<T, S> /// let set: HashSet<i32> = HashSet::with_hasher(hasher); /// let hasher: &RandomState = set.hasher(); /// ``` + #[inline] #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] pub fn hasher(&self) -> &S { self.map.hasher() @@ -353,11 +358,35 @@ impl<T, S> HashSet<T, S> /// set.reserve(10); /// assert!(set.capacity() >= 10); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { self.map.reserve(additional) } + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `HashSet<K,V>`. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::HashSet; + /// let mut set: HashSet<i32> = HashSet::new(); + /// set.try_reserve(10).expect("why is the test harness OOMing on 10 bytes?"); + /// ``` + #[inline] + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.map.try_reserve(additional) + } + /// Shrinks the capacity of the set as much as possible. It will drop /// down as much as possible while maintaining the internal rules /// and possibly leaving some space in accordance with the resize policy. @@ -374,6 +403,7 @@ impl<T, S> HashSet<T, S> /// set.shrink_to_fit(); /// assert!(set.capacity() >= 2); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn shrink_to_fit(&mut self) { self.map.shrink_to_fit() @@ -430,6 +460,7 @@ impl<T, S> HashSet<T, S> /// let diff: HashSet<_> = b.difference(&a).collect(); /// assert_eq!(diff, [4].iter().collect()); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn difference<'a>(&'a self, other: &'a HashSet<T, S>) -> Difference<'a, T, S> { Difference { @@ -459,6 +490,7 @@ impl<T, S> HashSet<T, S> /// assert_eq!(diff1, diff2); /// assert_eq!(diff1, [1, 4].iter().collect()); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn symmetric_difference<'a>(&'a self, other: &'a HashSet<T, S>) @@ -484,6 +516,7 @@ impl<T, S> HashSet<T, S> /// let intersection: HashSet<_> = a.intersection(&b).collect(); /// assert_eq!(intersection, [2, 3].iter().collect()); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn intersection<'a>(&'a self, other: &'a HashSet<T, S>) -> Intersection<'a, T, S> { if self.len() <= other.len() { @@ -517,6 +550,7 @@ impl<T, S> HashSet<T, S> /// let union: HashSet<_> = a.union(&b).collect(); /// assert_eq!(union, [1, 2, 3, 4].iter().collect()); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn union<'a>(&'a self, other: &'a HashSet<T, S>) -> Union<'a, T, S> { if self.len() <= other.len() { @@ -548,6 +582,7 @@ impl<T, S> HashSet<T, S> /// /// [`Eq`]: ../../std/cmp/trait.Eq.html /// [`Hash`]: ../../std/hash/trait.Hash.html + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool where T: Borrow<Q>, @@ -574,12 +609,69 @@ impl<T, S> HashSet<T, S> /// /// [`Eq`]: ../../std/cmp/trait.Eq.html /// [`Hash`]: ../../std/hash/trait.Hash.html + #[inline] #[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 { - Recover::get(&self.map, value) + self.map.get_key_value(value).map(|(k, _)| k) + } + + /// Inserts the given `value` into the set if it is not present, then + /// returns a reference to the value in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// + /// let mut set: HashSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.len(), 3); + /// assert_eq!(set.get_or_insert(2), &2); + /// assert_eq!(set.get_or_insert(100), &100); + /// assert_eq!(set.len(), 4); // 100 was inserted + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn get_or_insert(&mut self, value: T) -> &T { + // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with + // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. + self.map.raw_entry_mut().from_key(&value).or_insert(value, ()).0 + } + + /// Inserts a value computed from `f` into the set if the given `value` is + /// not present, then returns a reference to the value in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(hash_set_entry)] + /// + /// use std::collections::HashSet; + /// + /// let mut set: HashSet<String> = ["cat", "dog", "horse"] + /// .iter().map(|&pet| pet.to_owned()).collect(); + /// + /// assert_eq!(set.len(), 3); + /// for &pet in &["cat", "dog", "fish"] { + /// let value = set.get_or_insert_with(pet, str::to_owned); + /// assert_eq!(value, pet); + /// } + /// assert_eq!(set.len(), 4); // a new "fish" was inserted + /// ``` + #[inline] + #[unstable(feature = "hash_set_entry", issue = "60896")] + pub fn get_or_insert_with<Q: ?Sized, F>(&mut self, value: &Q, f: F) -> &T + where T: Borrow<Q>, + Q: Hash + Eq, + F: FnOnce(&Q) -> T + { + // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with + // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. + self.map.raw_entry_mut().from_key(value).or_insert_with(|| (f(value), ())).0 } /// Returns `true` if `self` has no elements in common with `other`. @@ -627,7 +719,11 @@ impl<T, S> HashSet<T, S> /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn is_subset(&self, other: &HashSet<T, S>) -> bool { - self.iter().all(|v| other.contains(v)) + if self.len() <= other.len() { + self.iter().all(|v| other.contains(v)) + } else { + false + } } /// Returns `true` if the set is a superset of another, @@ -673,6 +769,7 @@ impl<T, S> HashSet<T, S> /// assert_eq!(set.insert(2), false); /// assert_eq!(set.len(), 1); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(&mut self, value: T) -> bool { self.map.insert(value, ()).is_none() @@ -693,9 +790,16 @@ impl<T, S> HashSet<T, S> /// set.replace(Vec::with_capacity(10)); /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); /// ``` + #[inline] #[stable(feature = "set_recovery", since = "1.9.0")] pub fn replace(&mut self, value: T) -> Option<T> { - Recover::replace(&mut self.map, value) + match self.map.entry(value) { + map::Entry::Occupied(occupied) => Some(occupied.replace_key()), + map::Entry::Vacant(vacant) => { + vacant.insert(()); + None + } + } } /// Removes a value from the set. Returns whether the value was @@ -719,6 +823,7 @@ impl<T, S> HashSet<T, S> /// /// [`Eq`]: ../../std/cmp/trait.Eq.html /// [`Hash`]: ../../std/hash/trait.Hash.html + #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool where T: Borrow<Q>, @@ -745,12 +850,13 @@ impl<T, S> HashSet<T, S> /// /// [`Eq`]: ../../std/cmp/trait.Eq.html /// [`Hash`]: ../../std/hash/trait.Hash.html + #[inline] #[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 { - Recover::take(&mut self.map, value) + self.map.remove_entry(value).map(|(k, _)| k) } /// Retains only the elements specified by the predicate. @@ -801,7 +907,7 @@ impl<T, S> fmt::Debug for HashSet<T, S> where T: Eq + Hash + fmt::Debug, S: BuildHasher { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_set().entries(self.iter()).finish() } } @@ -811,6 +917,7 @@ impl<T, S> FromIterator<T> for HashSet<T, S> where T: Eq + Hash, S: BuildHasher + Default { + #[inline] fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> HashSet<T, S> { let mut set = HashSet::with_hasher(Default::default()); set.extend(iter); @@ -823,6 +930,7 @@ impl<T, S> Extend<T> for HashSet<T, S> where T: Eq + Hash, S: BuildHasher { + #[inline] fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { self.map.extend(iter.into_iter().map(|k| (k, ()))); } @@ -833,6 +941,7 @@ impl<'a, T, S> Extend<&'a T> for HashSet<T, S> where T: 'a + Eq + Hash + Copy, S: BuildHasher { + #[inline] fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } @@ -844,6 +953,7 @@ impl<T, S> Default for HashSet<T, S> S: BuildHasher + Default { /// Creates an empty `HashSet<T, S>` with the `Default` value for the hasher. + #[inline] fn default() -> HashSet<T, S> { HashSet { map: HashMap::default() } } @@ -1072,6 +1182,7 @@ impl<'a, T, S> IntoIterator for &'a HashSet<T, S> { type Item = &'a T; type IntoIter = Iter<'a, T>; + #[inline] fn into_iter(self) -> Iter<'a, T> { self.iter() } @@ -1102,6 +1213,7 @@ impl<T, S> IntoIterator for HashSet<T, S> { /// println!("{}", x); /// } /// ``` + #[inline] fn into_iter(self) -> IntoIter<T> { IntoIter { iter: self.map.into_iter() } } @@ -1109,6 +1221,7 @@ impl<T, S> IntoIterator for HashSet<T, S> { #[stable(feature = "rust1", since = "1.0.0")] impl<K> Clone for Iter<'_, K> { + #[inline] fn clone(&self) -> Self { Iter { iter: self.iter.clone() } } @@ -1117,15 +1230,18 @@ impl<K> Clone for Iter<'_, K> { impl<'a, K> Iterator for Iter<'a, K> { type Item = &'a K; + #[inline] fn next(&mut self) -> Option<&'a K> { self.iter.next() } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } } #[stable(feature = "rust1", since = "1.0.0")] impl<K> ExactSizeIterator for Iter<'_, K> { + #[inline] fn len(&self) -> usize { self.iter.len() } @@ -1135,7 +1251,7 @@ impl<K> FusedIterator for Iter<'_, K> {} #[stable(feature = "std_debug", since = "1.16.0")] impl<K: fmt::Debug> fmt::Debug for Iter<'_, K> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() } } @@ -1144,15 +1260,18 @@ impl<K: fmt::Debug> fmt::Debug for Iter<'_, K> { impl<K> Iterator for IntoIter<K> { type Item = K; + #[inline] fn next(&mut self) -> Option<K> { self.iter.next().map(|(k, _)| k) } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } } #[stable(feature = "rust1", since = "1.0.0")] impl<K> ExactSizeIterator for IntoIter<K> { + #[inline] fn len(&self) -> usize { self.iter.len() } @@ -1162,9 +1281,8 @@ impl<K> FusedIterator for IntoIter<K> {} #[stable(feature = "std_debug", since = "1.16.0")] impl<K: fmt::Debug> fmt::Debug for IntoIter<K> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let entries_iter = self.iter - .inner .iter() .map(|(k, _)| k); f.debug_list().entries(entries_iter).finish() @@ -1175,15 +1293,18 @@ impl<K: fmt::Debug> fmt::Debug for IntoIter<K> { impl<'a, K> Iterator for Drain<'a, K> { type Item = K; + #[inline] fn next(&mut self) -> Option<K> { self.iter.next().map(|(k, _)| k) } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } } #[stable(feature = "rust1", since = "1.0.0")] impl<K> ExactSizeIterator for Drain<'_, K> { + #[inline] fn len(&self) -> usize { self.iter.len() } @@ -1193,9 +1314,8 @@ impl<K> FusedIterator for Drain<'_, K> {} #[stable(feature = "std_debug", since = "1.16.0")] impl<K: fmt::Debug> fmt::Debug for Drain<'_, K> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let entries_iter = self.iter - .inner .iter() .map(|(k, _)| k); f.debug_list().entries(entries_iter).finish() @@ -1204,6 +1324,7 @@ impl<K: fmt::Debug> fmt::Debug for Drain<'_, K> { #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> Clone for Intersection<'_, T, S> { + #[inline] fn clone(&self) -> Self { Intersection { iter: self.iter.clone(), ..*self } } @@ -1216,6 +1337,7 @@ impl<'a, T, S> Iterator for Intersection<'a, T, S> { type Item = &'a T; + #[inline] fn next(&mut self) -> Option<&'a T> { loop { let elt = self.iter.next()?; @@ -1225,6 +1347,7 @@ impl<'a, T, S> Iterator for Intersection<'a, T, S> } } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { let (_, upper) = self.iter.size_hint(); (0, upper) @@ -1236,7 +1359,7 @@ impl<T, S> fmt::Debug for Intersection<'_, T, S> where T: fmt::Debug + Eq + Hash, S: BuildHasher { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() } } @@ -1250,6 +1373,7 @@ impl<T, S> FusedIterator for Intersection<'_, T, S> #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> Clone for Difference<'_, T, S> { + #[inline] fn clone(&self) -> Self { Difference { iter: self.iter.clone(), ..*self } } @@ -1262,6 +1386,7 @@ impl<'a, T, S> Iterator for Difference<'a, T, S> { type Item = &'a T; + #[inline] fn next(&mut self) -> Option<&'a T> { loop { let elt = self.iter.next()?; @@ -1271,6 +1396,7 @@ impl<'a, T, S> Iterator for Difference<'a, T, S> } } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { let (_, upper) = self.iter.size_hint(); (0, upper) @@ -1289,13 +1415,14 @@ impl<T, S> fmt::Debug for Difference<'_, T, S> where T: fmt::Debug + Eq + Hash, S: BuildHasher { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() } } #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> Clone for SymmetricDifference<'_, T, S> { + #[inline] fn clone(&self) -> Self { SymmetricDifference { iter: self.iter.clone() } } @@ -1308,9 +1435,11 @@ impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S> { type Item = &'a T; + #[inline] fn next(&mut self) -> Option<&'a T> { self.iter.next() } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } @@ -1328,13 +1457,14 @@ impl<T, S> fmt::Debug for SymmetricDifference<'_, T, S> where T: fmt::Debug + Eq + Hash, S: BuildHasher { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() } } #[stable(feature = "rust1", since = "1.0.0")] impl<T, S> Clone for Union<'_, T, S> { + #[inline] fn clone(&self) -> Self { Union { iter: self.iter.clone() } } @@ -1352,7 +1482,7 @@ impl<T, S> fmt::Debug for Union<'_, T, S> where T: fmt::Debug + Eq + Hash, S: BuildHasher { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() } } @@ -1364,9 +1494,11 @@ impl<'a, T, S> Iterator for Union<'a, T, S> { type Item = &'a T; + #[inline] fn next(&mut self) -> Option<&'a T> { self.iter.next() } + #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } @@ -1650,13 +1782,15 @@ mod test_set { #[test] fn test_from_iter() { - let xs = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let xs = [1, 2, 2, 3, 4, 5, 6, 7, 8, 9]; let set: HashSet<_> = xs.iter().cloned().collect(); for x in &xs { assert!(set.contains(x)); } + + assert_eq!(set.iter().len(), xs.len() - 1); } #[test] diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs deleted file mode 100644 index 31e7a693135..00000000000 --- a/src/libstd/collections/hash/table.rs +++ /dev/null @@ -1,1131 +0,0 @@ -use crate::alloc::{Global, Alloc, Layout, LayoutErr, handle_alloc_error}; -use crate::collections::CollectionAllocErr; -use crate::hash::{BuildHasher, Hash, Hasher}; -use crate::marker; -use crate::mem::{self, size_of, needs_drop}; -use crate::ops::{Deref, DerefMut}; -use crate::ptr::{self, Unique, NonNull}; -use crate::hint; - -use self::BucketState::*; - -/// 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; -const EMPTY: usize = 1; - -/// Special `Unique<HashUint>` that uses the lower bit of the pointer -/// to expose a boolean tag. -/// Note: when the pointer is initialized to EMPTY `.ptr()` will return -/// null and the tag functions shouldn't be used. -struct TaggedHashUintPtr(Unique<HashUint>); - -impl TaggedHashUintPtr { - #[inline] - unsafe fn new(ptr: *mut HashUint) -> Self { - debug_assert!(ptr as usize & 1 == 0 || ptr as usize == EMPTY as usize); - TaggedHashUintPtr(Unique::new_unchecked(ptr)) - } - - #[inline] - fn set_tag(&mut self, value: bool) { - let mut usize_ptr = self.0.as_ptr() as usize; - unsafe { - if value { - usize_ptr |= 1; - } else { - usize_ptr &= !1; - } - self.0 = Unique::new_unchecked(usize_ptr as *mut HashUint) - } - } - - #[inline] - fn tag(&self) -> bool { - (self.0.as_ptr() as usize) & 1 == 1 - } - - #[inline] - fn ptr(&self) -> *mut HashUint { - (self.0.as_ptr() as usize & !1) as *mut HashUint - } -} - -/// The raw hashtable, providing safe-ish access to the unzipped and highly -/// optimized arrays of hashes, and key-value pairs. -/// -/// 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: -/// -/// - if `t.hashes[i] == EMPTY_BUCKET`, then `Bucket::at_index(&t, i).raw` -/// points to 'undefined' contents. Don't read from it. This invariant is -/// enforced outside this module with the `EmptyBucket`, `FullBucket`, -/// and `SafeHash` types. -/// -/// - An `EmptyBucket` is only constructed at an index with -/// a hash of EMPTY_BUCKET. -/// -/// - A `FullBucket` is only constructed at an index with a -/// non-EMPTY_BUCKET hash. -/// -/// - A `SafeHash` is only constructed for non-`EMPTY_BUCKET` hash. We get -/// around hashes of zero by changing them to 0x8000_0000_0000_0000, -/// which will likely map to the same bucket, while not being confused -/// with "empty". -/// -/// - Both "arrays represented by pointers" are the same length: -/// `capacity`. This is set at creation and never changes. The arrays -/// 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)>>`. -/// -/// The hashtable also exposes a special boolean tag. The tag defaults to false -/// when the RawTable is created and is accessible with the `tag` and `set_tag` -/// functions. -pub struct RawTable<K, V> { - capacity_mask: usize, - size: usize, - hashes: TaggedHashUintPtr, - - // 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. - marker: marker::PhantomData<(K, V)>, -} - -// An unsafe view of a RawTable bucket -// Valid indexes are within [0..table_capacity) -pub struct RawBucket<K, V> { - hash_start: *mut HashUint, - // We use *const to ensure covariance with respect to K and V - pair_start: *const (K, V), - idx: usize, - _marker: marker::PhantomData<(K, V)>, -} - -impl<K, V> Copy for RawBucket<K, V> {} -impl<K, V> Clone for RawBucket<K, V> { - fn clone(&self) -> RawBucket<K, V> { - *self - } -} - -pub struct Bucket<K, V, M> { - raw: RawBucket<K, V>, - table: M, -} - -impl<K, V, M: Copy> Copy for Bucket<K, V, M> {} -impl<K, V, M: Copy> Clone for Bucket<K, V, M> { - fn clone(&self) -> Bucket<K, V, M> { - *self - } -} - -pub struct EmptyBucket<K, V, M> { - raw: RawBucket<K, V>, - table: M, -} - -pub struct FullBucket<K, V, M> { - raw: RawBucket<K, V>, - table: M, -} - -pub type FullBucketMut<'table, K, V> = FullBucket<K, V, &'table mut RawTable<K, V>>; - -pub enum BucketState<K, V, M> { - Empty(EmptyBucket<K, V, M>), - Full(FullBucket<K, V, M>), -} - -// A GapThenFull encapsulates the state of two consecutive buckets at once. -// The first bucket, called the gap, is known to be empty. -// The second bucket is full. -pub struct GapThenFull<K, V, M> { - gap: EmptyBucket<K, V, ()>, - full: FullBucket<K, V, M>, -} - -/// A hash that is not zero, since we use a hash of zero to represent empty -/// buckets. -#[derive(PartialEq, Copy, Clone)] -pub struct SafeHash { - hash: HashUint, -} - -impl SafeHash { - /// Peek at the hash value, which is guaranteed to be non-zero. - #[inline(always)] - 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. -/// This function wraps up `hash_keyed` to be the only way outside this -/// module to generate a SafeHash. -pub fn make_hash<T: ?Sized, S>(hash_state: &S, t: &T) -> SafeHash - where T: Hash, - S: BuildHasher -{ - let mut state = hash_state.build_hasher(); - t.hash(&mut state); - SafeHash::new(state.finish()) -} - -// `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 `HashUint` with a different name, this is -// safe. -// -// This test ensures that a `SafeHash` really IS the same size as a -// `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_hash() { - assert_eq!(size_of::<SafeHash>(), size_of::<HashUint>()) -} - -// RawBucket methods are unsafe as it's possible to -// make a RawBucket point to invalid memory using safe code. -impl<K, V> RawBucket<K, V> { - unsafe fn hash(&self) -> *mut HashUint { - self.hash_start.add(self.idx) - } - unsafe fn pair(&self) -> *mut (K, V) { - self.pair_start.add(self.idx) as *mut (K, V) - } - unsafe fn hash_pair(&self) -> (*mut HashUint, *mut (K, V)) { - (self.hash(), self.pair()) - } -} - -// Buckets hold references to the table. -impl<K, V, M> FullBucket<K, V, M> { - /// Borrow a reference to the table. - pub fn table(&self) -> &M { - &self.table - } - /// Borrow a mutable reference to the table. - pub fn table_mut(&mut self) -> &mut M { - &mut self.table - } - /// Move out the reference to the table. - pub fn into_table(self) -> M { - self.table - } - /// Gets the raw index. - pub fn index(&self) -> usize { - self.raw.idx - } - /// Gets the raw bucket. - pub fn raw(&self) -> RawBucket<K, V> { - self.raw - } -} - -impl<K, V, M> EmptyBucket<K, V, M> { - /// Borrow a reference to the table. - pub fn table(&self) -> &M { - &self.table - } - /// Borrow a mutable reference to the table. - pub fn table_mut(&mut self) -> &mut M { - &mut self.table - } -} - -impl<K, V, M> Bucket<K, V, M> { - /// Gets the raw index. - pub fn index(&self) -> usize { - self.raw.idx - } - /// get the table. - pub fn into_table(self) -> M { - self.table - } -} - -impl<K, V, M> Deref for FullBucket<K, V, M> - where M: Deref<Target = RawTable<K, V>> -{ - type Target = RawTable<K, V>; - fn deref(&self) -> &RawTable<K, V> { - &self.table - } -} - -/// `Put` is implemented for types which provide access to a table and cannot be invalidated -/// by filling a bucket. A similar implementation for `Take` is possible. -pub trait Put<K, V> { - unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V>; -} - - -impl<K, V> Put<K, V> for &mut RawTable<K, V> { - unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> { - *self - } -} - -impl<K, V, M> Put<K, V> for Bucket<K, V, M> - where M: Put<K, V> -{ - unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> { - self.table.borrow_table_mut() - } -} - -impl<K, V, M> Put<K, V> for FullBucket<K, V, M> - where M: Put<K, V> -{ - unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> { - self.table.borrow_table_mut() - } -} - -impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> { - #[inline] - pub fn new(table: M, hash: SafeHash) -> Bucket<K, V, M> { - Bucket::at_index(table, hash.inspect() as usize) - } - - pub fn new_from(r: RawBucket<K, V>, t: M) - -> Bucket<K, V, M> - { - Bucket { - raw: r, - table: t, - } - } - - #[inline] - pub fn at_index(table: M, ib_index: usize) -> Bucket<K, V, M> { - // if capacity is 0, then the RawBucket will be populated with bogus pointers. - // This is an uncommon case though, so avoid it in release builds. - debug_assert!(table.capacity() > 0, - "Table should have capacity at this point"); - let ib_index = ib_index & table.capacity_mask; - Bucket { - raw: table.raw_bucket_at(ib_index), - table, - } - } - - pub fn first(table: M) -> Bucket<K, V, M> { - Bucket { - raw: table.raw_bucket_at(0), - table, - } - } - - // "So a few of the first shall be last: for many be called, - // but few chosen." - // - // We'll most likely encounter a few buckets at the beginning that - // have their initial buckets near the end of the table. They were - // placed at the beginning as the probe wrapped around the table - // during insertion. We must skip forward to a bucket that won't - // get reinserted too early and won't unfairly steal others spot. - // This eliminates the need for robin hood. - pub fn head_bucket(table: M) -> Bucket<K, V, M> { - let mut bucket = Bucket::first(table); - - loop { - bucket = match bucket.peek() { - Full(full) => { - if full.displacement() == 0 { - // This bucket occupies its ideal spot. - // It indicates the start of another "cluster". - bucket = full.into_bucket(); - break; - } - // Leaving this bucket in the last cluster for later. - full.into_bucket() - } - Empty(b) => { - // Encountered a hole between clusters. - b.into_bucket() - } - }; - bucket.next(); - } - bucket - } - - /// Reads a bucket at a given index, returning an enum indicating whether - /// it's initialized or not. You need to match on this enum to get - /// the appropriate types to call most of the other functions in - /// this module. - pub fn peek(self) -> BucketState<K, V, M> { - match unsafe { *self.raw.hash() } { - EMPTY_BUCKET => { - Empty(EmptyBucket { - raw: self.raw, - table: self.table, - }) - } - _ => { - Full(FullBucket { - raw: self.raw, - table: self.table, - }) - } - } - } - - /// Modifies the bucket in place to make it point to the next slot. - pub fn next(&mut self) { - self.raw.idx = self.raw.idx.wrapping_add(1) & self.table.capacity_mask; - } - - /// Modifies the bucket in place to make it point to the previous slot. - pub fn prev(&mut self) { - self.raw.idx = self.raw.idx.wrapping_sub(1) & self.table.capacity_mask; - } -} - -impl<K, V, M: Deref<Target = RawTable<K, V>>> EmptyBucket<K, V, M> { - #[inline] - pub fn next(self) -> Bucket<K, V, M> { - let mut bucket = self.into_bucket(); - bucket.next(); - bucket - } - - #[inline] - pub fn into_bucket(self) -> Bucket<K, V, M> { - Bucket { - raw: self.raw, - table: self.table, - } - } - - pub fn gap_peek(self) -> Result<GapThenFull<K, V, M>, Bucket<K, V, M>> { - let gap = EmptyBucket { - raw: self.raw, - table: (), - }; - - match self.next().peek() { - Full(bucket) => { - Ok(GapThenFull { - gap, - full: bucket, - }) - } - Empty(e) => Err(e.into_bucket()), - } - } -} - -impl<K, V, M> EmptyBucket<K, V, M> - where M: Put<K, V> -{ - /// Puts given key and value pair, along with the key's hash, - /// into this bucket in the hashtable. Note how `self` is 'moved' into - /// this function, because this slot will no longer be empty when - /// we return! A `FullBucket` is returned for later use, pointing to - /// the newly-filled slot in the hashtable. - /// - /// Use `make_hash` to construct a `SafeHash` to pass to this function. - 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.pair(), (key, value)); - - self.table.borrow_table_mut().size += 1; - } - - FullBucket { - raw: self.raw, - table: self.table, - } - } -} - -impl<K, V, M: Deref<Target = RawTable<K, V>>> FullBucket<K, V, M> { - #[inline] - pub fn next(self) -> Bucket<K, V, M> { - let mut bucket = self.into_bucket(); - bucket.next(); - bucket - } - - #[inline] - pub fn into_bucket(self) -> Bucket<K, V, M> { - Bucket { - raw: self.raw, - table: self.table, - } - } - - /// Duplicates the current position. This can be useful for operations - /// on two or more buckets. - pub fn stash(self) -> FullBucket<K, V, Self> { - FullBucket { - raw: self.raw, - table: self, - } - } - - /// Gets the distance between this bucket and the 'ideal' location - /// as determined by the key's hash stored in it. - /// - /// In the cited blog posts above, this is called the "distance to - /// initial bucket", or DIB. Also known as "probe count". - pub fn displacement(&self) -> usize { - // Calculates the distance one has to travel when going from - // `hash mod capacity` onwards to `idx mod capacity`, wrapping around - // if the destination is not reached before the end of the table. - (self.raw.idx.wrapping_sub(self.hash().inspect() as usize)) & self.table.capacity_mask - } - - #[inline] - pub fn hash(&self) -> SafeHash { - unsafe { SafeHash { hash: *self.raw.hash() } } - } - - /// Gets references to the key and value at a given index. - pub fn read(&self) -> (&K, &V) { - unsafe { - let pair_ptr = self.raw.pair(); - (&(*pair_ptr).0, &(*pair_ptr).1) - } - } -} - -// We take a mutable reference to the table instead of accepting anything that -// implements `DerefMut` to prevent fn `take` from being called on `stash`ed -// buckets. -impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> { - /// Removes this bucket's key and value from the hashtable. - /// - /// This works similarly to `put`, building an `EmptyBucket` out of the - /// taken bucket. - pub fn take(self) -> (EmptyBucket<K, V, &'t mut RawTable<K, V>>, K, V) { - self.table.size -= 1; - - unsafe { - *self.raw.hash() = EMPTY_BUCKET; - let (k, v) = ptr::read(self.raw.pair()); - (EmptyBucket { - raw: self.raw, - table: self.table, - }, - k, - v) - } - } -} - -// This use of `Put` is misleading and restrictive, but safe and sufficient for our use cases -// where `M` is a full bucket or table reference type with mutable access to the table. -impl<K, V, M> FullBucket<K, V, M> - where M: Put<K, V> -{ - 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, old_val) = ptr::replace(self.raw.pair(), (k, v)); - - (old_hash, old_key, old_val) - } - } -} - -impl<K, V, M> FullBucket<K, V, M> - where M: Deref<Target = RawTable<K, V>> + DerefMut -{ - /// Gets mutable references to the key and value at a given index. - pub fn read_mut(&mut self) -> (&mut K, &mut V) { - unsafe { - let pair_ptr = self.raw.pair(); - (&mut (*pair_ptr).0, &mut (*pair_ptr).1) - } - } -} - -impl<'t, K, V, M> FullBucket<K, V, M> - where M: Deref<Target = RawTable<K, V>> + 't -{ - /// Exchange a bucket state for immutable references into the table. - /// Because the underlying reference to the table is also consumed, - /// no further changes to the structure of the table are possible; - /// 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 { - let pair_ptr = self.raw.pair(); - (&(*pair_ptr).0, &(*pair_ptr).1) - } - } -} - -impl<'t, K, V, M> FullBucket<K, V, M> - where M: Deref<Target = RawTable<K, V>> + DerefMut + 't -{ - /// 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 { - let pair_ptr = self.raw.pair(); - (&mut (*pair_ptr).0, &mut (*pair_ptr).1) - } - } -} - -impl<K, V, M> GapThenFull<K, V, M> - where M: Deref<Target = RawTable<K, V>> -{ - #[inline] - pub fn full(&self) -> &FullBucket<K, V, M> { - &self.full - } - - pub fn into_table(self) -> M { - self.full.into_table() - } - - pub fn shift(mut self) -> Result<GapThenFull<K, V, M>, Bucket<K, V, M>> { - unsafe { - let (gap_hash, gap_pair) = self.gap.raw.hash_pair(); - let (full_hash, full_pair) = self.full.raw.hash_pair(); - *gap_hash = mem::replace(&mut *full_hash, EMPTY_BUCKET); - ptr::copy_nonoverlapping(full_pair, gap_pair, 1); - } - - let FullBucket { raw: prev_raw, .. } = self.full; - - match self.full.next().peek() { - Full(bucket) => { - self.gap.raw = prev_raw; - - self.full = bucket; - - Ok(self) - } - Empty(b) => Err(b.into_bucket()), - } - } -} - -// Returns a Layout which describes the allocation required for a hash table, -// and the offset of the array of (key, value) pairs in the allocation. -#[inline(always)] -fn calculate_layout<K, V>(capacity: usize) -> Result<(Layout, usize), LayoutErr> { - let hashes = Layout::array::<HashUint>(capacity)?; - let pairs = Layout::array::<(K, V)>(capacity)?; - hashes.extend(pairs).map(|(layout, _)| { - // LLVM seems to have trouble properly const-propagating pairs.align(), - // possibly due to the use of NonZeroUsize. This little hack allows it - // to generate optimal code. - // - // See https://github.com/rust-lang/rust/issues/51346 for more details. - ( - layout, - hashes.size() + hashes.padding_needed_for(mem::align_of::<(K, V)>()), - ) - }) -} - -pub(crate) enum Fallibility { - Fallible, - Infallible, -} - -use self::Fallibility::*; - -impl<K, V> RawTable<K, V> { - /// Does not initialize the buckets. The caller should ensure they, - /// at the very least, set every hash to EMPTY_BUCKET. - /// Returns an error if it cannot allocate or capacity overflows. - unsafe fn new_uninitialized_internal( - capacity: usize, - fallibility: Fallibility, - ) -> Result<RawTable<K, V>, CollectionAllocErr> { - if capacity == 0 { - return Ok(RawTable { - size: 0, - capacity_mask: capacity.wrapping_sub(1), - hashes: TaggedHashUintPtr::new(EMPTY as *mut HashUint), - marker: marker::PhantomData, - }); - } - - // 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. - let (layout, _) = calculate_layout::<K, V>(capacity)?; - let buffer = Global.alloc(layout).map_err(|e| match fallibility { - Infallible => handle_alloc_error(layout), - Fallible => e, - })?; - - Ok(RawTable { - capacity_mask: capacity.wrapping_sub(1), - size: 0, - hashes: TaggedHashUintPtr::new(buffer.cast().as_ptr()), - marker: marker::PhantomData, - }) - } - - /// Does not initialize the buckets. The caller should ensure they, - /// at the very least, set every hash to EMPTY_BUCKET. - unsafe fn new_uninitialized(capacity: usize) -> RawTable<K, V> { - match Self::new_uninitialized_internal(capacity, Infallible) { - Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => unreachable!(), - Ok(table) => { table } - } - } - - #[inline(always)] - fn raw_bucket_at(&self, index: usize) -> RawBucket<K, V> { - let (_, pairs_offset) = calculate_layout::<K, V>(self.capacity()) - .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() }); - let buffer = self.hashes.ptr() as *mut u8; - unsafe { - RawBucket { - hash_start: buffer as *mut HashUint, - pair_start: buffer.add(pairs_offset) as *const (K, V), - idx: index, - _marker: marker::PhantomData, - } - } - } - - #[inline] - fn new_internal( - capacity: usize, - fallibility: Fallibility, - ) -> Result<RawTable<K, V>, CollectionAllocErr> { - unsafe { - let ret = RawTable::new_uninitialized_internal(capacity, fallibility)?; - if capacity > 0 { - ptr::write_bytes(ret.hashes.ptr(), 0, capacity); - } - Ok(ret) - } - } - - /// Tries to create a new raw table from a given capacity. If it cannot allocate, - /// it returns with AllocErr. - #[inline] - pub fn try_new(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> { - Self::new_internal(capacity, Fallible) - } - - /// Creates a new raw table from a given capacity. All buckets are - /// initially empty. - #[inline] - pub fn new(capacity: usize) -> RawTable<K, V> { - match Self::new_internal(capacity, Infallible) { - Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => unreachable!(), - Ok(table) => { table } - } - } - - /// The hashtable's capacity, similar to a vector's. - pub fn capacity(&self) -> usize { - self.capacity_mask.wrapping_add(1) - } - - /// The number of elements ever `put` in the hashtable, minus the number - /// of elements ever `take`n. - pub fn size(&self) -> usize { - self.size - } - - fn raw_buckets(&self) -> RawBuckets<K, V> { - RawBuckets { - raw: self.raw_bucket_at(0), - elems_left: self.size, - marker: marker::PhantomData, - } - } - - pub fn iter(&self) -> Iter<K, V> { - Iter { - iter: self.raw_buckets(), - } - } - - pub fn iter_mut(&mut self) -> IterMut<K, V> { - IterMut { - iter: self.raw_buckets(), - _marker: marker::PhantomData, - } - } - - pub fn into_iter(self) -> IntoIter<K, V> { - let RawBuckets { raw, elems_left, .. } = self.raw_buckets(); - // Replace the marker regardless of lifetime bounds on parameters. - IntoIter { - iter: RawBuckets { - raw, - elems_left, - marker: marker::PhantomData, - }, - table: self, - } - } - - pub fn drain(&mut self) -> Drain<K, V> { - let RawBuckets { raw, elems_left, .. } = self.raw_buckets(); - // Replace the marker regardless of lifetime bounds on parameters. - Drain { - iter: RawBuckets { - raw, - elems_left, - marker: marker::PhantomData, - }, - table: NonNull::from(self), - marker: marker::PhantomData, - } - } - - /// Drops buckets in reverse order. It leaves the table in an inconsistent - /// state and should only be used for dropping the table's remaining - /// entries. It's used in the implementation of Drop. - unsafe fn rev_drop_buckets(&mut self) { - // initialize the raw bucket past the end of the table - let mut raw = self.raw_bucket_at(self.capacity()); - let mut elems_left = self.size; - - while elems_left != 0 { - raw.idx -= 1; - - if *raw.hash() != EMPTY_BUCKET { - elems_left -= 1; - ptr::drop_in_place(raw.pair()); - } - } - } - - /// Sets the table tag. - pub fn set_tag(&mut self, value: bool) { - self.hashes.set_tag(value) - } - - /// Gets the table tag. - pub fn tag(&self) -> bool { - self.hashes.tag() - } -} - -/// A raw iterator. The basis for some other iterators in this module. Although -/// this interface is safe, it's not used outside this module. -struct RawBuckets<'a, K, V> { - raw: RawBucket<K, V>, - elems_left: usize, - - // Strictly speaking, this should be &'a (K,V), but that would - // require that K:'a, and we often use RawBuckets<'static...> for - // move iterations, so that messes up a lot of other things. So - // just use `&'a (K,V)` as this is not a publicly exposed type - // anyway. - marker: marker::PhantomData<&'a ()>, -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -impl<K, V> Clone for RawBuckets<'_, K, V> { - fn clone(&self) -> Self { - RawBuckets { - raw: self.raw, - elems_left: self.elems_left, - marker: marker::PhantomData, - } - } -} - - -impl<'a, K, V> Iterator for RawBuckets<'a, K, V> { - type Item = RawBucket<K, V>; - - fn next(&mut self) -> Option<RawBucket<K, V>> { - if self.elems_left == 0 { - return None; - } - - loop { - unsafe { - let item = self.raw; - self.raw.idx += 1; - if *item.hash() != EMPTY_BUCKET { - self.elems_left -= 1; - return Some(item); - } - } - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (self.elems_left, Some(self.elems_left)) - } -} - -impl<K, V> ExactSizeIterator for RawBuckets<'_, K, V> { - fn len(&self) -> usize { - self.elems_left - } -} - -/// Iterator over shared references to entries in a table. -pub struct Iter<'a, K: 'a, V: 'a> { - iter: RawBuckets<'a, K, V>, -} - -unsafe impl<K: Sync, V: Sync> Sync for Iter<'_, K, V> {} -unsafe impl<K: Sync, V: Sync> Send for Iter<'_, K, V> {} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -impl<K, V> Clone for Iter<'_, K, V> { - fn clone(&self) -> Self { - Iter { - iter: self.iter.clone(), - } - } -} - -/// Iterator over mutable references to entries in a table. -pub struct IterMut<'a, K: 'a, V: 'a> { - iter: RawBuckets<'a, K, V>, - // To ensure invariance with respect to V - _marker: marker::PhantomData<&'a mut V>, -} - -unsafe impl<K: Sync, V: Sync> Sync for IterMut<'_, K, V> {} -// Both K: Sync and K: Send are correct for IterMut's Send impl, -// but Send is the more useful bound -unsafe impl<K: Send, V: Send> Send for IterMut<'_, K, V> {} - -impl<'a, K: 'a, V: 'a> IterMut<'a, K, V> { - pub fn iter(&self) -> Iter<K, V> { - Iter { - iter: self.iter.clone(), - } - } -} - -/// Iterator over the entries in a table, consuming the table. -pub struct IntoIter<K, V> { - table: RawTable<K, V>, - iter: RawBuckets<'static, K, V>, -} - -unsafe impl<K: Sync, V: Sync> Sync for IntoIter<K, V> {} -unsafe impl<K: Send, V: Send> Send for IntoIter<K, V> {} - -impl<K, V> IntoIter<K, V> { - pub fn iter(&self) -> Iter<K, V> { - Iter { - iter: self.iter.clone(), - } - } -} - -/// Iterator over the entries in a table, clearing the table. -pub struct Drain<'a, K: 'a, V: 'a> { - table: NonNull<RawTable<K, V>>, - iter: RawBuckets<'static, K, V>, - marker: marker::PhantomData<&'a RawTable<K, V>>, -} - -unsafe impl<K: Sync, V: Sync> Sync for Drain<'_, K, V> {} -unsafe impl<K: Send, V: Send> Send for Drain<'_, K, V> {} - -impl<'a, K, V> Drain<'a, K, V> { - pub fn iter(&self) -> Iter<K, V> { - Iter { - iter: self.iter.clone(), - } - } -} - -impl<'a, K, V> Iterator for Iter<'a, K, V> { - type Item = (&'a K, &'a V); - - fn next(&mut self) -> Option<(&'a K, &'a V)> { - self.iter.next().map(|raw| unsafe { - let pair_ptr = raw.pair(); - (&(*pair_ptr).0, &(*pair_ptr).1) - }) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -impl<K, V> ExactSizeIterator for Iter<'_, K, V> { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl<'a, K, V> Iterator for IterMut<'a, K, V> { - type Item = (&'a K, &'a mut V); - - fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - self.iter.next().map(|raw| unsafe { - let pair_ptr = raw.pair(); - (&(*pair_ptr).0, &mut (*pair_ptr).1) - }) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -impl<K, V> ExactSizeIterator for IterMut<'_, K, V> { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl<K, V> Iterator for IntoIter<K, V> { - type Item = (SafeHash, K, V); - - fn next(&mut self) -> Option<(SafeHash, K, V)> { - self.iter.next().map(|raw| { - self.table.size -= 1; - unsafe { - let (k, v) = ptr::read(raw.pair()); - (SafeHash { hash: *raw.hash() }, k, v) - } - }) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -impl<K, V> ExactSizeIterator for IntoIter<K, V> { - fn len(&self) -> usize { - self.iter().len() - } -} - -impl<'a, K, V> Iterator for Drain<'a, K, V> { - type Item = (SafeHash, K, V); - - #[inline] - fn next(&mut self) -> Option<(SafeHash, K, V)> { - self.iter.next().map(|raw| { - unsafe { - self.table.as_mut().size -= 1; - let (k, v) = ptr::read(raw.pair()); - (SafeHash { hash: ptr::replace(&mut *raw.hash(), EMPTY_BUCKET) }, k, v) - } - }) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -impl<K, V> ExactSizeIterator for Drain<'_, K, V> { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl<K, V> Drop for Drain<'_, K, V> { - fn drop(&mut self) { - self.for_each(drop); - } -} - -impl<K: Clone, V: Clone> Clone for RawTable<K, V> { - fn clone(&self) -> RawTable<K, V> { - unsafe { - let cap = self.capacity(); - let mut new_ht = RawTable::new_uninitialized(cap); - - let mut new_buckets = new_ht.raw_bucket_at(0); - let mut buckets = self.raw_bucket_at(0); - while buckets.idx < cap { - *new_buckets.hash() = *buckets.hash(); - if *new_buckets.hash() != EMPTY_BUCKET { - let pair_ptr = buckets.pair(); - let kv = ((*pair_ptr).0.clone(), (*pair_ptr).1.clone()); - ptr::write(new_buckets.pair(), kv); - } - buckets.idx += 1; - new_buckets.idx += 1; - } - - new_ht.size = self.size(); - new_ht.set_tag(self.tag()); - - new_ht - } - } -} - -unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> { - fn drop(&mut self) { - if self.capacity() == 0 { - return; - } - - // This is done in reverse because we've likely partially taken - // some elements out with `.into_iter()` from the front. - // Check if the size is 0, so we don't do a useless scan when - // dropping empty tables such as on resize. - // Also avoid double drop of elements that have been already moved out. - unsafe { - if needs_drop::<(K, V)>() { - // avoid linear runtime for types that don't need drop - self.rev_drop_buckets(); - } - } - - let (layout, _) = calculate_layout::<K, V>(self.capacity()) - .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() }); - unsafe { - Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).cast(), layout); - // Remember how everything was allocated out of one buffer - // during initialization? We only need one call to free here. - } - } -} diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index 286ce2d389b..15c2532f8b4 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -150,10 +150,9 @@ //! 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 -//! elements will be inserted, or at least have a reasonable upper-bound on that -//! number. +//! this. See collection-specific documentation for details. In general, 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 //! methods can be used to hint to the collection how much room it should make diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 97c67f562a7..1f5de25b65c 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -156,7 +156,7 @@ impl Iterator for Vars { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Vars { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Vars { .. }") } } @@ -170,7 +170,7 @@ impl Iterator for VarsOs { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for VarsOs { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("VarsOs { .. }") } } @@ -253,7 +253,7 @@ pub enum VarError { #[stable(feature = "env", since = "1.0.0")] impl fmt::Display for VarError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { VarError::NotPresent => write!(f, "environment variable not found"), VarError::NotUnicode(ref s) => { @@ -359,9 +359,12 @@ fn _remove_var(k: &OsStr) { /// An iterator that splits an environment variable into paths according to /// platform-specific conventions. /// +/// The iterator element type is [`PathBuf`]. +/// /// This structure is created by the [`std::env::split_paths`] function. See its /// documentation for more. /// +/// [`PathBuf`]: ../../std/path/struct.PathBuf.html /// [`std::env::split_paths`]: fn.split_paths.html #[stable(feature = "env", since = "1.0.0")] pub struct SplitPaths<'a> { inner: os_imp::SplitPaths<'a> } @@ -369,7 +372,8 @@ pub struct SplitPaths<'a> { inner: os_imp::SplitPaths<'a> } /// Parses input according to platform conventions for the `PATH` /// environment variable. /// -/// Returns an iterator over the paths contained in `unparsed`. +/// Returns an iterator over the paths contained in `unparsed`. The iterator +/// element type is [`PathBuf`]. /// /// # Examples /// @@ -386,8 +390,10 @@ pub struct SplitPaths<'a> { inner: os_imp::SplitPaths<'a> } /// None => println!("{} is not defined in the environment.", key) /// } /// ``` +/// +/// [`PathBuf`]: ../../std/path/struct.PathBuf.html #[stable(feature = "env", since = "1.0.0")] -pub fn split_paths<T: AsRef<OsStr> + ?Sized>(unparsed: &T) -> SplitPaths { +pub fn split_paths<T: AsRef<OsStr> + ?Sized>(unparsed: &T) -> SplitPaths<'_> { SplitPaths { inner: os_imp::split_paths(unparsed.as_ref()) } } @@ -400,7 +406,7 @@ impl<'a> Iterator for SplitPaths<'a> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for SplitPaths<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("SplitPaths { .. }") } } @@ -459,7 +465,7 @@ pub struct JoinPathsError { /// # } /// ``` /// -/// Using `env::join_paths` with `env::spit_paths` to append an item to the `PATH` environment +/// Using `env::join_paths` with [`env::split_paths`] to append an item to the `PATH` environment /// variable: /// /// ``` @@ -477,6 +483,8 @@ pub struct JoinPathsError { /// Ok(()) /// } /// ``` +/// +/// [`env::split_paths`]: fn.split_paths.html #[stable(feature = "env", since = "1.0.0")] pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> where I: IntoIterator<Item=T>, T: AsRef<OsStr> @@ -488,7 +496,7 @@ pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> #[stable(feature = "env", since = "1.0.0")] impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } @@ -757,7 +765,7 @@ impl DoubleEndedIterator for Args { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Args { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Args") .field("inner", &self.inner.inner.inner_debug()) .finish() @@ -790,7 +798,7 @@ impl DoubleEndedIterator for ArgsOs { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for ArgsOs { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ArgsOs") .field("inner", &self.inner.inner_debug()) .finish() @@ -839,7 +847,6 @@ pub mod consts { /// - ios /// - freebsd /// - dragonfly - /// - bitrig /// - netbsd /// - openbsd /// - solaris @@ -968,6 +975,11 @@ mod arch { pub const ARCH: &str = "wasm32"; } +#[cfg(target_arch = "hexagon")] +mod arch { + pub const ARCH: &'static str = "hexagon"; +} + #[cfg(test)] mod tests { use super::*; @@ -975,7 +987,7 @@ mod tests { use crate::path::Path; #[test] - #[cfg_attr(target_os = "emscripten", ignore)] + #[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)] fn test_self_exe_path() { let path = current_exe(); assert!(path.is_ok()); @@ -989,6 +1001,7 @@ mod tests { fn test() { assert!((!Path::new("test-path").is_absolute())); + #[cfg(not(target_env = "sgx"))] current_dir().unwrap(); } diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 3eb289501cb..117a430eec6 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -84,7 +84,7 @@ pub trait Error: Debug + Display { /// } /// /// impl fmt::Display for SuperError { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "SuperError is here!") /// } /// } @@ -94,7 +94,7 @@ pub trait Error: Debug + Display { /// "I'm the superhero of errors" /// } /// - /// fn cause(&self) -> Option<&Error> { + /// fn cause(&self) -> Option<&dyn Error> { /// Some(&self.side) /// } /// } @@ -103,7 +103,7 @@ pub trait Error: Debug + Display { /// struct SuperErrorSideKick; /// /// impl fmt::Display for SuperErrorSideKick { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "SuperErrorSideKick is here!") /// } /// } @@ -149,7 +149,7 @@ pub trait Error: Debug + Display { /// } /// /// impl fmt::Display for SuperError { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "SuperError is here!") /// } /// } @@ -168,7 +168,7 @@ pub trait Error: Debug + Display { /// struct SuperErrorSideKick; /// /// impl fmt::Display for SuperErrorSideKick { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "SuperErrorSideKick is here!") /// } /// } @@ -198,16 +198,28 @@ pub trait Error: Debug + Display { /// Gets the `TypeId` of `self` #[doc(hidden)] - #[stable(feature = "error_type_id", since = "1.34.0")] - fn type_id(&self) -> TypeId where Self: 'static { + #[unstable(feature = "error_type_id", + reason = "this is memory unsafe to override in user code", + issue = "60784")] + fn type_id(&self, _: private::Internal) -> TypeId where Self: 'static { TypeId::of::<Self>() } } +mod private { + // This is a hack to prevent `type_id` from being overridden by `Error` + // implementations, since that can enable unsound downcasting. + #[unstable(feature = "error_type_id", issue = "60784")] + #[derive(Debug)] + pub struct Internal; +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> { /// Converts a type of [`Error`] into a box of dyn [`Error`]. /// + /// [`Error`]: ../error/trait.Error.html + /// /// # Examples /// /// ``` @@ -219,7 +231,7 @@ impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> { /// struct AnError; /// /// impl fmt::Display for AnError { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f , "An error") /// } /// } @@ -232,7 +244,7 @@ impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> { /// /// let an_error = AnError; /// assert!(0 == mem::size_of_val(&an_error)); - /// let a_boxed_error = Box::<Error>::from(an_error); + /// let a_boxed_error = Box::<dyn Error>::from(an_error); /// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error)) /// ``` fn from(err: E) -> Box<dyn Error + 'a> { @@ -242,8 +254,10 @@ impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + 'a> { - /// Converts a type of [`Error`] + [`Send`] + [`Sync`] into a box of dyn [`Error`] + - /// [`Send`] + [`Sync`]. + /// Converts a type of [`Error`] + [`trait@Send`] + [`trait@Sync`] into a box of + /// dyn [`Error`] + [`trait@Send`] + [`trait@Sync`]. + /// + /// [`Error`]: ../error/trait.Error.html /// /// # Examples /// @@ -256,7 +270,7 @@ impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + /// struct AnError; /// /// impl fmt::Display for AnError { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f , "An error") /// } /// } @@ -273,7 +287,7 @@ impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + /// /// let an_error = AnError; /// assert!(0 == mem::size_of_val(&an_error)); - /// let a_boxed_error = Box::<Error + Send + Sync>::from(an_error); + /// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(an_error); /// assert!( /// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error)) /// ``` @@ -284,7 +298,9 @@ impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + #[stable(feature = "rust1", since = "1.0.0")] impl From<String> for Box<dyn Error + Send + Sync> { - /// Converts a [`String`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. + /// Converts a [`String`] into a box of dyn [`Error`] + [`trait@Send`] + [`trait@Sync`]. + /// + /// [`Error`]: ../error/trait.Error.html /// /// # Examples /// @@ -293,12 +309,11 @@ impl From<String> for Box<dyn Error + Send + Sync> { /// use std::mem; /// /// let a_string_error = "a string error".to_string(); - /// let a_boxed_error = Box::<Error + Send + Sync>::from(a_string_error); + /// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_string_error); /// assert!( /// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error)) /// ``` fn from(err: String) -> Box<dyn Error + Send + Sync> { - #[derive(Debug)] struct StringError(String); impl Error for StringError { @@ -306,11 +321,18 @@ impl From<String> for Box<dyn Error + Send + Sync> { } impl Display for StringError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) } } + // Purposefully skip printing "StringError(..)" + impl Debug for StringError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.0, f) + } + } + Box::new(StringError(err)) } } @@ -319,6 +341,8 @@ impl From<String> for Box<dyn Error + Send + Sync> { impl From<String> for Box<dyn Error> { /// Converts a [`String`] into a box of dyn [`Error`]. /// + /// [`Error`]: ../error/trait.Error.html + /// /// # Examples /// /// ``` @@ -326,7 +350,7 @@ impl From<String> for Box<dyn Error> { /// use std::mem; /// /// let a_string_error = "a string error".to_string(); - /// let a_boxed_error = Box::<Error>::from(a_string_error); + /// let a_boxed_error = Box::<dyn Error>::from(a_string_error); /// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error)) /// ``` fn from(str_err: String) -> Box<dyn Error> { @@ -338,7 +362,9 @@ impl From<String> for Box<dyn Error> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> { - /// Converts a [`str`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. + /// Converts a [`str`] into a box of dyn [`Error`] + [`trait@Send`] + [`trait@Sync`]. + /// + /// [`Error`]: ../error/trait.Error.html /// /// # Examples /// @@ -347,7 +373,7 @@ impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> { /// use std::mem; /// /// let a_str_error = "a str error"; - /// let a_boxed_error = Box::<Error + Send + Sync>::from(a_str_error); + /// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_str_error); /// assert!( /// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error)) /// ``` @@ -360,6 +386,8 @@ impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> { impl From<&str> for Box<dyn Error> { /// Converts a [`str`] into a box of dyn [`Error`]. /// + /// [`Error`]: ../error/trait.Error.html + /// /// # Examples /// /// ``` @@ -367,7 +395,7 @@ impl From<&str> for Box<dyn Error> { /// use std::mem; /// /// let a_str_error = "a str error"; - /// let a_boxed_error = Box::<Error>::from(a_str_error); + /// let a_boxed_error = Box::<dyn Error>::from(a_str_error); /// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error)) /// ``` fn from(err: &str) -> Box<dyn Error> { @@ -377,7 +405,10 @@ impl From<&str> for Box<dyn Error> { #[stable(feature = "cow_box_error", since = "1.22.0")] impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + Send + Sync + 'a> { - /// Converts a [`Cow`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. + /// Converts a [`Cow`] into a box of dyn [`Error`] + [`trait@Send`] + [`trait@Sync`]. + /// + /// [`Cow`]: ../borrow/enum.Cow.html + /// [`Error`]: ../error/trait.Error.html /// /// # Examples /// @@ -387,7 +418,7 @@ impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + Send + Sync + 'a> { /// use std::borrow::Cow; /// /// let a_cow_str_error = Cow::from("a str error"); - /// let a_boxed_error = Box::<Error + Send + Sync>::from(a_cow_str_error); + /// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_cow_str_error); /// assert!( /// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error)) /// ``` @@ -400,6 +431,9 @@ impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + Send + Sync + 'a> { impl<'a> From<Cow<'a, str>> for Box<dyn Error> { /// Converts a [`Cow`] into a box of dyn [`Error`]. /// + /// [`Cow`]: ../borrow/enum.Cow.html + /// [`Error`]: ../error/trait.Error.html + /// /// # Examples /// /// ``` @@ -408,7 +442,7 @@ impl<'a> From<Cow<'a, str>> for Box<dyn Error> { /// use std::borrow::Cow; /// /// let a_cow_str_error = Cow::from("a str error"); - /// let a_boxed_error = Box::<Error>::from(a_cow_str_error); + /// let a_boxed_error = Box::<dyn Error>::from(a_cow_str_error); /// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error)) /// ``` fn from(err: Cow<'a, str>) -> Box<dyn Error> { @@ -526,6 +560,10 @@ impl<T: Error> Error for Box<T> { fn cause(&self) -> Option<&dyn Error> { Error::cause(&**self) } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + Error::source(&**self) + } } #[stable(feature = "fmt_error", since = "1.11.0")] @@ -573,7 +611,7 @@ impl dyn Error + 'static { let t = TypeId::of::<T>(); // Get TypeId of the type in the trait object - let boxed = self.type_id(); + let boxed = self.type_id(private::Internal); // Compare both TypeIds on equality t == boxed @@ -686,13 +724,13 @@ impl dyn Error { /// struct B(Option<Box<dyn Error + 'static>>); /// /// impl fmt::Display for A { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "A") /// } /// } /// /// impl fmt::Display for B { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "B") /// } /// } @@ -721,7 +759,7 @@ impl dyn Error { /// [`source`]: trait.Error.html#method.source #[unstable(feature = "error_iter", issue = "58520")] #[inline] - pub fn iter_chain(&self) -> ErrorIter { + pub fn iter_chain(&self) -> ErrorIter<'_> { ErrorIter { current: Some(self), } @@ -747,19 +785,19 @@ impl dyn Error { /// struct C(Option<Box<dyn Error + 'static>>); /// /// impl fmt::Display for A { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "A") /// } /// } /// /// impl fmt::Display for B { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "B") /// } /// } /// /// impl fmt::Display for C { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "C") /// } /// } @@ -795,7 +833,7 @@ impl dyn Error { /// [`source`]: trait.Error.html#method.source #[inline] #[unstable(feature = "error_iter", issue = "58520")] - pub fn iter_sources(&self) -> ErrorIter { + pub fn iter_sources(&self) -> ErrorIter<'_> { ErrorIter { current: self.source(), } @@ -861,12 +899,12 @@ mod tests { struct B; impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "A") } } impl fmt::Display for B { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "B") } } diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index a2b12d00a78..f649170c403 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -32,11 +32,13 @@ impl f32 { /// # Examples /// /// ``` - /// let f = 3.99_f32; + /// let f = 3.7_f32; /// let g = 3.0_f32; + /// let h = -3.7_f32; /// /// assert_eq!(f.floor(), 3.0); /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -104,11 +106,13 @@ impl f32 { /// # Examples /// /// ``` - /// let f = 3.3_f32; - /// let g = -3.7_f32; + /// let f = 3.7_f32; + /// let g = 3.0_f32; + /// let h = -3.7_f32; /// /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), -3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -184,21 +188,20 @@ impl f32 { if self.is_nan() { NAN } else { - unsafe { intrinsics::copysignf32(1.0, self) } + 1.0_f32.copysign(self) } } /// Returns a number composed of the magnitude of `self` and the sign of - /// `y`. + /// `sign`. /// - /// Equal to `self` if the sign of `self` and `y` are the same, otherwise + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise /// equal to `-self`. If `self` is a `NAN`, then a `NAN` with the sign of - /// `y` is returned. + /// `sign` is returned. /// /// # Examples /// /// ``` - /// #![feature(copysign)] /// use std::f32; /// /// let f = 3.5_f32; @@ -212,9 +215,9 @@ impl f32 { /// ``` #[inline] #[must_use] - #[unstable(feature="copysign", issue="55169")] - pub fn copysign(self, y: f32) -> f32 { - unsafe { intrinsics::copysignf32(self, y) } + #[stable(feature = "copysign", since = "1.35.0")] + pub fn copysign(self, sign: f32) -> f32 { + unsafe { intrinsics::copysignf32(self, sign) } } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -253,7 +256,6 @@ impl f32 { /// # Examples /// /// ``` - /// #![feature(euclidean_division)] /// let a: f32 = 7.0; /// let b = 4.0; /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 @@ -262,7 +264,7 @@ impl f32 { /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 /// ``` #[inline] - #[unstable(feature = "euclidean_division", issue = "49048")] + #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f32) -> f32 { let q = (self / rhs).trunc(); if self % rhs < 0.0 { @@ -285,7 +287,6 @@ impl f32 { /// # Examples /// /// ``` - /// #![feature(euclidean_division)] /// let a: f32 = 7.0; /// let b = 4.0; /// assert_eq!(a.rem_euclid(b), 3.0); @@ -296,7 +297,7 @@ impl f32 { /// assert!((-std::f32::EPSILON).rem_euclid(3.0) != 0.0); /// ``` #[inline] - #[unstable(feature = "euclidean_division", issue = "49048")] + #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn rem_euclid(self, rhs: f32) -> f32 { let r = self % rhs; if r < 0.0 { @@ -956,16 +957,27 @@ impl f32 { pub fn atanh(self) -> f32 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } - /// Returns max if self is greater than max, and min if self is less than min. - /// Otherwise this returns self. Panics if min > max, min equals NaN, or max equals NaN. + + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Not that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. /// /// # Examples /// /// ``` /// #![feature(clamp)] - /// assert!((-3.0f32).clamp(-2.0f32, 1.0f32) == -2.0f32); - /// assert!((0.0f32).clamp(-2.0f32, 1.0f32) == 0.0f32); - /// assert!((2.0f32).clamp(-2.0f32, 1.0f32) == 1.0f32); + /// assert!((-3.0f32).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0); + /// assert!((std::f32::NAN).clamp(-2.0, 1.0).is_nan()); /// ``` #[unstable(feature = "clamp", issue = "44095")] #[inline] @@ -1577,4 +1589,22 @@ mod tests { assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); } + + #[test] + #[should_panic] + fn test_clamp_min_greater_than_max() { + 1.0f32.clamp(3.0, 1.0); + } + + #[test] + #[should_panic] + fn test_clamp_min_is_nan() { + 1.0f32.clamp(NAN, 1.0); + } + + #[test] + #[should_panic] + fn test_clamp_max_is_nan() { + 1.0f32.clamp(3.0, NAN); + } } diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index be5cd92d416..f61630997dc 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -32,11 +32,13 @@ impl f64 { /// # Examples /// /// ``` - /// let f = 3.99_f64; + /// let f = 3.7_f64; /// let g = 3.0_f64; + /// let h = -3.7_f64; /// /// assert_eq!(f.floor(), 3.0); /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -84,11 +86,13 @@ impl f64 { /// # Examples /// /// ``` - /// let f = 3.3_f64; - /// let g = -3.7_f64; + /// let f = 3.7_f64; + /// let g = 3.0_f64; + /// let h = -3.7_f64; /// /// assert_eq!(f.trunc(), 3.0); - /// assert_eq!(g.trunc(), -3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -162,21 +166,20 @@ impl f64 { if self.is_nan() { NAN } else { - unsafe { intrinsics::copysignf64(1.0, self) } + 1.0_f64.copysign(self) } } /// Returns a number composed of the magnitude of `self` and the sign of - /// `y`. + /// `sign`. /// - /// Equal to `self` if the sign of `self` and `y` are the same, otherwise + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise /// equal to `-self`. If `self` is a `NAN`, then a `NAN` with the sign of - /// `y` is returned. + /// `sign` is returned. /// /// # Examples /// /// ``` - /// #![feature(copysign)] /// use std::f64; /// /// let f = 3.5_f64; @@ -190,9 +193,9 @@ impl f64 { /// ``` #[inline] #[must_use] - #[unstable(feature="copysign", issue="55169")] - pub fn copysign(self, y: f64) -> f64 { - unsafe { intrinsics::copysignf64(self, y) } + #[stable(feature = "copysign", since = "1.35.0")] + pub fn copysign(self, sign: f64) -> f64 { + unsafe { intrinsics::copysignf64(self, sign) } } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding @@ -229,7 +232,6 @@ impl f64 { /// # Examples /// /// ``` - /// #![feature(euclidean_division)] /// let a: f64 = 7.0; /// let b = 4.0; /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 @@ -238,7 +240,7 @@ impl f64 { /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 /// ``` #[inline] - #[unstable(feature = "euclidean_division", issue = "49048")] + #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn div_euclid(self, rhs: f64) -> f64 { let q = (self / rhs).trunc(); if self % rhs < 0.0 { @@ -261,7 +263,6 @@ impl f64 { /// # Examples /// /// ``` - /// #![feature(euclidean_division)] /// let a: f64 = 7.0; /// let b = 4.0; /// assert_eq!(a.rem_euclid(b), 3.0); @@ -272,7 +273,7 @@ impl f64 { /// assert!((-std::f64::EPSILON).rem_euclid(3.0) != 0.0); /// ``` #[inline] - #[unstable(feature = "euclidean_division", issue = "49048")] + #[stable(feature = "euclidean_division", since = "1.38.0")] pub fn rem_euclid(self, rhs: f64) -> f64 { let r = self % rhs; if r < 0.0 { @@ -878,16 +879,26 @@ impl f64 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } - /// Returns max if self is greater than max, and min if self is less than min. - /// Otherwise this returns self. Panics if min > max, min equals NaN, or max equals NaN. + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Not that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. /// /// # Examples /// /// ``` /// #![feature(clamp)] - /// assert!((-3.0f64).clamp(-2.0f64, 1.0f64) == -2.0f64); - /// assert!((0.0f64).clamp(-2.0f64, 1.0f64) == 0.0f64); - /// assert!((2.0f64).clamp(-2.0f64, 1.0f64) == 1.0f64); + /// assert!((-3.0f64).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0); + /// assert!((std::f64::NAN).clamp(-2.0, 1.0).is_nan()); /// ``` #[unstable(feature = "clamp", issue = "44095")] #[inline] @@ -1518,4 +1529,22 @@ mod tests { assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); } + + #[test] + #[should_panic] + fn test_clamp_min_greater_than_max() { + 1.0f64.clamp(3.0, 1.0); + } + + #[test] + #[should_panic] + fn test_clamp_min_is_nan() { + 1.0f64.clamp(NAN, 1.0); + } + + #[test] + #[should_panic] + fn test_clamp_max_is_nan() { + 1.0f64.clamp(3.0, NAN); + } } diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index ad3f45bfada..6e111f24236 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -43,7 +43,9 @@ use crate::sys; /// `CString` implements a [`as_ptr`] method through the [`Deref`] /// trait. This method will give you a `*const c_char` which you can /// feed directly to extern functions that expect a nul-terminated -/// string, like C's `strdup()`. +/// string, like C's `strdup()`. Notice that [`as_ptr`] returns a +/// read-only pointer; if the C code writes to it, that causes +/// undefined behavior. /// /// # Extracting a slice of the whole C string /// @@ -61,7 +63,7 @@ use crate::sys; /// /// Once you have the kind of slice you need (with or without a nul /// terminator), you can call the slice's own -/// [`as_ptr`][slice.as_ptr] method to get a raw pointer to pass to +/// [`as_ptr`][slice.as_ptr] method to get a read-only raw pointer to pass to /// extern functions. See the documentation for that function for a /// discussion on ensuring the lifetime of the raw pointer. /// @@ -597,11 +599,12 @@ impl CString { /// /// [`Drop`]: ../ops/trait.Drop.html fn into_inner(self) -> Box<[u8]> { - unsafe { - let result = ptr::read(&self.inner); - mem::forget(self); - result - } + // Rationale: `mem::forget(self)` invalidates the previous call to `ptr::read(&self.inner)` + // so we use `ManuallyDrop` to ensure `self` is not dropped. + // Then we can return the box directly without invalidating it. + // See https://github.com/rust-lang/rust/issues/62553. + let this = mem::ManuallyDrop::new(self); + unsafe { ptr::read(&this.inner) } } } @@ -628,7 +631,7 @@ impl ops::Deref for CString { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for CString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } @@ -649,7 +652,7 @@ impl From<CString> for Vec<u8> { #[stable(feature = "cstr_debug", since = "1.3.0")] impl fmt::Debug for CStr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\"")?; for byte in self.to_bytes().iter().flat_map(|&b| ascii::escape_default(b)) { f.write_char(byte as char)?; @@ -847,7 +850,7 @@ impl Error for NulError { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for NulError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "nul byte found in provided data at position: {}", self.0) } } @@ -878,7 +881,7 @@ impl Error for FromBytesWithNulError { #[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")] impl fmt::Display for FromBytesWithNulError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.description())?; if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind { write!(f, " at byte pos {}", pos)?; @@ -917,7 +920,7 @@ impl Error for IntoStringError { #[stable(feature = "cstring_into", since = "1.7.0")] impl fmt::Display for IntoStringError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.description().fmt(f) } } @@ -1043,13 +1046,16 @@ impl CStr { /// /// **WARNING** /// + /// The returned pointer is read-only; writing to it (including passing it + /// to C code that writes to it) causes undefined behavior. + /// /// It is your responsibility to make sure that the underlying memory is not /// freed too early. For example, the following code will cause undefined /// behavior when `ptr` is used inside the `unsafe` block: /// /// ```no_run /// # #![allow(unused_must_use)] - /// use std::ffi::{CString}; + /// use std::ffi::CString; /// /// let ptr = CString::new("Hello").expect("CString::new failed").as_ptr(); /// unsafe { @@ -1065,7 +1071,7 @@ impl CStr { /// /// ```no_run /// # #![allow(unused_must_use)] - /// use std::ffi::{CString}; + /// use std::ffi::CString; /// /// let hello = CString::new("Hello").expect("CString::new failed"); /// let ptr = hello.as_ptr(); @@ -1208,11 +1214,11 @@ impl CStr { /// .expect("CStr::from_bytes_with_nul failed"); /// assert_eq!( /// c_str.to_string_lossy(), - /// Cow::Owned(String::from("Hello �World")) as Cow<str> + /// Cow::Owned(String::from("Hello �World")) as Cow<'_, str> /// ); /// ``` #[stable(feature = "cstr_to_str", since = "1.4.0")] - pub fn to_string_lossy(&self) -> Cow<str> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { String::from_utf8_lossy(self.to_bytes()) } diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs index 7a38f0ebd5a..69fcfa8b39c 100644 --- a/src/libstd/ffi/mod.rs +++ b/src/libstd/ffi/mod.rs @@ -170,7 +170,7 @@ pub use core::ffi::c_void; reason = "the `c_variadic` feature has not been properly tested on \ all supported platforms", issue = "44930")] -pub use core::ffi::VaList; +pub use core::ffi::{VaList, VaListImpl}; mod c_str; mod os_str; diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 3a0590021c9..54e1c876f4c 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -32,7 +32,7 @@ use crate::sys_common::{AsInner, IntoInner, FromInner}; /// in each pair are owned strings; the latter are borrowed /// references. /// -/// Note, `OsString` and `OsStr` internally do not necessarily hold strings in +/// Note, `OsString` and [`OsStr`] internally do not necessarily hold strings in /// the form native to the platform; While on Unix, strings are stored as a /// sequence of 8-bit values, on Windows, where strings are 16-bit value based /// as just discussed, strings are also actually stored as a sequence of 8-bit @@ -351,6 +351,8 @@ impl From<String> for OsString { /// Converts a [`String`] into a [`OsString`]. /// /// The conversion copies the data, and includes an allocation on the heap. + /// + /// [`OsString`]: ../../std/ffi/struct.OsString.html fn from(s: String) -> OsString { OsString { inner: Buf::from_string(s) } } @@ -394,7 +396,7 @@ impl Default for OsString { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for OsString { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, formatter) } } @@ -563,7 +565,7 @@ impl OsStr { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_string_lossy(&self) -> Cow<str> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { self.inner.to_string_lossy() } @@ -665,10 +667,11 @@ impl From<&OsStr> for Box<OsStr> { #[stable(feature = "os_string_from_box", since = "1.18.0")] impl From<Box<OsStr>> for OsString { - /// Converts a `Box<OsStr>` into a `OsString` without copying or allocating. + /// Converts a [`Box`]`<`[`OsStr`]`>` into a `OsString` without copying or + /// allocating. /// /// [`Box`]: ../boxed/struct.Box.html - /// [`OsString`]: ../ffi/struct.OsString.html + /// [`OsStr`]: ../ffi/struct.OsStr.html fn from(boxed: Box<OsStr>) -> OsString { boxed.into_os_string() } @@ -891,13 +894,13 @@ impl Hash for OsStr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for OsStr { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.inner, formatter) } } impl OsStr { - pub(crate) fn display(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + pub(crate) fn display(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.inner, formatter) } } @@ -960,6 +963,7 @@ impl IntoInner<Buf> for OsString { } impl AsInner<Slice> for OsStr { + #[inline] fn as_inner(&self) -> &Slice { &self.inner } diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 25f2dd73504..5f76875bd66 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + //! Filesystem manipulation operations. //! //! This module contains basic methods to manipulate the contents of the local @@ -9,7 +11,7 @@ use crate::fmt; use crate::ffi::OsString; -use crate::io::{self, SeekFrom, Seek, Read, Initializer, Write}; +use crate::io::{self, SeekFrom, Seek, Read, Initializer, Write, IoSlice, IoSliceMut}; use crate::path::{Path, PathBuf}; use crate::sys::fs as fs_imp; use crate::sys_common::{AsInnerMut, FromInner, AsInner, IntoInner}; @@ -21,7 +23,9 @@ use crate::time::SystemTime; /// it was opened with. Files also implement [`Seek`] to alter the logical cursor /// that the file contains internally. /// -/// Files are automatically closed when they go out of scope. +/// Files are automatically closed when they go out of scope. Errors detected +/// on closing are ignored by the implementation of `Drop`. Use the method +/// [`sync_all`] if these errors must be manually handled. /// /// # Examples /// @@ -84,6 +88,7 @@ use crate::time::SystemTime; /// [`Read`]: ../io/trait.Read.html /// [`Write`]: ../io/trait.Write.html /// [`BufReader<R>`]: ../io/struct.BufReader.html +/// [`sync_all`]: struct.File.html#method.sync_all #[stable(feature = "rust1", since = "1.0.0")] pub struct File { inner: fs_imp::File, @@ -211,7 +216,7 @@ pub struct DirBuilder { recursive: bool, } -/// How large a buffer to pre-allocate before reading the entire file. +/// Indicates how large a buffer to pre-allocate before reading the entire file. fn initial_buffer_size(file: &File) -> usize { // Allocate one extra byte so the buffer doesn't need to grow before the // final `read` call at the end of the file. Don't worry about `usize` @@ -391,9 +396,13 @@ impl File { /// Attempts to sync all OS-internal metadata to disk. /// - /// This function will attempt to ensure that all in-core data reaches the + /// This function will attempt to ensure that all in-memory data reaches the /// filesystem before returning. /// + /// This can be used to handle errors that would otherwise only be caught + /// when the `File` is closed. Dropping a file will ignore errors in + /// synchronizing this in-memory data. + /// /// # Examples /// /// ```no_run @@ -459,6 +468,8 @@ impl File { /// # Errors /// /// This function will return an error if the file is not opened for writing. + /// Also, std::io::ErrorKind::InvalidInput will be returned if the desired + /// length would cause an overflow due to the implementation specifics. /// /// # Examples /// @@ -597,7 +608,7 @@ impl IntoInner<fs_imp::File> for File { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } @@ -608,6 +619,10 @@ impl Read for File { self.inner.read(buf) } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.inner.read_vectored(bufs) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -618,6 +633,11 @@ impl Write for File { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.inner.write(buf) } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.inner.write_vectored(bufs) + } + fn flush(&mut self) -> io::Result<()> { self.inner.flush() } } #[stable(feature = "rust1", since = "1.0.0")] @@ -632,6 +652,10 @@ impl Read for &File { self.inner.read(buf) } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.inner.read_vectored(bufs) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -642,6 +666,11 @@ impl Write for &File { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.inner.write(buf) } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.inner.write_vectored(bufs) + } + fn flush(&mut self) -> io::Result<()> { self.inner.flush() } } #[stable(feature = "rust1", since = "1.0.0")] @@ -876,11 +905,14 @@ impl OpenOptions { } fn _open(&self, path: &Path) -> io::Result<File> { - let inner = fs_imp::File::open(path, &self.0)?; - Ok(File { inner }) + fs_imp::File::open(path, &self.0).map(|inner| File { inner }) } } +impl AsInner<fs_imp::OpenOptions> for OpenOptions { + fn as_inner(&self) -> &fs_imp::OpenOptions { &self.0 } +} + impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions { fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { &mut self.0 } } @@ -1087,7 +1119,7 @@ impl Metadata { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Metadata { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Metadata") .field("file_type", &self.file_type()) .field("is_dir", &self.is_dir()) @@ -1104,6 +1136,10 @@ impl AsInner<fs_imp::FileAttr> for Metadata { fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 } } +impl FromInner<fs_imp::FileAttr> for Metadata { + fn from_inner(attr: fs_imp::FileAttr) -> Metadata { Metadata(attr) } +} + impl Permissions { /// Returns `true` if these permissions describe a readonly (unwritable) file. /// @@ -1394,7 +1430,7 @@ 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("DirEntry") .field(&self.path()) .finish() @@ -1581,7 +1617,8 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> /// `O_CLOEXEC` is set for returned file descriptors. /// On Windows, this function currently corresponds to `CopyFileEx`. Alternate /// NTFS streams are copied but only the size of the main stream is returned by -/// this function. +/// this function. On MacOS, this function corresponds to `fclonefileat` and +/// `fcopyfile`. /// Note that, this [may change in the future][changes]. /// /// [changes]: ../io/index.html#platform-specific-behavior @@ -1777,6 +1814,8 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { /// function.) /// * `path` already exists. /// +/// [`create_dir_all`]: fn.create_dir_all.html +/// /// # Examples /// /// ```no_run @@ -1939,7 +1978,7 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// use std::path::Path; /// /// // one possible implementation of walking a directory only visiting files -/// fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> { +/// fn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> io::Result<()> { /// if dir.is_dir() { /// for entry in fs::read_dir(dir)? { /// let entry = entry?; @@ -2094,7 +2133,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder { } } -#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))] +#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))] mod tests { use crate::io::prelude::*; @@ -2837,6 +2876,26 @@ mod tests { } #[test] + fn copy_file_follows_dst_symlink() { + let tmp = tmpdir(); + if !got_symlink_permission(&tmp) { return }; + + let in_path = tmp.join("in.txt"); + let out_path = tmp.join("out.txt"); + let out_path_symlink = tmp.join("out_symlink.txt"); + + check!(fs::write(&in_path, "foo")); + check!(fs::write(&out_path, "bar")); + check!(symlink_file(&out_path, &out_path_symlink)); + + check!(fs::copy(&in_path, &out_path_symlink)); + + assert!(check!(out_path_symlink.symlink_metadata()).file_type().is_symlink()); + assert_eq!(check!(fs::read(&out_path_symlink)), b"foo".to_vec()); + assert_eq!(check!(fs::read(&out_path)), b"foo".to_vec()); + } + + #[test] fn symlinks_work() { let tmpdir = tmpdir(); if !got_symlink_permission(&tmpdir) { return }; @@ -3259,11 +3318,11 @@ mod tests { fs::create_dir_all(&d).unwrap(); File::create(&f).unwrap(); if cfg!(not(windows)) { - symlink_dir("../d/e", &c).unwrap(); + symlink_file("../d/e", &c).unwrap(); symlink_file("../f", &e).unwrap(); } if cfg!(windows) { - symlink_dir(r"..\d\e", &c).unwrap(); + symlink_file(r"..\d\e", &c).unwrap(); symlink_file(r"..\f", &e).unwrap(); } diff --git a/src/libstd/future.rs b/src/libstd/future.rs index aa784746122..0406549ff07 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -5,22 +5,25 @@ use core::marker::Unpin; use core::pin::Pin; use core::option::Option; use core::ptr::NonNull; -use core::task::{Waker, Poll}; +use core::task::{Context, Poll}; use core::ops::{Drop, Generator, GeneratorState}; #[doc(inline)] +#[stable(feature = "futures_api", since = "1.36.0")] pub use core::future::*; /// Wrap a generator in a future. /// /// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give /// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). +#[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] pub fn from_generator<T: Generator<Yield = ()>>(x: T) -> impl Future<Output = T::Return> { GenFuture(x) } /// A wrapper around generators used to implement `Future` for `async`/`await` code. +#[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] struct GenFuture<T: Generator<Yield = ()>>(T); @@ -29,13 +32,14 @@ struct GenFuture<T: Generator<Yield = ()>>(T); // self-referential borrows in the underlying generator. impl<T: Generator<Yield = ()>> !Unpin for GenFuture<T> {} +#[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] impl<T: Generator<Yield = ()>> Future for GenFuture<T> { type Output = T::Return; - fn poll(self: Pin<&mut Self>, waker: &Waker) -> Poll<Self::Output> { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { // Safe because we're !Unpin + !Drop mapping to a ?Unpin value let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) }; - set_task_waker(waker, || match gen.resume() { + set_task_context(cx, || match gen.resume() { GeneratorState::Yielded(()) => Poll::Pending, GeneratorState::Complete(x) => Poll::Ready(x), }) @@ -43,61 +47,75 @@ impl<T: Generator<Yield = ()>> Future for GenFuture<T> { } thread_local! { - static TLS_WAKER: Cell<Option<NonNull<Waker>>> = Cell::new(None); + static TLS_CX: Cell<Option<NonNull<Context<'static>>>> = Cell::new(None); } -struct SetOnDrop(Option<NonNull<Waker>>); +struct SetOnDrop(Option<NonNull<Context<'static>>>); impl Drop for SetOnDrop { fn drop(&mut self) { - TLS_WAKER.with(|tls_waker| { - tls_waker.set(self.0.take()); + TLS_CX.with(|tls_cx| { + tls_cx.set(self.0.take()); }); } } +#[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] /// Sets the thread-local task context used by async/await futures. -pub fn set_task_waker<F, R>(waker: &Waker, f: F) -> R +pub fn set_task_context<F, R>(cx: &mut Context<'_>, f: F) -> R where F: FnOnce() -> R { - let old_waker = TLS_WAKER.with(|tls_waker| { - tls_waker.replace(Some(NonNull::from(waker))) + // transmute the context's lifetime to 'static so we can store it. + let cx = unsafe { + core::mem::transmute::<&mut Context<'_>, &mut Context<'static>>(cx) + }; + let old_cx = TLS_CX.with(|tls_cx| { + tls_cx.replace(Some(NonNull::from(cx))) }); - let _reset_waker = SetOnDrop(old_waker); + let _reset = SetOnDrop(old_cx); f() } +#[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] -/// Retrieves the thread-local task waker used by async/await futures. +/// Retrieves the thread-local task context used by async/await futures. /// -/// This function acquires exclusive access to the task waker. +/// This function acquires exclusive access to the task context. /// -/// Panics if no waker has been set or if the waker has already been -/// retrieved by a surrounding call to get_task_waker. -pub fn get_task_waker<F, R>(f: F) -> R +/// Panics if no context has been set or if the context has already been +/// retrieved by a surrounding call to get_task_context. +pub fn get_task_context<F, R>(f: F) -> R where - F: FnOnce(&Waker) -> R + F: FnOnce(&mut Context<'_>) -> R { - let waker_ptr = TLS_WAKER.with(|tls_waker| { + let cx_ptr = TLS_CX.with(|tls_cx| { // Clear the entry so that nested `get_task_waker` calls // will fail or set their own value. - tls_waker.replace(None) + tls_cx.replace(None) }); - let _reset_waker = SetOnDrop(waker_ptr); + let _reset = SetOnDrop(cx_ptr); - let waker_ptr = waker_ptr.expect( - "TLS Waker not set. This is a rustc bug. \ + let mut cx_ptr = cx_ptr.expect( + "TLS Context not set. This is a rustc bug. \ Please file an issue on https://github.com/rust-lang/rust."); - unsafe { f(waker_ptr.as_ref()) } + + // Safety: we've ensured exclusive access to the context by + // removing the pointer from TLS, only to be replaced once + // we're done with it. + // + // The pointer that was inserted came from an `&mut Context<'_>`, + // so it is safe to treat as mutable. + unsafe { f(cx_ptr.as_mut()) } } +#[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] /// Polls a future in the current thread-local task waker. -pub fn poll_with_tls_waker<F>(f: Pin<&mut F>) -> Poll<F::Output> +pub fn poll_with_tls_context<F>(f: Pin<&mut F>) -> Poll<F::Output> where F: Future { - get_task_waker(|waker| F::poll(f, waker)) + get_task_context(|cx| F::poll(f, cx)) } diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index a14c10d0b61..aaf628e6c26 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -5,7 +5,8 @@ use crate::io::prelude::*; use crate::cmp; use crate::error; use crate::fmt; -use crate::io::{self, Initializer, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom, IoVec, IoVecMut}; +use crate::io::{self, Initializer, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom, IoSlice, + IoSliceMut}; use crate::memchr; /// The `BufReader` struct adds buffering to any reader. @@ -21,6 +22,10 @@ use crate::memchr; /// times. It also provides no advantage when reading from a source that is /// already in memory, like a `Vec<u8>`. /// +/// When the `BufReader` is dropped, the contents of its buffer will be +/// discarded. Creating multiple instances of a `BufReader` on the same +/// stream can cause data loss. +/// /// [`Read`]: ../../std/io/trait.Read.html /// [`TcpStream::read`]: ../../std/net/struct.TcpStream.html#method.read /// [`TcpStream`]: ../../std/net/struct.TcpStream.html @@ -88,10 +93,10 @@ impl<R: Read> BufReader<R> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(cap: usize, inner: R) -> BufReader<R> { + pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> { unsafe { - let mut buffer = Vec::with_capacity(cap); - buffer.set_len(cap); + let mut buffer = Vec::with_capacity(capacity); + buffer.set_len(capacity); inner.initializer().initialize(&mut buffer); BufReader { inner, @@ -153,7 +158,6 @@ impl<R> BufReader<R> { /// # Examples /// /// ```no_run - /// # #![feature(bufreader_buffer)] /// use std::io::{BufReader, BufRead}; /// use std::fs::File; /// @@ -168,7 +172,7 @@ impl<R> BufReader<R> { /// Ok(()) /// } /// ``` - #[unstable(feature = "bufreader_buffer", issue = "45323")] + #[stable(feature = "bufreader_buffer", since = "1.37.0")] pub fn buffer(&self) -> &[u8] { &self.buf[self.pos..self.cap] } @@ -193,6 +197,13 @@ impl<R> BufReader<R> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn into_inner(self) -> R { self.inner } + + /// Invalidates all data in the internal buffer. + #[inline] + fn discard_buffer(&mut self) { + self.pos = 0; + self.cap = 0; + } } impl<R: Seek> BufReader<R> { @@ -227,6 +238,7 @@ impl<R: Read> Read for BufReader<R> { // (larger than our internal buffer), bypass our internal buffer // entirely. if self.pos == self.cap && buf.len() >= self.buf.len() { + self.discard_buffer(); return self.inner.read(buf); } let nread = { @@ -237,9 +249,10 @@ impl<R: Read> Read for BufReader<R> { Ok(nread) } - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let total_len = bufs.iter().map(|b| b.len()).sum::<usize>(); if self.pos == self.cap && total_len >= self.buf.len() { + self.discard_buffer(); return self.inner.read_vectored(bufs); } let nread = { @@ -278,7 +291,7 @@ impl<R: Read> BufRead for BufReader<R> { #[stable(feature = "rust1", since = "1.0.0")] impl<R> fmt::Debug for BufReader<R> where R: fmt::Debug { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("BufReader") .field("reader", &self.inner) .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buf.len())) @@ -325,14 +338,14 @@ impl<R: Seek> Seek for BufReader<R> { } else { // seek backwards by our remainder, and then by the offset self.inner.seek(SeekFrom::Current(-remainder))?; - self.pos = self.cap; // empty the buffer + self.discard_buffer(); result = self.inner.seek(SeekFrom::Current(n))?; } } else { // Seeking with Start/End doesn't care about our buffer length. result = self.inner.seek(pos)?; } - self.pos = self.cap; // empty the buffer + self.discard_buffer(); Ok(result) } } @@ -341,7 +354,7 @@ impl<R: Seek> Seek for BufReader<R> { /// /// It can be excessively inefficient to work directly with something that /// implements [`Write`]. For example, every call to -/// [`write`][`Tcpstream::write`] on [`TcpStream`] results in a system call. A +/// [`write`][`TcpStream::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. /// @@ -392,7 +405,7 @@ impl<R: Seek> Seek for BufReader<R> { /// the `stream` is dropped. /// /// [`Write`]: ../../std/io/trait.Write.html -/// [`Tcpstream::write`]: ../../std/net/struct.TcpStream.html#method.write +/// [`TcpStream::write`]: ../../std/net/struct.TcpStream.html#method.write /// [`TcpStream`]: ../../std/net/struct.TcpStream.html /// [`flush`]: #method.flush #[stable(feature = "rust1", since = "1.0.0")] @@ -464,10 +477,10 @@ impl<W: Write> BufWriter<W> { /// let mut buffer = BufWriter::with_capacity(100, stream); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(cap: usize, inner: W) -> BufWriter<W> { + pub fn with_capacity(capacity: usize, inner: W) -> BufWriter<W> { BufWriter { inner: Some(inner), - buf: Vec::with_capacity(cap), + buf: Vec::with_capacity(capacity), panicked: false, } } @@ -538,7 +551,6 @@ impl<W: Write> BufWriter<W> { /// # Examples /// /// ```no_run - /// # #![feature(bufreader_buffer)] /// use std::io::BufWriter; /// use std::net::TcpStream; /// @@ -547,7 +559,7 @@ impl<W: Write> BufWriter<W> { /// // See how many bytes are currently buffered /// let bytes_buffered = buf_writer.buffer().len(); /// ``` - #[unstable(feature = "bufreader_buffer", issue = "45323")] + #[stable(feature = "bufreader_buffer", since = "1.37.0")] pub fn buffer(&self) -> &[u8] { &self.buf } @@ -588,7 +600,7 @@ impl<W: Write> Write for BufWriter<W> { } if buf.len() >= self.buf.capacity() { self.panicked = true; - let r = self.inner.as_mut().unwrap().write(buf); + let r = self.get_mut().write(buf); self.panicked = false; r } else { @@ -596,14 +608,14 @@ impl<W: Write> Write for BufWriter<W> { } } - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let total_len = bufs.iter().map(|b| b.len()).sum::<usize>(); if self.buf.len() + total_len > self.buf.capacity() { self.flush_buf()?; } if total_len >= self.buf.capacity() { self.panicked = true; - let r = self.inner.as_mut().unwrap().write_vectored(bufs); + let r = self.get_mut().write_vectored(bufs); self.panicked = false; r } else { @@ -618,7 +630,7 @@ impl<W: Write> Write for BufWriter<W> { #[stable(feature = "rust1", since = "1.0.0")] impl<W: Write> fmt::Debug for BufWriter<W> where W: fmt::Debug { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("BufWriter") .field("writer", &self.inner.as_ref().unwrap()) .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) @@ -726,7 +738,7 @@ impl<W: Send + fmt::Debug> error::Error for IntoInnerError<W> { #[stable(feature = "rust1", since = "1.0.0")] impl<W> fmt::Display for IntoInnerError<W> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.error().fmt(f) } } @@ -740,7 +752,7 @@ impl<W> fmt::Display for IntoInnerError<W> { /// completed, rather than the entire buffer at once. Enter `LineWriter`. It /// does exactly that. /// -/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the +/// Like [`BufWriter`][bufwriter], a `LineWriter`’s buffer will also be flushed when the /// `LineWriter` goes out of scope or when its internal buffer is full. /// /// [bufwriter]: struct.BufWriter.html @@ -838,9 +850,9 @@ impl<W: Write> LineWriter<W> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(cap: usize, inner: W) -> LineWriter<W> { + pub fn with_capacity(capacity: usize, inner: W) -> LineWriter<W> { LineWriter { - inner: BufWriter::with_capacity(cap, inner), + inner: BufWriter::with_capacity(capacity, inner), need_flush: false, } } @@ -968,7 +980,7 @@ impl<W: Write> Write for LineWriter<W> { #[stable(feature = "rust1", since = "1.0.0")] impl<W: Write> fmt::Debug for LineWriter<W> where W: fmt::Debug { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("LineWriter") .field("writer", &self.inner.inner) .field("buffer", @@ -1069,6 +1081,40 @@ mod tests { } #[test] + fn test_buffered_reader_invalidated_after_read() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(3, io::Cursor::new(inner)); + + assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); + reader.consume(3); + + let mut buffer = [0, 0, 0, 0, 0]; + assert_eq!(reader.read(&mut buffer).ok(), Some(5)); + assert_eq!(buffer, [0, 1, 2, 3, 4]); + + assert!(reader.seek_relative(-2).is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).ok(), Some(2)); + assert_eq!(buffer, [3, 4]); + } + + #[test] + fn test_buffered_reader_invalidated_after_seek() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(3, io::Cursor::new(inner)); + + assert_eq!(reader.fill_buf().ok(), Some(&[5, 6, 7][..])); + reader.consume(3); + + assert!(reader.seek(SeekFrom::Current(5)).is_ok()); + + assert!(reader.seek_relative(-2).is_ok()); + let mut buffer = [0, 0]; + assert_eq!(reader.read(&mut buffer).ok(), Some(2)); + assert_eq!(buffer, [3, 4]); + } + + #[test] fn test_buffered_reader_seek_underflow() { // gimmick reader that yields its position modulo 256 for each byte struct PositionReader { @@ -1115,6 +1161,41 @@ mod tests { } #[test] + fn test_buffered_reader_seek_underflow_discard_buffer_between_seeks() { + // gimmick reader that returns Err after first seek + struct ErrAfterFirstSeekReader { + first_seek: bool, + } + impl Read for ErrAfterFirstSeekReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + for x in &mut *buf { + *x = 0; + } + Ok(buf.len()) + } + } + impl Seek for ErrAfterFirstSeekReader { + fn seek(&mut self, _: SeekFrom) -> io::Result<u64> { + if self.first_seek { + self.first_seek = false; + Ok(0) + } else { + Err(io::Error::new(io::ErrorKind::Other, "oh no!")) + } + } + } + + let mut reader = BufReader::with_capacity(5, ErrAfterFirstSeekReader { first_seek: true }); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 0, 0, 0, 0][..])); + + // The following seek will require two underlying seeks. The first will + // succeed but the second will fail. This should still invalidate the + // buffer. + assert!(reader.seek(SeekFrom::Current(i64::min_value())).is_err()); + assert_eq!(reader.buffer().len(), 0); + } + + #[test] fn test_buffered_writer() { let inner = Vec::new(); let mut writer = BufWriter::with_capacity(2, inner); diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 873da0898c7..a94176e7100 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -1,7 +1,7 @@ use crate::io::prelude::*; use crate::cmp; -use crate::io::{self, Initializer, SeekFrom, Error, ErrorKind, IoVec, IoVecMut}; +use crate::io::{self, Initializer, SeekFrom, Error, ErrorKind, IoSlice, IoSliceMut}; use core::convert::TryInto; @@ -72,7 +72,7 @@ use core::convert::TryInto; /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Cursor<T> { inner: T, pos: u64, @@ -212,6 +212,14 @@ impl<T> io::Seek for Cursor<T> where T: AsRef<[u8]> { "invalid seek to a negative or overflowing position")) } } + + fn stream_len(&mut self) -> io::Result<u64> { + Ok(self.inner.as_ref().len() as u64) + } + + fn stream_position(&mut self) -> io::Result<u64> { + Ok(self.pos) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -222,7 +230,7 @@ impl<T> Read for Cursor<T> where T: AsRef<[u8]> { Ok(n) } - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let mut nread = 0; for buf in bufs { let n = self.read(buf)?; @@ -257,6 +265,7 @@ impl<T> BufRead for Cursor<T> where T: AsRef<[u8]> { } // Non-resizing write implementation +#[inline] fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<usize> { let pos = cmp::min(*pos_mut, slice.len() as u64); let amt = (&mut slice[(pos as usize)..]).write(buf)?; @@ -264,10 +273,11 @@ fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<us Ok(amt) } +#[inline] fn slice_write_vectored( pos_mut: &mut u64, slice: &mut [u8], - bufs: &[IoVec<'_>], + bufs: &[IoSlice<'_>], ) -> io::Result<usize> { let mut nwritten = 0; @@ -311,7 +321,7 @@ fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usi fn vec_write_vectored( pos_mut: &mut u64, vec: &mut Vec<u8>, - bufs: &[IoVec<'_>], + bufs: &[IoSlice<'_>], ) -> io::Result<usize> { let mut nwritten = 0; @@ -329,10 +339,11 @@ impl Write for Cursor<&mut [u8]> { } #[inline] - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { slice_write_vectored(&mut self.pos, self.inner, bufs) } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } } @@ -342,10 +353,11 @@ impl Write for Cursor<&mut Vec<u8>> { vec_write(&mut self.pos, self.inner, buf) } - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { vec_write_vectored(&mut self.pos, self.inner, bufs) } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } } @@ -355,10 +367,11 @@ impl Write for Cursor<Vec<u8>> { vec_write(&mut self.pos, &mut self.inner, buf) } - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { vec_write_vectored(&mut self.pos, &mut self.inner, bufs) } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } } @@ -370,17 +383,18 @@ impl Write for Cursor<Box<[u8]>> { } #[inline] - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { slice_write_vectored(&mut self.pos, &mut self.inner, bufs) } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } } #[cfg(test)] mod tests { use crate::io::prelude::*; - use crate::io::{Cursor, SeekFrom, IoVec, IoVecMut}; + use crate::io::{Cursor, SeekFrom, IoSlice, IoSliceMut}; #[test] fn test_vec_writer() { @@ -389,7 +403,7 @@ mod tests { assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); assert_eq!(writer.write_vectored( - &[IoVec::new(&[]), IoVec::new(&[8, 9]), IoVec::new(&[10])], + &[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])], ).unwrap(), 3); let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; assert_eq!(writer, b); @@ -402,7 +416,7 @@ mod tests { assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); assert_eq!(writer.write_vectored( - &[IoVec::new(&[]), IoVec::new(&[8, 9]), IoVec::new(&[10])], + &[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])], ).unwrap(), 3); let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; assert_eq!(&writer.get_ref()[..], b); @@ -416,7 +430,7 @@ mod tests { assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); assert_eq!(writer.write_vectored( - &[IoVec::new(&[]), IoVec::new(&[8, 9]), IoVec::new(&[10])], + &[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])], ).unwrap(), 3); let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; assert_eq!(&writer.get_ref()[..], b); @@ -444,18 +458,21 @@ mod tests { fn test_box_slice_writer_vectored() { let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); assert_eq!(writer.position(), 0); - assert_eq!(writer.write_vectored(&[IoVec::new(&[0])]).unwrap(), 1); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1); assert_eq!(writer.position(), 1); assert_eq!( - writer.write_vectored(&[IoVec::new(&[1, 2, 3]), IoVec::new(&[4, 5, 6, 7])]).unwrap(), + writer.write_vectored(&[ + IoSlice::new(&[1, 2, 3]), + IoSlice::new(&[4, 5, 6, 7]), + ]).unwrap(), 7, ); assert_eq!(writer.position(), 8); assert_eq!(writer.write_vectored(&[]).unwrap(), 0); assert_eq!(writer.position(), 8); - assert_eq!(writer.write_vectored(&[IoVec::new(&[8, 9])]).unwrap(), 1); - assert_eq!(writer.write_vectored(&[IoVec::new(&[10])]).unwrap(), 0); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0); let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; assert_eq!(&**writer.get_ref(), b); } @@ -487,11 +504,11 @@ mod tests { { let mut writer = Cursor::new(&mut buf[..]); assert_eq!(writer.position(), 0); - assert_eq!(writer.write_vectored(&[IoVec::new(&[0])]).unwrap(), 1); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1); assert_eq!(writer.position(), 1); assert_eq!( writer.write_vectored( - &[IoVec::new(&[1, 2, 3]), IoVec::new(&[4, 5, 6, 7])], + &[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7])], ).unwrap(), 7, ); @@ -499,8 +516,8 @@ mod tests { assert_eq!(writer.write_vectored(&[]).unwrap(), 0); assert_eq!(writer.position(), 8); - assert_eq!(writer.write_vectored(&[IoVec::new(&[8, 9])]).unwrap(), 1); - assert_eq!(writer.write_vectored(&[IoVec::new(&[10])]).unwrap(), 0); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1); + assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0); } let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; assert_eq!(buf, b); @@ -570,11 +587,14 @@ mod tests { fn test_mem_reader_vectored() { let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); let mut buf = []; - assert_eq!(reader.read_vectored(&mut [IoVecMut::new(&mut buf)]).unwrap(), 0); + assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); assert_eq!(reader.position(), 0); let mut buf = [0]; assert_eq!( - reader.read_vectored(&mut [IoVecMut::new(&mut []), IoVecMut::new(&mut buf)]).unwrap(), + reader.read_vectored(&mut [ + IoSliceMut::new(&mut []), + IoSliceMut::new(&mut buf), + ]).unwrap(), 1, ); assert_eq!(reader.position(), 1); @@ -583,9 +603,10 @@ mod tests { let mut buf1 = [0; 4]; let mut buf2 = [0; 4]; assert_eq!( - reader.read_vectored( - &mut [IoVecMut::new(&mut buf1), IoVecMut::new(&mut buf2)], - ).unwrap(), + reader.read_vectored(&mut [ + IoSliceMut::new(&mut buf1), + IoSliceMut::new(&mut buf2), + ]).unwrap(), 7, ); let b1: &[_] = &[1, 2, 3, 4]; @@ -621,11 +642,14 @@ mod tests { fn test_boxed_slice_reader_vectored() { let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); let mut buf = []; - assert_eq!(reader.read_vectored(&mut [IoVecMut::new(&mut buf)]).unwrap(), 0); + assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); assert_eq!(reader.position(), 0); let mut buf = [0]; assert_eq!( - reader.read_vectored(&mut [IoVecMut::new(&mut []), IoVecMut::new(&mut buf)]).unwrap(), + reader.read_vectored(&mut [ + IoSliceMut::new(&mut []), + IoSliceMut::new(&mut buf), + ]).unwrap(), 1, ); assert_eq!(reader.position(), 1); @@ -635,7 +659,7 @@ mod tests { let mut buf2 = [0; 4]; assert_eq!( reader.read_vectored( - &mut [IoVecMut::new(&mut buf1), IoVecMut::new(&mut buf2)], + &mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)], ).unwrap(), 7, ); @@ -681,10 +705,13 @@ mod tests { let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; let reader = &mut &in_buf[..]; let mut buf = []; - assert_eq!(reader.read_vectored(&mut [IoVecMut::new(&mut buf)]).unwrap(), 0); + assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0); let mut buf = [0]; assert_eq!( - reader.read_vectored(&mut [IoVecMut::new(&mut []), IoVecMut::new(&mut buf)]).unwrap(), + reader.read_vectored(&mut [ + IoSliceMut::new(&mut []), + IoSliceMut::new(&mut buf), + ]).unwrap(), 1, ); assert_eq!(reader.len(), 7); @@ -694,7 +721,7 @@ mod tests { let mut buf2 = [0; 4]; assert_eq!( reader.read_vectored( - &mut [IoVecMut::new(&mut buf1), IoVecMut::new(&mut buf2)], + &mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)], ).unwrap(), 7, ); diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs index 614b79124cc..c29a68e6f02 100644 --- a/src/libstd/io/error.rs +++ b/src/libstd/io/error.rs @@ -59,7 +59,7 @@ pub struct Error { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.repr, f) } } @@ -413,7 +413,7 @@ impl Error { /// } /// /// impl Display for MyError { - /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "MyError: {}", &self.v) /// } /// } @@ -512,7 +512,7 @@ impl Error { } impl fmt::Debug for Repr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Repr::Os(code) => fmt.debug_struct("Os") @@ -527,7 +527,7 @@ impl fmt::Debug for Repr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.repr { Repr::Os(code) => { let detail = sys::os::error_string(code); @@ -612,7 +612,7 @@ mod test { struct TestError; impl fmt::Display for TestError { - fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { Ok(()) } } diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs index b286e4016da..c959f2d389b 100644 --- a/src/libstd/io/impls.rs +++ b/src/libstd/io/impls.rs @@ -1,6 +1,6 @@ use crate::cmp; -use crate::io::{self, SeekFrom, Read, Initializer, Write, Seek, BufRead, Error, ErrorKind, IoVecMut, - IoVec}; +use crate::io::{self, SeekFrom, Read, Initializer, Write, Seek, BufRead, Error, ErrorKind, + IoSliceMut, IoSlice}; use crate::fmt; use crate::mem; @@ -15,7 +15,7 @@ impl<R: Read + ?Sized> Read for &mut R { } #[inline] - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { (**self).read_vectored(bufs) } @@ -45,7 +45,7 @@ impl<W: Write + ?Sized> Write for &mut W { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) } #[inline] - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { (**self).write_vectored(bufs) } @@ -58,7 +58,7 @@ impl<W: Write + ?Sized> Write for &mut W { } #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> { + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { (**self).write_fmt(fmt) } } @@ -94,7 +94,7 @@ impl<R: Read + ?Sized> Read for Box<R> { } #[inline] - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { (**self).read_vectored(bufs) } @@ -124,7 +124,7 @@ impl<W: Write + ?Sized> Write for Box<W> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) } #[inline] - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { (**self).write_vectored(bufs) } @@ -137,7 +137,7 @@ impl<W: Write + ?Sized> Write for Box<W> { } #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> { + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { (**self).write_fmt(fmt) } } @@ -207,7 +207,7 @@ impl Read for &[u8] { } #[inline] - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let mut nread = 0; for buf in bufs { nread += self.read(buf)?; @@ -280,7 +280,7 @@ impl Write for &mut [u8] { } #[inline] - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let mut nwritten = 0; for buf in bufs { nwritten += self.write(buf)?; @@ -316,7 +316,7 @@ impl Write for Vec<u8> { } #[inline] - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let len = bufs.iter().map(|b| b.len()).sum(); self.reserve(len); for buf in bufs { diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index e3e2754a7aa..f2b6ce6feb2 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -24,9 +24,9 @@ //! let mut buffer = [0; 10]; //! //! // read up to 10 bytes -//! f.read(&mut buffer)?; +//! let n = f.read(&mut buffer)?; //! -//! println!("The bytes: {:?}", buffer); +//! println!("The bytes: {:?}", &buffer[..n]); //! Ok(()) //! } //! ``` @@ -56,9 +56,9 @@ //! f.seek(SeekFrom::End(-10))?; //! //! // read up to 10 bytes -//! f.read(&mut buffer)?; +//! let n = f.read(&mut buffer)?; //! -//! println!("The bytes: {:?}", buffer); +//! println!("The bytes: {:?}", &buffer[..n]); //! Ok(()) //! } //! ``` @@ -390,6 +390,28 @@ fn read_to_end_with_reservation<R: Read + ?Sized>(r: &mut R, ret } +pub(crate) fn default_read_vectored<F>(read: F, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> +where + F: FnOnce(&mut [u8]) -> Result<usize> +{ + let buf = bufs + .iter_mut() + .find(|b| !b.is_empty()) + .map_or(&mut [][..], |b| &mut **b); + read(buf) +} + +pub(crate) fn default_write_vectored<F>(write: F, bufs: &[IoSlice<'_>]) -> Result<usize> +where + F: FnOnce(&[u8]) -> Result<usize> +{ + let buf = bufs + .iter() + .find(|b| !b.is_empty()) + .map_or(&[][..], |b| &**b); + write(buf) +} + /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -484,9 +506,18 @@ pub trait Read { /// /// No guarantees are provided about the contents of `buf` when this /// function is called, implementations cannot rely on any property of the - /// contents of `buf` being true. It is recommended that implementations + /// contents of `buf` being true. It is recommended that *implementations* /// only write data to `buf` instead of reading its contents. /// + /// Correspondingly, however, *callers* of this method may not assume any guarantees + /// about how the implementation uses `buf`. The trait is safe to implement, + /// so it is possible that the code that's supposed to write to the buffer might also read + /// from it. It is your responsibility to make sure that `buf` is initialized + /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one + /// obtains via [`MaybeUninit<T>`]) is not safe, and can lead to undefined behavior. + /// + /// [`MaybeUninit<T>`]: ../mem/union.MaybeUninit.html + /// /// # Errors /// /// If this function encounters any form of I/O or other error, an error @@ -515,7 +546,9 @@ pub trait Read { /// let mut buffer = [0; 10]; /// /// // read up to 10 bytes - /// f.read(&mut buffer[..])?; + /// let n = f.read(&mut buffer[..])?; + /// + /// println!("The bytes: {:?}", &buffer[..n]); /// Ok(()) /// } /// ``` @@ -528,14 +561,11 @@ pub trait Read { /// written to possibly being only partially filled. This method must behave /// as a single call to `read` with the buffers concatenated would. /// - /// The default implementation simply passes the first nonempty buffer to - /// `read`. - #[unstable(feature = "iovec", issue = "58452")] - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> Result<usize> { - match bufs.iter_mut().find(|b| !b.is_empty()) { - Some(buf) => self.read(buf), - None => Ok(0), - } + /// The default implementation calls `read` with either the first nonempty + /// buffer provided, or an empty one if none exists. + #[stable(feature = "iovec", since = "1.36.0")] + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> { + default_read_vectored(|b| self.read(b), bufs) } /// Determines if this `Read`er can work with buffers of uninitialized @@ -890,32 +920,88 @@ pub trait Read { /// It is semantically a wrapper around an `&mut [u8]`, but is guaranteed to be /// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on /// Windows. -#[unstable(feature = "iovec", issue = "58452")] +#[stable(feature = "iovec", since = "1.36.0")] #[repr(transparent)] -pub struct IoVecMut<'a>(sys::io::IoVecMut<'a>); +pub struct IoSliceMut<'a>(sys::io::IoSliceMut<'a>); -#[unstable(feature = "iovec", issue = "58452")] -impl<'a> fmt::Debug for IoVecMut<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> fmt::Debug for IoSliceMut<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.0.as_slice(), fmt) } } -impl<'a> IoVecMut<'a> { - /// Creates a new `IoVecMut` wrapping a byte slice. +impl<'a> IoSliceMut<'a> { + /// Creates a new `IoSliceMut` wrapping a byte slice. /// /// # Panics /// /// Panics on Windows if the slice is larger than 4GB. - #[unstable(feature = "iovec", issue = "58452")] + #[stable(feature = "iovec", since = "1.36.0")] #[inline] - pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> { - IoVecMut(sys::io::IoVecMut::new(buf)) + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut(sys::io::IoSliceMut::new(buf)) + } + + /// Advance the internal cursor of the slice. + /// + /// # Notes + /// + /// Elements in the slice may be modified if the cursor is not advanced to + /// the end of the slice. For example if we have a slice of buffers with 2 + /// `IoSliceMut`s, both of length 8, and we advance the cursor by 10 bytes + /// the first `IoSliceMut` will be untouched however the second will be + /// modified to remove the first 2 bytes (10 - 8). + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_advance)] + /// + /// use std::io::IoSliceMut; + /// use std::mem; + /// use std::ops::Deref; + /// + /// let mut buf1 = [1; 8]; + /// let mut buf2 = [2; 16]; + /// let mut buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSliceMut::new(&mut buf1), + /// IoSliceMut::new(&mut buf2), + /// IoSliceMut::new(&mut buf3), + /// ][..]; + /// + /// // Mark 10 bytes as read. + /// bufs = IoSliceMut::advance(mem::replace(&mut bufs, &mut []), 10); + /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); + /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + /// ``` + #[unstable(feature = "io_slice_advance", issue = "62726")] + #[inline] + pub fn advance<'b>(bufs: &'b mut [IoSliceMut<'a>], n: usize) -> &'b mut [IoSliceMut<'a>] { + // Number of buffers to remove. + let mut remove = 0; + // Total length of all the to be removed buffers. + let mut accumulated_len = 0; + for buf in bufs.iter() { + if accumulated_len + buf.len() > n { + break; + } else { + accumulated_len += buf.len(); + remove += 1; + } + } + + let bufs = &mut bufs[remove..]; + if !bufs.is_empty() { + bufs[0].0.advance(n - accumulated_len) + } + bufs } } -#[unstable(feature = "iovec", issue = "58452")] -impl<'a> Deref for IoVecMut<'a> { +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> Deref for IoSliceMut<'a> { type Target = [u8]; #[inline] @@ -924,8 +1010,8 @@ impl<'a> Deref for IoVecMut<'a> { } } -#[unstable(feature = "iovec", issue = "58452")] -impl<'a> DerefMut for IoVecMut<'a> { +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> DerefMut for IoSliceMut<'a> { #[inline] fn deref_mut(&mut self) -> &mut [u8] { self.0.as_mut_slice() @@ -937,32 +1023,87 @@ impl<'a> DerefMut for IoVecMut<'a> { /// It is semantically a wrapper around an `&[u8]`, but is guaranteed to be /// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on /// Windows. -#[unstable(feature = "iovec", issue = "58452")] +#[stable(feature = "iovec", since = "1.36.0")] #[repr(transparent)] -pub struct IoVec<'a>(sys::io::IoVec<'a>); +pub struct IoSlice<'a>(sys::io::IoSlice<'a>); -#[unstable(feature = "iovec", issue = "58452")] -impl<'a> fmt::Debug for IoVec<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> fmt::Debug for IoSlice<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.0.as_slice(), fmt) } } -impl<'a> IoVec<'a> { - /// Creates a new `IoVec` wrapping a byte slice. +impl<'a> IoSlice<'a> { + /// Creates a new `IoSlice` wrapping a byte slice. /// /// # Panics /// /// Panics on Windows if the slice is larger than 4GB. - #[unstable(feature = "iovec", issue = "58452")] + #[stable(feature = "iovec", since = "1.36.0")] #[inline] - pub fn new(buf: &'a [u8]) -> IoVec<'a> { - IoVec(sys::io::IoVec::new(buf)) + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice(sys::io::IoSlice::new(buf)) + } + + /// Advance the internal cursor of the slice. + /// + /// # Notes + /// + /// Elements in the slice may be modified if the cursor is not advanced to + /// the end of the slice. For example if we have a slice of buffers with 2 + /// `IoSlice`s, both of length 8, and we advance the cursor by 10 bytes the + /// first `IoSlice` will be untouched however the second will be modified to + /// remove the first 2 bytes (10 - 8). + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_advance)] + /// + /// use std::io::IoSlice; + /// use std::mem; + /// use std::ops::Deref; + /// + /// let mut buf1 = [1; 8]; + /// let mut buf2 = [2; 16]; + /// let mut buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSlice::new(&mut buf1), + /// IoSlice::new(&mut buf2), + /// IoSlice::new(&mut buf3), + /// ][..]; + /// + /// // Mark 10 bytes as written. + /// bufs = IoSlice::advance(mem::replace(&mut bufs, &mut []), 10); + /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); + /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + #[unstable(feature = "io_slice_advance", issue = "62726")] + #[inline] + pub fn advance<'b>(bufs: &'b mut [IoSlice<'a>], n: usize) -> &'b mut [IoSlice<'a>] { + // Number of buffers to remove. + let mut remove = 0; + // Total length of all the to be removed buffers. + let mut accumulated_len = 0; + for buf in bufs.iter() { + if accumulated_len + buf.len() > n { + break; + } else { + accumulated_len += buf.len(); + remove += 1; + } + } + + let bufs = &mut bufs[remove..]; + if !bufs.is_empty() { + bufs[0].0.advance(n - accumulated_len) + } + bufs } } -#[unstable(feature = "iovec", issue = "58452")] -impl<'a> Deref for IoVec<'a> { +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> Deref for IoSlice<'a> { type Target = [u8]; #[inline] @@ -1043,12 +1184,23 @@ impl Initializer { /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { +/// let data = b"some bytes"; +/// +/// let mut pos = 0; /// let mut buffer = File::create("foo.txt")?; /// -/// buffer.write(b"some bytes")?; +/// while pos < data.len() { +/// let bytes_written = buffer.write(&data[pos..])?; +/// pos += bytes_written; +/// } /// Ok(()) /// } /// ``` +/// +/// The trait also provides convenience methods like [`write_all`], which calls +/// `write` in a loop until its entire input has been written. +/// +/// [`write_all`]: #method.write_all #[stable(feature = "rust1", since = "1.0.0")] #[doc(spotlight)] pub trait Write { @@ -1064,7 +1216,7 @@ pub trait Write { /// an [`Err`] variant. /// /// If the return value is [`Ok(n)`] then it must be guaranteed that - /// `0 <= n <= buf.len()`. A return value of `0` typically means that the + /// `n <= buf.len()`. A return value of `0` typically means that the /// underlying object is no longer able to accept bytes and will likely not /// be able to in the future as well, or that the buffer provided is empty. /// @@ -1103,18 +1255,15 @@ pub trait Write { /// Like `write`, except that it writes from a slice of buffers. /// - /// Data is copied to from each buffer in order, with the final buffer + /// Data is copied from each buffer in order, with the final buffer /// read from possibly being only partially consumed. This method must /// behave as a call to `write` with the buffers concatenated would. /// - /// The default implementation simply passes the first nonempty buffer to - /// `write`. - #[unstable(feature = "iovec", issue = "58452")] - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> Result<usize> { - match bufs.iter().find(|b| !b.is_empty()) { - Some(buf) => self.write(buf), - None => Ok(0), - } + /// The default implementation calls `write` with either the first nonempty + /// buffer provided, or an empty one if none exists. + #[stable(feature = "iovec", since = "1.36.0")] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> { + default_write_vectored(|b| self.write(b), bufs) } /// Flush this output stream, ensuring that all intermediately buffered @@ -1135,7 +1284,7 @@ pub trait Write { /// fn main() -> std::io::Result<()> { /// let mut buffer = BufWriter::new(File::create("foo.txt")?); /// - /// buffer.write(b"some bytes")?; + /// buffer.write_all(b"some bytes")?; /// buffer.flush()?; /// Ok(()) /// } @@ -1226,7 +1375,7 @@ pub trait Write { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()> { + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { // Create a shim which translates a Write to a fmt::Write and saves // off I/O errors. instead of discarding them struct Adaptor<'a, T: ?Sized + 'a> { @@ -1329,6 +1478,85 @@ pub trait Seek { /// [`SeekFrom::Start`]: enum.SeekFrom.html#variant.Start #[stable(feature = "rust1", since = "1.0.0")] fn seek(&mut self, pos: SeekFrom) -> Result<u64>; + + /// Returns the length of this stream (in bytes). + /// + /// This method is implemented using up to three seek operations. If this + /// method returns successfully, the seek position is unchanged (i.e. the + /// position before calling this method is the same as afterwards). + /// However, if this method returns an error, the seek position is + /// unspecified. + /// + /// If you need to obtain the length of *many* streams and you don't care + /// about the seek position afterwards, you can reduce the number of seek + /// operations by simply calling `seek(SeekFrom::End(0))` and using its + /// return value (it is also the stream length). + /// + /// Note that length of a stream can change over time (for example, when + /// data is appended to a file). So calling this method multiple times does + /// not necessarily return the same length each time. + /// + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_convenience)] + /// use std::{ + /// io::{self, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// + /// let len = f.stream_len()?; + /// println!("The file is currently {} bytes long", len); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_convenience", issue = "59359")] + fn stream_len(&mut self) -> Result<u64> { + let old_pos = self.stream_position()?; + let len = self.seek(SeekFrom::End(0))?; + + // Avoid seeking a third time when we were already at the end of the + // stream. The branch is usually way cheaper than a seek operation. + if old_pos != len { + self.seek(SeekFrom::Start(old_pos))?; + } + + Ok(len) + } + + /// Returns the current seek position from the start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. + /// + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_convenience)] + /// use std::{ + /// io::{self, BufRead, BufReader, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = BufReader::new(File::open("foo.txt")?); + /// + /// let before = f.stream_position()?; + /// f.read_line(&mut String::new())?; + /// let after = f.stream_position()?; + /// + /// println!("The first line was {} bytes long", after - before); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "seek_convenience", issue = "59359")] + fn stream_position(&mut self) -> Result<u64> { + self.seek(SeekFrom::Current(0)) + } } /// Enumeration of possible methods to seek within an I/O object. @@ -1471,18 +1699,13 @@ pub trait BufRead: Read { /// let stdin = io::stdin(); /// let mut stdin = stdin.lock(); /// - /// // we can't have two `&mut` references to `stdin`, so use a block - /// // to end the borrow early. - /// let length = { - /// let buffer = stdin.fill_buf().unwrap(); + /// let buffer = stdin.fill_buf().unwrap(); /// - /// // work with buffer - /// println!("{:?}", buffer); - /// - /// buffer.len() - /// }; + /// // work with buffer + /// println!("{:?}", buffer); /// /// // ensure the bytes we worked with aren't returned again later + /// let length = buffer.len(); /// stdin.consume(length); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1798,7 +2021,7 @@ impl<T, U> Chain<T, U> { #[stable(feature = "std_debug", since = "1.16.0")] impl<T: fmt::Debug, U: fmt::Debug> fmt::Debug for Chain<T, U> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Chain") .field("t", &self.first) .field("u", &self.second) @@ -1811,14 +2034,14 @@ 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 if buf.len() != 0 => self.done_first = true, + 0 if !buf.is_empty() => self.done_first = true, n => return Ok(n), } } self.second.read(buf) } - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> { if !self.done_first { match self.first.read_vectored(bufs)? { 0 if bufs.iter().any(|b| !b.is_empty()) => self.done_first = true, @@ -1843,7 +2066,7 @@ impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> { fn fill_buf(&mut self) -> Result<&[u8]> { if !self.done_first { match self.first.fill_buf()? { - buf if buf.len() == 0 => { self.done_first = true; } + buf if buf.is_empty() => { self.done_first = true; } buf => return Ok(buf), } } @@ -2156,9 +2379,10 @@ impl<B: BufRead> Iterator for Lines<B> { #[cfg(test)] mod tests { use crate::io::prelude::*; - use crate::io; - use super::Cursor; - use super::repeat; + use super::{Cursor, SeekFrom, repeat}; + use crate::io::{self, IoSlice, IoSliceMut}; + use crate::mem; + use crate::ops::Deref; #[test] #[cfg_attr(target_os = "emscripten", ignore)] @@ -2380,4 +2604,135 @@ mod tests { super::read_to_end(&mut lr, &mut vec) }); } + + #[test] + fn seek_len() -> io::Result<()> { + let mut c = Cursor::new(vec![0; 15]); + assert_eq!(c.stream_len()?, 15); + + c.seek(SeekFrom::End(0))?; + let old_pos = c.stream_position()?; + assert_eq!(c.stream_len()?, 15); + assert_eq!(c.stream_position()?, old_pos); + + c.seek(SeekFrom::Start(7))?; + c.seek(SeekFrom::Current(2))?; + let old_pos = c.stream_position()?; + assert_eq!(c.stream_len()?, 15); + assert_eq!(c.stream_position()?, old_pos); + + Ok(()) + } + + #[test] + fn seek_position() -> io::Result<()> { + // All `asserts` are duplicated here to make sure the method does not + // change anything about the seek state. + let mut c = Cursor::new(vec![0; 15]); + assert_eq!(c.stream_position()?, 0); + assert_eq!(c.stream_position()?, 0); + + c.seek(SeekFrom::End(0))?; + assert_eq!(c.stream_position()?, 15); + assert_eq!(c.stream_position()?, 15); + + + c.seek(SeekFrom::Start(7))?; + c.seek(SeekFrom::Current(2))?; + assert_eq!(c.stream_position()?, 9); + assert_eq!(c.stream_position()?, 9); + + c.seek(SeekFrom::End(-3))?; + c.seek(SeekFrom::Current(1))?; + c.seek(SeekFrom::Current(-5))?; + assert_eq!(c.stream_position()?, 8); + assert_eq!(c.stream_position()?, 8); + + Ok(()) + } + + #[test] + fn io_slice_mut_advance() { + let mut buf1 = [1; 8]; + let mut buf2 = [2; 16]; + let mut buf3 = [3; 8]; + let mut bufs = &mut [ + IoSliceMut::new(&mut buf1), + IoSliceMut::new(&mut buf2), + IoSliceMut::new(&mut buf3), + ][..]; + + // Only in a single buffer.. + bufs = IoSliceMut::advance(mem::replace(&mut bufs, &mut []), 1); + assert_eq!(bufs[0].deref(), [1; 7].as_ref()); + assert_eq!(bufs[1].deref(), [2; 16].as_ref()); + assert_eq!(bufs[2].deref(), [3; 8].as_ref()); + + // Removing a buffer, leaving others as is. + bufs = IoSliceMut::advance(mem::replace(&mut bufs, &mut []), 7); + assert_eq!(bufs[0].deref(), [2; 16].as_ref()); + assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + + // Removing a buffer and removing from the next buffer. + bufs = IoSliceMut::advance(mem::replace(&mut bufs, &mut []), 18); + assert_eq!(bufs[0].deref(), [3; 6].as_ref()); + } + + #[test] + fn io_slice_mut_advance_empty_slice() { + let mut empty_bufs = &mut [][..]; + // Shouldn't panic. + IoSliceMut::advance(&mut empty_bufs, 1); + } + + #[test] + fn io_slice_mut_advance_beyond_total_length() { + let mut buf1 = [1; 8]; + let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..]; + + // Going beyond the total length should be ok. + bufs = IoSliceMut::advance(mem::replace(&mut bufs, &mut []), 9); + assert!(bufs.is_empty()); + } + + #[test] + fn io_slice_advance() { + let mut buf1 = [1; 8]; + let mut buf2 = [2; 16]; + let mut buf3 = [3; 8]; + let mut bufs = + &mut [IoSlice::new(&mut buf1), IoSlice::new(&mut buf2), IoSlice::new(&mut buf3)][..]; + + // Only in a single buffer.. + bufs = IoSlice::advance(mem::replace(&mut bufs, &mut []), 1); + assert_eq!(bufs[0].deref(), [1; 7].as_ref()); + assert_eq!(bufs[1].deref(), [2; 16].as_ref()); + assert_eq!(bufs[2].deref(), [3; 8].as_ref()); + + // Removing a buffer, leaving others as is. + bufs = IoSlice::advance(mem::replace(&mut bufs, &mut []), 7); + assert_eq!(bufs[0].deref(), [2; 16].as_ref()); + assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + + // Removing a buffer and removing from the next buffer. + bufs = IoSlice::advance(mem::replace(&mut bufs, &mut []), 18); + assert_eq!(bufs[0].deref(), [3; 6].as_ref()); + } + + #[test] + fn io_slice_advance_empty_slice() { + let mut empty_bufs = &mut [][..]; + // Shouldn't panic. + IoSlice::advance(&mut empty_bufs, 1); + } + + #[test] + fn io_slice_advance_beyond_total_length() { + let mut buf1 = [1; 8]; + let mut bufs = &mut [IoSlice::new(&mut buf1)][..]; + + // Going beyond the total length should be ok. + bufs = IoSlice::advance(mem::replace(&mut bufs, &mut []), 9); + assert!(bufs.is_empty()); + } } diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index d53a294fa6a..990c0eb8955 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -5,7 +5,7 @@ use crate::io::prelude::*; use crate::cell::RefCell; use crate::fmt; use crate::io::lazy::Lazy; -use crate::io::{self, Initializer, BufReader, LineWriter}; +use crate::io::{self, Initializer, BufReader, LineWriter, IoSlice, IoSliceMut}; use crate::sync::{Arc, Mutex, MutexGuard}; use crate::sys::stdio; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; @@ -75,6 +75,10 @@ fn stderr_raw() -> io::Result<StderrRaw> { stdio::Stderr::new().map(StderrRaw) } impl Read for StdinRaw { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -82,10 +86,20 @@ impl Read for StdinRaw { } impl Write for StdoutRaw { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + fn flush(&mut self) -> io::Result<()> { self.0.flush() } } impl Write for StderrRaw { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + fn flush(&mut self) -> io::Result<()> { self.0.flush() } } @@ -102,6 +116,14 @@ impl<W: io::Write> io::Write for Maybe<W> { } } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let total = bufs.iter().map(|b| b.len()).sum(); + match self { + Maybe::Real(w) => handle_ebadf(w.write_vectored(bufs), total), + Maybe::Fake => Ok(total), + } + } + fn flush(&mut self) -> io::Result<()> { match *self { Maybe::Real(ref mut w) => handle_ebadf(w.flush(), ()), @@ -117,6 +139,13 @@ impl<R: io::Read> io::Read for Maybe<R> { Maybe::Fake => Ok(0) } } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + match self { + Maybe::Real(r) => handle_ebadf(r.read_vectored(bufs), 0), + Maybe::Fake => Ok(0) + } + } } fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> { @@ -255,7 +284,7 @@ impl Stdin { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> StdinLock { + pub fn lock(&self) -> StdinLock<'_> { StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } } @@ -295,7 +324,7 @@ impl Stdin { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stdin { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Stdin { .. }") } } @@ -305,6 +334,9 @@ impl Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.lock().read(buf) } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.lock().read_vectored(bufs) + } #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -325,6 +357,11 @@ impl Read for StdinLock<'_> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.inner.read(buf) } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.inner.read_vectored(bufs) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -339,7 +376,7 @@ impl BufRead for StdinLock<'_> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for StdinLock<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("StdinLock { .. }") } } @@ -405,7 +442,7 @@ pub struct StdoutLock<'a> { /// use std::io::{self, Write}; /// /// fn main() -> io::Result<()> { -/// io::stdout().write(b"hello world")?; +/// io::stdout().write_all(b"hello world")?; /// /// Ok(()) /// } @@ -420,7 +457,7 @@ pub struct StdoutLock<'a> { /// let stdout = io::stdout(); /// let mut handle = stdout.lock(); /// -/// handle.write(b"hello world")?; +/// handle.write_all(b"hello world")?; /// /// Ok(()) /// } @@ -460,20 +497,20 @@ impl Stdout { /// let stdout = io::stdout(); /// let mut handle = stdout.lock(); /// - /// handle.write(b"hello world")?; + /// handle.write_all(b"hello world")?; /// /// Ok(()) /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> StdoutLock { + pub fn lock(&self) -> StdoutLock<'_> { StdoutLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } } } #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stdout { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Stdout { .. }") } } @@ -483,13 +520,16 @@ impl Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.lock().write(buf) } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.lock().write_vectored(bufs) + } fn flush(&mut self) -> io::Result<()> { self.lock().flush() } fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { self.lock().write_all(buf) } - fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> { + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { self.lock().write_fmt(args) } } @@ -498,6 +538,9 @@ impl Write for StdoutLock<'_> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.inner.borrow_mut().write(buf) } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.inner.borrow_mut().write_vectored(bufs) + } fn flush(&mut self) -> io::Result<()> { self.inner.borrow_mut().flush() } @@ -505,7 +548,7 @@ impl Write for StdoutLock<'_> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for StdoutLock<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("StdoutLock { .. }") } } @@ -558,7 +601,7 @@ pub struct StderrLock<'a> { /// use std::io::{self, Write}; /// /// fn main() -> io::Result<()> { -/// io::stderr().write(b"hello world")?; +/// io::stderr().write_all(b"hello world")?; /// /// Ok(()) /// } @@ -573,7 +616,7 @@ pub struct StderrLock<'a> { /// let stderr = io::stderr(); /// let mut handle = stderr.lock(); /// -/// handle.write(b"hello world")?; +/// handle.write_all(b"hello world")?; /// /// Ok(()) /// } @@ -613,20 +656,20 @@ impl Stderr { /// let stderr = io::stderr(); /// let mut handle = stderr.lock(); /// - /// handle.write(b"hello world")?; + /// handle.write_all(b"hello world")?; /// /// Ok(()) /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> StderrLock { + pub fn lock(&self) -> StderrLock<'_> { StderrLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } } } #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stderr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Stderr { .. }") } } @@ -636,13 +679,16 @@ impl Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.lock().write(buf) } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.lock().write_vectored(bufs) + } fn flush(&mut self) -> io::Result<()> { self.lock().flush() } fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { self.lock().write_all(buf) } - fn write_fmt(&mut self, args: fmt::Arguments) -> io::Result<()> { + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { self.lock().write_fmt(args) } } @@ -651,6 +697,9 @@ impl Write for StderrLock<'_> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.inner.borrow_mut().write(buf) } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.inner.borrow_mut().write_vectored(bufs) + } fn flush(&mut self) -> io::Result<()> { self.inner.borrow_mut().flush() } @@ -658,7 +707,7 @@ impl Write for StderrLock<'_> { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for StderrLock<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("StderrLock { .. }") } } @@ -720,7 +769,7 @@ pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + /// /// However, if the actual I/O causes an error, this function does panic. fn print_to<T>( - args: fmt::Arguments, + args: fmt::Arguments<'_>, local_s: &'static LocalKey<RefCell<Option<Box<dyn Write+Send>>>>, global_s: fn() -> T, label: &str, @@ -749,7 +798,7 @@ where issue = "0")] #[doc(hidden)] #[cfg(not(test))] -pub fn _print(args: fmt::Arguments) { +pub fn _print(args: fmt::Arguments<'_>) { print_to(args, &LOCAL_STDOUT, stdout, "stdout"); } @@ -758,7 +807,7 @@ pub fn _print(args: fmt::Arguments) { issue = "0")] #[doc(hidden)] #[cfg(not(test))] -pub fn _eprint(args: fmt::Arguments) { +pub fn _eprint(args: fmt::Arguments<'_>) { print_to(args, &LOCAL_STDERR, stderr, "stderr"); } @@ -777,7 +826,7 @@ mod tests { } #[test] fn stdoutlock_unwind_safe() { - assert_unwind_safe::<StdoutLock>(); + assert_unwind_safe::<StdoutLock<'_>>(); assert_unwind_safe::<StdoutLock<'static>>(); } #[test] @@ -786,7 +835,7 @@ mod tests { } #[test] fn stderrlock_unwind_safe() { - assert_unwind_safe::<StderrLock>(); + assert_unwind_safe::<StderrLock<'_>>(); assert_unwind_safe::<StderrLock<'static>>(); } diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index 6aaf8f1889a..33cc87eb795 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -1,8 +1,8 @@ #![allow(missing_copy_implementations)] use crate::fmt; -use crate::io::{self, Read, Initializer, Write, ErrorKind, BufRead, IoVec, IoVecMut}; -use crate::mem; +use crate::io::{self, Read, Initializer, Write, ErrorKind, BufRead, IoSlice, IoSliceMut}; +use crate::mem::MaybeUninit; /// Copies the entire contents of a reader into a writer. /// @@ -43,21 +43,23 @@ use crate::mem; pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64> where R: Read, W: Write { - let mut buf = unsafe { - let mut buf: [u8; super::DEFAULT_BUF_SIZE] = mem::uninitialized(); - reader.initializer().initialize(&mut buf); - buf - }; + let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit(); + // FIXME(#53491): This is calling `get_mut` and `get_ref` on an uninitialized + // `MaybeUninit`. Revisit this once we decided whether that is valid or not. + // This is still technically undefined behavior due to creating a reference + // to uninitialized data, but within libstd we can rely on more guarantees + // than if this code were in an external lib. + unsafe { reader.initializer().initialize(buf.get_mut()); } let mut written = 0; loop { - let len = match reader.read(&mut buf) { + let len = match reader.read(unsafe { buf.get_mut() }) { Ok(0) => return Ok(written), Ok(len) => len, Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, Err(e) => return Err(e), }; - writer.write_all(&buf[..len])?; + writer.write_all(unsafe { &buf.get_ref()[..len] })?; written += len as u64; } } @@ -111,7 +113,7 @@ impl BufRead for Empty { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Empty { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Empty { .. }") } } @@ -153,7 +155,7 @@ impl Read for Repeat { } #[inline] - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let mut nwritten = 0; for buf in bufs { nwritten += self.read(buf)?; @@ -169,7 +171,7 @@ impl Read for Repeat { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Repeat { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Repeat { .. }") } } @@ -206,7 +208,7 @@ impl Write for Sink { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) } #[inline] - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let total_len = bufs.iter().map(|b| b.len()).sum(); Ok(total_len) } @@ -217,7 +219,7 @@ impl Write for Sink { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Sink { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Sink { .. }") } } diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index bef6bc92661..f5018485ef7 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -1,6 +1,6 @@ #[doc(keyword = "as")] // -/// The keyword for casting a value to a type. +/// Cast between types, or rename an import. /// /// `as` is most commonly used to turn primitive types into other primitive types, but it has other /// uses that include turning pointers into addresses, addresses into pointers, and pointers into @@ -25,14 +25,22 @@ /// /// For more information on what `as` is capable of, see the [Reference] /// -/// [Reference]: -/// https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions +/// [Reference]: ../reference/expressions/operator-expr.html#type-cast-expressions /// [`crate`]: keyword.crate.html mod as_keyword { } +#[doc(keyword = "break")] +// +/// Exit early from a loop. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod break_keyword { } + #[doc(keyword = "const")] // -/// The keyword for defining constants. +/// Compile-time constants and deterministic functions. /// /// Sometimes a certain value is used many times throughout a program, and it can become /// inconvenient to copy it over and over. What's more, it's not always possible or desirable to @@ -80,13 +88,22 @@ mod as_keyword { } /// /// [pointer]: primitive.pointer.html /// [Rust Book]: -/// https://doc.rust-lang.org/stable/book/2018-edition/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants -/// [Reference]: https://doc.rust-lang.org/reference/items/constant-items.html +/// ../book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants +/// [Reference]: ../reference/items/constant-items.html mod const_keyword { } +#[doc(keyword = "continue")] +// +/// Skip to the next iteration of a loop. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod continue_keyword { } + #[doc(keyword = "crate")] // -/// The `crate` keyword. +/// A Rust binary or library. /// /// The primary use of the `crate` keyword is as a part of `extern crate` declarations, which are /// used to specify a dependency on a crate external to the one it's declared in. Crates are the @@ -102,7 +119,7 @@ mod const_keyword { } /// The `as` keyword can be used to change what the crate is referred to as in your project. If a /// crate name includes a dash, it is implicitly imported with the dashes replaced by underscores. /// -/// `crate` is also used as in conjunction with `pub` to signify that the item it's attached to +/// `crate` can also be used as in conjunction with `pub` to signify that the item it's attached to /// is public only to other members of the same crate it's in. /// /// ```rust @@ -114,17 +131,31 @@ mod const_keyword { } /// } /// ``` /// -/// [Reference]: https://doc.rust-lang.org/reference/items/extern-crates.html +/// `crate` is also used to represent the absolute path of a module, where `crate` refers to the +/// root of the current crate. For instance, `crate::foo::bar` refers to the name `bar` inside the +/// module `foo`, from anywhere else in the same crate. +/// +/// [Reference]: ../reference/items/extern-crates.html mod crate_keyword { } +#[doc(keyword = "else")] +// +/// What to do when an [`if`] condition does not hold. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [`if`]: keyword.if.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod else_keyword { } + #[doc(keyword = "enum")] // -/// For defining enumerations. +/// A type that can be any one of several variants. /// /// Enums in Rust are similar to those of other compiled languages like C, but have important /// differences that make them considerably more powerful. What Rust calls enums are more commonly -/// known as [Algebraic Data Types] if you're coming from a functional programming background. The -/// important detail is that each enum variant can have data to go along with it. +/// known as [Algebraic Data Types][ADT] if you're coming from a functional programming background. +/// The important detail is that each enum variant can have data to go along with it. /// /// ```rust /// # struct Coord; @@ -167,15 +198,15 @@ mod crate_keyword { } /// /// For more information, take a look at the [Rust Book] or the [Reference] /// -/// [Algebraic Data Types]: https://en.wikipedia.org/wiki/Algebraic_data_type +/// [ADT]: https://en.wikipedia.org/wiki/Algebraic_data_type /// [`Option`]: option/enum.Option.html -/// [Rust Book]: https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html -/// [Reference]: https://doc.rust-lang.org/reference/items/enumerations.html +/// [Rust Book]: ../book/ch06-01-defining-an-enum.html +/// [Reference]: ../reference/items/enumerations.html mod enum_keyword { } #[doc(keyword = "extern")] // -/// For external connections in Rust code. +/// Link to or import external code. /// /// The `extern` keyword is used in two places in Rust. One is in conjunction with the [`crate`] /// keyword to make your Rust code aware of other Rust crates in your project, i.e., `extern crate @@ -211,13 +242,23 @@ mod enum_keyword { } /// For more information on FFI, check the [Rust book] or the [Reference]. /// /// [Rust book]: -/// https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code -/// [Reference]: https://doc.rust-lang.org/reference/items/external-blocks.html +/// ../book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code +/// [Reference]: ../reference/items/external-blocks.html mod extern_keyword { } +#[doc(keyword = "false")] +// +/// A value of type [`bool`] representing logical **false**. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [`bool`]: primitive.bool.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod false_keyword { } + #[doc(keyword = "fn")] // -/// The keyword for defining functions. +/// A function or function pointer. /// /// Functions are the primary way code is executed within Rust. Function blocks, usually just /// called functions, can be defined in a variety of different places and be assigned many @@ -278,19 +319,25 @@ mod extern_keyword { } /// /// [`impl`]: keyword.impl.html /// [`extern`]: keyword.extern.html -/// [Rust book]: https://doc.rust-lang.org/book/ch03-03-how-functions-work.html -/// [Reference]: https://doc.rust-lang.org/reference/items/functions.html +/// [Rust book]: ../book/ch03-03-how-functions-work.html +/// [Reference]: ../reference/items/functions.html mod fn_keyword { } #[doc(keyword = "for")] // -/// The `for` keyword. +/// Iteration with [`in`], trait implementation with [`impl`], or [higher-ranked trait bounds] +/// (`for<'a>`). +/// +/// The `for` keyword is used in many syntactic locations: +/// +/// * `for` is used in for-in-loops (see below). +/// * `for` is used when implementing traits as in `impl Trait for Type` (see [`impl`] for more info +/// on that). +/// * `for` is also used for [higher-ranked trait bounds] as in `for<'a> &'a T: PartialEq<i32>`. /// -/// `for` is primarily used in for-in-loops, but it has a few other pieces of syntactic uses such as -/// `impl Trait for Type` (see [`impl`] for more info on that). for-in-loops, or to be more -/// precise, iterator loops, are a simple syntactic sugar over an exceedingly common practice -/// within Rust, which is to loop over an iterator until that iterator returns `None` (or `break` -/// is called). +/// for-in-loops, or to be more precise, iterator loops, are a simple syntactic sugar over a common +/// practice within Rust, which is to loop over an iterator until that iterator returns `None` (or +/// `break` is called). /// /// ```rust /// for i in 0..5 { @@ -346,16 +393,18 @@ mod fn_keyword { } /// /// For more information on for-loops, see the [Rust book] or the [Reference]. /// +/// [`in`]: keyword.in.html /// [`impl`]: keyword.impl.html +/// [higher-ranked trait bounds]: ../reference/trait-bounds.html#higher-ranked-trait-bounds /// [`IntoIterator`]: iter/trait.IntoIterator.html /// [Rust book]: -/// https://doc.rust-lang.org/book/2018-edition/ch03-05-control-flow.html#looping-through-a-collection-with-for -/// [Reference]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#iterator-loops +/// ../book/ch03-05-control-flow.html#looping-through-a-collection-with-for +/// [Reference]: ../reference/expressions/loop-expr.html#iterator-loops mod for_keyword { } #[doc(keyword = "if")] // -/// If statements and expressions. +/// Evaluate a block if a condition holds. /// /// `if` is a familiar construct to most programmers, and is the main way you'll often do logic in /// your code. However, unlike in most languages, `if` blocks can also act as expressions. @@ -423,14 +472,13 @@ mod for_keyword { } /// /// For more information on `if` expressions, see the [Rust book] or the [Reference]. /// -/// [Rust book]: -/// https://doc.rust-lang.org/stable/book/2018-edition/ch03-05-control-flow.html#if-expressions -/// [Reference]: https://doc.rust-lang.org/reference/expressions/if-expr.html +/// [Rust book]: ../book/ch03-05-control-flow.html#if-expressions +/// [Reference]: ../reference/expressions/if-expr.html mod if_keyword { } #[doc(keyword = "impl")] // -/// The implementation-defining keyword. +/// Implement some functionality for a type. /// /// The `impl` keyword is primarily used to define implementations on types. Inherent /// implementations are standalone, while trait implementations are used to implement traits for @@ -486,15 +534,24 @@ mod if_keyword { } /// /// For more information on `impl Trait` syntax, see the [Rust book][book2]. /// -/// [book1]: https://doc.rust-lang.org/stable/book/2018-edition/ch05-03-method-syntax.html -/// [Reference]: https://doc.rust-lang.org/reference/items/implementations.html -/// [book2]: -/// https://doc.rust-lang.org/stable/book/2018-edition/ch10-02-traits.html#returning-traits +/// [book1]: ../book/ch05-03-method-syntax.html +/// [Reference]: ../reference/items/implementations.html +/// [book2]: ../book/ch10-02-traits.html#returning-types-that-implement-traits mod impl_keyword { } +#[doc(keyword = "in")] +// +/// Iterate over a series of values with [`for`]. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [`for`]: keyword.for.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod in_keyword { } + #[doc(keyword = "let")] // -/// The variable binding keyword. +/// Bind a value to a variable. /// /// The primary use for the `let` keyword is in `let` statements, which are used to introduce a new /// set of variables into the current scope, as given by a pattern. @@ -547,18 +604,73 @@ mod impl_keyword { } /// enumerations. `while let` also exists, which runs a loop with a pattern matched value until /// that pattern can't be matched. /// -/// For more information on the `let` keyword, see the [Rust book] or the [Reference] +/// For more information on the `let` keyword, see the [Rust book][book2] or the [Reference] /// -/// [book1]: https://doc.rust-lang.org/stable/book/2018-edition/ch06-02-match.html +/// [book1]: ../book/ch06-02-match.html /// [`if`]: keyword.if.html -/// [book2]: -/// https://doc.rust-lang.org/stable/book/2018-edition/ch18-01-all-the-places-for-patterns.html#let-statements -/// [Reference]: https://doc.rust-lang.org/reference/statements.html#let-statements +/// [book2]: ../book/ch18-01-all-the-places-for-patterns.html#let-statements +/// [Reference]: ../reference/statements.html#let-statements mod let_keyword { } +#[doc(keyword = "while")] +// +/// Loop while a condition is upheld. +/// +/// A `while` expression is used for predicate loops. The `while` expression runs the conditional +/// expression before running the loop body, then runs the loop body if the conditional +/// expression evaluates to `true`, or exits the loop otherwise. +/// +/// ```rust +/// let mut counter = 0; +/// +/// while counter < 10 { +/// println!("{}", counter); +/// counter += 1; +/// } +/// ``` +/// +/// Like the [`for`] expression, we can use `break` and `continue`. A `while` expression +/// cannot break with a value and always evaluates to `()` unlike [`loop`]. +/// +/// ```rust +/// let mut i = 1; +/// +/// while i < 100 { +/// i *= 2; +/// if i == 64 { +/// break; // Exit when `i` is 64. +/// } +/// } +/// ``` +/// +/// As `if` expressions have their pattern matching variant in `if let`, so too do `while` +/// expressions with `while let`. The `while let` expression matches the pattern against the +/// expression, then runs the loop body if pattern matching succeeds, or exits the loop otherwise. +/// We can use `break` and `continue` in `while let` expressions just like in `while`. +/// +/// ```rust +/// let mut counter = Some(0); +/// +/// while let Some(i) = counter { +/// if i == 10 { +/// counter = None; +/// } else { +/// println!("{}", i); +/// counter = Some (i + 1); +/// } +/// } +/// ``` +/// +/// For more information on `while` and loops in general, see the [reference]. +/// +/// [`for`]: keyword.for.html +/// [`loop`]: keyword.loop.html +/// [reference]: ../reference/expressions/loop-expr.html#predicate-loops +mod while_keyword { } + #[doc(keyword = "loop")] // -/// The loop-defining keyword. +/// Loop indefinitely. /// /// `loop` is used to define the simplest kind of loop supported in Rust. It runs the code inside /// it until the code uses `break` or the program exits. @@ -598,12 +710,107 @@ mod let_keyword { } /// /// For more information on `loop` and loops in general, see the [Reference]. /// -/// [Reference]: https://doc.rust-lang.org/reference/expressions/loop-expr.html +/// [Reference]: ../reference/expressions/loop-expr.html mod loop_keyword { } +#[doc(keyword = "match")] +// +/// Control flow based on pattern matching. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod match_keyword { } + +#[doc(keyword = "mod")] +// +/// Organize code into [modules]. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [modules]: ../reference/items/modules.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod mod_keyword { } + +#[doc(keyword = "move")] +// +/// Capture a [closure]'s environment by value. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [closure]: ../book/second-edition/ch13-01-closures.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod move_keyword { } + +#[doc(keyword = "mut")] +// +/// A mutable binding, reference, or pointer. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod mut_keyword { } + +#[doc(keyword = "pub")] +// +/// Make an item visible to others. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod pub_keyword { } + +#[doc(keyword = "ref")] +// +/// Bind by reference during pattern matching. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod ref_keyword { } + +#[doc(keyword = "return")] +// +/// Return a value from a function. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod return_keyword { } + +#[doc(keyword = "self")] +// +/// The receiver of a method, or the current module. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod self_keyword { } + +#[doc(keyword = "Self")] +// +/// The implementing type within a [`trait`] or [`impl`] block, or the current type within a type +/// definition. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [`impl`]: keyword.impl.html +/// [`trait`]: keyword.trait.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod self_upper_keyword { } + +#[doc(keyword = "static")] +// +/// A place that is valid for the duration of a program. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod static_keyword { } + #[doc(keyword = "struct")] // -/// The keyword used to define structs. +/// A type that is composed of other types. /// /// Structs in Rust come in three flavors: Structs with named fields, tuple structs, and unit /// structs. @@ -705,6 +912,116 @@ mod loop_keyword { } /// [Reference][reference]. /// /// [`PhantomData`]: marker/struct.PhantomData.html -/// [book]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html -/// [reference]: https://doc.rust-lang.org/reference/items/structs.html +/// [book]: ../book/ch05-01-defining-structs.html +/// [reference]: ../reference/items/structs.html mod struct_keyword { } + +#[doc(keyword = "super")] +// +/// The parent of the current [module]. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [module]: ../reference/items/modules.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod super_keyword { } + +#[doc(keyword = "trait")] +// +/// A common interface for a class of types. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod trait_keyword { } + +#[doc(keyword = "true")] +// +/// A value of type [`bool`] representing logical **true**. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [`bool`]: primitive.bool.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod true_keyword { } + +#[doc(keyword = "type")] +// +/// Define an alias for an existing type. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod type_keyword { } + +#[doc(keyword = "unsafe")] +// +/// Code or interfaces whose [memory safety] cannot be verified by the type system. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [memory safety]: ../book/ch19-01-unsafe-rust.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod unsafe_keyword { } + +#[doc(keyword = "use")] +// +/// Import or rename items from other crates or modules. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod use_keyword { } + +#[doc(keyword = "where")] +// +/// Add constraints that must be upheld to use an item. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod where_keyword { } + +// 2018 Edition keywords + +#[unstable(feature = "async_await", issue = "50547")] +#[doc(keyword = "async")] +// +/// Return a [`Future`] instead of blocking the current thread. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [`Future`]: ./future/trait.Future.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod async_keyword { } + +#[unstable(feature = "async_await", issue = "50547")] +#[doc(keyword = "await")] +// +/// Suspend execution until the result of a [`Future`] is ready. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [`Future`]: ./future/trait.Future.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod await_keyword { } + +#[doc(keyword = "dyn")] +// +/// Name the type of a [trait object]. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [trait object]: ../book/ch17-02-trait-objects.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod dyn_keyword { } + +#[doc(keyword = "union")] +// +/// The [Rust equivalent of a C-style union][union]. +/// +/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// +/// [union]: ../reference/items/unions.html +/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +mod union_keyword { } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index b830c90cc43..cfee49a7b55 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -205,13 +205,12 @@ // Don't link to std. We are std. #![no_std] -#![deny(missing_docs)] -#![deny(intra_doc_link_resolution_failure)] -#![deny(missing_debug_implementations)] - -#![deny(rust_2018_idioms)] +#![warn(deprecated_in_future)] +#![warn(missing_docs)] +#![warn(missing_debug_implementations)] +#![deny(intra_doc_link_resolution_failure)] // rustdoc is run without -D warnings #![allow(explicit_outlives_requirements)] -#![allow(elided_lifetimes_in_paths)] +#![allow(unused_lifetimes)] // Tell the compiler to link to either panic_abort or panic_unwind #![needs_panic_runtime] @@ -219,18 +218,19 @@ // std may use features in a platform-specific way #![allow(unused_features)] -#![cfg_attr(test, feature(print_internals, set_stdio, test, update_panic_count))] +#![cfg_attr(test, feature(print_internals, set_stdio, update_panic_count))] #![cfg_attr(all(target_vendor = "fortanix", target_env = "sgx"), - feature(global_asm, range_contains, slice_index_methods, - decl_macro, coerce_unsized, sgx_platform, ptr_wrapping_offset_from))] + feature(slice_index_methods, decl_macro, coerce_unsized, + sgx_platform, ptr_wrapping_offset_from))] +#![cfg_attr(all(test, target_vendor = "fortanix", target_env = "sgx"), + feature(fixed_size_array, maybe_uninit_extra))] // std is implemented with unstable features, many of which are internal // compiler details that will never be stable // NB: the following list is sorted to minimize merge conflicts. -#![feature(align_offset)] +#![cfg_attr(not(bootstrap), feature(__rust_unstable_column))] #![feature(alloc_error_handler)] #![feature(alloc_layout_extra)] -#![feature(alloc)] #![feature(allocator_api)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -251,6 +251,7 @@ #![feature(const_cstr_unchecked)] #![feature(const_raw_ptr_deref)] #![feature(core_intrinsics)] +#![feature(custom_test_frameworks)] #![feature(doc_alias)] #![feature(doc_cfg)] #![feature(doc_keyword)] @@ -261,20 +262,23 @@ #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] #![feature(external_doc)] -#![feature(fixed_size_array)] #![feature(fn_traits)] -#![feature(fnbox)] -#![feature(futures_api)] +#![feature(format_args_nl)] #![feature(generator_trait)] +#![feature(global_asm)] #![feature(hash_raw_entry)] #![feature(hashmap_internals)] #![feature(int_error_internals)] +#![feature(int_error_matching)] #![feature(integer_atomics)] #![feature(lang_items)] #![feature(libc)] #![feature(link_args)] #![feature(linkage)] -#![feature(maybe_uninit)] +#![feature(log_syntax)] +#![feature(maybe_uninit_ref)] +#![feature(maybe_uninit_slice)] +#![feature(mem_take)] #![feature(needs_panic_runtime)] #![feature(never_type)] #![feature(nll)] @@ -300,8 +304,11 @@ #![feature(stdsimd)] #![feature(stmt_expr_attributes)] #![feature(str_internals)] +#![feature(test)] #![feature(thread_local)] +#![feature(todo_macro)] #![feature(toowned_clone_into)] +#![feature(trace_macros)] #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(untagged_unions)] @@ -323,7 +330,7 @@ use prelude::v1::*; #[stable(feature = "rust1", since = "1.0.0")] pub use core::{assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::{unreachable, unimplemented, write, writeln, r#try}; +pub use core::{unreachable, unimplemented, write, writeln, r#try, todo}; #[allow(unused_imports)] // macros from `alloc` are not used on all platforms #[macro_use] @@ -337,6 +344,12 @@ extern crate libc; #[allow(unused_extern_crates)] extern crate unwind; +// Only needed for now for the `std_detect` module until that crate changes to +// use `cfg_if::cfg_if!` +#[macro_use] +#[cfg(not(test))] +extern crate cfg_if; + // During testing, this crate is not actually the "real" std library, but rather // it links to the real std library, which was compiled from this same source // code. So any lang items std defines are conditionally excluded (or else they @@ -436,6 +449,8 @@ pub use core::char; pub use core::u128; #[stable(feature = "core_hint", since = "1.27.0")] pub use core::hint; +#[stable(feature = "core_array", since = "1.36.0")] +pub use core::array; pub mod f32; pub mod f64; @@ -458,18 +473,15 @@ pub mod process; pub mod sync; pub mod time; -#[unstable(feature = "futures_api", - reason = "futures in libcore are unstable", - issue = "50547")] +#[stable(feature = "futures_api", since = "1.36.0")] pub mod task { //! Types and Traits for working with asynchronous tasks. #[doc(inline)] + #[stable(feature = "futures_api", since = "1.36.0")] pub use core::task::*; } -#[unstable(feature = "futures_api", - reason = "futures in libcore are unstable", - issue = "50547")] +#[stable(feature = "futures_api", since = "1.36.0")] pub mod future; // Platform-abstraction modules @@ -488,12 +500,12 @@ mod memchr; pub mod rt; // Pull in the `std_detect` crate directly into libstd. The contents of -// `std_detect` are in a different repository: rust-lang-nursery/stdsimd. +// `std_detect` are in a different repository: rust-lang/stdarch. // // `std_detect` depends on libstd, but the contents of this module are // set up in such a way that directly pulling it here works such that the // crate uses the this crate as its libstd. -#[path = "../stdsimd/crates/std_detect/src/mod.rs"] +#[path = "../stdarch/crates/std_detect/src/mod.rs"] #[allow(missing_debug_implementations, missing_docs, dead_code)] #[unstable(feature = "stdsimd", issue = "48556")] #[cfg(not(test))] @@ -504,6 +516,36 @@ mod std_detect; #[cfg(not(test))] pub use std_detect::detect; +// Document built-in macros in the crate root for consistency with libcore and existing tradition. +// FIXME: Attribute and derive macros are not reexported because rustdoc renders them +// as reexports rather than as macros, and that's not what we want. +#[cfg(rustdoc)] +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +pub use crate::prelude::v1::{ + __rust_unstable_column, + asm, + assert, + cfg, + column, + compile_error, + concat, + concat_idents, + env, + file, + format_args, + format_args_nl, + global_asm, + include, + include_bytes, + include_str, + line, + log_syntax, + module_path, + option_env, + stringify, + trace_macros, +}; + // Include a number of private modules that exist solely to provide // the rustdoc documentation for primitive types. Using `include!` // because rustdoc only looks for these modules at the crate level. diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 9d0eb2e6b1c..839b4c5656a 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -4,7 +4,7 @@ //! library. Each macro is available for use when linking against the standard //! library. -/// The entry point for panic of Rust threads. +/// Panics the current thread. /// /// This allows a program to terminate immediately and provide feedback /// to the caller of the program. `panic!` should be used when a program reaches @@ -56,13 +56,13 @@ #[allow_internal_unstable(__rust_unstable_column, libstd_sys_internals)] macro_rules! panic { () => ({ - panic!("explicit panic") + $crate::panic!("explicit panic") }); ($msg:expr) => ({ $crate::rt::begin_panic($msg, &(file!(), line!(), __rust_unstable_column!())) }); ($msg:expr,) => ({ - panic!($msg) + $crate::panic!($msg) }); ($fmt:expr, $($arg:tt)+) => ({ $crate::rt::begin_panic_fmt(&format_args!($fmt, $($arg)+), @@ -70,7 +70,7 @@ macro_rules! panic { }); } -/// Macro for printing to the standard output. +/// Prints to the standard output. /// /// Equivalent to the [`println!`] macro except that a newline is not printed at /// the end of the message. @@ -116,7 +116,7 @@ macro_rules! print { ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))); } -/// Macro for printing to the standard output, with a newline. +/// Prints to the standard output, with a newline. /// /// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone /// (no additional CARRIAGE RETURN (`\r`/`U+000D`). @@ -145,13 +145,13 @@ macro_rules! print { #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(print_internals, format_args_nl)] macro_rules! println { - () => (print!("\n")); + () => ($crate::print!("\n")); ($($arg:tt)*) => ({ $crate::io::_print(format_args_nl!($($arg)*)); }) } -/// Macro for printing to the standard error. +/// Prints to the standard error. /// /// Equivalent to the [`print!`] macro, except that output goes to /// [`io::stderr`] instead of `io::stdout`. See [`print!`] for @@ -179,7 +179,7 @@ macro_rules! eprint { ($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*))); } -/// Macro for printing to the standard error, with a newline. +/// Prints to the standard error, with a newline. /// /// Equivalent to the [`println!`] macro, except that output goes to /// [`io::stderr`] instead of `io::stdout`. See [`println!`] for @@ -204,14 +204,16 @@ macro_rules! eprint { #[stable(feature = "eprint", since = "1.19.0")] #[allow_internal_unstable(print_internals, format_args_nl)] macro_rules! eprintln { - () => (eprint!("\n")); + () => ($crate::eprint!("\n")); ($($arg:tt)*) => ({ $crate::io::_eprint(format_args_nl!($($arg)*)); }) } -/// A macro for quick and dirty debugging with which you can inspect -/// the value of a given expression. An example: +/// Prints and returns the value of a given expression for quick and dirty +/// debugging. +/// +/// An example: /// /// ```rust /// let a = 2; @@ -231,10 +233,14 @@ macro_rules! eprintln { /// to give up ownership, you can instead borrow with `dbg!(&expr)` /// for some expression `expr`. /// +/// The `dbg!` macro works exactly the same in release builds. +/// This is useful when debugging issues that only occur in release +/// builds or when debugging in release mode is significantly faster. +/// /// Note that the macro is intended as a debugging tool and therefore you /// should avoid having uses of it in version control for longer periods. /// Use cases involving debug output that should be added to version control -/// may be better served by macros such as `debug!` from the `log` crate. +/// are better served by macros such as [`debug!`] from the [`log`] crate. /// /// # Stability /// @@ -305,99 +311,50 @@ macro_rules! eprintln { /// let _ = dbg!(a); // <-- `a` is moved again; error! /// ``` /// +/// You can also use `dbg!()` without a value to just print the +/// file and line whenever it's reached. +/// +/// Finally, if you want to `dbg!(..)` multiple values, it will treat them as +/// a tuple (and return it, too): +/// +/// ``` +/// assert_eq!(dbg!(1usize, 2u32), (1, 2)); +/// ``` +/// +/// However, a single argument with a trailing comma will still not be treated +/// as a tuple, following the convention of ignoring trailing commas in macro +/// invocations. You can use a 1-tuple directly if you need one: +/// +/// ``` +/// assert_eq!(1, dbg!(1u32,)); // trailing comma ignored +/// assert_eq!((1,), dbg!((1u32,))); // 1-tuple +/// ``` +/// /// [stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr) +/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html +/// [`log`]: https://crates.io/crates/log #[macro_export] #[stable(feature = "dbg_macro", since = "1.32.0")] macro_rules! dbg { + () => { + $crate::eprintln!("[{}:{}]", file!(), line!()); + }; ($val:expr) => { // Use of `match` here is intentional because it affects the lifetimes // of temporaries - https://stackoverflow.com/a/48732525/1063961 match $val { tmp => { - eprintln!("[{}:{}] {} = {:#?}", + $crate::eprintln!("[{}:{}] {} = {:#?}", file!(), line!(), stringify!($val), &tmp); tmp } } - } -} - -/// A macro to await on an async call. -#[macro_export] -#[unstable(feature = "await_macro", issue = "50547")] -#[allow_internal_unstable(gen_future, generators)] -#[allow_internal_unsafe] -macro_rules! r#await { - ($e:expr) => { { - let mut pinned = $e; - loop { - if let $crate::task::Poll::Ready(x) = - $crate::future::poll_with_tls_waker(unsafe { - $crate::pin::Pin::new_unchecked(&mut pinned) - }) - { - break x; - } - // FIXME(cramertj) prior to stabilizing await, we have to ensure that this - // can't be used to create a generator on stable via `|| await!()`. - yield - } - } } -} - -/// A macro to select an event from a number of receivers. -/// -/// This macro is used to wait for the first event to occur on a number of -/// receivers. It places no restrictions on the types of receivers given to -/// this macro, this can be viewed as a heterogeneous select. -/// -/// # Examples -/// -/// ``` -/// #![feature(mpsc_select)] -/// -/// use std::thread; -/// use std::sync::mpsc; -/// -/// // two placeholder functions for now -/// fn long_running_thread() {} -/// fn calculate_the_answer() -> u32 { 42 } -/// -/// let (tx1, rx1) = mpsc::channel(); -/// let (tx2, rx2) = mpsc::channel(); -/// -/// thread::spawn(move|| { long_running_thread(); tx1.send(()).unwrap(); }); -/// thread::spawn(move|| { tx2.send(calculate_the_answer()).unwrap(); }); -/// -/// select! { -/// _ = rx1.recv() => println!("the long running thread finished first"), -/// answer = rx2.recv() => { -/// println!("the answer was: {}", answer.unwrap()); -/// } -/// } -/// # drop(rx1.recv()); -/// # drop(rx2.recv()); -/// ``` -/// -/// For more information about select, see the `std::sync::mpsc::Select` structure. -#[macro_export] -#[unstable(feature = "mpsc_select", issue = "27800")] -#[rustc_deprecated(since = "1.32.0", - reason = "channel selection will be removed in a future release")] -macro_rules! select { - ( - $($name:pat = $rx:ident.$meth:ident() => $code:expr),+ - ) => ({ - use $crate::sync::mpsc::Select; - let sel = Select::new(); - $( let mut $rx = sel.handle(&$rx); )+ - unsafe { - $( $rx.add(); )+ - } - let ret = sel.wait(); - $( if ret == $rx.id() { let $name = $rx.$meth(); $code } else )+ - { unreachable!() } - }) + }; + // Trailing comma with single argument is ignored + ($val:expr,) => { $crate::dbg!($val) }; + ($($val:expr),+ $(,)?) => { + ($($crate::dbg!($val)),+,) + }; } #[cfg(test)] @@ -408,568 +365,3 @@ macro_rules! assert_approx_eq { "{} is not approximately equal to {}", *a, *b); }) } - -/// Built-in macros to the compiler itself. -/// -/// These macros do not have any corresponding definition with a `macro_rules!` -/// macro, but are documented here. Their implementations can be found hardcoded -/// into libsyntax itself. -#[cfg(rustdoc)] -mod builtin { - - /// Unconditionally causes compilation to fail with the given error message when encountered. - /// - /// This macro should be used when a crate uses a conditional compilation strategy to provide - /// better error messages for erroneous conditions. It's the compiler-level form of [`panic!`], - /// which emits an error at *runtime*, rather than during compilation. - /// - /// # Examples - /// - /// Two such examples are macros and `#[cfg]` environments. - /// - /// Emit better compiler error if a macro is passed invalid values. Without the final branch, - /// the compiler would still emit an error, but the error's message would not mention the two - /// valid values. - /// - /// ```compile_fail - /// macro_rules! give_me_foo_or_bar { - /// (foo) => {}; - /// (bar) => {}; - /// ($x:ident) => { - /// compile_error!("This macro only accepts `foo` or `bar`"); - /// } - /// } - /// - /// give_me_foo_or_bar!(neither); - /// // ^ will fail at compile time with message "This macro only accepts `foo` or `bar`" - /// ``` - /// - /// Emit compiler error if one of a number of features isn't available. - /// - /// ```compile_fail - /// #[cfg(not(any(feature = "foo", feature = "bar")))] - /// compile_error!("Either feature \"foo\" or \"bar\" must be enabled for this crate.") - /// ``` - /// - /// [`panic!`]: ../std/macro.panic.html - #[stable(feature = "compile_error_macro", since = "1.20.0")] - #[rustc_doc_only_macro] - macro_rules! compile_error { - ($msg:expr) => ({ /* compiler built-in */ }); - ($msg:expr,) => ({ /* compiler built-in */ }); - } - - /// The core macro for formatted string creation & output. - /// - /// This macro functions by taking a formatting string literal containing - /// `{}` for each additional argument passed. `format_args!` prepares the - /// additional parameters to ensure the output can be interpreted as a string - /// and canonicalizes the arguments into a single type. Any value that implements - /// the [`Display`] trait can be passed to `format_args!`, as can any - /// [`Debug`] implementation be passed to a `{:?}` within the formatting string. - /// - /// This macro produces a value of type [`fmt::Arguments`]. This value can be - /// passed to the macros within [`std::fmt`] for performing useful redirection. - /// All other formatting macros ([`format!`], [`write!`], [`println!`], etc) are - /// proxied through this one. `format_args!`, unlike its derived macros, avoids - /// heap allocations. - /// - /// You can use the [`fmt::Arguments`] value that `format_args!` returns - /// in `Debug` and `Display` contexts as seen below. The example also shows - /// that `Debug` and `Display` format to the same thing: the interpolated - /// format string in `format_args!`. - /// - /// ```rust - /// let debug = format!("{:?}", format_args!("{} foo {:?}", 1, 2)); - /// let display = format!("{}", format_args!("{} foo {:?}", 1, 2)); - /// assert_eq!("1 foo 2", display); - /// assert_eq!(display, debug); - /// ``` - /// - /// For more information, see the documentation in [`std::fmt`]. - /// - /// [`Display`]: ../std/fmt/trait.Display.html - /// [`Debug`]: ../std/fmt/trait.Debug.html - /// [`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 - /// - /// ``` - /// use std::fmt; - /// - /// let s = fmt::format(format_args!("hello {}", "world")); - /// assert_eq!(s, format!("hello {}", "world")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! format_args { - ($fmt:expr) => ({ /* compiler built-in */ }); - ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }); - } - - /// Inspect an environment variable at compile time. - /// - /// This macro will expand to the value of the named environment variable at - /// compile time, yielding an expression of type `&'static str`. - /// - /// If the environment variable is not defined, then a compilation error - /// will be emitted. To not emit a compile error, use the [`option_env!`] - /// macro instead. - /// - /// [`option_env!`]: ../std/macro.option_env.html - /// - /// # Examples - /// - /// ``` - /// let path: &'static str = env!("PATH"); - /// println!("the $PATH variable at the time of compiling was: {}", path); - /// ``` - /// - /// You can customize the error message by passing a string as the second - /// parameter: - /// - /// ```compile_fail - /// let doc: &'static str = env!("documentation", "what's that?!"); - /// ``` - /// - /// If the `documentation` environment variable is not defined, you'll get - /// the following error: - /// - /// ```text - /// error: what's that?! - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! env { - ($name:expr) => ({ /* compiler built-in */ }); - ($name:expr,) => ({ /* compiler built-in */ }); - } - - /// Optionally inspect an environment variable at compile time. - /// - /// If the named environment variable is present at compile time, this will - /// expand into an expression of type `Option<&'static str>` whose value is - /// `Some` of the value of the environment variable. If the environment - /// variable is not present, then this will expand to `None`. See - /// [`Option<T>`][option] for more information on this type. - /// - /// A compile time error is never emitted when using this macro regardless - /// of whether the environment variable is present or not. - /// - /// [option]: ../std/option/enum.Option.html - /// - /// # Examples - /// - /// ``` - /// let key: Option<&'static str> = option_env!("SECRET_KEY"); - /// println!("the secret key might be: {:?}", key); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! option_env { - ($name:expr) => ({ /* compiler built-in */ }); - ($name:expr,) => ({ /* compiler built-in */ }); - } - - /// Concatenate identifiers into one identifier. - /// - /// This macro takes any number of comma-separated identifiers, and - /// concatenates them all into one, yielding an expression which is a new - /// identifier. Note that hygiene makes it such that this macro cannot - /// capture local variables. Also, as a general rule, macros are only - /// allowed in item, statement or expression position. That means while - /// you may use this macro for referring to existing variables, functions or - /// modules etc, you cannot define a new one with it. - /// - /// # Examples - /// - /// ``` - /// #![feature(concat_idents)] - /// - /// # fn main() { - /// fn foobar() -> u32 { 23 } - /// - /// let f = concat_idents!(foo, bar); - /// println!("{}", f()); - /// - /// // fn concat_idents!(new, fun, name) { } // not usable in this way! - /// # } - /// ``` - #[unstable(feature = "concat_idents_macro", issue = "29599")] - #[rustc_doc_only_macro] - macro_rules! concat_idents { - ($($e:ident),+) => ({ /* compiler built-in */ }); - ($($e:ident,)+) => ({ /* compiler built-in */ }); - } - - /// Concatenates literals into a static string slice. - /// - /// This macro takes any number of comma-separated literals, yielding an - /// expression of type `&'static str` which represents all of the literals - /// concatenated left-to-right. - /// - /// Integer and floating point literals are stringified in order to be - /// concatenated. - /// - /// # Examples - /// - /// ``` - /// let s = concat!("test", 10, 'b', true); - /// assert_eq!(s, "test10btrue"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! concat { - ($($e:expr),*) => ({ /* compiler built-in */ }); - ($($e:expr,)*) => ({ /* compiler built-in */ }); - } - - /// A macro which expands to the line number on which it was invoked. - /// - /// With [`column!`] and [`file!`], these macros provide debugging information for - /// developers about the location within the source. - /// - /// The expanded expression has type `u32` and is 1-based, so the first line - /// in each file evaluates to 1, the second to 2, etc. This is consistent - /// with error messages by common compilers or popular editors. - /// The returned line is *not necessarily* the line of the `line!` invocation itself, - /// but rather the first macro invocation leading up to the invocation - /// of the `line!` macro. - /// - /// [`column!`]: macro.column.html - /// [`file!`]: macro.file.html - /// - /// # Examples - /// - /// ``` - /// let current_line = line!(); - /// println!("defined on line: {}", current_line); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! line { () => ({ /* compiler built-in */ }) } - - /// A macro which expands to the column number on which it was invoked. - /// - /// With [`line!`] and [`file!`], these macros provide debugging information for - /// developers about the location within the source. - /// - /// The expanded expression has type `u32` and is 1-based, so the first column - /// in each line evaluates to 1, the second to 2, etc. This is consistent - /// with error messages by common compilers or popular editors. - /// The returned column is *not necessarily* the line of the `column!` invocation itself, - /// but rather the first macro invocation leading up to the invocation - /// of the `column!` macro. - /// - /// [`line!`]: macro.line.html - /// [`file!`]: macro.file.html - /// - /// # Examples - /// - /// ``` - /// let current_col = column!(); - /// println!("defined on column: {}", current_col); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! column { () => ({ /* compiler built-in */ }) } - - /// A macro which expands to the file name from which it was invoked. - /// - /// With [`line!`] and [`column!`], these macros provide debugging information for - /// developers about the location within the source. - /// - /// - /// The expanded expression has type `&'static str`, and the returned file - /// is not the invocation of the `file!` macro itself, but rather the - /// first macro invocation leading up to the invocation of the `file!` - /// macro. - /// - /// [`line!`]: macro.line.html - /// [`column!`]: macro.column.html - /// - /// # Examples - /// - /// ``` - /// let this_file = file!(); - /// println!("defined in file: {}", this_file); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! file { () => ({ /* compiler built-in */ }) } - - /// A macro which stringifies its arguments. - /// - /// This macro will yield an expression of type `&'static str` which is the - /// stringification of all the tokens passed to the macro. No restrictions - /// are placed on the syntax of the macro invocation itself. - /// - /// Note that the expanded results of the input tokens may change in the - /// future. You should be careful if you rely on the output. - /// - /// # Examples - /// - /// ``` - /// let one_plus_one = stringify!(1 + 1); - /// assert_eq!(one_plus_one, "1 + 1"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! stringify { ($($t:tt)*) => ({ /* compiler built-in */ }) } - - /// 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 file. - /// - /// # Examples - /// - /// Assume there are two files in the same directory with the following - /// contents: - /// - /// File 'spanish.in': - /// - /// ```text - /// adiós - /// ``` - /// - /// File 'main.rs': - /// - /// ```ignore (cannot-doctest-external-file-dependency) - /// fn main() { - /// let my_str = include_str!("spanish.in"); - /// assert_eq!(my_str, "adiós\n"); - /// print!("{}", my_str); - /// } - /// ``` - /// - /// Compiling 'main.rs' and running the resulting binary will print "adiós". - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! include_str { - ($file:expr) => ({ /* compiler built-in */ }); - ($file:expr,) => ({ /* compiler built-in */ }); - } - - /// 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 file. - /// - /// # Examples - /// - /// Assume there are two files in the same directory with the following - /// contents: - /// - /// File 'spanish.in': - /// - /// ```text - /// adiós - /// ``` - /// - /// File 'main.rs': - /// - /// ```ignore (cannot-doctest-external-file-dependency) - /// fn main() { - /// let bytes = include_bytes!("spanish.in"); - /// assert_eq!(bytes, b"adi\xc3\xb3s\n"); - /// print!("{}", String::from_utf8_lossy(bytes)); - /// } - /// ``` - /// - /// Compiling 'main.rs' and running the resulting binary will print "adiós". - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! include_bytes { - ($file:expr) => ({ /* compiler built-in */ }); - ($file:expr,) => ({ /* compiler built-in */ }); - } - - /// Expands to a string that represents the current module path. - /// - /// The current module path can be thought of as the hierarchy of modules - /// leading back up to the crate root. The first component of the path - /// returned is the name of the crate currently being compiled. - /// - /// # Examples - /// - /// ``` - /// mod test { - /// pub fn foo() { - /// assert!(module_path!().ends_with("test")); - /// } - /// } - /// - /// test::foo(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! module_path { () => ({ /* compiler built-in */ }) } - - /// Boolean evaluation of configuration flags, at compile-time. - /// - /// In addition to the `#[cfg]` attribute, this macro is provided to allow - /// boolean expression evaluation of configuration flags. This frequently - /// leads to less duplicated code. - /// - /// The syntax given to this macro is the same syntax as the `cfg` - /// attribute. - /// - /// # Examples - /// - /// ``` - /// let my_directory = if cfg!(windows) { - /// "windows-specific-directory" - /// } else { - /// "unix-directory" - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! cfg { ($($cfg:tt)*) => ({ /* compiler built-in */ }) } - - /// 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). - /// - /// 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 unhygienically. 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 - /// - /// Assume there are two files in the same directory with the following - /// contents: - /// - /// File 'monkeys.in': - /// - /// ```ignore (only-for-syntax-highlight) - /// ['🙈', '🙊', '🙉'] - /// .iter() - /// .cycle() - /// .take(6) - /// .collect::<String>() - /// ``` - /// - /// File 'main.rs': - /// - /// ```ignore (cannot-doctest-external-file-dependency) - /// fn main() { - /// let my_string = include!("monkeys.in"); - /// assert_eq!("🙈🙊🙉🙈🙊🙉", my_string); - /// println!("{}", my_string); - /// } - /// ``` - /// - /// Compiling 'main.rs' and running the resulting binary will print - /// "🙈🙊🙉🙈🙊🙉". - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! include { - ($file:expr) => ({ /* compiler built-in */ }); - ($file:expr,) => ({ /* compiler built-in */ }); - } - - /// Ensure that a boolean expression is `true` at runtime. - /// - /// This will invoke the [`panic!`] macro if the provided expression cannot be - /// evaluated to `true` at runtime. - /// - /// # Uses - /// - /// Assertions are always checked in both debug and release builds, and cannot - /// be disabled. See [`debug_assert!`] for assertions that are not enabled in - /// release builds by default. - /// - /// Unsafe code relies on `assert!` to enforce run-time invariants that, if - /// violated could lead to unsafety. - /// - /// Other use-cases of `assert!` include testing and enforcing run-time - /// invariants in safe code (whose violation cannot result in unsafety). - /// - /// # Custom Messages - /// - /// This macro has a second form, where a custom panic message can - /// be provided with or without arguments for formatting. See [`std::fmt`] - /// for syntax for this form. - /// - /// [`panic!`]: macro.panic.html - /// [`debug_assert!`]: macro.debug_assert.html - /// [`std::fmt`]: ../std/fmt/index.html - /// - /// # Examples - /// - /// ``` - /// // the panic message for these assertions is the stringified value of the - /// // expression given. - /// assert!(true); - /// - /// fn some_computation() -> bool { true } // a very simple function - /// - /// assert!(some_computation()); - /// - /// // assert with a custom message - /// let x = true; - /// assert!(x, "x wasn't true!"); - /// - /// let a = 3; let b = 27; - /// assert!(a + b == 30, "a = {}, b = {}", a, b); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_doc_only_macro] - macro_rules! assert { - ($cond:expr) => ({ /* compiler built-in */ }); - ($cond:expr,) => ({ /* compiler built-in */ }); - ($cond:expr, $($arg:tt)+) => ({ /* compiler built-in */ }); - } -} - -/// A macro for defining `#[cfg]` if-else statements. -/// -/// This is similar to the `if/elif` C preprocessor macro by allowing definition -/// of a cascade of `#[cfg]` cases, emitting the implementation which matches -/// first. -/// -/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code -/// without having to rewrite each clause multiple times. -macro_rules! cfg_if { - ($( - if #[cfg($($meta:meta),*)] { $($it:item)* } - ) else * else { - $($it2:item)* - }) => { - __cfg_if_items! { - () ; - $( ( ($($meta),*) ($($it)*) ), )* - ( () ($($it2)*) ), - } - } -} - -macro_rules! __cfg_if_items { - (($($not:meta,)*) ; ) => {}; - (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { - __cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* } - __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } - } -} - -macro_rules! __cfg_if_apply { - ($m:meta, $($it:item)*) => { - $(#[$m] $it)* - } -} diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index f5a87cc3ea6..ca86a175058 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -546,6 +546,9 @@ impl FromInner<c::sockaddr_in6> for SocketAddrV6 { #[stable(feature = "ip_from_ip", since = "1.16.0")] impl From<SocketAddrV4> for SocketAddr { /// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`]. + /// + /// [`SocketAddrV4`]: ../../std/net/struct.SocketAddrV4.html + /// [`SocketAddr::V4`]: ../../std/net/enum.SocketAddr.html#variant.V4 fn from(sock4: SocketAddrV4) -> SocketAddr { SocketAddr::V4(sock4) } @@ -554,6 +557,9 @@ impl From<SocketAddrV4> for SocketAddr { #[stable(feature = "ip_from_ip", since = "1.16.0")] impl From<SocketAddrV6> for SocketAddr { /// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`]. + /// + /// [`SocketAddrV6`]: ../../std/net/struct.SocketAddrV6.html + /// [`SocketAddr::V6`]: ../../std/net/enum.SocketAddr.html#variant.V6 fn from(sock6: SocketAddrV6) -> SocketAddr { SocketAddr::V6(sock6) } @@ -567,6 +573,13 @@ impl<I: Into<IpAddr>> From<(I, u16)> for SocketAddr { /// and creates a [`SocketAddr::V6`] for a [`IpAddr::V6`]. /// /// `u16` is treated as port of the newly created [`SocketAddr`]. + /// + /// [`IpAddr`]: ../../std/net/enum.IpAddr.html + /// [`IpAddr::V4`]: ../../std/net/enum.IpAddr.html#variant.V4 + /// [`IpAddr::V6`]: ../../std/net/enum.IpAddr.html#variant.V6 + /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html + /// [`SocketAddr::V4`]: ../../std/net/enum.SocketAddr.html#variant.V4 + /// [`SocketAddr::V6`]: ../../std/net/enum.SocketAddr.html#variant.V6 fn from(pieces: (I, u16)) -> SocketAddr { SocketAddr::new(pieces.0.into(), pieces.1) } @@ -587,7 +600,7 @@ impl<'a> IntoInner<(*const c::sockaddr, c::socklen_t)> for &'a SocketAddr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for SocketAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { SocketAddr::V4(ref a) => a.fmt(f), SocketAddr::V6(ref a) => a.fmt(f), @@ -597,28 +610,28 @@ impl fmt::Display for SocketAddr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for SocketAddrV4 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.ip(), self.port()) } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for SocketAddrV4 { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for SocketAddrV6 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "[{}]:{}", self.ip(), self.port()) } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for SocketAddrV6 { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } @@ -941,7 +954,10 @@ mod tests { assert_eq!(Ok(vec![a]), tsa(("2a02:6b8:0:1::1", 53))); let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924); + #[cfg(not(target_env = "sgx"))] assert!(tsa(("localhost", 23924)).unwrap().contains(&a)); + #[cfg(target_env = "sgx")] + let _ = a; } #[test] @@ -953,7 +969,10 @@ mod tests { assert_eq!(Ok(vec![a]), tsa("[2a02:6b8:0:1::1]:53")); let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924); + #[cfg(not(target_env = "sgx"))] assert!(tsa("localhost:23924").unwrap().contains(&a)); + #[cfg(target_env = "sgx")] + let _ = a; } #[test] @@ -968,9 +987,9 @@ mod tests { // s has been moved into the tsa call } - // FIXME: figure out why this fails on openbsd and bitrig and fix it + // FIXME: figure out why this fails on openbsd and fix it #[test] - #[cfg(not(any(windows, target_os = "openbsd", target_os = "bitrig")))] + #[cfg(not(any(windows, target_os = "openbsd")))] fn to_socket_addr_str_bad() { assert!(tsa("1200::AB00:1234::2552:7777:1313:34300").is_err()); } diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index fa256ce5086..6b504056e5f 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -502,12 +502,19 @@ impl Ipv4Addr { /// /// The following return false: /// - /// - private address (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16) - /// - the loopback address (127.0.0.0/8) - /// - the link-local address (169.254.0.0/16) - /// - the broadcast address (255.255.255.255/32) - /// - test addresses used for documentation (192.0.2.0/24, 198.51.100.0/24 and 203.0.113.0/24) - /// - the unspecified address (0.0.0.0) + /// - private addresses (see [`is_private()`](#method.is_private)) + /// - the loopback address (see [`is_loopback()`](#method.is_loopback)) + /// - the link-local address (see [`is_link_local()`](#method.is_link_local)) + /// - the broadcast address (see [`is_broadcast()`](#method.is_broadcast)) + /// - addresses used for documentation (see [`is_documentation()`](#method.is_documentation)) + /// - the unspecified address (see [`is_unspecified()`](#method.is_unspecified)), and the whole + /// 0.0.0.0/8 block + /// - addresses reserved for future protocols (see + /// [`is_ietf_protocol_assignment()`](#method.is_ietf_protocol_assignment), except + /// `192.0.0.9/32` and `192.0.0.10/32` which are globally routable + /// - addresses reserved for future use (see [`is_reserved()`](#method.is_reserved) + /// - addresses reserved for networking devices benchmarking (see + /// [`is_benchmarking`](#method.is_benchmarking)) /// /// [ipv4-sr]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml /// [`true`]: ../../std/primitive.bool.html @@ -520,16 +527,181 @@ impl Ipv4Addr { /// use std::net::Ipv4Addr; /// /// fn main() { + /// // private addresses are not global /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false); /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false); /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false); + /// + /// // the 0.0.0.0/8 block is not global + /// assert_eq!(Ipv4Addr::new(0, 1, 2, 3).is_global(), false); + /// // in particular, the unspecified address is not global /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_global(), false); + /// + /// // the loopback address is not global + /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_global(), false); + /// + /// // link local addresses are not global + /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false); + /// + /// // the broadcast address is not global + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_global(), false); + /// + /// // the broadcast address is not global + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false); + /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false); + /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false); + /// + /// // shared addresses are not global + /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false); + /// + /// // addresses reserved for protocol assignment are not global + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_global(), false); + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_global(), false); + /// + /// // addresses reserved for future use are not global + /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false); + /// + /// // addresses reserved for network devices benchmarking are not global + /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false); + /// + /// // All the other addresses are global + /// assert_eq!(Ipv4Addr::new(1, 1, 1, 1).is_global(), true); /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true); /// } /// ``` pub fn is_global(&self) -> bool { - !self.is_private() && !self.is_loopback() && !self.is_link_local() && - !self.is_broadcast() && !self.is_documentation() && !self.is_unspecified() + // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two + // globally routable addresses in the 192.0.0.0/24 range. + if u32::from(*self) == 0xc0000009 || u32::from(*self) == 0xc000000a { + return true; + } + !self.is_private() + && !self.is_loopback() + && !self.is_link_local() + && !self.is_broadcast() + && !self.is_documentation() + && !self.is_shared() + && !self.is_ietf_protocol_assignment() + && !self.is_reserved() + && !self.is_benchmarking() + // Make sure the address is not in 0.0.0.0/8 + && self.octets()[0] != 0 + } + + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + /// [`true`]: ../../std/primitive.bool.html + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// fn main() { + /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true); + /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true); + /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); + /// } + /// ``` + pub fn is_shared(&self) -> bool { + self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + + /// Returns [`true`] if this address is part of `192.0.0.0/24`, which is reserved to + /// IANA for IETF protocol assignments, as documented in [IETF RFC 6890]. + /// + /// Note that parts of this block are in use: + /// + /// - `192.0.0.8/32` is the "IPv4 dummy address" (see [IETF RFC 7600]) + /// - `192.0.0.9/32` is the "Port Control Protocol Anycast" (see [IETF RFC 7723]) + /// - `192.0.0.10/32` is used for NAT traversal (see [IETF RFC 8155]) + /// + /// [IETF RFC 6890]: https://tools.ietf.org/html/rfc6890 + /// [IETF RFC 7600]: https://tools.ietf.org/html/rfc7600 + /// [IETF RFC 7723]: https://tools.ietf.org/html/rfc7723 + /// [IETF RFC 8155]: https://tools.ietf.org/html/rfc8155 + /// [`true`]: ../../std/primitive.bool.html + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// fn main() { + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_ietf_protocol_assignment(), true); + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 8).is_ietf_protocol_assignment(), true); + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 9).is_ietf_protocol_assignment(), true); + /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_ietf_protocol_assignment(), true); + /// assert_eq!(Ipv4Addr::new(192, 0, 1, 0).is_ietf_protocol_assignment(), false); + /// assert_eq!(Ipv4Addr::new(191, 255, 255, 255).is_ietf_protocol_assignment(), false); + /// } + /// ``` + pub fn is_ietf_protocol_assignment(&self) -> bool { + self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0 + } + + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// [errate 423]: https://www.rfc-editor.org/errata/eid423 + /// [`true`]: ../../std/primitive.bool.html + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// fn main() { + /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false); + /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true); + /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true); + /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); + /// } + /// ``` + pub fn is_benchmarking(&self) -> bool { + self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 + } + + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitely excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// [`true`]: ../../std/primitive.bool.html + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// fn main() { + /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true); + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true); + /// + /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false); + /// // The broadcast address is not considered as reserved for future use by this + /// // implementation + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); + /// } + /// ``` + pub fn is_reserved(&self) -> bool { + self.octets()[0] & 240 == 240 && !self.is_broadcast() } /// Returns [`true`] if this is a multicast address (224.0.0.0/4). @@ -658,7 +830,7 @@ impl Ipv4Addr { #[stable(feature = "ip_addr", since = "1.7.0")] impl fmt::Display for IpAddr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { IpAddr::V4(ip) => ip.fmt(fmt), IpAddr::V6(ip) => ip.fmt(fmt), @@ -682,7 +854,7 @@ impl From<Ipv6Addr> for IpAddr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Ipv4Addr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let octets = self.octets(); write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) } @@ -690,7 +862,7 @@ impl fmt::Display for Ipv4Addr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Ipv4Addr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } @@ -1003,7 +1175,7 @@ impl Ipv6Addr { } } - /// Returns [`true`] if this is a unique local address (fc00::/7). + /// Returns [`true`] if this is a unique local address (`fc00::/7`). /// /// This property is defined in [IETF RFC 4193]. /// @@ -1027,12 +1199,83 @@ impl Ipv6Addr { (self.segments()[0] & 0xfe00) == 0xfc00 } - /// Returns [`true`] if the address is unicast and link-local (fe80::/10). + /// Returns [`true`] if the address is a unicast link-local address (`fe80::/64`). /// - /// This property is defined in [IETF RFC 4291]. + /// A common mis-conception is to think that "unicast link-local addresses start with + /// `fe80::`", but the [IETF RFC 4291] actually defines a stricter format for these addresses: + /// + /// ```no_rust + /// | 10 | + /// | bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// + /// This method validates the format defined in the RFC and won't recognize the following + /// addresses such as `fe80:0:0:1::` or `fe81::` as unicast link-local addresses for example. + /// If you need a less strict validation use [`is_unicast_link_local()`] instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// fn main() { + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0); + /// assert!(ip.is_unicast_link_local_strict()); + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff); + /// assert!(ip.is_unicast_link_local_strict()); + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0); + /// assert!(!ip.is_unicast_link_local_strict()); + /// assert!(ip.is_unicast_link_local()); + /// + /// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0); + /// assert!(!ip.is_unicast_link_local_strict()); + /// assert!(ip.is_unicast_link_local()); + /// } + /// ``` + /// + /// # See also + /// + /// - [IETF RFC 4291 section 2.5.6] + /// - [RFC 4291 errata 4406] + /// - [`is_unicast_link_local()`] /// /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [IETF RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 /// [`true`]: ../../std/primitive.bool.html + /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 + /// [`is_unicast_link_local()`]: ../../std/net/struct.Ipv6Addr.html#method.is_unicast_link_local + /// + pub fn is_unicast_link_local_strict(&self) -> bool { + (self.segments()[0] & 0xffff) == 0xfe80 + && (self.segments()[1] & 0xffff) == 0 + && (self.segments()[2] & 0xffff) == 0 + && (self.segments()[3] & 0xffff) == 0 + } + + /// Returns [`true`] if the address is a unicast link-local address (`fe80::/10`). + /// + /// This method returns [`true`] for addresses in the range reserved by [RFC 4291 section 2.4], + /// i.e. addresses with the following format: + /// + /// ```no_rust + /// | 10 | + /// | bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| arbitratry value | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// + /// As a result, this method consider addresses such as `fe80:0:0:1::` or `fe81::` to be + /// unicast link-local addresses, whereas [`is_unicast_link_local_strict()`] does not. If you + /// need a strict validation fully compliant with the RFC, use + /// [`is_unicast_link_local_strict()`]. /// /// # Examples /// @@ -1042,19 +1285,49 @@ impl Ipv6Addr { /// use std::net::Ipv6Addr; /// /// fn main() { - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_link_local(), - /// false); - /// assert_eq!(Ipv6Addr::new(0xfe8a, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0); + /// assert!(ip.is_unicast_link_local()); + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xffff, 0xffff, 0xffff, 0xffff); + /// assert!(ip.is_unicast_link_local()); + /// + /// let ip = Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0); + /// assert!(ip.is_unicast_link_local()); + /// assert!(!ip.is_unicast_link_local_strict()); + /// + /// let ip = Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0); + /// assert!(ip.is_unicast_link_local()); + /// assert!(!ip.is_unicast_link_local_strict()); /// } /// ``` + /// + /// # See also + /// + /// - [IETF RFC 4291 section 2.4] + /// - [RFC 4291 errata 4406] + /// + /// [IETF RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [`true`]: ../../std/primitive.bool.html + /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 + /// [`is_unicast_link_local_strict()`]: ../../std/net/struct.Ipv6Addr.html#method.is_unicast_link_local_strict + /// pub fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } - /// Returns [`true`] if this is a deprecated unicast site-local address - /// (fec0::/10). + /// Returns [`true`] if this is a deprecated unicast site-local address (fec0::/10). The + /// unicast site-local address format is defined in [RFC 4291 section 2.5.7] as: + /// + /// ```no_rust + /// | 10 | + /// | bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111011| subnet ID | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` /// /// [`true`]: ../../std/primitive.bool.html + /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 /// /// # Examples /// @@ -1069,6 +1342,14 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0xfec2, 0, 0, 0, 0, 0, 0, 0).is_unicast_site_local(), true); /// } /// ``` + /// + /// # Warning + /// + /// As per [RFC 3879], the whole `FEC0::/10` prefix is + /// deprecated. New software must not support site-local + /// addresses. + /// + /// [RFC 3879]: https://tools.ietf.org/html/rfc3879 pub fn is_unicast_site_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfec0 } @@ -1104,12 +1385,20 @@ impl Ipv6Addr { /// /// - the loopback address /// - the link-local addresses - /// - the (deprecated) site-local addresses /// - unique local addresses /// - the unspecified address /// - the address range reserved for documentation /// + /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7] + /// + /// ```no_rust + /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer + /// be supported in new implementations (i.e., new implementations must treat this prefix as + /// Global Unicast). + /// ``` + /// /// [`true`]: ../../std/primitive.bool.html + /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 /// /// # Examples /// @@ -1126,9 +1415,11 @@ impl Ipv6Addr { /// ``` pub fn is_unicast_global(&self) -> bool { !self.is_multicast() - && !self.is_loopback() && !self.is_unicast_link_local() - && !self.is_unicast_site_local() && !self.is_unique_local() - && !self.is_unspecified() && !self.is_documentation() + && !self.is_loopback() + && !self.is_unicast_link_local() + && !self.is_unique_local() + && !self.is_unspecified() + && !self.is_documentation() } /// Returns the address's multicast scope if the address is multicast. @@ -1229,7 +1520,7 @@ impl Ipv6Addr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Ipv6Addr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.segments() { // We need special cases for :: and ::1, otherwise they're formatted // as ::0.0.0.[01] @@ -1276,7 +1567,7 @@ impl fmt::Display for Ipv6Addr { let (zeros_at, zeros_len) = find_zero_slice(&self.segments()); if zeros_len > 1 { - fn fmt_subslice(segments: &[u16], fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt_subslice(segments: &[u16], fmt: &mut fmt::Formatter<'_>) -> fmt::Result { if !segments.is_empty() { write!(fmt, "{:x}", segments[0])?; for &seg in &segments[1..] { @@ -1301,7 +1592,7 @@ impl fmt::Display for Ipv6Addr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Ipv6Addr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, fmt) } } @@ -1510,8 +1801,8 @@ impl From<[u16; 8]> for IpAddr { #[cfg(all(test, not(target_os = "emscripten")))] mod tests { use crate::net::*; - use crate::net::Ipv6MulticastScope::*; use crate::net::test::{tsa, sa6, sa4}; + use crate::str::FromStr; #[test] fn test_from_str_ipv4() { @@ -1675,164 +1966,491 @@ mod tests { #[test] fn ip_properties() { - fn check4(octets: &[u8; 4], unspec: bool, loopback: bool, - global: bool, multicast: bool, documentation: bool) { - let ip = IpAddr::V4(Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3])); - assert_eq!(ip.is_unspecified(), unspec); - assert_eq!(ip.is_loopback(), loopback); - assert_eq!(ip.is_global(), global); - assert_eq!(ip.is_multicast(), multicast); - assert_eq!(ip.is_documentation(), documentation); + macro_rules! ip { + ($s:expr) => { + IpAddr::from_str($s).unwrap() + } } - fn check6(str_addr: &str, unspec: bool, loopback: bool, - global: bool, u_doc: bool, mcast: bool) { - let ip = IpAddr::V6(str_addr.parse().unwrap()); - assert_eq!(ip.is_unspecified(), unspec); - assert_eq!(ip.is_loopback(), loopback); - assert_eq!(ip.is_global(), global); - assert_eq!(ip.is_documentation(), u_doc); - assert_eq!(ip.is_multicast(), mcast); + macro_rules! check { + ($s:expr) => { + check!($s, 0); + }; + + ($s:expr, $mask:expr) => {{ + let unspec: u8 = 1 << 0; + let loopback: u8 = 1 << 1; + let global: u8 = 1 << 2; + let multicast: u8 = 1 << 3; + let doc: u8 = 1 << 4; + + if ($mask & unspec) == unspec { + assert!(ip!($s).is_unspecified()); + } else { + assert!(!ip!($s).is_unspecified()); + } + + if ($mask & loopback) == loopback { + assert!(ip!($s).is_loopback()); + } else { + assert!(!ip!($s).is_loopback()); + } + + if ($mask & global) == global { + assert!(ip!($s).is_global()); + } else { + assert!(!ip!($s).is_global()); + } + + if ($mask & multicast) == multicast { + assert!(ip!($s).is_multicast()); + } else { + assert!(!ip!($s).is_multicast()); + } + + if ($mask & doc) == doc { + assert!(ip!($s).is_documentation()); + } else { + assert!(!ip!($s).is_documentation()); + } + }} } - // address unspec loopbk global multicast doc - check4(&[0, 0, 0, 0], true, false, false, false, false); - check4(&[0, 0, 0, 1], false, false, true, false, false); - check4(&[0, 1, 0, 0], false, false, true, false, false); - check4(&[10, 9, 8, 7], false, false, false, false, false); - check4(&[127, 1, 2, 3], false, true, false, false, false); - check4(&[172, 31, 254, 253], false, false, false, false, false); - check4(&[169, 254, 253, 242], false, false, false, false, false); - check4(&[192, 0, 2, 183], false, false, false, false, true); - check4(&[192, 1, 2, 183], false, false, true, false, false); - check4(&[192, 168, 254, 253], false, false, false, false, false); - check4(&[198, 51, 100, 0], false, false, false, false, true); - check4(&[203, 0, 113, 0], false, false, false, false, true); - check4(&[203, 2, 113, 0], false, false, true, false, false); - check4(&[224, 0, 0, 0], false, false, true, true, false); - check4(&[239, 255, 255, 255], false, false, true, true, false); - check4(&[255, 255, 255, 255], false, false, false, false, false); - - // address unspec loopbk global doc mcast - check6("::", true, false, false, false, false); - check6("::1", false, true, false, false, false); - check6("::0.0.0.2", false, false, true, false, false); - check6("1::", false, false, true, false, false); - check6("fc00::", false, false, false, false, false); - check6("fdff:ffff::", false, false, false, false, false); - check6("fe80:ffff::", false, false, false, false, false); - check6("febf:ffff::", false, false, false, false, false); - check6("fec0::", false, false, false, false, false); - check6("ff01::", false, false, false, false, true); - check6("ff02::", false, false, false, false, true); - check6("ff03::", false, false, false, false, true); - check6("ff04::", false, false, false, false, true); - check6("ff05::", false, false, false, false, true); - check6("ff08::", false, false, false, false, true); - check6("ff0e::", false, false, true, false, true); - check6("2001:db8:85a3::8a2e:370:7334", false, false, false, true, false); - check6("102:304:506:708:90a:b0c:d0e:f10", false, false, true, false, false); + let unspec: u8 = 1 << 0; + let loopback: u8 = 1 << 1; + let global: u8 = 1 << 2; + let multicast: u8 = 1 << 3; + let doc: u8 = 1 << 4; + + check!("0.0.0.0", unspec); + check!("0.0.0.1"); + check!("0.1.0.0"); + check!("10.9.8.7"); + check!("127.1.2.3", loopback); + check!("172.31.254.253"); + check!("169.254.253.242"); + check!("192.0.2.183", doc); + check!("192.1.2.183", global); + check!("192.168.254.253"); + check!("198.51.100.0", doc); + check!("203.0.113.0", doc); + check!("203.2.113.0", global); + check!("224.0.0.0", global|multicast); + check!("239.255.255.255", global|multicast); + check!("255.255.255.255"); + // make sure benchmarking addresses are not global + check!("198.18.0.0"); + check!("198.18.54.2"); + check!("198.19.255.255"); + // make sure addresses reserved for protocol assignment are not global + check!("192.0.0.0"); + check!("192.0.0.255"); + check!("192.0.0.100"); + // make sure reserved addresses are not global + check!("240.0.0.0"); + check!("251.54.1.76"); + check!("254.255.255.255"); + // make sure shared addresses are not global + check!("100.64.0.0"); + check!("100.127.255.255"); + check!("100.100.100.0"); + + check!("::", unspec); + check!("::1", loopback); + check!("::0.0.0.2", global); + check!("1::", global); + check!("fc00::"); + check!("fdff:ffff::"); + check!("fe80:ffff::"); + check!("febf:ffff::"); + check!("fec0::", global); + check!("ff01::", multicast); + check!("ff02::", multicast); + check!("ff03::", multicast); + check!("ff04::", multicast); + check!("ff05::", multicast); + check!("ff08::", multicast); + check!("ff0e::", global|multicast); + check!("2001:db8:85a3::8a2e:370:7334", doc); + check!("102:304:506:708:90a:b0c:d0e:f10", global); } #[test] fn ipv4_properties() { - fn check(octets: &[u8; 4], unspec: bool, loopback: bool, - private: bool, link_local: bool, global: bool, - multicast: bool, broadcast: bool, documentation: bool) { - let ip = Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]); - assert_eq!(octets, &ip.octets()); - - assert_eq!(ip.is_unspecified(), unspec); - assert_eq!(ip.is_loopback(), loopback); - assert_eq!(ip.is_private(), private); - assert_eq!(ip.is_link_local(), link_local); - assert_eq!(ip.is_global(), global); - assert_eq!(ip.is_multicast(), multicast); - assert_eq!(ip.is_broadcast(), broadcast); - assert_eq!(ip.is_documentation(), documentation); + macro_rules! ip { + ($s:expr) => { + Ipv4Addr::from_str($s).unwrap() + } } - // address unspec loopbk privt linloc global multicast brdcast doc - check(&[0, 0, 0, 0], true, false, false, false, false, false, false, false); - check(&[0, 0, 0, 1], false, false, false, false, true, false, false, false); - check(&[0, 1, 0, 0], false, false, false, false, true, false, false, false); - check(&[10, 9, 8, 7], false, false, true, false, false, false, false, false); - check(&[127, 1, 2, 3], false, true, false, false, false, false, false, false); - check(&[172, 31, 254, 253], false, false, true, false, false, false, false, false); - check(&[169, 254, 253, 242], false, false, false, true, false, false, false, false); - check(&[192, 0, 2, 183], false, false, false, false, false, false, false, true); - check(&[192, 1, 2, 183], false, false, false, false, true, false, false, false); - check(&[192, 168, 254, 253], false, false, true, false, false, false, false, false); - check(&[198, 51, 100, 0], false, false, false, false, false, false, false, true); - check(&[203, 0, 113, 0], false, false, false, false, false, false, false, true); - check(&[203, 2, 113, 0], false, false, false, false, true, false, false, false); - check(&[224, 0, 0, 0], false, false, false, false, true, true, false, false); - check(&[239, 255, 255, 255], false, false, false, false, true, true, false, false); - check(&[255, 255, 255, 255], false, false, false, false, false, false, true, false); + macro_rules! check { + ($s:expr) => { + check!($s, 0); + }; + + ($s:expr, $mask:expr) => {{ + let unspec: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let private: u16 = 1 << 2; + let link_local: u16 = 1 << 3; + let global: u16 = 1 << 4; + let multicast: u16 = 1 << 5; + let broadcast: u16 = 1 << 6; + let documentation: u16 = 1 << 7; + let benchmarking: u16 = 1 << 8; + let ietf_protocol_assignment: u16 = 1 << 9; + let reserved: u16 = 1 << 10; + let shared: u16 = 1 << 11; + + if ($mask & unspec) == unspec { + assert!(ip!($s).is_unspecified()); + } else { + assert!(!ip!($s).is_unspecified()); + } + + if ($mask & loopback) == loopback { + assert!(ip!($s).is_loopback()); + } else { + assert!(!ip!($s).is_loopback()); + } + + if ($mask & private) == private { + assert!(ip!($s).is_private()); + } else { + assert!(!ip!($s).is_private()); + } + + if ($mask & link_local) == link_local { + assert!(ip!($s).is_link_local()); + } else { + assert!(!ip!($s).is_link_local()); + } + + if ($mask & global) == global { + assert!(ip!($s).is_global()); + } else { + assert!(!ip!($s).is_global()); + } + + if ($mask & multicast) == multicast { + assert!(ip!($s).is_multicast()); + } else { + assert!(!ip!($s).is_multicast()); + } + + if ($mask & broadcast) == broadcast { + assert!(ip!($s).is_broadcast()); + } else { + assert!(!ip!($s).is_broadcast()); + } + + if ($mask & documentation) == documentation { + assert!(ip!($s).is_documentation()); + } else { + assert!(!ip!($s).is_documentation()); + } + + if ($mask & benchmarking) == benchmarking { + assert!(ip!($s).is_benchmarking()); + } else { + assert!(!ip!($s).is_benchmarking()); + } + + if ($mask & ietf_protocol_assignment) == ietf_protocol_assignment { + assert!(ip!($s).is_ietf_protocol_assignment()); + } else { + assert!(!ip!($s).is_ietf_protocol_assignment()); + } + + if ($mask & reserved) == reserved { + assert!(ip!($s).is_reserved()); + } else { + assert!(!ip!($s).is_reserved()); + } + + if ($mask & shared) == shared { + assert!(ip!($s).is_shared()); + } else { + assert!(!ip!($s).is_shared()); + } + }} + } + + let unspec: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let private: u16 = 1 << 2; + let link_local: u16 = 1 << 3; + let global: u16 = 1 << 4; + let multicast: u16 = 1 << 5; + let broadcast: u16 = 1 << 6; + let documentation: u16 = 1 << 7; + let benchmarking: u16 = 1 << 8; + let ietf_protocol_assignment: u16 = 1 << 9; + let reserved: u16 = 1 << 10; + let shared: u16 = 1 << 11; + + check!("0.0.0.0", unspec); + check!("0.0.0.1"); + check!("0.1.0.0"); + check!("10.9.8.7", private); + check!("127.1.2.3", loopback); + check!("172.31.254.253", private); + check!("169.254.253.242", link_local); + check!("192.0.2.183", documentation); + check!("192.1.2.183", global); + check!("192.168.254.253", private); + check!("198.51.100.0", documentation); + check!("203.0.113.0", documentation); + check!("203.2.113.0", global); + check!("224.0.0.0", global|multicast); + check!("239.255.255.255", global|multicast); + check!("255.255.255.255", broadcast); + check!("198.18.0.0", benchmarking); + check!("198.18.54.2", benchmarking); + check!("198.19.255.255", benchmarking); + check!("192.0.0.0", ietf_protocol_assignment); + check!("192.0.0.255", ietf_protocol_assignment); + check!("192.0.0.100", ietf_protocol_assignment); + check!("240.0.0.0", reserved); + check!("251.54.1.76", reserved); + check!("254.255.255.255", reserved); + check!("100.64.0.0", shared); + check!("100.127.255.255", shared); + check!("100.100.100.0", shared); } #[test] fn ipv6_properties() { - fn check(str_addr: &str, octets: &[u8; 16], unspec: bool, loopback: bool, - unique_local: bool, global: bool, - u_link_local: bool, u_site_local: bool, u_global: bool, u_doc: bool, - m_scope: Option<Ipv6MulticastScope>) { - let ip: Ipv6Addr = str_addr.parse().unwrap(); - assert_eq!(str_addr, ip.to_string()); - assert_eq!(&ip.octets(), octets); - assert_eq!(Ipv6Addr::from(*octets), ip); - - assert_eq!(ip.is_unspecified(), unspec); - assert_eq!(ip.is_loopback(), loopback); - assert_eq!(ip.is_unique_local(), unique_local); - assert_eq!(ip.is_global(), global); - assert_eq!(ip.is_unicast_link_local(), u_link_local); - assert_eq!(ip.is_unicast_site_local(), u_site_local); - assert_eq!(ip.is_unicast_global(), u_global); - assert_eq!(ip.is_documentation(), u_doc); - assert_eq!(ip.multicast_scope(), m_scope); - assert_eq!(ip.is_multicast(), m_scope.is_some()); + macro_rules! ip { + ($s:expr) => { + Ipv6Addr::from_str($s).unwrap() + } + } + + macro_rules! check { + ($s:expr, &[$($octet:expr),*], $mask:expr) => { + assert_eq!($s, ip!($s).to_string()); + let octets = &[$($octet),*]; + assert_eq!(&ip!($s).octets(), octets); + assert_eq!(Ipv6Addr::from(*octets), ip!($s)); + + let unspecified: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let unique_local: u16 = 1 << 2; + let global: u16 = 1 << 3; + let unicast_link_local: u16 = 1 << 4; + let unicast_link_local_strict: u16 = 1 << 5; + let unicast_site_local: u16 = 1 << 6; + let unicast_global: u16 = 1 << 7; + let documentation: u16 = 1 << 8; + let multicast_interface_local: u16 = 1 << 9; + let multicast_link_local: u16 = 1 << 10; + let multicast_realm_local: u16 = 1 << 11; + let multicast_admin_local: u16 = 1 << 12; + let multicast_site_local: u16 = 1 << 13; + let multicast_organization_local: u16 = 1 << 14; + let multicast_global: u16 = 1 << 15; + let multicast: u16 = multicast_interface_local + | multicast_admin_local + | multicast_global + | multicast_link_local + | multicast_realm_local + | multicast_site_local + | multicast_organization_local; + + if ($mask & unspecified) == unspecified { + assert!(ip!($s).is_unspecified()); + } else { + assert!(!ip!($s).is_unspecified()); + } + if ($mask & loopback) == loopback { + assert!(ip!($s).is_loopback()); + } else { + assert!(!ip!($s).is_loopback()); + } + if ($mask & unique_local) == unique_local { + assert!(ip!($s).is_unique_local()); + } else { + assert!(!ip!($s).is_unique_local()); + } + if ($mask & global) == global { + assert!(ip!($s).is_global()); + } else { + assert!(!ip!($s).is_global()); + } + if ($mask & unicast_link_local) == unicast_link_local { + assert!(ip!($s).is_unicast_link_local()); + } else { + assert!(!ip!($s).is_unicast_link_local()); + } + if ($mask & unicast_link_local_strict) == unicast_link_local_strict { + assert!(ip!($s).is_unicast_link_local_strict()); + } else { + assert!(!ip!($s).is_unicast_link_local_strict()); + } + if ($mask & unicast_site_local) == unicast_site_local { + assert!(ip!($s).is_unicast_site_local()); + } else { + assert!(!ip!($s).is_unicast_site_local()); + } + if ($mask & unicast_global) == unicast_global { + assert!(ip!($s).is_unicast_global()); + } else { + assert!(!ip!($s).is_unicast_global()); + } + if ($mask & documentation) == documentation { + assert!(ip!($s).is_documentation()); + } else { + assert!(!ip!($s).is_documentation()); + } + if ($mask & multicast) != 0 { + assert!(ip!($s).multicast_scope().is_some()); + assert!(ip!($s).is_multicast()); + } else { + assert!(ip!($s).multicast_scope().is_none()); + assert!(!ip!($s).is_multicast()); + } + if ($mask & multicast_interface_local) == multicast_interface_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::InterfaceLocal); + } + if ($mask & multicast_link_local) == multicast_link_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::LinkLocal); + } + if ($mask & multicast_realm_local) == multicast_realm_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::RealmLocal); + } + if ($mask & multicast_admin_local) == multicast_admin_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::AdminLocal); + } + if ($mask & multicast_site_local) == multicast_site_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::SiteLocal); + } + if ($mask & multicast_organization_local) == multicast_organization_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::OrganizationLocal); + } + if ($mask & multicast_global) == multicast_global { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::Global); + } + } } - // unspec loopbk uniqlo global unill unisl uniglo doc mscope - check("::", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - true, false, false, false, false, false, false, false, None); - check("::1", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - false, true, false, false, false, false, false, false, None); - check("::0.0.0.2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], - false, false, false, true, false, false, true, false, None); - check("1::", &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, true, false, false, true, false, None); - check("fc00::", &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, true, false, false, false, false, false, None); - check("fdff:ffff::", &[0xfd, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, true, false, false, false, false, false, None); - check("fe80:ffff::", &[0xfe, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, false, true, false, false, false, None); - check("febf:ffff::", &[0xfe, 0xbf, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, false, true, false, false, false, None); - check("fec0::", &[0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, false, false, true, false, false, None); - check("ff01::", &[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, false, false, false, false, false, Some(InterfaceLocal)); - check("ff02::", &[0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, false, false, false, false, false, Some(LinkLocal)); - check("ff03::", &[0xff, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, false, false, false, false, false, Some(RealmLocal)); - check("ff04::", &[0xff, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, false, false, false, false, false, Some(AdminLocal)); - check("ff05::", &[0xff, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, false, false, false, false, false, Some(SiteLocal)); - check("ff08::", &[0xff, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, false, false, false, false, false, Some(OrganizationLocal)); - check("ff0e::", &[0xff, 0xe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - false, false, false, true, false, false, false, false, Some(Global)); - check("2001:db8:85a3::8a2e:370:7334", - &[0x20, 1, 0xd, 0xb8, 0x85, 0xa3, 0, 0, 0, 0, 0x8a, 0x2e, 3, 0x70, 0x73, 0x34], - false, false, false, false, false, false, false, true, None); - check("102:304:506:708:90a:b0c:d0e:f10", - &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - false, false, false, true, false, false, true, false, None); + let unspecified: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let unique_local: u16 = 1 << 2; + let global: u16 = 1 << 3; + let unicast_link_local: u16 = 1 << 4; + let unicast_link_local_strict: u16 = 1 << 5; + let unicast_site_local: u16 = 1 << 6; + let unicast_global: u16 = 1 << 7; + let documentation: u16 = 1 << 8; + let multicast_interface_local: u16 = 1 << 9; + let multicast_link_local: u16 = 1 << 10; + let multicast_realm_local: u16 = 1 << 11; + let multicast_admin_local: u16 = 1 << 12; + let multicast_site_local: u16 = 1 << 13; + let multicast_organization_local: u16 = 1 << 14; + let multicast_global: u16 = 1 << 15; + + check!("::", + &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unspecified); + + check!("::1", + &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + loopback); + + check!("::0.0.0.2", + &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], + global | unicast_global); + + check!("1::", + &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + global | unicast_global); + + check!("fc00::", + &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unique_local); + + check!("fdff:ffff::", + &[0xfd, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unique_local); + + check!("fe80:ffff::", + &[0xfe, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local); + + check!("fe80::", + &[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local|unicast_link_local_strict); + + check!("febf:ffff::", + &[0xfe, 0xbf, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local); + + check!("febf::", + &[0xfe, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local); + + check!("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + &[0xfe, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], + unicast_link_local); + + check!("fe80::ffff:ffff:ffff:ffff", + &[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], + unicast_link_local|unicast_link_local_strict); + + check!("fe80:0:0:1::", + &[0xfe, 0x80, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local); + + check!("fec0::", + &[0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_site_local|unicast_global|global); + + check!("ff01::", + &[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_interface_local); + + check!("ff02::", + &[0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_link_local); + + check!("ff03::", + &[0xff, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_realm_local); + + check!("ff04::", + &[0xff, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_admin_local); + + check!("ff05::", + &[0xff, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_site_local); + + check!("ff08::", + &[0xff, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_organization_local); + + check!("ff0e::", + &[0xff, 0xe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_global | global); + + check!("2001:db8:85a3::8a2e:370:7334", + &[0x20, 1, 0xd, 0xb8, 0x85, 0xa3, 0, 0, 0, 0, 0x8a, 0x2e, 3, 0x70, 0x73, 0x34], + documentation); + + check!("102:304:506:708:90a:b0c:d0e:f10", + &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + global| unicast_global); } #[test] diff --git a/src/libstd/net/parser.rs b/src/libstd/net/parser.rs index 7951cd6bcf2..5a76139530a 100644 --- a/src/libstd/net/parser.rs +++ b/src/libstd/net/parser.rs @@ -28,7 +28,7 @@ impl<'a> Parser<'a> { // Commit only if parser returns Some fn read_atomically<T, F>(&mut self, cb: F) -> Option<T> where - F: FnOnce(&mut Parser) -> Option<T>, + F: FnOnce(&mut Parser<'_>) -> Option<T>, { let pos = self.pos; let r = cb(self); @@ -40,7 +40,7 @@ impl<'a> Parser<'a> { // Commit only if parser read till EOF fn read_till_eof<T, F>(&mut self, cb: F) -> Option<T> where - F: FnOnce(&mut Parser) -> Option<T>, + F: FnOnce(&mut Parser<'_>) -> Option<T>, { self.read_atomically(move |p| { cb(p).filter(|_| p.is_eof()) @@ -48,10 +48,10 @@ impl<'a> Parser<'a> { } // Return result of first successful parser - fn read_or<T>(&mut self, parsers: &mut [Box<dyn FnMut(&mut Parser) -> Option<T> + 'static>]) + fn read_or<T>(&mut self, parsers: &mut [Box<dyn FnMut(&mut Parser<'_>) -> Option<T> + 'static>]) -> Option<T> { for pf in parsers { - if let Some(r) = self.read_atomically(|p: &mut Parser| pf(p)) { + if let Some(r) = self.read_atomically(|p: &mut Parser<'_>| pf(p)) { return Some(r); } } @@ -64,9 +64,9 @@ impl<'a> Parser<'a> { pb: PB, pc: PC) -> Option<(A, B, C)> where - PA: FnOnce(&mut Parser) -> Option<A>, - PB: FnOnce(&mut Parser) -> Option<B>, - PC: FnOnce(&mut Parser) -> Option<C>, + PA: FnOnce(&mut Parser<'_>) -> Option<A>, + PB: FnOnce(&mut Parser<'_>) -> Option<B>, + PC: FnOnce(&mut Parser<'_>) -> Option<C>, { self.read_atomically(move |p| { let a = pa(p); @@ -177,7 +177,7 @@ impl<'a> Parser<'a> { Ipv6Addr::new(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7]) } - fn read_groups(p: &mut Parser, groups: &mut [u16; 8], limit: usize) + fn read_groups(p: &mut Parser<'_>, groups: &mut [u16; 8], limit: usize) -> (usize, bool) { let mut i = 0; while i < limit { @@ -244,15 +244,15 @@ impl<'a> Parser<'a> { } fn read_ip_addr(&mut self) -> Option<IpAddr> { - let ipv4_addr = |p: &mut Parser| p.read_ipv4_addr().map(IpAddr::V4); - let ipv6_addr = |p: &mut Parser| p.read_ipv6_addr().map(IpAddr::V6); + let ipv4_addr = |p: &mut Parser<'_>| p.read_ipv4_addr().map(IpAddr::V4); + let ipv6_addr = |p: &mut Parser<'_>| p.read_ipv6_addr().map(IpAddr::V6); self.read_or(&mut [Box::new(ipv4_addr), Box::new(ipv6_addr)]) } fn read_socket_addr_v4(&mut self) -> Option<SocketAddrV4> { - let ip_addr = |p: &mut Parser| p.read_ipv4_addr(); - let colon = |p: &mut Parser| p.read_given_char(':'); - let port = |p: &mut Parser| { + let ip_addr = |p: &mut Parser<'_>| p.read_ipv4_addr(); + let colon = |p: &mut Parser<'_>| p.read_given_char(':'); + let port = |p: &mut Parser<'_>| { p.read_number(10, 5, 0x10000).map(|n| n as u16) }; @@ -263,14 +263,14 @@ impl<'a> Parser<'a> { } fn read_socket_addr_v6(&mut self) -> Option<SocketAddrV6> { - let ip_addr = |p: &mut Parser| { - let open_br = |p: &mut Parser| p.read_given_char('['); - let ip_addr = |p: &mut Parser| p.read_ipv6_addr(); - let clos_br = |p: &mut Parser| p.read_given_char(']'); + let ip_addr = |p: &mut Parser<'_>| { + let open_br = |p: &mut Parser<'_>| p.read_given_char('['); + let ip_addr = |p: &mut Parser<'_>| p.read_ipv6_addr(); + let clos_br = |p: &mut Parser<'_>| p.read_given_char(']'); p.read_seq_3(open_br, ip_addr, clos_br).map(|t| t.1) }; - let colon = |p: &mut Parser| p.read_given_char(':'); - let port = |p: &mut Parser| { + let colon = |p: &mut Parser<'_>| p.read_given_char(':'); + let port = |p: &mut Parser<'_>| { p.read_number(10, 5, 0x10000).map(|n| n as u16) }; @@ -281,8 +281,8 @@ impl<'a> Parser<'a> { } fn read_socket_addr(&mut self) -> Option<SocketAddr> { - let v4 = |p: &mut Parser| p.read_socket_addr_v4().map(SocketAddr::V4); - let v6 = |p: &mut Parser| p.read_socket_addr_v6().map(SocketAddr::V6); + let v4 = |p: &mut Parser<'_>| p.read_socket_addr_v4().map(SocketAddr::V4); + let v6 = |p: &mut Parser<'_>| p.read_socket_addr_v6().map(SocketAddr::V6); self.read_or(&mut [Box::new(v4), Box::new(v6)]) } } @@ -391,7 +391,7 @@ pub struct AddrParseError(()); #[stable(feature = "addr_parse_error_error", since = "1.4.0")] impl fmt::Display for AddrParseError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.write_str(self.description()) } } diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index ce0c5c0bb0d..cdffa390223 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -1,7 +1,7 @@ use crate::io::prelude::*; use crate::fmt; -use crate::io::{self, Initializer, IoVec, IoVecMut}; +use crate::io::{self, Initializer, IoSlice, IoSliceMut}; use crate::net::{ToSocketAddrs, SocketAddr, Shutdown}; use crate::sys_common::net as net_imp; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -569,7 +569,7 @@ impl TcpStream { impl Read for TcpStream { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.0.read_vectored(bufs) } @@ -582,7 +582,7 @@ impl Read for TcpStream { impl Write for TcpStream { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { self.0.write_vectored(bufs) } @@ -592,7 +592,7 @@ impl Write for TcpStream { impl Read for &TcpStream { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.0.read_vectored(bufs) } @@ -605,7 +605,7 @@ impl Read for &TcpStream { impl Write for &TcpStream { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { self.0.write_vectored(bufs) } @@ -626,7 +626,7 @@ impl IntoInner<net_imp::TcpStream> for TcpStream { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } @@ -771,7 +771,7 @@ impl TcpListener { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn incoming(&self) -> Incoming { + pub fn incoming(&self) -> Incoming<'_> { Incoming { listener: self } } @@ -922,19 +922,19 @@ impl IntoInner<net_imp::TcpListener> for TcpListener { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } #[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))] mod tests { - use crate::io::{ErrorKind, IoVec, IoVecMut}; + use crate::fmt; + use crate::io::{ErrorKind, IoSlice, IoSliceMut}; use crate::io::prelude::*; use crate::net::*; use crate::net::test::{next_test_ip4, next_test_ip6}; use crate::sync::mpsc::channel; - use crate::sys_common::AsInner; use crate::time::{Instant, Duration}; use crate::thread; @@ -1129,7 +1129,7 @@ mod tests { connect(i + 1, addr); t!(stream.write(&[i as u8])); }); - t.join().ok().unwrap(); + t.join().ok().expect("thread panicked"); } } @@ -1162,7 +1162,7 @@ mod tests { connect(i + 1, addr); t!(stream.write(&[99])); }); - t.join().ok().unwrap(); + t.join().ok().expect("thread panicked"); } } @@ -1216,7 +1216,7 @@ mod tests { let mut b = [0]; let mut c = [0; 3]; let len = t!(s2.read_vectored( - &mut [IoVecMut::new(&mut a), IoVecMut::new(&mut b), IoVecMut::new(&mut c)], + &mut [IoSliceMut::new(&mut a), IoSliceMut::new(&mut b), IoSliceMut::new(&mut c)], )); assert!(len > 0); assert_eq!(b, [10]); @@ -1235,7 +1235,7 @@ mod tests { let a = []; let b = [10]; let c = [11, 12]; - t!(s1.write_vectored(&[IoVec::new(&a), IoVec::new(&b), IoVec::new(&c)])); + t!(s1.write_vectored(&[IoSlice::new(&a), IoSlice::new(&b), IoSlice::new(&c)])); let mut buf = [0; 4]; let len = t!(s2.read(&mut buf)); @@ -1377,6 +1377,8 @@ mod tests { } #[test] + // FIXME: https://github.com/fortanix/rust-sgx/issues/110 + #[cfg_attr(target_env = "sgx", ignore)] fn shutdown_smoke() { each_ip(&mut |addr| { let a = t!(TcpListener::bind(&addr)); @@ -1397,6 +1399,8 @@ mod tests { } #[test] + // FIXME: https://github.com/fortanix/rust-sgx/issues/110 + #[cfg_attr(target_env = "sgx", ignore)] fn close_readwrite_smoke() { each_ip(&mut |addr| { let a = t!(TcpListener::bind(&addr)); @@ -1550,30 +1554,51 @@ mod tests { #[test] fn debug() { - let name = if cfg!(windows) {"socket"} else {"fd"}; + #[cfg(not(target_env = "sgx"))] + fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { + addr + } + #[cfg(target_env = "sgx")] + fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a { + addr.to_string() + } + + #[cfg(unix)] + use crate::os::unix::io::AsRawFd; + #[cfg(target_env = "sgx")] + use crate::os::fortanix_sgx::io::AsRawFd; + #[cfg(not(windows))] + fn render_inner(addr: &dyn AsRawFd) -> impl fmt::Debug { + addr.as_raw_fd() + } + #[cfg(windows)] + fn render_inner(addr: &dyn crate::os::windows::io::AsRawSocket) -> impl fmt::Debug { + addr.as_raw_socket() + } + + let inner_name = if cfg!(windows) {"socket"} else {"fd"}; let socket_addr = next_test_ip4(); let listener = t!(TcpListener::bind(&socket_addr)); - let listener_inner = listener.0.socket().as_inner(); let compare = format!("TcpListener {{ addr: {:?}, {}: {:?} }}", - socket_addr, name, listener_inner); + render_socket_addr(&socket_addr), + inner_name, + render_inner(&listener)); assert_eq!(format!("{:?}", listener), compare); - let stream = t!(TcpStream::connect(&("localhost", - socket_addr.port()))); - let stream_inner = stream.0.socket().as_inner(); - let compare = format!("TcpStream {{ addr: {:?}, \ - peer: {:?}, {}: {:?} }}", - stream.local_addr().unwrap(), - stream.peer_addr().unwrap(), - name, - stream_inner); + let stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); + let compare = format!("TcpStream {{ addr: {:?}, peer: {:?}, {}: {:?} }}", + render_socket_addr(&stream.local_addr().unwrap()), + render_socket_addr(&stream.peer_addr().unwrap()), + inner_name, + render_inner(&stream)); assert_eq!(format!("{:?}", stream), compare); } - // FIXME: re-enabled bitrig/openbsd tests once their socket timeout code + // FIXME: re-enabled openbsd tests once their socket timeout code // no longer has rounding errors. - #[cfg_attr(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"), ignore)] + #[cfg_attr(any(target_os = "netbsd", target_os = "openbsd"), ignore)] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 #[test] fn timeouts() { let addr = next_test_ip4(); @@ -1601,6 +1626,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn test_read_timeout() { let addr = next_test_ip4(); let listener = t!(TcpListener::bind(&addr)); @@ -1618,6 +1644,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn test_read_with_timeout() { let addr = next_test_ip4(); let listener = t!(TcpListener::bind(&addr)); @@ -1661,6 +1688,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] fn nodelay() { let addr = next_test_ip4(); let _listener = t!(TcpListener::bind(&addr)); @@ -1675,6 +1703,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] fn ttl() { let ttl = 100; @@ -1691,6 +1720,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] fn set_nonblocking() { let addr = next_test_ip4(); let listener = t!(TcpListener::bind(&addr)); @@ -1712,6 +1742,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn peek() { each_ip(&mut |addr| { let (txdone, rxdone) = channel(); @@ -1743,6 +1774,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn connect_timeout_valid() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); diff --git a/src/libstd/net/test.rs b/src/libstd/net/test.rs index 89fefd9d1d5..e2991cbdd88 100644 --- a/src/libstd/net/test.rs +++ b/src/libstd/net/test.rs @@ -36,12 +36,16 @@ pub fn tsa<A: ToSocketAddrs>(a: A) -> Result<Vec<SocketAddr>, String> { // all want to use ports. This function figures out which workspace // it is running in and assigns a port range based on it. fn base_port() -> u16 { - let cwd = env::current_dir().unwrap(); + let cwd = if cfg!(target_env = "sgx") { + String::from("sgx") + } else { + env::current_dir().unwrap().into_os_string().into_string().unwrap() + }; let dirs = ["32-opt", "32-nopt", "musl-64-opt", "cross-opt", "64-opt", "64-nopt", "64-opt-vg", "64-debug-opt", - "all-opt", "snap3", "dist"]; + "all-opt", "snap3", "dist", "sgx"]; dirs.iter().enumerate().find(|&(_, dir)| { - cwd.to_str().unwrap().contains(dir) + cwd.contains(dir) }).map(|p| p.0).unwrap_or(0) as u16 * 1000 + 19600 } diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index edc9d665444..c430e103951 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -180,6 +180,37 @@ impl UdpSocket { } } + /// Returns the socket address of the remote peer this socket was connected to. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(udp_peer_addr)] + /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.connect("192.168.0.1:41203").expect("couldn't connect to address"); + /// assert_eq!(socket.peer_addr().unwrap(), + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 41203))); + /// ``` + /// + /// If the socket isn't connected, it will return a [`NotConnected`] error. + /// + /// [`NotConnected`]: ../../std/io/enum.ErrorKind.html#variant.NotConnected + /// + /// ```no_run + /// #![feature(udp_peer_addr)] + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// assert_eq!(socket.peer_addr().unwrap_err().kind(), + /// ::std::io::ErrorKind::NotConnected); + /// ``` + #[unstable(feature = "udp_peer_addr", issue = "59127")] + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + self.0.peer_addr() + } + /// Returns the socket address that this socket was created from. /// /// # Examples @@ -391,7 +422,7 @@ impl UdpSocket { /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket. /// /// If enabled, multicast packets will be looped back to the local socket. - /// Note that this may not have any affect on IPv6 sockets. + /// Note that this may not have any effect on IPv6 sockets. /// /// # Examples /// @@ -433,7 +464,7 @@ impl UdpSocket { /// this socket. The default value is 1 which means that multicast packets /// don't leave the local network unless explicitly requested. /// - /// Note that this may not have any affect on IPv6 sockets. + /// Note that this may not have any effect on IPv6 sockets. /// /// # Examples /// @@ -801,12 +832,12 @@ impl IntoInner<net_imp::UdpSocket> for UdpSocket { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } -#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))] +#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))] mod tests { use crate::io::ErrorKind; use crate::net::*; @@ -865,7 +896,7 @@ mod tests { } #[test] - fn socket_name_ip4() { + fn socket_name() { each_ip(&mut |addr, _| { let server = t!(UdpSocket::bind(&addr)); assert_eq!(addr, t!(server.local_addr())); @@ -873,6 +904,16 @@ mod tests { } #[test] + fn socket_peer() { + each_ip(&mut |addr1, addr2| { + let server = t!(UdpSocket::bind(&addr1)); + assert_eq!(server.peer_addr().unwrap_err().kind(), ErrorKind::NotConnected); + t!(server.connect(&addr2)); + assert_eq!(addr2, t!(server.peer_addr())); + }) + } + + #[test] fn udp_clone_smoke() { each_ip(&mut |addr1, addr2| { let sock1 = t!(UdpSocket::bind(&addr1)); @@ -983,9 +1024,9 @@ mod tests { assert_eq!(format!("{:?}", udpsock), compare); } - // FIXME: re-enabled bitrig/openbsd/netbsd tests once their socket timeout code + // FIXME: re-enabled openbsd/netbsd tests once their socket timeout code // no longer has rounding errors. - #[cfg_attr(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd"), ignore)] + #[cfg_attr(any(target_os = "netbsd", target_os = "openbsd"), ignore)] #[test] fn timeouts() { let addr = next_test_ip4(); diff --git a/src/libstd/num.rs b/src/libstd/num.rs index 828d5720eec..2a2ca0b5237 100644 --- a/src/libstd/num.rs +++ b/src/libstd/num.rs @@ -13,6 +13,14 @@ pub use core::num::Wrapping; #[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; +#[stable(feature = "signed_nonzero", since = "1.34.0")] +pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize}; + +#[unstable(feature = "int_error_matching", + reason = "it can be useful to match errors when making error messages \ + for integer parsing", + issue = "22639")] +pub use core::num::IntErrorKind; #[cfg(test)] use crate::fmt; #[cfg(test)] use crate::ops::{Add, Sub, Mul, Div, Rem}; diff --git a/src/libstd/os/android/fs.rs b/src/libstd/os/android/fs.rs index 9b24f86204b..90fdee567ae 100644 --- a/src/libstd/os/android/fs.rs +++ b/src/libstd/os/android/fs.rs @@ -116,4 +116,3 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_blocks as u64 } } - diff --git a/src/libstd/os/android/raw.rs b/src/libstd/os/android/raw.rs index acf5ca1e429..946a77cbfd3 100644 --- a/src/libstd/os/android/raw.rs +++ b/src/libstd/os/android/raw.rs @@ -217,4 +217,3 @@ mod arch { __unused: [c_long; 3], } } - diff --git a/src/libstd/os/dragonfly/fs.rs b/src/libstd/os/dragonfly/fs.rs index ba38660224f..ba3d8d7867d 100644 --- a/src/libstd/os/dragonfly/fs.rs +++ b/src/libstd/os/dragonfly/fs.rs @@ -131,4 +131,3 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_lspare as u32 } } - diff --git a/src/libstd/os/fortanix_sgx/mod.rs b/src/libstd/os/fortanix_sgx/mod.rs index bca22e717d7..4e30b1edd15 100644 --- a/src/libstd/os/fortanix_sgx/mod.rs +++ b/src/libstd/os/fortanix_sgx/mod.rs @@ -43,3 +43,8 @@ pub mod mem { } pub use crate::sys::ext::{io, arch, ffi}; + +/// Functions for querying thread-related information. +pub mod thread { + pub use crate::sys::abi::thread::current; +} diff --git a/src/libstd/os/freebsd/fs.rs b/src/libstd/os/freebsd/fs.rs index 4cc3a4b91fb..cfe8d575c67 100644 --- a/src/libstd/os/freebsd/fs.rs +++ b/src/libstd/os/freebsd/fs.rs @@ -141,4 +141,3 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_lspare as u32 } } - diff --git a/src/libstd/os/ios/fs.rs b/src/libstd/os/ios/fs.rs index 7b625f5e3fe..9bdfa8e690b 100644 --- a/src/libstd/os/ios/fs.rs +++ b/src/libstd/os/ios/fs.rs @@ -141,4 +141,3 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_lspare as u32 } } - diff --git a/src/libstd/os/linux/fs.rs b/src/libstd/os/linux/fs.rs index ec5e9837076..78321ac3185 100644 --- a/src/libstd/os/linux/fs.rs +++ b/src/libstd/os/linux/fs.rs @@ -34,9 +34,10 @@ pub trait MetadataExt { /// } /// ``` #[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")] + #[rustc_deprecated( + since = "1.8.0", + reason = "other methods of this trait are now prefered" + )] #[allow(deprecated)] fn as_raw_stat(&self) -> &raw::stat; diff --git a/src/libstd/os/linux/raw.rs b/src/libstd/os/linux/raw.rs index 77eeacb4b47..21e1cf8a22b 100644 --- a/src/libstd/os/linux/raw.rs +++ b/src/libstd/os/linux/raw.rs @@ -147,6 +147,62 @@ mod arch { } } +#[cfg(target_arch = "hexagon")] +mod arch { + use crate::os::raw::{c_long, c_int, c_longlong, culonglong}; + + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = c_longlong; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = c_long; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = c_ulonglong; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = c_uint; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = c_longlong; + #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = c_long; + + #[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: ::c_ulonglong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: ::c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: ::c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: ::c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: ::c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: ::c_ulonglong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad1: ::c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: ::c_longlong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: ::blksize_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: ::c_int, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: ::blkcnt_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 __pad3: [::c_int;2], + } +} + #[cfg(any(target_arch = "mips64", target_arch = "s390x", target_arch = "sparc64"))] diff --git a/src/libstd/os/macos/fs.rs b/src/libstd/os/macos/fs.rs index 1bd66ad4c76..bf951ee18c9 100644 --- a/src/libstd/os/macos/fs.rs +++ b/src/libstd/os/macos/fs.rs @@ -147,4 +147,3 @@ impl MetadataExt for Metadata { [qspare[0] as u64, qspare[1] as u64] } } - diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs index 5cce3df71d6..fcd81f0a1b2 100644 --- a/src/libstd/os/mod.rs +++ b/src/libstd/os/mod.rs @@ -3,7 +3,7 @@ #![stable(feature = "os", since = "1.0.0")] #![allow(missing_docs, nonstandard_style, missing_debug_implementations)] -cfg_if! { +cfg_if::cfg_if! { if #[cfg(rustdoc)] { // When documenting libstd we want to show unix/windows/linux modules as @@ -24,7 +24,7 @@ cfg_if! { // If we're not documenting libstd then we just expose the main modules // as we otherwise would. - #[cfg(any(target_os = "redox", unix))] + #[cfg(any(target_os = "redox", unix, target_os = "vxworks"))] #[stable(feature = "rust1", since = "1.0.0")] pub use crate::sys::ext as unix; @@ -39,7 +39,6 @@ cfg_if! { } #[cfg(target_os = "android")] pub mod android; -#[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; @@ -51,6 +50,9 @@ cfg_if! { #[cfg(target_os = "emscripten")] pub mod emscripten; #[cfg(target_os = "fuchsia")] pub mod fuchsia; #[cfg(target_os = "hermit")] pub mod hermit; +#[cfg(target_os = "redox")] pub mod redox; +#[cfg(target_os = "wasi")] pub mod wasi; +#[cfg(target_os = "vxworks")] pub mod vxworks; #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] pub mod fortanix_sgx; pub mod raw; diff --git a/src/libstd/os/netbsd/fs.rs b/src/libstd/os/netbsd/fs.rs index 6dffb70b5dc..dedfc6390ce 100644 --- a/src/libstd/os/netbsd/fs.rs +++ b/src/libstd/os/netbsd/fs.rs @@ -136,4 +136,3 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_flags as u32 } } - diff --git a/src/libstd/os/openbsd/fs.rs b/src/libstd/os/openbsd/fs.rs index 73f9757f3b7..1c019159be0 100644 --- a/src/libstd/os/openbsd/fs.rs +++ b/src/libstd/os/openbsd/fs.rs @@ -136,4 +136,3 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_flags as u32 } } - diff --git a/src/libstd/os/raw/mod.rs b/src/libstd/os/raw/mod.rs index e9043b4b40d..0761c50f4b2 100644 --- a/src/libstd/os/raw/mod.rs +++ b/src/libstd/os/raw/mod.rs @@ -8,9 +8,11 @@ #![stable(feature = "raw_os", since = "1.1.0")] -#[doc(include = "os/raw/char.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/char.md"))] +#[cfg_attr(not(bootstrap), doc(include = "char.md"))] #[cfg(any(all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm", + target_arch = "hexagon", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "s390x")), @@ -25,11 +27,17 @@ target_arch = "arm", target_arch = "powerpc")), all(target_os = "openbsd", target_arch = "aarch64"), + all(target_os = "vxworks", any(target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc64", + target_arch = "powerpc")), all(target_os = "fuchsia", target_arch = "aarch64")))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = u8; -#[doc(include = "os/raw/char.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/char.md"))] +#[cfg_attr(not(bootstrap), doc(include = "char.md"))] #[cfg(not(any(all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm", + target_arch = "hexagon", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "s390x")), @@ -44,39 +52,57 @@ target_arch = "arm", target_arch = "powerpc")), all(target_os = "openbsd", target_arch = "aarch64"), + all(target_os = "vxworks", any(target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc64", + target_arch = "powerpc")), all(target_os = "fuchsia", target_arch = "aarch64"))))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = i8; -#[doc(include = "os/raw/schar.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/schar.md"))] +#[cfg_attr(not(bootstrap), doc(include = "schar.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_schar = i8; -#[doc(include = "os/raw/uchar.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/uchar.md"))] +#[cfg_attr(not(bootstrap), doc(include = "uchar.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_uchar = u8; -#[doc(include = "os/raw/short.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/short.md"))] +#[cfg_attr(not(bootstrap), doc(include = "short.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_short = i16; -#[doc(include = "os/raw/ushort.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/ushort.md"))] +#[cfg_attr(not(bootstrap), doc(include = "ushort.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_ushort = u16; -#[doc(include = "os/raw/int.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/int.md"))] +#[cfg_attr(not(bootstrap), doc(include = "int.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_int = i32; -#[doc(include = "os/raw/uint.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/uint.md"))] +#[cfg_attr(not(bootstrap), doc(include = "uint.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_uint = u32; -#[doc(include = "os/raw/long.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/long.md"))] +#[cfg_attr(not(bootstrap), doc(include = "long.md"))] #[cfg(any(target_pointer_width = "32", windows))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_long = i32; -#[doc(include = "os/raw/ulong.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/ulong.md"))] +#[cfg_attr(not(bootstrap), doc(include = "ulong.md"))] #[cfg(any(target_pointer_width = "32", windows))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_ulong = u32; -#[doc(include = "os/raw/long.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/long.md"))] +#[cfg_attr(not(bootstrap), doc(include = "long.md"))] #[cfg(all(target_pointer_width = "64", not(windows)))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_long = i64; -#[doc(include = "os/raw/ulong.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/ulong.md"))] +#[cfg_attr(not(bootstrap), doc(include = "ulong.md"))] #[cfg(all(target_pointer_width = "64", not(windows)))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_ulong = u64; -#[doc(include = "os/raw/longlong.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/longlong.md"))] +#[cfg_attr(not(bootstrap), doc(include = "longlong.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_longlong = i64; -#[doc(include = "os/raw/ulonglong.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/ulonglong.md"))] +#[cfg_attr(not(bootstrap), doc(include = "ulonglong.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_ulonglong = u64; -#[doc(include = "os/raw/float.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/float.md"))] +#[cfg_attr(not(bootstrap), doc(include = "float.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_float = f32; -#[doc(include = "os/raw/double.md")] +#[cfg_attr(bootstrap, doc(include = "os/raw/double.md"))] +#[cfg_attr(not(bootstrap), doc(include = "double.md"))] #[stable(feature = "raw_os", since = "1.1.0")] pub type c_double = f64; #[stable(feature = "raw_os", since = "1.1.0")] diff --git a/src/libstd/os/redox/fs.rs b/src/libstd/os/redox/fs.rs new file mode 100644 index 00000000000..80a12907619 --- /dev/null +++ b/src/libstd/os/redox/fs.rs @@ -0,0 +1,383 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::redox::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html +#[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. + /// + /// [`stat`]: ../../../../std/os/redox/raw/struct.stat.html + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let stat = meta.as_raw_stat(); + /// Ok(()) + /// } + /// ``` + #[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; + + /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_dev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ino()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + /// Returns the file type and mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mode()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + /// Returns the number of hard links to file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_nlink()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + /// Returns the user ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_uid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + /// Returns the group ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_gid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + /// Returns the device ID that this file represents. Only relevant for special file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_rdev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes. + /// + /// The size of a symbolic link is the length of the pathname it contains, + /// without a terminating null byte. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_size()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + /// Returns the last access time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + /// Returns the last access time of the file, in nanoseconds since [`st_atime`]. + /// + /// [`st_atime`]: #tymethod.st_atime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + /// Returns the last modification time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`]. + /// + /// [`st_mtime`]: #tymethod.st_mtime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + /// Returns the last status change time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`]. + /// + /// [`st_ctime`]: #tymethod.st_ctime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + /// Returns the "preferred" blocksize for efficient filesystem I/O. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blksize()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + /// Returns the number of blocks allocated to the file, 512-byte units. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::redox::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blocks()); + /// Ok(()) + /// } + /// ``` + #[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_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/bitrig/mod.rs b/src/libstd/os/redox/mod.rs index 0bc105bb2b4..c60da5926da 100644 --- a/src/libstd/os/bitrig/mod.rs +++ b/src/libstd/os/redox/mod.rs @@ -1,4 +1,4 @@ -//! Bitrig-specific definitions +//! Redox-specific definitions #![stable(feature = "raw_ext", since = "1.1.0")] diff --git a/src/libstd/os/bitrig/raw.rs b/src/libstd/os/redox/raw.rs index c966d5a8e5b..23d8ff7694b 100644 --- a/src/libstd/os/bitrig/raw.rs +++ b/src/libstd/os/redox/raw.rs @@ -1,4 +1,4 @@ -//! Bitrig-specific raw type definitions +//! Redox-specific raw type definitions #![stable(feature = "raw_ext", since = "1.1.0")] #![rustc_deprecated(since = "1.8.0", @@ -7,65 +7,61 @@ crates.io should be used instead for the correct \ definitions")] #![allow(deprecated)] +#![allow(missing_debug_implementations)] -use crate::os::raw::c_long; -use crate::os::unix::raw::{uid_t, gid_t}; +use crate::os::raw::{c_char, c_int, c_long, c_ulong, c_void}; -#[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 dev_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type fflags_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; -#[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 = 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; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = c_long; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type gid_t = c_int; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = c_int; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type uid_t = c_int; #[stable(feature = "pthread_t", since = "1.8.0")] -pub type pthread_t = usize; +pub type pthread_t = *mut c_void; + +#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = c_ulong; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = c_ulong; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = c_ulong; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = c_ulong; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = c_long; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = c_long; #[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_mode: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u32, + pub st_dev: dev_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, + pub st_ino: ino_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, + pub st_nlink: nlink_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: i32, + pub st_mode: mode_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i64, + pub st_uid: uid_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: i64, + pub st_gid: gid_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: u64, + pub st_rdev: dev_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: i64, + pub st_size: off_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i64, + pub st_blksize: blksize_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: i64, + pub st_blocks: blkcnt_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, + pub st_atime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, + pub st_atime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: u32, + pub st_mtime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_flags: u32, + pub st_mtime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gen: u32, + pub st_ctime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_birthtime: i64, + pub st_ctime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_birthtime_nsec: i64, + pub _pad: [c_char; 24], } diff --git a/src/libstd/os/bitrig/fs.rs b/src/libstd/os/vxworks/fs.rs index 849d4aa67f2..57ab4fb943e 100644 --- a/src/libstd/os/bitrig/fs.rs +++ b/src/libstd/os/vxworks/fs.rs @@ -3,27 +3,10 @@ use crate::fs::Metadata; use crate::sys_common::AsInner; -#[allow(deprecated)] -use crate::os::bitrig::raw; - -/// OS-specific extensions to [`fs::Metadata`]. /// /// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html #[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")] @@ -43,38 +26,19 @@ pub trait MetadataExt { #[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_birthtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime_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_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; + fn st_attrib(&self) -> u8; } #[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 } @@ -102,38 +66,19 @@ impl MetadataExt for Metadata { 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_birthtime(&self) -> i64 { - self.as_inner().as_inner().st_birthtime as i64 - } - fn st_birthtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_birthtime_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 } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 + fn st_attrib(&self) -> u8 { + self.as_inner().as_inner().st_attrib as u8 } } - diff --git a/src/libstd/os/vxworks/mod.rs b/src/libstd/os/vxworks/mod.rs new file mode 100644 index 00000000000..2255a103d35 --- /dev/null +++ b/src/libstd/os/vxworks/mod.rs @@ -0,0 +1,6 @@ +//! VxWorks-specific definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod raw; +pub mod fs; diff --git a/src/libstd/os/vxworks/raw.rs b/src/libstd/os/vxworks/raw.rs new file mode 100644 index 00000000000..ae0560a5a92 --- /dev/null +++ b/src/libstd/os/vxworks/raw.rs @@ -0,0 +1,7 @@ +//! VxWorks-specific raw type definitions +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::os::raw::{c_ulong}; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_ulong; diff --git a/src/libstd/os/wasi.rs b/src/libstd/os/wasi.rs new file mode 100644 index 00000000000..d25b8d39ed6 --- /dev/null +++ b/src/libstd/os/wasi.rs @@ -0,0 +1,6 @@ +//! WASI-specific definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys::ext::*; diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 6a16414c141..1d4fd98dd75 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -4,6 +4,7 @@ use crate::any::Any; use crate::cell::UnsafeCell; +use crate::collections; use crate::fmt; use crate::future::Future; use crate::pin::Pin; @@ -12,7 +13,7 @@ use crate::panicking; use crate::ptr::{Unique, NonNull}; use crate::rc::Rc; use crate::sync::{Arc, Mutex, RwLock, atomic}; -use crate::task::{Waker, Poll}; +use crate::task::{Context, Poll}; use crate::thread::Result; #[stable(feature = "panic_hooks", since = "1.10.0")] @@ -285,6 +286,11 @@ impl RefUnwindSafe for atomic::AtomicBool {} #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] impl<T> RefUnwindSafe for atomic::AtomicPtr<T> {} +// https://github.com/rust-lang/rust/issues/62301 +#[stable(feature = "hashbrown", since = "1.36.0")] +impl<K, V, S> UnwindSafe for collections::HashMap<K, V, S> + where K: UnwindSafe, V: UnwindSafe, S: UnwindSafe {} + #[stable(feature = "catch_unwind", since = "1.9.0")] impl<T> Deref for AssertUnwindSafe<T> { type Target = T; @@ -312,20 +318,20 @@ impl<R, F: FnOnce() -> R> FnOnce<()> for AssertUnwindSafe<F> { #[stable(feature = "std_debug", since = "1.16.0")] impl<T: fmt::Debug> fmt::Debug for AssertUnwindSafe<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("AssertUnwindSafe") .field(&self.0) .finish() } } -#[unstable(feature = "futures_api", issue = "50547")] +#[stable(feature = "futures_api", since = "1.36.0")] impl<F: Future> Future for AssertUnwindSafe<F> { type Output = F::Output; - fn poll(self: Pin<&mut Self>, waker: &Waker) -> Poll<Self::Output> { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { let pinned_field = unsafe { Pin::map_unchecked_mut(self, |x| &mut x.0) }; - F::poll(pinned_field, waker) + F::poll(pinned_field, cx) } } diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index eae885602d3..952fd9ebfdf 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -51,7 +51,7 @@ extern { #[derive(Copy, Clone)] enum Hook { Default, - Custom(*mut (dyn Fn(&PanicInfo) + 'static + Sync + Send)), + Custom(*mut (dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send)), } static HOOK_LOCK: RWLock = RWLock::new(); @@ -91,7 +91,7 @@ static mut HOOK: Hook = Hook::Default; /// panic!("Normal panic"); /// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] -pub fn set_hook(hook: Box<dyn Fn(&PanicInfo) + 'static + Sync + Send>) { +pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); } @@ -103,7 +103,9 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo) + 'static + Sync + Send>) { HOOK_LOCK.write_unlock(); if let Hook::Custom(ptr) = old_hook { - Box::from_raw(ptr); + #[allow(unused_must_use)] { + Box::from_raw(ptr); + } } } } @@ -136,7 +138,7 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo) + 'static + Sync + Send>) { /// panic!("Normal panic"); /// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] -pub fn take_hook() -> Box<dyn Fn(&PanicInfo) + 'static + Sync + Send> { +pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); } @@ -154,7 +156,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo) + 'static + Sync + Send> { } } -fn default_hook(info: &PanicInfo) { +fn default_hook(info: &PanicInfo<'_>) { #[cfg(feature = "backtrace")] use crate::sys_common::backtrace; @@ -171,7 +173,8 @@ fn default_hook(info: &PanicInfo) { } }; - let location = info.location().unwrap(); // The current implementation always returns Some + // The current implementation always returns `Some`. + let location = info.location().unwrap(); let msg = match info.payload().downcast_ref::<&'static str>() { Some(s) => *s, @@ -196,7 +199,7 @@ fn default_hook(info: &PanicInfo) { if let Some(format) = log_backtrace { let _ = backtrace::print(err, format); } else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) { - let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` \ + let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \ environment variable to display a backtrace."); } } @@ -304,7 +307,7 @@ pub fn panicking() -> bool { #[cfg(not(test))] #[panic_handler] #[unwind(allowed)] -pub fn rust_begin_panic(info: &PanicInfo) -> ! { +pub fn rust_begin_panic(info: &PanicInfo<'_>) -> ! { continue_panic_fmt(&info) } @@ -322,7 +325,7 @@ pub fn rust_begin_panic(info: &PanicInfo) -> ! { // otherwise avoid inlining because of it is cold path. #[cfg_attr(not(feature="panic_immediate_abort"),inline(never))] #[cfg_attr( feature="panic_immediate_abort" ,inline)] -pub fn begin_panic_fmt(msg: &fmt::Arguments, +pub fn begin_panic_fmt(msg: &fmt::Arguments<'_>, file_line_col: &(&'static str, u32, u32)) -> ! { if cfg!(feature = "panic_immediate_abort") { unsafe { intrinsics::abort() } @@ -336,7 +339,7 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments, continue_panic_fmt(&info) } -fn continue_panic_fmt(info: &PanicInfo) -> ! { +fn continue_panic_fmt(info: &PanicInfo<'_>) -> ! { struct PanicPayload<'a> { inner: &'a fmt::Arguments<'a>, string: Option<String>, @@ -361,7 +364,7 @@ fn continue_panic_fmt(info: &PanicInfo) -> ! { unsafe impl<'a> BoxMeUp for PanicPayload<'a> { fn box_me_up(&mut self) -> *mut (dyn Any + Send) { - let contents = mem::replace(self.fill(), String::new()); + let contents = mem::take(self.fill()); Box::into_raw(Box::new(contents)) } @@ -441,7 +444,7 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3 /// panics, panic hooks, and finally dispatching to the panic runtime to either /// abort or unwind. fn rust_panic_with_hook(payload: &mut dyn BoxMeUp, - message: Option<&fmt::Arguments>, + message: Option<&fmt::Arguments<'_>>, file_line_col: &(&str, u32, u32)) -> ! { let (file, line, col) = *file_line_col; diff --git a/src/libstd/path.rs b/src/libstd/path.rs index ea3fcd8ce28..87c071b512a 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + //! Cross-platform path manipulation. //! //! This module provides two types, [`PathBuf`] and [`Path`][`Path`] (akin to [`String`] @@ -315,7 +317,7 @@ unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { // Detect scheme on Redox fn has_redox_scheme(s: &[u8]) -> bool { - cfg!(target_os = "redox") && s.split(|b| *b == b'/').next().unwrap_or(b"").contains(&b':') + cfg!(target_os = "redox") && s.contains(&b':') } //////////////////////////////////////////////////////////////////////////////// @@ -323,7 +325,7 @@ fn has_redox_scheme(s: &[u8]) -> bool { //////////////////////////////////////////////////////////////////////////////// /// Says whether the first byte after the prefix is a separator. -fn has_physical_root(s: &[u8], prefix: Option<Prefix>) -> bool { +fn has_physical_root(s: &[u8], prefix: Option<Prefix<'_>>) -> bool { let path = if let Some(p) = prefix { &s[p.len()..] } else { @@ -630,11 +632,11 @@ pub struct Iter<'a> { #[stable(feature = "path_components_debug", since = "1.13.0")] impl fmt::Debug for Components<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct DebugHelper<'a>(&'a Path); impl fmt::Debug for DebugHelper<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list() .entries(self.0.components()) .finish() @@ -828,11 +830,11 @@ impl AsRef<OsStr> for Components<'_> { #[stable(feature = "path_iter_debug", since = "1.13.0")] impl fmt::Debug for Iter<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct DebugHelper<'a>(&'a Path); impl fmt::Debug for DebugHelper<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list() .entries(self.0.iter()) .finish() @@ -1551,15 +1553,13 @@ impl<P: AsRef<Path>> iter::FromIterator<P> for PathBuf { #[stable(feature = "rust1", since = "1.0.0")] impl<P: AsRef<Path>> iter::Extend<P> for PathBuf { fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) { - for p in iter { - self.push(p.as_ref()) - } + iter.into_iter().for_each(move |p| self.push(p.as_ref())); } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for PathBuf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, formatter) } } @@ -1819,6 +1819,8 @@ impl Path { /// Yields a [`&str`] slice if the `Path` is valid unicode. /// /// This conversion may entail doing a check for UTF-8 validity. + /// Note that validation is performed because non-UTF-8 strings are + /// perfectly valid for some OS. /// /// [`&str`]: ../primitive.str.html /// @@ -1857,7 +1859,7 @@ impl Path { /// Had `path` contained invalid unicode, the `to_string_lossy` call might /// have returned `"fo�.txt"`. #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_string_lossy(&self) -> Cow<str> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { self.inner.to_string_lossy() } @@ -1926,7 +1928,7 @@ impl Path { !self.is_absolute() } - fn prefix(&self) -> Option<Prefix> { + fn prefix(&self) -> Option<Prefix<'_>> { self.components().prefix } @@ -2007,7 +2009,7 @@ impl Path { /// [`None`]: ../../std/option/enum.Option.html#variant.None /// [`parent`]: struct.Path.html#method.parent #[stable(feature = "path_ancestors", since = "1.28.0")] - pub fn ancestors(&self) -> Ancestors { + pub fn ancestors(&self) -> Ancestors<'_> { Ancestors { next: Some(&self), } @@ -2305,7 +2307,7 @@ impl Path { /// [`Component`]: enum.Component.html /// [`CurDir`]: enum.Component.html#variant.CurDir #[stable(feature = "rust1", since = "1.0.0")] - pub fn components(&self) -> Components { + pub fn components(&self) -> Components<'_> { let prefix = parse_prefix(self.as_os_str()); Components { path: self.as_u8_slice(), @@ -2339,7 +2341,7 @@ impl Path { /// assert_eq!(it.next(), None) /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter<'_> { Iter { inner: self.components() } } @@ -2358,7 +2360,7 @@ impl Path { /// println!("{}", path.display()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn display(&self) -> Display { + pub fn display(&self) -> Display<'_> { Display { path: self } } @@ -2578,7 +2580,7 @@ impl AsRef<OsStr> for Path { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Path { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.inner, formatter) } } @@ -2610,14 +2612,14 @@ pub struct Display<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Display<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.path, f) } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Display<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.path.inner.display(f) } } @@ -2805,7 +2807,7 @@ impl_cmp_os_str!(Cow<'a, Path>, OsString); #[stable(since = "1.7.0", feature = "strip_prefix")] impl fmt::Display for StripPrefixError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.description().fmt(f) } } @@ -2915,7 +2917,7 @@ mod tests { { let path: &Path = &pathbuf; - let borrowed_cow_path: Cow<Path> = path.into(); + let borrowed_cow_path: Cow<'_, Path> = path.into(); assert_eq!(static_cow_path, borrowed_cow_path); } @@ -3801,7 +3803,7 @@ mod tests { }); ); - if cfg!(unix) { + if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) { tp!("", "foo", "foo"); tp!("foo", "bar", "foo/bar"); tp!("foo/", "bar", "foo/bar"); @@ -3960,7 +3962,7 @@ mod tests { tfn!("foo", "bar", "bar"); tfn!("foo", "", ""); tfn!("", "foo", "foo"); - if cfg!(unix) { + if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) { tfn!(".", "foo", "./foo"); tfn!("foo/", "bar", "bar"); tfn!("foo/.", "bar", "bar"); @@ -4013,8 +4015,8 @@ mod tests { let mut owned: PathBuf = PathBuf::new(); owned.push("foo"); owned.push("bar"); - let borrowed_cow: Cow<Path> = borrowed.into(); - let owned_cow: Cow<Path> = owned.clone().into(); + let borrowed_cow: Cow<'_, Path> = borrowed.into(); + let owned_cow: Cow<'_, Path> = owned.clone().into(); macro_rules! t { ($($current:expr),+) => { diff --git a/src/libstd/prelude/mod.rs b/src/libstd/prelude/mod.rs index 551e982a3c6..3085c3d8296 100644 --- a/src/libstd/prelude/mod.rs +++ b/src/libstd/prelude/mod.rs @@ -71,9 +71,6 @@ //! * [`std::result`]::[`Result`]::{`self`, `Ok`, `Err`}. A type for functions //! that may succeed or fail. Like [`Option`], its variants are exported as //! well. -//! * [`std::slice`]::[`SliceConcatExt`], a trait that exists for technical -//! reasons, but shouldn't have to exist. It provides a few useful methods on -//! slices. //! * [`std::string`]::{[`String`], [`ToString`]}, heap allocated strings. //! * [`std::vec`]::[`Vec`](../vec/struct.Vec.html), a growable, heap-allocated //! vector. diff --git a/src/libstd/prelude/v1.rs b/src/libstd/prelude/v1.rs index ce1e8e3319c..1c61f21f7df 100644 --- a/src/libstd/prelude/v1.rs +++ b/src/libstd/prelude/v1.rs @@ -7,9 +7,13 @@ #![stable(feature = "rust1", since = "1.0.0")] // Re-exported core operators +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] -pub use crate::marker::{Copy, Send, Sized, Sync, Unpin}; +pub use crate::marker::Copy; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::marker::{Send, Sized, Sync, Unpin}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; @@ -20,15 +24,18 @@ pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; pub use crate::mem::drop; // Re-exported types and traits +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::clone::Clone; +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::cmp::{PartialEq, PartialOrd, Eq, Ord}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::convert::{AsRef, AsMut, Into, From}; +#[cfg(bootstrap)] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::default::Default; @@ -45,6 +52,58 @@ pub use crate::option::Option::{self, Some, None}; #[doc(no_inline)] pub use crate::result::Result::{self, Ok, Err}; +// Re-exported built-in macros +#[cfg(not(bootstrap))] +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use core::prelude::v1::{ + __rust_unstable_column, + asm, + assert, + cfg, + column, + compile_error, + concat, + concat_idents, + env, + file, + format_args, + format_args_nl, + global_asm, + include, + include_bytes, + include_str, + line, + log_syntax, + module_path, + option_env, + stringify, + trace_macros, +}; + +// FIXME: Attribute and derive macros are not documented because for them rustdoc generates +// dead links which fail link checker testing. +#[cfg(not(bootstrap))] +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] +#[doc(hidden)] +pub use core::prelude::v1::{ + Clone, + Copy, + Debug, + Default, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + RustcDecodable, + RustcEncodable, + bench, + global_allocator, + test, + test_case, +}; // The file so far is equivalent to src/libcore/prelude/v1.rs, // and below to src/liballoc/prelude.rs. @@ -60,9 +119,6 @@ pub use crate::boxed::Box; pub use crate::borrow::ToOwned; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] -pub use crate::slice::SliceConcatExt; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] pub use crate::string::{String, ToString}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index 6bb7f28efeb..d9a3da66a67 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -120,7 +120,7 @@ mod prim_bool { } /// When implementing this trait for [`String`] we need to pick a type for [`Err`]. And since /// converting a string into a string will never result in an error, the appropriate type is `!`. /// (Currently the type actually used is an enum with no variants, though this is only because `!` -/// was added to Rust at a later date and it may change in the future). With an [`Err`] type of +/// was added to Rust at a later date and it may change in the future.) With an [`Err`] type of /// `!`, if we have to call [`String::from_str`] for some reason the result will be a /// [`Result<String, !>`] which we can unpack like this: /// @@ -204,10 +204,10 @@ mod prim_bool { } /// #![feature(never_type)] /// # use std::fmt; /// # trait Debug { -/// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result; +/// # fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result; /// # } /// impl Debug for ! { -/// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +/// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { /// *self /// } /// } @@ -279,7 +279,7 @@ mod prim_never { } /// /// As always, remember that a human intuition for 'character' may not map to /// Unicode's definitions. For example, despite looking similar, the 'é' -/// character is one Unicode code point while 'é' is two Unicode code points: +/// character is one Unicode code point while 'é' is two Unicode code points: /// /// ``` /// let mut chars = "é".chars(); @@ -362,8 +362,13 @@ mod prim_unit { } /// /// *[See also the `std::ptr` module](ptr/index.html).* /// -/// Working with raw pointers in Rust is uncommon, -/// typically limited to a few patterns. +/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. +/// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is +/// dereferenced (using the `*` operator), it must be non-null and aligned. +/// +/// Storing through a raw pointer using `*ptr = data` calls `drop` on the old value, so +/// [`write`] must be used if the type has drop glue and memory is not already +/// initialized - otherwise `drop` would be called on the uninitialized memory. /// /// Use the [`null`] and [`null_mut`] functions to create null pointers, and the /// [`is_null`] method of the `*const T` and `*mut T` types to check for null. @@ -442,6 +447,7 @@ mod prim_unit { } /// [`offset`]: ../std/primitive.pointer.html#method.offset /// [`into_raw`]: ../std/boxed/struct.Box.html#method.into_raw /// [`drop`]: ../std/mem/fn.drop.html +/// [`write`]: ../std/ptr/fn.write.html #[stable(feature = "rust1", since = "1.0.0")] mod prim_pointer { } @@ -482,8 +488,8 @@ mod prim_pointer { } /// an array. Indeed, this provides most of the API for working with arrays. /// Slices have a dynamic size and do not coerce to arrays. /// -/// There is no way to move elements out of an array. See [`mem::replace`][replace] -/// for an alternative. +/// You can move elements out of an array with a slice pattern. If you want +/// one element, see [`mem::replace`][replace]. /// /// # Examples /// @@ -525,6 +531,16 @@ mod prim_pointer { } /// for x in &array { } /// ``` /// +/// You can use a slice pattern to move elements out of an array: +/// +/// ``` +/// fn move_away(_: String) { /* Do interesting things. */ } +/// +/// let [john, roa] = ["John".to_string(), "Roa".to_string()]; +/// move_away(john); +/// move_away(roa); +/// ``` +/// /// [slice]: primitive.slice.html /// [copy]: marker/trait.Copy.html /// [clone]: clone/trait.Clone.html @@ -683,6 +699,10 @@ mod prim_str { } /// assert_eq!(tuple.2, 'c'); /// ``` /// +/// The sequential nature of the tuple applies to its implementations of various +/// traits. For example, in `PartialOrd` and `Ord`, the elements are compared +/// sequentially until the first non-equal set is found. +/// /// For more about tuples, see [the book](../book/ch03-02-data-types.html#the-tuple-type). /// /// # Trait implementations @@ -877,9 +897,13 @@ mod prim_usize { } /// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut` /// operators on a value, or by using a `ref` or `ref mut` pattern. /// -/// For those familiar with pointers, a reference is just a pointer that is assumed to not be null. -/// In fact, `Option<&T>` has the same memory representation as a nullable pointer, and can be -/// passed across FFI boundaries as such. +/// For those familiar with pointers, a reference is just a pointer that is assumed to be +/// aligned, not null, and pointing to memory containing a valid value of `T` - for example, +/// `&bool` can only point to an allocation containing the integer values `1` (`true`) or `0` +/// (`false`), but creating a `&bool` that points to an allocation containing +/// the value `3` causes undefined behaviour. +/// In fact, `Option<&T>` has the same memory representation as a +/// nullable but aligned pointer, and can be passed across FFI boundaries as such. /// /// In most cases, references can be used much like the original value. Field access, method /// calling, and indexing work the same (save for mutability rules, of course). In addition, the @@ -1022,6 +1046,11 @@ mod prim_ref { } /// [`FnMut`]: ops/trait.FnMut.html /// [`FnOnce`]: ops/trait.FnOnce.html /// +/// Function pointers are pointers that point to *code*, not data. They can be called +/// just like functions. Like references, function pointers are, among other things, assumed to +/// not be null, so if you want to pass a function pointer over FFI and be able to accommodate null +/// pointers, make your type `Option<fn()>` with your required signature. +/// /// Plain function pointers are obtained by casting either plain functions, or closures that don't /// capture an environment: /// @@ -1077,10 +1106,6 @@ mod prim_ref { } /// /// These markers can be combined, so `unsafe extern "stdcall" fn()` is a valid type. /// -/// Like references in rust, function pointers are assumed to not be null, so if you want to pass a -/// function pointer over FFI and be able to accommodate null pointers, make your type -/// `Option<fn()>` with your required signature. -/// /// Function pointers implement the following traits: /// /// * [`Clone`] diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 56840009344..000f80f99e7 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -8,7 +8,7 @@ //! //! The [`Command`] struct is used to configure and spawn processes: //! -//! ``` +//! ```no_run //! use std::process::Command; //! //! let output = Command::new("echo") @@ -111,7 +111,7 @@ use crate::io::prelude::*; use crate::ffi::OsStr; use crate::fmt; use crate::fs; -use crate::io::{self, Initializer}; +use crate::io::{self, Initializer, IoSlice, IoSliceMut}; use crate::path::Path; use crate::str; use crate::sys::pipe::{read2, AnonPipe}; @@ -134,6 +134,18 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; /// the parent process wait until the child has actually exited before /// continuing. /// +/// # Warning +/// +/// On some system, calling [`wait`] or similar is necessary for the OS to +/// release resources. A process that terminated but has not been waited on is +/// still around as a "zombie". Leaving too many zombies around may exhaust +/// global resources (for example process IDs). +/// +/// The standard library does *not* automatically wait on child processes (not +/// even if the `Child` is dropped), it is up to the application developer to do +/// so. As a consequence, dropping `Child` handles without waiting on them first +/// is not recommended in long-running applications. +/// /// # Examples /// /// ```should_panic @@ -194,7 +206,7 @@ impl IntoInner<imp::Process> for Child { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Child { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Child") .field("stdin", &self.stdin) .field("stdout", &self.stdout) @@ -225,6 +237,10 @@ impl Write for ChildStdin { self.inner.write(buf) } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.inner.write_vectored(bufs) + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -246,7 +262,7 @@ impl FromInner<AnonPipe> for ChildStdin { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for ChildStdin { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("ChildStdin { .. }") } } @@ -271,6 +287,11 @@ impl Read for ChildStdout { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.inner.read(buf) } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.inner.read_vectored(bufs) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -293,7 +314,7 @@ impl FromInner<AnonPipe> for ChildStdout { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for ChildStdout { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("ChildStdout { .. }") } } @@ -318,6 +339,11 @@ impl Read for ChildStderr { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.inner.read(buf) } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.inner.read_vectored(bufs) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -340,7 +366,7 @@ impl FromInner<AnonPipe> for ChildStderr { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for ChildStderr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("ChildStderr { .. }") } } @@ -803,7 +829,7 @@ impl fmt::Debug for Command { /// Format the program and arguments of a Command for display. Any /// non-utf8 data is lossily converted using the utf8 replacement /// character. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } @@ -844,7 +870,7 @@ pub struct Output { // strings, otherwise it prints the byte sequence instead #[stable(feature = "process_output_debug", since = "1.7.0")] impl fmt::Debug for Output { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let stdout_utf8 = str::from_utf8(&self.stdout); let stdout_debug: &dyn fmt::Debug = match stdout_utf8 { @@ -1002,7 +1028,7 @@ impl FromInner<imp::Stdio> for Stdio { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Stdio { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Stdio { .. }") } } @@ -1127,10 +1153,13 @@ impl From<fs::File> for Stdio { /// /// This `struct` is used to represent the exit status of a child process. /// Child processes are created via the [`Command`] struct and their exit -/// status is exposed through the [`status`] method. +/// status is exposed through the [`status`] method, or the [`wait`] method +/// of a [`Child`] process. /// /// [`Command`]: struct.Command.html +/// [`Child`]: struct.Child.html /// [`status`]: struct.Command.html#method.status +/// [`wait`]: struct.Child.html#method.wait #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[stable(feature = "process", since = "1.0.0")] pub struct ExitStatus(imp::ExitStatus); @@ -1199,7 +1228,7 @@ impl FromInner<imp::ExitStatus> for ExitStatus { #[stable(feature = "process", since = "1.0.0")] impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } @@ -1621,7 +1650,7 @@ impl Termination for ExitCode { } } -#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))] +#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))] mod tests { use crate::io::prelude::*; @@ -1736,33 +1765,6 @@ mod tests { assert_eq!(out, "foobar\n"); } - - #[test] - #[cfg_attr(target_os = "android", ignore)] - #[cfg(unix)] - fn uid_works() { - use crate::os::unix::prelude::*; - - let mut p = Command::new("/bin/sh") - .arg("-c").arg("true") - .uid(unsafe { libc::getuid() }) - .gid(unsafe { libc::getgid() }) - .spawn().unwrap(); - assert!(p.wait().unwrap().success()); - } - - #[test] - #[cfg_attr(target_os = "android", ignore)] - #[cfg(unix)] - fn uid_to_root_fails() { - use crate::os::unix::prelude::*; - - // if we're already root, this isn't a valid test. Most of the bots run - // as non-root though (android is an exception). - if unsafe { libc::getuid() == 0 } { return } - assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err()); - } - #[test] #[cfg_attr(target_os = "android", ignore)] fn test_process_status() { diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs index a4205daba8b..23ba63a6109 100644 --- a/src/libstd/sync/barrier.rs +++ b/src/libstd/sync/barrier.rs @@ -59,7 +59,7 @@ pub struct BarrierWaitResult(bool); #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Barrier { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Barrier { .. }") } } @@ -151,7 +151,7 @@ impl Barrier { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for BarrierWaitResult { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BarrierWaitResult") .field("is_leader", &self.is_leader()) .finish() diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 5ebb61754e1..aeff57716e8 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -36,7 +36,7 @@ impl WaitTimeoutResult { /// let pair2 = pair.clone(); /// /// thread::spawn(move|| { - /// let &(ref lock, ref cvar) = &*pair2; + /// let (lock, cvar) = &*pair2; /// /// // Let's wait 20 milliseconds before notifying the condvar. /// thread::sleep(Duration::from_millis(20)); @@ -48,7 +48,7 @@ impl WaitTimeoutResult { /// }); /// /// // Wait for the thread to start up. - /// let &(ref lock, ref cvar) = &*pair; + /// let (lock, cvar) = &*pair; /// let mut started = lock.lock().unwrap(); /// loop { /// // Let's put a timeout on the condvar's wait. @@ -94,7 +94,7 @@ impl WaitTimeoutResult { /// /// // Inside of our lock, spawn a new thread, and then wait for it to start. /// thread::spawn(move|| { -/// let &(ref lock, ref cvar) = &*pair2; +/// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; /// // We notify the condvar that the value has changed. @@ -102,7 +102,7 @@ impl WaitTimeoutResult { /// }); /// /// // Wait for the thread to start up. -/// let &(ref lock, ref cvar) = &*pair; +/// let (lock, cvar) = &*pair; /// let mut started = lock.lock().unwrap(); /// while !*started { /// started = cvar.wait(started).unwrap(); @@ -180,7 +180,7 @@ impl Condvar { /// let pair2 = pair.clone(); /// /// thread::spawn(move|| { - /// let &(ref lock, ref cvar) = &*pair2; + /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; /// // We notify the condvar that the value has changed. @@ -188,9 +188,9 @@ impl Condvar { /// }); /// /// // Wait for the thread to start up. - /// let &(ref lock, ref cvar) = &*pair; + /// let (lock, cvar) = &*pair; /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is false, we wait. + /// // As long as the value inside the `Mutex<bool>` is `false`, we wait. /// while !*started { /// started = cvar.wait(started).unwrap(); /// } @@ -245,7 +245,7 @@ impl Condvar { /// let pair2 = pair.clone(); /// /// thread::spawn(move|| { - /// let &(ref lock, ref cvar) = &*pair2; + /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; /// // We notify the condvar that the value has changed. @@ -253,8 +253,8 @@ impl Condvar { /// }); /// /// // Wait for the thread to start up. - /// let &(ref lock, ref cvar) = &*pair; - /// // As long as the value inside the `Mutex` is false, we wait. + /// let (lock, cvar) = &*pair; + /// // As long as the value inside the `Mutex<bool>` is `false`, we wait. /// let _guard = cvar.wait_until(lock.lock().unwrap(), |started| { *started }).unwrap(); /// ``` #[unstable(feature = "wait_until", issue = "47960")] @@ -301,7 +301,7 @@ impl Condvar { /// let pair2 = pair.clone(); /// /// thread::spawn(move|| { - /// let &(ref lock, ref cvar) = &*pair2; + /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; /// // We notify the condvar that the value has changed. @@ -309,9 +309,9 @@ impl Condvar { /// }); /// /// // Wait for the thread to start up. - /// let &(ref lock, ref cvar) = &*pair; + /// let (lock, cvar) = &*pair; /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is false, we wait. + /// // As long as the value inside the `Mutex<bool>` is `false`, we wait. /// loop { /// let result = cvar.wait_timeout_ms(started, 10).unwrap(); /// // 10 milliseconds have passed, or maybe the value changed! @@ -374,7 +374,7 @@ impl Condvar { /// let pair2 = pair.clone(); /// /// thread::spawn(move|| { - /// let &(ref lock, ref cvar) = &*pair2; + /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; /// // We notify the condvar that the value has changed. @@ -382,9 +382,9 @@ impl Condvar { /// }); /// /// // wait for the thread to start up - /// let &(ref lock, ref cvar) = &*pair; + /// let (lock, cvar) = &*pair; /// let mut started = lock.lock().unwrap(); - /// // as long as the value inside the `Mutex` is false, we wait + /// // as long as the value inside the `Mutex<bool>` is `false`, we wait /// loop { /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); /// // 10 milliseconds have passed, or maybe the value changed! @@ -449,7 +449,7 @@ impl Condvar { /// let pair2 = pair.clone(); /// /// thread::spawn(move|| { - /// let &(ref lock, ref cvar) = &*pair2; + /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; /// // We notify the condvar that the value has changed. @@ -457,7 +457,7 @@ impl Condvar { /// }); /// /// // wait for the thread to start up - /// let &(ref lock, ref cvar) = &*pair; + /// let (lock, cvar) = &*pair; /// let result = cvar.wait_timeout_until( /// lock.lock().unwrap(), /// Duration::from_millis(100), @@ -508,7 +508,7 @@ impl Condvar { /// let pair2 = pair.clone(); /// /// thread::spawn(move|| { - /// let &(ref lock, ref cvar) = &*pair2; + /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; /// // We notify the condvar that the value has changed. @@ -516,9 +516,9 @@ impl Condvar { /// }); /// /// // Wait for the thread to start up. - /// let &(ref lock, ref cvar) = &*pair; + /// let (lock, cvar) = &*pair; /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is false, we wait. + /// // As long as the value inside the `Mutex<bool>` is `false`, we wait. /// while !*started { /// started = cvar.wait(started).unwrap(); /// } @@ -548,7 +548,7 @@ impl Condvar { /// let pair2 = pair.clone(); /// /// thread::spawn(move|| { - /// let &(ref lock, ref cvar) = &*pair2; + /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; /// // We notify the condvar that the value has changed. @@ -556,9 +556,9 @@ impl Condvar { /// }); /// /// // Wait for the thread to start up. - /// let &(ref lock, ref cvar) = &*pair; + /// let (lock, cvar) = &*pair; /// let mut started = lock.lock().unwrap(); - /// // As long as the value inside the `Mutex` is false, we wait. + /// // As long as the value inside the `Mutex<bool>` is `false`, we wait. /// while !*started { /// started = cvar.wait(started).unwrap(); /// } @@ -589,7 +589,7 @@ impl Condvar { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Condvar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Condvar { .. }") } } @@ -705,6 +705,7 @@ mod tests { #[test] #[cfg_attr(target_os = "emscripten", ignore)] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn wait_timeout_wait() { let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); @@ -724,6 +725,7 @@ mod tests { #[test] #[cfg_attr(target_os = "emscripten", ignore)] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn wait_timeout_until_wait() { let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); @@ -748,6 +750,7 @@ mod tests { #[test] #[cfg_attr(target_os = "emscripten", ignore)] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn wait_timeout_until_wake() { let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair_copy = pair.clone(); @@ -771,6 +774,7 @@ mod tests { #[test] #[cfg_attr(target_os = "emscripten", ignore)] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn wait_timeout_wake() { let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); diff --git a/src/libstd/sync/mod.rs b/src/libstd/sync/mod.rs index 809ee882698..e29faf18d83 100644 --- a/src/libstd/sync/mod.rs +++ b/src/libstd/sync/mod.rs @@ -163,6 +163,8 @@ pub use self::condvar::{Condvar, WaitTimeoutResult}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::mutex::{Mutex, MutexGuard}; #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(bootstrap, allow(deprecated_in_future))] +#[allow(deprecated)] pub use self::once::{Once, OnceState, ONCE_INIT}; #[stable(feature = "rust1", since = "1.0.0")] pub use crate::sys_common::poison::{PoisonError, TryLockError, TryLockResult, LockResult}; diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index 90c5c50c23b..69ecd201063 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + //! Multi-producer, single-consumer FIFO queue communication primitives. //! //! This module provides message-based communication over channels, concretely @@ -114,7 +116,6 @@ //! ``` #![stable(feature = "rust1", since = "1.0.0")] -#![allow(deprecated)] // for mpsc_select // A description of how Rust's channel implementation works // @@ -261,6 +262,8 @@ // believe that there is anything fundamental that needs to change about these // channels, however, in order to support a more efficient select(). // +// FIXME: Select is now removed, so these factors are ready to be cleaned up! +// // # Conclusion // // And now that you've seen all the races that I found and attempted to fix, @@ -273,18 +276,8 @@ use crate::mem; use crate::cell::UnsafeCell; use crate::time::{Duration, Instant}; -#[unstable(feature = "mpsc_select", issue = "27800")] -pub use self::select::{Select, Handle}; -use self::select::StartResult; -use self::select::StartResult::*; -use self::blocking::SignalToken; - -#[cfg(all(test, not(target_os = "emscripten")))] -mod select_tests; - mod blocking; mod oneshot; -mod select; mod shared; mod stream; mod sync; @@ -914,7 +907,7 @@ impl<T> Drop for Sender<T> { #[stable(feature = "mpsc_debug", since = "1.8.0")] impl<T> fmt::Debug for Sender<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Sender").finish() } } @@ -1005,7 +998,7 @@ impl<T> SyncSender<T> { /// thread::spawn(move || { /// // This will return an error and send /// // no message if the buffer is full - /// sync_sender2.try_send(3).is_err(); + /// let _ = sync_sender2.try_send(3); /// }); /// /// let mut msg; @@ -1044,7 +1037,7 @@ impl<T> Drop for SyncSender<T> { #[stable(feature = "mpsc_debug", since = "1.8.0")] impl<T> fmt::Debug for SyncSender<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SyncSender").finish() } } @@ -1463,7 +1456,7 @@ impl<T> Receiver<T> { /// assert_eq!(iter.next(), None); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<T> { + pub fn iter(&self) -> Iter<'_, T> { Iter { rx: self } } @@ -1506,84 +1499,12 @@ impl<T> Receiver<T> { /// assert_eq!(iter.next(), None); /// ``` #[stable(feature = "receiver_try_iter", since = "1.15.0")] - pub fn try_iter(&self) -> TryIter<T> { + pub fn try_iter(&self) -> TryIter<'_, T> { TryIter { rx: self } } } -impl<T> select::Packet for Receiver<T> { - fn can_recv(&self) -> bool { - loop { - let new_port = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => { - match p.can_recv() { - Ok(ret) => return ret, - Err(upgrade) => upgrade, - } - } - Flavor::Stream(ref p) => { - match p.can_recv() { - Ok(ret) => return ret, - Err(upgrade) => upgrade, - } - } - Flavor::Shared(ref p) => return p.can_recv(), - Flavor::Sync(ref p) => return p.can_recv(), - }; - unsafe { - mem::swap(self.inner_mut(), - new_port.inner_mut()); - } - } - } - - fn start_selection(&self, mut token: SignalToken) -> StartResult { - loop { - let (t, new_port) = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => { - match p.start_selection(token) { - oneshot::SelSuccess => return Installed, - oneshot::SelCanceled => return Abort, - oneshot::SelUpgraded(t, rx) => (t, rx), - } - } - Flavor::Stream(ref p) => { - match p.start_selection(token) { - stream::SelSuccess => return Installed, - stream::SelCanceled => return Abort, - stream::SelUpgraded(t, rx) => (t, rx), - } - } - Flavor::Shared(ref p) => return p.start_selection(token), - Flavor::Sync(ref p) => return p.start_selection(token), - }; - token = t; - unsafe { - mem::swap(self.inner_mut(), new_port.inner_mut()); - } - } - } - - fn abort_selection(&self) -> bool { - let mut was_upgrade = false; - loop { - let result = match *unsafe { self.inner() } { - Flavor::Oneshot(ref p) => p.abort_selection(), - Flavor::Stream(ref p) => p.abort_selection(was_upgrade), - Flavor::Shared(ref p) => return p.abort_selection(was_upgrade), - Flavor::Sync(ref p) => return p.abort_selection(), - }; - let new_port = match result { Ok(b) => return b, Err(p) => p }; - was_upgrade = true; - unsafe { - mem::swap(self.inner_mut(), - new_port.inner_mut()); - } - } - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Iterator for Iter<'a, T> { type Item = T; @@ -1636,21 +1557,21 @@ impl<T> Drop for Receiver<T> { #[stable(feature = "mpsc_debug", since = "1.8.0")] impl<T> fmt::Debug for Receiver<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Receiver").finish() } } #[stable(feature = "rust1", since = "1.0.0")] impl<T> fmt::Debug for SendError<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "SendError(..)".fmt(f) } } #[stable(feature = "rust1", since = "1.0.0")] impl<T> fmt::Display for SendError<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "sending on a closed channel".fmt(f) } } @@ -1668,7 +1589,7 @@ impl<T: Send> error::Error for SendError<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> fmt::Debug for TrySendError<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { TrySendError::Full(..) => "Full(..)".fmt(f), TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f), @@ -1678,7 +1599,7 @@ impl<T> fmt::Debug for TrySendError<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> fmt::Display for TrySendError<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { TrySendError::Full(..) => { "sending on a full channel".fmt(f) @@ -1720,7 +1641,7 @@ impl<T> From<SendError<T>> for TrySendError<T> { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for RecvError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "receiving on a closed channel".fmt(f) } } @@ -1739,7 +1660,7 @@ impl error::Error for RecvError { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for TryRecvError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { TryRecvError::Empty => { "receiving on an empty channel".fmt(f) @@ -1781,7 +1702,7 @@ impl From<RecvError> for TryRecvError { #[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] impl fmt::Display for RecvTimeoutError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { RecvTimeoutError::Timeout => { "timed out waiting on channel".fmt(f) @@ -1951,7 +1872,7 @@ mod tests { for _ in 0..10000 { assert_eq!(rx.recv().unwrap(), 1); } - t.join().ok().unwrap(); + t.join().ok().expect("thread panicked"); } #[test] @@ -1977,7 +1898,7 @@ mod tests { }); } drop(tx); - t.join().ok().unwrap(); + t.join().ok().expect("thread panicked"); } #[test] @@ -1996,8 +1917,8 @@ mod tests { tx2.send(1).unwrap(); } }); - t1.join().ok().unwrap(); - t2.join().ok().unwrap(); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); } #[test] @@ -2011,7 +1932,7 @@ mod tests { for _ in 0..40 { tx.send(1).unwrap(); } - t.join().ok().unwrap(); + t.join().ok().expect("thread panicked"); } #[test] @@ -2026,8 +1947,8 @@ mod tests { tx1.send(1).unwrap(); assert_eq!(rx2.recv().unwrap(), 2); }); - t1.join().ok().unwrap(); - t2.join().ok().unwrap(); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); } #[test] @@ -2225,6 +2146,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn oneshot_single_thread_recv_timeout() { let (tx, rx) = channel(); tx.send(()).unwrap(); @@ -2235,6 +2157,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn stress_recv_timeout_two_threads() { let (tx, rx) = channel(); let stress = stress_factor() + 100; @@ -2265,6 +2188,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn recv_timeout_upgrade() { let (tx, rx) = channel::<()>(); let timeout = Duration::from_millis(1); @@ -2276,6 +2200,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn stress_recv_timeout_shared() { let (tx, rx) = channel(); let stress = stress_factor() + 100; @@ -2306,6 +2231,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn very_long_recv_timeout_wont_panic() { let (tx, rx) = channel::<()>(); let join_handle = thread::spawn(move || { @@ -2325,6 +2251,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn shared_recv_timeout() { let (tx, rx) = channel(); let total = 5; @@ -2550,6 +2477,7 @@ mod sync_tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn recv_timeout() { let (tx, rx) = sync_channel::<i32>(1); assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); @@ -2639,6 +2567,7 @@ mod sync_tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn stress_recv_timeout_two_threads() { let (tx, rx) = sync_channel::<i32>(0); @@ -2662,6 +2591,7 @@ mod sync_tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn stress_recv_timeout_shared() { const AMT: u32 = 1000; const NTHREADS: u32 = 8; diff --git a/src/libstd/sync/mpsc/oneshot.rs b/src/libstd/sync/mpsc/oneshot.rs index 5c516d5de0f..e7a5cc46b31 100644 --- a/src/libstd/sync/mpsc/oneshot.rs +++ b/src/libstd/sync/mpsc/oneshot.rs @@ -24,7 +24,6 @@ pub use self::Failure::*; pub use self::UpgradeResult::*; -pub use self::SelectionResult::*; use self::MyUpgrade::*; use crate::sync::mpsc::Receiver; @@ -66,12 +65,6 @@ pub enum UpgradeResult { UpWoke(SignalToken), } -pub enum SelectionResult<T> { - SelCanceled, - SelUpgraded(SignalToken, Receiver<T>), - SelSuccess, -} - enum MyUpgrade<T> { NothingSent, SendUsed, @@ -264,71 +257,6 @@ impl<T> Packet<T> { // select implementation //////////////////////////////////////////////////////////////////////////// - // If Ok, the value is whether this port has data, if Err, then the upgraded - // port needs to be checked instead of this one. - pub fn can_recv(&self) -> Result<bool, Receiver<T>> { - unsafe { - match self.state.load(Ordering::SeqCst) { - EMPTY => Ok(false), // Welp, we tried - DATA => Ok(true), // we have some un-acquired data - DISCONNECTED if (*self.data.get()).is_some() => Ok(true), // we have data - DISCONNECTED => { - match ptr::replace(self.upgrade.get(), SendUsed) { - // The other end sent us an upgrade, so we need to - // propagate upwards whether the upgrade can receive - // data - GoUp(upgrade) => Err(upgrade), - - // If the other end disconnected without sending an - // upgrade, then we have data to receive (the channel is - // disconnected). - up => { ptr::write(self.upgrade.get(), up); Ok(true) } - } - } - _ => unreachable!(), // we're the "one blocker" - } - } - } - - // Attempts to start selection on this port. This can either succeed, fail - // because there is data, or fail because there is an upgrade pending. - pub fn start_selection(&self, token: SignalToken) -> SelectionResult<T> { - unsafe { - let ptr = token.cast_to_usize(); - match self.state.compare_and_swap(EMPTY, ptr, Ordering::SeqCst) { - EMPTY => SelSuccess, - DATA => { - drop(SignalToken::cast_from_usize(ptr)); - SelCanceled - } - DISCONNECTED if (*self.data.get()).is_some() => { - drop(SignalToken::cast_from_usize(ptr)); - SelCanceled - } - DISCONNECTED => { - match ptr::replace(self.upgrade.get(), SendUsed) { - // The other end sent us an upgrade, so we need to - // propagate upwards whether the upgrade can receive - // data - GoUp(upgrade) => { - SelUpgraded(SignalToken::cast_from_usize(ptr), upgrade) - } - - // If the other end disconnected without sending an - // upgrade, then we have data to receive (the channel is - // disconnected). - up => { - ptr::write(self.upgrade.get(), up); - drop(SignalToken::cast_from_usize(ptr)); - SelCanceled - } - } - } - _ => unreachable!(), // we're the "one blocker" - } - } - } - // Remove a previous selecting thread from this port. This ensures that the // blocked thread will no longer be visible to any other threads. // diff --git a/src/libstd/sync/mpsc/select.rs b/src/libstd/sync/mpsc/select.rs deleted file mode 100644 index 19c94908645..00000000000 --- a/src/libstd/sync/mpsc/select.rs +++ /dev/null @@ -1,352 +0,0 @@ -//! Selection over an array of receivers -//! -//! This module contains the implementation machinery necessary for selecting -//! over a number of receivers. One large goal of this module is to provide an -//! efficient interface to selecting over any receiver of any type. -//! -//! This is achieved through an architecture of a "receiver set" in which -//! receivers are added to a set and then the entire set is waited on at once. -//! The set can be waited on multiple times to prevent re-adding each receiver -//! to the set. -//! -//! Usage of this module is currently encouraged to go through the use of the -//! `select!` macro. This macro allows naturally binding of variables to the -//! received values of receivers in a much more natural syntax then usage of the -//! `Select` structure directly. -//! -//! # Examples -//! -//! ```rust -//! #![feature(mpsc_select)] -//! -//! use std::sync::mpsc::channel; -//! -//! let (tx1, rx1) = channel(); -//! let (tx2, rx2) = channel(); -//! -//! tx1.send(1).unwrap(); -//! tx2.send(2).unwrap(); -//! -//! select! { -//! val = rx1.recv() => { -//! assert_eq!(val.unwrap(), 1); -//! }, -//! val = rx2.recv() => { -//! assert_eq!(val.unwrap(), 2); -//! } -//! } -//! ``` - -#![allow(dead_code)] -#![unstable(feature = "mpsc_select", - reason = "This implementation, while likely sufficient, is unsafe and \ - likely to be error prone. At some point in the future this \ - module will be removed.", - issue = "27800")] -#![rustc_deprecated(since = "1.32.0", - reason = "channel selection will be removed in a future release")] - -use core::cell::{Cell, UnsafeCell}; -use core::marker; -use core::ptr; -use core::usize; - -use crate::fmt; -use crate::sync::mpsc::{Receiver, RecvError}; -use crate::sync::mpsc::blocking::{self, SignalToken}; - -/// The "receiver set" of the select interface. This structure is used to manage -/// a set of receivers which are being selected over. -pub struct Select { - inner: UnsafeCell<SelectInner>, - next_id: Cell<usize>, -} - -struct SelectInner { - head: *mut Handle<'static, ()>, - tail: *mut Handle<'static, ()>, -} - -impl !marker::Send for Select {} - -/// A handle to a receiver which is currently a member of a `Select` set of -/// receivers. This handle is used to keep the receiver in the set as well as -/// interact with the underlying receiver. -pub struct Handle<'rx, T:Send+'rx> { - /// The ID of this handle, used to compare against the return value of - /// `Select::wait()`. - id: usize, - selector: *mut SelectInner, - next: *mut Handle<'static, ()>, - prev: *mut Handle<'static, ()>, - added: bool, - packet: &'rx (dyn Packet+'rx), - - // due to our fun transmutes, we be sure to place this at the end. (nothing - // previous relies on T) - rx: &'rx Receiver<T>, -} - -struct Packets { cur: *mut Handle<'static, ()> } - -#[doc(hidden)] -#[derive(PartialEq, Eq)] -pub enum StartResult { - Installed, - Abort, -} - -#[doc(hidden)] -pub trait Packet { - fn can_recv(&self) -> bool; - fn start_selection(&self, token: SignalToken) -> StartResult; - fn abort_selection(&self) -> bool; -} - -impl Select { - /// Creates a new selection structure. This set is initially empty. - /// - /// Usage of this struct directly can sometimes be burdensome, and usage is much easier through - /// the `select!` macro. - /// - /// # Examples - /// - /// ``` - /// #![feature(mpsc_select)] - /// - /// use std::sync::mpsc::Select; - /// - /// let select = Select::new(); - /// ``` - pub fn new() -> Select { - Select { - inner: UnsafeCell::new(SelectInner { - head: ptr::null_mut(), - tail: ptr::null_mut(), - }), - next_id: Cell::new(1), - } - } - - /// Creates a new handle into this receiver set for a new receiver. Note - /// that this does *not* add the receiver to the receiver set, for that you - /// must call the `add` method on the handle itself. - pub fn handle<'a, T: Send>(&'a self, rx: &'a Receiver<T>) -> Handle<'a, T> { - let id = self.next_id.get(); - self.next_id.set(id + 1); - Handle { - id, - selector: self.inner.get(), - next: ptr::null_mut(), - prev: ptr::null_mut(), - added: false, - rx, - packet: rx, - } - } - - /// Waits for an event on this receiver set. The returned value is *not* an - /// index, but rather an ID. This ID can be queried against any active - /// `Handle` structures (each one has an `id` method). The handle with - /// the matching `id` will have some sort of event available on it. The - /// event could either be that data is available or the corresponding - /// channel has been closed. - pub fn wait(&self) -> usize { - self.wait2(true) - } - - /// Helper method for skipping the preflight checks during testing - pub(super) fn wait2(&self, do_preflight_checks: bool) -> usize { - // Note that this is currently an inefficient implementation. We in - // theory have knowledge about all receivers in the set ahead of time, - // so this method shouldn't really have to iterate over all of them yet - // again. The idea with this "receiver set" interface is to get the - // interface right this time around, and later this implementation can - // be optimized. - // - // This implementation can be summarized by: - // - // fn select(receivers) { - // if any receiver ready { return ready index } - // deschedule { - // block on all receivers - // } - // unblock on all receivers - // return ready index - // } - // - // Most notably, the iterations over all of the receivers shouldn't be - // necessary. - unsafe { - // Stage 1: preflight checks. Look for any packets ready to receive - if do_preflight_checks { - for handle in self.iter() { - if (*handle).packet.can_recv() { - return (*handle).id(); - } - } - } - - // Stage 2: begin the blocking process - // - // Create a number of signal tokens, and install each one - // sequentially until one fails. If one fails, then abort the - // selection on the already-installed tokens. - let (wait_token, signal_token) = blocking::tokens(); - for (i, handle) in self.iter().enumerate() { - match (*handle).packet.start_selection(signal_token.clone()) { - StartResult::Installed => {} - StartResult::Abort => { - // Go back and abort the already-begun selections - for handle in self.iter().take(i) { - (*handle).packet.abort_selection(); - } - return (*handle).id; - } - } - } - - // Stage 3: no messages available, actually block - wait_token.wait(); - - // Stage 4: there *must* be message available; find it. - // - // Abort the selection process on each receiver. If the abort - // process returns `true`, then that means that the receiver is - // ready to receive some data. Note that this also means that the - // receiver may have yet to have fully read the `to_wake` field and - // woken us up (although the wakeup is guaranteed to fail). - // - // This situation happens in the window of where a sender invokes - // increment(), sees -1, and then decides to wake up the thread. After - // all this is done, the sending thread will set `selecting` to - // `false`. Until this is done, we cannot return. If we were to - // return, then a sender could wake up a receiver which has gone - // back to sleep after this call to `select`. - // - // Note that it is a "fairly small window" in which an increment() - // views that it should wake a thread up until the `selecting` bit - // is set to false. For now, the implementation currently just spins - // in a yield loop. This is very distasteful, but this - // implementation is already nowhere near what it should ideally be. - // A rewrite should focus on avoiding a yield loop, and for now this - // implementation is tying us over to a more efficient "don't - // iterate over everything every time" implementation. - let mut ready_id = usize::MAX; - for handle in self.iter() { - if (*handle).packet.abort_selection() { - ready_id = (*handle).id; - } - } - - // We must have found a ready receiver - assert!(ready_id != usize::MAX); - return ready_id; - } - } - - fn iter(&self) -> Packets { Packets { cur: unsafe { &*self.inner.get() }.head } } -} - -impl<'rx, T: Send> Handle<'rx, T> { - /// Retrieves the ID of this handle. - #[inline] - pub fn id(&self) -> usize { self.id } - - /// Blocks to receive a value on the underlying receiver, returning `Some` on - /// success or `None` if the channel disconnects. This function has the same - /// semantics as `Receiver.recv` - pub fn recv(&mut self) -> Result<T, RecvError> { self.rx.recv() } - - /// Adds this handle to the receiver set that the handle was created from. This - /// method can be called multiple times, but it has no effect if `add` was - /// called previously. - /// - /// This method is unsafe because it requires that the `Handle` is not moved - /// while it is added to the `Select` set. - pub unsafe fn add(&mut self) { - if self.added { return } - let selector = &mut *self.selector; - let me = self as *mut Handle<'rx, T> as *mut Handle<'static, ()>; - - if selector.head.is_null() { - selector.head = me; - selector.tail = me; - } else { - (*me).prev = selector.tail; - assert!((*me).next.is_null()); - (*selector.tail).next = me; - selector.tail = me; - } - self.added = true; - } - - /// Removes this handle from the `Select` set. This method is unsafe because - /// it has no guarantee that the `Handle` was not moved since `add` was - /// called. - pub unsafe fn remove(&mut self) { - if !self.added { return } - - let selector = &mut *self.selector; - let me = self as *mut Handle<'rx, T> as *mut Handle<'static, ()>; - - if self.prev.is_null() { - assert_eq!(selector.head, me); - selector.head = self.next; - } else { - (*self.prev).next = self.next; - } - if self.next.is_null() { - assert_eq!(selector.tail, me); - selector.tail = self.prev; - } else { - (*self.next).prev = self.prev; - } - - self.next = ptr::null_mut(); - self.prev = ptr::null_mut(); - - self.added = false; - } -} - -impl Drop for Select { - fn drop(&mut self) { - unsafe { - assert!((&*self.inner.get()).head.is_null()); - assert!((&*self.inner.get()).tail.is_null()); - } - } -} - -impl<T: Send> Drop for Handle<'_, T> { - fn drop(&mut self) { - unsafe { self.remove() } - } -} - -impl Iterator for Packets { - type Item = *mut Handle<'static, ()>; - - fn next(&mut self) -> Option<*mut Handle<'static, ()>> { - if self.cur.is_null() { - None - } else { - let ret = Some(self.cur); - unsafe { self.cur = (*self.cur).next; } - ret - } - } -} - -impl fmt::Debug for Select { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Select").finish() - } -} - -impl<T: Send> fmt::Debug for Handle<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Handle").finish() - } -} diff --git a/src/libstd/sync/mpsc/select_tests.rs b/src/libstd/sync/mpsc/select_tests.rs deleted file mode 100644 index 18d93462c78..00000000000 --- a/src/libstd/sync/mpsc/select_tests.rs +++ /dev/null @@ -1,413 +0,0 @@ -#![allow(unused_imports)] - -/// This file exists to hack around https://github.com/rust-lang/rust/issues/47238 - -use crate::thread; -use crate::sync::mpsc::*; - -// Don't use the libstd version so we can pull in the right Select structure -// (std::comm points at the wrong one) -macro_rules! select { - ( - $($name:pat = $rx:ident.$meth:ident() => $code:expr),+ - ) => ({ - let sel = Select::new(); - $( let mut $rx = sel.handle(&$rx); )+ - unsafe { - $( $rx.add(); )+ - } - let ret = sel.wait(); - $( if ret == $rx.id() { let $name = $rx.$meth(); $code } else )+ - { unreachable!() } - }) -} - -#[test] -fn smoke() { - let (tx1, rx1) = channel::<i32>(); - let (tx2, rx2) = channel::<i32>(); - tx1.send(1).unwrap(); - select! { - foo = rx1.recv() => { assert_eq!(foo.unwrap(), 1); }, - _bar = rx2.recv() => { panic!() } - } - tx2.send(2).unwrap(); - select! { - _foo = rx1.recv() => { panic!() }, - bar = rx2.recv() => { assert_eq!(bar.unwrap(), 2) } - } - drop(tx1); - select! { - foo = rx1.recv() => { assert!(foo.is_err()); }, - _bar = rx2.recv() => { panic!() } - } - drop(tx2); - select! { - bar = rx2.recv() => { assert!(bar.is_err()); } - } -} - -#[test] -fn smoke2() { - let (_tx1, rx1) = channel::<i32>(); - let (_tx2, rx2) = channel::<i32>(); - let (_tx3, rx3) = channel::<i32>(); - let (_tx4, rx4) = channel::<i32>(); - let (tx5, rx5) = channel::<i32>(); - tx5.send(4).unwrap(); - select! { - _foo = rx1.recv() => { panic!("1") }, - _foo = rx2.recv() => { panic!("2") }, - _foo = rx3.recv() => { panic!("3") }, - _foo = rx4.recv() => { panic!("4") }, - foo = rx5.recv() => { assert_eq!(foo.unwrap(), 4); } - } -} - -#[test] -fn closed() { - let (_tx1, rx1) = channel::<i32>(); - let (tx2, rx2) = channel::<i32>(); - drop(tx2); - - select! { - _a1 = rx1.recv() => { panic!() }, - a2 = rx2.recv() => { assert!(a2.is_err()); } - } -} - -#[test] -fn unblocks() { - let (tx1, rx1) = channel::<i32>(); - let (_tx2, rx2) = channel::<i32>(); - let (tx3, rx3) = channel::<i32>(); - - let _t = thread::spawn(move|| { - for _ in 0..20 { thread::yield_now(); } - tx1.send(1).unwrap(); - rx3.recv().unwrap(); - for _ in 0..20 { thread::yield_now(); } - }); - - select! { - a = rx1.recv() => { assert_eq!(a.unwrap(), 1); }, - _b = rx2.recv() => { panic!() } - } - tx3.send(1).unwrap(); - select! { - a = rx1.recv() => { assert!(a.is_err()) }, - _b = rx2.recv() => { panic!() } - } -} - -#[test] -fn both_ready() { - let (tx1, rx1) = channel::<i32>(); - let (tx2, rx2) = channel::<i32>(); - let (tx3, rx3) = channel::<()>(); - - let _t = thread::spawn(move|| { - for _ in 0..20 { thread::yield_now(); } - tx1.send(1).unwrap(); - tx2.send(2).unwrap(); - rx3.recv().unwrap(); - }); - - select! { - a = rx1.recv() => { assert_eq!(a.unwrap(), 1); }, - a = rx2.recv() => { assert_eq!(a.unwrap(), 2); } - } - select! { - a = rx1.recv() => { assert_eq!(a.unwrap(), 1); }, - a = rx2.recv() => { assert_eq!(a.unwrap(), 2); } - } - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - assert_eq!(rx2.try_recv(), Err(TryRecvError::Empty)); - tx3.send(()).unwrap(); -} - -#[test] -fn stress() { - const AMT: i32 = 10000; - let (tx1, rx1) = channel::<i32>(); - let (tx2, rx2) = channel::<i32>(); - let (tx3, rx3) = channel::<()>(); - - let _t = thread::spawn(move|| { - for i in 0..AMT { - if i % 2 == 0 { - tx1.send(i).unwrap(); - } else { - tx2.send(i).unwrap(); - } - rx3.recv().unwrap(); - } - }); - - for i in 0..AMT { - select! { - i1 = rx1.recv() => { assert!(i % 2 == 0 && i == i1.unwrap()); }, - i2 = rx2.recv() => { assert!(i % 2 == 1 && i == i2.unwrap()); } - } - tx3.send(()).unwrap(); - } -} - -#[allow(unused_must_use)] -#[test] -fn cloning() { - let (tx1, rx1) = channel::<i32>(); - let (_tx2, rx2) = channel::<i32>(); - let (tx3, rx3) = channel::<()>(); - - let _t = thread::spawn(move|| { - rx3.recv().unwrap(); - tx1.clone(); - assert_eq!(rx3.try_recv(), Err(TryRecvError::Empty)); - tx1.send(2).unwrap(); - rx3.recv().unwrap(); - }); - - tx3.send(()).unwrap(); - select! { - _i1 = rx1.recv() => {}, - _i2 = rx2.recv() => panic!() - } - tx3.send(()).unwrap(); -} - -#[allow(unused_must_use)] -#[test] -fn cloning2() { - let (tx1, rx1) = channel::<i32>(); - let (_tx2, rx2) = channel::<i32>(); - let (tx3, rx3) = channel::<()>(); - - let _t = thread::spawn(move|| { - rx3.recv().unwrap(); - tx1.clone(); - assert_eq!(rx3.try_recv(), Err(TryRecvError::Empty)); - tx1.send(2).unwrap(); - rx3.recv().unwrap(); - }); - - tx3.send(()).unwrap(); - select! { - _i1 = rx1.recv() => {}, - _i2 = rx2.recv() => panic!() - } - tx3.send(()).unwrap(); -} - -#[test] -fn cloning3() { - let (tx1, rx1) = channel::<()>(); - let (tx2, rx2) = channel::<()>(); - let (tx3, rx3) = channel::<()>(); - let _t = thread::spawn(move|| { - let s = Select::new(); - let mut h1 = s.handle(&rx1); - let mut h2 = s.handle(&rx2); - unsafe { h2.add(); } - unsafe { h1.add(); } - assert_eq!(s.wait(), h2.id()); - tx3.send(()).unwrap(); - }); - - for _ in 0..1000 { thread::yield_now(); } - drop(tx1.clone()); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); -} - -#[test] -fn preflight1() { - let (tx, rx) = channel(); - tx.send(()).unwrap(); - select! { - _n = rx.recv() => {} - } -} - -#[test] -fn preflight2() { - let (tx, rx) = channel(); - tx.send(()).unwrap(); - tx.send(()).unwrap(); - select! { - _n = rx.recv() => {} - } -} - -#[test] -fn preflight3() { - let (tx, rx) = channel(); - drop(tx.clone()); - tx.send(()).unwrap(); - select! { - _n = rx.recv() => {} - } -} - -#[test] -fn preflight4() { - let (tx, rx) = channel(); - tx.send(()).unwrap(); - let s = Select::new(); - let mut h = s.handle(&rx); - unsafe { h.add(); } - assert_eq!(s.wait2(false), h.id()); -} - -#[test] -fn preflight5() { - let (tx, rx) = channel(); - tx.send(()).unwrap(); - tx.send(()).unwrap(); - let s = Select::new(); - let mut h = s.handle(&rx); - unsafe { h.add(); } - assert_eq!(s.wait2(false), h.id()); -} - -#[test] -fn preflight6() { - let (tx, rx) = channel(); - drop(tx.clone()); - tx.send(()).unwrap(); - let s = Select::new(); - let mut h = s.handle(&rx); - unsafe { h.add(); } - assert_eq!(s.wait2(false), h.id()); -} - -#[test] -fn preflight7() { - let (tx, rx) = channel::<()>(); - drop(tx); - let s = Select::new(); - let mut h = s.handle(&rx); - unsafe { h.add(); } - assert_eq!(s.wait2(false), h.id()); -} - -#[test] -fn preflight8() { - let (tx, rx) = channel(); - tx.send(()).unwrap(); - drop(tx); - rx.recv().unwrap(); - let s = Select::new(); - let mut h = s.handle(&rx); - unsafe { h.add(); } - assert_eq!(s.wait2(false), h.id()); -} - -#[test] -fn preflight9() { - let (tx, rx) = channel(); - drop(tx.clone()); - tx.send(()).unwrap(); - drop(tx); - rx.recv().unwrap(); - let s = Select::new(); - let mut h = s.handle(&rx); - unsafe { h.add(); } - assert_eq!(s.wait2(false), h.id()); -} - -#[test] -fn oneshot_data_waiting() { - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let _t = thread::spawn(move|| { - select! { - _n = rx1.recv() => {} - } - tx2.send(()).unwrap(); - }); - - for _ in 0..100 { thread::yield_now() } - tx1.send(()).unwrap(); - rx2.recv().unwrap(); -} - -#[test] -fn stream_data_waiting() { - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - tx1.send(()).unwrap(); - tx1.send(()).unwrap(); - rx1.recv().unwrap(); - rx1.recv().unwrap(); - let _t = thread::spawn(move|| { - select! { - _n = rx1.recv() => {} - } - tx2.send(()).unwrap(); - }); - - for _ in 0..100 { thread::yield_now() } - tx1.send(()).unwrap(); - rx2.recv().unwrap(); -} - -#[test] -fn shared_data_waiting() { - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - drop(tx1.clone()); - tx1.send(()).unwrap(); - rx1.recv().unwrap(); - let _t = thread::spawn(move|| { - select! { - _n = rx1.recv() => {} - } - tx2.send(()).unwrap(); - }); - - for _ in 0..100 { thread::yield_now() } - tx1.send(()).unwrap(); - rx2.recv().unwrap(); -} - -#[test] -fn sync1() { - let (tx, rx) = sync_channel::<i32>(1); - tx.send(1).unwrap(); - select! { - n = rx.recv() => { assert_eq!(n.unwrap(), 1); } - } -} - -#[test] -fn sync2() { - let (tx, rx) = sync_channel::<i32>(0); - let _t = thread::spawn(move|| { - for _ in 0..100 { thread::yield_now() } - tx.send(1).unwrap(); - }); - select! { - n = rx.recv() => { assert_eq!(n.unwrap(), 1); } - } -} - -#[test] -fn sync3() { - let (tx1, rx1) = sync_channel::<i32>(0); - let (tx2, rx2): (Sender<i32>, Receiver<i32>) = channel(); - let _t = thread::spawn(move|| { tx1.send(1).unwrap(); }); - let _t = thread::spawn(move|| { tx2.send(2).unwrap(); }); - select! { - n = rx1.recv() => { - let n = n.unwrap(); - assert_eq!(n, 1); - assert_eq!(rx2.recv().unwrap(), 2); - }, - n = rx2.recv() => { - let n = n.unwrap(); - assert_eq!(n, 2); - assert_eq!(rx1.recv().unwrap(), 1); - } - } -} diff --git a/src/libstd/sync/mpsc/shared.rs b/src/libstd/sync/mpsc/shared.rs index 6a5d861f0e9..dbcdcdac932 100644 --- a/src/libstd/sync/mpsc/shared.rs +++ b/src/libstd/sync/mpsc/shared.rs @@ -9,6 +9,7 @@ /// channels are quite similar, and this is no coincidence! pub use self::Failure::*; +use self::StartResult::*; use core::cmp; use core::intrinsics::abort; @@ -19,8 +20,6 @@ use crate::ptr; use crate::sync::atomic::{AtomicUsize, AtomicIsize, AtomicBool, Ordering}; use crate::sync::mpsc::blocking::{self, SignalToken}; use crate::sync::mpsc::mpsc_queue as mpsc; -use crate::sync::mpsc::select::StartResult::*; -use crate::sync::mpsc::select::StartResult; use crate::sync::{Mutex, MutexGuard}; use crate::thread; use crate::time::Instant; @@ -57,6 +56,12 @@ pub enum Failure { Disconnected, } +#[derive(PartialEq, Eq)] +enum StartResult { + Installed, + Abort, +} + impl<T> Packet<T> { // Creation of a packet *must* be followed by a call to postinit_lock // and later by inherit_blocker @@ -78,7 +83,7 @@ impl<T> Packet<T> { // In other case mutex data will be duplicated while cloning // and that could cause problems on platforms where it is // represented by opaque data structure - pub fn postinit_lock(&self) -> MutexGuard<()> { + pub fn postinit_lock(&self) -> MutexGuard<'_, ()> { self.select_lock.lock().unwrap() } @@ -89,7 +94,7 @@ impl<T> Packet<T> { // This can only be called at channel-creation time pub fn inherit_blocker(&self, token: Option<SignalToken>, - guard: MutexGuard<()>) { + guard: MutexGuard<'_, ()>) { token.map(|token| { assert_eq!(self.cnt.load(Ordering::SeqCst), 0); assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); @@ -394,16 +399,6 @@ impl<T> Packet<T> { // select implementation //////////////////////////////////////////////////////////////////////////// - // Helper function for select, tests whether this port can receive without - // blocking (obviously not an atomic decision). - // - // This is different than the stream version because there's no need to peek - // at the queue, we can just look at the local count. - pub fn can_recv(&self) -> bool { - let cnt = self.cnt.load(Ordering::SeqCst); - cnt == DISCONNECTED || cnt - unsafe { *self.steals.get() } > 0 - } - // increment the count on the channel (used for selection) fn bump(&self, amt: isize) -> isize { match self.cnt.fetch_add(amt, Ordering::SeqCst) { @@ -415,22 +410,6 @@ impl<T> Packet<T> { } } - // Inserts the signal token for selection on this port, returning true if - // blocking should proceed. - // - // The code here is the same as in stream.rs, except that it doesn't need to - // peek at the channel to see if an upgrade is pending. - pub fn start_selection(&self, token: SignalToken) -> StartResult { - match self.decrement(token) { - Installed => Installed, - Abort => { - let prev = self.bump(1); - assert!(prev == DISCONNECTED || prev >= 0); - Abort - } - } - } - // Cancels a previous thread waiting on this port, returning whether there's // data on the port. // diff --git a/src/libstd/sync/mpsc/stream.rs b/src/libstd/sync/mpsc/stream.rs index 7ae6f68b514..40877282761 100644 --- a/src/libstd/sync/mpsc/stream.rs +++ b/src/libstd/sync/mpsc/stream.rs @@ -9,7 +9,6 @@ pub use self::Failure::*; pub use self::UpgradeResult::*; -pub use self::SelectionResult::*; use self::Message::*; use core::cmp; @@ -60,12 +59,6 @@ pub enum UpgradeResult { UpWoke(SignalToken), } -pub enum SelectionResult<T> { - SelSuccess, - SelCanceled, - SelUpgraded(SignalToken, Receiver<T>), -} - // Any message could contain an "upgrade request" to a new shared port, so the // internal queue it's a queue of T, but rather Message<T> enum Message<T> { @@ -338,27 +331,6 @@ impl<T> Packet<T> { // select implementation //////////////////////////////////////////////////////////////////////////// - // Tests to see whether this port can receive without blocking. If Ok is - // returned, then that's the answer. If Err is returned, then the returned - // port needs to be queried instead (an upgrade happened) - pub fn can_recv(&self) -> Result<bool, Receiver<T>> { - // We peek at the queue to see if there's anything on it, and we use - // this return value to determine if we should pop from the queue and - // upgrade this channel immediately. If it looks like we've got an - // upgrade pending, then go through the whole recv rigamarole to update - // the internal state. - match self.queue.peek() { - Some(&mut GoUp(..)) => { - match self.recv(None) { - Err(Upgraded(port)) => Err(port), - _ => unreachable!(), - } - } - Some(..) => Ok(true), - None => Ok(false) - } - } - // increment the count on the channel (used for selection) fn bump(&self, amt: isize) -> isize { match self.queue.producer_addition().cnt.fetch_add(amt, Ordering::SeqCst) { @@ -370,31 +342,6 @@ impl<T> Packet<T> { } } - // Attempts to start selecting on this port. Like a oneshot, this can fail - // immediately because of an upgrade. - pub fn start_selection(&self, token: SignalToken) -> SelectionResult<T> { - match self.decrement(token) { - Ok(()) => SelSuccess, - Err(token) => { - let ret = match self.queue.peek() { - Some(&mut GoUp(..)) => { - match self.queue.pop() { - Some(GoUp(port)) => SelUpgraded(token, port), - _ => unreachable!(), - } - } - Some(..) => SelCanceled, - None => SelCanceled, - }; - // Undo our decrement above, and we should be guaranteed that the - // previous value is positive because we're not going to sleep - let prev = self.bump(1); - assert!(prev == DISCONNECTED || prev >= 0); - ret - } - } - } - // Removes a previous thread from being blocked in this port pub fn abort_selection(&self, was_upgrade: bool) -> Result<bool, Receiver<T>> { diff --git a/src/libstd/sync/mpsc/sync.rs b/src/libstd/sync/mpsc/sync.rs index 485234a9495..58a4b716afb 100644 --- a/src/libstd/sync/mpsc/sync.rs +++ b/src/libstd/sync/mpsc/sync.rs @@ -33,7 +33,6 @@ use core::ptr; use crate::sync::atomic::{Ordering, AtomicUsize}; use crate::sync::mpsc::blocking::{self, WaitToken, SignalToken}; -use crate::sync::mpsc::select::StartResult::{self, Installed, Abort}; use crate::sync::{Mutex, MutexGuard}; use crate::time::Instant; @@ -141,7 +140,7 @@ fn wait_timeout_receiver<'a, 'b, T>(lock: &'a Mutex<State<T>>, new_guard } -fn abort_selection<'a, T>(guard: &mut MutexGuard<'a , State<T>>) -> bool { +fn abort_selection<T>(guard: &mut MutexGuard<'_, State<T>>) -> bool { match mem::replace(&mut guard.blocker, NoneBlocked) { NoneBlocked => true, BlockedSender(token) => { @@ -153,7 +152,7 @@ fn abort_selection<'a, T>(guard: &mut MutexGuard<'a , State<T>>) -> bool { } /// Wakes up a thread, dropping the lock at the correct time -fn wakeup<T>(token: SignalToken, guard: MutexGuard<State<T>>) { +fn wakeup<T>(token: SignalToken, guard: MutexGuard<'_, State<T>>) { // We need to be careful to wake up the waiting thread *outside* of the mutex // in case it incurs a context switch. drop(guard); @@ -161,20 +160,20 @@ fn wakeup<T>(token: SignalToken, guard: MutexGuard<State<T>>) { } impl<T> Packet<T> { - pub fn new(cap: usize) -> Packet<T> { + pub fn new(capacity: usize) -> Packet<T> { Packet { channels: AtomicUsize::new(1), lock: Mutex::new(State { disconnected: false, blocker: NoneBlocked, - cap, + cap: capacity, canceled: None, queue: Queue { head: ptr::null_mut(), tail: ptr::null_mut(), }, buf: Buffer { - buf: (0..cap + if cap == 0 {1} else {0}).map(|_| None).collect(), + buf: (0..capacity + if capacity == 0 {1} else {0}).map(|_| None).collect(), start: 0, size: 0, }, @@ -184,12 +183,12 @@ impl<T> Packet<T> { // wait until a send slot is available, returning locked access to // the channel state. - fn acquire_send_slot(&self) -> MutexGuard<State<T>> { + fn acquire_send_slot(&self) -> MutexGuard<'_, State<T>> { let mut node = Node { token: None, next: ptr::null_mut() }; loop { let mut guard = self.lock.lock().unwrap(); // are we ready to go? - if guard.disconnected || guard.buf.size() < guard.buf.cap() { + if guard.disconnected || guard.buf.size() < guard.buf.capacity() { return guard; } // no room; actually block @@ -231,7 +230,7 @@ impl<T> Packet<T> { let mut guard = self.lock.lock().unwrap(); if guard.disconnected { Err(super::TrySendError::Disconnected(t)) - } else if guard.buf.size() == guard.buf.cap() { + } else if guard.buf.size() == guard.buf.capacity() { Err(super::TrySendError::Full(t)) } else if guard.cap == 0 { // With capacity 0, even though we have buffer space we can't @@ -249,7 +248,7 @@ impl<T> Packet<T> { // If the buffer has some space and the capacity isn't 0, then we // just enqueue the data for later retrieval, ensuring to wake up // any blocked receiver if there is one. - assert!(guard.buf.size() < guard.buf.cap()); + assert!(guard.buf.size() < guard.buf.capacity()); guard.buf.enqueue(t); match mem::replace(&mut guard.blocker, NoneBlocked) { BlockedReceiver(token) => wakeup(token, guard), @@ -316,7 +315,7 @@ impl<T> Packet<T> { // * `waited` - flag if the receiver blocked to receive some data, or if it // just picked up some data on the way out // * `guard` - the lock guard that is held over this channel's lock - fn wakeup_senders(&self, waited: bool, mut guard: MutexGuard<State<T>>) { + fn wakeup_senders(&self, waited: bool, mut guard: MutexGuard<'_, State<T>>) { let pending_sender1: Option<SignalToken> = guard.queue.dequeue(); // If this is a no-buffer channel (cap == 0), then if we didn't wait we @@ -384,7 +383,7 @@ impl<T> Packet<T> { // needs to be careful to destroy the data *outside* of the lock to // prevent deadlock. let _data = if guard.cap != 0 { - mem::replace(&mut guard.buf.buf, Vec::new()) + mem::take(&mut guard.buf.buf) } else { Vec::new() }; @@ -406,42 +405,6 @@ impl<T> Packet<T> { while let Some(token) = queue.dequeue() { token.signal(); } waiter.map(|t| t.signal()); } - - //////////////////////////////////////////////////////////////////////////// - // select implementation - //////////////////////////////////////////////////////////////////////////// - - // If Ok, the value is whether this port has data, if Err, then the upgraded - // port needs to be checked instead of this one. - pub fn can_recv(&self) -> bool { - let guard = self.lock.lock().unwrap(); - guard.disconnected || guard.buf.size() > 0 - } - - // Attempts to start selection on this port. This can either succeed or fail - // because there is data waiting. - pub fn start_selection(&self, token: SignalToken) -> StartResult { - let mut guard = self.lock.lock().unwrap(); - if guard.disconnected || guard.buf.size() > 0 { - Abort - } else { - match mem::replace(&mut guard.blocker, BlockedReceiver(token)) { - NoneBlocked => {} - BlockedSender(..) => unreachable!(), - BlockedReceiver(..) => unreachable!(), - } - Installed - } - } - - // Remove a previous selecting thread from this port. This ensures that the - // blocked thread will no longer be visible to any other threads. - // - // The return value indicates whether there's data on this port. - pub fn abort_selection(&self) -> bool { - let mut guard = self.lock.lock().unwrap(); - abort_selection(&mut guard) - } } impl<T> Drop for Packet<T> { @@ -475,7 +438,7 @@ impl<T> Buffer<T> { } fn size(&self) -> usize { self.size } - fn cap(&self) -> usize { self.buf.len() } + fn capacity(&self) -> usize { self.buf.len() } } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 6b812e65b72..87c2318a937 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -215,7 +215,7 @@ impl<T: ?Sized> Mutex<T> { /// assert_eq!(*mutex.lock().unwrap(), 10); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn lock(&self) -> LockResult<MutexGuard<T>> { + pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> { unsafe { self.inner.raw_lock(); MutexGuard::new(self) @@ -258,7 +258,7 @@ impl<T: ?Sized> Mutex<T> { /// assert_eq!(*mutex.lock().unwrap(), 10); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> { + pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> { unsafe { if self.inner.try_lock() { Ok(MutexGuard::new(self)?) @@ -376,6 +376,8 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Mutex<T> { impl<T> From<T> for Mutex<T> { /// Creates a new mutex in an unlocked state ready for use. /// This is equivalent to [`Mutex::new`]. + /// + /// [`Mutex::new`]: ../../std/sync/struct.Mutex.html#method.new fn from(t: T) -> Self { Mutex::new(t) } @@ -391,7 +393,7 @@ impl<T: ?Sized + Default> Default for Mutex<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.try_lock() { Ok(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(), Err(TryLockError::Poisoned(err)) => { @@ -400,7 +402,9 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> { Err(TryLockError::WouldBlock) => { struct LockedPlaceholder; impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("<locked>") } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("<locked>") + } } f.debug_struct("Mutex").field("data", &LockedPlaceholder).finish() @@ -449,14 +453,14 @@ impl<T: ?Sized> Drop for MutexGuard<'_, T> { #[stable(feature = "std_debug", since = "1.16.0")] impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } #[stable(feature = "std_guard_impls", since = "1.20.0")] impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index a036c266662..e529b8c4227 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -115,6 +115,11 @@ pub struct OnceState { /// static START: Once = ONCE_INIT; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "1.38.0", + reason = "the `new` function is now preferred", + suggestion = "Once::new()", +)] pub const ONCE_INIT: Once = Once::new(); // Four states that a Once can be in, encoded into the lower bits of `state` in @@ -431,7 +436,7 @@ impl Once { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Once { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Once { .. }") } } diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index 0be83c76d62..b1b56f321fc 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -180,7 +180,7 @@ impl<T: ?Sized> RwLock<T> { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn read(&self) -> LockResult<RwLockReadGuard<T>> { + pub fn read(&self) -> LockResult<RwLockReadGuard<'_, T>> { unsafe { self.inner.read(); RwLockReadGuard::new(self) @@ -219,7 +219,7 @@ impl<T: ?Sized> RwLock<T> { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<T>> { + pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<'_, T>> { unsafe { if self.inner.try_read() { Ok(RwLockReadGuard::new(self)?) @@ -262,7 +262,7 @@ impl<T: ?Sized> RwLock<T> { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> { + pub fn write(&self) -> LockResult<RwLockWriteGuard<'_, T>> { unsafe { self.inner.write(); RwLockWriteGuard::new(self) @@ -301,7 +301,7 @@ impl<T: ?Sized> RwLock<T> { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<T>> { + pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<'_, T>> { unsafe { if self.inner.try_write() { Ok(RwLockWriteGuard::new(self)?) @@ -421,7 +421,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for RwLock<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.try_read() { Ok(guard) => f.debug_struct("RwLock").field("data", &&*guard).finish(), Err(TryLockError::Poisoned(err)) => { @@ -430,7 +430,9 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> { Err(TryLockError::WouldBlock) => { struct LockedPlaceholder; impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("<locked>") } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("<locked>") + } } f.debug_struct("RwLock").field("data", &LockedPlaceholder).finish() @@ -451,6 +453,8 @@ impl<T: Default> Default for RwLock<T> { impl<T> From<T> for RwLock<T> { /// Creates a new instance of an `RwLock<T>` which is unlocked. /// This is equivalent to [`RwLock::new`]. + /// + /// [`RwLock::new`]: ../../std/sync/struct.RwLock.html#method.new fn from(t: T) -> Self { RwLock::new(t) } @@ -481,7 +485,7 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { #[stable(feature = "std_debug", since = "1.16.0")] impl<T: fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RwLockReadGuard") .field("lock", &self.__lock) .finish() @@ -490,14 +494,14 @@ impl<T: fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> { #[stable(feature = "std_guard_impls", since = "1.20.0")] impl<T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } #[stable(feature = "std_debug", since = "1.16.0")] impl<T: fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RwLockWriteGuard") .field("lock", &self.__lock) .finish() @@ -506,7 +510,7 @@ impl<T: fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> { #[stable(feature = "std_guard_impls", since = "1.20.0")] impl<T: ?Sized + fmt::Display> fmt::Display for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } diff --git a/src/libstd/sys/cloudabi/abi/cloudabi.rs b/src/libstd/sys/cloudabi/abi/cloudabi.rs index 2307e2167c5..9addba8b611 100644 --- a/src/libstd/sys/cloudabi/abi/cloudabi.rs +++ b/src/libstd/sys/cloudabi/abi/cloudabi.rs @@ -1884,7 +1884,7 @@ pub unsafe fn clock_res_get(clock_id_: clockid, resolution_: &mut timestamp) -> /// **time**: /// The time value of the clock. #[inline] -pub unsafe fn clock_time_get(clock_id_: clockid, precision_: timestamp, time_: &mut timestamp) -> errno { +pub unsafe fn clock_time_get(clock_id_: clockid, precision_: timestamp, time_: *mut timestamp) -> errno { cloudabi_sys_clock_time_get(clock_id_, precision_, time_) } @@ -2643,7 +2643,7 @@ pub unsafe fn mem_unmap(mapping_: &mut [u8]) -> errno { /// **nevents**: /// The number of events stored. #[inline] -pub unsafe fn poll(in_: *const subscription, out_: *mut event, nsubscriptions_: usize, nevents_: &mut usize) -> errno { +pub unsafe fn poll(in_: *const subscription, out_: *mut event, nsubscriptions_: usize, nevents_: *mut usize) -> errno { cloudabi_sys_poll(in_, out_, nsubscriptions_, nevents_) } diff --git a/src/libstd/sys/cloudabi/backtrace.rs b/src/libstd/sys/cloudabi/backtrace.rs deleted file mode 100644 index a15d2238e55..00000000000 --- a/src/libstd/sys/cloudabi/backtrace.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::error::Error; -use crate::ffi::CStr; -use crate::fmt; -use crate::intrinsics; -use crate::io; -use crate::sys_common::backtrace::Frame; - -use unwind as uw; - -pub struct BacktraceContext; - -struct Context<'a> { - idx: usize, - frames: &'a mut [Frame], -} - -#[derive(Debug)] -struct UnwindError(uw::_Unwind_Reason_Code); - -impl Error for UnwindError { - fn description(&self) -> &'static str { - "unexpected return value while unwinding" - } -} - -impl fmt::Display for UnwindError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {:?}", self.description(), self.0) - } -} - -#[inline(never)] // if we know this is a function call, we can skip it when - // tracing -pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> { - let mut cx = Context { idx: 0, frames }; - let result_unwind = - unsafe { uw::_Unwind_Backtrace(trace_fn, &mut cx as *mut Context as *mut libc::c_void) }; - // See libunwind:src/unwind/Backtrace.c for the return values. - // No, there is no doc. - match result_unwind { - // These return codes seem to be benign and need to be ignored for backtraces - // to show up properly on all tested platforms. - uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => { - Ok((cx.idx, BacktraceContext)) - } - _ => Err(io::Error::new( - io::ErrorKind::Other, - UnwindError(result_unwind), - )), - } -} - -extern "C" fn trace_fn( - ctx: *mut uw::_Unwind_Context, - arg: *mut libc::c_void, -) -> uw::_Unwind_Reason_Code { - let cx = unsafe { &mut *(arg as *mut Context) }; - if cx.idx >= cx.frames.len() { - return uw::_URC_NORMAL_STOP; - } - - let mut ip_before_insn = 0; - let mut ip = unsafe { uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void }; - if !ip.is_null() && ip_before_insn == 0 { - // this is a non-signaling frame, so `ip` refers to the address - // after the calling instruction. account for that. - ip = (ip as usize - 1) as *mut _; - } - - let symaddr = unsafe { uw::_Unwind_FindEnclosingFunction(ip) }; - cx.frames[cx.idx] = Frame { - symbol_addr: symaddr as *mut u8, - exact_position: ip as *mut u8, - inline_context: 0, - }; - cx.idx += 1; - - uw::_URC_NO_REASON -} - -pub fn foreach_symbol_fileline<F>(_: Frame, _: F, _: &BacktraceContext) -> io::Result<bool> -where - F: FnMut(&[u8], u32) -> io::Result<()>, -{ - // No way to obtain this information on CloudABI. - Ok(false) -} - -pub fn resolve_symname<F>(frame: Frame, callback: F, _: &BacktraceContext) -> io::Result<()> -where - F: FnOnce(Option<&str>) -> io::Result<()>, -{ - unsafe { - let mut info: Dl_info = intrinsics::init(); - let symname = - if dladdr(frame.exact_position as *mut _, &mut info) == 0 || info.dli_sname.is_null() { - None - } else { - CStr::from_ptr(info.dli_sname).to_str().ok() - }; - callback(symname) - } -} - -#[repr(C)] -struct Dl_info { - dli_fname: *const libc::c_char, - dli_fbase: *mut libc::c_void, - dli_sname: *const libc::c_char, - dli_saddr: *mut libc::c_void, -} - -extern "C" { - fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int; -} diff --git a/src/libstd/sys/cloudabi/condvar.rs b/src/libstd/sys/cloudabi/condvar.rs index 7aa0b0b6f49..ec1fca7805a 100644 --- a/src/libstd/sys/cloudabi/condvar.rs +++ b/src/libstd/sys/cloudabi/condvar.rs @@ -79,16 +79,21 @@ impl Condvar { }, ..mem::zeroed() }; - let mut event: abi::event = mem::uninitialized(); - let mut nevents: usize = mem::uninitialized(); - let ret = abi::poll(&subscription, &mut event, 1, &mut nevents); + let mut event: mem::MaybeUninit<abi::event> = mem::MaybeUninit::uninit(); + let mut nevents: mem::MaybeUninit<usize> = mem::MaybeUninit::uninit(); + let ret = abi::poll( + &subscription, + event.as_mut_ptr(), + 1, + nevents.as_mut_ptr() + ); assert_eq!( ret, abi::errno::SUCCESS, "Failed to wait on condition variable" ); assert_eq!( - event.error, + event.assume_init().error, abi::errno::SUCCESS, "Failed to wait on condition variable" ); @@ -131,21 +136,27 @@ impl Condvar { ..mem::zeroed() }, ]; - let mut events: [abi::event; 2] = mem::uninitialized(); - let mut nevents: usize = mem::uninitialized(); - let ret = abi::poll(subscriptions.as_ptr(), events.as_mut_ptr(), 2, &mut nevents); + let mut events: [mem::MaybeUninit<abi::event>; 2] = [mem::MaybeUninit::uninit(); 2]; + let mut nevents: mem::MaybeUninit<usize> = mem::MaybeUninit::uninit(); + let ret = abi::poll( + subscriptions.as_ptr(), + mem::MaybeUninit::first_ptr_mut(&mut events), + 2, + nevents.as_mut_ptr() + ); assert_eq!( ret, abi::errno::SUCCESS, "Failed to wait on condition variable" ); + let nevents = nevents.assume_init(); for i in 0..nevents { assert_eq!( - events[i].error, + events[i].assume_init().error, abi::errno::SUCCESS, "Failed to wait on condition variable" ); - if events[i].type_ == abi::eventtype::CONDVAR { + if events[i].assume_init().type_ == abi::eventtype::CONDVAR { return true; } } diff --git a/src/libstd/sys/cloudabi/io.rs b/src/libstd/sys/cloudabi/io.rs index 8b02d3fd19d..976e122463d 100644 --- a/src/libstd/sys/cloudabi/io.rs +++ b/src/libstd/sys/cloudabi/io.rs @@ -1,9 +1,16 @@ -pub struct IoVec<'a>(&'a [u8]); +use crate::mem; + +pub struct IoSlice<'a>(&'a [u8]); + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice(buf) + } -impl<'a> IoVec<'a> { #[inline] - pub fn new(buf: &'a [u8]) -> IoVec<'a> { - IoVec(buf) + pub fn advance(&mut self, n: usize) { + self.0 = &self.0[n..] } #[inline] @@ -12,12 +19,19 @@ impl<'a> IoVec<'a> { } } -pub struct IoVecMut<'a>(&'a mut [u8]); +pub struct IoSliceMut<'a>(&'a mut [u8]); + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut(buf) + } -impl<'a> IoVecMut<'a> { #[inline] - pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> { - IoVecMut(buf) + pub fn advance(&mut self, n: usize) { + let slice = mem::replace(&mut self.0, &mut []); + let (_, remaining) = slice.split_at_mut(n); + self.0 = remaining; } #[inline] diff --git a/src/libstd/sys/cloudabi/mod.rs b/src/libstd/sys/cloudabi/mod.rs index 47c90fddd86..6e147612eb4 100644 --- a/src/libstd/sys/cloudabi/mod.rs +++ b/src/libstd/sys/cloudabi/mod.rs @@ -1,11 +1,11 @@ +#![allow(deprecated_in_future)] // mem::uninitialized; becomes `deprecated` when nightly is 1.39 + use crate::io::ErrorKind; use crate::mem; #[path = "../unix/alloc.rs"] pub mod alloc; pub mod args; -#[cfg(feature = "backtrace")] -pub mod backtrace; #[path = "../unix/cmath.rs"] pub mod cmath; pub mod condvar; @@ -14,8 +14,6 @@ pub mod io; pub mod memchr; pub mod mutex; pub mod os; -#[path = "../unix/os_str.rs"] -pub mod os_str; pub mod rwlock; pub mod stack_overflow; pub mod stdio; @@ -24,6 +22,8 @@ pub mod thread; pub mod thread_local; pub mod time; +pub use crate::sys_common::os_str_bytes as os_str; + mod abi; mod shims; @@ -61,8 +61,11 @@ pub use libc::strlen; pub fn hashmap_random_keys() -> (u64, u64) { unsafe { - let mut v = mem::uninitialized(); - libc::arc4random_buf(&mut v as *mut _ as *mut libc::c_void, mem::size_of_val(&v)); - v + let mut v: mem::MaybeUninit<(u64, u64)> = mem::MaybeUninit::uninit(); + libc::arc4random_buf( + v.as_mut_ptr() as *mut libc::c_void, + mem::size_of_val(&v) + ); + v.assume_init() } } diff --git a/src/libstd/sys/cloudabi/mutex.rs b/src/libstd/sys/cloudabi/mutex.rs index 5e191e31d5f..d3ff0077b20 100644 --- a/src/libstd/sys/cloudabi/mutex.rs +++ b/src/libstd/sys/cloudabi/mutex.rs @@ -1,5 +1,6 @@ use crate::cell::UnsafeCell; use crate::mem; +use crate::mem::MaybeUninit; use crate::sync::atomic::{AtomicU32, Ordering}; use crate::sys::cloudabi::abi; use crate::sys::rwlock::{self, RWLock}; @@ -47,24 +48,27 @@ impl Mutex { } pub struct ReentrantMutex { - lock: UnsafeCell<AtomicU32>, - recursion: UnsafeCell<u32>, + lock: UnsafeCell<MaybeUninit<AtomicU32>>, + recursion: UnsafeCell<MaybeUninit<u32>>, } impl ReentrantMutex { pub unsafe fn uninitialized() -> ReentrantMutex { - mem::uninitialized() + ReentrantMutex { + lock: UnsafeCell::new(MaybeUninit::uninit()), + recursion: UnsafeCell::new(MaybeUninit::uninit()) + } } pub unsafe fn init(&mut self) { - self.lock = UnsafeCell::new(AtomicU32::new(abi::LOCK_UNLOCKED.0)); - self.recursion = UnsafeCell::new(0); + self.lock = UnsafeCell::new(MaybeUninit::new(AtomicU32::new(abi::LOCK_UNLOCKED.0))); + self.recursion = UnsafeCell::new(MaybeUninit::new(0)); } pub unsafe fn try_lock(&self) -> bool { // Attempt to acquire the lock. - let lock = self.lock.get(); - let recursion = self.recursion.get(); + let lock = (*self.lock.get()).as_mut_ptr(); + let recursion = (*self.recursion.get()).as_mut_ptr(); if let Err(old) = (*lock).compare_exchange( abi::LOCK_UNLOCKED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, @@ -109,8 +113,8 @@ impl ReentrantMutex { } pub unsafe fn unlock(&self) { - let lock = self.lock.get(); - let recursion = self.recursion.get(); + let lock = (*self.lock.get()).as_mut_ptr(); + let recursion = (*self.recursion.get()).as_mut_ptr(); assert_eq!( (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, @@ -136,8 +140,8 @@ impl ReentrantMutex { } pub unsafe fn destroy(&self) { - let lock = self.lock.get(); - let recursion = self.recursion.get(); + let lock = (*self.lock.get()).as_mut_ptr(); + let recursion = (*self.recursion.get()).as_mut_ptr(); assert_eq!( (*lock).load(Ordering::Relaxed), abi::LOCK_UNLOCKED.0, diff --git a/src/libstd/sys/cloudabi/shims/fs.rs b/src/libstd/sys/cloudabi/shims/fs.rs index 56667bef007..05f91541011 100644 --- a/src/libstd/sys/cloudabi/shims/fs.rs +++ b/src/libstd/sys/cloudabi/shims/fs.rs @@ -1,7 +1,7 @@ use crate::ffi::OsString; use crate::fmt; use crate::hash::{Hash, Hasher}; -use crate::io::{self, SeekFrom}; +use crate::io::{self, SeekFrom, IoSlice, IoSliceMut}; use crate::path::{Path, PathBuf}; use crate::sys::time::SystemTime; use crate::sys::{unsupported, Void}; @@ -81,7 +81,7 @@ impl PartialEq for FilePermissions { impl Eq for FilePermissions {} impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -123,13 +123,13 @@ impl Hash for FileType { } impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -198,10 +198,18 @@ impl File { match self.0 {} } + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { match self.0 {} } + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn flush(&self) -> io::Result<()> { match self.0 {} } @@ -234,7 +242,7 @@ impl DirBuilder { } impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } diff --git a/src/libstd/sys/cloudabi/shims/net.rs b/src/libstd/sys/cloudabi/shims/net.rs index 6d2a4962ab4..8d609cdfad5 100644 --- a/src/libstd/sys/cloudabi/shims/net.rs +++ b/src/libstd/sys/cloudabi/shims/net.rs @@ -1,5 +1,5 @@ use crate::fmt; -use crate::io::{self, IoVec, IoVecMut}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::time::Duration; use crate::sys::{unsupported, Void}; @@ -43,7 +43,7 @@ impl TcpStream { match self.0 {} } - pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> { + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { match self.0 {} } @@ -51,7 +51,7 @@ impl TcpStream { match self.0 {} } - pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> { + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { match self.0 {} } @@ -97,7 +97,7 @@ impl TcpStream { } impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -147,7 +147,7 @@ impl TcpListener { } impl fmt::Debug for TcpListener { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -159,6 +159,10 @@ impl UdpSocket { unsupported() } + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + match self.0 {} + } + pub fn socket_addr(&self) -> io::Result<SocketAddr> { match self.0 {} } @@ -277,7 +281,7 @@ impl UdpSocket { } impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } diff --git a/src/libstd/sys/cloudabi/shims/os.rs b/src/libstd/sys/cloudabi/shims/os.rs index 0c4690e12b0..944b9525b3b 100644 --- a/src/libstd/sys/cloudabi/shims/os.rs +++ b/src/libstd/sys/cloudabi/shims/os.rs @@ -34,7 +34,7 @@ pub fn unsetenv(_: &OsStr) -> io::Result<()> { pub struct SplitPaths<'a>(&'a Void); -pub fn split_paths(_unparsed: &OsStr) -> SplitPaths { +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") } @@ -57,7 +57,7 @@ where } impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "not supported on CloudABI yet".fmt(f) } } diff --git a/src/libstd/sys/cloudabi/shims/pipe.rs b/src/libstd/sys/cloudabi/shims/pipe.rs index f3debb95047..fb14dc59101 100644 --- a/src/libstd/sys/cloudabi/shims/pipe.rs +++ b/src/libstd/sys/cloudabi/shims/pipe.rs @@ -1,4 +1,4 @@ -use crate::io; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::sys::Void; pub struct AnonPipe(Void); @@ -8,10 +8,18 @@ impl AnonPipe { match self.0 {} } + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { match self.0 {} } + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn diverge(&self) -> ! { match self.0 {} } diff --git a/src/libstd/sys/cloudabi/shims/process.rs b/src/libstd/sys/cloudabi/shims/process.rs index 710c42c1149..e719b362cbf 100644 --- a/src/libstd/sys/cloudabi/shims/process.rs +++ b/src/libstd/sys/cloudabi/shims/process.rs @@ -71,7 +71,7 @@ impl From<File> for Stdio { } impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { Ok(()) } } @@ -105,13 +105,13 @@ impl PartialEq for ExitStatus { impl Eq for ExitStatus {} impl fmt::Debug for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } impl fmt::Display for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } diff --git a/src/libstd/sys/cloudabi/thread.rs b/src/libstd/sys/cloudabi/thread.rs index f853346e0e6..7da16c4d247 100644 --- a/src/libstd/sys/cloudabi/thread.rs +++ b/src/libstd/sys/cloudabi/thread.rs @@ -1,4 +1,3 @@ -use crate::boxed::FnBox; use crate::cmp; use crate::ffi::CStr; use crate::io; @@ -22,7 +21,7 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { + pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { let p = box p; let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); diff --git a/src/libstd/sys/cloudabi/time.rs b/src/libstd/sys/cloudabi/time.rs index d7502c61eff..5e502dcb2ba 100644 --- a/src/libstd/sys/cloudabi/time.rs +++ b/src/libstd/sys/cloudabi/time.rs @@ -18,10 +18,10 @@ pub fn checked_dur2intervals(dur: &Duration) -> Option<abi::timestamp> { impl Instant { pub fn now() -> Instant { unsafe { - let mut t = mem::uninitialized(); - let ret = abi::clock_time_get(abi::clockid::MONOTONIC, 0, &mut t); + let mut t: mem::MaybeUninit<abi::timestamp> = mem::MaybeUninit::uninit(); + let ret = abi::clock_time_get(abi::clockid::MONOTONIC, 0, t.as_mut_ptr()); assert_eq!(ret, abi::errno::SUCCESS); - Instant { t } + Instant { t: t.assume_init() } } } @@ -33,11 +33,9 @@ impl Instant { Instant { t: 0 } } - pub fn sub_instant(&self, other: &Instant) -> Duration { - let diff = self.t - .checked_sub(other.t) - .expect("second instant is later than self"); - Duration::new(diff / NSEC_PER_SEC, (diff % NSEC_PER_SEC) as u32) + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + let diff = self.t.checked_sub(other.t)?; + Some(Duration::new(diff / NSEC_PER_SEC, (diff % NSEC_PER_SEC) as u32)) } pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { @@ -61,10 +59,10 @@ pub struct SystemTime { impl SystemTime { pub fn now() -> SystemTime { unsafe { - let mut t = mem::uninitialized(); - let ret = abi::clock_time_get(abi::clockid::REALTIME, 0, &mut t); + let mut t: mem::MaybeUninit<abi::timestamp> = mem::MaybeUninit::uninit(); + let ret = abi::clock_time_get(abi::clockid::REALTIME, 0, t.as_mut_ptr()); assert_eq!(ret, abi::errno::SUCCESS); - SystemTime { t } + SystemTime { t: t.assume_init() } } } diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs index 5ba9304c09b..5a5859a6ad8 100644 --- a/src/libstd/sys/mod.rs +++ b/src/libstd/sys/mod.rs @@ -22,8 +22,11 @@ #![allow(missing_debug_implementations)] -cfg_if! { - if #[cfg(unix)] { +cfg_if::cfg_if! { + if #[cfg(target_os = "vxworks")] { + mod vxworks; + pub use self::vxworks::*; + } else if #[cfg(unix)] { mod unix; pub use self::unix::*; } else if #[cfg(windows)] { @@ -32,9 +35,9 @@ cfg_if! { } else if #[cfg(target_os = "cloudabi")] { mod cloudabi; pub use self::cloudabi::*; - } else if #[cfg(target_os = "redox")] { - mod redox; - pub use self::redox::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use self::wasi::*; } else if #[cfg(target_arch = "wasm32")] { mod wasm; pub use self::wasm::*; @@ -51,8 +54,8 @@ cfg_if! { // Windows when we're compiling for Linux. #[cfg(rustdoc)] -cfg_if! { - if #[cfg(any(unix, target_os = "redox"))] { +cfg_if::cfg_if! { + if #[cfg(unix)] { // On unix we'll document what's already available #[stable(feature = "rust1", since = "1.0.0")] pub use self::ext as unix_ext; @@ -74,7 +77,7 @@ cfg_if! { } #[cfg(rustdoc)] -cfg_if! { +cfg_if::cfg_if! { if #[cfg(windows)] { // On windows we'll just be documenting what's already available #[allow(missing_docs)] diff --git a/src/libstd/sys/redox/backtrace/mod.rs b/src/libstd/sys/redox/backtrace/mod.rs deleted file mode 100644 index 8ea2783580a..00000000000 --- a/src/libstd/sys/redox/backtrace/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -/// See sys/unix/backtrace/mod.rs for an explanation of the method used here. - -pub use self::tracing::unwind_backtrace; -pub use self::printing::{foreach_symbol_fileline, resolve_symname}; - -// tracing impls: -mod tracing; -// symbol resolvers: -mod printing; - -pub mod gnu { - use crate::io; - use crate::fs; - use crate::vec::Vec; - use crate::ffi::OsStr; - use crate::os::unix::ffi::OsStrExt; - use crate::io::Read; - use libc::c_char; - - pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> { - let mut exefile = fs::File::open("sys:exe")?; - let mut exename = Vec::new(); - exefile.read_to_end(&mut exename)?; - if exename.last() == Some(&b'\n') { - exename.pop(); - } - let file = fs::File::open(OsStr::from_bytes(&exename))?; - Ok((exename.into_iter().map(|c| c as c_char).collect(), file)) - } -} - -pub struct BacktraceContext; diff --git a/src/libstd/sys/redox/backtrace/printing.rs b/src/libstd/sys/redox/backtrace/printing.rs deleted file mode 100644 index 489eed4562d..00000000000 --- a/src/libstd/sys/redox/backtrace/printing.rs +++ /dev/null @@ -1 +0,0 @@ -pub use crate::sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname}; diff --git a/src/libstd/sys/redox/backtrace/tracing.rs b/src/libstd/sys/redox/backtrace/tracing.rs deleted file mode 100644 index e7a68eadbde..00000000000 --- a/src/libstd/sys/redox/backtrace/tracing.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::error::Error; -use crate::fmt; -use crate::io; -use crate::sys::backtrace::BacktraceContext; -use crate::sys_common::backtrace::Frame; - -use unwind as uw; - -struct Context<'a> { - idx: usize, - frames: &'a mut [Frame], -} - -#[derive(Debug)] -struct UnwindError(uw::_Unwind_Reason_Code); - -impl Error for UnwindError { - fn description(&self) -> &'static str { - "unexpected return value while unwinding" - } -} - -impl fmt::Display for UnwindError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {:?}", self.description(), self.0) - } -} - -#[inline(never)] // if we know this is a function call, we can skip it when - // tracing -pub fn unwind_backtrace(frames: &mut [Frame]) - -> io::Result<(usize, BacktraceContext)> -{ - let mut cx = Context { - idx: 0, - frames: frames, - }; - let result_unwind = unsafe { - uw::_Unwind_Backtrace(trace_fn, - &mut cx as *mut Context - as *mut libc::c_void) - }; - // See libunwind:src/unwind/Backtrace.c for the return values. - // No, there is no doc. - match result_unwind { - // These return codes seem to be benign and need to be ignored for backtraces - // to show up properly on all tested platforms. - uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => { - Ok((cx.idx, BacktraceContext)) - } - _ => { - Err(io::Error::new(io::ErrorKind::Other, - UnwindError(result_unwind))) - } - } -} - -extern fn trace_fn(ctx: *mut uw::_Unwind_Context, - arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { - let cx = unsafe { &mut *(arg as *mut Context) }; - if cx.idx >= cx.frames.len() { - return uw::_URC_NORMAL_STOP; - } - - let mut ip_before_insn = 0; - let mut ip = unsafe { - uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void - }; - if !ip.is_null() && ip_before_insn == 0 { - // this is a non-signaling frame, so `ip` refers to the address - // after the calling instruction. account for that. - ip = (ip as usize - 1) as *mut _; - } - - // dladdr() on osx gets whiny when we use FindEnclosingFunction, and - // it appears to work fine without it, so we only use - // FindEnclosingFunction on non-osx platforms. In doing so, we get a - // slightly more accurate stack trace in the process. - // - // This is often because panic involves the last instruction of a - // function being "call std::rt::begin_unwind", with no ret - // instructions after it. This means that the return instruction - // pointer points *outside* of the calling function, and by - // unwinding it we go back to the original function. - let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { - ip - } else { - unsafe { uw::_Unwind_FindEnclosingFunction(ip) } - }; - - cx.frames[cx.idx] = Frame { - symbol_addr: symaddr as *mut u8, - exact_position: ip as *mut u8, - inline_context: 0, - }; - cx.idx += 1; - - uw::_URC_NO_REASON -} diff --git a/src/libstd/sys/redox/condvar.rs b/src/libstd/sys/redox/condvar.rs deleted file mode 100644 index a6365cac23e..00000000000 --- a/src/libstd/sys/redox/condvar.rs +++ /dev/null @@ -1,111 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::intrinsics::{atomic_cxchg, atomic_load, atomic_xadd, atomic_xchg}; -use crate::ptr; -use crate::time::Duration; - -use crate::sys::mutex::{mutex_unlock, Mutex}; -use crate::sys::syscall::{futex, TimeSpec, FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE}; - -pub struct Condvar { - lock: UnsafeCell<*mut i32>, - seq: UnsafeCell<i32> -} - -impl Condvar { - pub const fn new() -> Condvar { - Condvar { - lock: UnsafeCell::new(ptr::null_mut()), - seq: UnsafeCell::new(0) - } - } - - #[inline] - pub unsafe fn init(&self) { - *self.lock.get() = ptr::null_mut(); - *self.seq.get() = 0; - } - - #[inline] - pub fn notify_one(&self) { - unsafe { - let seq = self.seq.get(); - - atomic_xadd(seq, 1); - - let _ = futex(seq, FUTEX_WAKE, 1, 0, ptr::null_mut()); - } - } - - #[inline] - pub fn notify_all(&self) { - unsafe { - let lock = self.lock.get(); - let seq = self.seq.get(); - - if *lock == ptr::null_mut() { - return; - } - - atomic_xadd(seq, 1); - - let _ = futex(seq, FUTEX_REQUEUE, 1, crate::usize::MAX, *lock); - } - } - - #[inline] - unsafe fn wait_inner(&self, mutex: &Mutex, timeout_ptr: *const TimeSpec) -> bool { - let lock = self.lock.get(); - let seq = self.seq.get(); - - if *lock != mutex.lock.get() { - if *lock != ptr::null_mut() { - panic!("Condvar used with more than one Mutex"); - } - - atomic_cxchg(lock as *mut usize, 0, mutex.lock.get() as usize); - } - - mutex_unlock(*lock); - - let seq_before = atomic_load(seq); - - let _ = futex(seq, FUTEX_WAIT, seq_before, timeout_ptr as usize, ptr::null_mut()); - - let seq_after = atomic_load(seq); - - while atomic_xchg(*lock, 2) != 0 { - let _ = futex(*lock, FUTEX_WAIT, 2, 0, ptr::null_mut()); - } - - seq_before != seq_after - } - - #[inline] - pub fn wait(&self, mutex: &Mutex) { - unsafe { - assert!(self.wait_inner(mutex, ptr::null())); - } - } - - #[inline] - pub fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - unsafe { - let timeout = TimeSpec { - tv_sec: dur.as_secs() as i64, - tv_nsec: dur.subsec_nanos() as i32 - }; - - self.wait_inner(mutex, &timeout as *const TimeSpec) - } - } - - #[inline] - pub unsafe fn destroy(&self) { - *self.lock.get() = ptr::null_mut(); - *self.seq.get() = 0; - } -} - -unsafe impl Send for Condvar {} - -unsafe impl Sync for Condvar {} diff --git a/src/libstd/sys/redox/ext/ffi.rs b/src/libstd/sys/redox/ext/ffi.rs deleted file mode 100644 index 671498bc390..00000000000 --- a/src/libstd/sys/redox/ext/ffi.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Redox-specific extension to the primitives in the `std::ffi` module. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::ffi::{OsStr, OsString}; -use crate::mem; -use crate::sys::os_str::Buf; -use crate::sys_common::{FromInner, IntoInner, AsInner}; - -/// Redox-specific extensions to [`OsString`]. -/// -/// [`OsString`]: ../../../../std/ffi/struct.OsString.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait OsStringExt { - /// Creates an `OsString` from a byte vector. - #[stable(feature = "rust1", since = "1.0.0")] - fn from_vec(vec: Vec<u8>) -> Self; - - /// Yields the underlying byte vector of this `OsString`. - #[stable(feature = "rust1", since = "1.0.0")] - fn into_vec(self) -> Vec<u8>; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl OsStringExt for OsString { - fn from_vec(vec: Vec<u8>) -> OsString { - FromInner::from_inner(Buf { inner: vec }) - } - fn into_vec(self) -> Vec<u8> { - self.into_inner().inner - } -} - -/// Redox-specific extensions to [`OsStr`]. -/// -/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait OsStrExt { - #[stable(feature = "rust1", since = "1.0.0")] - fn from_bytes(slice: &[u8]) -> &Self; - - /// Gets the underlying byte view of the `OsStr` slice. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_bytes(&self) -> &[u8]; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl OsStrExt for OsStr { - fn from_bytes(slice: &[u8]) -> &OsStr { - unsafe { mem::transmute(slice) } - } - fn as_bytes(&self) -> &[u8] { - &self.as_inner().inner - } -} diff --git a/src/libstd/sys/redox/ext/fs.rs b/src/libstd/sys/redox/ext/fs.rs deleted file mode 100644 index 53b9dd68f73..00000000000 --- a/src/libstd/sys/redox/ext/fs.rs +++ /dev/null @@ -1,339 +0,0 @@ -//! Redox-specific extensions to primitives in the `std::fs` module. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::fs::{self, Permissions, OpenOptions}; -use crate::io; -use crate::path::Path; -use crate::sys; -use crate::sys_common::{FromInner, AsInner, AsInnerMut}; - -/// Redox-specific extensions to [`fs::Permissions`]. -/// -/// [`fs::Permissions`]: ../../../../std/fs/struct.Permissions.html -#[stable(feature = "fs_ext", since = "1.1.0")] -pub trait PermissionsExt { - /// Returns the underlying raw `mode_t` bits that are the standard Redox - /// permissions for this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::os::redox::fs::PermissionsExt; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// let permissions = metadata.permissions(); - /// - /// println!("permissions: {}", permissions.mode()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn mode(&self) -> u32; - - /// Sets the underlying raw bits for this set of permissions. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::os::redox::fs::PermissionsExt; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// let mut permissions = metadata.permissions(); - /// - /// permissions.set_mode(0o644); // Read/write for owner and read for others. - /// assert_eq!(permissions.mode(), 0o644); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn set_mode(&mut self, mode: u32); - - /// Creates a new instance of `Permissions` from the given set of Redox - /// permission bits. - /// - /// # Examples - /// - /// ``` - /// use std::fs::Permissions; - /// use std::os::redox::fs::PermissionsExt; - /// - /// // Read/write for owner and read for others. - /// let permissions = Permissions::from_mode(0o644); - /// assert_eq!(permissions.mode(), 0o644); - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn from_mode(mode: u32) -> Self; -} - -#[stable(feature = "fs_ext", since = "1.1.0")] -impl PermissionsExt for Permissions { - fn mode(&self) -> u32 { - self.as_inner().mode() - } - - fn set_mode(&mut self, mode: u32) { - *self = Permissions::from_inner(FromInner::from_inner(mode)); - } - - fn from_mode(mode: u32) -> Permissions { - Permissions::from_inner(FromInner::from_inner(mode)) - } -} - -/// Redox-specific extensions to [`fs::OpenOptions`]. -/// -/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html -#[stable(feature = "fs_ext", since = "1.1.0")] -pub trait OpenOptionsExt { - /// Sets the mode bits that a new file will be created with. - /// - /// If a new file is created as part of a `File::open_opts` call then this - /// specified `mode` will be used as the permission bits for the new file. - /// If no `mode` is set, the default of `0o666` will be used. - /// The operating system masks out bits with the systems `umask`, to produce - /// the final permissions. - /// - /// # Examples - /// - /// ```no_run - /// # #![feature(libc)] - /// extern crate libc; - /// use std::fs::OpenOptions; - /// use std::os::redox::fs::OpenOptionsExt; - /// - /// # fn main() { - /// let mut options = OpenOptions::new(); - /// options.mode(0o644); // Give read/write for owner and read for others. - /// let file = options.open("foo.txt"); - /// # } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn mode(&mut self, mode: u32) -> &mut Self; - - /// Passes custom flags to the `flags` argument of `open`. - /// - /// The bits that define the access mode are masked out with `O_ACCMODE`, to - /// ensure they do not interfere with the access mode set by Rusts options. - /// - /// Custom flags can only set flags, not remove flags set by Rusts options. - /// This options overwrites any previously set custom flags. - /// - /// # Examples - /// - /// ```no_run - /// # #![feature(libc)] - /// extern crate libc; - /// use std::fs::OpenOptions; - /// use std::os::redox::fs::OpenOptionsExt; - /// - /// # fn main() { - /// let mut options = OpenOptions::new(); - /// options.write(true); - /// if cfg!(target_os = "redox") { - /// options.custom_flags(libc::O_NOFOLLOW); - /// } - /// let file = options.open("foo.txt"); - /// # } - /// ``` - #[stable(feature = "open_options_ext", since = "1.10.0")] - fn custom_flags(&mut self, flags: i32) -> &mut Self; -} - -#[stable(feature = "fs_ext", since = "1.1.0")] -impl OpenOptionsExt for OpenOptions { - fn mode(&mut self, mode: u32) -> &mut OpenOptions { - self.as_inner_mut().mode(mode); self - } - - fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { - self.as_inner_mut().custom_flags(flags); self - } -} - -/// Redox-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn dev(&self) -> u64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ino(&self) -> u64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn mode(&self) -> u32; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn nlink(&self) -> u64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn uid(&self) -> u32; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn gid(&self) -> u32; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn size(&self) -> u64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn atime(&self) -> i64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn mtime(&self) -> i64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ctime(&self) -> i64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn blksize(&self) -> u64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn blocks(&self) -> u64; -} - -// Hm, why are there casts here to the returned type, shouldn't the types always -// be the same? Right you are! Turns out, however, on android at least the types -// in the raw `stat` structure are not the same as the types being returned. Who -// knew! -// -// As a result to make sure this compiles for all platforms we do the manual -// casts and rely on manual lowering to `stat` if the raw type is desired. -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for fs::Metadata { - fn dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } -} - -/// Redox-specific extensions for [`FileType`]. -/// -/// Adds support for special Unix file types such as block/character devices, -/// pipes, and sockets. -/// -/// [`FileType`]: ../../../../std/fs/struct.FileType.html -#[stable(feature = "file_type_ext", since = "1.5.0")] -pub trait FileTypeExt { - /// Returns whether this file type is a block device. - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_block_device(&self) -> bool; - /// Returns whether this file type is a char device. - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_char_device(&self) -> bool; - /// Returns whether this file type is a fifo. - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_fifo(&self) -> bool; - /// Returns whether this file type is a socket. - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_socket(&self) -> bool; -} - -#[stable(feature = "file_type_ext", since = "1.5.0")] -impl FileTypeExt for fs::FileType { - fn is_block_device(&self) -> bool { false /*FIXME: Implement block device mode*/ } - fn is_char_device(&self) -> bool { false /*FIXME: Implement char device mode*/ } - fn is_fifo(&self) -> bool { false /*FIXME: Implement fifo mode*/ } - fn is_socket(&self) -> bool { false /*FIXME: Implement socket mode*/ } -} - -/// Creates a new symbolic link on the filesystem. -/// -/// The `dst` path will be a symbolic link pointing to the `src` path. -/// -/// # Note -/// -/// On Windows, you must specify whether a symbolic link points to a file -/// or directory. Use `os::windows::fs::symlink_file` to create a -/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a -/// symbolic link to a directory. Additionally, the process must have -/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a -/// symbolic link. -/// -/// # Examples -/// -/// ```no_run -/// use std::os::redox::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::symlink("a.txt", "b.txt")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "symlink", since = "1.1.0")] -pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> -{ - sys::fs::symlink(src.as_ref(), dst.as_ref()) -} - -/// Redox-specific extensions to [`fs::DirBuilder`]. -/// -/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html -#[stable(feature = "dir_builder", since = "1.6.0")] -pub trait DirBuilderExt { - /// Sets the mode to create new directories with. This option defaults to - /// 0o777. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::DirBuilder; - /// use std::os::redox::fs::DirBuilderExt; - /// - /// let mut builder = DirBuilder::new(); - /// builder.mode(0o755); - /// ``` - #[stable(feature = "dir_builder", since = "1.6.0")] - fn mode(&mut self, mode: u32) -> &mut Self; -} - -#[stable(feature = "dir_builder", since = "1.6.0")] -impl DirBuilderExt for fs::DirBuilder { - fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder { - self.as_inner_mut().set_mode(mode); - self - } -} diff --git a/src/libstd/sys/redox/ext/mod.rs b/src/libstd/sys/redox/ext/mod.rs deleted file mode 100644 index 8a2d243c7ff..00000000000 --- a/src/libstd/sys/redox/ext/mod.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Experimental extensions to `std` for Unix platforms. -//! -//! For now, this module is limited to extracting file descriptors, -//! but its functionality will grow over time. -//! -//! # Examples -//! -//! ```no_run -//! use std::fs::File; -//! use std::os::unix::prelude::*; -//! -//! fn main() { -//! let f = File::create("foo.txt").unwrap(); -//! let fd = f.as_raw_fd(); -//! -//! // use fd with native unix bindings -//! } -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] -#![doc(cfg(target_os = "redox"))] - -pub mod ffi; -pub mod fs; -pub mod io; -pub mod net; -pub mod process; -pub mod thread; - -/// A prelude for conveniently writing platform-specific code. -/// -/// Includes all extension traits, and some important type definitions. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod prelude { - #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd}; - #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::ffi::{OsStrExt, OsStringExt}; - #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::fs::{FileTypeExt, PermissionsExt, OpenOptionsExt, MetadataExt}; - #[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")] - pub use super::process::{CommandExt, ExitStatusExt}; -} diff --git a/src/libstd/sys/redox/ext/net.rs b/src/libstd/sys/redox/ext/net.rs deleted file mode 100644 index 2c121787804..00000000000 --- a/src/libstd/sys/redox/ext/net.rs +++ /dev/null @@ -1,759 +0,0 @@ -#![stable(feature = "unix_socket_redox", since = "1.29")] - -//! Unix-specific networking functionality - -use crate::fmt; -use crate::io::{self, Error, ErrorKind, Initializer}; -use crate::net::Shutdown; -use crate::os::unix::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd}; -use crate::path::Path; -use crate::time::Duration; -use crate::sys::{cvt, fd::FileDesc, syscall}; - -/// An address associated with a Unix socket. -/// -/// # Examples -/// -/// ``` -/// use std::os::unix::net::UnixListener; -/// -/// let socket = match UnixListener::bind("/tmp/sock") { -/// Ok(sock) => sock, -/// Err(e) => { -/// println!("Couldn't bind: {:?}", e); -/// return -/// } -/// }; -/// let addr = socket.local_addr().expect("Couldn't get local address"); -/// ``` -#[derive(Clone)] -#[stable(feature = "unix_socket_redox", since = "1.29")] -pub struct SocketAddr(()); - -impl SocketAddr { - /// Returns the contents of this address if it is a `pathname` address. - /// - /// # Examples - /// - /// With a pathname: - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// use std::path::Path; - /// - /// let socket = UnixListener::bind("/tmp/sock").unwrap(); - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); - /// ``` - /// - /// Without a pathname: - /// - /// ``` - /// use std::os::unix::net::UnixDatagram; - /// - /// let socket = UnixDatagram::unbound().unwrap(); - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(addr.as_pathname(), None); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn as_pathname(&self) -> Option<&Path> { - None - } - - /// Returns `true` if the address is unnamed. - /// - /// # Examples - /// - /// A named address: - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// let socket = UnixListener::bind("/tmp/sock").unwrap(); - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(addr.is_unnamed(), false); - /// ``` - /// - /// An unnamed address: - /// - /// ``` - /// use std::os::unix::net::UnixDatagram; - /// - /// let socket = UnixDatagram::unbound().unwrap(); - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// assert_eq!(addr.is_unnamed(), true); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn is_unnamed(&self) -> bool { - false - } -} -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl fmt::Debug for SocketAddr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "SocketAddr") - } -} - -/// A Unix stream socket. -/// -/// # Examples -/// -/// ```no_run -/// use std::os::unix::net::UnixStream; -/// use std::io::prelude::*; -/// -/// let mut stream = UnixStream::connect("/path/to/my/socket").unwrap(); -/// stream.write_all(b"hello world").unwrap(); -/// let mut response = String::new(); -/// stream.read_to_string(&mut response).unwrap(); -/// println!("{}", response); -/// ``` -#[stable(feature = "unix_socket_redox", since = "1.29")] -pub struct UnixStream(FileDesc); - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl fmt::Debug for UnixStream { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let mut builder = fmt.debug_struct("UnixStream"); - builder.field("fd", &self.0.raw()); - if let Ok(addr) = self.local_addr() { - builder.field("local", &addr); - } - if let Ok(addr) = self.peer_addr() { - builder.field("peer", &addr); - } - builder.finish() - } -} - -impl UnixStream { - /// Connects to the socket named by `path`. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// let socket = match UnixStream::connect("/tmp/sock") { - /// Ok(sock) => sock, - /// Err(e) => { - /// println!("Couldn't connect: {:?}", e); - /// return - /// } - /// }; - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> { - if let Some(s) = path.as_ref().to_str() { - cvt(syscall::open(format!("chan:{}", s), syscall::O_CLOEXEC)) - .map(FileDesc::new) - .map(UnixStream) - } else { - Err(Error::new( - ErrorKind::Other, - "UnixStream::connect: non-utf8 paths not supported on redox" - )) - } - } - - /// Creates an unnamed pair of connected sockets. - /// - /// Returns two `UnixStream`s which are connected to each other. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// let (sock1, sock2) = match UnixStream::pair() { - /// Ok((sock1, sock2)) => (sock1, sock2), - /// Err(e) => { - /// println!("Couldn't create a pair of sockets: {:?}", e); - /// return - /// } - /// }; - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn pair() -> io::Result<(UnixStream, UnixStream)> { - let server = cvt(syscall::open("chan:", syscall::O_CREAT | syscall::O_CLOEXEC)) - .map(FileDesc::new)?; - let client = server.duplicate_path(b"connect")?; - let stream = server.duplicate_path(b"listen")?; - Ok((UnixStream(client), UnixStream(stream))) - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned `UnixStream` is a reference to the same stream that this - /// object references. Both handles will read and write the same stream of - /// data, and options set on one stream will be propagated to the other - /// stream. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn try_clone(&self) -> io::Result<UnixStream> { - self.0.duplicate().map(UnixStream) - } - - /// Returns the socket address of the local half of this connection. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// let addr = socket.local_addr().expect("Couldn't get local address"); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn local_addr(&self) -> io::Result<SocketAddr> { - Err(Error::new(ErrorKind::Other, "UnixStream::local_addr unimplemented on redox")) - } - - /// Returns the socket address of the remote half of this connection. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// let addr = socket.peer_addr().expect("Couldn't get peer address"); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn peer_addr(&self) -> io::Result<SocketAddr> { - Err(Error::new(ErrorKind::Other, "UnixStream::peer_addr unimplemented on redox")) - } - - /// Sets the read timeout for the socket. - /// - /// If the provided value is [`None`], then [`read`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method. - /// - /// [`None`]: ../../../../std/option/enum.Option.html#variant.None - /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err - /// [`read`]: ../../../../std/io/trait.Read.html#tymethod.read - /// [`Duration`]: ../../../../std/time/struct.Duration.html - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn set_read_timeout(&self, _timeout: Option<Duration>) -> io::Result<()> { - Err(Error::new(ErrorKind::Other, "UnixStream::set_read_timeout unimplemented on redox")) - } - - /// Sets the write timeout for the socket. - /// - /// If the provided value is [`None`], then [`write`] calls will block - /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is - /// passed to this method. - /// - /// [`None`]: ../../../../std/option/enum.Option.html#variant.None - /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err - /// [`write`]: ../../../../std/io/trait.Write.html#tymethod.write - /// [`Duration`]: ../../../../std/time/struct.Duration.html - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout"); - /// ``` - /// - /// An [`Err`] is returned if the zero [`Duration`] is passed to this - /// method: - /// - /// ```no_run - /// use std::io; - /// use std::net::UdpSocket; - /// use std::time::Duration; - /// - /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap(); - /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); - /// let err = result.unwrap_err(); - /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn set_write_timeout(&self, _timeout: Option<Duration>) -> io::Result<()> { - Err(Error::new(ErrorKind::Other, "UnixStream::set_write_timeout unimplemented on redox")) - } - - /// Returns the read timeout of this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); - /// assert_eq!(socket.read_timeout().unwrap(), Some(Duration::new(1, 0))); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn read_timeout(&self) -> io::Result<Option<Duration>> { - Err(Error::new(ErrorKind::Other, "UnixStream::read_timeout unimplemented on redox")) - } - - /// Returns the write timeout of this socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::time::Duration; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout"); - /// assert_eq!(socket.write_timeout().unwrap(), Some(Duration::new(1, 0))); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn write_timeout(&self) -> io::Result<Option<Duration>> { - Err(Error::new(ErrorKind::Other, "UnixStream::write_timeout unimplemented on redox")) - } - - /// Moves the socket into or out of nonblocking mode. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } - - /// Returns the value of the `SO_ERROR` option. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// if let Ok(Some(err)) = socket.take_error() { - /// println!("Got error: {:?}", err); - /// } - /// ``` - /// - /// # Platform specific - /// On Redox this always returns `None`. - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - Ok(None) - } - - /// Shuts down the read, write, or both halves of this connection. - /// - /// This function will cause all pending and future I/O calls on the - /// specified portions to immediately return with an appropriate value - /// (see the documentation of [`Shutdown`]). - /// - /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixStream; - /// use std::net::Shutdown; - /// - /// let socket = UnixStream::connect("/tmp/sock").unwrap(); - /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn shutdown(&self, _how: Shutdown) -> io::Result<()> { - Err(Error::new(ErrorKind::Other, "UnixStream::shutdown unimplemented on redox")) - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl io::Read for UnixStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - io::Read::read(&mut &*self, buf) - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl<'a> io::Read for &'a UnixStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - self.0.read(buf) - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl io::Write for UnixStream { - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - io::Write::write(&mut &*self, buf) - } - - fn flush(&mut self) -> io::Result<()> { - io::Write::flush(&mut &*self) - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl<'a> io::Write for &'a UnixStream { - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl AsRawFd for UnixStream { - fn as_raw_fd(&self) -> RawFd { - self.0.raw() - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl FromRawFd for UnixStream { - unsafe fn from_raw_fd(fd: RawFd) -> UnixStream { - UnixStream(FileDesc::new(fd)) - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl IntoRawFd for UnixStream { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw() - } -} - -/// A structure representing a Unix domain socket server. -/// -/// # Examples -/// -/// ```no_run -/// use std::thread; -/// use std::os::unix::net::{UnixStream, UnixListener}; -/// -/// fn handle_client(stream: UnixStream) { -/// // ... -/// } -/// -/// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); -/// -/// // accept connections and process them, spawning a new thread for each one -/// for stream in listener.incoming() { -/// match stream { -/// Ok(stream) => { -/// /* connection succeeded */ -/// thread::spawn(|| handle_client(stream)); -/// } -/// Err(err) => { -/// /* connection failed */ -/// break; -/// } -/// } -/// } -/// ``` -#[stable(feature = "unix_socket_redox", since = "1.29")] -pub struct UnixListener(FileDesc); - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl fmt::Debug for UnixListener { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let mut builder = fmt.debug_struct("UnixListener"); - builder.field("fd", &self.0.raw()); - if let Ok(addr) = self.local_addr() { - builder.field("local", &addr); - } - builder.finish() - } -} - -impl UnixListener { - /// Creates a new `UnixListener` bound to the specified socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// let listener = match UnixListener::bind("/path/to/the/socket") { - /// Ok(sock) => sock, - /// Err(e) => { - /// println!("Couldn't connect: {:?}", e); - /// return - /// } - /// }; - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> { - if let Some(s) = path.as_ref().to_str() { - cvt(syscall::open(format!("chan:{}", s), syscall::O_CREAT | syscall::O_CLOEXEC)) - .map(FileDesc::new) - .map(UnixListener) - } else { - Err(Error::new( - ErrorKind::Other, - "UnixListener::bind: non-utf8 paths not supported on redox" - )) - } - } - - /// Accepts a new incoming connection to this listener. - /// - /// This function will block the calling thread until a new Unix connection - /// is established. When established, the corresponding [`UnixStream`] and - /// the remote peer's address will be returned. - /// - /// [`UnixStream`]: ../../../../std/os/unix/net/struct.UnixStream.html - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); - /// - /// match listener.accept() { - /// Ok((socket, addr)) => println!("Got a client: {:?}", addr), - /// Err(e) => println!("accept function failed: {:?}", e), - /// } - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { - self.0.duplicate_path(b"listen").map(|fd| (UnixStream(fd), SocketAddr(()))) - } - - /// Creates a new independently owned handle to the underlying socket. - /// - /// The returned `UnixListener` is a reference to the same socket that this - /// object references. Both handles can be used to accept incoming - /// connections and options set on one listener will affect the other. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); - /// - /// let listener_copy = listener.try_clone().expect("try_clone failed"); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn try_clone(&self) -> io::Result<UnixListener> { - self.0.duplicate().map(UnixListener) - } - - /// Returns the local socket address of this listener. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); - /// - /// let addr = listener.local_addr().expect("Couldn't get local address"); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn local_addr(&self) -> io::Result<SocketAddr> { - Err(Error::new(ErrorKind::Other, "UnixListener::local_addr unimplemented on redox")) - } - - /// Moves the socket into or out of nonblocking mode. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); - /// - /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.0.set_nonblocking(nonblocking) - } - - /// Returns the value of the `SO_ERROR` option. - /// - /// # Examples - /// - /// ```no_run - /// use std::os::unix::net::UnixListener; - /// - /// let listener = UnixListener::bind("/tmp/sock").unwrap(); - /// - /// if let Ok(Some(err)) = listener.take_error() { - /// println!("Got error: {:?}", err); - /// } - /// ``` - /// - /// # Platform specific - /// On Redox this always returns `None`. - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - Ok(None) - } - - /// Returns an iterator over incoming connections. - /// - /// The iterator will never return [`None`] and will also not yield the - /// peer's [`SocketAddr`] structure. - /// - /// [`None`]: ../../../../std/option/enum.Option.html#variant.None - /// [`SocketAddr`]: struct.SocketAddr.html - /// - /// # Examples - /// - /// ```no_run - /// use std::thread; - /// use std::os::unix::net::{UnixStream, UnixListener}; - /// - /// fn handle_client(stream: UnixStream) { - /// // ... - /// } - /// - /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); - /// - /// for stream in listener.incoming() { - /// match stream { - /// Ok(stream) => { - /// thread::spawn(|| handle_client(stream)); - /// } - /// Err(err) => { - /// break; - /// } - /// } - /// } - /// ``` - #[stable(feature = "unix_socket_redox", since = "1.29")] - pub fn incoming<'a>(&'a self) -> Incoming<'a> { - Incoming { listener: self } - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl AsRawFd for UnixListener { - fn as_raw_fd(&self) -> RawFd { - self.0.raw() - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl FromRawFd for UnixListener { - unsafe fn from_raw_fd(fd: RawFd) -> UnixListener { - UnixListener(FileDesc::new(fd)) - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl IntoRawFd for UnixListener { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw() - } -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl<'a> IntoIterator for &'a UnixListener { - type Item = io::Result<UnixStream>; - type IntoIter = Incoming<'a>; - - fn into_iter(self) -> Incoming<'a> { - self.incoming() - } -} - -/// An iterator over incoming connections to a [`UnixListener`]. -/// -/// It will never return [`None`]. -/// -/// [`None`]: ../../../../std/option/enum.Option.html#variant.None -/// [`UnixListener`]: struct.UnixListener.html -/// -/// # Examples -/// -/// ```no_run -/// use std::thread; -/// use std::os::unix::net::{UnixStream, UnixListener}; -/// -/// fn handle_client(stream: UnixStream) { -/// // ... -/// } -/// -/// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); -/// -/// for stream in listener.incoming() { -/// match stream { -/// Ok(stream) => { -/// thread::spawn(|| handle_client(stream)); -/// } -/// Err(err) => { -/// break; -/// } -/// } -/// } -/// ``` -#[derive(Debug)] -#[stable(feature = "unix_socket_redox", since = "1.29")] -pub struct Incoming<'a> { - listener: &'a UnixListener, -} - -#[stable(feature = "unix_socket_redox", since = "1.29")] -impl<'a> Iterator for Incoming<'a> { - type Item = io::Result<UnixStream>; - - fn next(&mut self) -> Option<io::Result<UnixStream>> { - Some(self.listener.accept().map(|s| s.0)) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - (usize::max_value(), None) - } -} diff --git a/src/libstd/sys/redox/ext/thread.rs b/src/libstd/sys/redox/ext/thread.rs deleted file mode 100644 index 629eaef04ce..00000000000 --- a/src/libstd/sys/redox/ext/thread.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Redox-specific extensions to primitives in the `std::thread` module. - -#![stable(feature = "thread_extensions", since = "1.9.0")] - -use crate::sys_common::{AsInner, IntoInner}; -use crate::thread::JoinHandle; - -#[stable(feature = "thread_extensions", since = "1.9.0")] -#[allow(deprecated)] -pub type RawPthread = usize; - -/// Redox-specific extensions to [`thread::JoinHandle`]. -/// -/// [`thread::JoinHandle`]: ../../../../std/thread/struct.JoinHandle.html -#[stable(feature = "thread_extensions", since = "1.9.0")] -pub trait JoinHandleExt { - /// Extracts the raw pthread_t without taking ownership - #[stable(feature = "thread_extensions", since = "1.9.0")] - fn as_pthread_t(&self) -> RawPthread; - - /// Consumes the thread, returning the raw pthread_t - /// - /// This function **transfers ownership** of the underlying pthread_t to - /// the caller. Callers are then the unique owners of the pthread_t and - /// must either detach or join the pthread_t once it's no longer needed. - #[stable(feature = "thread_extensions", since = "1.9.0")] - fn into_pthread_t(self) -> RawPthread; -} - -#[stable(feature = "thread_extensions", since = "1.9.0")] -impl<T> JoinHandleExt for JoinHandle<T> { - fn as_pthread_t(&self) -> RawPthread { - self.as_inner().id() as RawPthread - } - - fn into_pthread_t(self) -> RawPthread { - self.into_inner().into_id() as RawPthread - } -} diff --git a/src/libstd/sys/redox/fast_thread_local.rs b/src/libstd/sys/redox/fast_thread_local.rs deleted file mode 100644 index 1202708a476..00000000000 --- a/src/libstd/sys/redox/fast_thread_local.rs +++ /dev/null @@ -1,111 +0,0 @@ -#![cfg(target_thread_local)] -#![unstable(feature = "thread_local_internals", issue = "0")] - -use crate::cell::{Cell, UnsafeCell}; -use crate::mem; -use crate::ptr; - - -pub struct Key<T> { - inner: UnsafeCell<Option<T>>, - - // Metadata to keep track of the state of the destructor. Remember that - // these variables are thread-local, not global. - dtor_registered: Cell<bool>, - dtor_running: Cell<bool>, -} - -unsafe impl<T> Sync for Key<T> { } - -impl<T> Key<T> { - pub const fn new() -> Key<T> { - Key { - inner: UnsafeCell::new(None), - dtor_registered: Cell::new(false), - dtor_running: Cell::new(false) - } - } - - pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { - unsafe { - if mem::needs_drop::<T>() && self.dtor_running.get() { - return None - } - self.register_dtor(); - } - Some(&self.inner) - } - - unsafe fn register_dtor(&self) { - if !mem::needs_drop::<T>() || self.dtor_registered.get() { - return - } - - register_dtor(self as *const _ as *mut u8, - destroy_value::<T>); - self.dtor_registered.set(true); - } -} - -pub unsafe fn register_dtor(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. - // - // The destructor for DTORS is a little special in that it has a `while` - // loop to continuously drain the list of registered destructors. It - // *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 crate::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() { - let v: Box<List> = box Vec::new(); - DTORS.set(Box::into_raw(v) as *mut u8); - } - let list: &mut List = &mut *(DTORS.get() as *mut List); - list.push((t, dtor)); - - unsafe extern fn run_dtors(mut ptr: *mut u8) { - while !ptr.is_null() { - let list: Box<List> = Box::from_raw(ptr as *mut List); - for (ptr, dtor) in list.into_iter() { - dtor(ptr); - } - ptr = DTORS.get(); - DTORS.set(ptr::null_mut()); - } - } -} - -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 - // destructor as running for this thread so calls to `get` will return - // `None`. - (*ptr).dtor_running.set(true); - - // The macOS implementation of TLS apparently had an odd aspect to it - // where the pointer we have may be overwritten while this destructor - // is running. Specifically if a TLS destructor re-accesses TLS it may - // trigger a re-initialization of all TLS variables, paving over at - // least some destroyed ones with initial values. - // - // This means that if we drop a TLS value in place on macOS that we could - // revert the value to its original state halfway through the - // destructor, which would be bad! - // - // Hence, we use `ptr::read` on macOS (to move to a "safe" location) - // instead of drop_in_place. - if cfg!(target_os = "macos") { - ptr::read((*ptr).inner.get()); - } else { - ptr::drop_in_place((*ptr).inner.get()); - } -} - -pub fn requires_move_before_drop() -> bool { - false -} diff --git a/src/libstd/sys/redox/fd.rs b/src/libstd/sys/redox/fd.rs deleted file mode 100644 index a42e486db22..00000000000 --- a/src/libstd/sys/redox/fd.rs +++ /dev/null @@ -1,88 +0,0 @@ -#![unstable(reason = "not public", issue = "0", feature = "fd")] - -use crate::io::{self, Read}; -use crate::mem; -use crate::sys::{cvt, syscall}; -use crate::sys_common::AsInner; - -pub struct FileDesc { - fd: usize, -} - -impl FileDesc { - pub fn new(fd: usize) -> FileDesc { - FileDesc { fd } - } - - pub fn raw(&self) -> usize { self.fd } - - /// Extracts the actual file descriptor without closing it. - pub fn into_raw(self) -> usize { - let fd = self.fd; - mem::forget(self); - fd - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { - cvt(syscall::read(self.fd, buf)) - } - - pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { - let mut me = self; - (&mut me).read_to_end(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result<usize> { - cvt(syscall::write(self.fd, buf)) - } - - pub fn duplicate(&self) -> io::Result<FileDesc> { - self.duplicate_path(&[]) - } - pub fn duplicate_path(&self, path: &[u8]) -> io::Result<FileDesc> { - let new_fd = cvt(syscall::dup(self.fd, path))?; - Ok(FileDesc::new(new_fd)) - } - - pub fn nonblocking(&self) -> io::Result<bool> { - let flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?; - Ok(flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK) - } - - pub fn set_cloexec(&self) -> io::Result<()> { - let mut flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFD, 0))?; - flags |= syscall::O_CLOEXEC; - cvt(syscall::fcntl(self.fd, syscall::F_SETFD, flags)).and(Ok(())) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?; - if nonblocking { - flags |= syscall::O_NONBLOCK; - } else { - flags &= !syscall::O_NONBLOCK; - } - cvt(syscall::fcntl(self.fd, syscall::F_SETFL, flags)).and(Ok(())) - } -} - -impl<'a> Read for &'a FileDesc { - fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - (**self).read(buf) - } -} - -impl AsInner<usize> for FileDesc { - fn as_inner(&self) -> &usize { &self.fd } -} - -impl Drop for FileDesc { - fn drop(&mut self) { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // (opened after we closed ours. - let _ = syscall::close(self.fd); - } -} diff --git a/src/libstd/sys/redox/fs.rs b/src/libstd/sys/redox/fs.rs deleted file mode 100644 index 159ee9911bd..00000000000 --- a/src/libstd/sys/redox/fs.rs +++ /dev/null @@ -1,473 +0,0 @@ -use crate::os::unix::prelude::*; - -use crate::ffi::{OsString, OsStr}; -use crate::fmt; -use crate::io::{self, Error, ErrorKind, SeekFrom}; -use crate::path::{Path, PathBuf}; -use crate::sync::Arc; -use crate::sys::fd::FileDesc; -use crate::sys::time::SystemTime; -use crate::sys::{cvt, syscall}; -use crate::sys_common::{AsInner, FromInner}; - -pub struct File(FileDesc); - -#[derive(Clone)] -pub struct FileAttr { - stat: syscall::Stat, -} - -pub struct ReadDir { - data: Vec<u8>, - i: usize, - root: Arc<PathBuf>, -} - -struct Dir(FileDesc); - -unsafe impl Send for Dir {} -unsafe impl Sync for Dir {} - -pub struct DirEntry { - root: Arc<PathBuf>, - name: Box<[u8]> -} - -#[derive(Clone, Debug)] -pub struct OpenOptions { - // generic - read: bool, - write: bool, - append: bool, - truncate: bool, - create: bool, - create_new: bool, - // system-specific - custom_flags: i32, - mode: u16, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { mode: u16 } - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct FileType { mode: u16 } - -#[derive(Debug)] -pub struct DirBuilder { mode: u16 } - -impl FileAttr { - pub fn size(&self) -> u64 { self.stat.st_size as u64 } - pub fn perm(&self) -> FilePermissions { - FilePermissions { mode: (self.stat.st_mode as u16) & 0o777 } - } - - pub fn file_type(&self) -> FileType { - FileType { mode: self.stat.st_mode as u16 } - } -} - -impl FileAttr { - pub fn modified(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(syscall::TimeSpec { - tv_sec: self.stat.st_mtime as i64, - tv_nsec: self.stat.st_mtime_nsec as i32, - })) - } - - pub fn accessed(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(syscall::TimeSpec { - tv_sec: self.stat.st_atime as i64, - tv_nsec: self.stat.st_atime_nsec as i32, - })) - } - - pub fn created(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(syscall::TimeSpec { - tv_sec: self.stat.st_ctime as i64, - tv_nsec: self.stat.st_ctime_nsec as i32, - })) - } -} - -impl AsInner<syscall::Stat> for FileAttr { - fn as_inner(&self) -> &syscall::Stat { &self.stat } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } - pub fn set_readonly(&mut self, readonly: bool) { - if readonly { - self.mode &= !0o222; - } else { - self.mode |= 0o222; - } - } - pub fn mode(&self) -> u32 { self.mode as u32 } -} - -impl FileType { - pub fn is_dir(&self) -> bool { self.is(syscall::MODE_DIR) } - pub fn is_file(&self) -> bool { self.is(syscall::MODE_FILE) } - pub fn is_symlink(&self) -> bool { self.is(syscall::MODE_SYMLINK) } - - pub fn is(&self, mode: u16) -> bool { - self.mode & syscall::MODE_TYPE == mode - } -} - -impl FromInner<u32> for FilePermissions { - fn from_inner(mode: u32) -> FilePermissions { - FilePermissions { mode: mode as u16 } - } -} - -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>; - - fn next(&mut self) -> Option<io::Result<DirEntry>> { - loop { - let start = self.i; - let mut i = self.i; - while i < self.data.len() { - self.i += 1; - if self.data[i] == b'\n' { - break; - } - i += 1; - } - if start < self.i { - let ret = DirEntry { - name: self.data[start .. i].to_owned().into_boxed_slice(), - root: self.root.clone() - }; - if ret.name_bytes() != b"." && ret.name_bytes() != b".." { - return Some(Ok(ret)) - } - } else { - return None; - } - } - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - self.root.join(OsStr::from_bytes(self.name_bytes())) - } - - pub fn file_name(&self) -> OsString { - OsStr::from_bytes(self.name_bytes()).to_os_string() - } - - pub fn metadata(&self) -> io::Result<FileAttr> { - lstat(&self.path()) - } - - pub fn file_type(&self) -> io::Result<FileType> { - lstat(&self.path()).map(|m| m.file_type()) - } - - fn name_bytes(&self) -> &[u8] { - &*self.name - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - OpenOptions { - // generic - read: false, - write: false, - append: false, - truncate: false, - create: false, - create_new: false, - // system-specific - custom_flags: 0, - mode: 0o666, - } - } - - pub fn read(&mut self, read: bool) { self.read = read; } - pub fn write(&mut self, write: bool) { self.write = write; } - pub fn append(&mut self, append: bool) { self.append = append; } - pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; } - pub fn create(&mut self, create: bool) { self.create = create; } - pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; } - - pub fn custom_flags(&mut self, flags: i32) { self.custom_flags = flags; } - pub fn mode(&mut self, mode: u32) { self.mode = mode as u16; } - - fn get_access_mode(&self) -> io::Result<usize> { - match (self.read, self.write, self.append) { - (true, false, false) => Ok(syscall::O_RDONLY), - (false, true, false) => Ok(syscall::O_WRONLY), - (true, true, false) => Ok(syscall::O_RDWR), - (false, _, true) => Ok(syscall::O_WRONLY | syscall::O_APPEND), - (true, _, true) => Ok(syscall::O_RDWR | syscall::O_APPEND), - (false, false, false) => Err(Error::from_raw_os_error(syscall::EINVAL)), - } - } - - fn get_creation_mode(&self) -> io::Result<usize> { - match (self.write, self.append) { - (true, false) => {} - (false, false) => - if self.truncate || self.create || self.create_new { - return Err(Error::from_raw_os_error(syscall::EINVAL)); - }, - (_, true) => - if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(syscall::EINVAL)); - }, - } - - Ok(match (self.create, self.truncate, self.create_new) { - (false, false, false) => 0, - (true, false, false) => syscall::O_CREAT, - (false, true, false) => syscall::O_TRUNC, - (true, true, false) => syscall::O_CREAT | syscall::O_TRUNC, - (_, _, true) => syscall::O_CREAT | syscall::O_EXCL, - }) - } -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { - let flags = syscall::O_CLOEXEC | - opts.get_access_mode()? as usize | - opts.get_creation_mode()? as usize | - (opts.custom_flags as usize & !syscall::O_ACCMODE); - let fd = cvt(syscall::open(path.to_str().unwrap(), flags | opts.mode as usize))?; - Ok(File(FileDesc::new(fd))) - } - - pub fn file_attr(&self) -> io::Result<FileAttr> { - let mut stat = syscall::Stat::default(); - cvt(syscall::fstat(self.0.raw(), &mut stat))?; - Ok(FileAttr { stat }) - } - - pub fn fsync(&self) -> io::Result<()> { - cvt(syscall::fsync(self.0.raw()))?; - Ok(()) - } - - pub fn datasync(&self) -> io::Result<()> { - self.fsync() - } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - cvt(syscall::ftruncate(self.0.raw(), size as usize))?; - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { - self.0.read(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result<usize> { - self.0.write(buf) - } - - pub fn flush(&self) -> io::Result<()> { Ok(()) } - - pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { - let (whence, pos) = match pos { - // Casting to `i64` is fine, too large values will end up as - // negative which will cause an error in `lseek64`. - SeekFrom::Start(off) => (syscall::SEEK_SET, off as i64), - SeekFrom::End(off) => (syscall::SEEK_END, off), - SeekFrom::Current(off) => (syscall::SEEK_CUR, off), - }; - let n = cvt(syscall::lseek(self.0.raw(), pos as isize, whence))?; - Ok(n as u64) - } - - pub fn duplicate(&self) -> io::Result<File> { - self.0.duplicate().map(File) - } - - pub fn dup(&self, buf: &[u8]) -> io::Result<File> { - let fd = cvt(syscall::dup(*self.fd().as_inner() as usize, buf))?; - Ok(File(FileDesc::new(fd))) - } - - pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - set_perm(&self.path()?, perm) - } - - pub fn path(&self) -> io::Result<PathBuf> { - let mut buf: [u8; 4096] = [0; 4096]; - let count = cvt(syscall::fpath(*self.fd().as_inner() as usize, &mut buf))?; - Ok(PathBuf::from(unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) })) - } - - pub fn fd(&self) -> &FileDesc { &self.0 } - - pub fn into_fd(self) -> FileDesc { self.0 } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder { mode: 0o777 } - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let flags = syscall::O_CREAT | syscall::O_CLOEXEC | syscall::O_DIRECTORY | syscall::O_EXCL; - let fd = cvt(syscall::open(p.to_str().unwrap(), flags | (self.mode as usize & 0o777)))?; - let _ = syscall::close(fd); - Ok(()) - } - - pub fn set_mode(&mut self, mode: u32) { - self.mode = mode as u16; - } -} - -impl FromInner<usize> for File { - fn from_inner(fd: usize) -> File { - File(FileDesc::new(fd)) - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut b = f.debug_struct("File"); - b.field("fd", &self.0.raw()); - if let Ok(path) = self.path() { - b.field("path", &path); - } - /* - if let Some((read, write)) = get_mode(fd) { - b.field("read", &read).field("write", &write); - } - */ - b.finish() - } -} - -pub fn readdir(p: &Path) -> io::Result<ReadDir> { - let root = Arc::new(p.to_path_buf()); - - let flags = syscall::O_CLOEXEC | syscall::O_RDONLY | syscall::O_DIRECTORY; - let fd = cvt(syscall::open(p.to_str().unwrap(), flags))?; - let file = FileDesc::new(fd); - let mut data = Vec::new(); - file.read_to_end(&mut data)?; - - Ok(ReadDir { data: data, i: 0, root: root }) -} - -pub fn unlink(p: &Path) -> io::Result<()> { - cvt(syscall::unlink(p.to_str().unwrap()))?; - Ok(()) -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let fd = cvt(syscall::open(old.to_str().unwrap(), - syscall::O_CLOEXEC | syscall::O_STAT | syscall::O_NOFOLLOW))?; - let res = cvt(syscall::frename(fd, new.to_str().unwrap())); - cvt(syscall::close(fd))?; - res?; - Ok(()) -} - -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - cvt(syscall::chmod(p.to_str().unwrap(), perm.mode as usize))?; - Ok(()) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - cvt(syscall::rmdir(p.to_str().unwrap()))?; - Ok(()) -} - -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let filetype = lstat(path)?.file_type(); - if filetype.is_symlink() { - unlink(path) - } else { - remove_dir_all_recursive(path) - } -} - -fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { - for child in readdir(path)? { - let child = child?; - if child.file_type()?.is_dir() { - remove_dir_all_recursive(&child.path())?; - } else { - unlink(&child.path())?; - } - } - rmdir(path) -} - -pub fn readlink(p: &Path) -> io::Result<PathBuf> { - let fd = cvt(syscall::open(p.to_str().unwrap(), - syscall::O_CLOEXEC | syscall::O_SYMLINK | syscall::O_RDONLY))?; - let mut buf: [u8; 4096] = [0; 4096]; - let res = cvt(syscall::read(fd, &mut buf)); - cvt(syscall::close(fd))?; - let count = res?; - Ok(PathBuf::from(unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) })) -} - -pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { - let fd = cvt(syscall::open(dst.to_str().unwrap(), - syscall::O_CLOEXEC | syscall::O_SYMLINK | - syscall::O_CREAT | syscall::O_WRONLY | 0o777))?; - let res = cvt(syscall::write(fd, src.to_str().unwrap().as_bytes())); - cvt(syscall::close(fd))?; - res?; - Ok(()) -} - -pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { - Err(Error::from_raw_os_error(syscall::ENOSYS)) -} - -pub fn stat(p: &Path) -> io::Result<FileAttr> { - let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_CLOEXEC | syscall::O_STAT))?; - let file = File(FileDesc::new(fd)); - file.file_attr() -} - -pub fn lstat(p: &Path) -> io::Result<FileAttr> { - let fd = cvt(syscall::open(p.to_str().unwrap(), - syscall::O_CLOEXEC | syscall::O_STAT | syscall::O_NOFOLLOW))?; - let file = File(FileDesc::new(fd)); - file.file_attr() -} - -pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { - let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_CLOEXEC | syscall::O_STAT))?; - let file = File(FileDesc::new(fd)); - file.path() -} - -pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { - use crate::fs::{File, set_permissions}; - if !from.is_file() { - return Err(Error::new(ErrorKind::InvalidInput, - "the source path is not an existing regular file")) - } - - let mut reader = File::open(from)?; - let mut writer = File::create(to)?; - let perm = reader.metadata()?.permissions(); - - let ret = io::copy(&mut reader, &mut writer)?; - set_permissions(to, perm)?; - Ok(ret) -} diff --git a/src/libstd/sys/redox/io.rs b/src/libstd/sys/redox/io.rs deleted file mode 100644 index 8b02d3fd19d..00000000000 --- a/src/libstd/sys/redox/io.rs +++ /dev/null @@ -1,32 +0,0 @@ -pub struct IoVec<'a>(&'a [u8]); - -impl<'a> IoVec<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoVec<'a> { - IoVec(buf) - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - self.0 - } -} - -pub struct IoVecMut<'a>(&'a mut [u8]); - -impl<'a> IoVecMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> { - IoVecMut(buf) - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - self.0 - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - self.0 - } -} diff --git a/src/libstd/sys/redox/memchr.rs b/src/libstd/sys/redox/memchr.rs deleted file mode 100644 index d2bfcce86f4..00000000000 --- a/src/libstd/sys/redox/memchr.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Original implementation taken from rust-memchr. -// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch - -pub use core::slice::memchr::{memchr, memrchr}; diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs deleted file mode 100644 index 7f3ac1f1bb5..00000000000 --- a/src/libstd/sys/redox/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![allow(dead_code, missing_docs, nonstandard_style)] - -use crate::io::ErrorKind; - -pub use libc::strlen; -pub use self::rand::hashmap_random_keys; - -#[path = "../unix/alloc.rs"] -pub mod alloc; -pub mod args; -#[cfg(feature = "backtrace")] -pub mod backtrace; -pub mod cmath; -pub mod condvar; -pub mod env; -pub mod ext; -pub mod fast_thread_local; -pub mod fd; -pub mod fs; -pub mod io; -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; -pub mod rwlock; -pub mod stack_overflow; -pub mod stdio; -pub mod syscall; -pub mod thread; -pub mod thread_local; -pub mod time; - -#[cfg(not(test))] -pub fn init() {} - -pub fn decode_error_kind(errno: i32) -> ErrorKind { - match errno { - syscall::ECONNREFUSED => ErrorKind::ConnectionRefused, - syscall::ECONNRESET => ErrorKind::ConnectionReset, - syscall::EPERM | syscall::EACCES => ErrorKind::PermissionDenied, - syscall::EPIPE => ErrorKind::BrokenPipe, - syscall::ENOTCONN => ErrorKind::NotConnected, - syscall::ECONNABORTED => ErrorKind::ConnectionAborted, - syscall::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - syscall::EADDRINUSE => ErrorKind::AddrInUse, - syscall::ENOENT => ErrorKind::NotFound, - syscall::EINTR => ErrorKind::Interrupted, - syscall::EINVAL => ErrorKind::InvalidInput, - syscall::ETIMEDOUT => ErrorKind::TimedOut, - syscall::EEXIST => ErrorKind::AlreadyExists, - - // These two constants can have the same value on some systems, - // but different values on others, so we can't use a match - // clause - x if x == syscall::EAGAIN || x == syscall::EWOULDBLOCK => - ErrorKind::WouldBlock, - - _ => ErrorKind::Other, - } -} - -pub fn cvt(result: Result<usize, syscall::Error>) -> crate::io::Result<usize> { - result.map_err(|err| crate::io::Error::from_raw_os_error(err.errno)) -} - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt_libc<T: IsMinusOne>(t: T) -> crate::io::Result<T> { - if t.is_minus_one() { - Err(crate::io::Error::last_os_error()) - } else { - Ok(t) - } -} - -/// On Redox, use an illegal instruction to abort -pub unsafe fn abort_internal() -> ! { - core::intrinsics::abort(); -} diff --git a/src/libstd/sys/redox/mutex.rs b/src/libstd/sys/redox/mutex.rs deleted file mode 100644 index 59399df0294..00000000000 --- a/src/libstd/sys/redox/mutex.rs +++ /dev/null @@ -1,169 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::intrinsics::{atomic_cxchg, atomic_xchg}; -use crate::ptr; - -use crate::sys::syscall::{futex, getpid, FUTEX_WAIT, FUTEX_WAKE}; - -pub unsafe fn mutex_try_lock(m: *mut i32) -> bool { - atomic_cxchg(m, 0, 1).0 == 0 -} - -pub unsafe fn mutex_lock(m: *mut i32) { - let mut c = 0; - //Set to larger value for longer spin test - for _i in 0..100 { - c = atomic_cxchg(m, 0, 1).0; - if c == 0 { - break; - } - //cpu_relax() - } - if c == 1 { - c = atomic_xchg(m, 2); - } - while c != 0 { - let _ = futex(m, FUTEX_WAIT, 2, 0, ptr::null_mut()); - c = atomic_xchg(m, 2); - } -} - -pub unsafe fn mutex_unlock(m: *mut i32) { - if *m == 2 { - *m = 0; - } else if atomic_xchg(m, 0) == 1 { - return; - } - //Set to larger value for longer spin test - for _i in 0..100 { - if *m != 0 { - if atomic_cxchg(m, 1, 2).0 != 0 { - return; - } - } - //cpu_relax() - } - let _ = futex(m, FUTEX_WAKE, 1, 0, ptr::null_mut()); -} - -pub struct Mutex { - pub lock: UnsafeCell<i32>, -} - -impl Mutex { - /// Creates a new mutex. - pub const fn new() -> Self { - Mutex { - lock: UnsafeCell::new(0), - } - } - - #[inline] - pub unsafe fn init(&self) { - *self.lock.get() = 0; - } - - /// Try to lock the mutex - #[inline] - pub unsafe fn try_lock(&self) -> bool { - mutex_try_lock(self.lock.get()) - } - - /// Lock the mutex - #[inline] - pub unsafe fn lock(&self) { - mutex_lock(self.lock.get()); - } - - /// Unlock the mutex - #[inline] - pub unsafe fn unlock(&self) { - mutex_unlock(self.lock.get()); - } - - #[inline] - pub unsafe fn destroy(&self) { - *self.lock.get() = 0; - } -} - -unsafe impl Send for Mutex {} - -unsafe impl Sync for Mutex {} - -pub struct ReentrantMutex { - pub lock: UnsafeCell<i32>, - pub owner: UnsafeCell<usize>, - pub own_count: UnsafeCell<usize>, -} - -impl ReentrantMutex { - pub const fn uninitialized() -> Self { - ReentrantMutex { - lock: UnsafeCell::new(0), - owner: UnsafeCell::new(0), - own_count: UnsafeCell::new(0), - } - } - - #[inline] - pub unsafe fn init(&mut self) { - *self.lock.get() = 0; - *self.owner.get() = 0; - *self.own_count.get() = 0; - } - - /// Try to lock the mutex - #[inline] - pub unsafe fn try_lock(&self) -> bool { - let pid = getpid().unwrap(); - if *self.own_count.get() > 0 && *self.owner.get() == pid { - *self.own_count.get() += 1; - true - } else { - if mutex_try_lock(self.lock.get()) { - *self.owner.get() = pid; - *self.own_count.get() = 1; - true - } else { - false - } - } - } - - /// Lock the mutex - #[inline] - pub unsafe fn lock(&self) { - let pid = getpid().unwrap(); - if *self.own_count.get() > 0 && *self.owner.get() == pid { - *self.own_count.get() += 1; - } else { - mutex_lock(self.lock.get()); - *self.owner.get() = pid; - *self.own_count.get() = 1; - } - } - - /// Unlock the mutex - #[inline] - pub unsafe fn unlock(&self) { - let pid = getpid().unwrap(); - if *self.own_count.get() > 0 && *self.owner.get() == pid { - *self.own_count.get() -= 1; - if *self.own_count.get() == 0 { - *self.owner.get() = 0; - mutex_unlock(self.lock.get()); - } - } - } - - #[inline] - pub unsafe fn destroy(&self) { - *self.lock.get() = 0; - *self.owner.get() = 0; - *self.own_count.get() = 0; - } -} - -unsafe impl Send for ReentrantMutex {} - -unsafe impl Sync for ReentrantMutex {} diff --git a/src/libstd/sys/redox/net/dns/answer.rs b/src/libstd/sys/redox/net/dns/answer.rs deleted file mode 100644 index e9b406bc685..00000000000 --- a/src/libstd/sys/redox/net/dns/answer.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::string::String; -use crate::vec::Vec; - -#[derive(Clone, Debug)] -pub struct DnsAnswer { - pub name: String, - pub a_type: u16, - pub a_class: u16, - pub ttl_a: u16, - pub ttl_b: u16, - pub data: Vec<u8> -} diff --git a/src/libstd/sys/redox/net/dns/mod.rs b/src/libstd/sys/redox/net/dns/mod.rs deleted file mode 100644 index 6533e0d5efb..00000000000 --- a/src/libstd/sys/redox/net/dns/mod.rs +++ /dev/null @@ -1,205 +0,0 @@ -pub use self::answer::DnsAnswer; -pub use self::query::DnsQuery; - -use crate::slice; -use crate::u16; -use crate::string::String; -use crate::vec::Vec; - -mod answer; -mod query; - -#[unstable(feature = "n16", issue="0")] -#[allow(non_camel_case_types)] -#[derive(Copy, Clone, Debug, Default)] -#[repr(packed)] -pub struct n16 { - inner: u16 -} - -impl n16 { - #[unstable(feature = "n16", issue="0")] - pub fn as_bytes(&self) -> &[u8] { - unsafe { slice::from_raw_parts((&self.inner as *const u16) as *const u8, 2) } - } - - #[unstable(feature = "n16", issue="0")] - pub fn from_bytes(bytes: &[u8]) -> Self { - n16 { - inner: unsafe { slice::from_raw_parts(bytes.as_ptr() as *const u16, bytes.len()/2)[0] } - } - } -} - -#[unstable(feature = "n16", issue="0")] -impl From<u16> for n16 { - fn from(value: u16) -> Self { - n16 { - inner: value.to_be() - } - } -} - -#[unstable(feature = "n16", issue="0")] -impl From<n16> for u16 { - fn from(value: n16) -> Self { - u16::from_be(value.inner) - } -} - -#[derive(Clone, Debug)] -pub struct Dns { - pub transaction_id: u16, - pub flags: u16, - pub queries: Vec<DnsQuery>, - pub answers: Vec<DnsAnswer> -} - -impl Dns { - pub fn compile(&self) -> Vec<u8> { - let mut data = Vec::new(); - - macro_rules! push_u8 { - ($value:expr) => { - data.push($value); - }; - }; - - macro_rules! push_n16 { - ($value:expr) => { - data.extend_from_slice(n16::from($value).as_bytes()); - }; - }; - - push_n16!(self.transaction_id); - push_n16!(self.flags); - push_n16!(self.queries.len() as u16); - push_n16!(self.answers.len() as u16); - push_n16!(0); - push_n16!(0); - - for query in self.queries.iter() { - for part in query.name.split('.') { - push_u8!(part.len() as u8); - data.extend_from_slice(part.as_bytes()); - } - push_u8!(0); - push_n16!(query.q_type); - push_n16!(query.q_class); - } - - data - } - - pub fn parse(data: &[u8]) -> Result<Self, String> { - let name_ind = 0b11000000; - let mut i = 0; - - macro_rules! pop_u8 { - () => { - { - i += 1; - if i > data.len() { - return Err(format!("{}: {}: pop_u8", file!(), line!())); - } - data[i - 1] - } - }; - }; - - macro_rules! pop_n16 { - () => { - { - i += 2; - if i > data.len() { - return Err(format!("{}: {}: pop_n16", file!(), line!())); - } - u16::from(n16::from_bytes(&data[i - 2 .. i])) - } - }; - }; - - macro_rules! pop_data { - () => { - { - let mut data = Vec::new(); - - let data_len = pop_n16!(); - for _data_i in 0..data_len { - data.push(pop_u8!()); - } - - data - } - }; - }; - - macro_rules! pop_name { - () => { - { - let mut name = String::new(); - let old_i = i; - - loop { - let name_len = pop_u8!(); - if name_len & name_ind == name_ind { - i -= 1; - i = (pop_n16!() - ((name_ind as u16) << 8)) as usize; - continue; - } - if name_len == 0 { - break; - } - if ! name.is_empty() { - name.push('.'); - } - for _name_i in 0..name_len { - name.push(pop_u8!() as char); - } - } - - if i <= old_i { - i = old_i + 2; - } - - name - } - }; - }; - - let transaction_id = pop_n16!(); - let flags = pop_n16!(); - let queries_len = pop_n16!(); - let answers_len = pop_n16!(); - pop_n16!(); - pop_n16!(); - - let mut queries = Vec::new(); - for _query_i in 0..queries_len { - queries.push(DnsQuery { - name: pop_name!(), - q_type: pop_n16!(), - q_class: pop_n16!() - }); - } - - let mut answers = Vec::new(); - for _answer_i in 0..answers_len { - answers.push(DnsAnswer { - name: pop_name!(), - a_type: pop_n16!(), - a_class: pop_n16!(), - ttl_a: pop_n16!(), - ttl_b: pop_n16!(), - data: pop_data!() - }); - } - - Ok(Dns { - transaction_id, - flags, - queries, - answers, - }) - } -} diff --git a/src/libstd/sys/redox/net/dns/query.rs b/src/libstd/sys/redox/net/dns/query.rs deleted file mode 100644 index 65fb241b037..00000000000 --- a/src/libstd/sys/redox/net/dns/query.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::string::String; - -#[derive(Clone, Debug)] -pub struct DnsQuery { - pub name: String, - pub q_type: u16, - pub q_class: u16 -} diff --git a/src/libstd/sys/redox/net/mod.rs b/src/libstd/sys/redox/net/mod.rs deleted file mode 100644 index dbaa140ed8a..00000000000 --- a/src/libstd/sys/redox/net/mod.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::fs::File; -use crate::io::{Error, Read, self}; -use crate::iter::Iterator; -use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; -use crate::str::FromStr; -use crate::string::{String, ToString}; -use crate::sys::syscall::EINVAL; -use crate::time::{self, Duration}; -use crate::vec::{IntoIter, Vec}; -use crate::convert::{TryFrom, TryInto}; - -use self::dns::{Dns, DnsQuery}; - -pub use self::tcp::{TcpStream, TcpListener}; -pub use self::udp::UdpSocket; - -pub mod netc; - -mod dns; -mod tcp; -mod udp; - -pub struct LookupHost(IntoIter<SocketAddr>, u16); - -impl LookupHost { - pub fn port(&self) -> u16 { - self.1 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option<Self::Item> { - self.0.next() - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result<LookupHost> { - macro_rules! try_opt { - ($e:expr, $msg:expr) => ( - match $e { - Some(r) => r, - None => return Err(io::Error::new(io::ErrorKind::InvalidInput, - $msg)), - } - ) - } - - // split the string by ':' and convert the second part to u16 - let mut parts_iter = s.rsplitn(2, ':'); - let port_str = try_opt!(parts_iter.next(), "invalid socket address"); - let host = try_opt!(parts_iter.next(), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - - (host, port).try_into() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> { - let mut ip_string = String::new(); - File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?; - let ip: Vec<u8> = ip_string.trim().split('.').map(|part| part.parse::<u8>() - .unwrap_or(0)).collect(); - - let mut dns_string = String::new(); - File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?; - let dns: Vec<u8> = dns_string.trim().split('.').map(|part| part.parse::<u8>() - .unwrap_or(0)).collect(); - - if ip.len() == 4 && dns.len() == 4 { - let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap(); - let tid = (time.subsec_nanos() >> 16) as u16; - - let packet = Dns { - transaction_id: tid, - flags: 0x0100, - queries: vec![DnsQuery { - name: host.to_string(), - q_type: 0x0001, - q_class: 0x0001, - }], - answers: vec![] - }; - - let packet_data = packet.compile(); - - let my_ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]); - let dns_ip = Ipv4Addr::new(dns[0], dns[1], dns[2], dns[3]); - let socket = UdpSocket::bind(Ok(&SocketAddr::V4(SocketAddrV4::new(my_ip, 0))))?; - socket.set_read_timeout(Some(Duration::new(5, 0)))?; - socket.set_write_timeout(Some(Duration::new(5, 0)))?; - socket.connect(Ok(&SocketAddr::V4(SocketAddrV4::new(dns_ip, 53))))?; - socket.send(&packet_data)?; - - let mut buf = [0; 65536]; - let count = socket.recv(&mut buf)?; - - match Dns::parse(&buf[.. count]) { - Ok(response) => { - let mut addrs = vec![]; - for answer in response.answers.iter() { - if answer.a_type == 0x0001 && answer.a_class == 0x0001 - && answer.data.len() == 4 - { - let answer_ip = Ipv4Addr::new(answer.data[0], - answer.data[1], - answer.data[2], - answer.data[3]); - addrs.push(SocketAddr::V4(SocketAddrV4::new(answer_ip, 0))); - } - } - Ok(LookupHost(addrs.into_iter(), port)) - }, - Err(_err) => Err(Error::from_raw_os_error(EINVAL)) - } - } else { - Err(Error::from_raw_os_error(EINVAL)) - } - } -} - -fn path_to_peer_addr(path_str: &str) -> SocketAddr { - let mut parts = path_str.split('/').next().unwrap_or("").split(':').skip(1); - let host = Ipv4Addr::from_str(parts.next().unwrap_or("")).unwrap_or(Ipv4Addr::new(0, 0, 0, 0)); - let port = parts.next().unwrap_or("").parse::<u16>().unwrap_or(0); - SocketAddr::V4(SocketAddrV4::new(host, port)) -} - -fn path_to_local_addr(path_str: &str) -> SocketAddr { - let mut parts = path_str.split('/').nth(1).unwrap_or("").split(':'); - let host = Ipv4Addr::from_str(parts.next().unwrap_or("")).unwrap_or(Ipv4Addr::new(0, 0, 0, 0)); - let port = parts.next().unwrap_or("").parse::<u16>().unwrap_or(0); - SocketAddr::V4(SocketAddrV4::new(host, port)) -} diff --git a/src/libstd/sys/redox/net/netc.rs b/src/libstd/sys/redox/net/netc.rs deleted file mode 100644 index 420a15a4063..00000000000 --- a/src/libstd/sys/redox/net/netc.rs +++ /dev/null @@ -1,47 +0,0 @@ -pub type in_addr_t = u32; -pub type in_port_t = u16; - -pub type socklen_t = u32; -pub type sa_family_t = u16; - -pub const AF_INET: sa_family_t = 2; -pub const AF_INET6: sa_family_t = 23; - -#[derive(Copy, Clone)] -#[repr(C)] -pub struct in_addr { - pub s_addr: in_addr_t, -} - -#[derive(Copy, Clone)] -#[repr(align(4))] -#[repr(C)] -pub struct in6_addr { - pub s6_addr: [u8; 16], -} - -#[derive(Copy, Clone)] -#[repr(C)] -pub struct sockaddr { - pub sa_family: sa_family_t, - pub sa_data: [u8; 14], -} - -#[derive(Copy, Clone)] -#[repr(C)] -pub struct sockaddr_in { - pub sin_family: sa_family_t, - pub sin_port: in_port_t, - pub sin_addr: in_addr, - pub sin_zero: [u8; 8], -} - -#[derive(Copy, Clone)] -#[repr(C)] -pub struct sockaddr_in6 { - pub sin6_family: sa_family_t, - pub sin6_port: in_port_t, - pub sin6_flowinfo: u32, - pub sin6_addr: in6_addr, - pub sin6_scope_id: u32, -} diff --git a/src/libstd/sys/redox/net/tcp.rs b/src/libstd/sys/redox/net/tcp.rs deleted file mode 100644 index 5081c3de73c..00000000000 --- a/src/libstd/sys/redox/net/tcp.rs +++ /dev/null @@ -1,257 +0,0 @@ -use crate::cmp; -use crate::io::{self, Error, ErrorKind, Result, IoVec, IoVecMut}; -use crate::mem; -use crate::net::{SocketAddr, Shutdown}; -use crate::path::Path; -use crate::sys::fs::{File, OpenOptions}; -use crate::sys::syscall::TimeSpec; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; - -use super::{path_to_peer_addr, path_to_local_addr}; - -#[derive(Debug)] -pub struct TcpStream(File); - -impl TcpStream { - pub fn connect(addr: Result<&SocketAddr>) -> Result<TcpStream> { - let path = format!("tcp:{}", addr?); - let mut options = OpenOptions::new(); - options.read(true); - options.write(true); - Ok(TcpStream(File::open(Path::new(path.as_str()), &options)?)) - } - - pub fn connect_timeout(_addr: &SocketAddr, _timeout: Duration) -> Result<TcpStream> { - Err(Error::new(ErrorKind::Other, "TcpStream::connect_timeout not implemented")) - } - - pub fn duplicate(&self) -> Result<TcpStream> { - Ok(TcpStream(self.0.dup(&[])?)) - } - - pub fn read(&self, buf: &mut [u8]) -> Result<usize> { - self.0.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { - match bufs.iter_mut().find(|b| !b.is_empty()) { - Some(buf) => self.read(buf), - None => Ok(0), - } - } - - pub fn write(&self, buf: &[u8]) -> Result<usize> { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> { - match bufs.iter().find(|b| !b.is_empty()) { - Some(buf) => self.write(buf), - None => Ok(0), - } - } - - pub fn take_error(&self) -> Result<Option<Error>> { - Ok(None) - } - - pub fn peer_addr(&self) -> Result<SocketAddr> { - let path = self.0.path()?; - Ok(path_to_peer_addr(path.to_str().unwrap_or(""))) - } - - pub fn socket_addr(&self) -> Result<SocketAddr> { - let path = self.0.path()?; - Ok(path_to_local_addr(path.to_str().unwrap_or(""))) - } - - pub fn peek(&self, _buf: &mut [u8]) -> Result<usize> { - Err(Error::new(ErrorKind::Other, "TcpStream::peek not implemented")) - } - - pub fn shutdown(&self, _how: Shutdown) -> Result<()> { - Err(Error::new(ErrorKind::Other, "TcpStream::shutdown not implemented")) - } - - pub fn nodelay(&self) -> Result<bool> { - Err(Error::new(ErrorKind::Other, "TcpStream::nodelay not implemented")) - } - - pub fn nonblocking(&self) -> Result<bool> { - self.0.fd().nonblocking() - } - - pub fn only_v6(&self) -> Result<bool> { - Err(Error::new(ErrorKind::Other, "TcpStream::only_v6 not implemented")) - } - - pub fn ttl(&self) -> Result<u32> { - let mut ttl = [0]; - let file = self.0.dup(b"ttl")?; - file.read(&mut ttl)?; - Ok(ttl[0] as u32) - } - - pub fn read_timeout(&self) -> Result<Option<Duration>> { - let mut time = TimeSpec::default(); - let file = self.0.dup(b"read_timeout")?; - if file.read(&mut time)? >= mem::size_of::<TimeSpec>() { - Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32))) - } else { - Ok(None) - } - } - - pub fn write_timeout(&self) -> Result<Option<Duration>> { - let mut time = TimeSpec::default(); - let file = self.0.dup(b"write_timeout")?; - if file.read(&mut time)? >= mem::size_of::<TimeSpec>() { - Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32))) - } else { - Ok(None) - } - } - - pub fn set_nodelay(&self, _nodelay: bool) -> Result<()> { - Err(Error::new(ErrorKind::Other, "TcpStream::set_nodelay not implemented")) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { - self.0.fd().set_nonblocking(nonblocking) - } - - pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> { - Err(Error::new(ErrorKind::Other, "TcpStream::set_only_v6 not implemented")) - } - - pub fn set_ttl(&self, ttl: u32) -> Result<()> { - let file = self.0.dup(b"ttl")?; - file.write(&[cmp::min(ttl, 255) as u8])?; - Ok(()) - } - - pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> { - let file = self.0.dup(b"read_timeout")?; - if let Some(duration) = duration_option { - if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout")); - } - file.write(&TimeSpec { - tv_sec: duration.as_secs() as i64, - tv_nsec: duration.subsec_nanos() as i32 - })?; - } else { - file.write(&[])?; - } - Ok(()) - } - - pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> { - let file = self.0.dup(b"write_timeout")?; - if let Some(duration) = duration_option { - if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout")); - } - file.write(&TimeSpec { - tv_sec: duration.as_secs() as i64, - tv_nsec: duration.subsec_nanos() as i32 - })?; - } else { - file.write(&[])?; - } - Ok(()) - } -} - -impl AsInner<File> for TcpStream { - fn as_inner(&self) -> &File { &self.0 } -} - -impl FromInner<File> for TcpStream { - fn from_inner(file: File) -> TcpStream { - TcpStream(file) - } -} - -impl IntoInner<File> for TcpStream { - fn into_inner(self) -> File { self.0 } -} - -#[derive(Debug)] -pub struct TcpListener(File); - -impl TcpListener { - pub fn bind(addr: Result<&SocketAddr>) -> Result<TcpListener> { - let path = format!("tcp:/{}", addr?); - let mut options = OpenOptions::new(); - options.read(true); - options.write(true); - Ok(TcpListener(File::open(Path::new(path.as_str()), &options)?)) - } - - pub fn accept(&self) -> Result<(TcpStream, SocketAddr)> { - let file = self.0.dup(b"listen")?; - let path = file.path()?; - let peer_addr = path_to_peer_addr(path.to_str().unwrap_or("")); - Ok((TcpStream(file), peer_addr)) - } - - pub fn duplicate(&self) -> Result<TcpListener> { - Ok(TcpListener(self.0.dup(&[])?)) - } - - pub fn take_error(&self) -> Result<Option<Error>> { - Ok(None) - } - - pub fn socket_addr(&self) -> Result<SocketAddr> { - let path = self.0.path()?; - Ok(path_to_local_addr(path.to_str().unwrap_or(""))) - } - - pub fn nonblocking(&self) -> Result<bool> { - Err(Error::new(ErrorKind::Other, "TcpListener::nonblocking not implemented")) - } - - pub fn only_v6(&self) -> Result<bool> { - Err(Error::new(ErrorKind::Other, "TcpListener::only_v6 not implemented")) - } - - pub fn ttl(&self) -> Result<u32> { - let mut ttl = [0]; - let file = self.0.dup(b"ttl")?; - file.read(&mut ttl)?; - Ok(ttl[0] as u32) - } - - pub fn set_nonblocking(&self, _nonblocking: bool) -> Result<()> { - Err(Error::new(ErrorKind::Other, "TcpListener::set_nonblocking not implemented")) - } - - pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> { - Err(Error::new(ErrorKind::Other, "TcpListener::set_only_v6 not implemented")) - } - - pub fn set_ttl(&self, ttl: u32) -> Result<()> { - let file = self.0.dup(b"ttl")?; - file.write(&[cmp::min(ttl, 255) as u8])?; - Ok(()) - } -} - -impl AsInner<File> for TcpListener { - fn as_inner(&self) -> &File { &self.0 } -} - -impl FromInner<File> for TcpListener { - fn from_inner(file: File) -> TcpListener { - TcpListener(file) - } -} - -impl IntoInner<File> for TcpListener { - fn into_inner(self) -> File { self.0 } -} diff --git a/src/libstd/sys/redox/net/udp.rs b/src/libstd/sys/redox/net/udp.rs deleted file mode 100644 index b1a60b14570..00000000000 --- a/src/libstd/sys/redox/net/udp.rs +++ /dev/null @@ -1,232 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::cmp; -use crate::io::{self, Error, ErrorKind, Result}; -use crate::mem; -use crate::net::{SocketAddr, Ipv4Addr, Ipv6Addr}; -use crate::path::Path; -use crate::sys::fs::{File, OpenOptions}; -use crate::sys::syscall::TimeSpec; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; - -use super::{path_to_peer_addr, path_to_local_addr}; - -#[derive(Debug)] -pub struct UdpSocket(File, UnsafeCell<Option<SocketAddr>>); - -impl UdpSocket { - pub fn bind(addr: Result<&SocketAddr>) -> Result<UdpSocket> { - let path = format!("udp:/{}", addr?); - let mut options = OpenOptions::new(); - options.read(true); - options.write(true); - Ok(UdpSocket(File::open(Path::new(path.as_str()), &options)?, UnsafeCell::new(None))) - } - - fn get_conn(&self) -> &mut Option<SocketAddr> { - unsafe { &mut *(self.1.get()) } - } - - pub fn connect(&self, addr: Result<&SocketAddr>) -> Result<()> { - unsafe { *self.1.get() = Some(*addr?) }; - Ok(()) - } - - pub fn duplicate(&self) -> Result<UdpSocket> { - let new_bind = self.0.dup(&[])?; - let new_conn = *self.get_conn(); - Ok(UdpSocket(new_bind, UnsafeCell::new(new_conn))) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> { - let from = self.0.dup(b"listen")?; - let path = from.path()?; - let peer_addr = path_to_peer_addr(path.to_str().unwrap_or("")); - let count = from.read(buf)?; - Ok((count, peer_addr)) - } - - pub fn recv(&self, buf: &mut [u8]) -> Result<usize> { - if let Some(addr) = *self.get_conn() { - let from = self.0.dup(addr.to_string().as_bytes())?; - from.read(buf) - } else { - Err(Error::new(ErrorKind::Other, "UdpSocket::recv not connected")) - } - } - - pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> Result<usize> { - let to = self.0.dup(format!("{}", addr).as_bytes())?; - to.write(buf) - } - - pub fn send(&self, buf: &[u8]) -> Result<usize> { - if let Some(addr) = *self.get_conn() { - self.send_to(buf, &addr) - } else { - Err(Error::new(ErrorKind::Other, "UdpSocket::send not connected")) - } - } - - pub fn take_error(&self) -> Result<Option<Error>> { - Ok(None) - } - - pub fn socket_addr(&self) -> Result<SocketAddr> { - let path = self.0.path()?; - Ok(path_to_local_addr(path.to_str().unwrap_or(""))) - } - - pub fn peek(&self, _buf: &mut [u8]) -> Result<usize> { - Err(Error::new(ErrorKind::Other, "UdpSocket::peek not implemented")) - } - - pub fn peek_from(&self, _buf: &mut [u8]) -> Result<(usize, SocketAddr)> { - Err(Error::new(ErrorKind::Other, "UdpSocket::peek_from not implemented")) - } - - pub fn broadcast(&self) -> Result<bool> { - Err(Error::new(ErrorKind::Other, "UdpSocket::broadcast not implemented")) - } - - pub fn multicast_loop_v4(&self) -> Result<bool> { - Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_loop_v4 not implemented")) - } - - pub fn multicast_loop_v6(&self) -> Result<bool> { - Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_loop_v6 not implemented")) - } - - pub fn multicast_ttl_v4(&self) -> Result<u32> { - Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_ttl_v4 not implemented")) - } - - pub fn nonblocking(&self) -> Result<bool> { - self.0.fd().nonblocking() - } - - pub fn only_v6(&self) -> Result<bool> { - Err(Error::new(ErrorKind::Other, "UdpSocket::only_v6 not implemented")) - } - - pub fn ttl(&self) -> Result<u32> { - let mut ttl = [0]; - let file = self.0.dup(b"ttl")?; - file.read(&mut ttl)?; - Ok(ttl[0] as u32) - } - - pub fn read_timeout(&self) -> Result<Option<Duration>> { - let mut time = TimeSpec::default(); - let file = self.0.dup(b"read_timeout")?; - if file.read(&mut time)? >= mem::size_of::<TimeSpec>() { - Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32))) - } else { - Ok(None) - } - } - - pub fn write_timeout(&self) -> Result<Option<Duration>> { - let mut time = TimeSpec::default(); - let file = self.0.dup(b"write_timeout")?; - if file.read(&mut time)? >= mem::size_of::<TimeSpec>() { - Ok(Some(Duration::new(time.tv_sec as u64, time.tv_nsec as u32))) - } else { - Ok(None) - } - } - - pub fn set_broadcast(&self, _broadcast: bool) -> Result<()> { - Err(Error::new(ErrorKind::Other, "UdpSocket::set_broadcast not implemented")) - } - - pub fn set_multicast_loop_v4(&self, _multicast_loop_v4: bool) -> Result<()> { - Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_loop_v4 not implemented")) - } - - pub fn set_multicast_loop_v6(&self, _multicast_loop_v6: bool) -> Result<()> { - Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_loop_v6 not implemented")) - } - - pub fn set_multicast_ttl_v4(&self, _multicast_ttl_v4: u32) -> Result<()> { - Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_ttl_v4 not implemented")) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { - self.0.fd().set_nonblocking(nonblocking) - } - - pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> { - Err(Error::new(ErrorKind::Other, "UdpSocket::set_only_v6 not implemented")) - } - - pub fn set_ttl(&self, ttl: u32) -> Result<()> { - let file = self.0.dup(b"ttl")?; - file.write(&[cmp::min(ttl, 255) as u8])?; - Ok(()) - } - - pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> { - let file = self.0.dup(b"read_timeout")?; - if let Some(duration) = duration_option { - if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout")); - } - file.write(&TimeSpec { - tv_sec: duration.as_secs() as i64, - tv_nsec: duration.subsec_nanos() as i32 - })?; - } else { - file.write(&[])?; - } - Ok(()) - } - - pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> { - let file = self.0.dup(b"write_timeout")?; - if let Some(duration) = duration_option { - if duration.as_secs() == 0 && duration.subsec_nanos() == 0 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout")); - } - file.write(&TimeSpec { - tv_sec: duration.as_secs() as i64, - tv_nsec: duration.subsec_nanos() as i32 - })?; - } else { - file.write(&[])?; - } - Ok(()) - } - - pub fn join_multicast_v4(&self, _multiaddr: &Ipv4Addr, _interface: &Ipv4Addr) -> Result<()> { - Err(Error::new(ErrorKind::Other, "UdpSocket::join_multicast_v4 not implemented")) - } - - pub fn join_multicast_v6(&self, _multiaddr: &Ipv6Addr, _interface: u32) -> Result<()> { - Err(Error::new(ErrorKind::Other, "UdpSocket::join_multicast_v6 not implemented")) - } - - pub fn leave_multicast_v4(&self, _multiaddr: &Ipv4Addr, _interface: &Ipv4Addr) -> Result<()> { - Err(Error::new(ErrorKind::Other, "UdpSocket::leave_multicast_v4 not implemented")) - } - - pub fn leave_multicast_v6(&self, _multiaddr: &Ipv6Addr, _interface: u32) -> Result<()> { - Err(Error::new(ErrorKind::Other, "UdpSocket::leave_multicast_v6 not implemented")) - } -} - -impl AsInner<File> for UdpSocket { - fn as_inner(&self) -> &File { &self.0 } -} - -impl FromInner<File> for UdpSocket { - fn from_inner(file: File) -> UdpSocket { - UdpSocket(file, UnsafeCell::new(None)) - } -} - -impl IntoInner<File> for UdpSocket { - fn into_inner(self) -> File { self.0 } -} diff --git a/src/libstd/sys/redox/os_str.rs b/src/libstd/sys/redox/os_str.rs deleted file mode 100644 index 79b43458d00..00000000000 --- a/src/libstd/sys/redox/os_str.rs +++ /dev/null @@ -1,180 +0,0 @@ -/// The underlying OsString/OsStr implementation on Unix systems: just -/// a `Vec<u8>`/`[u8]`. - -use crate::borrow::Cow; -use crate::fmt; -use crate::str; -use crate::mem; -use crate::rc::Rc; -use crate::sync::Arc; -use crate::sys_common::{AsInner, IntoInner}; -use crate::sys_common::bytestring::debug_fmt_bytestring; - -use core::str::lossy::Utf8Lossy; - -#[derive(Clone, Hash)] -pub struct Buf { - pub inner: Vec<u8> -} - -pub struct Slice { - pub inner: [u8] -} - -impl fmt::Debug for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - debug_fmt_bytestring(&self.inner, formatter) - } -} - -impl fmt::Display for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) - } -} - -impl fmt::Debug for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self.as_slice(), formatter) - } -} - -impl fmt::Display for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self.as_slice(), formatter) - } -} - -impl IntoInner<Vec<u8>> for Buf { - fn into_inner(self) -> Vec<u8> { - self.inner - } -} - -impl AsInner<[u8]> for Buf { - fn as_inner(&self) -> &[u8] { - &self.inner - } -} - - -impl Buf { - pub fn from_string(s: String) -> Buf { - Buf { inner: s.into_bytes() } - } - - #[inline] - pub fn with_capacity(capacity: usize) -> Buf { - Buf { - inner: Vec::with_capacity(capacity) - } - } - - #[inline] - pub fn clear(&mut self) { - self.inner.clear() - } - - #[inline] - pub fn capacity(&self) -> usize { - self.inner.capacity() - } - - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.inner.reserve(additional) - } - - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.inner.reserve_exact(additional) - } - - #[inline] - pub fn shrink_to_fit(&mut self) { - self.inner.shrink_to_fit() - } - - #[inline] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.inner.shrink_to(min_capacity) - } - - pub fn as_slice(&self) -> &Slice { - unsafe { mem::transmute(&*self.inner) } - } - - pub fn into_string(self) -> Result<String, Buf> { - String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) - } - - pub fn push_slice(&mut self, s: &Slice) { - self.inner.extend_from_slice(&s.inner) - } - - #[inline] - pub fn into_box(self) -> Box<Slice> { - unsafe { mem::transmute(self.inner.into_boxed_slice()) } - } - - #[inline] - pub fn from_box(boxed: Box<Slice>) -> Buf { - let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; - Buf { inner: inner.into_vec() } - } - - #[inline] - pub fn into_arc(&self) -> Arc<Slice> { - self.as_slice().into_arc() - } - - #[inline] - pub fn into_rc(&self) -> Rc<Slice> { - self.as_slice().into_rc() - } -} - -impl Slice { - fn from_u8_slice(s: &[u8]) -> &Slice { - unsafe { mem::transmute(s) } - } - - pub fn from_str(s: &str) -> &Slice { - Slice::from_u8_slice(s.as_bytes()) - } - - pub fn to_str(&self) -> Option<&str> { - str::from_utf8(&self.inner).ok() - } - - pub fn to_string_lossy(&self) -> Cow<str> { - String::from_utf8_lossy(&self.inner) - } - - pub fn to_owned(&self) -> Buf { - Buf { inner: self.inner.to_vec() } - } - - #[inline] - pub fn into_box(&self) -> Box<Slice> { - let boxed: Box<[u8]> = self.inner.into(); - unsafe { mem::transmute(boxed) } - } - - pub fn empty_box() -> Box<Slice> { - let boxed: Box<[u8]> = Default::default(); - unsafe { mem::transmute(boxed) } - } - - #[inline] - pub fn into_arc(&self) -> Arc<Slice> { - let arc: Arc<[u8]> = Arc::from(&self.inner); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } - } - - #[inline] - pub fn into_rc(&self) -> Rc<Slice> { - let rc: Rc<[u8]> = Rc::from(&self.inner); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } - } -} diff --git a/src/libstd/sys/redox/path.rs b/src/libstd/sys/redox/path.rs deleted file mode 100644 index 618d61e6fcb..00000000000 --- a/src/libstd/sys/redox/path.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::ffi::OsStr; -use crate::path::Prefix; - -#[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(path: &OsStr) -> Option<Prefix> { - if let Some(path_str) = path.to_str() { - if let Some(_i) = path_str.find(':') { - // FIXME: Redox specific prefix - // Some(Prefix::Verbatim(OsStr::new(&path_str[..i]))) - None - } else { - None - } - } else { - None - } -} - -pub const MAIN_SEP_STR: &str = "/"; -pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/redox/pipe.rs b/src/libstd/sys/redox/pipe.rs deleted file mode 100644 index 911ba9c3f65..00000000000 --- a/src/libstd/sys/redox/pipe.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::io; -use crate::sys::{cvt, syscall}; -use crate::sys::fd::FileDesc; - -//////////////////////////////////////////////////////////////////////////////// -// Anonymous pipes -//////////////////////////////////////////////////////////////////////////////// - -pub struct AnonPipe(FileDesc); - -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - let mut fds = [0; 2]; - cvt(syscall::pipe2(&mut fds, syscall::O_CLOEXEC))?; - Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))) -} - -impl AnonPipe { - pub fn from_fd(fd: FileDesc) -> io::Result<AnonPipe> { - fd.set_cloexec()?; - Ok(AnonPipe(fd)) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { - self.0.read(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result<usize> { - self.0.write(buf) - } - - pub fn fd(&self) -> &FileDesc { &self.0 } - pub fn into_fd(self) -> FileDesc { self.0 } -} - -pub fn read2(p1: AnonPipe, - v1: &mut Vec<u8>, - p2: AnonPipe, - v2: &mut Vec<u8>) -> io::Result<()> { - //FIXME: Use event based I/O multiplexing - //unimplemented!() - - p1.0.read_to_end(v1)?; - p2.0.read_to_end(v2)?; - - Ok(()) - - /* - // Set both pipes into nonblocking mode as we're gonna be reading from both - // in the `select` loop below, and we wouldn't want one to block the other! - let p1 = p1.into_fd(); - let p2 = p2.into_fd(); - p1.set_nonblocking(true)?; - p2.set_nonblocking(true)?; - - loop { - // wait for either pipe to become readable using `select` - cvt_r(|| unsafe { - let mut read: libc::fd_set = mem::zeroed(); - libc::FD_SET(p1.raw(), &mut read); - libc::FD_SET(p2.raw(), &mut read); - libc::select(max + 1, &mut read, ptr::null_mut(), ptr::null_mut(), - ptr::null_mut()) - })?; - - // Read as much as we can from each pipe, ignoring EWOULDBLOCK or - // EAGAIN. If we hit EOF, then this will happen because the underlying - // reader will return Ok(0), in which case we'll see `Ok` ourselves. In - // this case we flip the other fd back into blocking mode and read - // whatever's leftover on that file descriptor. - let read = |fd: &FileDesc, dst: &mut Vec<u8>| { - match fd.read_to_end(dst) { - Ok(_) => Ok(true), - Err(e) => { - if e.raw_os_error() == Some(libc::EWOULDBLOCK) || - e.raw_os_error() == Some(libc::EAGAIN) { - Ok(false) - } else { - Err(e) - } - } - } - }; - if read(&p1, v1)? { - p2.set_nonblocking(false)?; - return p2.read_to_end(v2).map(|_| ()); - } - if read(&p2, v2)? { - p1.set_nonblocking(false)?; - return p1.read_to_end(v1).map(|_| ()); - } - } - */ -} diff --git a/src/libstd/sys/redox/process.rs b/src/libstd/sys/redox/process.rs deleted file mode 100644 index 8830cdf333e..00000000000 --- a/src/libstd/sys/redox/process.rs +++ /dev/null @@ -1,614 +0,0 @@ -use crate::env::{self, split_paths}; -use crate::ffi::{CStr, OsStr}; -use crate::fmt; -use crate::fs::File; -use crate::io::{self, prelude::*, BufReader, Error, ErrorKind, SeekFrom}; -use crate::os::unix::ffi::OsStrExt; -use crate::path::{Path, PathBuf}; -use crate::ptr; -use crate::sys::ext::fs::MetadataExt; -use crate::sys::ext::io::AsRawFd; -use crate::sys::fd::FileDesc; -use crate::sys::fs::{File as SysFile, OpenOptions}; -use crate::sys::os::{ENV_LOCK, environ}; -use crate::sys::pipe::{self, AnonPipe}; -use crate::sys::{cvt, syscall}; -use crate::sys_common::process::{CommandEnv, DefaultEnvKey}; - -use libc::{EXIT_SUCCESS, EXIT_FAILURE}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - // Currently we try hard to ensure that the call to `.exec()` doesn't - // actually allocate any memory. While many platforms try to ensure that - // memory allocation works after a fork in a multithreaded process, it's - // been observed to be buggy and somewhat unreliable, so we do our best to - // just not do it at all! - // - // Along those lines, the `argv` and `envp` raw pointers here are exactly - // what's gonna get passed to `execvp`. The `argv` array starts with the - // `program` and ends with a NULL, and the `envp` pointer, if present, is - // also null-terminated. - // - // Right now we don't support removing arguments, so there's no much fancy - // support there, but we support adding and removing environment variables, - // so a side table is used to track where in the `envp` array each key is - // located. Whenever we add a key we update it in place if it's already - // present, and whenever we remove a key we update the locations of all - // other keys. - program: String, - args: Vec<String>, - env: CommandEnv<DefaultEnvKey>, - - cwd: Option<String>, - uid: Option<u32>, - gid: Option<u32>, - saw_nul: bool, - closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>, - stdin: Option<Stdio>, - stdout: Option<Stdio>, - stderr: Option<Stdio>, -} - -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option<AnonPipe>, - pub stdout: Option<AnonPipe>, - pub stderr: Option<AnonPipe>, -} - -// passed to do_exec() with configuration of what the child stdio should look -// like -struct ChildPipes { - stdin: ChildStdio, - stdout: ChildStdio, - stderr: ChildStdio, -} - -enum ChildStdio { - Inherit, - Explicit(usize), - Owned(FileDesc), -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, - Fd(FileDesc), -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - Command { - program: program.to_str().unwrap().to_owned(), - args: Vec::new(), - env: Default::default(), - cwd: None, - uid: None, - gid: None, - saw_nul: false, - closures: Vec::new(), - stdin: None, - stdout: None, - stderr: None, - } - } - - pub fn arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_str().unwrap().to_owned()); - } - - pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> { - &mut self.env - } - - pub fn cwd(&mut self, dir: &OsStr) { - self.cwd = Some(dir.to_str().unwrap().to_owned()); - } - pub fn uid(&mut self, id: u32) { - self.uid = Some(id); - } - pub fn gid(&mut self, id: u32) { - self.gid = Some(id); - } - - pub unsafe fn pre_exec( - &mut self, - f: Box<dyn FnMut() -> io::Result<()> + Send + Sync>, - ) { - self.closures.push(f); - } - - pub fn stdin(&mut self, stdin: Stdio) { - self.stdin = Some(stdin); - } - pub fn stdout(&mut self, stdout: Stdio) { - self.stdout = Some(stdout); - } - pub fn stderr(&mut self, stderr: Stdio) { - self.stderr = Some(stderr); - } - - pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) - -> io::Result<(Process, StdioPipes)> { - const CLOEXEC_MSG_FOOTER: &[u8] = b"NOEX"; - - if self.saw_nul { - return Err(io::Error::new(ErrorKind::InvalidInput, - "nul byte found in provided data")); - } - - let (ours, theirs) = self.setup_io(default, needs_stdin)?; - let (input, output) = pipe::anon_pipe()?; - - let pid = unsafe { - match cvt(syscall::clone(0))? { - 0 => { - drop(input); - let err = self.do_exec(theirs); - let errno = err.raw_os_error().unwrap_or(syscall::EINVAL) as u32; - let bytes = [ - (errno >> 24) as u8, - (errno >> 16) as u8, - (errno >> 8) as u8, - (errno >> 0) as u8, - CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], - CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] - ]; - // pipe I/O up to PIPE_BUF bytes should be atomic, and then - // we want to be sure we *don't* run at_exit destructors as - // we're being torn down regardless - assert!(output.write(&bytes).is_ok()); - let _ = syscall::exit(1); - panic!("failed to exit"); - } - n => n, - } - }; - - let mut p = Process { pid: pid, status: None }; - drop(output); - let mut bytes = [0; 8]; - - // loop to handle EINTR - loop { - match input.read(&mut bytes) { - Ok(0) => return Ok((p, ours)), - Ok(8) => { - assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), - "Validation on the CLOEXEC pipe failed: {:?}", bytes); - let errno = combine(&bytes[0.. 4]); - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - return Err(Error::from_raw_os_error(errno)) - } - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => { - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - panic!("the CLOEXEC pipe failed: {:?}", e) - }, - Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - panic!("short read on the CLOEXEC pipe") - } - } - } - - fn combine(arr: &[u8]) -> i32 { - let a = arr[0] as u32; - let b = arr[1] as u32; - let c = arr[2] as u32; - let d = arr[3] as u32; - - ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 - } - } - - pub fn exec(&mut self, default: Stdio) -> io::Error { - if self.saw_nul { - return io::Error::new(ErrorKind::InvalidInput, - "nul byte found in provided data") - } - - match self.setup_io(default, true) { - Ok((_, theirs)) => unsafe { self.do_exec(theirs) }, - Err(e) => e, - } - } - - // And at this point we've reached a special time in the life of the - // child. The child must now be considered hamstrung and unable to - // do anything other than syscalls really. Consider the following - // scenario: - // - // 1. Thread A of process 1 grabs the malloc() mutex - // 2. Thread B of process 1 forks(), creating thread C - // 3. Thread C of process 2 then attempts to malloc() - // 4. The memory of process 2 is the same as the memory of - // process 1, so the mutex is locked. - // - // This situation looks a lot like deadlock, right? It turns out - // that this is what pthread_atfork() takes care of, which is - // presumably implemented across platforms. The first thing that - // threads to *before* forking is to do things like grab the malloc - // mutex, and then after the fork they unlock it. - // - // Despite this information, libnative's spawn has been witnessed to - // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but - // all collected backtraces point at malloc/free traffic in the - // child spawned process. - // - // For this reason, the block of code below should contain 0 - // invocations of either malloc of free (or their related friends). - // - // As an example of not having malloc/free traffic, we don't close - // this file descriptor by dropping the FileDesc (which contains an - // allocation). Instead we just close it manually. This will never - // have the drop glue anyway because this code never returns (the - // child will either exec() or invoke syscall::exit) - unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error { - macro_rules! t { - ($e:expr) => (match $e { - Ok(e) => e, - Err(e) => return e, - }) - } - - if let Some(fd) = stdio.stderr.fd() { - t!(cvt(syscall::dup2(fd, 2, &[]))); - let mut flags = t!(cvt(syscall::fcntl(2, syscall::F_GETFD, 0))); - flags &= ! syscall::O_CLOEXEC; - t!(cvt(syscall::fcntl(2, syscall::F_SETFD, flags))); - } - if let Some(fd) = stdio.stdout.fd() { - t!(cvt(syscall::dup2(fd, 1, &[]))); - let mut flags = t!(cvt(syscall::fcntl(1, syscall::F_GETFD, 0))); - flags &= ! syscall::O_CLOEXEC; - t!(cvt(syscall::fcntl(1, syscall::F_SETFD, flags))); - } - if let Some(fd) = stdio.stdin.fd() { - t!(cvt(syscall::dup2(fd, 0, &[]))); - let mut flags = t!(cvt(syscall::fcntl(0, syscall::F_GETFD, 0))); - flags &= ! syscall::O_CLOEXEC; - t!(cvt(syscall::fcntl(0, syscall::F_SETFD, flags))); - } - - if let Some(g) = self.gid { - t!(cvt(syscall::setregid(g as usize, g as usize))); - } - if let Some(u) = self.uid { - t!(cvt(syscall::setreuid(u as usize, u as usize))); - } - if let Some(ref cwd) = self.cwd { - t!(cvt(syscall::chdir(cwd))); - } - - for callback in self.closures.iter_mut() { - t!(callback()); - } - - self.env.apply(); - - let program = if self.program.contains(':') || self.program.contains('/') { - Some(PathBuf::from(&self.program)) - } else if let Ok(path_env) = env::var("PATH") { - let mut program = None; - for mut path in split_paths(&path_env) { - path.push(&self.program); - if path.exists() { - program = Some(path); - break; - } - } - program - } else { - None - }; - - let mut file = if let Some(program) = program { - t!(File::open(program.as_os_str())) - } else { - return io::Error::from_raw_os_error(syscall::ENOENT); - }; - - // Push all the arguments - let mut args: Vec<[usize; 2]> = Vec::with_capacity(1 + self.args.len()); - - let interpreter = { - let mut reader = BufReader::new(&file); - - let mut shebang = [0; 2]; - let mut read = 0; - loop { - match t!(reader.read(&mut shebang[read..])) { - 0 => break, - n => read += n, - } - } - - if &shebang == b"#!" { - // This is an interpreted script. - // First of all, since we'll be passing another file to - // fexec(), we need to manually check that we have permission - // to execute this file: - let uid = t!(cvt(syscall::getuid())); - let gid = t!(cvt(syscall::getgid())); - let meta = t!(file.metadata()); - - let mode = if uid == meta.uid() as usize { - meta.mode() >> 3*2 & 0o7 - } else if gid == meta.gid() as usize { - meta.mode() >> 3*1 & 0o7 - } else { - meta.mode() & 0o7 - }; - if mode & 1 == 0 { - return io::Error::from_raw_os_error(syscall::EPERM); - } - - // Second of all, we need to actually read which interpreter it wants - let mut interpreter = Vec::new(); - t!(reader.read_until(b'\n', &mut interpreter)); - // Pop one trailing newline, if any - if interpreter.ends_with(&[b'\n']) { - interpreter.pop().unwrap(); - } - - // FIXME: Here we could just reassign `file` directly, if it - // wasn't for lexical lifetimes. Remove the whole `let - // interpreter = { ... };` hack once NLL lands. - // NOTE: Although DO REMEMBER to make sure the interpreter path - // still lives long enough to reach fexec. - Some(interpreter) - } else { - None - } - }; - if let Some(ref interpreter) = interpreter { - let path: &OsStr = OsStr::from_bytes(&interpreter); - file = t!(File::open(path)); - - args.push([interpreter.as_ptr() as usize, interpreter.len()]); - } else { - t!(file.seek(SeekFrom::Start(0))); - } - - args.push([self.program.as_ptr() as usize, self.program.len()]); - args.extend(self.args.iter().map(|arg| [arg.as_ptr() as usize, arg.len()])); - - // Push all the variables - let mut vars: Vec<[usize; 2]> = Vec::new(); - { - let _guard = ENV_LOCK.lock(); - let mut environ = *environ(); - while *environ != ptr::null() { - let var = CStr::from_ptr(*environ).to_bytes(); - vars.push([var.as_ptr() as usize, var.len()]); - environ = environ.offset(1); - } - } - - if let Err(err) = syscall::fexec(file.as_raw_fd(), &args, &vars) { - io::Error::from_raw_os_error(err.errno as i32) - } else { - panic!("return from exec without err"); - } - } - - - fn setup_io(&self, default: Stdio, needs_stdin: bool) - -> io::Result<(StdioPipes, ChildPipes)> { - let null = Stdio::Null; - let default_stdin = if needs_stdin {&default} else {&null}; - let stdin = self.stdin.as_ref().unwrap_or(default_stdin); - let stdout = self.stdout.as_ref().unwrap_or(&default); - let stderr = self.stderr.as_ref().unwrap_or(&default); - let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; - let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; - let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; - let ours = StdioPipes { - stdin: our_stdin, - stdout: our_stdout, - stderr: our_stderr, - }; - let theirs = ChildPipes { - stdin: their_stdin, - stdout: their_stdout, - stderr: their_stderr, - }; - Ok((ours, theirs)) - } -} - -impl Stdio { - fn to_child_stdio(&self, readable: bool) - -> io::Result<(ChildStdio, Option<AnonPipe>)> { - match *self { - Stdio::Inherit => Ok((ChildStdio::Inherit, None)), - - // Make sure that the source descriptors are not an stdio - // descriptor, otherwise the order which we set the child's - // descriptors may blow away a descriptor which we are hoping to - // save. For example, suppose we want the child's stderr to be the - // parent's stdout, and the child's stdout to be the parent's - // stderr. No matter which we dup first, the second will get - // overwritten prematurely. - Stdio::Fd(ref fd) => { - if fd.raw() <= 2 { - Ok((ChildStdio::Owned(fd.duplicate()?), None)) - } else { - Ok((ChildStdio::Explicit(fd.raw()), None)) - } - } - - Stdio::MakePipe => { - let (reader, writer) = pipe::anon_pipe()?; - let (ours, theirs) = if readable { - (writer, reader) - } else { - (reader, writer) - }; - Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) - } - - Stdio::Null => { - let mut opts = OpenOptions::new(); - opts.read(readable); - opts.write(!readable); - let fd = SysFile::open(Path::new("null:"), &opts)?; - Ok((ChildStdio::Owned(fd.into_fd()), None)) - } - } - } -} - -impl From<AnonPipe> for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_fd()) - } -} - -impl From<SysFile> for Stdio { - fn from(file: SysFile) -> Stdio { - Stdio::Fd(file.into_fd()) - } -} - -impl ChildStdio { - fn fd(&self) -> Option<usize> { - match *self { - ChildStdio::Inherit => None, - ChildStdio::Explicit(fd) => Some(fd), - ChildStdio::Owned(ref fd) => Some(fd.raw()), - } - } -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.program)?; - for arg in &self.args { - write!(f, " {:?}", arg)?; - } - Ok(()) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - -/// Unix exit statuses -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatus(i32); - -impl ExitStatus { - fn exited(&self) -> bool { - self.0 & 0x7F == 0 - } - - pub fn success(&self) -> bool { - self.code() == Some(0) - } - - pub fn code(&self) -> Option<i32> { - if self.exited() { - Some((self.0 >> 8) & 0xFF) - } else { - None - } - } - - pub fn signal(&self) -> Option<i32> { - if !self.exited() { - Some(self.0 & 0x7F) - } else { - None - } - } -} - -impl From<i32> for ExitStatus { - fn from(a: i32) -> ExitStatus { - ExitStatus(a) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(code) = self.code() { - write!(f, "exit code: {}", code) - } else { - let signal = self.signal().unwrap(); - write!(f, "signal: {}", signal) - } - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(u8); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); - pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); - - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} - -/// The unique ID of the process (this should never be negative). -pub struct Process { - pid: usize, - status: Option<ExitStatus>, -} - -impl Process { - pub fn id(&self) -> u32 { - self.pid as u32 - } - - pub fn kill(&mut self) -> io::Result<()> { - // If we've already waited on this process then the pid can be recycled - // and used for another process, and we probably shouldn't be killing - // random processes, so just return an error. - if self.status.is_some() { - Err(Error::new(ErrorKind::InvalidInput, - "invalid argument: can't kill an exited process")) - } else { - cvt(syscall::kill(self.pid, syscall::SIGKILL))?; - Ok(()) - } - } - - pub fn wait(&mut self) -> io::Result<ExitStatus> { - if let Some(status) = self.status { - return Ok(status) - } - let mut status = 0; - cvt(syscall::waitpid(self.pid, &mut status, 0))?; - self.status = Some(ExitStatus(status as i32)); - Ok(ExitStatus(status as i32)) - } - - pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { - if let Some(status) = self.status { - return Ok(Some(status)) - } - let mut status = 0; - let pid = cvt(syscall::waitpid(self.pid, &mut status, syscall::WNOHANG))?; - if pid == 0 { - Ok(None) - } else { - self.status = Some(ExitStatus(status as i32)); - Ok(Some(ExitStatus(status as i32))) - } - } -} diff --git a/src/libstd/sys/redox/rand.rs b/src/libstd/sys/redox/rand.rs deleted file mode 100644 index 5b58d1782bf..00000000000 --- a/src/libstd/sys/redox/rand.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn hashmap_random_keys() -> (u64, u64) { - (0, 0) -} diff --git a/src/libstd/sys/redox/rwlock.rs b/src/libstd/sys/redox/rwlock.rs deleted file mode 100644 index 990e7551114..00000000000 --- a/src/libstd/sys/redox/rwlock.rs +++ /dev/null @@ -1,51 +0,0 @@ -use super::mutex::Mutex; - -pub struct RWLock { - mutex: Mutex -} - -unsafe impl Send for RWLock {} -unsafe impl Sync for RWLock {} - -impl RWLock { - pub const fn new() -> RWLock { - RWLock { - mutex: Mutex::new() - } - } - - #[inline] - pub unsafe fn read(&self) { - self.mutex.lock(); - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - self.mutex.try_lock() - } - - #[inline] - pub unsafe fn write(&self) { - self.mutex.lock(); - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - self.mutex.try_lock() - } - - #[inline] - pub unsafe fn read_unlock(&self) { - self.mutex.unlock(); - } - - #[inline] - pub unsafe fn write_unlock(&self) { - self.mutex.unlock(); - } - - #[inline] - pub unsafe fn destroy(&self) { - self.mutex.destroy(); - } -} diff --git a/src/libstd/sys/redox/syscall/arch/arm.rs b/src/libstd/sys/redox/syscall/arch/arm.rs deleted file mode 100644 index e640f7ed376..00000000000 --- a/src/libstd/sys/redox/syscall/arch/arm.rs +++ /dev/null @@ -1,73 +0,0 @@ -use super::error::{Error, Result}; - -pub unsafe fn syscall0(mut a: usize) -> Result<usize> { - asm!("swi $$0" - : "={r0}"(a) - : "{r7}"(a) - : "memory" - : "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> { - asm!("swi $$0" - : "={r0}"(a) - : "{r7}"(a), "{r0}"(b) - : "memory" - : "volatile"); - - Error::demux(a) -} - -// Clobbers all registers - special for clone -pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> { - asm!("swi $$0" - : "={r0}"(a) - : "{r7}"(a), "{r0}"(b) - : "memory", "r0", "r1", "r2", "r3", "r4" - : "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> { - asm!("swi $$0" - : "={r0}"(a) - : "{r7}"(a), "{r0}"(b), "{r1}"(c) - : "memory" - : "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> { - asm!("swi $$0" - : "={r0}"(a) - : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d) - : "memory" - : "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> { - asm!("swi $$0" - : "={r0}"(a) - : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d), "{r3}"(e) - : "memory" - : "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - -> Result<usize> { - asm!("swi $$0" - : "={r0}"(a) - : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d), "{r3}"(e), "{r4}"(f) - : "memory" - : "volatile"); - - Error::demux(a) -} diff --git a/src/libstd/sys/redox/syscall/arch/x86.rs b/src/libstd/sys/redox/syscall/arch/x86.rs deleted file mode 100644 index 0cd6409bb06..00000000000 --- a/src/libstd/sys/redox/syscall/arch/x86.rs +++ /dev/null @@ -1,73 +0,0 @@ -use super::error::{Error, Result}; - -pub unsafe fn syscall0(mut a: usize) -> Result<usize> { - asm!("int 0x80" - : "={eax}"(a) - : "{eax}"(a) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> { - asm!("int 0x80" - : "={eax}"(a) - : "{eax}"(a), "{ebx}"(b) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -// Clobbers all registers - special for clone -pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> { - asm!("int 0x80" - : "={eax}"(a) - : "{eax}"(a), "{ebx}"(b) - : "memory", "ebx", "ecx", "edx", "esi", "edi" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> { - asm!("int 0x80" - : "={eax}"(a) - : "{eax}"(a), "{ebx}"(b), "{ecx}"(c) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> { - asm!("int 0x80" - : "={eax}"(a) - : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> { - asm!("int 0x80" - : "={eax}"(a) - : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d), "{esi}"(e) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - -> Result<usize> { - asm!("int 0x80" - : "={eax}"(a) - : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d), "{esi}"(e), "{edi}"(f) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} diff --git a/src/libstd/sys/redox/syscall/arch/x86_64.rs b/src/libstd/sys/redox/syscall/arch/x86_64.rs deleted file mode 100644 index 52ad01bd4a8..00000000000 --- a/src/libstd/sys/redox/syscall/arch/x86_64.rs +++ /dev/null @@ -1,74 +0,0 @@ -use super::error::{Error, Result}; - -pub unsafe fn syscall0(mut a: usize) -> Result<usize> { - asm!("int 0x80" - : "={rax}"(a) - : "{rax}"(a) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> { - asm!("int 0x80" - : "={rax}"(a) - : "{rax}"(a), "{rbx}"(b) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -// Clobbers all registers - special for clone -pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> { - asm!("int 0x80" - : "={rax}"(a) - : "{rax}"(a), "{rbx}"(b) - : "memory", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", - "r9", "r10", "r11", "r12", "r13", "r14", "r15" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> { - asm!("int 0x80" - : "={rax}"(a) - : "{rax}"(a), "{rbx}"(b), "{rcx}"(c) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> { - asm!("int 0x80" - : "={rax}"(a) - : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> { - asm!("int 0x80" - : "={rax}"(a) - : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d), "{rsi}"(e) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} - -pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - -> Result<usize> { - asm!("int 0x80" - : "={rax}"(a) - : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d), "{rsi}"(e), "{rdi}"(f) - : "memory" - : "intel", "volatile"); - - Error::demux(a) -} diff --git a/src/libstd/sys/redox/syscall/call.rs b/src/libstd/sys/redox/syscall/call.rs deleted file mode 100644 index b9abb48a8d3..00000000000 --- a/src/libstd/sys/redox/syscall/call.rs +++ /dev/null @@ -1,348 +0,0 @@ -use super::arch::*; -use super::data::{SigAction, Stat, StatVfs, TimeSpec}; -use super::error::Result; -use super::number::*; - -use core::{mem, ptr}; - -// Signal restorer -extern "C" fn restorer() -> ! { - sigreturn().unwrap(); - unreachable!(); -} - -/// Set the end of the process's heap -/// -/// When `addr` is `0`, this function will return the current break. -/// -/// When `addr` is nonzero, this function will attempt to set the end of the process's -/// heap to `addr` and return the new program break. The new program break should be -/// checked by the allocator, it may not be exactly `addr`, as it may be aligned to a page -/// boundary. -/// -/// On error, `Err(ENOMEM)` will be returned indicating that no memory is available -pub unsafe fn brk(addr: usize) -> Result<usize> { - syscall1(SYS_BRK, addr) -} - -/// Changes the process's working directory. -/// -/// This function will attempt to set the process's working directory to `path`, which can be -/// either a relative, scheme relative, or absolute path. -/// -/// On success, `Ok(0)` will be returned. On error, one of the following errors will be returned. -/// -/// # Errors -/// -/// * `EACCES` - permission is denied for one of the components of `path`, or `path` -/// * `EFAULT` - `path` does not point to the process's addressable memory -/// * `EIO` - an I/O error occurred -/// * `ENOENT` - `path` does not exit -/// * `ENOTDIR` - `path` is not a directory -pub fn chdir<T: AsRef<[u8]>>(path: T) -> Result<usize> { - unsafe { syscall2(SYS_CHDIR, path.as_ref().as_ptr() as usize, path.as_ref().len()) } -} - -pub fn chmod<T: AsRef<[u8]>>(path: T, mode: usize) -> Result<usize> { - unsafe { syscall3(SYS_CHMOD, path.as_ref().as_ptr() as usize, path.as_ref().len(), mode) } -} - -/// Produces a fork of the current process, or a new process thread. -pub unsafe fn clone(flags: usize) -> Result<usize> { - syscall1_clobber(SYS_CLONE, flags) -} - -/// Closes a file. -pub fn close(fd: usize) -> Result<usize> { - unsafe { syscall1(SYS_CLOSE, fd) } -} - -/// Gets the current system time. -pub fn clock_gettime(clock: usize, tp: &mut TimeSpec) -> Result<usize> { - unsafe { syscall2(SYS_CLOCK_GETTIME, clock, tp as *mut TimeSpec as usize) } -} - -/// Copies and transforms a file descriptor. -pub fn dup(fd: usize, buf: &[u8]) -> Result<usize> { - unsafe { syscall3(SYS_DUP, fd, buf.as_ptr() as usize, buf.len()) } -} - -/// Copies and transforms a file descriptor. -pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result<usize> { - unsafe { syscall4(SYS_DUP2, fd, newfd, buf.as_ptr() as usize, buf.len()) } -} - -/// Exits the current process. -pub fn exit(status: usize) -> Result<usize> { - unsafe { syscall1(SYS_EXIT, status) } -} - -/// Changes file permissions. -pub fn fchmod(fd: usize, mode: u16) -> Result<usize> { - unsafe { syscall2(SYS_FCHMOD, fd, mode as usize) } - -} - -/// Changes file ownership. -pub fn fchown(fd: usize, uid: u32, gid: u32) -> Result<usize> { - unsafe { syscall3(SYS_FCHOWN, fd, uid as usize, gid as usize) } - -} - -/// Changes file descriptor flags. -pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> { - unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) } -} - -/// Replaces the current process with a new executable. -pub fn fexec(fd: usize, args: &[[usize; 2]], vars: &[[usize; 2]]) -> Result<usize> { - unsafe { syscall5(SYS_FEXEC, fd, args.as_ptr() as usize, args.len(), - vars.as_ptr() as usize, vars.len()) } -} - -/// Maps a file into memory. -pub unsafe fn fmap(fd: usize, offset: usize, size: usize) -> Result<usize> { - syscall3(SYS_FMAP, fd, offset, size) -} - -/// Unmaps a memory-mapped file. -pub unsafe fn funmap(addr: usize) -> Result<usize> { - syscall1(SYS_FUNMAP, addr) -} - -/// Retrieves the canonical path of a file. -pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> { - unsafe { syscall3(SYS_FPATH, fd, buf.as_mut_ptr() as usize, buf.len()) } -} - -/// Renames a file. -pub fn frename<T: AsRef<[u8]>>(fd: usize, path: T) -> Result<usize> { - unsafe { syscall3(SYS_FRENAME, fd, path.as_ref().as_ptr() as usize, path.as_ref().len()) } -} - -/// Gets metadata about a file. -pub fn fstat(fd: usize, stat: &mut Stat) -> Result<usize> { - unsafe { syscall3(SYS_FSTAT, fd, stat as *mut Stat as usize, mem::size_of::<Stat>()) } -} - -/// Gets metadata about a filesystem. -pub fn fstatvfs(fd: usize, stat: &mut StatVfs) -> Result<usize> { - unsafe { syscall3(SYS_FSTATVFS, fd, stat as *mut StatVfs as usize, mem::size_of::<StatVfs>()) } -} - -/// Syncs a file descriptor to its underlying medium. -pub fn fsync(fd: usize) -> Result<usize> { - unsafe { syscall1(SYS_FSYNC, fd) } -} - -/// Truncate or extend a file to a specified length -pub fn ftruncate(fd: usize, len: usize) -> Result<usize> { - unsafe { syscall2(SYS_FTRUNCATE, fd, len) } -} - -// Change modify and/or access times -pub fn futimens(fd: usize, times: &[TimeSpec]) -> Result<usize> { - unsafe { syscall3(SYS_FUTIMENS, fd, times.as_ptr() as usize, - times.len() * mem::size_of::<TimeSpec>()) } -} - -/// Fast userspace mutex -pub unsafe fn futex(addr: *mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) - -> Result<usize> { - syscall5(SYS_FUTEX, addr as usize, op, (val as isize) as usize, val2, addr2 as usize) -} - -/// Gets the current working directory. -pub fn getcwd(buf: &mut [u8]) -> Result<usize> { - unsafe { syscall2(SYS_GETCWD, buf.as_mut_ptr() as usize, buf.len()) } -} - -/// Gets the effective group ID. -pub fn getegid() -> Result<usize> { - unsafe { syscall0(SYS_GETEGID) } -} - -/// Gets the effective namespace. -pub fn getens() -> Result<usize> { - unsafe { syscall0(SYS_GETENS) } -} - -/// Gets the effective user ID. -pub fn geteuid() -> Result<usize> { - unsafe { syscall0(SYS_GETEUID) } -} - -/// Gets the current group ID. -pub fn getgid() -> Result<usize> { - unsafe { syscall0(SYS_GETGID) } -} - -/// Gets the current namespace. -pub fn getns() -> Result<usize> { - unsafe { syscall0(SYS_GETNS) } -} - -/// Gets the current process ID. -pub fn getpid() -> Result<usize> { - unsafe { syscall0(SYS_GETPID) } -} - -/// Gets the process group ID. -pub fn getpgid(pid: usize) -> Result<usize> { - unsafe { syscall1(SYS_GETPGID, pid) } -} - -/// Gets the parent process ID. -pub fn getppid() -> Result<usize> { - unsafe { syscall0(SYS_GETPPID) } -} - -/// Gets the current user ID. -pub fn getuid() -> Result<usize> { - unsafe { syscall0(SYS_GETUID) } -} - -/// Sets the I/O privilege level -pub unsafe fn iopl(level: usize) -> Result<usize> { - syscall1(SYS_IOPL, level) -} - -/// Sends a signal `sig` to the process identified by `pid`. -pub fn kill(pid: usize, sig: usize) -> Result<usize> { - unsafe { syscall2(SYS_KILL, pid, sig) } -} - -/// Creates a link to a file. -pub unsafe fn link(old: *const u8, new: *const u8) -> Result<usize> { - syscall2(SYS_LINK, old as usize, new as usize) -} - -/// Seeks to `offset` bytes in a file descriptor. -pub fn lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> { - unsafe { syscall3(SYS_LSEEK, fd, offset as usize, whence) } -} - -/// Makes a new scheme namespace. -pub fn mkns(schemes: &[[usize; 2]]) -> Result<usize> { - unsafe { syscall2(SYS_MKNS, schemes.as_ptr() as usize, schemes.len()) } -} - -/// Sleeps for the time specified in `req`. -pub fn nanosleep(req: &TimeSpec, rem: &mut TimeSpec) -> Result<usize> { - unsafe { syscall2(SYS_NANOSLEEP, req as *const TimeSpec as usize, - rem as *mut TimeSpec as usize) } -} - -/// Opens a file. -pub fn open<T: AsRef<[u8]>>(path: T, flags: usize) -> Result<usize> { - unsafe { syscall3(SYS_OPEN, path.as_ref().as_ptr() as usize, path.as_ref().len(), flags) } -} - -/// Allocates pages, linearly in physical memory. -pub unsafe fn physalloc(size: usize) -> Result<usize> { - syscall1(SYS_PHYSALLOC, size) -} - -/// Frees physically allocated pages. -pub unsafe fn physfree(physical_address: usize, size: usize) -> Result<usize> { - syscall2(SYS_PHYSFREE, physical_address, size) -} - -/// Maps physical memory to virtual memory. -pub unsafe fn physmap(physical_address: usize, size: usize, flags: usize) -> Result<usize> { - syscall3(SYS_PHYSMAP, physical_address, size, flags) -} - -/// Unmaps previously mapped physical memory. -pub unsafe fn physunmap(virtual_address: usize) -> Result<usize> { - syscall1(SYS_PHYSUNMAP, virtual_address) -} - -/// Creates a pair of file descriptors referencing the read and write ends of a pipe. -pub fn pipe2(fds: &mut [usize; 2], flags: usize) -> Result<usize> { - unsafe { syscall2(SYS_PIPE2, fds.as_ptr() as usize, flags) } -} - -/// Read from a file descriptor into a buffer -pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> { - unsafe { syscall3(SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) } -} - -/// Removes a directory. -pub fn rmdir<T: AsRef<[u8]>>(path: T) -> Result<usize> { - unsafe { syscall2(SYS_RMDIR, path.as_ref().as_ptr() as usize, path.as_ref().len()) } -} - -/// Sets the process group ID. -pub fn setpgid(pid: usize, pgid: usize) -> Result<usize> { - unsafe { syscall2(SYS_SETPGID, pid, pgid) } -} - -/// Sets the current process group IDs. -pub fn setregid(rgid: usize, egid: usize) -> Result<usize> { - unsafe { syscall2(SYS_SETREGID, rgid, egid) } -} - -/// Makes a new scheme namespace. -pub fn setrens(rns: usize, ens: usize) -> Result<usize> { - unsafe { syscall2(SYS_SETRENS, rns, ens) } -} - -/// Sets the current process user IDs. -pub fn setreuid(ruid: usize, euid: usize) -> Result<usize> { - unsafe { syscall2(SYS_SETREUID, ruid, euid) } -} - -/// Sets up a signal handler. -pub fn sigaction(sig: usize, act: Option<&SigAction>, oldact: Option<&mut SigAction>) --> Result<usize> { - unsafe { syscall4(SYS_SIGACTION, sig, - act.map(|x| x as *const _).unwrap_or_else(ptr::null) as usize, - oldact.map(|x| x as *mut _).unwrap_or_else(ptr::null_mut) as usize, - restorer as usize) } -} - -/// Returns from signal handler. -pub fn sigreturn() -> Result<usize> { - unsafe { syscall0(SYS_SIGRETURN) } -} - -/// Removes a file. -pub fn unlink<T: AsRef<[u8]>>(path: T) -> Result<usize> { - unsafe { syscall2(SYS_UNLINK, path.as_ref().as_ptr() as usize, path.as_ref().len()) } -} - -/// Converts a virtual address to a physical one. -pub unsafe fn virttophys(virtual_address: usize) -> Result<usize> { - syscall1(SYS_VIRTTOPHYS, virtual_address) -} - -/// Checks if a child process has exited or received a signal. -pub fn waitpid(pid: usize, status: &mut usize, options: usize) -> Result<usize> { - unsafe { syscall3(SYS_WAITPID, pid, status as *mut usize as usize, options) } -} - -/// Writes a buffer to a file descriptor. -/// -/// The kernel will attempt to write the bytes in `buf` to the file descriptor `fd`, returning -/// either an `Err`, explained below, or `Ok(count)` where `count` is the number of bytes which -/// were written. -/// -/// # Errors -/// -/// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block -/// * `EBADF` - the file descriptor is not valid or is not open for writing -/// * `EFAULT` - `buf` does not point to the process's addressable memory -/// * `EIO` - an I/O error occurred -/// * `ENOSPC` - the device containing the file descriptor has no room for data -/// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed -pub fn write(fd: usize, buf: &[u8]) -> Result<usize> { - unsafe { syscall3(SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) } -} - -/// Yields the process's time slice to the kernel. -/// -/// This function will return Ok(0) on success -pub fn sched_yield() -> Result<usize> { - unsafe { syscall0(SYS_YIELD) } -} diff --git a/src/libstd/sys/redox/syscall/data.rs b/src/libstd/sys/redox/syscall/data.rs deleted file mode 100644 index 0b458ea89b8..00000000000 --- a/src/libstd/sys/redox/syscall/data.rs +++ /dev/null @@ -1,191 +0,0 @@ -use core::ops::{Deref, DerefMut}; -use core::{mem, slice}; - -#[derive(Copy, Clone, Debug, Default)] -pub struct Event { - pub id: usize, - pub flags: usize, - pub data: usize -} - -impl Deref for Event { - type Target = [u8]; - fn deref(&self) -> &[u8] { - unsafe { - slice::from_raw_parts( - self as *const Event as *const u8, - mem::size_of::<Event>() - ) as &[u8] - } - } -} - -impl DerefMut for Event { - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { - slice::from_raw_parts_mut( - self as *mut Event as *mut u8, - mem::size_of::<Event>() - ) as &mut [u8] - } - } -} - -#[derive(Copy, Clone, Debug, Default)] -#[repr(C)] -pub struct Packet { - pub id: u64, - pub pid: usize, - pub uid: u32, - pub gid: u32, - pub a: usize, - pub b: usize, - pub c: usize, - pub d: usize -} - -impl Deref for Packet { - type Target = [u8]; - fn deref(&self) -> &[u8] { - unsafe { - slice::from_raw_parts( - self as *const Packet as *const u8, - mem::size_of::<Packet>() - ) as &[u8] - } - } -} - -impl DerefMut for Packet { - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { - slice::from_raw_parts_mut( - self as *mut Packet as *mut u8, - mem::size_of::<Packet>() - ) as &mut [u8] - } - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct SigAction { - pub sa_handler: extern "C" fn(usize), - pub sa_mask: [u64; 2], - pub sa_flags: usize, -} - -impl Default for SigAction { - fn default() -> Self { - Self { - sa_handler: unsafe { mem::transmute(0usize) }, - sa_mask: [0; 2], - sa_flags: 0, - } - } -} - -#[derive(Copy, Clone, Debug, Default)] -#[repr(C)] -pub struct Stat { - pub st_dev: u64, - pub st_ino: u64, - pub st_mode: u16, - pub st_nlink: u32, - pub st_uid: u32, - pub st_gid: u32, - pub st_size: u64, - pub st_blksize: u32, - pub st_blocks: u64, - pub st_mtime: u64, - pub st_mtime_nsec: u32, - pub st_atime: u64, - pub st_atime_nsec: u32, - pub st_ctime: u64, - pub st_ctime_nsec: u32, -} - -impl Deref for Stat { - type Target = [u8]; - fn deref(&self) -> &[u8] { - unsafe { - slice::from_raw_parts( - self as *const Stat as *const u8, - mem::size_of::<Stat>() - ) as &[u8] - } - } -} - -impl DerefMut for Stat { - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { - slice::from_raw_parts_mut( - self as *mut Stat as *mut u8, - mem::size_of::<Stat>() - ) as &mut [u8] - } - } -} - -#[derive(Copy, Clone, Debug, Default)] -#[repr(C)] -pub struct StatVfs { - pub f_bsize: u32, - pub f_blocks: u64, - pub f_bfree: u64, - pub f_bavail: u64, -} - -impl Deref for StatVfs { - type Target = [u8]; - fn deref(&self) -> &[u8] { - unsafe { - slice::from_raw_parts( - self as *const StatVfs as *const u8, - mem::size_of::<StatVfs>() - ) as &[u8] - } - } -} - -impl DerefMut for StatVfs { - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { - slice::from_raw_parts_mut( - self as *mut StatVfs as *mut u8, - mem::size_of::<StatVfs>() - ) as &mut [u8] - } - } -} - -#[derive(Copy, Clone, Debug, Default)] -#[repr(C)] -pub struct TimeSpec { - pub tv_sec: i64, - pub tv_nsec: i32, -} - -impl Deref for TimeSpec { - type Target = [u8]; - fn deref(&self) -> &[u8] { - unsafe { - slice::from_raw_parts( - self as *const TimeSpec as *const u8, - mem::size_of::<TimeSpec>() - ) as &[u8] - } - } -} - -impl DerefMut for TimeSpec { - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { - slice::from_raw_parts_mut( - self as *mut TimeSpec as *mut u8, - mem::size_of::<TimeSpec>() - ) as &mut [u8] - } - } -} diff --git a/src/libstd/sys/redox/syscall/error.rs b/src/libstd/sys/redox/syscall/error.rs deleted file mode 100644 index f5b7bf75f30..00000000000 --- a/src/libstd/sys/redox/syscall/error.rs +++ /dev/null @@ -1,315 +0,0 @@ -use core::{fmt, result}; - -#[derive(Eq, PartialEq)] -pub struct Error { - pub errno: i32, -} - -pub type Result<T> = result::Result<T, Error>; - -impl Error { - pub fn new(errno: i32) -> Error { - Error { errno } - } - - pub fn mux(result: Result<usize>) -> usize { - match result { - Ok(value) => value, - Err(error) => -error.errno as usize, - } - } - - pub fn demux(value: usize) -> Result<usize> { - let errno = -(value as i32); - if errno >= 1 && errno < STR_ERROR.len() as i32 { - Err(Error::new(errno)) - } else { - Ok(value) - } - } - - pub fn text(&self) -> &str { - if let Some(description) = STR_ERROR.get(self.errno as usize) { - description - } else { - "Unknown Error" - } - } -} - -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(self.text()) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(self.text()) - } -} - -pub const EPERM: i32 = 1; /* Operation not permitted */ -pub const ENOENT: i32 = 2; /* No such file or directory */ -pub const ESRCH: i32 = 3; /* No such process */ -pub const EINTR: i32 = 4; /* Interrupted system call */ -pub const EIO: i32 = 5; /* I/O error */ -pub const ENXIO: i32 = 6; /* No such device or address */ -pub const E2BIG: i32 = 7; /* Argument list too long */ -pub const ENOEXEC: i32 = 8; /* Exec format error */ -pub const EBADF: i32 = 9; /* Bad file number */ -pub const ECHILD: i32 = 10; /* No child processes */ -pub const EAGAIN: i32 = 11; /* Try again */ -pub const ENOMEM: i32 = 12; /* Out of memory */ -pub const EACCES: i32 = 13; /* Permission denied */ -pub const EFAULT: i32 = 14; /* Bad address */ -pub const ENOTBLK: i32 = 15; /* Block device required */ -pub const EBUSY: i32 = 16; /* Device or resource busy */ -pub const EEXIST: i32 = 17; /* File exists */ -pub const EXDEV: i32 = 18; /* Cross-device link */ -pub const ENODEV: i32 = 19; /* No such device */ -pub const ENOTDIR: i32 = 20; /* Not a directory */ -pub const EISDIR: i32 = 21; /* Is a directory */ -pub const EINVAL: i32 = 22; /* Invalid argument */ -pub const ENFILE: i32 = 23; /* File table overflow */ -pub const EMFILE: i32 = 24; /* Too many open files */ -pub const ENOTTY: i32 = 25; /* Not a typewriter */ -pub const ETXTBSY: i32 = 26; /* Text file busy */ -pub const EFBIG: i32 = 27; /* File too large */ -pub const ENOSPC: i32 = 28; /* No space left on device */ -pub const ESPIPE: i32 = 29; /* Illegal seek */ -pub const EROFS: i32 = 30; /* Read-only file system */ -pub const EMLINK: i32 = 31; /* Too many links */ -pub const EPIPE: i32 = 32; /* Broken pipe */ -pub const EDOM: i32 = 33; /* Math argument out of domain of func */ -pub const ERANGE: i32 = 34; /* Math result not representable */ -pub const EDEADLK: i32 = 35; /* Resource deadlock would occur */ -pub const ENAMETOOLONG: i32 = 36; /* File name too long */ -pub const ENOLCK: i32 = 37; /* No record locks available */ -pub const ENOSYS: i32 = 38; /* Function not implemented */ -pub const ENOTEMPTY: i32 = 39; /* Directory not empty */ -pub const ELOOP: i32 = 40; /* Too many symbolic links encountered */ -pub const EWOULDBLOCK: i32 = 41; /* Operation would block */ -pub const ENOMSG: i32 = 42; /* No message of desired type */ -pub const EIDRM: i32 = 43; /* Identifier removed */ -pub const ECHRNG: i32 = 44; /* Channel number out of range */ -pub const EL2NSYNC: i32 = 45; /* Level 2 not synchronized */ -pub const EL3HLT: i32 = 46; /* Level 3 halted */ -pub const EL3RST: i32 = 47; /* Level 3 reset */ -pub const ELNRNG: i32 = 48; /* Link number out of range */ -pub const EUNATCH: i32 = 49; /* Protocol driver not attached */ -pub const ENOCSI: i32 = 50; /* No CSI structure available */ -pub const EL2HLT: i32 = 51; /* Level 2 halted */ -pub const EBADE: i32 = 52; /* Invalid exchange */ -pub const EBADR: i32 = 53; /* Invalid request descriptor */ -pub const EXFULL: i32 = 54; /* Exchange full */ -pub const ENOANO: i32 = 55; /* No anode */ -pub const EBADRQC: i32 = 56; /* Invalid request code */ -pub const EBADSLT: i32 = 57; /* Invalid slot */ -pub const EDEADLOCK: i32 = 58; /* Resource deadlock would occur */ -pub const EBFONT: i32 = 59; /* Bad font file format */ -pub const ENOSTR: i32 = 60; /* Device not a stream */ -pub const ENODATA: i32 = 61; /* No data available */ -pub const ETIME: i32 = 62; /* Timer expired */ -pub const ENOSR: i32 = 63; /* Out of streams resources */ -pub const ENONET: i32 = 64; /* Machine is not on the network */ -pub const ENOPKG: i32 = 65; /* Package not installed */ -pub const EREMOTE: i32 = 66; /* Object is remote */ -pub const ENOLINK: i32 = 67; /* Link has been severed */ -pub const EADV: i32 = 68; /* Advertise error */ -pub const ESRMNT: i32 = 69; /* Srmount error */ -pub const ECOMM: i32 = 70; /* Communication error on send */ -pub const EPROTO: i32 = 71; /* Protocol error */ -pub const EMULTIHOP: i32 = 72; /* Multihop attempted */ -pub const EDOTDOT: i32 = 73; /* RFS specific error */ -pub const EBADMSG: i32 = 74; /* Not a data message */ -pub const EOVERFLOW: i32 = 75; /* Value too large for defined data type */ -pub const ENOTUNIQ: i32 = 76; /* Name not unique on network */ -pub const EBADFD: i32 = 77; /* File descriptor in bad state */ -pub const EREMCHG: i32 = 78; /* Remote address changed */ -pub const ELIBACC: i32 = 79; /* Can not access a needed shared library */ -pub const ELIBBAD: i32 = 80; /* Accessing a corrupted shared library */ -pub const ELIBSCN: i32 = 81; /* .lib section in a.out corrupted */ -pub const ELIBMAX: i32 = 82; /* Attempting to link in too many shared libraries */ -pub const ELIBEXEC: i32 = 83; /* Cannot exec a shared library directly */ -pub const EILSEQ: i32 = 84; /* Illegal byte sequence */ -pub const ERESTART: i32 = 85; /* Interrupted system call should be restarted */ -pub const ESTRPIPE: i32 = 86; /* Streams pipe error */ -pub const EUSERS: i32 = 87; /* Too many users */ -pub const ENOTSOCK: i32 = 88; /* Socket operation on non-socket */ -pub const EDESTADDRREQ: i32 = 89; /* Destination address required */ -pub const EMSGSIZE: i32 = 90; /* Message too long */ -pub const EPROTOTYPE: i32 = 91; /* Protocol wrong type for socket */ -pub const ENOPROTOOPT: i32 = 92; /* Protocol not available */ -pub const EPROTONOSUPPORT: i32 = 93; /* Protocol not supported */ -pub const ESOCKTNOSUPPORT: i32 = 94; /* Socket type not supported */ -pub const EOPNOTSUPP: i32 = 95; /* Operation not supported on transport endpoint */ -pub const EPFNOSUPPORT: i32 = 96; /* Protocol family not supported */ -pub const EAFNOSUPPORT: i32 = 97; /* Address family not supported by protocol */ -pub const EADDRINUSE: i32 = 98; /* Address already in use */ -pub const EADDRNOTAVAIL: i32 = 99; /* Cannot assign requested address */ -pub const ENETDOWN: i32 = 100; /* Network is down */ -pub const ENETUNREACH: i32 = 101; /* Network is unreachable */ -pub const ENETRESET: i32 = 102; /* Network dropped connection because of reset */ -pub const ECONNABORTED: i32 = 103; /* Software caused connection abort */ -pub const ECONNRESET: i32 = 104; /* Connection reset by peer */ -pub const ENOBUFS: i32 = 105; /* No buffer space available */ -pub const EISCONN: i32 = 106; /* Transport endpoint is already connected */ -pub const ENOTCONN: i32 = 107; /* Transport endpoint is not connected */ -pub const ESHUTDOWN: i32 = 108; /* Cannot send after transport endpoint shutdown */ -pub const ETOOMANYREFS: i32 = 109; /* Too many references: cannot splice */ -pub const ETIMEDOUT: i32 = 110; /* Connection timed out */ -pub const ECONNREFUSED: i32 = 111; /* Connection refused */ -pub const EHOSTDOWN: i32 = 112; /* Host is down */ -pub const EHOSTUNREACH: i32 = 113; /* No route to host */ -pub const EALREADY: i32 = 114; /* Operation already in progress */ -pub const EINPROGRESS: i32 = 115; /* Operation now in progress */ -pub const ESTALE: i32 = 116; /* Stale NFS file handle */ -pub const EUCLEAN: i32 = 117; /* Structure needs cleaning */ -pub const ENOTNAM: i32 = 118; /* Not a XENIX named type file */ -pub const ENAVAIL: i32 = 119; /* No XENIX semaphores available */ -pub const EISNAM: i32 = 120; /* Is a named type file */ -pub const EREMOTEIO: i32 = 121; /* Remote I/O error */ -pub const EDQUOT: i32 = 122; /* Quota exceeded */ -pub const ENOMEDIUM: i32 = 123; /* No medium found */ -pub const EMEDIUMTYPE: i32 = 124; /* Wrong medium type */ -pub const ECANCELED: i32 = 125; /* Operation Canceled */ -pub const ENOKEY: i32 = 126; /* Required key not available */ -pub const EKEYEXPIRED: i32 = 127; /* Key has expired */ -pub const EKEYREVOKED: i32 = 128; /* Key has been revoked */ -pub const EKEYREJECTED: i32 = 129; /* Key was rejected by service */ -pub const EOWNERDEAD: i32 = 130; /* Owner died */ -pub const ENOTRECOVERABLE: i32 = 131; /* State not recoverable */ - -pub static STR_ERROR: [&'static str; 132] = ["Success", - "Operation not permitted", - "No such file or directory", - "No such process", - "Interrupted system call", - "I/O error", - "No such device or address", - "Argument list too long", - "Exec format error", - "Bad file number", - "No child processes", - "Try again", - "Out of memory", - "Permission denied", - "Bad address", - "Block device required", - "Device or resource busy", - "File exists", - "Cross-device link", - "No such device", - "Not a directory", - "Is a directory", - "Invalid argument", - "File table overflow", - "Too many open files", - "Not a typewriter", - "Text file busy", - "File too large", - "No space left on device", - "Illegal seek", - "Read-only file system", - "Too many links", - "Broken pipe", - "Math argument out of domain of func", - "Math result not representable", - "Resource deadlock would occur", - "File name too long", - "No record locks available", - "Function not implemented", - "Directory not empty", - "Too many symbolic links encountered", - "Operation would block", - "No message of desired type", - "Identifier removed", - "Channel number out of range", - "Level 2 not synchronized", - "Level 3 halted", - "Level 3 reset", - "Link number out of range", - "Protocol driver not attached", - "No CSI structure available", - "Level 2 halted", - "Invalid exchange", - "Invalid request descriptor", - "Exchange full", - "No anode", - "Invalid request code", - "Invalid slot", - "Resource deadlock would occur", - "Bad font file format", - "Device not a stream", - "No data available", - "Timer expired", - "Out of streams resources", - "Machine is not on the network", - "Package not installed", - "Object is remote", - "Link has been severed", - "Advertise error", - "Srmount error", - "Communication error on send", - "Protocol error", - "Multihop attempted", - "RFS specific error", - "Not a data message", - "Value too large for defined data type", - "Name not unique on network", - "File descriptor in bad state", - "Remote address changed", - "Can not access a needed shared library", - "Accessing a corrupted shared library", - ".lib section in a.out corrupted", - "Attempting to link in too many shared libraries", - "Cannot exec a shared library directly", - "Illegal byte sequence", - "Interrupted system call should be restarted", - "Streams pipe error", - "Too many users", - "Socket operation on non-socket", - "Destination address required", - "Message too long", - "Protocol wrong type for socket", - "Protocol not available", - "Protocol not supported", - "Socket type not supported", - "Operation not supported on transport endpoint", - "Protocol family not supported", - "Address family not supported by protocol", - "Address already in use", - "Cannot assign requested address", - "Network is down", - "Network is unreachable", - "Network dropped connection because of reset", - "Software caused connection abort", - "Connection reset by peer", - "No buffer space available", - "Transport endpoint is already connected", - "Transport endpoint is not connected", - "Cannot send after transport endpoint shutdown", - "Too many references: cannot splice", - "Connection timed out", - "Connection refused", - "Host is down", - "No route to host", - "Operation already in progress", - "Operation now in progress", - "Stale NFS file handle", - "Structure needs cleaning", - "Not a XENIX named type file", - "No XENIX semaphores available", - "Is a named type file", - "Remote I/O error", - "Quota exceeded", - "No medium found", - "Wrong medium type", - "Operation Canceled", - "Required key not available", - "Key has expired", - "Key has been revoked", - "Key was rejected by service", - "Owner died", - "State not recoverable"]; diff --git a/src/libstd/sys/redox/syscall/flag.rs b/src/libstd/sys/redox/syscall/flag.rs deleted file mode 100644 index 5820f1ad03a..00000000000 --- a/src/libstd/sys/redox/syscall/flag.rs +++ /dev/null @@ -1,148 +0,0 @@ -pub const CLONE_VM: usize = 0x100; -pub const CLONE_FS: usize = 0x200; -pub const CLONE_FILES: usize = 0x400; -pub const CLONE_SIGHAND: usize = 0x800; -pub const CLONE_VFORK: usize = 0x4000; -pub const CLONE_THREAD: usize = 0x10000; - -pub const CLOCK_REALTIME: usize = 1; -pub const CLOCK_MONOTONIC: usize = 4; - -pub const EVENT_NONE: usize = 0; -pub const EVENT_READ: usize = 1; -pub const EVENT_WRITE: usize = 2; - -pub const F_DUPFD: usize = 0; -pub const F_GETFD: usize = 1; -pub const F_SETFD: usize = 2; -pub const F_GETFL: usize = 3; -pub const F_SETFL: usize = 4; - -pub const FUTEX_WAIT: usize = 0; -pub const FUTEX_WAKE: usize = 1; -pub const FUTEX_REQUEUE: usize = 2; - -pub const MAP_WRITE: usize = 1; -pub const MAP_WRITE_COMBINE: usize = 2; - -pub const MODE_TYPE: u16 = 0xF000; -pub const MODE_DIR: u16 = 0x4000; -pub const MODE_FILE: u16 = 0x8000; -pub const MODE_SYMLINK: u16 = 0xA000; -pub const MODE_FIFO: u16 = 0x1000; -pub const MODE_CHR: u16 = 0x2000; - -pub const MODE_PERM: u16 = 0x0FFF; -pub const MODE_SETUID: u16 = 0o4000; -pub const MODE_SETGID: u16 = 0o2000; - -pub const O_RDONLY: usize = 0x0001_0000; -pub const O_WRONLY: usize = 0x0002_0000; -pub const O_RDWR: usize = 0x0003_0000; -pub const O_NONBLOCK: usize = 0x0004_0000; -pub const O_APPEND: usize = 0x0008_0000; -pub const O_SHLOCK: usize = 0x0010_0000; -pub const O_EXLOCK: usize = 0x0020_0000; -pub const O_ASYNC: usize = 0x0040_0000; -pub const O_FSYNC: usize = 0x0080_0000; -pub const O_CLOEXEC: usize = 0x0100_0000; -pub const O_CREAT: usize = 0x0200_0000; -pub const O_TRUNC: usize = 0x0400_0000; -pub const O_EXCL: usize = 0x0800_0000; -pub const O_DIRECTORY: usize = 0x1000_0000; -pub const O_STAT: usize = 0x2000_0000; -pub const O_SYMLINK: usize = 0x4000_0000; -pub const O_NOFOLLOW: usize = 0x8000_0000; -pub const O_ACCMODE: usize = O_RDONLY | O_WRONLY | O_RDWR; - -pub const SEEK_SET: usize = 0; -pub const SEEK_CUR: usize = 1; -pub const SEEK_END: usize = 2; - -pub const SIGHUP: usize = 1; -pub const SIGINT: usize = 2; -pub const SIGQUIT: usize = 3; -pub const SIGILL: usize = 4; -pub const SIGTRAP: usize = 5; -pub const SIGABRT: usize = 6; -pub const SIGBUS: usize = 7; -pub const SIGFPE: usize = 8; -pub const SIGKILL: usize = 9; -pub const SIGUSR1: usize = 10; -pub const SIGSEGV: usize = 11; -pub const SIGUSR2: usize = 12; -pub const SIGPIPE: usize = 13; -pub const SIGALRM: usize = 14; -pub const SIGTERM: usize = 15; -pub const SIGSTKFLT: usize= 16; -pub const SIGCHLD: usize = 17; -pub const SIGCONT: usize = 18; -pub const SIGSTOP: usize = 19; -pub const SIGTSTP: usize = 20; -pub const SIGTTIN: usize = 21; -pub const SIGTTOU: usize = 22; -pub const SIGURG: usize = 23; -pub const SIGXCPU: usize = 24; -pub const SIGXFSZ: usize = 25; -pub const SIGVTALRM: usize= 26; -pub const SIGPROF: usize = 27; -pub const SIGWINCH: usize = 28; -pub const SIGIO: usize = 29; -pub const SIGPWR: usize = 30; -pub const SIGSYS: usize = 31; - -pub const SIG_DFL: usize = 0; -pub const SIG_IGN: usize = 1; - -pub const SA_NOCLDSTOP: usize = 0x00000001; -pub const SA_NOCLDWAIT: usize = 0x00000002; -pub const SA_SIGINFO: usize = 0x00000004; -pub const SA_RESTORER: usize = 0x04000000; -pub const SA_ONSTACK: usize = 0x08000000; -pub const SA_RESTART: usize = 0x10000000; -pub const SA_NODEFER: usize = 0x40000000; -pub const SA_RESETHAND: usize = 0x80000000; - -pub const WNOHANG: usize = 0x01; -pub const WUNTRACED: usize = 0x02; -pub const WCONTINUED: usize = 0x08; - -/// Returns `true` if status indicates the child is stopped. -pub fn wifstopped(status: usize) -> bool { - (status & 0xff) == 0x7f -} - -/// If wifstopped(status), returns the signal that stopped the child. -pub fn wstopsig(status: usize) -> usize { - (status >> 8) & 0xff -} - -/// Returns `true` if status indicates the child continued after a stop. -pub fn wifcontinued(status: usize) -> bool { - status == 0xffff -} - -/// Returns `true` if status indicates termination by a signal. -pub fn wifsignaled(status: usize) -> bool { - ((status & 0x7f) + 1) as i8 >= 2 -} - -/// If wifsignaled(status), returns the terminating signal. -pub fn wtermsig(status: usize) -> usize { - status & 0x7f -} - -/// Returns `true` if status indicates normal termination. -pub fn wifexited(status: usize) -> bool { - wtermsig(status) == 0 -} - -/// If wifexited(status), returns the exit status. -pub fn wexitstatus(status: usize) -> usize { - (status >> 8) & 0xff -} - -/// Returns `true` if status indicates a core dump was created. -pub fn wcoredump(status: usize) -> bool { - (status & 0x80) != 0 -} diff --git a/src/libstd/sys/redox/syscall/mod.rs b/src/libstd/sys/redox/syscall/mod.rs deleted file mode 100644 index b16f5dbd918..00000000000 --- a/src/libstd/sys/redox/syscall/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -pub use self::arch::*; -pub use self::call::*; -pub use self::data::*; -pub use self::error::*; -pub use self::flag::*; -pub use self::number::*; - -#[cfg(target_arch = "arm")] -#[path="arch/arm.rs"] -mod arch; - -#[cfg(target_arch = "x86")] -#[path="arch/x86.rs"] -mod arch; - -#[cfg(target_arch = "x86_64")] -#[path="arch/x86_64.rs"] -mod arch; - -/// Function definitions -pub mod call; - -/// Complex structures that are used for some system calls -pub mod data; - -/// All errors that can be generated by a system call -pub mod error; - -/// Flags used as an argument to many system calls -pub mod flag; - -/// Call numbers used by each system call -pub mod number; diff --git a/src/libstd/sys/redox/syscall/number.rs b/src/libstd/sys/redox/syscall/number.rs deleted file mode 100644 index f8884aa2ed8..00000000000 --- a/src/libstd/sys/redox/syscall/number.rs +++ /dev/null @@ -1,73 +0,0 @@ -pub const SYS_CLASS: usize = 0xF000_0000; -pub const SYS_CLASS_PATH: usize=0x1000_0000; -pub const SYS_CLASS_FILE: usize=0x2000_0000; - -pub const SYS_ARG: usize = 0x0F00_0000; -pub const SYS_ARG_SLICE: usize =0x0100_0000; -pub const SYS_ARG_MSLICE: usize=0x0200_0000; -pub const SYS_ARG_PATH: usize = 0x0300_0000; - -pub const SYS_RET: usize = 0x00F0_0000; -pub const SYS_RET_FILE: usize = 0x0010_0000; - -pub const SYS_LINK: usize = SYS_CLASS_PATH | SYS_ARG_PATH | 9; -pub const SYS_OPEN: usize = SYS_CLASS_PATH | SYS_RET_FILE | 5; -pub const SYS_CHMOD: usize = SYS_CLASS_PATH | 15; -pub const SYS_RMDIR: usize = SYS_CLASS_PATH | 84; -pub const SYS_UNLINK: usize = SYS_CLASS_PATH | 10; - -pub const SYS_CLOSE: usize = SYS_CLASS_FILE | 6; -pub const SYS_DUP: usize = SYS_CLASS_FILE | SYS_RET_FILE | 41; -pub const SYS_DUP2: usize = SYS_CLASS_FILE | SYS_RET_FILE | 63; -pub const SYS_READ: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 3; -pub const SYS_WRITE: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 4; -pub const SYS_LSEEK: usize = SYS_CLASS_FILE | 19; -pub const SYS_FCHMOD: usize = SYS_CLASS_FILE | 94; -pub const SYS_FCHOWN: usize = SYS_CLASS_FILE | 207; -pub const SYS_FCNTL: usize = SYS_CLASS_FILE | 55; -pub const SYS_FEVENT: usize = SYS_CLASS_FILE | 927; -pub const SYS_FEXEC: usize = SYS_CLASS_FILE | 11; -pub const SYS_FMAP: usize = SYS_CLASS_FILE | 90; -pub const SYS_FUNMAP: usize = SYS_CLASS_FILE | 91; -pub const SYS_FPATH: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 928; -pub const SYS_FRENAME: usize = SYS_CLASS_FILE | SYS_ARG_PATH | 38; -pub const SYS_FSTAT: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 28; -pub const SYS_FSTATVFS: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 100; -pub const SYS_FSYNC: usize = SYS_CLASS_FILE | 118; -pub const SYS_FTRUNCATE: usize =SYS_CLASS_FILE | 93; -pub const SYS_FUTIMENS: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 320; - -pub const SYS_BRK: usize = 45; -pub const SYS_CHDIR: usize = 12; -pub const SYS_CLOCK_GETTIME: usize = 265; -pub const SYS_CLONE: usize = 120; -pub const SYS_EXIT: usize = 1; -pub const SYS_FUTEX: usize = 240; -pub const SYS_GETCWD: usize = 183; -pub const SYS_GETEGID: usize = 202; -pub const SYS_GETENS: usize = 951; -pub const SYS_GETEUID: usize = 201; -pub const SYS_GETGID: usize = 200; -pub const SYS_GETNS: usize = 950; -pub const SYS_GETPID: usize = 20; -pub const SYS_GETPGID: usize = 132; -pub const SYS_GETPPID: usize = 64; -pub const SYS_GETUID: usize = 199; -pub const SYS_IOPL: usize = 110; -pub const SYS_KILL: usize = 37; -pub const SYS_MKNS: usize = 984; -pub const SYS_NANOSLEEP: usize =162; -pub const SYS_PHYSALLOC: usize =945; -pub const SYS_PHYSFREE: usize = 946; -pub const SYS_PHYSMAP: usize = 947; -pub const SYS_PHYSUNMAP: usize =948; -pub const SYS_VIRTTOPHYS: usize=949; -pub const SYS_PIPE2: usize = 331; -pub const SYS_SETPGID: usize = 57; -pub const SYS_SETREGID: usize = 204; -pub const SYS_SETRENS: usize = 952; -pub const SYS_SETREUID: usize = 203; -pub const SYS_SIGACTION: usize =67; -pub const SYS_SIGRETURN: usize =119; -pub const SYS_WAITPID: usize = 7; -pub const SYS_YIELD: usize = 158; diff --git a/src/libstd/sys/redox/thread.rs b/src/libstd/sys/redox/thread.rs deleted file mode 100644 index ae0b91b4d6c..00000000000 --- a/src/libstd/sys/redox/thread.rs +++ /dev/null @@ -1,85 +0,0 @@ -use crate::boxed::FnBox; -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::sys_common::thread::start_thread; -use crate::sys::{cvt, syscall}; -use crate::time::Duration; - -pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; - -pub struct Thread { - id: usize, -} - -// Some platforms may have pthread_t as a pointer in which case we still want -// a thread to be Send/Sync -unsafe impl Send for Thread {} -unsafe impl Sync for Thread {} - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { - let p = box p; - - let id = cvt(syscall::clone(syscall::CLONE_VM | syscall::CLONE_FS | syscall::CLONE_FILES))?; - if id == 0 { - start_thread(&*p as *const _ as *mut _); - let _ = syscall::exit(0); - panic!("thread failed to exit"); - } else { - mem::forget(p); - Ok(Thread { id }) - } - } - - pub fn yield_now() { - let ret = syscall::sched_yield().expect("failed to sched_yield"); - debug_assert_eq!(ret, 0); - } - - pub fn set_name(_name: &CStr) { - - } - - pub fn sleep(dur: Duration) { - let mut secs = dur.as_secs(); - let mut nsecs = dur.subsec_nanos() as i32; - - // If we're awoken with a signal then the return value will be -1 and - // nanosleep will fill in `ts` with the remaining time. - while secs > 0 || nsecs > 0 { - let req = syscall::TimeSpec { - tv_sec: secs as i64, - tv_nsec: nsecs, - }; - secs -= req.tv_sec as u64; - let mut rem = syscall::TimeSpec::default(); - if syscall::nanosleep(&req, &mut rem).is_err() { - secs += rem.tv_sec as u64; - nsecs = rem.tv_nsec; - } else { - nsecs = 0; - } - } - } - - pub fn join(self) { - let mut status = 0; - syscall::waitpid(self.id, &mut status, 0).unwrap(); - } - - pub fn id(&self) -> usize { self.id } - - pub fn into_id(self) -> usize { - let id = self.id; - mem::forget(self); - id - } -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { None } - pub unsafe fn init() -> Option<Guard> { None } -} diff --git a/src/libstd/sys/redox/thread_local.rs b/src/libstd/sys/redox/thread_local.rs deleted file mode 100644 index 4bc8c4d5883..00000000000 --- a/src/libstd/sys/redox/thread_local.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![allow(dead_code)] // not used on all platforms - -use crate::collections::BTreeMap; -use crate::ptr; -use crate::sync::atomic::{AtomicUsize, Ordering}; - -pub type Key = usize; - -type Dtor = unsafe extern fn(*mut u8); - -static NEXT_KEY: AtomicUsize = AtomicUsize::new(0); - -static mut KEYS: *mut BTreeMap<Key, Option<Dtor>> = ptr::null_mut(); - -#[thread_local] -static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut(); - -unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> { - if KEYS == ptr::null_mut() { - KEYS = Box::into_raw(Box::new(BTreeMap::new())); - } - &mut *KEYS -} - -unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> { - if LOCALS == ptr::null_mut() { - LOCALS = Box::into_raw(Box::new(BTreeMap::new())); - } - &mut *LOCALS -} - -#[inline] -pub unsafe fn create(dtor: Option<Dtor>) -> Key { - let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); - keys().insert(key, dtor); - key -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - if let Some(&entry) = locals().get(&key) { - entry - } else { - ptr::null_mut() - } -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - locals().insert(key, value); -} - -#[inline] -pub unsafe fn destroy(key: Key) { - keys().remove(&key); -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - false -} diff --git a/src/libstd/sys/redox/time.rs b/src/libstd/sys/redox/time.rs deleted file mode 100644 index 9db3e85ca9c..00000000000 --- a/src/libstd/sys/redox/time.rs +++ /dev/null @@ -1,209 +0,0 @@ -use crate::cmp::Ordering; -use crate::fmt; -use crate::sys::{cvt, syscall}; -use crate::time::Duration; -use crate::convert::TryInto; - -use core::hash::{Hash, Hasher}; - -const NSEC_PER_SEC: u64 = 1_000_000_000; - -#[derive(Copy, Clone)] -struct Timespec { - t: syscall::TimeSpec, -} - -impl Timespec { - fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { - if self >= other { - Ok(if self.t.tv_nsec >= other.t.tv_nsec { - Duration::new((self.t.tv_sec - other.t.tv_sec) as u64, - (self.t.tv_nsec - other.t.tv_nsec) as u32) - } else { - Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - - other.t.tv_nsec as u32) - }) - } else { - match other.sub_timespec(self) { - Ok(d) => Err(d), - Err(d) => Ok(d), - } - } - } - - fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { - let mut secs = other - .as_secs() - .try_into() // <- target type would be `i64` - .ok() - .and_then(|secs| self.t.tv_sec.checked_add(secs))?; - - // Nano calculations can't overflow because nanos are <1B which fit - // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; - secs = secs.checked_add(1)?; - } - Some(Timespec { - t: syscall::TimeSpec { - tv_sec: secs, - tv_nsec: nsec as i32, - }, - }) - } - - fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { - let mut secs = other - .as_secs() - .try_into() // <- target type would be `i64` - .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; - - // Similar to above, nanos can't overflow. - let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; - if nsec < 0 { - nsec += NSEC_PER_SEC as i32; - secs = secs.checked_sub(1)?; - } - Some(Timespec { - t: syscall::TimeSpec { - tv_sec: secs, - tv_nsec: nsec as i32, - }, - }) - } -} - -impl PartialEq for Timespec { - fn eq(&self, other: &Timespec) -> bool { - self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec - } -} - -impl Eq for Timespec {} - -impl PartialOrd for Timespec { - fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -impl Ord for Timespec { - fn cmp(&self, other: &Timespec) -> Ordering { - let me = (self.t.tv_sec, self.t.tv_nsec); - let other = (other.t.tv_sec, other.t.tv_nsec); - me.cmp(&other) - } -} - -impl Hash for Timespec { - fn hash<H : Hasher>(&self, state: &mut H) { - self.t.tv_sec.hash(state); - self.t.tv_nsec.hash(state); - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Instant { - t: Timespec, -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct SystemTime { - t: Timespec, -} - -pub const UNIX_EPOCH: SystemTime = SystemTime { - t: Timespec { - t: syscall::TimeSpec { - tv_sec: 0, - tv_nsec: 0, - }, - }, -}; - -impl Instant { - pub fn now() -> Instant { - Instant { t: now(syscall::CLOCK_MONOTONIC) } - } - - pub const fn zero() -> Instant { - Instant { t: Timespec { t: syscall::TimeSpec { tv_sec: 0, tv_nsec: 0 } } } - } - - pub fn actually_monotonic() -> bool { - false - } - - pub fn sub_instant(&self, other: &Instant) -> Duration { - self.t.sub_timespec(&other.t).unwrap_or_else(|_| { - panic!("specified instant was later than self") - }) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { - Some(Instant { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { - Some(Instant { t: self.t.checked_sub_duration(other)? }) - } -} - -impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Instant") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } -} - -impl SystemTime { - pub fn now() -> SystemTime { - SystemTime { t: now(syscall::CLOCK_REALTIME) } - } - - pub fn sub_time(&self, other: &SystemTime) - -> Result<Duration, Duration> { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) - } -} - -impl From<syscall::TimeSpec> for SystemTime { - fn from(t: syscall::TimeSpec) -> SystemTime { - SystemTime { t: Timespec { t } } - } -} - -impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("SystemTime") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } -} - -pub type clock_t = usize; - -fn now(clock: clock_t) -> Timespec { - let mut t = Timespec { - t: syscall::TimeSpec { - tv_sec: 0, - tv_nsec: 0, - } - }; - cvt(syscall::clock_gettime(clock, &mut t.t)).unwrap(); - t -} diff --git a/src/libstd/sys/sgx/abi/entry.S b/src/libstd/sys/sgx/abi/entry.S index c03e3869aa3..c35e49b1dc6 100644 --- a/src/libstd/sys/sgx/abi/entry.S +++ b/src/libstd/sys/sgx/abi/entry.S @@ -65,10 +65,6 @@ IMAGE_BASE: /* The size in bytes of enclacve EH_FRM_HDR section */ globvar EH_FRM_HDR_SIZE 8 -.Lreentry_panic_msg: - .asciz "Re-entered aborted enclave!" -.Lreentry_panic_msg_end: - .org .Lxsave_clear+512 .Lxsave_header: .int 0, 0 /* XSTATE_BV */ @@ -210,10 +206,8 @@ sgx_entry: /* end sgx_entry */ .Lreentry_panic: - lea .Lreentry_panic_msg(%rip),%rdi - mov $.Lreentry_panic_msg_end-.Lreentry_panic_msg,%esi orq $8,%rsp - jmp panic_msg + jmp abort_reentry /* This *MUST* be called with 6 parameters, otherwise register information */ /* might leak! */ @@ -279,10 +273,8 @@ usercall: /* The following functions need to be defined externally: ``` -// Called by entry code when it needs to panic -extern "C" fn panic_msg(msg: &'static str) -> ! { - panic!(msg) -} +// Called by entry code on re-entry after exit +extern "C" fn abort_reentry() -> !; // Called once when a TCS is first entered extern "C" fn tcs_init(secondary: bool); diff --git a/src/libstd/sys/sgx/abi/mem.rs b/src/libstd/sys/sgx/abi/mem.rs index 808f1ce3ff2..d9051733da2 100644 --- a/src/libstd/sys/sgx/abi/mem.rs +++ b/src/libstd/sys/sgx/abi/mem.rs @@ -27,19 +27,23 @@ pub fn image_base() -> u64 { } /// Returns `true` if the specified memory range is in the enclave. +/// +/// `p + len` must not overflow. #[unstable(feature = "sgx_platform", issue = "56975")] pub fn is_enclave_range(p: *const u8, len: usize) -> bool { - let start=p as u64; - let end=start + (len as u64); + let start = p as u64; + let end = start + (len as u64); start >= image_base() && end <= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant } /// Returns `true` if the specified memory range is in userspace. +/// +/// `p + len` must not overflow. #[unstable(feature = "sgx_platform", issue = "56975")] pub fn is_user_range(p: *const u8, len: usize) -> bool { - let start=p as u64; - let end=start + (len as u64); + let start = p as u64; + let end = start + (len as u64); end <= image_base() || start >= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant } diff --git a/src/libstd/sys/sgx/abi/mod.rs b/src/libstd/sys/sgx/abi/mod.rs index 7426f7be9e9..0f107de83f0 100644 --- a/src/libstd/sys/sgx/abi/mod.rs +++ b/src/libstd/sys/sgx/abi/mod.rs @@ -1,3 +1,5 @@ +#![cfg_attr(test, allow(unused))] // RT initialization logic is not compiled for test + use core::sync::atomic::{AtomicUsize, Ordering}; use crate::io::Write; @@ -12,8 +14,10 @@ pub mod tls; #[macro_use] pub mod usercalls; +#[cfg(not(test))] global_asm!(include_str!("entry.S")); +#[cfg(not(test))] #[no_mangle] unsafe extern "C" fn tcs_init(secondary: bool) { // Be very careful when changing this code: it runs before the binary has been @@ -25,7 +29,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) { static RELOC_STATE: AtomicUsize = AtomicUsize::new(UNINIT); if secondary && RELOC_STATE.load(Ordering::Relaxed) != DONE { - panic::panic_msg("Entered secondary TCS before main TCS!") + rtabort!("Entered secondary TCS before main TCS!") } // Try to atomically swap UNINIT with BUSY. The returned state can be: @@ -48,6 +52,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) { // FIXME: this item should only exist if this is linked into an executable // (main function exists). If this is a library, the crate author should be // able to specify this +#[cfg(not(test))] #[no_mangle] extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) { // FIXME: how to support TLS in library mode? @@ -64,9 +69,9 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64 } // check entry is being called according to ABI - assert_eq!(p3, 0); - assert_eq!(p4, 0); - assert_eq!(p5, 0); + rtassert!(p3 == 0); + rtassert!(p4 == 0); + rtassert!(p5 == 0); unsafe { // The actual types of these arguments are `p1: *const Arg, p2: @@ -87,3 +92,9 @@ pub(super) fn exit_with_code(code: isize) -> ! { } usercalls::exit(code != 0); } + +#[cfg(not(test))] +#[no_mangle] +extern "C" fn abort_reentry() -> ! { + usercalls::exit(false) +} diff --git a/src/libstd/sys/sgx/abi/panic.rs b/src/libstd/sys/sgx/abi/panic.rs index 83411cb5b4c..2401476716f 100644 --- a/src/libstd/sys/sgx/abi/panic.rs +++ b/src/libstd/sys/sgx/abi/panic.rs @@ -1,4 +1,4 @@ -use super::usercalls::{alloc::UserRef, self}; +use super::usercalls::alloc::UserRef; use crate::cmp; use crate::io::{self, Write}; use crate::mem; @@ -48,9 +48,3 @@ impl Write for SgxPanicOutput { Ok(()) } } - -#[no_mangle] -pub extern "C" fn panic_msg(msg: &str) -> ! { - let _ = SgxPanicOutput::new().map(|mut out| out.write(msg.as_bytes())); - usercalls::exit(true) -} diff --git a/src/libstd/sys/sgx/abi/reloc.rs b/src/libstd/sys/sgx/abi/reloc.rs index a39841bc36f..6dd24c524fc 100644 --- a/src/libstd/sys/sgx/abi/reloc.rs +++ b/src/libstd/sys/sgx/abi/reloc.rs @@ -23,7 +23,7 @@ pub fn relocate_elf_rela() { }; for rela in relas { if rela.info != (/*0 << 32 |*/ R_X86_64_RELATIVE as u64) { - panic!("Invalid relocation"); + rtabort!("Invalid relocation"); } unsafe { *mem::rel_ptr_mut::<*const ()>(rela.offset) = mem::rel_ptr(rela.addend) }; } diff --git a/src/libstd/sys/sgx/abi/thread.rs b/src/libstd/sys/sgx/abi/thread.rs index 86fe09d0035..c17fa2d0015 100644 --- a/src/libstd/sys/sgx/abi/thread.rs +++ b/src/libstd/sys/sgx/abi/thread.rs @@ -4,6 +4,7 @@ use fortanix_sgx_abi::Tcs; /// all currently running threads in the enclave, and it is guaranteed to be /// constant for the lifetime of the thread. More specifically for SGX, there /// is a one-to-one correspondence of the ID to the address of the TCS. +#[unstable(feature = "sgx_platform", issue = "56975")] pub fn current() -> Tcs { extern "C" { fn get_tcs_addr() -> Tcs; } unsafe { get_tcs_addr() } diff --git a/src/libstd/sys/sgx/abi/tls.rs b/src/libstd/sys/sgx/abi/tls.rs index b2a812c7231..03e08ad547d 100644 --- a/src/libstd/sys/sgx/abi/tls.rs +++ b/src/libstd/sys/sgx/abi/tls.rs @@ -10,45 +10,16 @@ const USIZE_BITS: usize = 64; const TLS_KEYS: usize = 128; // Same as POSIX minimum const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; +#[cfg_attr(test, linkage = "available_externally")] +#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE"] static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; macro_rules! dup { ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); (() $($val:tt)*) => ([$($val),*]) } -static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = [ - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), -]; +#[cfg_attr(test, linkage = "available_externally")] +#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE"] +static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0))); extern "C" { fn get_tls_ptr() -> *const u8; @@ -113,7 +84,7 @@ impl Tls { Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) } } - pub unsafe fn activate(&self) -> ActiveTls { + pub unsafe fn activate(&self) -> ActiveTls<'_> { set_tls_ptr(self as *const Tls as _); ActiveTls { tls: self } } @@ -129,20 +100,24 @@ impl Tls { } pub fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { - let index = TLS_KEY_IN_USE.set().expect("TLS limit exceeded"); + let index = if let Some(index) = TLS_KEY_IN_USE.set() { + index + } else { + rtabort!("TLS limit exceeded") + }; TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed); Key::from_index(index) } pub fn set(key: Key, value: *mut u8) { let index = key.to_index(); - assert!(TLS_KEY_IN_USE.get(index)); + rtassert!(TLS_KEY_IN_USE.get(index)); unsafe { Self::current() }.data[index].set(value); } pub fn get(key: Key) -> *mut u8 { let index = key.to_index(); - assert!(TLS_KEY_IN_USE.get(index)); + rtassert!(TLS_KEY_IN_USE.get(index)); unsafe { Self::current() }.data[index].get() } @@ -170,7 +145,7 @@ mod sync_bitset { } /// Not atomic. - pub fn iter(&self) -> SyncBitsetIter { + pub fn iter(&self) -> SyncBitsetIter<'_> { SyncBitsetIter { iter: self.0.iter().enumerate().peekable(), elem_idx: 0, diff --git a/src/libstd/sys/sgx/abi/usercalls/alloc.rs b/src/libstd/sys/sgx/abi/usercalls/alloc.rs index b787bd1a5ab..c9ff53d0a4f 100644 --- a/src/libstd/sys/sgx/abi/usercalls/alloc.rs +++ b/src/libstd/sys/sgx/abi/usercalls/alloc.rs @@ -85,8 +85,10 @@ pub unsafe trait UserSafe { /// /// * the pointer is not aligned. /// * the pointer is null. + /// * the pointed-to range does not fit in the address space. /// * the pointed-to range is not in user memory. unsafe fn from_raw_sized(ptr: *mut u8, size: usize) -> NonNull<Self> { + assert!(ptr.wrapping_add(size) >= ptr); let ret = Self::from_raw_sized_unchecked(ptr, size); Self::check_ptr(ret); NonNull::new_unchecked(ret as _) @@ -188,8 +190,17 @@ impl<T: ?Sized> User<T> where T: UserSafe { // from outside as obtained by `super::alloc`. fn new_uninit_bytes(size: usize) -> Self { unsafe { - let ptr = super::alloc(size, T::align_of()).expect("User memory allocation failed"); - User(NonNull::new_userref(T::from_raw_sized(ptr as _, size))) + // Mustn't call alloc with size 0. + let ptr = if size > 0 { + rtunwrap!(Ok, super::alloc(size, T::align_of())) as _ + } else { + T::align_of() as _ // dangling pointer ok for size 0 + }; + if let Ok(v) = crate::panic::catch_unwind(|| T::from_raw_sized(ptr, size)) { + User(NonNull::new_userref(v)) + } else { + rtabort!("Got invalid pointer from alloc() usercall") + } } } @@ -259,6 +270,7 @@ impl<T> User<[T]> where [T]: UserSafe { /// /// * The pointer is not aligned /// * The pointer is null + /// * The pointed-to range does not fit in the address space /// * The pointed-to range is not in user memory pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self { User(NonNull::new_userref(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()))) @@ -363,6 +375,7 @@ impl<T> UserRef<[T]> where [T]: UserSafe { /// /// * The pointer is not aligned /// * The pointer is null + /// * The pointed-to range does not fit in the address space /// * The pointed-to range is not in user memory pub unsafe fn from_raw_parts<'a>(ptr: *const T, len: usize) -> &'a Self { &*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()).as_ptr() as *const Self) @@ -380,6 +393,7 @@ impl<T> UserRef<[T]> where [T]: UserSafe { /// /// * The pointer is not aligned /// * The pointer is null + /// * The pointed-to range does not fit in the address space /// * The pointed-to range is not in user memory pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut T, len: usize) -> &'a mut Self { &mut*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()).as_ptr() as *mut Self) @@ -424,7 +438,7 @@ impl<T> UserRef<[T]> where [T]: UserSafe { } /// Returns an iterator over the slice. - pub fn iter(&self) -> Iter<T> + pub fn iter(&self) -> Iter<'_, T> where T: UserSafe // FIXME: should be implied by [T]: UserSafe? { unsafe { @@ -433,7 +447,7 @@ impl<T> UserRef<[T]> where [T]: UserSafe { } /// Returns an iterator that allows modifying each value. - pub fn iter_mut(&mut self) -> IterMut<T> + pub fn iter_mut(&mut self) -> IterMut<'_, T> where T: UserSafe // FIXME: should be implied by [T]: UserSafe? { unsafe { @@ -514,7 +528,11 @@ impl<T, I: SliceIndex<[T]>> Index<I> for UserRef<[T]> where [T]: UserSafe, I::Ou #[inline] fn index(&self, index: I) -> &UserRef<I::Output> { unsafe { - UserRef::from_ptr(index.index(&*self.as_raw_ptr())) + if let Some(slice) = index.get(&*self.as_raw_ptr()) { + UserRef::from_ptr(slice) + } else { + rtabort!("index out of range for user slice"); + } } } } @@ -524,7 +542,11 @@ impl<T, I: SliceIndex<[T]>> IndexMut<I> for UserRef<[T]> where [T]: UserSafe, I: #[inline] fn index_mut(&mut self, index: I) -> &mut UserRef<I::Output> { unsafe { - UserRef::from_mut_ptr(index.index_mut(&mut*self.as_raw_mut_ptr())) + if let Some(slice) = index.get_mut(&mut*self.as_raw_mut_ptr()) { + UserRef::from_mut_ptr(slice) + } else { + rtabort!("index out of range for user slice"); + } } } } @@ -535,10 +557,11 @@ impl UserRef<super::raw::ByteBuffer> { /// enclave memory. /// /// # Panics - /// This function panics if: + /// This function panics if, in the user `ByteBuffer`: /// - /// * The pointer in the user `ByteBuffer` is null - /// * The pointed-to range in the user `ByteBuffer` is not in user memory + /// * The pointer is null + /// * The pointed-to range does not fit in the address space + /// * The pointed-to range is not in user memory pub fn copy_user_buffer(&self) -> Vec<u8> { unsafe { let buf = self.to_enclave(); diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs index d84b6154cbe..fca62e028de 100644 --- a/src/libstd/sys/sgx/abi/usercalls/mod.rs +++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs @@ -1,4 +1,5 @@ -use crate::io::{Error as IoError, Result as IoResult}; +use crate::cmp; +use crate::io::{Error as IoError, Result as IoResult, IoSlice, IoSliceMut}; use crate::time::Duration; pub(crate) mod alloc; @@ -8,13 +9,27 @@ pub(crate) mod raw; use self::raw::*; /// Usercall `read`. See the ABI documentation for more information. +/// +/// This will do a single `read` usercall and scatter the read data among +/// `bufs`. To read to a single buffer, just pass a slice of length one. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn read(fd: Fd, buf: &mut [u8]) -> IoResult<usize> { +pub fn read(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> IoResult<usize> { unsafe { - let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.len()); - let len = raw::read(fd, userbuf.as_mut_ptr(), userbuf.len()).from_sgx_result()?; - userbuf[..len].copy_to_enclave(&mut buf[..len]); - Ok(len) + let total_len = bufs.iter().fold(0usize, |sum, buf| sum.saturating_add(buf.len())); + let mut userbuf = alloc::User::<[u8]>::uninitialized(total_len); + let ret_len = raw::read(fd, userbuf.as_mut_ptr(), userbuf.len()).from_sgx_result()?; + let userbuf = &userbuf[..ret_len]; + let mut index = 0; + for buf in bufs { + let end = cmp::min(index + buf.len(), userbuf.len()); + if let Some(buflen) = end.checked_sub(index) { + userbuf[index..end].copy_to_enclave(&mut buf[..buflen]); + index += buf.len(); + } else { + break + } + } + Ok(userbuf.len()) } } @@ -30,10 +45,24 @@ pub fn read_alloc(fd: Fd) -> IoResult<Vec<u8>> { } /// Usercall `write`. See the ABI documentation for more information. +/// +/// This will do a single `write` usercall and gather the written data from +/// `bufs`. To write from a single buffer, just pass a slice of length one. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn write(fd: Fd, buf: &[u8]) -> IoResult<usize> { +pub fn write(fd: Fd, bufs: &[IoSlice<'_>]) -> IoResult<usize> { unsafe { - let userbuf = alloc::User::new_from_enclave(buf); + let total_len = bufs.iter().fold(0usize, |sum, buf| sum.saturating_add(buf.len())); + let mut userbuf = alloc::User::<[u8]>::uninitialized(total_len); + let mut index = 0; + for buf in bufs { + let end = cmp::min(index + buf.len(), userbuf.len()); + if let Some(buflen) = end.checked_sub(index) { + userbuf[index..end].copy_from_enclave(&buf[..buflen]); + index += buf.len(); + } else { + break + } + } raw::write(fd, userbuf.as_ptr(), userbuf.len()).from_sgx_result() } } @@ -52,7 +81,7 @@ pub fn close(fd: Fd) { fn string_from_bytebuffer(buf: &alloc::UserRef<ByteBuffer>, usercall: &str, arg: &str) -> String { String::from_utf8(buf.copy_user_buffer()) - .unwrap_or_else(|_| panic!("Usercall {}: expected {} to be valid UTF-8", usercall, arg)) + .unwrap_or_else(|_| rtabort!("Usercall {}: expected {} to be valid UTF-8", usercall, arg)) } /// Usercall `bind_stream`. See the ABI documentation for more information. @@ -176,7 +205,7 @@ fn check_os_error(err: Result) -> i32 { { err } else { - panic!("Usercall: returned invalid error value {}", err) + rtabort!("Usercall: returned invalid error value {}", err) } } diff --git a/src/libstd/sys/sgx/abi/usercalls/raw.rs b/src/libstd/sys/sgx/abi/usercalls/raw.rs index ad0b6d7b3d8..e4694a8827a 100644 --- a/src/libstd/sys/sgx/abi/usercalls/raw.rs +++ b/src/libstd/sys/sgx/abi/usercalls/raw.rs @@ -131,22 +131,22 @@ impl<T: RegisterArgument> RegisterArgument for Option<NonNull<T>> { impl ReturnValue for ! { fn from_registers(call: &'static str, _regs: (Register, Register)) -> Self { - panic!("Usercall {}: did not expect to be re-entered", call); + rtabort!("Usercall {}: did not expect to be re-entered", call); } } impl ReturnValue for () { - fn from_registers(call: &'static str, regs: (Register, Register)) -> Self { - assert_eq!(regs.0, 0, "Usercall {}: expected {} return value to be 0", call, "1st"); - assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd"); + fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self { + rtassert!(usercall_retval.0 == 0); + rtassert!(usercall_retval.1 == 0); () } } impl<T: RegisterArgument> ReturnValue for T { - fn from_registers(call: &'static str, regs: (Register, Register)) -> Self { - assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd"); - T::from_register(regs.0) + fn from_registers(call: &'static str, usercall_retval: (Register, Register)) -> Self { + rtassert!(usercall_retval.1 == 0); + T::from_register(usercall_retval.0) } } @@ -174,8 +174,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[inline(always)] pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3, $n4: $t4) -> $r { ReturnValue::from_registers(stringify!($f), do_usercall( - NonZeroU64::new(Usercalls::$f as Register) - .expect("Usercall number must be non-zero"), + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), RegisterArgument::into_register($n1), RegisterArgument::into_register($n2), RegisterArgument::into_register($n3), @@ -191,8 +190,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[inline(always)] pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r { ReturnValue::from_registers(stringify!($f), do_usercall( - NonZeroU64::new(Usercalls::$f as Register) - .expect("Usercall number must be non-zero"), + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), RegisterArgument::into_register($n1), RegisterArgument::into_register($n2), RegisterArgument::into_register($n3), @@ -208,8 +206,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[inline(always)] pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r { ReturnValue::from_registers(stringify!($f), do_usercall( - NonZeroU64::new(Usercalls::$f as Register) - .expect("Usercall number must be non-zero"), + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), RegisterArgument::into_register($n1), RegisterArgument::into_register($n2), 0,0, @@ -224,8 +221,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[inline(always)] pub unsafe fn $f($n1: $t1) -> $r { ReturnValue::from_registers(stringify!($f), do_usercall( - NonZeroU64::new(Usercalls::$f as Register) - .expect("Usercall number must be non-zero"), + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), RegisterArgument::into_register($n1), 0,0,0, return_type_is_abort!($r) @@ -239,8 +235,7 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[inline(always)] pub unsafe fn $f() -> $r { ReturnValue::from_registers(stringify!($f), do_usercall( - NonZeroU64::new(Usercalls::$f as Register) - .expect("Usercall number must be non-zero"), + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), 0,0,0,0, return_type_is_abort!($r) )) diff --git a/src/libstd/sys/sgx/alloc.rs b/src/libstd/sys/sgx/alloc.rs index 94dc8ec25b5..40daec758a9 100644 --- a/src/libstd/sys/sgx/alloc.rs +++ b/src/libstd/sys/sgx/alloc.rs @@ -4,6 +4,8 @@ use super::waitqueue::SpinMutex; // Using a SpinMutex because we never want to exit the enclave waiting for the // allocator. +#[cfg_attr(test, linkage = "available_externally")] +#[export_name = "_ZN16__rust_internals3std3sys3sgx5alloc8DLMALLOCE"] static DLMALLOC: SpinMutex<dlmalloc::Dlmalloc> = SpinMutex::new(dlmalloc::DLMALLOC_INIT); #[stable(feature = "alloc_system_type", since = "1.28.0")] @@ -28,3 +30,17 @@ unsafe impl GlobalAlloc for System { DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) } } + +// The following functions are needed by libunwind. These symbols are named +// in pre-link args for the target specification, so keep that in sync. +#[cfg(not(test))] +#[no_mangle] +pub unsafe extern "C" fn __rust_c_alloc(size: usize, align: usize) -> *mut u8 { + crate::alloc::alloc(Layout::from_size_align_unchecked(size, align)) +} + +#[cfg(not(test))] +#[no_mangle] +pub unsafe extern "C" fn __rust_c_dealloc(ptr: *mut u8, size: usize, align: usize) { + crate::alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, align)) +} diff --git a/src/libstd/sys/sgx/args.rs b/src/libstd/sys/sgx/args.rs index b73bf9213b7..a84ab413876 100644 --- a/src/libstd/sys/sgx/args.rs +++ b/src/libstd/sys/sgx/args.rs @@ -5,9 +5,12 @@ use crate::sys::os_str::Buf; use crate::sys_common::FromInner; use crate::slice; +#[cfg_attr(test, linkage = "available_externally")] +#[export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE"] static ARGS: AtomicUsize = AtomicUsize::new(0); type ArgsStore = Vec<OsString>; +#[cfg_attr(test, allow(dead_code))] pub unsafe fn init(argc: isize, argv: *const *const u8) { if argc != 0 { let args = alloc::User::<[ByteBuffer]>::from_raw_parts(argv as _, argc as _); diff --git a/src/libstd/sys/sgx/backtrace.rs b/src/libstd/sys/sgx/backtrace.rs deleted file mode 100644 index d0361574e39..00000000000 --- a/src/libstd/sys/sgx/backtrace.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::io; -use crate::error::Error; -use crate::fmt; -use crate::sys_common::backtrace::Frame; -use crate::sys::sgx::abi::mem::image_base; - -use unwind as uw; - -pub struct BacktraceContext; - -struct Context<'a> { - idx: usize, - frames: &'a mut [Frame], -} - -#[derive(Debug)] -struct UnwindError(uw::_Unwind_Reason_Code); - -impl Error for UnwindError { - fn description(&self) -> &'static str { - "unexpected return value while unwinding" - } -} - -impl fmt::Display for UnwindError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {:?}", self.description(), self.0) - } -} - -#[inline(never)] // this function call can be skipped it when tracing. -pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> { - let mut cx = Context { idx: 0, frames }; - let result_unwind = - unsafe { uw::_Unwind_Backtrace(trace_fn, &mut cx as *mut Context as *mut libc::c_void) }; - // See libunwind:src/unwind/Backtrace.c for the return values. - // No, there is no doc. - let res = match result_unwind { - // These return codes seem to be benign and need to be ignored for backtraces - // to show up properly on all tested platforms. - uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => { - Ok((cx.idx, BacktraceContext)) - } - _ => Err(io::Error::new( - io::ErrorKind::Other, - UnwindError(result_unwind), - )), - }; - res -} - -extern "C" fn trace_fn( - ctx: *mut uw::_Unwind_Context, - arg: *mut libc::c_void, -) -> uw::_Unwind_Reason_Code { - let cx = unsafe { &mut *(arg as *mut Context) }; - if cx.idx >= cx.frames.len() { - return uw::_URC_NORMAL_STOP; - } - - let mut ip_before_insn = 0; - let mut ip = unsafe { uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void }; - if !ip.is_null() && ip_before_insn == 0 { - // this is a non-signaling frame, so `ip` refers to the address - // after the calling instruction. account for that. - ip = (ip as usize - 1) as *mut _; - } - - let symaddr = unsafe { uw::_Unwind_FindEnclosingFunction(ip) }; - cx.frames[cx.idx] = Frame { - symbol_addr: symaddr as *mut u8, - exact_position: ip as *mut u8, - inline_context: 0, - }; - cx.idx += 1; - - uw::_URC_NO_REASON -} - -// To reduce TCB size in Sgx enclave, we do not want to implement resolve_symname functionality. -// Rather, we print the offset of the address here, which could be later mapped to correct function. -pub fn resolve_symname<F>(frame: Frame, - callback: F, - _: &BacktraceContext) -> io::Result<()> - where F: FnOnce(Option<&str>) -> io::Result<()> -{ - callback(Some(&format!("0x{:x}", - (frame.symbol_addr.wrapping_offset_from(image_base() as _))))) -} - -pub fn foreach_symbol_fileline<F>(_: Frame, - _: F, - _: &BacktraceContext) -> io::Result<bool> - where F: FnMut(&[u8], u32) -> io::Result<()> -{ - Ok(false) -} diff --git a/src/libstd/sys/sgx/condvar.rs b/src/libstd/sys/sgx/condvar.rs index e9a7684f74d..000bb19f269 100644 --- a/src/libstd/sys/sgx/condvar.rs +++ b/src/libstd/sys/sgx/condvar.rs @@ -33,7 +33,7 @@ impl Condvar { } pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { - panic!("timeout not supported in SGX"); + rtabort!("timeout not supported in SGX"); } #[inline] diff --git a/src/libstd/sys/sgx/ext/arch.rs b/src/libstd/sys/sgx/ext/arch.rs index 53fb371947a..5056e388112 100644 --- a/src/libstd/sys/sgx/ext/arch.rs +++ b/src/libstd/sys/sgx/ext/arch.rs @@ -28,7 +28,7 @@ const ENCLU_EGETKEY: u32 = 1; #[unstable(feature = "sgx_platform", issue = "56975")] pub fn egetkey(request: &Align512<[u8; 512]>) -> Result<Align16<[u8; 16]>, u32> { unsafe { - let mut out = MaybeUninit::uninitialized(); + let mut out = MaybeUninit::uninit(); let error; asm!( @@ -41,7 +41,7 @@ pub fn egetkey(request: &Align512<[u8; 512]>) -> Result<Align16<[u8; 16]>, u32> ); match error { - 0 => Ok(out.into_initialized()), + 0 => Ok(out.assume_init()), err => Err(err), } } @@ -58,7 +58,7 @@ pub fn ereport( reportdata: &Align128<[u8; 64]>, ) -> Align512<[u8; 432]> { unsafe { - let mut report = MaybeUninit::uninitialized(); + let mut report = MaybeUninit::uninit(); asm!( "enclu" @@ -69,6 +69,6 @@ pub fn ereport( "{rdx}"(report.as_mut_ptr()) ); - report.into_initialized() + report.assume_init() } } diff --git a/src/libstd/sys/sgx/ext/ffi.rs b/src/libstd/sys/sgx/ext/ffi.rs index ad7ade9b17d..63fc5ff2866 100644 --- a/src/libstd/sys/sgx/ext/ffi.rs +++ b/src/libstd/sys/sgx/ext/ffi.rs @@ -1,109 +1,38 @@ //! SGX-specific extension to the primitives in the `std::ffi` module +//! +//! # Examples +//! +//! ``` +//! use std::ffi::OsString; +//! use std::os::fortanix_sgx::ffi::OsStringExt; +//! +//! let bytes = b"foo".to_vec(); +//! +//! // OsStringExt::from_vec +//! let os_string = OsString::from_vec(bytes); +//! assert_eq!(os_string.to_str(), Some("foo")); +//! +//! // OsStringExt::into_vec +//! let bytes = os_string.into_vec(); +//! assert_eq!(bytes, b"foo"); +//! ``` +//! +//! ``` +//! use std::ffi::OsStr; +//! use std::os::fortanix_sgx::ffi::OsStrExt; +//! +//! let bytes = b"foo"; +//! +//! // OsStrExt::from_bytes +//! let os_str = OsStr::from_bytes(bytes); +//! assert_eq!(os_str.to_str(), Some("foo")); +//! +//! // OsStrExt::as_bytes +//! let bytes = os_str.as_bytes(); +//! assert_eq!(bytes, b"foo"); +//! ``` #![unstable(feature = "sgx_platform", issue = "56975")] -use crate::ffi::{OsStr, OsString}; -use crate::mem; -use crate::sys::os_str::Buf; -use crate::sys_common::{FromInner, IntoInner, AsInner}; - -/// SGX-specific extensions to [`OsString`]. -/// -/// [`OsString`]: ../../../../std/ffi/struct.OsString.html -#[unstable(feature = "sgx_platform", issue = "56975")] -pub trait OsStringExt { - /// Creates an [`OsString`] from a byte vector. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// use std::os::unix::ffi::OsStringExt; - /// - /// let bytes = b"foo".to_vec(); - /// let os_string = OsString::from_vec(bytes); - /// assert_eq!(os_string.to_str(), Some("foo")); - /// ``` - /// - /// [`OsString`]: ../../../ffi/struct.OsString.html - #[unstable(feature = "sgx_platform", issue = "56975")] - fn from_vec(vec: Vec<u8>) -> Self; - - /// Yields the underlying byte vector of this [`OsString`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// use std::os::unix::ffi::OsStringExt; - /// - /// let mut os_string = OsString::new(); - /// os_string.push("foo"); - /// let bytes = os_string.into_vec(); - /// assert_eq!(bytes, b"foo"); - /// ``` - /// - /// [`OsString`]: ../../../ffi/struct.OsString.html - #[unstable(feature = "sgx_platform", issue = "56975")] - fn into_vec(self) -> Vec<u8>; -} - -#[unstable(feature = "sgx_platform", issue = "56975")] -impl OsStringExt for OsString { - fn from_vec(vec: Vec<u8>) -> OsString { - FromInner::from_inner(Buf { inner: vec }) - } - fn into_vec(self) -> Vec<u8> { - self.into_inner().inner - } -} - -/// SGX-specific extensions to [`OsStr`]. -/// -/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html -#[unstable(feature = "sgx_platform", issue = "56975")] -pub trait OsStrExt { - #[unstable(feature = "sgx_platform", issue = "56975")] - /// Creates an [`OsStr`] from a byte slice. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// use std::os::unix::ffi::OsStrExt; - /// - /// let bytes = b"foo"; - /// let os_str = OsStr::from_bytes(bytes); - /// assert_eq!(os_str.to_str(), Some("foo")); - /// ``` - /// - /// [`OsStr`]: ../../../ffi/struct.OsStr.html - fn from_bytes(slice: &[u8]) -> &Self; - - /// Gets the underlying byte view of the [`OsStr`] slice. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// use std::os::unix::ffi::OsStrExt; - /// - /// let mut os_str = OsStr::new("foo"); - /// let bytes = os_str.as_bytes(); - /// assert_eq!(bytes, b"foo"); - /// ``` - /// - /// [`OsStr`]: ../../../ffi/struct.OsStr.html - #[unstable(feature = "sgx_platform", issue = "56975")] - fn as_bytes(&self) -> &[u8]; -} - #[unstable(feature = "sgx_platform", issue = "56975")] -impl OsStrExt for OsStr { - fn from_bytes(slice: &[u8]) -> &OsStr { - unsafe { mem::transmute(slice) } - } - fn as_bytes(&self) -> &[u8] { - &self.as_inner().inner - } -} +pub use crate::sys_common::os_str_bytes::*; diff --git a/src/libstd/sys/sgx/fd.rs b/src/libstd/sys/sgx/fd.rs index a9924f55f12..a1c4af81966 100644 --- a/src/libstd/sys/sgx/fd.rs +++ b/src/libstd/sys/sgx/fd.rs @@ -1,6 +1,6 @@ use fortanix_sgx_abi::Fd; -use crate::io; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; use crate::sys::{AsInner, FromInner, IntoInner}; use super::abi::usercalls; @@ -25,11 +25,19 @@ impl FileDesc { } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { - usercalls::read(self.fd, buf) + usercalls::read(self.fd, &mut [IoSliceMut::new(buf)]) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + usercalls::read(self.fd, bufs) } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { - usercalls::write(self.fd, buf) + usercalls::write(self.fd, &[IoSlice::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + usercalls::write(self.fd, bufs) } pub fn flush(&self) -> io::Result<()> { diff --git a/src/libstd/sys/sgx/fs.rs b/src/libstd/sys/sgx/fs.rs index 485d2c87fbd..e9095b375fe 100644 --- a/src/libstd/sys/sgx/fs.rs +++ b/src/libstd/sys/sgx/fs.rs @@ -1,7 +1,7 @@ use crate::ffi::OsString; use crate::fmt; use crate::hash::{Hash, Hasher}; -use crate::io::{self, SeekFrom}; +use crate::io::{self, SeekFrom, IoSlice, IoSliceMut}; use crate::path::{Path, PathBuf}; use crate::sys::time::SystemTime; use crate::sys::{unsupported, Void}; @@ -82,7 +82,7 @@ impl Eq for FilePermissions { } impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -125,13 +125,13 @@ impl Hash for FileType { } impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -200,10 +200,18 @@ impl File { match self.0 {} } + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { match self.0 {} } + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn flush(&self) -> io::Result<()> { match self.0 {} } @@ -236,7 +244,7 @@ impl DirBuilder { } impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } diff --git a/src/libstd/sys/sgx/io.rs b/src/libstd/sys/sgx/io.rs index 8b02d3fd19d..976e122463d 100644 --- a/src/libstd/sys/sgx/io.rs +++ b/src/libstd/sys/sgx/io.rs @@ -1,9 +1,16 @@ -pub struct IoVec<'a>(&'a [u8]); +use crate::mem; + +pub struct IoSlice<'a>(&'a [u8]); + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice(buf) + } -impl<'a> IoVec<'a> { #[inline] - pub fn new(buf: &'a [u8]) -> IoVec<'a> { - IoVec(buf) + pub fn advance(&mut self, n: usize) { + self.0 = &self.0[n..] } #[inline] @@ -12,12 +19,19 @@ impl<'a> IoVec<'a> { } } -pub struct IoVecMut<'a>(&'a mut [u8]); +pub struct IoSliceMut<'a>(&'a mut [u8]); + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut(buf) + } -impl<'a> IoVecMut<'a> { #[inline] - pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> { - IoVecMut(buf) + pub fn advance(&mut self, n: usize) { + let slice = mem::replace(&mut self.0, &mut []); + let (_, remaining) = slice.split_at_mut(n); + self.0 = remaining; } #[inline] diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs index 325df7688f3..601957acd5c 100644 --- a/src/libstd/sys/sgx/mod.rs +++ b/src/libstd/sys/sgx/mod.rs @@ -12,8 +12,6 @@ mod waitqueue; pub mod alloc; pub mod args; -#[cfg(feature = "backtrace")] -pub mod backtrace; pub mod cmath; pub mod condvar; pub mod env; @@ -25,7 +23,6 @@ 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; @@ -36,6 +33,8 @@ pub mod thread_local; pub mod time; pub mod stdio; +pub use crate::sys_common::os_str_bytes as os_str; + #[cfg(not(test))] pub fn init() { } @@ -129,16 +128,25 @@ pub unsafe fn abort_internal() -> ! { abi::usercalls::exit(true) } +// This function is needed by the panic runtime. The symbol is named in +// pre-link args for the target specification, so keep that in sync. +#[cfg(not(test))] +#[no_mangle] +// NB. used by both libunwind and libpanic_abort +pub unsafe extern "C" fn __rust_abort() { + abort_internal(); +} + pub fn hashmap_random_keys() -> (u64, u64) { fn rdrand64() -> u64 { unsafe { - let mut ret: u64 = crate::mem::uninitialized(); + let mut ret: u64 = 0; for _ in 0..10 { if crate::arch::x86_64::_rdrand64_step(&mut ret) == 1 { return ret; } } - panic!("Failed to obtain random data"); + rtabort!("Failed to obtain random data"); } } (rdrand64(), rdrand64()) diff --git a/src/libstd/sys/sgx/net.rs b/src/libstd/sys/sgx/net.rs index e5e42e3d0b0..f9eca9f4cb3 100644 --- a/src/libstd/sys/sgx/net.rs +++ b/src/libstd/sys/sgx/net.rs @@ -1,5 +1,5 @@ use crate::fmt; -use crate::io::{self, IoVec, IoVecMut}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr, ToSocketAddrs}; use crate::time::Duration; use crate::sys::{unsupported, Void, sgx_ineffective, AsInner, FromInner, IntoInner, TryIntoInner}; @@ -41,12 +41,29 @@ impl FromInner<FileDesc> for Socket { } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct TcpStream { inner: Socket, peer_addr: Option<String>, } +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpStream"); + + if let Some(ref addr) = self.inner.local_addr { + res.field("addr", addr); + } + + if let Some(ref peer) = self.peer_addr { + res.field("peer", peer); + } + + res.field("fd", &self.inner.inner.as_inner()) + .finish() + } +} + fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result<String> { match result { Ok(saddr) => Ok(saddr.to_string()), @@ -75,16 +92,32 @@ impl TcpStream { Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) }) } - pub fn connect_timeout(addr: &SocketAddr, _: Duration) -> io::Result<TcpStream> { + pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result<TcpStream> { + if dur == Duration::default() { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } Self::connect(Ok(addr)) // FIXME: ignoring timeout } - pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { - sgx_ineffective(()) + pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { + match dur { + Some(dur) if dur == Duration::default() => { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + _ => sgx_ineffective(()) + } } - pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { - sgx_ineffective(()) + pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { + match dur { + Some(dur) if dur == Duration::default() => { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + _ => sgx_ineffective(()) + } } pub fn read_timeout(&self) -> io::Result<Option<Duration>> { @@ -103,24 +136,16 @@ impl TcpStream { self.inner.inner.read(buf) } - pub fn read_vectored(&self, buf: &mut [IoVecMut<'_>]) -> io::Result<usize> { - let buf = match buf.get_mut(0) { - Some(buf) => buf, - None => return Ok(0), - }; - self.read(buf) + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.inner.inner.read_vectored(bufs) } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.inner.inner.write(buf) } - pub fn write_vectored(&self, buf: &[IoVec<'_>]) -> io::Result<usize> { - let buf = match buf.get(0) { - Some(buf) => buf, - None => return Ok(0), - }; - self.write(buf) + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.inner.inner.write_vectored(bufs) } pub fn peer_addr(&self) -> io::Result<SocketAddr> { @@ -182,11 +207,24 @@ impl FromInner<(Socket, Option<String>)> for TcpStream { } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct TcpListener { inner: Socket, } +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpListener"); + + if let Some(ref addr) = self.inner.local_addr { + res.field("addr", addr); + } + + res.field("fd", &self.inner.inner.as_inner()) + .finish() + } +} + impl TcpListener { pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> { let addr = io_err_to_addr(addr)?; @@ -257,6 +295,10 @@ impl UdpSocket { unsupported() } + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + match self.0 {} + } + pub fn socket_addr(&self) -> io::Result<SocketAddr> { match self.0 {} } @@ -379,7 +421,7 @@ impl UdpSocket { } impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -396,7 +438,7 @@ impl error::Error for NonIpSockAddr { } impl fmt::Display for NonIpSockAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Failed to convert address to SocketAddr: {}", self.host) } } diff --git a/src/libstd/sys/sgx/os.rs b/src/libstd/sys/sgx/os.rs index 2725e66ce5d..8b12c49edba 100644 --- a/src/libstd/sys/sgx/os.rs +++ b/src/libstd/sys/sgx/os.rs @@ -37,7 +37,7 @@ pub fn chdir(_: &path::Path) -> io::Result<()> { pub struct SplitPaths<'a>(&'a Void); -pub fn split_paths(_unparsed: &OsStr) -> SplitPaths { +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") } @@ -58,7 +58,7 @@ pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError> } impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "not supported in SGX yet".fmt(f) } } @@ -73,7 +73,11 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } +#[cfg_attr(test, linkage = "available_externally")] +#[export_name = "_ZN16__rust_internals3std3sys3sgx2os3ENVE"] static ENV: AtomicUsize = AtomicUsize::new(0); +#[cfg_attr(test, linkage = "available_externally")] +#[export_name = "_ZN16__rust_internals3std3sys3sgx2os8ENV_INITE"] static ENV_INIT: Once = Once::new(); type EnvStore = Mutex<HashMap<OsString, OsString>>; diff --git a/src/libstd/sys/sgx/os_str.rs b/src/libstd/sys/sgx/os_str.rs deleted file mode 100644 index 79b43458d00..00000000000 --- a/src/libstd/sys/sgx/os_str.rs +++ /dev/null @@ -1,180 +0,0 @@ -/// The underlying OsString/OsStr implementation on Unix systems: just -/// a `Vec<u8>`/`[u8]`. - -use crate::borrow::Cow; -use crate::fmt; -use crate::str; -use crate::mem; -use crate::rc::Rc; -use crate::sync::Arc; -use crate::sys_common::{AsInner, IntoInner}; -use crate::sys_common::bytestring::debug_fmt_bytestring; - -use core::str::lossy::Utf8Lossy; - -#[derive(Clone, Hash)] -pub struct Buf { - pub inner: Vec<u8> -} - -pub struct Slice { - pub inner: [u8] -} - -impl fmt::Debug for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - debug_fmt_bytestring(&self.inner, formatter) - } -} - -impl fmt::Display for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) - } -} - -impl fmt::Debug for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self.as_slice(), formatter) - } -} - -impl fmt::Display for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self.as_slice(), formatter) - } -} - -impl IntoInner<Vec<u8>> for Buf { - fn into_inner(self) -> Vec<u8> { - self.inner - } -} - -impl AsInner<[u8]> for Buf { - fn as_inner(&self) -> &[u8] { - &self.inner - } -} - - -impl Buf { - pub fn from_string(s: String) -> Buf { - Buf { inner: s.into_bytes() } - } - - #[inline] - pub fn with_capacity(capacity: usize) -> Buf { - Buf { - inner: Vec::with_capacity(capacity) - } - } - - #[inline] - pub fn clear(&mut self) { - self.inner.clear() - } - - #[inline] - pub fn capacity(&self) -> usize { - self.inner.capacity() - } - - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.inner.reserve(additional) - } - - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.inner.reserve_exact(additional) - } - - #[inline] - pub fn shrink_to_fit(&mut self) { - self.inner.shrink_to_fit() - } - - #[inline] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.inner.shrink_to(min_capacity) - } - - pub fn as_slice(&self) -> &Slice { - unsafe { mem::transmute(&*self.inner) } - } - - pub fn into_string(self) -> Result<String, Buf> { - String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) - } - - pub fn push_slice(&mut self, s: &Slice) { - self.inner.extend_from_slice(&s.inner) - } - - #[inline] - pub fn into_box(self) -> Box<Slice> { - unsafe { mem::transmute(self.inner.into_boxed_slice()) } - } - - #[inline] - pub fn from_box(boxed: Box<Slice>) -> Buf { - let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; - Buf { inner: inner.into_vec() } - } - - #[inline] - pub fn into_arc(&self) -> Arc<Slice> { - self.as_slice().into_arc() - } - - #[inline] - pub fn into_rc(&self) -> Rc<Slice> { - self.as_slice().into_rc() - } -} - -impl Slice { - fn from_u8_slice(s: &[u8]) -> &Slice { - unsafe { mem::transmute(s) } - } - - pub fn from_str(s: &str) -> &Slice { - Slice::from_u8_slice(s.as_bytes()) - } - - pub fn to_str(&self) -> Option<&str> { - str::from_utf8(&self.inner).ok() - } - - pub fn to_string_lossy(&self) -> Cow<str> { - String::from_utf8_lossy(&self.inner) - } - - pub fn to_owned(&self) -> Buf { - Buf { inner: self.inner.to_vec() } - } - - #[inline] - pub fn into_box(&self) -> Box<Slice> { - let boxed: Box<[u8]> = self.inner.into(); - unsafe { mem::transmute(boxed) } - } - - pub fn empty_box() -> Box<Slice> { - let boxed: Box<[u8]> = Default::default(); - unsafe { mem::transmute(boxed) } - } - - #[inline] - pub fn into_arc(&self) -> Arc<Slice> { - let arc: Arc<[u8]> = Arc::from(&self.inner); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } - } - - #[inline] - pub fn into_rc(&self) -> Rc<Slice> { - let rc: Rc<[u8]> = Rc::from(&self.inner); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } - } -} diff --git a/src/libstd/sys/sgx/path.rs b/src/libstd/sys/sgx/path.rs index 1115de1fbe5..b5fd7e3ae6d 100644 --- a/src/libstd/sys/sgx/path.rs +++ b/src/libstd/sys/sgx/path.rs @@ -11,7 +11,7 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'/' } -pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { +pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> { None } diff --git a/src/libstd/sys/sgx/pipe.rs b/src/libstd/sys/sgx/pipe.rs index 2582b993b60..fb14dc59101 100644 --- a/src/libstd/sys/sgx/pipe.rs +++ b/src/libstd/sys/sgx/pipe.rs @@ -1,4 +1,4 @@ -use crate::io; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::sys::Void; pub struct AnonPipe(Void); @@ -8,18 +8,23 @@ impl AnonPipe { match self.0 {} } + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { match self.0 {} } + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn diverge(&self) -> ! { match self.0 {} } } -pub fn read2(p1: AnonPipe, - _v1: &mut Vec<u8>, - _p2: AnonPipe, - _v2: &mut Vec<u8>) -> io::Result<()> { +pub fn read2(p1: AnonPipe, _v1: &mut Vec<u8>, _p2: AnonPipe, _v2: &mut Vec<u8>) -> io::Result<()> { match p1.0 {} } diff --git a/src/libstd/sys/sgx/process.rs b/src/libstd/sys/sgx/process.rs index c49daaa1632..a02e009d953 100644 --- a/src/libstd/sys/sgx/process.rs +++ b/src/libstd/sys/sgx/process.rs @@ -73,7 +73,7 @@ impl From<File> for Stdio { } impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { Ok(()) } } @@ -108,13 +108,13 @@ impl Eq for ExitStatus { } impl fmt::Debug for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } impl fmt::Display for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } diff --git a/src/libstd/sys/sgx/rwlock.rs b/src/libstd/sys/sgx/rwlock.rs index 372760bbf26..30c47e44eef 100644 --- a/src/libstd/sys/sgx/rwlock.rs +++ b/src/libstd/sys/sgx/rwlock.rs @@ -1,7 +1,4 @@ -use crate::alloc::{self, Layout}; use crate::num::NonZeroUsize; -use crate::slice; -use crate::str; use super::waitqueue::{ try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable, @@ -90,8 +87,8 @@ impl RWLock { #[inline] unsafe fn __read_unlock( &self, - mut rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>, - wguard: SpinMutexGuard<WaitVariable<bool>>, + mut rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>, + wguard: SpinMutexGuard<'_, WaitVariable<bool>>, ) { *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1); if rguard.lock_var().is_some() { @@ -102,7 +99,7 @@ impl RWLock { *wguard.lock_var_mut() = true; } else { // No writers were waiting, the lock is released - assert!(rguard.queue_empty()); + rtassert!(rguard.queue_empty()); } } } @@ -117,8 +114,8 @@ impl RWLock { #[inline] unsafe fn __write_unlock( &self, - rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>, - wguard: SpinMutexGuard<WaitVariable<bool>>, + rguard: SpinMutexGuard<'_, WaitVariable<Option<NonZeroUsize>>>, + wguard: SpinMutexGuard<'_, WaitVariable<bool>>, ) { if let Err(mut wguard) = WaitQueue::notify_one(wguard) { // No writers waiting, release the write lock @@ -147,6 +144,7 @@ impl RWLock { // only used by __rust_rwlock_unlock below #[inline] + #[cfg_attr(test, allow(dead_code))] unsafe fn unlock(&self) { let rguard = self.readers.lock(); let wguard = self.writer.lock(); @@ -161,9 +159,12 @@ impl RWLock { pub unsafe fn destroy(&self) {} } +// The following functions are needed by libunwind. These symbols are named +// in pre-link args for the target specification, so keep that in sync. +#[cfg(not(test))] const EINVAL: i32 = 22; -// used by libunwind port +#[cfg(not(test))] #[no_mangle] pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 { if p.is_null() { @@ -173,6 +174,7 @@ pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 { return 0; } +#[cfg(not(test))] #[no_mangle] pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 { if p.is_null() { @@ -181,6 +183,7 @@ pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 { (*p).write(); return 0; } +#[cfg(not(test))] #[no_mangle] pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 { if p.is_null() { @@ -190,46 +193,15 @@ pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 { return 0; } -// the following functions are also used by the libunwind port. They're -// included here to make sure parallel codegen and LTO don't mess things up. -#[no_mangle] -pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { - if s < 0 { - return; - } - let buf = slice::from_raw_parts(m as *const u8, s as _); - if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) { - eprint!("{}", s); - } -} - -#[no_mangle] -// NB. used by both libunwind and libpanic_abort -pub unsafe extern "C" fn __rust_abort() { - crate::sys::abort_internal(); -} - -#[no_mangle] -pub unsafe extern "C" fn __rust_c_alloc(size: usize, align: usize) -> *mut u8 { - alloc::alloc(Layout::from_size_align_unchecked(size, align)) -} - -#[no_mangle] -pub unsafe extern "C" fn __rust_c_dealloc(ptr: *mut u8, size: usize, align: usize) { - alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, align)) -} - #[cfg(test)] mod tests { - use super::*; use core::array::FixedSizeArray; - use crate::mem::MaybeUninit; - use crate::{mem, ptr}; + use crate::mem::{self, MaybeUninit}; - // The below test verifies that the bytes of initialized RWLock are the ones - // we use in libunwind. - // If they change we need to update src/UnwindRustSgx.h in libunwind. + // Verify that the bytes of initialized RWLock are the same as in + // libunwind. If they change, `src/UnwindRustSgx.h` in libunwind needs to + // be changed too. #[test] fn test_c_rwlock_initializer() { const RWLOCK_INIT: &[u8] = &[ @@ -251,11 +223,28 @@ mod tests { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ]; - let mut init = MaybeUninit::<RWLock>::zeroed(); - init.set(RWLock::new()); - assert_eq!( - mem::transmute::<_, [u8; 128]>(init.into_inner()).as_slice(), - RWLOCK_INIT - ); + #[inline(never)] + fn zero_stack() { + test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed()); + } + + #[inline(never)] + unsafe fn rwlock_new(init: &mut MaybeUninit<RWLock>) { + init.write(RWLock::new()); + } + + unsafe { + // try hard to make sure that the padding/unused bytes in RWLock + // get initialized as 0. If the assertion below fails, that might + // just be an issue with the test code and not with the value of + // RWLOCK_INIT. + zero_stack(); + let mut init = MaybeUninit::<RWLock>::zeroed(); + rwlock_new(&mut init); + assert_eq!( + mem::transmute::<_, [u8; 128]>(init.assume_init()).as_slice(), + RWLOCK_INIT + ) + }; } } diff --git a/src/libstd/sys/sgx/stack_overflow.rs b/src/libstd/sys/sgx/stack_overflow.rs index c0e7c824615..e63fa2bed65 100644 --- a/src/libstd/sys/sgx/stack_overflow.rs +++ b/src/libstd/sys/sgx/stack_overflow.rs @@ -6,6 +6,7 @@ impl Handler { } } +#[cfg_attr(test, allow(dead_code))] pub unsafe fn init() { } diff --git a/src/libstd/sys/sgx/stdio.rs b/src/libstd/sys/sgx/stdio.rs index f2c6892bfb7..a575401f5f6 100644 --- a/src/libstd/sys/sgx/stdio.rs +++ b/src/libstd/sys/sgx/stdio.rs @@ -2,6 +2,10 @@ use fortanix_sgx_abi as abi; use crate::io; use crate::sys::fd::FileDesc; +#[cfg(not(test))] +use crate::slice; +#[cfg(not(test))] +use crate::str; pub struct Stdin(()); pub struct Stdout(()); @@ -62,3 +66,17 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub fn panic_output() -> Option<impl io::Write> { super::abi::panic::SgxPanicOutput::new() } + +// This function is needed by libunwind. The symbol is named in pre-link args +// for the target specification, so keep that in sync. +#[cfg(not(test))] +#[no_mangle] +pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { + if s < 0 { + return; + } + let buf = slice::from_raw_parts(m as *const u8, s as _); + if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) { + eprint!("{}", s); + } +} diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs index 13569062ac1..b9f42d4ad1c 100644 --- a/src/libstd/sys/sgx/thread.rs +++ b/src/libstd/sys/sgx/thread.rs @@ -1,4 +1,4 @@ -use crate::boxed::FnBox; +#![cfg_attr(test, allow(dead_code))] // why is this necessary? use crate::ffi::CStr; use crate::io; use crate::time::Duration; @@ -12,17 +12,16 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; mod task_queue { use crate::sync::{Mutex, MutexGuard, Once}; use crate::sync::mpsc; - use crate::boxed::FnBox; pub type JoinHandle = mpsc::Receiver<()>; pub(super) struct Task { - p: Box<dyn FnBox()>, + p: Box<dyn FnOnce()>, done: mpsc::Sender<()>, } impl Task { - pub(super) fn new(p: Box<dyn FnBox()>) -> (Task, JoinHandle) { + pub(super) fn new(p: Box<dyn FnOnce()>) -> (Task, JoinHandle) { let (done, recv) = mpsc::channel(); (Task { p, done }, recv) } @@ -33,7 +32,11 @@ mod task_queue { } } + #[cfg_attr(test, linkage = "available_externally")] + #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread15TASK_QUEUE_INITE"] static TASK_QUEUE_INIT: Once = Once::new(); + #[cfg_attr(test, linkage = "available_externally")] + #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread10TASK_QUEUEE"] static mut TASK_QUEUE: Option<Mutex<Vec<Task>>> = None; pub(super) fn lock() -> MutexGuard<'static, Vec<Task>> { @@ -46,7 +49,7 @@ mod task_queue { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, p: Box<dyn FnBox()>) + pub unsafe fn new(_stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { let mut queue_lock = task_queue::lock(); @@ -57,17 +60,15 @@ impl Thread { } pub(super) fn entry() { - let mut guard = task_queue::lock(); - let task = guard.pop().expect("Thread started but no tasks pending"); - drop(guard); // make sure to not hold the task queue lock longer than necessary + let mut pending_tasks = task_queue::lock(); + let task = rtunwrap!(Some, pending_tasks.pop()); + drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary task.run() } pub fn yield_now() { - assert_eq!( - usercalls::wait(0, usercalls::raw::WAIT_NO).unwrap_err().kind(), - io::ErrorKind::WouldBlock - ); + let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); + rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); } pub fn set_name(_name: &CStr) { @@ -75,7 +76,7 @@ impl Thread { } pub fn sleep(_dur: Duration) { - panic!("can't sleep"); // FIXME + rtabort!("can't sleep"); // FIXME } pub fn join(self) { diff --git a/src/libstd/sys/sgx/time.rs b/src/libstd/sys/sgx/time.rs index e4f789c3e36..4659f7ba71f 100644 --- a/src/libstd/sys/sgx/time.rs +++ b/src/libstd/sys/sgx/time.rs @@ -14,8 +14,8 @@ impl Instant { Instant(usercalls::insecure_time()) } - pub fn sub_instant(&self, other: &Instant) -> Duration { - self.0 - other.0 + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + self.0.checked_sub(other.0) } pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs index 1dbf2afbf49..d542f9b4101 100644 --- a/src/libstd/sys/sgx/waitqueue.rs +++ b/src/libstd/sys/sgx/waitqueue.rs @@ -121,7 +121,7 @@ impl<'a, T> Drop for WaitGuard<'a, T> { NotifiedTcs::Single(tcs) => Some(tcs), NotifiedTcs::All { .. } => None }; - usercalls::send(EV_UNPARK, target_tcs).unwrap(); + rtunwrap!(Ok, usercalls::send(EV_UNPARK, target_tcs)); } } @@ -140,7 +140,8 @@ impl WaitQueue { /// until a wakeup event. /// /// This function does not return until this thread has been awoken. - pub fn wait<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) { + pub fn wait<T>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>) { + // very unsafe: check requirements of UnsafeList::push unsafe { let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry { tcs: thread::current(), @@ -149,10 +150,9 @@ impl WaitQueue { let entry = guard.queue.inner.push(&mut entry); drop(guard); while !entry.lock().wake { - assert_eq!( - usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap() & EV_UNPARK, - EV_UNPARK - ); + // don't panic, this would invalidate `entry` during unwinding + let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE)); + rtassert!(eventset & EV_UNPARK == EV_UNPARK); } } } @@ -162,8 +162,8 @@ impl WaitQueue { /// /// If a waiter is found, a `WaitGuard` is returned which will notify the /// waiter when it is dropped. - pub fn notify_one<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) - -> Result<WaitGuard<T>, SpinMutexGuard<WaitVariable<T>>> + pub fn notify_one<T>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>) + -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> { unsafe { if let Some(entry) = guard.queue.inner.pop() { @@ -186,8 +186,8 @@ impl WaitQueue { /// /// If at least one waiter is found, a `WaitGuard` is returned which will /// notify all waiters when it is dropped. - pub fn notify_all<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) - -> Result<WaitGuard<T>, SpinMutexGuard<WaitVariable<T>>> + pub fn notify_all<T>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>) + -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> { unsafe { let mut count = 0; @@ -269,7 +269,7 @@ mod unsafe_list { // ,-------> /---------\ next ---, // | |head_tail| | // `--- prev \---------/ <-------` - assert_eq!(self.head_tail.as_ref().prev, first); + rtassert!(self.head_tail.as_ref().prev == first); true } else { false @@ -285,7 +285,9 @@ mod unsafe_list { /// # Safety /// /// The entry must remain allocated until the entry is removed from the - /// list AND the caller who popped is done using the entry. + /// list AND the caller who popped is done using the entry. Special + /// care must be taken in the caller of `push` to ensure unwinding does + /// not destroy the stack frame containing the entry. pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry<T>) -> &'a T { self.init(); @@ -303,6 +305,7 @@ mod unsafe_list { entry.as_mut().prev = prev_tail; entry.as_mut().next = self.head_tail; prev_tail.as_mut().next = entry; + // unwrap ok: always `Some` on non-dummy entries (*entry.as_ptr()).value.as_ref().unwrap() } @@ -333,6 +336,7 @@ mod unsafe_list { second.as_mut().prev = self.head_tail; first.as_mut().next = NonNull::dangling(); first.as_mut().prev = NonNull::dangling(); + // unwrap ok: always `Some` on non-dummy entries Some((*first.as_ptr()).value.as_ref().unwrap()) } } @@ -433,7 +437,7 @@ mod spin_mutex { } #[inline(always)] - pub fn lock(&self) -> SpinMutexGuard<T> { + pub fn lock(&self) -> SpinMutexGuard<'_, T> { loop { match self.try_lock() { None => while self.lock.load(Ordering::Relaxed) { @@ -445,7 +449,7 @@ mod spin_mutex { } #[inline(always)] - pub fn try_lock(&self) -> Option<SpinMutexGuard<T>> { + pub fn try_lock(&self) -> Option<SpinMutexGuard<'_, T>> { if !self.lock.compare_and_swap(false, true, Ordering::Acquire) { Some(SpinMutexGuard { mutex: self, @@ -498,6 +502,7 @@ mod spin_mutex { use super::*; use crate::sync::Arc; use crate::thread; + use crate::time::{SystemTime, Duration}; #[test] fn sleep() { @@ -507,7 +512,13 @@ mod spin_mutex { let t1 = thread::spawn(move || { *mutex2.lock() = 1; }); - thread::sleep_ms(50); + + // "sleep" for 50ms + // FIXME: https://github.com/fortanix/rust-sgx/issues/31 + let start = SystemTime::now(); + let max = Duration::from_millis(50); + while start.elapsed().unwrap() < max {} + assert_eq!(*guard, 0); drop(guard); t1.join().unwrap(); @@ -530,7 +541,8 @@ mod tests { let locked = wq.lock(); let t1 = thread::spawn(move || { - assert!(WaitQueue::notify_one(wq2.lock()).is_none()) + // if we obtain the lock, the main thread should be waiting + assert!(WaitQueue::notify_one(wq2.lock()).is_ok()); }); WaitQueue::wait(locked); diff --git a/src/libstd/sys/unix/alloc.rs b/src/libstd/sys/unix/alloc.rs index 8e8f5017da7..f47dc92d2de 100644 --- a/src/libstd/sys/unix/alloc.rs +++ b/src/libstd/sys/unix/alloc.rs @@ -6,6 +6,10 @@ use crate::alloc::{GlobalAlloc, Layout, System}; unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // jemalloc provides alignment less than MIN_ALIGN for small allocations. + // So only rely on MIN_ALIGN if size >= align. + // Also see <https://github.com/rust-lang/rust/issues/45955> and + // <https://github.com/rust-lang/rust/issues/62251#issuecomment-507580914>. if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { libc::malloc(layout.size()) as *mut u8 } else { @@ -21,10 +25,11 @@ unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // See the comment above in `alloc` for why this check looks the way it does. if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { libc::calloc(layout.size(), 1) as *mut u8 } else { - let ptr = self.alloc(layout.clone()); + let ptr = self.alloc(layout); if !ptr.is_null() { ptr::write_bytes(ptr, 0, layout.size()); } @@ -80,7 +85,10 @@ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); - let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); + // posix_memalign requires that the alignment be a multiple of `sizeof(void*)`. + // Since these are all powers of 2, we can just use max. + let align = layout.align().max(crate::mem::size_of::<usize>()); + let ret = libc::posix_memalign(&mut out, align, layout.size()); if ret != 0 { ptr::null_mut() } else { diff --git a/src/libstd/sys/unix/args.rs b/src/libstd/sys/unix/args.rs index 18de1096df2..288e9b5c126 100644 --- a/src/libstd/sys/unix/args.rs +++ b/src/libstd/sys/unix/args.rs @@ -49,7 +49,6 @@ impl DoubleEndedIterator for Args { target_os = "android", target_os = "freebsd", target_os = "dragonfly", - target_os = "bitrig", target_os = "netbsd", target_os = "openbsd", target_os = "solaris", @@ -57,7 +56,8 @@ impl DoubleEndedIterator for Args { target_os = "haiku", target_os = "l4re", target_os = "fuchsia", - target_os = "hermit"))] + target_os = "hermit", + target_os = "redox"))] mod imp { use crate::os::unix::prelude::*; use crate::ptr; diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs index 47fb6792f08..0a93fbf8ea7 100644 --- a/src/libstd/sys/unix/condvar.rs +++ b/src/libstd/sys/unix/condvar.rs @@ -31,24 +31,26 @@ impl Condvar { target_os = "ios", target_os = "l4re", target_os = "android", - target_os = "hermit"))] + target_os = "hermit", + target_os = "redox"))] pub unsafe fn init(&mut self) {} #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "l4re", target_os = "android", - target_os = "hermit")))] + target_os = "hermit", + target_os = "redox")))] pub unsafe fn init(&mut self) { - use crate::mem; - let mut attr: libc::pthread_condattr_t = mem::uninitialized(); - let r = libc::pthread_condattr_init(&mut attr); + use crate::mem::MaybeUninit; + let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit(); + let r = libc::pthread_condattr_init(attr.as_mut_ptr()); assert_eq!(r, 0); - let r = libc::pthread_condattr_setclock(&mut attr, libc::CLOCK_MONOTONIC); + let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); assert_eq!(r, 0); - let r = libc::pthread_cond_init(self.inner.get(), &attr); + let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr()); assert_eq!(r, 0); - let r = libc::pthread_condattr_destroy(&mut attr); + let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); assert_eq!(r, 0); } diff --git a/src/libstd/sys/unix/env.rs b/src/libstd/sys/unix/env.rs index f9592d5c45f..d724eeb8b3f 100644 --- a/src/libstd/sys/unix/env.rs +++ b/src/libstd/sys/unix/env.rs @@ -53,17 +53,6 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } -#[cfg(target_os = "bitrig")] -pub mod os { - pub const FAMILY: &str = "unix"; - pub const OS: &str = "bitrig"; - pub const DLL_PREFIX: &str = "lib"; - pub const DLL_SUFFIX: &str = ".so"; - pub const DLL_EXTENSION: &str = "so"; - pub const EXE_SUFFIX: &str = ""; - pub const EXE_EXTENSION: &str = ""; -} - #[cfg(target_os = "netbsd")] pub mod os { pub const FAMILY: &str = "unix"; @@ -173,3 +162,14 @@ pub mod os { pub const EXE_SUFFIX: &str = ""; pub const EXE_EXTENSION: &str = ""; } + +#[cfg(target_os = "redox")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "redox"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} diff --git a/src/libstd/sys/unix/ext/ffi.rs b/src/libstd/sys/unix/ext/ffi.rs index 0c023239815..76b34a6b5d8 100644 --- a/src/libstd/sys/unix/ext/ffi.rs +++ b/src/libstd/sys/unix/ext/ffi.rs @@ -1,109 +1,38 @@ //! Unix-specific extension to the primitives in the `std::ffi` module +//! +//! # Examples +//! +//! ``` +//! use std::ffi::OsString; +//! use std::os::unix::ffi::OsStringExt; +//! +//! let bytes = b"foo".to_vec(); +//! +//! // OsStringExt::from_vec +//! let os_string = OsString::from_vec(bytes); +//! assert_eq!(os_string.to_str(), Some("foo")); +//! +//! // OsStringExt::into_vec +//! let bytes = os_string.into_vec(); +//! assert_eq!(bytes, b"foo"); +//! ``` +//! +//! ``` +//! use std::ffi::OsStr; +//! use std::os::unix::ffi::OsStrExt; +//! +//! let bytes = b"foo"; +//! +//! // OsStrExt::from_bytes +//! let os_str = OsStr::from_bytes(bytes); +//! assert_eq!(os_str.to_str(), Some("foo")); +//! +//! // OsStrExt::as_bytes +//! let bytes = os_str.as_bytes(); +//! assert_eq!(bytes, b"foo"); +//! ``` #![stable(feature = "rust1", since = "1.0.0")] -use crate::ffi::{OsStr, OsString}; -use crate::mem; -use crate::sys::os_str::Buf; -use crate::sys_common::{FromInner, IntoInner, AsInner}; - -/// Unix-specific extensions to [`OsString`]. -/// -/// [`OsString`]: ../../../../std/ffi/struct.OsString.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait OsStringExt { - /// Creates an [`OsString`] from a byte vector. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// use std::os::unix::ffi::OsStringExt; - /// - /// let bytes = b"foo".to_vec(); - /// let os_string = OsString::from_vec(bytes); - /// assert_eq!(os_string.to_str(), Some("foo")); - /// ``` - /// - /// [`OsString`]: ../../../ffi/struct.OsString.html - #[stable(feature = "rust1", since = "1.0.0")] - fn from_vec(vec: Vec<u8>) -> Self; - - /// Yields the underlying byte vector of this [`OsString`]. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsString; - /// use std::os::unix::ffi::OsStringExt; - /// - /// let mut os_string = OsString::new(); - /// os_string.push("foo"); - /// let bytes = os_string.into_vec(); - /// assert_eq!(bytes, b"foo"); - /// ``` - /// - /// [`OsString`]: ../../../ffi/struct.OsString.html - #[stable(feature = "rust1", since = "1.0.0")] - fn into_vec(self) -> Vec<u8>; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl OsStringExt for OsString { - fn from_vec(vec: Vec<u8>) -> OsString { - FromInner::from_inner(Buf { inner: vec }) - } - fn into_vec(self) -> Vec<u8> { - self.into_inner().inner - } -} - -/// Unix-specific extensions to [`OsStr`]. -/// -/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html -#[stable(feature = "rust1", since = "1.0.0")] -pub trait OsStrExt { - #[stable(feature = "rust1", since = "1.0.0")] - /// Creates an [`OsStr`] from a byte slice. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// use std::os::unix::ffi::OsStrExt; - /// - /// let bytes = b"foo"; - /// let os_str = OsStr::from_bytes(bytes); - /// assert_eq!(os_str.to_str(), Some("foo")); - /// ``` - /// - /// [`OsStr`]: ../../../ffi/struct.OsStr.html - fn from_bytes(slice: &[u8]) -> &Self; - - /// Gets the underlying byte view of the [`OsStr`] slice. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::OsStr; - /// use std::os::unix::ffi::OsStrExt; - /// - /// let mut os_str = OsStr::new("foo"); - /// let bytes = os_str.as_bytes(); - /// assert_eq!(bytes, b"foo"); - /// ``` - /// - /// [`OsStr`]: ../../../ffi/struct.OsStr.html - #[stable(feature = "rust1", since = "1.0.0")] - fn as_bytes(&self) -> &[u8]; -} - #[stable(feature = "rust1", since = "1.0.0")] -impl OsStrExt for OsStr { - fn from_bytes(slice: &[u8]) -> &OsStr { - unsafe { mem::transmute(slice) } - } - fn as_bytes(&self) -> &[u8] { - &self.as_inner().inner - } -} +pub use crate::sys_common::os_str_bytes::*; diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index d9baac993c4..c033c60cbe9 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -238,7 +238,7 @@ pub trait PermissionsExt { /// let metadata = f.metadata()?; /// let permissions = metadata.permissions(); /// - /// println!("permissions: {}", permissions.mode()); + /// println!("permissions: {:o}", permissions.mode()); /// Ok(()) } /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] diff --git a/src/libstd/sys/unix/ext/io.rs b/src/libstd/sys/unix/ext/io.rs index 1a0b3b8962b..6bcc59495e3 100644 --- a/src/libstd/sys/unix/ext/io.rs +++ b/src/libstd/sys/unix/ext/io.rs @@ -95,3 +95,18 @@ impl AsRawFd for io::Stdout { impl AsRawFd for io::Stderr { fn as_raw_fd(&self) -> RawFd { libc::STDERR_FILENO } } + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StdinLock<'a> { + fn as_raw_fd(&self) -> RawFd { libc::STDIN_FILENO } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StdoutLock<'a> { + fn as_raw_fd(&self) -> RawFd { libc::STDOUT_FILENO } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StderrLock<'a> { + fn as_raw_fd(&self) -> RawFd { libc::STDERR_FILENO } +} diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 4fc79efe7ce..42edd5dbbea 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -18,7 +18,7 @@ mod libc { use crate::ascii; use crate::ffi::OsStr; use crate::fmt; -use crate::io::{self, Initializer, IoVec, IoVecMut}; +use crate::io::{self, Initializer, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{self, Shutdown}; use crate::os::unix::ffi::OsStrExt; @@ -32,18 +32,17 @@ use crate::sys_common::{self, 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"))] + target_os = "haiku"))] 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")))] + target_os = "haiku")))] const MSG_NOSIGNAL: libc::c_int = 0x0; -fn sun_path_offset() -> usize { +fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { // Work with an actual instance of the type since using a null pointer is UB - let addr: libc::sockaddr_un = unsafe { mem::uninitialized() }; - let base = &addr as *const _ as usize; + let base = addr as *const _ as usize; let path = &addr.sun_path as *const _ as usize; path - base } @@ -69,7 +68,7 @@ unsafe fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::sockl // null byte for pathname addresses is already there because we zeroed the // struct - let mut len = sun_path_offset() + bytes.len(); + let mut len = sun_path_offset(&addr) + bytes.len(); match bytes.get(0) { Some(&0) | None => {} Some(_) => len += 1, @@ -122,7 +121,7 @@ impl SocketAddr { if len == 0 { // When there is a datagram from unnamed unix socket // linux returns zero bytes of address - len = sun_path_offset() as libc::socklen_t; // i.e., zero-length address + len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { return Err(io::Error::new(io::ErrorKind::InvalidInput, "file descriptor did not correspond to a Unix socket")); @@ -199,8 +198,8 @@ impl SocketAddr { } } - fn address<'a>(&'a self) -> AddressKind<'a> { - let len = self.len as usize - sun_path_offset(); + fn address(&self) -> AddressKind<'_> { + let len = self.len as usize - sun_path_offset(&self.addr); let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses @@ -219,7 +218,7 @@ impl SocketAddr { #[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for SocketAddr { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.address() { AddressKind::Unnamed => write!(fmt, "(unnamed)"), AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)), @@ -231,7 +230,7 @@ impl fmt::Debug for SocketAddr { struct AsciiEscaped<'a>(&'a [u8]); impl<'a> fmt::Display for AsciiEscaped<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "\"")?; for byte in self.0.iter().cloned().flat_map(ascii::escape_default) { write!(fmt, "{}", byte as char)?; @@ -259,7 +258,7 @@ pub struct UnixStream(Socket); #[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for UnixStream { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut builder = fmt.debug_struct("UnixStream"); builder.field("fd", self.0.as_inner()); if let Ok(addr) = self.local_addr() { @@ -551,7 +550,7 @@ impl io::Read for UnixStream { io::Read::read(&mut &*self, buf) } - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { io::Read::read_vectored(&mut &*self, bufs) } @@ -567,7 +566,7 @@ impl<'a> io::Read for &'a UnixStream { self.0.read(buf) } - fn read_vectored(&mut self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.0.read_vectored(bufs) } @@ -583,7 +582,7 @@ impl io::Write for UnixStream { io::Write::write(&mut &*self, buf) } - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { io::Write::write_vectored(&mut &*self, bufs) } @@ -598,7 +597,7 @@ impl<'a> io::Write for &'a UnixStream { self.0.write(buf) } - fn write_vectored(&mut self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { self.0.write_vectored(bufs) } @@ -719,7 +718,7 @@ pub struct UnixListener(Socket); #[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for UnixListener { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut builder = fmt.debug_struct("UnixListener"); builder.field("fd", self.0.as_inner()); if let Ok(addr) = self.local_addr() { @@ -895,7 +894,7 @@ impl UnixListener { /// } /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] - pub fn incoming<'a>(&'a self) -> Incoming<'a> { + pub fn incoming(&self) -> Incoming<'_> { Incoming { listener: self } } } @@ -998,7 +997,7 @@ pub struct UnixDatagram(Socket); #[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for UnixDatagram { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut builder = fmt.debug_struct("UnixDatagram"); builder.field("fd", self.0.as_inner()); if let Ok(addr) = self.local_addr() { @@ -1531,14 +1530,14 @@ mod test { let (mut s1, mut s2) = or_panic!(UnixStream::pair()); let len = or_panic!(s1.write_vectored( - &[IoVec::new(b"hello"), IoVec::new(b" "), IoVec::new(b"world!")], + &[IoSlice::new(b"hello"), IoSlice::new(b" "), IoSlice::new(b"world!")], )); assert_eq!(len, 12); let mut buf1 = [0; 6]; let mut buf2 = [0; 7]; let len = or_panic!(s2.read_vectored( - &mut [IoVecMut::new(&mut buf1), IoVecMut::new(&mut buf2)], + &mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)], )); assert_eq!(len, 12); assert_eq!(&buf1, b"hello "); diff --git a/src/libstd/sys/unix/fast_thread_local.rs b/src/libstd/sys/unix/fast_thread_local.rs index 17478dce4fe..952ba40ee87 100644 --- a/src/libstd/sys/unix/fast_thread_local.rs +++ b/src/libstd/sys/unix/fast_thread_local.rs @@ -10,7 +10,7 @@ // fallback implementation to use as well. // // Due to rust-lang/rust#18804, make sure this is not generic! -#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "hermit"))] +#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "hermit", target_os = "redox"))] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { use crate::mem; use crate::sys_common::thread_local::register_dtor_fallback; @@ -82,7 +82,3 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { } } } - -pub fn requires_move_before_drop() -> bool { - false -} diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index c274ad26cb1..ac43526b50f 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -1,7 +1,7 @@ #![unstable(reason = "not public", issue = "0", feature = "fd")] use crate::cmp; -use crate::io::{self, Read, Initializer, IoVec, IoVecMut}; +use crate::io::{self, Read, Initializer, IoSlice, IoSliceMut}; use crate::mem; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sys::cvt; @@ -53,7 +53,7 @@ impl FileDesc { Ok(ret as usize) } - pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let ret = cvt(unsafe { libc::readv(self.fd, bufs.as_ptr() as *const libc::iovec, @@ -115,7 +115,7 @@ impl FileDesc { Ok(ret as usize) } - pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let ret = cvt(unsafe { libc::writev(self.fd, bufs.as_ptr() as *const libc::iovec, @@ -175,7 +175,9 @@ impl FileDesc { target_os = "emscripten", target_os = "fuchsia", target_os = "l4re", - target_os = "haiku")))] + target_os = "linux", + target_os = "haiku", + target_os = "redox")))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; @@ -187,7 +189,9 @@ impl FileDesc { target_os = "emscripten", target_os = "fuchsia", target_os = "l4re", - target_os = "haiku"))] + target_os = "linux", + target_os = "haiku", + target_os = "redox"))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 3b80b475a93..3b1eb86b84f 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -2,7 +2,7 @@ use crate::os::unix::prelude::*; use crate::ffi::{CString, CStr, OsString, OsStr}; use crate::fmt; -use crate::io::{self, Error, ErrorKind, SeekFrom}; +use crate::io::{self, Error, ErrorKind, SeekFrom, IoSlice, IoSliceMut}; use crate::mem; use crate::path::{Path, PathBuf}; use crate::ptr; @@ -33,9 +33,12 @@ use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off_t as off64_t, target_os = "emscripten", target_os = "solaris", target_os = "l4re", - target_os = "fuchsia")))] + target_os = "fuchsia", + target_os = "redox")))] use libc::{readdir_r as readdir64_r}; +pub use crate::sys_common::fs::remove_dir_all; + pub struct File(FileDesc); #[derive(Clone)] @@ -67,7 +70,7 @@ pub struct DirEntry { // on Solaris and Fuchsia because a) it uses a zero-length // array to store the name, b) its lifetime between readdir // calls is not guaranteed. - #[cfg(any(target_os = "solaris", target_os = "fuchsia"))] + #[cfg(any(target_os = "solaris", target_os = "fuchsia", target_os = "redox"))] name: Box<[u8]> } @@ -145,8 +148,7 @@ impl FileAttr { })) } - #[cfg(any(target_os = "bitrig", - target_os = "freebsd", + #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "macos", target_os = "ios"))] @@ -157,8 +159,7 @@ impl FileAttr { })) } - #[cfg(not(any(target_os = "bitrig", - target_os = "freebsd", + #[cfg(not(any(target_os = "freebsd", target_os = "openbsd", target_os = "macos", target_os = "ios")))] @@ -206,7 +207,7 @@ impl FromInner<u32> for FilePermissions { } impl fmt::Debug for ReadDir { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + 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.inner.root, f) @@ -216,7 +217,7 @@ impl fmt::Debug for ReadDir { impl Iterator for ReadDir { type Item = io::Result<DirEntry>; - #[cfg(any(target_os = "solaris", target_os = "fuchsia"))] + #[cfg(any(target_os = "solaris", target_os = "fuchsia", target_os = "redox"))] fn next(&mut self) -> Option<io::Result<DirEntry>> { use crate::slice; @@ -253,7 +254,7 @@ impl Iterator for ReadDir { } } - #[cfg(not(any(target_os = "solaris", target_os = "fuchsia")))] + #[cfg(not(any(target_os = "solaris", target_os = "fuchsia", target_os = "redox")))] fn next(&mut self) -> Option<io::Result<DirEntry>> { if self.end_of_stream { return None; @@ -346,14 +347,14 @@ impl DirEntry { target_os = "haiku", target_os = "l4re", target_os = "fuchsia", - target_os = "hermit"))] + target_os = "hermit", + target_os = "redox"))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 } #[cfg(any(target_os = "freebsd", target_os = "openbsd", - target_os = "bitrig", target_os = "netbsd", target_os = "dragonfly"))] pub fn ino(&self) -> u64 { @@ -365,8 +366,7 @@ impl DirEntry { target_os = "netbsd", target_os = "openbsd", target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig"))] + target_os = "dragonfly"))] fn name_bytes(&self) -> &[u8] { use crate::slice; unsafe { @@ -386,7 +386,8 @@ impl DirEntry { } } #[cfg(any(target_os = "solaris", - target_os = "fuchsia"))] + target_os = "fuchsia", + target_os = "redox"))] fn name_bytes(&self) -> &[u8] { &*self.name } @@ -524,8 +525,15 @@ impl File { } pub fn fsync(&self) -> io::Result<()> { - cvt_r(|| unsafe { libc::fsync(self.0.raw()) })?; - Ok(()) + cvt_r(|| unsafe { os_fsync(self.0.raw()) })?; + return Ok(()); + + #[cfg(any(target_os = "macos", target_os = "ios"))] + unsafe fn os_fsync(fd: c_int) -> c_int { + libc::fcntl(fd, libc::F_FULLFSYNC) + } + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + unsafe fn os_fsync(fd: c_int) -> c_int { libc::fsync(fd) } } pub fn datasync(&self) -> io::Result<()> { @@ -549,15 +557,25 @@ impl File { return crate::sys::android::ftruncate64(self.0.raw(), size); #[cfg(not(target_os = "android"))] - return cvt_r(|| unsafe { - ftruncate64(self.0.raw(), size as off64_t) - }).map(|_| ()); + { + use crate::convert::TryInto; + let size: off64_t = size + .try_into() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; + cvt_r(|| unsafe { + ftruncate64(self.0.raw(), size) + }).map(|_| ()) + } } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { self.0.read_at(buf, offset) } @@ -566,6 +584,10 @@ impl File { self.0.write(buf) } + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { self.0.write_at(buf, offset) } @@ -627,7 +649,7 @@ impl FromInner<c_int> for File { } impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(target_os = "linux")] fn get_path(fd: c_int) -> Option<PathBuf> { let mut p = PathBuf::from("/proc/self/fd"); @@ -734,27 +756,6 @@ pub fn rmdir(p: &Path) -> io::Result<()> { Ok(()) } -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let filetype = lstat(path)?.file_type(); - if filetype.is_symlink() { - unlink(path) - } else { - remove_dir_all_recursive(path) - } -} - -fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { - for child in readdir(path)? { - let child = child?; - if child.file_type()?.is_dir() { - remove_dir_all_recursive(&child.path())?; - } else { - unlink(&child.path())?; - } - } - rmdir(path) -} - pub fn readlink(p: &Path) -> io::Result<PathBuf> { let c_path = cstr(p)?; let p = c_path.as_ptr(); @@ -827,27 +828,59 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { Ok(PathBuf::from(OsString::from_vec(buf))) } -#[cfg(not(any(target_os = "linux", target_os = "android")))] -pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { +fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::File; - if !from.is_file() { - return Err(Error::new(ErrorKind::InvalidInput, - "the source path is not an existing regular file")) + + let reader = File::open(from)?; + let metadata = reader.metadata()?; + if !metadata.is_file() { + return Err(Error::new( + ErrorKind::InvalidInput, + "the source path is not an existing regular file", + )); } + Ok((reader, metadata)) +} + +fn open_to_and_set_permissions( + to: &Path, + reader_metadata: crate::fs::Metadata, +) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { + use crate::fs::OpenOptions; + use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt}; + + let perm = reader_metadata.permissions(); + let writer = OpenOptions::new() + // create the file with the correct mode right away + .mode(perm.mode()) + .write(true) + .create(true) + .truncate(true) + .open(to)?; + let writer_metadata = writer.metadata()?; + if writer_metadata.is_file() { + // Set the correct file permissions, in case the file already existed. + // Don't set the permissions on already existing non-files like + // pipes/FIFOs or device nodes. + writer.set_permissions(perm)?; + } + Ok((writer, writer_metadata)) +} - let mut reader = File::open(from)?; - let mut writer = File::create(to)?; - let perm = reader.metadata()?.permissions(); +#[cfg(not(any(target_os = "linux", + target_os = "android", + target_os = "macos", + target_os = "ios")))] +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + let (mut reader, reader_metadata) = open_from(from)?; + let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; - let ret = io::copy(&mut reader, &mut writer)?; - writer.set_permissions(perm)?; - Ok(ret) + io::copy(&mut reader, &mut writer) } #[cfg(any(target_os = "linux", target_os = "android"))] pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { use crate::cmp; - use crate::fs::File; use crate::sync::atomic::{AtomicBool, Ordering}; // Kernel prior to 4.5 don't have copy_file_range @@ -873,17 +906,9 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { ) } - if !from.is_file() { - return Err(Error::new(ErrorKind::InvalidInput, - "the source path is not an existing regular file")) - } - - let mut reader = File::open(from)?; - let mut writer = File::create(to)?; - let (perm, len) = { - let metadata = reader.metadata()?; - (metadata.permissions(), metadata.size()) - }; + let (mut reader, reader_metadata) = open_from(from)?; + let len = reader_metadata.len(); + let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed); let mut written = 0u64; @@ -893,13 +918,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { let copy_result = unsafe { // We actually don't have to adjust the offsets, // because copy_file_range adjusts the file offset automatically - cvt(copy_file_range(reader.as_raw_fd(), - ptr::null_mut(), - writer.as_raw_fd(), - ptr::null_mut(), - bytes_to_copy, - 0) - ) + cvt(copy_file_range( + reader.as_raw_fd(), + ptr::null_mut(), + writer.as_raw_fd(), + ptr::null_mut(), + bytes_to_copy, + 0, + )) }; if let Err(ref copy_err) = copy_result { match copy_err.raw_os_error() { @@ -917,23 +943,152 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { Ok(ret) => written += ret as u64, Err(err) => { match err.raw_os_error() { - Some(os_err) if os_err == libc::ENOSYS - || os_err == libc::EXDEV - || os_err == libc::EPERM => { - // Try fallback io::copy if either: - // - Kernel version is < 4.5 (ENOSYS) - // - Files are mounted on different fs (EXDEV) - // - copy_file_range is disallowed, for example by seccomp (EPERM) - assert_eq!(written, 0); - let ret = io::copy(&mut reader, &mut writer)?; - writer.set_permissions(perm)?; - return Ok(ret) - }, + Some(os_err) + if os_err == libc::ENOSYS + || os_err == libc::EXDEV + || os_err == libc::EINVAL + || os_err == libc::EPERM => + { + // Try fallback io::copy if either: + // - Kernel version is < 4.5 (ENOSYS) + // - Files are mounted on different fs (EXDEV) + // - copy_file_range is disallowed, for example by seccomp (EPERM) + // - copy_file_range cannot be used with pipes or device nodes (EINVAL) + assert_eq!(written, 0); + return io::copy(&mut reader, &mut writer); + } _ => return Err(err), } } } } - writer.set_permissions(perm)?; Ok(written) } + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + use crate::sync::atomic::{AtomicBool, Ordering}; + + const COPYFILE_ACL: u32 = 1 << 0; + const COPYFILE_STAT: u32 = 1 << 1; + const COPYFILE_XATTR: u32 = 1 << 2; + const COPYFILE_DATA: u32 = 1 << 3; + + const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL; + const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR; + const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA; + + const COPYFILE_STATE_COPIED: u32 = 8; + + #[allow(non_camel_case_types)] + type copyfile_state_t = *mut libc::c_void; + #[allow(non_camel_case_types)] + type copyfile_flags_t = u32; + + extern "C" { + fn fcopyfile( + from: libc::c_int, + to: libc::c_int, + state: copyfile_state_t, + flags: copyfile_flags_t, + ) -> libc::c_int; + fn copyfile_state_alloc() -> copyfile_state_t; + fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int; + fn copyfile_state_get( + state: copyfile_state_t, + flag: u32, + dst: *mut libc::c_void, + ) -> libc::c_int; + } + + struct FreeOnDrop(copyfile_state_t); + impl Drop for FreeOnDrop { + fn drop(&mut self) { + // The code below ensures that `FreeOnDrop` is never a null pointer + unsafe { + // `copyfile_state_free` returns -1 if the `to` or `from` files + // cannot be closed. However, this is not considerd this an + // error. + copyfile_state_free(self.0); + } + } + } + + // MacOS prior to 10.12 don't support `fclonefileat` + // We store the availability in a global to avoid unnecessary syscalls + static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true); + syscall! { + fn fclonefileat( + srcfd: libc::c_int, + dst_dirfd: libc::c_int, + dst: *const libc::c_char, + flags: libc::c_int + ) -> libc::c_int + } + + let (reader, reader_metadata) = open_from(from)?; + + // Opportunistically attempt to create a copy-on-write clone of `from` + // using `fclonefileat`. + if HAS_FCLONEFILEAT.load(Ordering::Relaxed) { + let to = cstr(to)?; + let clonefile_result = cvt(unsafe { + fclonefileat( + reader.as_raw_fd(), + libc::AT_FDCWD, + to.as_ptr(), + 0, + ) + }); + match clonefile_result { + Ok(_) => return Ok(reader_metadata.len()), + Err(err) => match err.raw_os_error() { + // `fclonefileat` will fail on non-APFS volumes, if the + // destination already exists, or if the source and destination + // are on different devices. In all these cases `fcopyfile` + // should succeed. + Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (), + Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed), + _ => return Err(err), + } + } + } + + // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. + let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?; + + // We ensure that `FreeOnDrop` never contains a null pointer so it is + // always safe to call `copyfile_state_free` + let state = unsafe { + let state = copyfile_state_alloc(); + if state.is_null() { + return Err(crate::io::Error::last_os_error()); + } + FreeOnDrop(state) + }; + + let flags = if writer_metadata.is_file() { + COPYFILE_ALL + } else { + COPYFILE_DATA + }; + + cvt(unsafe { + fcopyfile( + reader.as_raw_fd(), + writer.as_raw_fd(), + state.0, + flags, + ) + })?; + + let mut bytes_copied: libc::off_t = 0; + cvt(unsafe { + copyfile_state_get( + state.0, + COPYFILE_STATE_COPIED, + &mut bytes_copied as *mut libc::off_t as *mut libc::c_void, + ) + })?; + Ok(bytes_copied as u64) +} diff --git a/src/libstd/sys/unix/io.rs b/src/libstd/sys/unix/io.rs index eb3fa470a65..a3a72919176 100644 --- a/src/libstd/sys/unix/io.rs +++ b/src/libstd/sys/unix/io.rs @@ -4,15 +4,15 @@ use crate::slice; use libc::{iovec, c_void}; #[repr(transparent)] -pub struct IoVec<'a> { +pub struct IoSlice<'a> { vec: iovec, _p: PhantomData<&'a [u8]>, } -impl<'a> IoVec<'a> { +impl<'a> IoSlice<'a> { #[inline] - pub fn new(buf: &'a [u8]) -> IoVec<'a> { - IoVec { + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() @@ -22,6 +22,18 @@ impl<'a> IoVec<'a> { } #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) @@ -29,15 +41,16 @@ impl<'a> IoVec<'a> { } } -pub struct IoVecMut<'a> { +#[repr(transparent)] +pub struct IoSliceMut<'a> { vec: iovec, _p: PhantomData<&'a mut [u8]>, } -impl<'a> IoVecMut<'a> { +impl<'a> IoSliceMut<'a> { #[inline] - pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> { - IoVecMut { + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() @@ -47,6 +60,18 @@ impl<'a> IoVecMut<'a> { } #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSliceMut beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) diff --git a/src/libstd/sys/unix/l4re.rs b/src/libstd/sys/unix/l4re.rs index b9e725371a3..2c6f21aa21a 100644 --- a/src/libstd/sys/unix/l4re.rs +++ b/src/libstd/sys/unix/l4re.rs @@ -5,7 +5,7 @@ macro_rules! unimpl { pub mod net { #![allow(warnings)] use crate::fmt; - use crate::io::{self, IoVec, IoVecMut}; + use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::sys::fd::FileDesc; @@ -46,7 +46,7 @@ pub mod net { unimpl!(); } - pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> { + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { unimpl!(); } @@ -66,7 +66,7 @@ pub mod net { unimpl!(); } - pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> { + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { unimpl!(); } @@ -152,7 +152,7 @@ pub mod net { unimpl!(); } - pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> { + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { unimpl!(); } @@ -160,7 +160,7 @@ pub mod net { unimpl!(); } - pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> { + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { unimpl!(); } @@ -212,7 +212,7 @@ pub mod net { } impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "No networking support available on L4Re") } } @@ -274,7 +274,7 @@ pub mod net { } impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "No networking support available on L4Re.") } } @@ -292,6 +292,10 @@ pub mod net { pub fn into_socket(self) -> Socket { self.inner } + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + unimpl!(); + } + pub fn socket_addr(&self) -> io::Result<SocketAddr> { unimpl!(); } @@ -420,7 +424,7 @@ pub mod net { } impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "No networking support on L4Re available.") } } @@ -463,4 +467,3 @@ pub mod net { } } } - diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index fbe3444311e..b1f7aac8b4b 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -5,7 +5,6 @@ use crate::io::ErrorKind; #[cfg(any(rustdoc, target_os = "linux"))] pub use crate::os::linux as platform; #[cfg(all(not(rustdoc), target_os = "android"))] pub use crate::os::android as platform; -#[cfg(all(not(rustdoc), target_os = "bitrig"))] pub use crate::os::bitrig as platform; #[cfg(all(not(rustdoc), target_os = "dragonfly"))] pub use crate::os::dragonfly as platform; #[cfg(all(not(rustdoc), target_os = "freebsd"))] pub use crate::os::freebsd as platform; #[cfg(all(not(rustdoc), target_os = "haiku"))] pub use crate::os::haiku as platform; @@ -18,6 +17,7 @@ use crate::io::ErrorKind; #[cfg(all(not(rustdoc), target_os = "fuchsia"))] pub use crate::os::fuchsia as platform; #[cfg(all(not(rustdoc), target_os = "l4re"))] pub use crate::os::linux as platform; #[cfg(all(not(rustdoc), target_os = "hermit"))] pub use crate::os::hermit as platform; +#[cfg(all(not(rustdoc), target_os = "redox"))] pub use crate::os::redox as platform; pub use self::rand::hashmap_random_keys; pub use libc::strlen; @@ -28,8 +28,6 @@ pub mod weak; pub mod alloc; pub mod args; pub mod android; -#[cfg(feature = "backtrace")] -pub mod backtrace; pub mod cmath; pub mod condvar; pub mod env; @@ -47,7 +45,6 @@ mod l4re; #[cfg(target_os = "l4re")] pub use self::l4re::net; pub mod os; -pub mod os_str; pub mod path; pub mod pipe; pub mod process; @@ -59,6 +56,8 @@ pub mod thread_local; pub mod time; pub mod stdio; +pub use crate::sys_common::os_str_bytes as os_str; + #[cfg(not(test))] pub fn init() { // By default, some platforms will send a *signal* when an EPIPE error diff --git a/src/libstd/sys/unix/mutex.rs b/src/libstd/sys/unix/mutex.rs index b6a22e1962a..b43af8fdcaa 100644 --- a/src/libstd/sys/unix/mutex.rs +++ b/src/libstd/sys/unix/mutex.rs @@ -1,5 +1,5 @@ use crate::cell::UnsafeCell; -use crate::mem; +use crate::mem::MaybeUninit; pub struct Mutex { inner: UnsafeCell<libc::pthread_mutex_t> } @@ -40,14 +40,14 @@ impl Mutex { // references, we instead create the mutex with type // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to // re-lock it from the same thread, thus avoiding undefined behavior. - let mut attr: libc::pthread_mutexattr_t = mem::uninitialized(); - let r = libc::pthread_mutexattr_init(&mut attr); + let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); + let r = libc::pthread_mutexattr_init(attr.as_mut_ptr()); debug_assert_eq!(r, 0); - let r = libc::pthread_mutexattr_settype(&mut attr, libc::PTHREAD_MUTEX_NORMAL); + let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL); debug_assert_eq!(r, 0); - let r = libc::pthread_mutex_init(self.inner.get(), &attr); + let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); debug_assert_eq!(r, 0); - let r = libc::pthread_mutexattr_destroy(&mut attr); + let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); debug_assert_eq!(r, 0); } #[inline] @@ -89,19 +89,19 @@ unsafe impl Sync for ReentrantMutex {} impl ReentrantMutex { pub unsafe fn uninitialized() -> ReentrantMutex { - ReentrantMutex { inner: mem::uninitialized() } + ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } } pub unsafe fn init(&mut self) { - let mut attr: libc::pthread_mutexattr_t = mem::uninitialized(); - let result = libc::pthread_mutexattr_init(&mut attr as *mut _); + let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); + let result = libc::pthread_mutexattr_init(attr.as_mut_ptr()); debug_assert_eq!(result, 0); - let result = libc::pthread_mutexattr_settype(&mut attr as *mut _, + let result = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE); debug_assert_eq!(result, 0); - let result = libc::pthread_mutex_init(self.inner.get(), &attr as *const _); + let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); debug_assert_eq!(result, 0); - let result = libc::pthread_mutexattr_destroy(&mut attr as *mut _); + let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); debug_assert_eq!(result, 0); } diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 7712a41ded4..75750b5c4e5 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -1,5 +1,5 @@ use crate::ffi::CStr; -use crate::io::{self, IoVec, IoVecMut}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{SocketAddr, Shutdown}; use crate::str; @@ -244,7 +244,7 @@ impl Socket { self.recv_with_flags(buf, MSG_PEEK) } - pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.0.read_vectored(bufs) } @@ -276,7 +276,7 @@ impl Socket { self.0.write(buf) } - pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { self.0.write_vectored(bufs) } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index e16d50d437b..169bb57ef78 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -25,6 +25,13 @@ use libc::{c_int, c_char, c_void}; const TMPBUF_SZ: usize = 128; +cfg_if::cfg_if! { + if #[cfg(target_os = "redox")] { + const PATH_SEPARATOR: u8 = b';'; + } else { + const PATH_SEPARATOR: u8 = b':'; + } +} extern { #[cfg(not(target_os = "dragonfly"))] @@ -33,11 +40,11 @@ extern { target_os = "fuchsia", target_os = "l4re"), link_name = "__errno_location")] - #[cfg_attr(any(target_os = "bitrig", - target_os = "netbsd", + #[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "android", target_os = "hermit", + target_os = "redox", target_env = "newlib"), link_name = "__errno")] #[cfg_attr(target_os = "solaris", link_name = "___errno")] @@ -152,14 +159,14 @@ pub struct SplitPaths<'a> { fn(&'a [u8]) -> PathBuf>, } -pub fn split_paths(unparsed: &OsStr) -> SplitPaths { +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { fn bytes_to_path(b: &[u8]) -> PathBuf { PathBuf::from(<OsStr as OsStrExt>::from_bytes(b)) } - fn is_colon(b: &u8) -> bool { *b == b':' } + fn is_separator(b: &u8) -> bool { *b == PATH_SEPARATOR } let unparsed = unparsed.as_bytes(); SplitPaths { - iter: unparsed.split(is_colon as fn(&u8) -> bool) + iter: unparsed.split(is_separator as fn(&u8) -> bool) .map(bytes_to_path as fn(&[u8]) -> PathBuf) } } @@ -177,12 +184,11 @@ pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> where I: Iterator<Item=T>, T: AsRef<OsStr> { let mut joined = Vec::new(); - let sep = b':'; for (i, path) in paths.enumerate() { let path = path.as_ref().as_bytes(); - if i > 0 { joined.push(sep) } - if path.contains(&sep) { + if i > 0 { joined.push(PATH_SEPARATOR) } + if path.contains(&PATH_SEPARATOR) { return Err(JoinPathsError) } joined.extend_from_slice(path); @@ -191,8 +197,8 @@ pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> } impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - "path segment contains separator `:`".fmt(f) + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "path segment contains separator `{}`", PATH_SEPARATOR) } } @@ -257,7 +263,7 @@ pub fn current_exe() -> io::Result<PathBuf> { sysctl().or_else(|_| procfs()) } -#[cfg(any(target_os = "bitrig", target_os = "openbsd"))] +#[cfg(target_os = "openbsd")] pub fn current_exe() -> io::Result<PathBuf> { unsafe { let mut mib = [libc::CTL_KERN, @@ -383,6 +389,11 @@ pub fn current_exe() -> io::Result<PathBuf> { } } +#[cfg(target_os = "redox")] +pub fn current_exe() -> io::Result<PathBuf> { + crate::fs::read_to_string("sys:exe").map(PathBuf::from) +} + #[cfg(any(target_os = "fuchsia", target_os = "l4re", target_os = "hermit"))] pub fn current_exe() -> io::Result<PathBuf> { use crate::io::ErrorKind; @@ -512,11 +523,13 @@ pub fn home_dir() -> Option<PathBuf> { #[cfg(any(target_os = "android", target_os = "ios", - target_os = "emscripten"))] + target_os = "emscripten", + target_os = "redox"))] unsafe fn fallback() -> Option<OsString> { None } #[cfg(not(any(target_os = "android", target_os = "ios", - target_os = "emscripten")))] + target_os = "emscripten", + target_os = "redox")))] unsafe fn fallback() -> Option<OsString> { let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { n if n < 0 => 512 as usize, diff --git a/src/libstd/sys/unix/path.rs b/src/libstd/sys/unix/path.rs index 5c062e7c97c..7a183956107 100644 --- a/src/libstd/sys/unix/path.rs +++ b/src/libstd/sys/unix/path.rs @@ -11,7 +11,7 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'/' } -pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { +pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> { None } diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index bc3c026adab..029f4216b7e 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -1,4 +1,4 @@ -use crate::io; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sys::fd::FileDesc; @@ -26,7 +26,8 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { target_os = "freebsd", target_os = "linux", target_os = "netbsd", - target_os = "openbsd")) && + target_os = "openbsd", + target_os = "redox")) && !INVALID.load(Ordering::SeqCst) { @@ -60,10 +61,18 @@ impl AnonPipe { self.0.read(buf) } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + pub fn fd(&self) -> &FileDesc { &self.0 } pub fn into_fd(self) -> FileDesc { self.0 } } diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs index 856d202be03..6bb20bbe087 100644 --- a/src/libstd/sys/unix/process/process_common.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -12,6 +12,14 @@ use crate::collections::BTreeMap; use libc::{c_int, gid_t, uid_t, c_char, EXIT_SUCCESS, EXIT_FAILURE}; +cfg_if::cfg_if! { + if #[cfg(target_os = "redox")] { + const DEV_NULL: &'static str = "null:\0"; + } else { + const DEV_NULL: &'static str = "/dev/null\0"; + } +} + //////////////////////////////////////////////////////////////////////////////// // Command //////////////////////////////////////////////////////////////////////////////// @@ -298,7 +306,7 @@ impl Stdio { opts.read(readable); opts.write(!readable); let path = unsafe { - CStr::from_ptr("/dev/null\0".as_ptr() as *const _) + CStr::from_ptr(DEV_NULL.as_ptr() as *const _) }; let fd = File::open_c(&path, &opts)?; Ok((ChildStdio::Owned(fd.into_fd()), None)) @@ -330,7 +338,7 @@ impl ChildStdio { } impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.program)?; for arg in &self.args { write!(f, " {:?}", arg)?; @@ -380,7 +388,7 @@ impl From<c_int> for ExitStatus { } impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(code) = self.code() { write!(f, "exit code: {}", code) } else { @@ -437,7 +445,7 @@ mod tests { #[cfg(target_os = "android")] unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int { - libc::memset(set as *mut _, 0, mem::size_of::<libc::sigset_t>()); + set.write_bytes(0u8, 1); return 0; } @@ -466,11 +474,11 @@ mod tests { // Test to make sure that a signal mask does not get inherited. let mut cmd = Command::new(OsStr::new("cat")); - let mut set: libc::sigset_t = mem::uninitialized(); - let mut old_set: libc::sigset_t = mem::uninitialized(); - t!(cvt(sigemptyset(&mut set))); - t!(cvt(sigaddset(&mut set, libc::SIGINT))); - t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, &mut old_set))); + let mut set = mem::MaybeUninit::<libc::sigset_t>::uninit(); + let mut old_set = mem::MaybeUninit::<libc::sigset_t>::uninit(); + t!(cvt(sigemptyset(set.as_mut_ptr()))); + t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT))); + t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), old_set.as_mut_ptr()))); cmd.stdin(Stdio::MakePipe); cmd.stdout(Stdio::MakePipe); @@ -479,7 +487,7 @@ mod tests { let stdin_write = pipes.stdin.take().unwrap(); let stdout_read = pipes.stdout.take().unwrap(); - t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &old_set, + t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, old_set.as_ptr(), ptr::null_mut()))); t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT))); diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index 220b1fd4531..327d82e60cf 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -47,7 +47,7 @@ impl Command { match result { 0 => { drop(input); - let err = self.do_exec(theirs, envp.as_ref()); + let Err(err) = self.do_exec(theirs, envp.as_ref()); let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; let bytes = [ (errno >> 24) as u8, @@ -123,7 +123,8 @@ impl Command { // environment lock before we try to exec. let _lock = sys::os::env_lock(); - self.do_exec(theirs, envp.as_ref()) + let Err(e) = self.do_exec(theirs, envp.as_ref()); + e } } Err(e) => e, @@ -164,51 +165,47 @@ impl Command { &mut self, stdio: ChildPipes, maybe_envp: Option<&CStringArray> - ) -> io::Error { + ) -> Result<!, io::Error> { use crate::sys::{self, cvt_r}; - macro_rules! t { - ($e:expr) => (match $e { - Ok(e) => e, - Err(e) => return e, - }) - } - if let Some(fd) = stdio.stdin.fd() { - t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); + cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?; } if let Some(fd) = stdio.stdout.fd() { - t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); + cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?; } if let Some(fd) = stdio.stderr.fd() { - t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); + cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?; } if cfg!(not(any(target_os = "l4re"))) { if let Some(u) = self.get_gid() { - t!(cvt(libc::setgid(u as gid_t))); + cvt(libc::setgid(u as gid_t))?; } if let Some(u) = self.get_uid() { - // When dropping privileges from root, the `setgroups` call - // will remove any extraneous groups. If we don't call this, - // then even though our uid has dropped, we may still have - // groups that enable us to do super-user things. This will - // fail if we aren't root, so don't bother checking the - // return value, this is just done as an optimistic - // privilege dropping function. - let _ = libc::setgroups(0, ptr::null()); - - t!(cvt(libc::setuid(u as uid_t))); + //FIXME: Redox kernel does not support setgroups yet + if cfg!(not(target_os = "redox")) { + // When dropping privileges from root, the `setgroups` call + // will remove any extraneous groups. If we don't call this, + // then even though our uid has dropped, we may still have + // groups that enable us to do super-user things. This will + // fail if we aren't root, so don't bother checking the + // return value, this is just done as an optimistic + // privilege dropping function. + let _ = libc::setgroups(0, ptr::null()); + } + + cvt(libc::setuid(u as uid_t))?; } } if let Some(ref cwd) = *self.get_cwd() { - t!(cvt(libc::chdir(cwd.as_ptr()))); + cvt(libc::chdir(cwd.as_ptr()))?; } // emscripten has no signal support. #[cfg(not(any(target_os = "emscripten")))] { - use crate::mem; + use crate::mem::MaybeUninit; // 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 @@ -216,27 +213,25 @@ impl Command { // UNIX programs do not reset these things on their own, so we // need to clean things up now to avoid confusing the program // we're about to run. - let mut set: libc::sigset_t = mem::uninitialized(); + let mut set = MaybeUninit::<libc::sigset_t>::uninit(); if cfg!(target_os = "android") { // Implementing sigemptyset allow us to support older Android // versions. See the comment about Android and sig* functions in // process_common.rs - libc::memset(&mut set as *mut _ as *mut _, - 0, - mem::size_of::<libc::sigset_t>()); + set.as_mut_ptr().write_bytes(0u8, 1); } else { - t!(cvt(libc::sigemptyset(&mut set))); + cvt(libc::sigemptyset(set.as_mut_ptr()))?; } - t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, - ptr::null_mut()))); + cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), + ptr::null_mut()))?; let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL); if ret == libc::SIG_ERR { - return io::Error::last_os_error() + return Err(io::Error::last_os_error()) } } for callback in self.get_closures().iter_mut() { - t!(callback()); + callback()?; } // Although we're performing an exec here we may also return with an @@ -261,7 +256,7 @@ impl Command { } libc::execvp(self.get_argv()[0], self.get_argv().as_ptr()); - io::Error::last_os_error() + Err(io::Error::last_os_error()) } #[cfg(not(any(target_os = "macos", target_os = "freebsd", @@ -279,13 +274,13 @@ impl Command { fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>) -> io::Result<Option<Process>> { - use crate::mem; + use crate::mem::MaybeUninit; use crate::sys; if self.get_gid().is_some() || self.get_uid().is_some() || self.env_saw_path() || - self.get_closures().len() != 0 { + !self.get_closures().is_empty() { return Ok(None) } @@ -321,63 +316,63 @@ impl Command { let mut p = Process { pid: 0, status: None }; - struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t); + struct PosixSpawnFileActions(MaybeUninit<libc::posix_spawn_file_actions_t>); impl Drop for PosixSpawnFileActions { fn drop(&mut self) { unsafe { - libc::posix_spawn_file_actions_destroy(&mut self.0); + libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr()); } } } - struct PosixSpawnattr(libc::posix_spawnattr_t); + struct PosixSpawnattr(MaybeUninit<libc::posix_spawnattr_t>); impl Drop for PosixSpawnattr { fn drop(&mut self) { unsafe { - libc::posix_spawnattr_destroy(&mut self.0); + libc::posix_spawnattr_destroy(self.0.as_mut_ptr()); } } } unsafe { - let mut file_actions = PosixSpawnFileActions(mem::uninitialized()); - let mut attrs = PosixSpawnattr(mem::uninitialized()); + let mut file_actions = PosixSpawnFileActions(MaybeUninit::uninit()); + let mut attrs = PosixSpawnattr(MaybeUninit::uninit()); - libc::posix_spawnattr_init(&mut attrs.0); - libc::posix_spawn_file_actions_init(&mut file_actions.0); + libc::posix_spawnattr_init(attrs.0.as_mut_ptr()); + libc::posix_spawn_file_actions_init(file_actions.0.as_mut_ptr()); if let Some(fd) = stdio.stdin.fd() { - cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0, + cvt(libc::posix_spawn_file_actions_adddup2(file_actions.0.as_mut_ptr(), fd, libc::STDIN_FILENO))?; } if let Some(fd) = stdio.stdout.fd() { - cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0, + cvt(libc::posix_spawn_file_actions_adddup2(file_actions.0.as_mut_ptr(), fd, libc::STDOUT_FILENO))?; } if let Some(fd) = stdio.stderr.fd() { - cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0, + cvt(libc::posix_spawn_file_actions_adddup2(file_actions.0.as_mut_ptr(), fd, libc::STDERR_FILENO))?; } if let Some((f, cwd)) = addchdir { - cvt(f(&mut file_actions.0, cwd.as_ptr()))?; + cvt(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?; } - let mut set: libc::sigset_t = mem::uninitialized(); - cvt(libc::sigemptyset(&mut set))?; - cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0, - &set))?; - cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?; - cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0, - &set))?; + let mut set = MaybeUninit::<libc::sigset_t>::uninit(); + cvt(libc::sigemptyset(set.as_mut_ptr()))?; + cvt(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), + set.as_ptr()))?; + cvt(libc::sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?; + cvt(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), + set.as_ptr()))?; let flags = libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK; - cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?; + cvt(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource let _env_lock = sys::os::env_lock(); @@ -386,8 +381,8 @@ impl Command { let ret = libc::posix_spawnp( &mut p.pid, self.get_argv()[0], - &file_actions.0, - &attrs.0, + file_actions.0.as_ptr(), + attrs.0.as_ptr(), self.get_argv().as_ptr() as *const _, envp as *const _, ); diff --git a/src/libstd/sys/unix/rand.rs b/src/libstd/sys/unix/rand.rs index 122f22b37a2..c5be1763302 100644 --- a/src/libstd/sys/unix/rand.rs +++ b/src/libstd/sys/unix/rand.rs @@ -15,7 +15,8 @@ pub fn hashmap_random_keys() -> (u64, u64) { not(target_os = "ios"), not(target_os = "openbsd"), not(target_os = "freebsd"), - not(target_os = "fuchsia")))] + not(target_os = "fuchsia"), + not(target_os = "redox")))] mod imp { use crate::fs::File; use crate::io::Read; @@ -47,7 +48,12 @@ mod imp { let err = errno() as libc::c_int; if err == libc::EINTR { continue; - } else if err == libc::ENOSYS { + } else if err == libc::ENOSYS || err == libc::EPERM { + // Fall back to reading /dev/urandom if `getrandom` is not + // supported on the current kernel. + // + // Also fall back in case it is disabled by something like + // seccomp or inside of virtual machines. GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed); return false; } else if err == libc::EAGAIN { @@ -99,6 +105,13 @@ mod imp { } } +// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with +// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded +// from `/dev/random` and which runs on its own thread accessed via GCD. +// This seems needlessly heavyweight for the purposes of generating two u64s +// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is +// only used on iOS where direct access to `/dev/urandom` is blocked by the +// sandbox. #[cfg(target_os = "ios")] mod imp { use crate::io; @@ -162,3 +175,15 @@ mod imp { unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) } } } + +#[cfg(target_os = "redox")] +mod imp { + use crate::fs::File; + use crate::io::Read; + + pub fn fill_bytes(v: &mut [u8]) { + // Open rand:, read from it, and close it again. + let mut file = File::open("rand:").expect("failed to open rand:"); + file.read_exact(v).expect("failed to read rand:") + } +} diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs index 8c60bddc238..fe1095fa0c2 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -25,7 +25,6 @@ impl Drop for Handler { #[cfg(any(target_os = "linux", target_os = "macos", - target_os = "bitrig", target_os = "dragonfly", target_os = "freebsd", target_os = "solaris", @@ -139,7 +138,7 @@ mod imp { #[cfg(any(target_os = "linux", target_os = "macos", - target_os = "bitrig", + target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "solaris"))] @@ -147,8 +146,7 @@ mod imp { libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ } } - #[cfg(any(target_os = "freebsd", - target_os = "dragonfly"))] + #[cfg(target_os = "dragonfly")] unsafe fn get_stack() -> libc::stack_t { libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ } } @@ -185,7 +183,6 @@ mod imp { #[cfg(not(any(target_os = "linux", target_os = "macos", - target_os = "bitrig", target_os = "dragonfly", target_os = "freebsd", target_os = "solaris", diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index 35f163bbdb1..f9b017df240 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -1,5 +1,6 @@ -use crate::io; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::sys::fd::FileDesc; +use crate::mem::ManuallyDrop; pub struct Stdin(()); pub struct Stdout(()); @@ -11,10 +12,11 @@ impl Stdin { impl io::Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - let fd = FileDesc::new(libc::STDIN_FILENO); - let ret = fd.read(buf); - fd.into_raw(); // do not close this FD - ret + ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs) } } @@ -24,10 +26,11 @@ impl Stdout { impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - let fd = FileDesc::new(libc::STDOUT_FILENO); - let ret = fd.write(buf); - fd.into_raw(); // do not close this FD - ret + ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs) } fn flush(&mut self) -> io::Result<()> { @@ -41,10 +44,11 @@ impl Stderr { impl io::Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - let fd = FileDesc::new(libc::STDERR_FILENO); - let ret = fd.write(buf); - fd.into_raw(); // do not close this FD - ret + ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs) } fn flush(&mut self) -> io::Result<()> { diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index feb15e8f585..988881e3596 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -1,4 +1,3 @@ -use crate::boxed::FnBox; use crate::cmp; use crate::ffi::CStr; use crate::io; @@ -39,7 +38,7 @@ unsafe fn pthread_attr_setstacksize(_attr: *mut libc::pthread_attr_t, impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>) + pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { let p = box p; let mut native: libc::pthread_t = mem::zeroed(); @@ -100,7 +99,6 @@ impl Thread { #[cfg(any(target_os = "freebsd", target_os = "dragonfly", - target_os = "bitrig", target_os = "openbsd"))] pub fn set_name(name: &CStr) { unsafe { @@ -124,12 +122,26 @@ impl Thread { name.as_ptr() as *mut libc::c_void); } } + + #[cfg(target_os = "solaris")] + pub fn set_name(name: &CStr) { + weak! { + fn pthread_setname_np( + libc::pthread_t, *const libc::c_char + ) -> libc::c_int + } + + if let Some(f) = pthread_setname_np.get() { + unsafe { f(libc::pthread_self(), name.as_ptr()); } + } + } + #[cfg(any(target_env = "newlib", - target_os = "solaris", target_os = "haiku", target_os = "l4re", target_os = "emscripten", - target_os = "hermit"))] + target_os = "hermit", + target_os = "redox"))] pub fn set_name(_name: &CStr) { // Newlib, Illumos, Haiku, and Emscripten have no way to set a thread name. } @@ -190,7 +202,6 @@ impl Drop for Thread { #[cfg(all(not(all(target_os = "linux", not(target_env = "musl"))), not(target_os = "freebsd"), not(target_os = "macos"), - not(target_os = "bitrig"), not(all(target_os = "netbsd", not(target_vendor = "rumprun"))), not(target_os = "openbsd"), not(target_os = "solaris")))] @@ -206,7 +217,6 @@ pub mod guard { #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "freebsd", target_os = "macos", - target_os = "bitrig", all(target_os = "netbsd", not(target_vendor = "rumprun")), target_os = "openbsd", target_os = "solaris"))] @@ -237,16 +247,15 @@ pub mod guard { Some(stackaddr as *mut libc::c_void) } - #[cfg(any(target_os = "openbsd", target_os = "bitrig"))] + #[cfg(target_os = "openbsd")] unsafe fn get_stack_start() -> Option<*mut libc::c_void> { let mut current_stack: libc::stack_t = crate::mem::zeroed(); assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); - let extra = if cfg!(target_os = "bitrig") {3} else {1} * PAGE_SIZE; let stackaddr = if libc::pthread_main_np() == 1 { // main thread - current_stack.ss_sp as usize - current_stack.ss_size + extra + current_stack.ss_sp as usize - current_stack.ss_size + PAGE_SIZE } else { // new thread current_stack.ss_sp as usize - current_stack.ss_size @@ -344,7 +353,6 @@ pub mod guard { } #[cfg(any(target_os = "macos", - target_os = "bitrig", target_os = "openbsd", target_os = "solaris"))] pub unsafe fn current() -> Option<Guard> { diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index cbb0615911a..02f377d55c9 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -114,7 +114,8 @@ impl Hash for Timespec { #[cfg(any(target_os = "macos", target_os = "ios"))] mod inner { use crate::fmt; - use crate::sync::Once; + use crate::mem; + use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use crate::sys::cvt; use crate::sys_common::mul_div_u64; use crate::time::Duration; @@ -136,9 +137,21 @@ mod inner { t: Timespec::zero(), }; + #[repr(C)] + #[derive(Copy, Clone)] + struct mach_timebase_info { + numer: u32, + denom: u32, + } + type mach_timebase_info_t = *mut mach_timebase_info; + type kern_return_t = libc::c_int; + impl Instant { pub fn now() -> Instant { - Instant { t: unsafe { libc::mach_absolute_time() } } + extern "C" { + fn mach_absolute_time() -> u64; + } + Instant { t: unsafe { mach_absolute_time() } } } pub const fn zero() -> Instant { @@ -149,12 +162,11 @@ mod inner { true } - pub fn sub_instant(&self, other: &Instant) -> Duration { + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + let diff = self.t.checked_sub(other.t)?; let info = info(); - let diff = self.t.checked_sub(other.t) - .expect("second instant is later than self"); let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64); - Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32) + Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32)) } pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { @@ -214,7 +226,7 @@ mod inner { } impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("tv_sec", &self.t.t.tv_sec) .field("tv_nsec", &self.t.t.tv_nsec) @@ -230,18 +242,35 @@ mod inner { Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) } - fn info() -> &'static libc::mach_timebase_info { - static mut INFO: libc::mach_timebase_info = libc::mach_timebase_info { + fn info() -> mach_timebase_info { + static mut INFO: mach_timebase_info = mach_timebase_info { numer: 0, denom: 0, }; - static ONCE: Once = Once::new(); + static STATE: AtomicUsize = AtomicUsize::new(0); unsafe { - ONCE.call_once(|| { - libc::mach_timebase_info(&mut INFO); - }); - &INFO + // If a previous thread has filled in this global state, use that. + if STATE.load(SeqCst) == 2 { + return INFO; + } + + // ... otherwise learn for ourselves ... + let mut info = mem::zeroed(); + extern "C" { + fn mach_timebase_info(info: mach_timebase_info_t) + -> kern_return_t; + } + + mach_timebase_info(&mut info); + + // ... and attempt to be the one thread that stores it globally for + // all other threads + if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { + INFO = info; + STATE.store(2, SeqCst); + } + return info; } } } @@ -285,10 +314,8 @@ mod inner { false // last clause, used so `||` is always trailing above } - pub fn sub_instant(&self, other: &Instant) -> Duration { - self.t.sub_timespec(&other.t).unwrap_or_else(|_| { - panic!("specified instant was later than self") - }) + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + self.t.sub_timespec(&other.t).ok() } pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { @@ -301,7 +328,7 @@ mod inner { } impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") .field("tv_sec", &self.t.t.tv_sec) .field("tv_nsec", &self.t.t.tv_nsec) @@ -335,7 +362,7 @@ mod inner { } impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("tv_sec", &self.t.t.tv_sec) .field("tv_nsec", &self.t.t.tv_nsec) diff --git a/src/libstd/sys/vxworks/alloc.rs b/src/libstd/sys/vxworks/alloc.rs new file mode 100644 index 00000000000..e0c560b9214 --- /dev/null +++ b/src/libstd/sys/vxworks/alloc.rs @@ -0,0 +1,53 @@ +use crate::ptr; +use crate::sys_common::alloc::{MIN_ALIGN, realloc_fallback}; +use crate::alloc::{GlobalAlloc, Layout, System}; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::malloc(layout.size()) as *mut u8 + } else { + aligned_malloc(&layout) + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::calloc(layout.size(), 1) as *mut u8 + } else { + let ptr = self.alloc(layout.clone()); + if !ptr.is_null() { + ptr::write_bytes(ptr, 0, layout.size()); + } + ptr + } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + libc::free(ptr as *mut libc::c_void) + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + } else { + realloc_fallback(self, ptr, layout, new_size) + } + } +} + +#[inline] +unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { + let mut out = ptr::null_mut(); + let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); + if ret != 0 { + ptr::null_mut() + } else { + out as *mut u8 + } +} diff --git a/src/libstd/sys/redox/args.rs b/src/libstd/sys/vxworks/args.rs index f9e2f5ba311..11c3cb78819 100644 --- a/src/libstd/sys/redox/args.rs +++ b/src/libstd/sys/vxworks/args.rs @@ -1,10 +1,4 @@ -//! Global initialization and retrieval 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 crate::ffi::OsString; use crate::marker::PhantomData; use crate::vec; @@ -46,51 +40,46 @@ impl DoubleEndedIterator for Args { } mod imp { - use crate::os::unix::prelude::*; - use crate::mem; + use crate::ptr; use crate::ffi::{CStr, OsString}; use crate::marker::PhantomData; + use libc; use super::Args; use crate::sys_common::mutex::Mutex; - static mut GLOBAL_ARGS_PTR: usize = 0; + static mut ARGC: isize = 0; + static mut ARGV: *const *const u8 = ptr::null(); 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(); - let _guard = LOCK.lock(); - let ptr = get_global_ptr(); - assert!((*ptr).is_none()); - (*ptr) = Some(box args); + ARGC = argc; + ARGV = argv; } pub unsafe fn cleanup() { let _guard = LOCK.lock(); - *get_global_ptr() = None; + ARGC = 0; + ARGV = ptr::null(); } pub fn args() -> Args { - let bytes = clone().unwrap_or_default(); - 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 } + Args { + iter: clone().into_iter(), + _dont_send_or_sync_me: PhantomData + } } - fn clone() -> Option<Vec<Vec<u8>>> { + fn clone() -> Vec<OsString> { unsafe { let _guard = LOCK.lock(); - let ptr = get_global_ptr(); - (*ptr).as_ref().map(|s| (**s).clone()) + let ret = (0..ARGC).map(|i| { + let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char); + use crate::sys::vxworks::ext::ffi::OsStringExt; + OsStringExt::from_vec(cstr.to_bytes().to_vec()) + }).collect(); + return ret } } - - unsafe fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> { - mem::transmute(&GLOBAL_ARGS_PTR) - } - } diff --git a/src/libstd/sys/unix/backtrace/mod.rs b/src/libstd/sys/vxworks/backtrace/mod.rs index 0887e5a4df9..0887e5a4df9 100644 --- a/src/libstd/sys/unix/backtrace/mod.rs +++ b/src/libstd/sys/vxworks/backtrace/mod.rs diff --git a/src/libstd/sys/unix/backtrace/printing/dladdr.rs b/src/libstd/sys/vxworks/backtrace/printing/dladdr.rs index cf3bda640e9..202164dd3c4 100644 --- a/src/libstd/sys/unix/backtrace/printing/dladdr.rs +++ b/src/libstd/sys/vxworks/backtrace/printing/dladdr.rs @@ -30,5 +30,6 @@ struct Dl_info { } extern { + #[ link_name = "_rtld_dladdr" ] fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int; } diff --git a/src/libstd/sys/unix/backtrace/printing/mod.rs b/src/libstd/sys/vxworks/backtrace/printing/mod.rs index d090caede43..d090caede43 100644 --- a/src/libstd/sys/unix/backtrace/printing/mod.rs +++ b/src/libstd/sys/vxworks/backtrace/printing/mod.rs diff --git a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs b/src/libstd/sys/vxworks/backtrace/tracing/backtrace_fn.rs index a628d107ad6..a628d107ad6 100644 --- a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs +++ b/src/libstd/sys/vxworks/backtrace/tracing/backtrace_fn.rs diff --git a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs b/src/libstd/sys/vxworks/backtrace/tracing/gcc_s.rs index abbeca0fde6..e6379132baf 100644 --- a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs +++ b/src/libstd/sys/vxworks/backtrace/tracing/gcc_s.rs @@ -21,7 +21,7 @@ impl Error for UnwindError { } impl fmt::Display for UnwindError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}: {:?}", self.description(), self.0) } } @@ -37,7 +37,7 @@ pub fn unwind_backtrace(frames: &mut [Frame]) }; let result_unwind = unsafe { uw::_Unwind_Backtrace(trace_fn, - &mut cx as *mut Context + &mut cx as *mut Context<'_> as *mut libc::c_void) }; // See libunwind:src/unwind/Backtrace.c for the return values. @@ -57,7 +57,7 @@ pub fn unwind_backtrace(frames: &mut [Frame]) extern fn trace_fn(ctx: *mut uw::_Unwind_Context, arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { - let cx = unsafe { &mut *(arg as *mut Context) }; + let cx = unsafe { &mut *(arg as *mut Context<'_>) }; if cx.idx >= cx.frames.len() { return uw::_URC_NORMAL_STOP; } diff --git a/src/libstd/sys/unix/backtrace/tracing/mod.rs b/src/libstd/sys/vxworks/backtrace/tracing/mod.rs index 11863e64545..11863e64545 100644 --- a/src/libstd/sys/unix/backtrace/tracing/mod.rs +++ b/src/libstd/sys/vxworks/backtrace/tracing/mod.rs diff --git a/src/libstd/sys/redox/cmath.rs b/src/libstd/sys/vxworks/cmath.rs index f6bb58934fc..f6bb58934fc 100644 --- a/src/libstd/sys/redox/cmath.rs +++ b/src/libstd/sys/vxworks/cmath.rs diff --git a/src/libstd/sys/vxworks/condvar.rs b/src/libstd/sys/vxworks/condvar.rs new file mode 100644 index 00000000000..783c3eb7c76 --- /dev/null +++ b/src/libstd/sys/vxworks/condvar.rs @@ -0,0 +1,96 @@ +use crate::cell::UnsafeCell; +use crate::sys::mutex::{self, Mutex}; +use crate::time::Duration; + +pub struct Condvar { inner: UnsafeCell<libc::pthread_cond_t> } + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +const TIMESPEC_MAX: libc::timespec = libc::timespec { + tv_sec: <libc::time_t>::max_value(), + tv_nsec: 1_000_000_000 - 1, +}; + +fn saturating_cast_to_time_t(value: u64) -> libc::time_t { + if value > <libc::time_t>::max_value() as u64 { + <libc::time_t>::max_value() + } else { + value as libc::time_t + } +} + +impl Condvar { + pub const fn new() -> Condvar { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) } + } + + pub unsafe fn init(&mut self) { + use crate::mem::MaybeUninit; + let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit(); + let r = libc::pthread_condattr_init(attr.as_mut_ptr()); + assert_eq!(r, 0); + let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); + assert_eq!(r, 0); + let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr()); + assert_eq!(r, 0); + let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); + assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn notify_one(&self) { + let r = libc::pthread_cond_signal(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn notify_all(&self) { + let r = libc::pthread_cond_broadcast(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = libc::pthread_cond_wait(self.inner.get(), mutex::raw(mutex)); + debug_assert_eq!(r, 0); + } + + // This implementation is used on systems that support pthread_condattr_setclock + // where we configure condition variable to use monotonic clock (instead of + // default system clock). This approach avoids all problems that result + // from changes made to the system time. + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + use crate::mem; + + let mut now: libc::timespec = mem::zeroed(); + let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now); + assert_eq!(r, 0); + + // Nanosecond calculations can't overflow because both values are below 1e9. + let nsec = dur.subsec_nanos() + now.tv_nsec as u32; + + let sec = saturating_cast_to_time_t(dur.as_secs()) + .checked_add((nsec / 1_000_000_000) as libc::time_t) + .and_then(|s| s.checked_add(now.tv_sec)); + let nsec = nsec % 1_000_000_000; + + let timeout = sec.map(|s| { + libc::timespec { tv_sec: s, tv_nsec: nsec as _} + }).unwrap_or(TIMESPEC_MAX); + + let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), + &timeout); + assert!(r == libc::ETIMEDOUT || r == 0); + r == 0 + } + + + #[inline] + pub unsafe fn destroy(&self) { + let r = libc::pthread_cond_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/redox/env.rs b/src/libstd/sys/vxworks/env.rs index bcc0e126b9d..fe1aedd5859 100644 --- a/src/libstd/sys/redox/env.rs +++ b/src/libstd/sys/vxworks/env.rs @@ -1,6 +1,6 @@ pub mod os { - pub const FAMILY: &str = "redox"; - pub const OS: &str = "redox"; + pub const FAMILY: &str = "vxworks"; + pub const OS: &str = "vxworks"; pub const DLL_PREFIX: &str = "lib"; pub const DLL_SUFFIX: &str = ".so"; pub const DLL_EXTENSION: &str = "so"; diff --git a/src/libstd/sys/vxworks/ext/ffi.rs b/src/libstd/sys/vxworks/ext/ffi.rs new file mode 100644 index 00000000000..76b34a6b5d8 --- /dev/null +++ b/src/libstd/sys/vxworks/ext/ffi.rs @@ -0,0 +1,38 @@ +//! Unix-specific extension to the primitives in the `std::ffi` module +//! +//! # Examples +//! +//! ``` +//! use std::ffi::OsString; +//! use std::os::unix::ffi::OsStringExt; +//! +//! let bytes = b"foo".to_vec(); +//! +//! // OsStringExt::from_vec +//! let os_string = OsString::from_vec(bytes); +//! assert_eq!(os_string.to_str(), Some("foo")); +//! +//! // OsStringExt::into_vec +//! let bytes = os_string.into_vec(); +//! assert_eq!(bytes, b"foo"); +//! ``` +//! +//! ``` +//! use std::ffi::OsStr; +//! use std::os::unix::ffi::OsStrExt; +//! +//! let bytes = b"foo"; +//! +//! // OsStrExt::from_bytes +//! let os_str = OsStr::from_bytes(bytes); +//! assert_eq!(os_str.to_str(), Some("foo")); +//! +//! // OsStrExt::as_bytes +//! let bytes = os_str.as_bytes(); +//! assert_eq!(bytes, b"foo"); +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys_common::os_str_bytes::*; diff --git a/src/libstd/sys/vxworks/ext/fs.rs b/src/libstd/sys/vxworks/ext/fs.rs new file mode 100644 index 00000000000..7ab7f2a1a26 --- /dev/null +++ b/src/libstd/sys/vxworks/ext/fs.rs @@ -0,0 +1,801 @@ +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::fs::{self, Permissions}; +use crate::io; +use libc; +use crate::path::Path; +use crate::sys; +use crate::sys_common::{FromInner, AsInner, AsInnerMut}; +use crate::sys::platform::fs::MetadataExt as UnixMetadataExt; + +/// Unix-specific extensions to [`File`]. +/// +/// [`File`]: ../../../../std/fs/struct.File.html +#[stable(feature = "file_offset", since = "1.15.0")] +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. + /// + /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs::File; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let mut buf = [0u8; 8]; + /// let file = File::open("foo.txt")?; + /// + /// // We now read 8 bytes from the offset 10. + /// let num_bytes_read = file.read_at(&mut buf, 10)?; + /// println!("read {} bytes: {:?}", num_bytes_read, buf); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; + + /// Reads the exact number of byte required to fill `buf` from the given offset. + /// + /// 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. + /// + /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`. + /// + /// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact + /// [`read_at`]: #tymethod.read_at + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted + /// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof + /// + /// # Examples + /// + /// ```no_run + /// #![feature(rw_exact_all_at)] + /// use std::io; + /// use std::fs::File; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let mut buf = [0u8; 8]; + /// let file = File::open("foo.txt")?; + /// + /// // We now read exactly 8 bytes from the offset 10. + /// file.read_exact_at(&mut buf, 10)?; + /// println!("read {} bytes: {:?}", buf.len(), buf); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.read_at(buf, offset) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + offset += n as u64; + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(io::Error::new(io::ErrorKind::UnexpectedEof, + "failed to fill whole buffer")) + } else { + Ok(()) + } + } + + /// 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 appropriately + /// 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. + /// + /// [`File::write`]: ../../../../std/fs/struct.File.html#write.v + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let file = File::open("foo.txt")?; + /// + /// // We now write at the offset 10. + /// file.write_at(b"sushi", 10)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_offset", since = "1.15.0")] + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>; + + /// Attempts to write an entire buffer starting from a given offset. + /// + /// 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. + /// + /// This method will continuously call [`write_at`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns. + /// + /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted + /// [`write_at`]: #tymethod.write_at + /// + /// # Examples + /// + /// ```no_run + /// #![feature(rw_exact_all_at)] + /// use std::fs::File; + /// use std::io; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// let file = File::open("foo.txt")?; + /// + /// // We now write at the offset 10. + /// file.write_all_at(b"sushi", 10)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.write_at(buf, offset) { + Ok(0) => return Err(io::Error::new(io::ErrorKind::WriteZero, + "failed to write whole buffer")), + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } +} + +#[stable(feature = "file_offset", since = "1.15.0")] +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 [`fs::Permissions`]. +/// +/// [`fs::Permissions`]: ../../../../std/fs/struct.Permissions.html +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait PermissionsExt { + /// Returns the underlying raw `st_mode` bits that contain the standard + /// Unix permissions for this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// let permissions = metadata.permissions(); + /// + /// println!("permissions: {}", permissions.mode()); + /// Ok(()) } + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn mode(&self) -> u32; + + /// Sets the underlying raw bits for this set of permissions. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; + /// let mut permissions = metadata.permissions(); + /// + /// permissions.set_mode(0o644); // Read/write for owner and read for others. + /// assert_eq!(permissions.mode(), 0o644); + /// Ok(()) } + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn set_mode(&mut self, mode: u32); + + /// Creates a new instance of `Permissions` from the given set of Unix + /// permission bits. + /// + /// # Examples + /// + /// ``` + /// use std::fs::Permissions; + /// use std::os::unix::fs::PermissionsExt; + /// + /// // Read/write for owner and read for others. + /// let permissions = Permissions::from_mode(0o644); + /// assert_eq!(permissions.mode(), 0o644); + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn from_mode(mode: u32) -> Self; +} + +#[stable(feature = "fs_ext", since = "1.1.0")] +impl PermissionsExt for Permissions { + fn mode(&self) -> u32 { + self.as_inner().mode() + } + + fn set_mode(&mut self, mode: u32) { + *self = Permissions::from_inner(FromInner::from_inner(mode)); + } + + fn from_mode(mode: u32) -> Permissions { + Permissions::from_inner(FromInner::from_inner(mode)) + } +} + +/// Unix-specific extensions to [`fs::OpenOptions`]. +/// +/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of a `File::open_opts` call then this + /// specified `mode` will be used as the permission bits for the new file. + /// If no `mode` is set, the default of `0o666` will be used. + /// The operating system masks out bits with the systems `umask`, to produce + /// the final permissions. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// # fn main() { + /// let mut options = OpenOptions::new(); + /// options.mode(0o644); // Give read/write for owner and read for others. + /// let file = options.open("foo.txt"); + /// # } + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn mode(&mut self, mode: u32) -> &mut Self; + + /// Pass custom flags to the `flags` argument of `open`. + /// + /// The bits that define the access mode are masked out with `O_ACCMODE`, to + /// ensure they do not interfere with the access mode set by Rusts options. + /// + /// Custom flags can only set flags, not remove flags set by Rusts options. + /// This options overwrites any previously set custom flags. + /// + /// # Examples + /// + /// ```no_run + /// # #![feature(libc)] + /// extern crate libc; + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// # fn main() { + /// let mut options = OpenOptions::new(); + /// options.write(true); + /// if cfg!(unix) { + /// options.custom_flags(libc::O_NOFOLLOW); + /// } + /// let file = options.open("foo.txt"); + /// # } + /// ``` + #[stable(feature = "open_options_ext", since = "1.10.0")] + fn custom_flags(&mut self, flags: i32) -> &mut Self; +} + +/*#[stable(feature = "fs_ext", since = "1.1.0")] +impl OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: u32) -> &mut OpenOptions { + self.as_inner_mut().mode(mode); self + } + + fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { + self.as_inner_mut().custom_flags(flags); self + } +} +*/ + +/// Unix-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the ID of the device containing the file. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let dev_id = meta.dev(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn dev(&self) -> u64; + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let inode = meta.ino(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ino(&self) -> u64; + /// Returns the rights applied to this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let mode = meta.mode(); + /// let user_has_write_access = mode & 0o200; + /// let user_has_read_write_access = mode & 0o600; + /// let group_has_read_access = mode & 0o040; + /// let others_have_exec_access = mode & 0o001; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mode(&self) -> u32; + /// Returns the number of hard links pointing to this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nb_hard_links = meta.nlink(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn nlink(&self) -> u64; + /// Returns the user ID of the owner of this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let user_id = meta.uid(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn uid(&self) -> u32; + /// Returns the group ID of the owner of this file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let group_id = meta.gid(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn gid(&self) -> u32; + /// Returns the device ID of this file (if it is a special one). + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let device_id = meta.rdev(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn rdev(&self) -> u64; + /// Returns the total size of this file in bytes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let file_size = meta.size(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn size(&self) -> u64; + /// Returns the time of the last access to the file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let last_access_time = meta.atime(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn atime(&self) -> i64; + /// Returns the time of the last access to the file in nanoseconds. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_access_time = meta.atime_nsec(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mtime(&self) -> i64; + /// Returns the time of the last modification of the file in nanoseconds. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_modification_time = meta.mtime_nsec(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ctime(&self) -> i64; + /// Returns the time of the last status change of the file in nanoseconds. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let nano_last_status_change_time = meta.ctime_nsec(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn blksize(&self) -> u64; + /// Returns the number of blocks allocated to the file, in 512-byte units. + /// + /// Please note that this may be smaller than `st_size / 512` when the file has holes. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::MetadataExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let blocks = meta.blocks(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn blocks(&self) -> u64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn attrib(&self) -> u8; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for fs::Metadata { + fn dev(&self) -> u64 { self.st_dev() } + fn ino(&self) -> u64 { self.st_ino() } + fn mode(&self) -> u32 { self.st_mode() } + fn nlink(&self) -> u64 { self.st_nlink() } + fn uid(&self) -> u32 { self.st_uid() } + fn gid(&self) -> u32 { self.st_gid() } + fn rdev(&self) -> u64 { self.st_rdev() } + fn size(&self) -> u64 { self.st_size() } + fn atime(&self) -> i64 { self.st_atime() } + fn mtime(&self) -> i64 { self.st_mtime() } + fn ctime(&self) -> i64 { self.st_ctime() } + fn blksize(&self) -> u64 { self.st_blksize() } + fn blocks(&self) -> u64 { self.st_blocks() } + fn attrib(&self) -> u8 {self.st_attrib() } +} + +/// Unix-specific extensions for [`FileType`]. +/// +/// Adds support for special Unix file types such as block/character devices, +/// pipes, and sockets. +/// +/// [`FileType`]: ../../../../std/fs/struct.FileType.html +#[stable(feature = "file_type_ext", since = "1.5.0")] +pub trait FileTypeExt { + /// Returns whether this file type is a block device. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("block_device_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_block_device()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_block_device(&self) -> bool; + /// Returns whether this file type is a char device. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("char_device_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_char_device()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_char_device(&self) -> bool; + /// Returns whether this file type is a fifo. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("fifo_file")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_fifo()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_fifo(&self) -> bool; + /// Returns whether this file type is a socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::unix::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("unix.socket")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_socket()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_socket(&self) -> bool; +} + +#[stable(feature = "file_type_ext", since = "1.5.0")] +impl FileTypeExt for fs::FileType { + fn is_block_device(&self) -> bool { self.as_inner().is(libc::S_IFBLK) } + fn is_char_device(&self) -> bool { self.as_inner().is(libc::S_IFCHR) } + fn is_fifo(&self) -> bool { self.as_inner().is(libc::S_IFIFO) } + fn is_socket(&self) -> bool { self.as_inner().is(libc::S_IFSOCK) } +} + +/// Unix-specific extension methods for [`fs::DirEntry`]. +/// +/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html +#[stable(feature = "dir_entry_ext", since = "1.1.0")] +pub trait DirEntryExt { + /// Returns the underlying `d_ino` field in the contained `dirent` + /// structure. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::unix::fs::DirEntryExt; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// println!("{:?}: {}", entry.file_name(), entry.ino()); + /// } + /// } + /// } + /// ``` + #[stable(feature = "dir_entry_ext", since = "1.1.0")] + fn ino(&self) -> u64; +} + +#[stable(feature = "dir_entry_ext", since = "1.1.0")] +impl DirEntryExt for fs::DirEntry { + fn ino(&self) -> u64 { self.as_inner().ino() } +} + +/// Creates a new symbolic link on the filesystem. +/// +/// The `dst` path will be a symbolic link pointing to the `src` path. +/// +/// # Note +/// +/// On Windows, you must specify whether a symbolic link points to a file +/// or directory. Use `os::windows::fs::symlink_file` to create a +/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a +/// symbolic link to a directory. Additionally, the process must have +/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a +/// symbolic link. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::unix::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::symlink("a.txt", "b.txt")?; +/// Ok(()) +/// } +/// ``` +#[stable(feature = "symlink", since = "1.1.0")] +pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> +{ + sys::fs::symlink(src.as_ref(), dst.as_ref()) +} + +/// Unix-specific extensions to [`fs::DirBuilder`]. +/// +/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html +#[stable(feature = "dir_builder", since = "1.6.0")] +pub trait DirBuilderExt { + /// Sets the mode to create new directories with. This option defaults to + /// 0o777. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::DirBuilder; + /// use std::os::unix::fs::DirBuilderExt; + /// + /// let mut builder = DirBuilder::new(); + /// builder.mode(0o755); + /// ``` + #[stable(feature = "dir_builder", since = "1.6.0")] + fn mode(&mut self, mode: u32) -> &mut Self; +} + +#[stable(feature = "dir_builder", since = "1.6.0")] +impl DirBuilderExt for fs::DirBuilder { + fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder { + self.as_inner_mut().set_mode(mode); + self + } +} diff --git a/src/libstd/sys/redox/ext/io.rs b/src/libstd/sys/vxworks/ext/io.rs index f431f96c541..6bcc59495e3 100644 --- a/src/libstd/sys/redox/ext/io.rs +++ b/src/libstd/sys/vxworks/ext/io.rs @@ -3,14 +3,14 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::fs; -use crate::net; +use crate::os::raw; use crate::sys; use crate::io; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; /// Raw file descriptors. #[stable(feature = "rust1", since = "1.0.0")] -pub type RawFd = usize; +pub type RawFd = raw::c_int; /// A trait to extract the raw unix file descriptor from an underlying /// object. @@ -33,7 +33,7 @@ pub trait AsRawFd { /// descriptor. #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawFd { - /// Constructs a new instances of `Self` from the given raw file + /// Constructs a new instance of `Self` from the given raw file /// descriptor. /// /// This function **consumes ownership** of the specified file @@ -81,77 +81,32 @@ impl IntoRawFd for fs::File { } } -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for net::TcpStream { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().as_inner().fd().raw() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for net::TcpListener { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().as_inner().fd().raw() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for net::UdpSocket { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().as_inner().fd().raw() - } -} - #[stable(feature = "asraw_stdio", since = "1.21.0")] impl AsRawFd for io::Stdin { - fn as_raw_fd(&self) -> RawFd { 0 } + fn as_raw_fd(&self) -> RawFd { libc::STDIN_FILENO } } #[stable(feature = "asraw_stdio", since = "1.21.0")] impl AsRawFd for io::Stdout { - fn as_raw_fd(&self) -> RawFd { 1 } + fn as_raw_fd(&self) -> RawFd { libc::STDOUT_FILENO } } #[stable(feature = "asraw_stdio", since = "1.21.0")] impl AsRawFd for io::Stderr { - fn as_raw_fd(&self) -> RawFd { 2 } + fn as_raw_fd(&self) -> RawFd { libc::STDERR_FILENO } } -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for net::TcpStream { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { - let file = sys::fs::File::from_inner(fd); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(file)) - } -} -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for net::TcpListener { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { - let file = sys::fs::File::from_inner(fd); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(file)) - } -} -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for net::UdpSocket { - unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { - let file = sys::fs::File::from_inner(fd); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(file)) - } +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StdinLock<'a> { + fn as_raw_fd(&self) -> RawFd { libc::STDIN_FILENO } } -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for net::TcpStream { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_inner().into_fd().into_raw() - } +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StdoutLock<'a> { + fn as_raw_fd(&self) -> RawFd { libc::STDOUT_FILENO } } -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for net::TcpListener { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_inner().into_fd().into_raw() - } -} -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for net::UdpSocket { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_inner().into_fd().into_raw() - } + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StderrLock<'a> { + fn as_raw_fd(&self) -> RawFd { libc::STDERR_FILENO } } diff --git a/src/libstd/sys/vxworks/ext/mod.rs b/src/libstd/sys/vxworks/ext/mod.rs new file mode 100644 index 00000000000..c2ebc38c300 --- /dev/null +++ b/src/libstd/sys/vxworks/ext/mod.rs @@ -0,0 +1,20 @@ +// Uhhh +#![stable(feature = "rust1", since = "1.0.0")] +#![allow(missing_docs)] + +pub mod io; +pub mod ffi; +pub mod fs; +pub mod raw; +pub mod process; +pub mod net; + +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt}; +} diff --git a/src/libstd/sys/vxworks/ext/net.rs b/src/libstd/sys/vxworks/ext/net.rs new file mode 100644 index 00000000000..3f0a7e9e843 --- /dev/null +++ b/src/libstd/sys/vxworks/ext/net.rs @@ -0,0 +1,1825 @@ +#![stable(feature = "unix_socket", since = "1.10.0")] + +//! Unix-specific networking functionality + +#[cfg(unix)] +use libc; + +use crate::ascii; +use crate::ffi::OsStr; +use crate::fmt; +use crate::io::{self, Initializer, IoSlice, IoSliceMut}; +use crate::mem; +use crate::net::{self, Shutdown}; +use crate::os::unix::ffi::OsStrExt; +use crate::os::unix::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd}; +use crate::path::Path; +use crate::time::Duration; +use crate::sys::{self, cvt}; +use crate::sys::net::Socket; +use crate::sys_common::{self, AsInner, FromInner, IntoInner}; + +const MSG_NOSIGNAL: libc::c_int = 0x0; + +fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { + // Work with an actual instance of the type since using a null pointer is UB + let base = addr as *const _ as usize; + let path = &addr.sun_path as *const _ as usize; + path - base +} + +unsafe fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> { + let mut addr: libc::sockaddr_un = mem::zeroed(); + addr.sun_family = libc::AF_UNIX as libc::sa_family_t; + + let bytes = path.as_os_str().as_bytes(); + + if bytes.contains(&0) { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "paths may not contain interior null bytes")); + } + + if bytes.len() >= addr.sun_path.len() { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "path must be shorter than SUN_LEN")); + } + for (dst, src) in addr.sun_path.iter_mut().zip(bytes.iter()) { + *dst = *src as libc::c_char; + } + // null byte for pathname addresses is already there because we zeroed the + // struct + + let mut len = sun_path_offset(&addr) + bytes.len(); + match bytes.get(0) { + Some(&0) | None => {} + Some(_) => len += 1, + } + Ok((addr, len as libc::socklen_t)) +} + +enum AddressKind<'a> { + Unnamed, + Pathname(&'a Path), + Abstract(&'a [u8]), +} + +/// An address associated with a Unix socket. +/// +/// # Examples +/// +/// ``` +/// use std::os::unix::net::UnixListener; +/// +/// let socket = match UnixListener::bind("/tmp/sock") { +/// Ok(sock) => sock, +/// Err(e) => { +/// println!("Couldn't bind: {:?}", e); +/// return +/// } +/// }; +/// let addr = socket.local_addr().expect("Couldn't get local address"); +/// ``` +#[derive(Clone)] +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct SocketAddr { + addr: libc::sockaddr_un, + len: libc::socklen_t, +} + +impl SocketAddr { + fn new<F>(f: F) -> io::Result<SocketAddr> + where F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int + { + unsafe { + let mut addr: libc::sockaddr_un = mem::zeroed(); + let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t; + cvt(f(&mut addr as *mut _ as *mut _, &mut len))?; + SocketAddr::from_parts(addr, len) + } + } + + fn from_parts(addr: libc::sockaddr_un, mut len: libc::socklen_t) -> io::Result<SocketAddr> { + if len == 0 { + // When there is a datagram from unnamed unix socket + // linux returns zero bytes of address + len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address + } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "file descriptor did not correspond to a Unix socket")); + } + + Ok(SocketAddr { + addr, + len, + }) + } + + /// Returns `true` if the address is unnamed. + /// + /// # Examples + /// + /// A named address: + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let socket = UnixListener::bind("/tmp/sock").unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), false); + /// ``` + /// + /// An unnamed address: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound().unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), true); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn is_unnamed(&self) -> bool { + if let AddressKind::Unnamed = self.address() { + true + } else { + false + } + } + + /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// use std::path::Path; + /// + /// let socket = UnixListener::bind("/tmp/sock").unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); + /// ``` + /// + /// Without a pathname: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound().unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), None); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn as_pathname(&self) -> Option<&Path> { + if let AddressKind::Pathname(path) = self.address() { + Some(path) + } else { + None + } + } + + fn address<'a>(&'a self) -> AddressKind<'a> { + let len = self.len as usize - sun_path_offset(&self.addr); + let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; + + if self.addr.sun_path[0] == 0 { + AddressKind::Abstract(&path[1..len]) + } else { + AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) + } + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.address() { + AddressKind::Unnamed => write!(fmt, "(unnamed)"), + AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)), + AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path), + } + } +} + +struct AsciiEscaped<'a>(&'a [u8]); + +impl<'a> fmt::Display for AsciiEscaped<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "\"")?; + for byte in self.0.iter().cloned().flat_map(ascii::escape_default) { + write!(fmt, "{}", byte as char)?; + } + write!(fmt, "\"") + } +} + +/// A Unix stream socket. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::unix::net::UnixStream; +/// use std::io::prelude::*; +/// +/// let mut stream = UnixStream::connect("/path/to/my/socket").unwrap(); +/// stream.write_all(b"hello world").unwrap(); +/// let mut response = String::new(); +/// stream.read_to_string(&mut response).unwrap(); +/// println!("{}", response); +/// ``` +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct UnixStream(Socket); + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl fmt::Debug for UnixStream { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixStream"); + builder.field("fd", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + if let Ok(addr) = self.peer_addr() { + builder.field("peer", &addr); + } + builder.finish() + } +} + +impl UnixStream { + /// Connects to the socket named by `path`. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = match UnixStream::connect("/tmp/sock") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> { + fn inner(path: &Path) -> io::Result<UnixStream> { + unsafe { + let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let (addr, len) = sockaddr_un(path)?; + + cvt(libc::connect(*inner.as_inner(), &addr as *const _ as *const _, len))?; + Ok(UnixStream(inner)) + } + } + inner(path.as_ref()) + } + + /// Creates an unnamed pair of connected sockets. + /// + /// Returns two `UnixStream`s which are connected to each other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let (sock1, sock2) = match UnixStream::pair() { + /// Ok((sock1, sock2)) => (sock1, sock2), + /// Err(e) => { + /// println!("Couldn't create a pair of sockets: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn pair() -> io::Result<(UnixStream, UnixStream)> { + let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_STREAM)?; + Ok((UnixStream(i1), UnixStream(i2))) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixStream` is a reference to the same stream that this + /// object references. Both handles will read and write the same stream of + /// data, and options set on one stream will be propagated to the other + /// stream. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn try_clone(&self) -> io::Result<UnixStream> { + self.0.duplicate().map(UnixStream) + } + + /// Returns the socket address of the local half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn local_addr(&self) -> io::Result<SocketAddr> { + SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + } + + /// Returns the socket address of the remote half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) + } + + /// Sets the read timeout for the socket. + /// + /// If the provided value is [`None`], then [`read`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err + /// [`read`]: ../../../../std/io/trait.Read.html#tymethod.read + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { + self.0.set_timeout(timeout, libc::SO_RCVTIMEO) + } + + /// Sets the write timeout for the socket. + /// + /// If the provided value is [`None`], then [`write`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err + /// [`write`]: ../../../../std/io/trait.Write.html#tymethod.write + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout"); + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::net::UdpSocket; + /// use std::time::Duration; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap(); + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { + self.0.set_timeout(timeout, libc::SO_SNDTIMEO) + } + + /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// assert_eq!(socket.read_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + self.0.timeout(libc::SO_RCVTIMEO) + } + + /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout"); + /// assert_eq!(socket.write_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + self.0.timeout(libc::SO_SNDTIMEO) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// if let Ok(Some(err)) = socket.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// ``` + /// + /// # Platform specific + /// On Redox this always returns `None`. + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + self.0.take_error() + } + + /// Shuts down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O calls on the + /// specified portions to immediately return with an appropriate value + /// (see the documentation of [`Shutdown`]). + /// + /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::net::Shutdown; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl io::Read for UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + io::Read::read(&mut &*self, buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + io::Read::read_vectored(&mut &*self, bufs) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl<'a> io::Read for &'a UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl io::Write for UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + io::Write::write(&mut &*self, buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + io::Write::write_vectored(&mut &*self, bufs) + } + + fn flush(&mut self) -> io::Result<()> { + io::Write::flush(&mut &*self) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl<'a> io::Write for &'a UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl AsRawFd for UnixStream { + fn as_raw_fd(&self) -> RawFd { + *self.0.as_inner() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl FromRawFd for UnixStream { + unsafe fn from_raw_fd(fd: RawFd) -> UnixStream { + UnixStream(Socket::from_inner(fd)) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl IntoRawFd for UnixStream { + fn into_raw_fd(self) -> RawFd { + self.0.into_inner() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpStream { + fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpListener { + fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::UdpSocket { + fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { + let socket = sys::net::Socket::from_inner(fd); + net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(socket)) + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { + let socket = sys::net::Socket::from_inner(fd); + net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(socket)) + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { + let socket = sys::net::Socket::from_inner(fd); + net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(socket)) + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::TcpStream { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_socket().into_inner() + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::TcpListener { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_socket().into_inner() + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::UdpSocket { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_socket().into_inner() + } +} + +/// A structure representing a Unix domain socket server. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// use std::os::unix::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); +/// +/// // accept connections and process them, spawning a new thread for each one +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// /* connection succeeded */ +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// /* connection failed */ +/// break; +/// } +/// } +/// } +/// ``` +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct UnixListener(Socket); + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl fmt::Debug for UnixListener { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixListener"); + builder.field("fd", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + builder.finish() + } +} + +impl UnixListener { + /// Creates a new `UnixListener` bound to the specified socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = match UnixListener::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> { + fn inner(path: &Path) -> io::Result<UnixListener> { + unsafe { + let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + let (addr, len) = sockaddr_un(path)?; + + cvt(libc::bind(*inner.as_inner(), &addr as *const _ as *const _, len as _))?; + cvt(libc::listen(*inner.as_inner(), 128))?; + + Ok(UnixListener(inner)) + } + } + inner(path.as_ref()) + } + + /// Accepts a new incoming connection to this listener. + /// + /// This function will block the calling thread until a new Unix connection + /// is established. When established, the corresponding [`UnixStream`] and + /// the remote peer's address will be returned. + /// + /// [`UnixStream`]: ../../../../std/os/unix/net/struct.UnixStream.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// match listener.accept() { + /// Ok((socket, addr)) => println!("Got a client: {:?}", addr), + /// Err(e) => println!("accept function failed: {:?}", e), + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { + let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() }; + let mut len = mem::size_of_val(&storage) as libc::socklen_t; + let sock = self.0.accept(&mut storage as *mut _ as *mut _, &mut len)?; + let addr = SocketAddr::from_parts(storage, len)?; + Ok((UnixStream(sock), addr)) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixListener` is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one listener will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn try_clone(&self) -> io::Result<UnixListener> { + self.0.duplicate().map(UnixListener) + } + + /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn local_addr(&self) -> io::Result<SocketAddr> { + SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/tmp/sock").unwrap(); + /// + /// if let Ok(Some(err)) = listener.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// ``` + /// + /// # Platform specific + /// On Redox this always returns `None`. + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + self.0.take_error() + } + + /// Returns an iterator over incoming connections. + /// + /// The iterator will never return [`None`] and will also not yield the + /// peer's [`SocketAddr`] structure. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`SocketAddr`]: struct.SocketAddr.html + /// + /// # Examples + /// + /// ```no_run + /// use std::thread; + /// use std::os::unix::net::{UnixStream, UnixListener}; + /// + /// fn handle_client(stream: UnixStream) { + /// // ... + /// } + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// thread::spawn(|| handle_client(stream)); + /// } + /// Err(err) => { + /// break; + /// } + /// } + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn incoming<'a>(&'a self) -> Incoming<'a> { + Incoming { listener: self } + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl AsRawFd for UnixListener { + fn as_raw_fd(&self) -> RawFd { + *self.0.as_inner() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl FromRawFd for UnixListener { + unsafe fn from_raw_fd(fd: RawFd) -> UnixListener { + UnixListener(Socket::from_inner(fd)) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl IntoRawFd for UnixListener { + fn into_raw_fd(self) -> RawFd { + self.0.into_inner() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl<'a> IntoIterator for &'a UnixListener { + type Item = io::Result<UnixStream>; + type IntoIter = Incoming<'a>; + + fn into_iter(self) -> Incoming<'a> { + self.incoming() + } +} + +/// An iterator over incoming connections to a [`UnixListener`]. +/// +/// It will never return [`None`]. +/// +/// [`None`]: ../../../../std/option/enum.Option.html#variant.None +/// [`UnixListener`]: struct.UnixListener.html +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// use std::os::unix::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); +/// +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// break; +/// } +/// } +/// } +/// ``` +#[derive(Debug)] +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct Incoming<'a> { + listener: &'a UnixListener, +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl<'a> Iterator for Incoming<'a> { + type Item = io::Result<UnixStream>; + + fn next(&mut self) -> Option<io::Result<UnixStream>> { + Some(self.listener.accept().map(|s| s.0)) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (usize::max_value(), None) + } +} + +/// A Unix datagram socket. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::unix::net::UnixDatagram; +/// +/// let socket = UnixDatagram::bind("/path/to/my/socket").unwrap(); +/// socket.send_to(b"hello world", "/path/to/other/socket").unwrap(); +/// let mut buf = [0; 100]; +/// let (count, address) = socket.recv_from(&mut buf).unwrap(); +/// println!("socket {:?} sent {:?}", address, &buf[..count]); +/// ``` +#[stable(feature = "unix_socket", since = "1.10.0")] +pub struct UnixDatagram(Socket); + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl fmt::Debug for UnixDatagram { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixDatagram"); + builder.field("fd", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + if let Ok(addr) = self.peer_addr() { + builder.field("peer", &addr); + } + builder.finish() + } +} + +impl UnixDatagram { + /// Creates a Unix datagram socket bound to the given path. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = match UnixDatagram::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't bind: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> { + fn inner(path: &Path) -> io::Result<UnixDatagram> { + unsafe { + let socket = UnixDatagram::unbound()?; + let (addr, len) = sockaddr_un(path)?; + + cvt(libc::bind(*socket.0.as_inner(), &addr as *const _ as *const _, len as _))?; + + Ok(socket) + } + } + inner(path.as_ref()) + } + + /// Creates a Unix Datagram socket which is not bound to any address. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = match UnixDatagram::unbound() { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't unbound: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn unbound() -> io::Result<UnixDatagram> { + let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_DGRAM)?; + Ok(UnixDatagram(inner)) + } + + /// Creates an unnamed pair of connected sockets. + /// + /// Returns two `UnixDatagrams`s which are connected to each other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let (sock1, sock2) = match UnixDatagram::pair() { + /// Ok((sock1, sock2)) => (sock1, sock2), + /// Err(e) => { + /// println!("Couldn't unbound: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { + let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_DGRAM)?; + Ok((UnixDatagram(i1), UnixDatagram(i2))) + } + + /// Connects the socket to the specified address. + /// + /// The [`send`] method may be used to send data to the specified address. + /// [`recv`] and [`recv_from`] will only receive data from that address. + /// + /// [`send`]: #method.send + /// [`recv`]: #method.recv + /// [`recv_from`]: #method.recv_from + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// match sock.connect("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return + /// } + /// }; + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { + fn inner(d: &UnixDatagram, path: &Path) -> io::Result<()> { + unsafe { + let (addr, len) = sockaddr_un(path)?; + + cvt(libc::connect(*d.0.as_inner(), &addr as *const _ as *const _, len))?; + + Ok(()) + } + } + inner(self, path.as_ref()) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixDatagram` is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one side will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap(); + /// + /// let sock_copy = sock.try_clone().expect("try_clone failed"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn try_clone(&self) -> io::Result<UnixDatagram> { + self.0.duplicate().map(UnixDatagram) + } + + /// Returns the address of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap(); + /// + /// let addr = sock.local_addr().expect("Couldn't get local address"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn local_addr(&self) -> io::Result<SocketAddr> { + SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + } + + /// Returns the address of this socket's peer. + /// + /// The [`connect`] method will connect the socket to a peer. + /// + /// [`connect`]: #method.connect + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.connect("/path/to/the/socket").unwrap(); + /// + /// let addr = sock.peer_addr().expect("Couldn't get peer address"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) + } + + /// Receives data from the socket. + /// + /// On success, returns the number of bytes read and the address from + /// whence the data came. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// let mut buf = vec![0; 10]; + /// match sock.recv_from(buf.as_mut_slice()) { + /// Ok((size, sender)) => println!("received {} bytes from {:?}", size, sender), + /// Err(e) => println!("recv_from function failed: {:?}", e), + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + let mut count = 0; + let addr = SocketAddr::new(|addr, len| { + unsafe { + count = libc::recvfrom(*self.0.as_inner(), + buf.as_mut_ptr() as *mut _, + buf.len(), + 0, + addr, + len); + if count > 0 { + 1 + } else if count == 0 { + 0 + } else { + -1 + } + } + })?; + + Ok((count as usize, addr)) + } + + /// Receives data from the socket. + /// + /// On success, returns the number of bytes read. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap(); + /// let mut buf = vec![0; 10]; + /// sock.recv(buf.as_mut_slice()).expect("recv function failed"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + /// Sends data on the socket to the specified address. + /// + /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> { + fn inner(d: &UnixDatagram, buf: &[u8], path: &Path) -> io::Result<usize> { + unsafe { + let (addr, len) = sockaddr_un(path)?; + + let count = cvt(libc::sendto(*d.0.as_inner(), + buf.as_ptr() as *const _, + buf.len(), + MSG_NOSIGNAL, + &addr as *const _ as *const _, + len))?; + Ok(count as usize) + } + } + inner(self, buf, path.as_ref()) + } + + /// Sends data on the socket to the socket's peer. + /// + /// The peer address may be set by the `connect` method, and this method + /// will return an error if the socket has not already been connected. + /// + /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.connect("/some/sock").expect("Couldn't connect"); + /// sock.send(b"omelette au fromage").expect("send_to function failed"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn send(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + /// Sets the read timeout for the socket. + /// + /// If the provided value is [`None`], then [`recv`] and [`recv_from`] calls will + /// block indefinitely. An [`Err`] is returned if the zero [`Duration`] + /// is passed to this method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err + /// [`recv`]: #method.recv + /// [`recv_from`]: #method.recv_from + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed"); + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let socket = UnixDatagram::unbound().unwrap(); + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { + self.0.set_timeout(timeout, libc::SO_RCVTIMEO) + } + + /// Sets the write timeout for the socket. + /// + /// If the provided value is [`None`], then [`send`] and [`send_to`] calls will + /// block indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`send`]: #method.send + /// [`send_to`]: #method.send_to + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// use std::io; + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let socket = UnixDatagram::unbound().unwrap(); + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput) + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { + self.0.set_timeout(timeout, libc::SO_SNDTIMEO) + } + + /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed"); + /// assert_eq!(sock.read_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + self.0.timeout(libc::SO_RCVTIMEO) + } + + /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// assert_eq!(sock.write_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + self.0.timeout(libc::SO_SNDTIMEO) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_nonblocking(true).expect("set_nonblocking function failed"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// if let Ok(Some(err)) = sock.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + self.0.take_error() + } + + /// Shut down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O calls on the + /// specified portions to immediately return with an appropriate value + /// (see the documentation of [`Shutdown`]). + /// + /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// use std::net::Shutdown; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl AsRawFd for UnixDatagram { + fn as_raw_fd(&self) -> RawFd { + *self.0.as_inner() + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl FromRawFd for UnixDatagram { + unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { + UnixDatagram(Socket::from_inner(fd)) + } +} + +#[stable(feature = "unix_socket", since = "1.10.0")] +impl IntoRawFd for UnixDatagram { + fn into_raw_fd(self) -> RawFd { + self.0.into_inner() + } +} + +#[cfg(all(test, not(target_os = "emscripten")))] +mod test { + use crate::thread; + use crate::io::{self, ErrorKind}; + use crate::io::prelude::*; + use crate::time::Duration; + use crate::sys_common::io::test::tmpdir; + + use super::*; + + macro_rules! or_panic { + ($e:expr) => { + match $e { + Ok(e) => e, + Err(e) => panic!("{}", e), + } + } + } + + #[test] + fn basic() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + let msg1 = b"hello"; + let msg2 = b"world!"; + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let thread = thread::spawn(move || { + let mut stream = or_panic!(listener.accept()).0; + let mut buf = [0; 5]; + or_panic!(stream.read(&mut buf)); + assert_eq!(&msg1[..], &buf[..]); + or_panic!(stream.write_all(msg2)); + }); + + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + assert_eq!(Some(&*socket_path), + stream.peer_addr().unwrap().as_pathname()); + or_panic!(stream.write_all(msg1)); + let mut buf = vec![]; + or_panic!(stream.read_to_end(&mut buf)); + assert_eq!(&msg2[..], &buf[..]); + drop(stream); + + thread.join().unwrap(); + } + + #[test] + fn vectored() { + let (mut s1, mut s2) = or_panic!(UnixStream::pair()); + + let len = or_panic!(s1.write_vectored( + &[IoSlice::new(b"hello"), IoSlice::new(b" "), IoSlice::new(b"world!")], + )); + assert_eq!(len, 12); + + let mut buf1 = [0; 6]; + let mut buf2 = [0; 7]; + let len = or_panic!(s2.read_vectored( + &mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)], + )); + assert_eq!(len, 12); + assert_eq!(&buf1, b"hello "); + assert_eq!(&buf2, b"world!\0"); + } + + #[test] + fn pair() { + let msg1 = b"hello"; + let msg2 = b"world!"; + + let (mut s1, mut s2) = or_panic!(UnixStream::pair()); + let thread = thread::spawn(move || { + // s1 must be moved in or the test will hang! + let mut buf = [0; 5]; + or_panic!(s1.read(&mut buf)); + assert_eq!(&msg1[..], &buf[..]); + or_panic!(s1.write_all(msg2)); + }); + + or_panic!(s2.write_all(msg1)); + let mut buf = vec![]; + or_panic!(s2.read_to_end(&mut buf)); + assert_eq!(&msg2[..], &buf[..]); + drop(s2); + + thread.join().unwrap(); + } + + #[test] + fn try_clone() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + let msg1 = b"hello"; + let msg2 = b"world"; + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let thread = thread::spawn(move || { + let mut stream = or_panic!(listener.accept()).0; + or_panic!(stream.write_all(msg1)); + or_panic!(stream.write_all(msg2)); + }); + + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + let mut stream2 = or_panic!(stream.try_clone()); + + let mut buf = [0; 5]; + or_panic!(stream.read(&mut buf)); + assert_eq!(&msg1[..], &buf[..]); + or_panic!(stream2.read(&mut buf)); + assert_eq!(&msg2[..], &buf[..]); + + thread.join().unwrap(); + } + + #[test] + fn iter() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let thread = thread::spawn(move || { + for stream in listener.incoming().take(2) { + let mut stream = or_panic!(stream); + let mut buf = [0]; + or_panic!(stream.read(&mut buf)); + } + }); + + for _ in 0..2 { + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + or_panic!(stream.write_all(&[0])); + } + + thread.join().unwrap(); + } + + #[test] + fn long_path() { + let dir = tmpdir(); + let socket_path = dir.path() + .join("asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa\ + sasdfasdfasdasdfasdfasdfadfasdfasdfasdfasdfasdf"); + match UnixStream::connect(&socket_path) { + Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} + Err(e) => panic!("unexpected error {}", e), + Ok(_) => panic!("unexpected success"), + } + + match UnixListener::bind(&socket_path) { + Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} + Err(e) => panic!("unexpected error {}", e), + Ok(_) => panic!("unexpected success"), + } + + match UnixDatagram::bind(&socket_path) { + Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} + Err(e) => panic!("unexpected error {}", e), + Ok(_) => panic!("unexpected success"), + } + } + + #[test] + fn timeouts() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let _listener = or_panic!(UnixListener::bind(&socket_path)); + + let stream = or_panic!(UnixStream::connect(&socket_path)); + let dur = Duration::new(15410, 0); + + assert_eq!(None, or_panic!(stream.read_timeout())); + + or_panic!(stream.set_read_timeout(Some(dur))); + assert_eq!(Some(dur), or_panic!(stream.read_timeout())); + + assert_eq!(None, or_panic!(stream.write_timeout())); + + or_panic!(stream.set_write_timeout(Some(dur))); + assert_eq!(Some(dur), or_panic!(stream.write_timeout())); + + or_panic!(stream.set_read_timeout(None)); + assert_eq!(None, or_panic!(stream.read_timeout())); + + or_panic!(stream.set_write_timeout(None)); + assert_eq!(None, or_panic!(stream.write_timeout())); + } + + #[test] + fn test_read_timeout() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let _listener = or_panic!(UnixListener::bind(&socket_path)); + + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); + + let mut buf = [0; 10]; + let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); + assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", kind); + } + + #[test] + fn test_read_with_timeout() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let listener = or_panic!(UnixListener::bind(&socket_path)); + + let mut stream = or_panic!(UnixStream::connect(&socket_path)); + or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); + + let mut other_end = or_panic!(listener.accept()).0; + or_panic!(other_end.write_all(b"hello world")); + + let mut buf = [0; 11]; + or_panic!(stream.read(&mut buf)); + assert_eq!(b"hello world", &buf[..]); + + let kind = stream.read_exact(&mut buf).err().expect("expected error").kind(); + assert!(kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", kind); + } + + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_unix_stream_timeout_zero_duration() { + let dir = tmpdir(); + let socket_path = dir.path().join("sock"); + + let listener = or_panic!(UnixListener::bind(&socket_path)); + let stream = or_panic!(UnixStream::connect(&socket_path)); + + let result = stream.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = stream.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + drop(listener); + } + + #[test] + fn test_unix_datagram() { + let dir = tmpdir(); + let path1 = dir.path().join("sock1"); + let path2 = dir.path().join("sock2"); + + let sock1 = or_panic!(UnixDatagram::bind(&path1)); + let sock2 = or_panic!(UnixDatagram::bind(&path2)); + + let msg = b"hello world"; + or_panic!(sock1.send_to(msg, &path2)); + let mut buf = [0; 11]; + or_panic!(sock2.recv_from(&mut buf)); + assert_eq!(msg, &buf[..]); + } + + #[test] + fn test_unnamed_unix_datagram() { + let dir = tmpdir(); + let path1 = dir.path().join("sock1"); + + let sock1 = or_panic!(UnixDatagram::bind(&path1)); + let sock2 = or_panic!(UnixDatagram::unbound()); + + let msg = b"hello world"; + or_panic!(sock2.send_to(msg, &path1)); + let mut buf = [0; 11]; + let (usize, addr) = or_panic!(sock1.recv_from(&mut buf)); + assert_eq!(usize, 11); + assert!(addr.is_unnamed()); + assert_eq!(msg, &buf[..]); + } + + #[test] + fn test_connect_unix_datagram() { + let dir = tmpdir(); + let path1 = dir.path().join("sock1"); + let path2 = dir.path().join("sock2"); + + let bsock1 = or_panic!(UnixDatagram::bind(&path1)); + let bsock2 = or_panic!(UnixDatagram::bind(&path2)); + let sock = or_panic!(UnixDatagram::unbound()); + or_panic!(sock.connect(&path1)); + + // Check send() + let msg = b"hello there"; + or_panic!(sock.send(msg)); + let mut buf = [0; 11]; + let (usize, addr) = or_panic!(bsock1.recv_from(&mut buf)); + assert_eq!(usize, 11); + assert!(addr.is_unnamed()); + assert_eq!(msg, &buf[..]); + + // Changing default socket works too + or_panic!(sock.connect(&path2)); + or_panic!(sock.send(msg)); + or_panic!(bsock2.recv_from(&mut buf)); + } + + #[test] + fn test_unix_datagram_recv() { + let dir = tmpdir(); + let path1 = dir.path().join("sock1"); + + let sock1 = or_panic!(UnixDatagram::bind(&path1)); + let sock2 = or_panic!(UnixDatagram::unbound()); + or_panic!(sock2.connect(&path1)); + + let msg = b"hello world"; + or_panic!(sock2.send(msg)); + let mut buf = [0; 11]; + let size = or_panic!(sock1.recv(&mut buf)); + assert_eq!(size, 11); + assert_eq!(msg, &buf[..]); + } + + #[test] + fn datagram_pair() { + let msg1 = b"hello"; + let msg2 = b"world!"; + + let (s1, s2) = or_panic!(UnixDatagram::pair()); + let thread = thread::spawn(move || { + // s1 must be moved in or the test will hang! + let mut buf = [0; 5]; + or_panic!(s1.recv(&mut buf)); + assert_eq!(&msg1[..], &buf[..]); + or_panic!(s1.send(msg2)); + }); + + or_panic!(s2.send(msg1)); + let mut buf = [0; 6]; + or_panic!(s2.recv(&mut buf)); + assert_eq!(&msg2[..], &buf[..]); + drop(s2); + + thread.join().unwrap(); + } + + // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors + // when passed zero Durations + #[test] + fn test_unix_datagram_timeout_zero_duration() { + let dir = tmpdir(); + let path = dir.path().join("sock"); + + let datagram = or_panic!(UnixDatagram::bind(&path)); + + let result = datagram.set_write_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + + let result = datagram.set_read_timeout(Some(Duration::new(0, 0))); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + } + + #[test] + fn abstract_namespace_not_allowed() { + assert!(UnixStream::connect("\0asdf").is_err()); + } +} diff --git a/src/libstd/sys/redox/ext/process.rs b/src/libstd/sys/vxworks/ext/process.rs index e981cb93d44..4de72fa1816 100644 --- a/src/libstd/sys/redox/ext/process.rs +++ b/src/libstd/sys/vxworks/ext/process.rs @@ -1,14 +1,14 @@ -//! Redox-specific extensions to primitives in the `std::process` module. +//! Unix-specific extensions to primitives in the `std::process` module. #![stable(feature = "rust1", since = "1.0.0")] use crate::io; -use crate::os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd}; +use crate::sys::vxworks::ext::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd}; use crate::process; use crate::sys; use crate::sys_common::{AsInnerMut, AsInner, FromInner, IntoInner}; -/// Redox-specific extensions to the [`process::Command`] builder, +/// Unix-specific extensions to the [`process::Command`] builder. /// /// [`process::Command`]: ../../../../std/process/struct.Command.html #[stable(feature = "rust1", since = "1.0.0")] @@ -17,12 +17,12 @@ pub trait CommandExt { /// `setuid` call in the child process. Failure in the `setuid` /// call will cause the spawn to fail. #[stable(feature = "rust1", since = "1.0.0")] - fn uid(&mut self, id: u32) -> &mut process::Command; + fn uid(&mut self, id: u16) -> &mut process::Command; /// Similar to `uid`, but sets the group ID of the child process. This has /// the same semantics as the `uid` field. #[stable(feature = "rust1", since = "1.0.0")] - fn gid(&mut self, id: u32) -> &mut process::Command; + fn gid(&mut self, id: u16) -> &mut process::Command; /// Schedules a closure to be run just before the `exec` function is /// invoked. @@ -79,10 +79,20 @@ pub trait CommandExt { /// an error indicating why the exec (or another part of the setup of the /// `Command`) failed. /// + /// `exec` not returning has the same implications as calling + /// [`process::exit`] – no destructors on the current stack or any other + /// thread’s stack will be run. Therefore, it is recommended to only call + /// `exec` at a point where it is fine to not run any destructors. Note, + /// that the `execvp` syscall independently guarantees that all memory is + /// freed and all file descriptors with the `CLOEXEC` option (set by default + /// on all file descriptors opened by the standard library) are closed. + /// /// This function, unlike `spawn`, will **not** `fork` the process to create /// a new child. Like spawn, however, the default behavior for the stdio /// descriptors will be to inherited from the current process. /// + /// [`process::exit`]: ../../../process/fn.exit.html + /// /// # Notes /// /// The process may be in a "broken state" if this function returns in @@ -97,12 +107,12 @@ pub trait CommandExt { #[stable(feature = "rust1", since = "1.0.0")] impl CommandExt for process::Command { - fn uid(&mut self, id: u32) -> &mut process::Command { + fn uid(&mut self, id: u16) -> &mut process::Command { self.as_inner_mut().uid(id); self } - fn gid(&mut self, id: u32) -> &mut process::Command { + fn gid(&mut self, id: u16) -> &mut process::Command { self.as_inner_mut().gid(id); self } @@ -119,7 +129,7 @@ impl CommandExt for process::Command { } } -/// Redox-specific extensions to [`process::ExitStatus`]. +/// Unix-specific extensions to [`process::ExitStatus`]. /// /// [`process::ExitStatus`]: ../../../../std/process/struct.ExitStatus.html #[stable(feature = "rust1", since = "1.0.0")] @@ -195,3 +205,9 @@ impl IntoRawFd for process::ChildStderr { self.into_inner().into_fd().into_raw() } } + +/// Returns the OS-assigned process identifier associated with this process's parent. +#[stable(feature = "unix_ppid", since = "1.27.0")] +pub fn parent_id() -> u32 { + crate::sys::os::getppid() +} diff --git a/src/libstd/sys/vxworks/ext/raw.rs b/src/libstd/sys/vxworks/ext/raw.rs new file mode 100644 index 00000000000..1f134f4e2d1 --- /dev/null +++ b/src/libstd/sys/vxworks/ext/raw.rs @@ -0,0 +1,5 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] + +#[doc(inline)] +#[stable(feature = "pthread_t", since = "1.8.0")] +pub use crate::sys::platform::raw::pthread_t; diff --git a/src/libstd/sys/vxworks/fast_thread_local.rs b/src/libstd/sys/vxworks/fast_thread_local.rs new file mode 100644 index 00000000000..f5a2e263d25 --- /dev/null +++ b/src/libstd/sys/vxworks/fast_thread_local.rs @@ -0,0 +1,36 @@ +#![cfg(target_thread_local)] +#![unstable(feature = "thread_local_internals", issue = "0")] + +// 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! +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { + use crate::mem; + use crate::sys_common::thread_local::register_dtor_fallback; + + 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); +} + +pub fn requires_move_before_drop() -> bool { + false +} diff --git a/src/libstd/sys/vxworks/fd.rs b/src/libstd/sys/vxworks/fd.rs new file mode 100644 index 00000000000..db2865d2529 --- /dev/null +++ b/src/libstd/sys/vxworks/fd.rs @@ -0,0 +1,188 @@ +#![unstable(reason = "not public", issue = "0", feature = "fd")] + +use crate::cmp; +use crate::io::{self, Read, Initializer, IoSlice, IoSliceMut}; +use crate::mem; +use crate::sys::cvt; +use crate::sys_common::AsInner; + +use libc::{self, c_int, c_void, ssize_t}; + +#[derive(Debug)] +pub struct FileDesc { + fd: c_int, +} + +fn max_len() -> usize { + // The maximum read limit on most posix-like systems is `SSIZE_MAX`, + // with the man page quoting that if the count of bytes to read is + // greater than `SSIZE_MAX` the result is "unspecified". + <ssize_t>::max_value() as usize +} + +impl FileDesc { + pub fn new(fd: c_int) -> FileDesc { + FileDesc { fd: fd } + } + + pub fn raw(&self) -> c_int { self.fd } + + /// Extracts the actual filedescriptor without closing it. + pub fn into_raw(self) -> c_int { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::read(self.fd, + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), max_len())) + })?; + Ok(ret as usize) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::readv(self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), c_int::max_value() as usize) as c_int) + })?; + Ok(ret as usize) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + unsafe fn cvt_pread(fd: c_int, buf: *mut c_void, count: usize, offset: i64) + -> io::Result<isize> + { + use libc::pread; + cvt(pread(fd, buf, count, offset)) + } + + unsafe { + cvt_pread(self.fd, + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), max_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, + cmp::min(buf.len(), max_len())) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::writev(self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), c_int::max_value() as usize) as c_int) + })?; + Ok(ret as usize) + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + unsafe fn cvt_pwrite(fd: c_int, buf: *const c_void, count: usize, offset: i64) + -> io::Result<isize> + { + use libc::pwrite; + cvt(pwrite(fd, buf, count, offset)) + } + + unsafe { + cvt_pwrite(self.fd, + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), max_len()), + offset as i64) + .map(|n| n as usize) + } + } + + pub fn get_cloexec(&self) -> io::Result<bool> { + unsafe { + Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) + } + } + + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; + let new = previous | libc::FD_CLOEXEC; + if new != previous { + cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; + } + Ok(()) + } + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + unsafe { + let v = nonblocking as c_int; + cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?; + Ok(()) + } + } + + // refer to pxPipeDrv library documentation. + // VxWorks uses fcntl to set O_NONBLOCK to the pipes + pub fn set_nonblocking_pipe(&self, nonblocking: bool) -> io::Result<()> { + unsafe { + let mut flags = cvt(libc::fcntl(self.fd, libc::F_GETFL, 0))?; + flags = if nonblocking { + flags | libc::O_NONBLOCK + } else { + flags & !libc::O_NONBLOCK + }; + cvt(libc::fcntl(self.fd, libc::F_SETFL, flags))?; + Ok(()) + } + } + + + pub fn duplicate(&self) -> io::Result<FileDesc> { + let fd = self.raw(); + match cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) }) { + Ok(newfd) => { + Ok(FileDesc::new(newfd)) + } + Err(e) => return Err(e), + } + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (**self).read(buf) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +impl AsInner<c_int> for FileDesc { + fn as_inner(&self) -> &c_int { &self.fd } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // (opened after we closed ours. + let _ = unsafe { libc::close(self.fd) }; + } +} diff --git a/src/libstd/sys/vxworks/fs.rs b/src/libstd/sys/vxworks/fs.rs new file mode 100644 index 00000000000..d537d2258fd --- /dev/null +++ b/src/libstd/sys/vxworks/fs.rs @@ -0,0 +1,575 @@ +// copies from linuxx +use crate::ffi::{CString, CStr, OsString, OsStr}; +use crate::sys::vxworks::ext::ffi::OsStrExt; +use crate::fmt; +use crate::io::{self, Error, ErrorKind, SeekFrom, IoSlice, IoSliceMut}; +use crate::mem; +use crate::path::{Path, PathBuf}; +use crate::ptr; +use crate::sync::Arc; +use crate::sys::fd::FileDesc; +use crate::sys::time::SystemTime; +use crate::sys::{cvt, cvt_r}; +use crate::sys_common::{AsInner, FromInner}; +use libc::{self, c_int, mode_t, stat64, off_t}; +use libc::{ftruncate, lseek, dirent, readdir_r as readdir64_r, open}; +use crate::sys::vxworks::ext::ffi::OsStringExt; +pub struct File(FileDesc); + +#[derive(Clone)] +pub struct FileAttr { + stat: stat64, +} + +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + dirp: Dir, + root: PathBuf, +} + +#[derive(Clone)] +pub struct ReadDir { + inner: Arc<InnerReadDir>, + end_of_stream: bool, +} + +struct Dir(*mut libc::DIR); + +unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} + +pub struct DirEntry { + entry: dirent, + dir: ReadDir, +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + // generic + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, + // system-specific + custom_flags: i32, + mode: mode_t, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { mode: mode_t } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType { mode: mode_t } + +#[derive(Debug)] +pub struct DirBuilder { mode: mode_t } + +impl FileAttr { + pub fn size(&self) -> u64 { self.stat.st_size as u64 } + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat.st_mode as mode_t) } + } + + pub fn file_type(&self) -> FileType { + FileType { mode: self.stat.st_mode as mode_t } + } + + pub fn modified(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_mtime as libc::time_t, + tv_nsec: 0, // hack 2.0; + })) + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_atime as libc::time_t, + tv_nsec: 0, // hack - a proper fix would be better + })) + } + + pub fn created(&self) -> io::Result<SystemTime> { + Err(io::Error::new(io::ErrorKind::Other, + "creation time is not available on this platform currently")) + } + +} + +impl AsInner<stat64> for FileAttr { + fn as_inner(&self) -> &stat64 { &self.stat } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 + } + + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + // remove write permission for all classes; equivalent to `chmod a-w <file>` + self.mode &= !0o222; + } else { + // add write permission for all classes; equivalent to `chmod a+w <file>` + self.mode |= 0o222; + } + } + pub fn mode(&self) -> u32 { self.mode as u32 } +} + +impl FileType { + pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) } + pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) } + pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) } + + pub fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode } +} + +impl FromInner<u32> for FilePermissions { + fn from_inner(mode: u32) -> FilePermissions { + FilePermissions { mode: mode as mode_t } + } +} + +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.inner.root, f) + } +} + +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; + fn next(&mut self) -> Option<io::Result<DirEntry>> { + if self.end_of_stream { + return None; + } + + unsafe { + let mut ret = DirEntry { + entry: mem::zeroed(), + dir: self.clone(), + }; + let mut entry_ptr = ptr::null_mut(); + loop { + if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { + if entry_ptr.is_null() { + // We encountered an error (which will be returned in this iteration), but + // we also reached the end of the directory stream. The `end_of_stream` + // flag is enabled to make sure that we return `None` in the next iteration + // (instead of looping forever) + self.end_of_stream = true; + } + return Some(Err(Error::last_os_error())) + } + if entry_ptr.is_null() { + return None + } + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)) + } + } + } + } +} + +impl Drop for Dir { + fn drop(&mut self) { + let r = unsafe { libc::closedir(self.0) }; + debug_assert_eq!(r, 0); + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + use crate::sys::vxworks::ext::ffi::OsStrExt; + self.dir.inner.root.join(OsStr::from_bytes(self.name_bytes())) + } + + pub fn file_name(&self) -> OsString { + OsStr::from_bytes(self.name_bytes()).to_os_string() + } + + + pub fn metadata(&self) -> io::Result<FileAttr> { + lstat(&self.path()) + } + + pub fn file_type(&self) -> io::Result<FileType> { + lstat(&self.path()).map(|m| m.file_type()) + + } + + pub fn ino(&self) -> u64 { + self.entry.d_ino as u64 + } + + fn name_bytes(&self) -> &[u8] { + unsafe { + //&*self.name + CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() + } + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { + // generic + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + // system-specific + custom_flags: 0, + mode: 0o666, + } + } + + pub fn read(&mut self, read: bool) { self.read = read; } + pub fn write(&mut self, write: bool) { self.write = write; } + pub fn append(&mut self, append: bool) { self.append = append; } + pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; } + pub fn create(&mut self, create: bool) { self.create = create; } + pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; } + pub fn mode(&mut self, mode: u32) { self.mode = mode as mode_t; } + + fn get_access_mode(&self) -> io::Result<c_int> { + match (self.read, self.write, self.append) { + (true, false, false) => Ok(libc::O_RDONLY), + (false, true, false) => Ok(libc::O_WRONLY), + (true, true, false) => Ok(libc::O_RDWR), + (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), + (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), + (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), + } + } + + fn get_creation_mode(&self) -> io::Result<c_int> { + match (self.write, self.append) { + (true, false) => {} + (false, false) => + if self.truncate || self.create || self.create_new { + return Err(Error::from_raw_os_error(libc::EINVAL)); + }, + (_, true) => + if self.truncate && !self.create_new { + return Err(Error::from_raw_os_error(libc::EINVAL)); + }, + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => 0, + (true, false, false) => libc::O_CREAT, + (false, true, false) => libc::O_TRUNC, + (true, true, false) => libc::O_CREAT | libc::O_TRUNC, + (_, _, true) => libc::O_CREAT | libc::O_EXCL, + }) + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { + let path = cstr(path)?; + File::open_c(&path, opts) + } + + pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> { + let flags = libc::O_CLOEXEC | + opts.get_access_mode()? | + opts.get_creation_mode()? | + (opts.custom_flags as c_int & !libc::O_ACCMODE); + let fd = cvt_r(|| unsafe { + open(path.as_ptr(), flags, opts.mode as c_int) + })?; + Ok(File(FileDesc::new(fd))) + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { + ::libc::fstat(self.0.raw(), &mut stat) + })?; + Ok(FileAttr { stat: stat }) + } + + pub fn fsync(&self) -> io::Result<()> { + cvt_r(|| unsafe { libc::fsync(self.0.raw()) })?; + Ok(()) + } + + pub fn datasync(&self) -> io::Result<()> { + cvt_r(|| unsafe { os_datasync(self.0.raw()) })?; + return Ok(()); + unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) } //not supported + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + return cvt_r(|| unsafe { + ftruncate(self.0.raw(), size as off_t) + }).map(|_| ()); + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + + 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_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + 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> { + let (whence, pos) = match pos { + // Casting to `i64` is fine, too large values will end up as + // negative which will cause an error in `"lseek64"`. + SeekFrom::Start(off) => (libc::SEEK_SET, off as i64), + SeekFrom::End(off) => (libc::SEEK_END, off), + SeekFrom::Current(off) => (libc::SEEK_CUR, off), + }; + let n = cvt(unsafe { lseek(self.0.raw(), pos, whence) })?; + Ok(n as u64) + } + + pub fn duplicate(&self) -> io::Result<File> { + self.0.duplicate().map(File) + } + + pub fn fd(&self) -> &FileDesc { &self.0 } + + pub fn into_fd(self) -> FileDesc { self.0 } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?; + Ok(()) + } + + pub fn diverge(&self) -> ! { + panic!() + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; + Ok(()) + } + + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as mode_t; + } +} + +fn cstr(path: &Path) -> io::Result<CString> { + use crate::sys::vxworks::ext::ffi::OsStrExt; + Ok(CString::new(path.as_os_str().as_bytes())?) +} + +impl FromInner<c_int> for File { + fn from_inner(fd: c_int) -> File { + File(FileDesc::new(fd)) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn get_path(_fd: c_int) -> Option<PathBuf> { + // FIXME(#:(): implement this for VxWorks + None + } + fn get_mode(_fd: c_int) -> Option<(bool, bool)> { + // FIXME(#:(): implement this for VxWorks + None + } + + let fd = self.0.raw(); + let mut b = f.debug_struct("File"); + b.field("fd", &fd); + if let Some(path) = get_path(fd) { + b.field("path", &path); + } + if let Some((read, write)) = get_mode(fd) { + b.field("read", &read).field("write", &write); + } + b.finish() + } +} + +pub fn readdir(p: &Path) -> io::Result<ReadDir> { + let root = p.to_path_buf(); + let p = cstr(p)?; + unsafe { + let ptr = libc::opendir(p.as_ptr()); + if ptr.is_null() { + Err(Error::last_os_error()) + } else { + let inner = InnerReadDir { dirp: Dir(ptr), root }; + Ok(ReadDir{ + inner: Arc::new(inner), + end_of_stream: false, + }) + } + } +} + +pub fn unlink(p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::unlink(p.as_ptr()) })?; + Ok(()) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old = cstr(old)?; + let new = cstr(new)?; + cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?; + Ok(()) +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + let p = cstr(p)?; + cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?; + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::rmdir(p.as_ptr()) })?; + Ok(()) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let filetype = lstat(path)?.file_type(); + if filetype.is_symlink() { + unlink(path) + } else { + remove_dir_all_recursive(path) + } +} + +fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { + for child in readdir(path)? { + let child = child?; + if child.file_type()?.is_dir() { + remove_dir_all_recursive(&child.path())?; + } else { + unlink(&child.path())?; + } + } + rmdir(path) +} + +pub fn readlink(p: &Path) -> io::Result<PathBuf> { + let c_path = cstr(p)?; + let p = c_path.as_ptr(); + + let mut buf = Vec::with_capacity(256); + + loop { + let buf_read = cvt(unsafe { + libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) + })? as usize; + + unsafe { buf.set_len(buf_read); } + + if buf_read != buf.capacity() { + buf.shrink_to_fit(); + + return Ok(PathBuf::from(OsString::from_vec(buf))); + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. The length is guaranteed to be + // the same as the capacity due to the if statement above. + buf.reserve(1); + } +} + +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + let src = cstr(src)?; + let dst = cstr(dst)?; + cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?; + Ok(()) +} + +pub fn link(src: &Path, dst: &Path) -> io::Result<()> { + let src = cstr(src)?; + let dst = cstr(dst)?; + cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; + Ok(()) +} + +pub fn stat(p: &Path) -> io::Result<FileAttr> { + let p = cstr(p)?; + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { + libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _) + })?; + Ok(FileAttr { stat }) +} + +pub fn lstat(p: &Path) -> io::Result<FileAttr> { + let p = cstr(p)?; + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { + ::libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _) + })?; + Ok(FileAttr { stat }) +} + +pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { + use crate::sys::vxworks::ext::ffi::OsStrExt; + let path = CString::new(p.as_os_str().as_bytes())?; + let buf; + unsafe { + let r = libc::realpath(path.as_ptr(), ptr::null_mut()); + if r.is_null() { + return Err(io::Error::last_os_error()) + } + buf = CStr::from_ptr(r).to_bytes().to_vec(); + libc::free(r as *mut _); + } + Ok(PathBuf::from(OsString::from_vec(buf))) +} + +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + use crate::fs::File; + if !from.is_file() { + return Err(Error::new(ErrorKind::InvalidInput, + "the source path is not an existing regular file")) + } + + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + let perm = reader.metadata()?.permissions(); + + let ret = io::copy(&mut reader, &mut writer)?; + writer.set_permissions(perm)?; + Ok(ret) +} diff --git a/src/libstd/sys/vxworks/io.rs b/src/libstd/sys/vxworks/io.rs new file mode 100644 index 00000000000..8cd11cbf5df --- /dev/null +++ b/src/libstd/sys/vxworks/io.rs @@ -0,0 +1,86 @@ +use crate::marker::PhantomData; +use crate::slice; + +use libc::{iovec, c_void}; + +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: iovec, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { + vec: iovec { + iov_base: buf.as_ptr() as *mut u8 as *mut c_void, + iov_len: buf.len() + }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { + slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) + } + } +} + +pub struct IoSliceMut<'a> { + vec: iovec, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { + vec: iovec { + iov_base: buf.as_mut_ptr() as *mut c_void, + iov_len: buf.len() + }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSliceMut beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { + slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) + } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { + slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) + } + } +} diff --git a/src/libstd/sys/vxworks/memchr.rs b/src/libstd/sys/vxworks/memchr.rs new file mode 100644 index 00000000000..b5b4e6d9c13 --- /dev/null +++ b/src/libstd/sys/vxworks/memchr.rs @@ -0,0 +1,24 @@ +// Original implementation taken from rust-memchr. +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> { + 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> { + fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> { + core::slice::memchr::memrchr(needle, haystack) + } + + memrchr_specific(needle, haystack) +} diff --git a/src/libstd/sys/vxworks/mod.rs b/src/libstd/sys/vxworks/mod.rs new file mode 100644 index 00000000000..1eff4fbcd83 --- /dev/null +++ b/src/libstd/sys/vxworks/mod.rs @@ -0,0 +1,125 @@ +#![allow(dead_code)] +#![allow(missing_docs, nonstandard_style)] + +use crate::io::ErrorKind; + +pub use crate::os::vxworks as platform; +pub use self::rand::hashmap_random_keys; +pub use libc::strlen; + +pub mod alloc; +pub mod args; +pub mod cmath; +pub mod condvar; +pub mod env; +pub mod ext; +pub mod fast_thread_local; +pub mod fd; +pub mod fs; +pub mod memchr; +pub mod io; +pub mod mutex; +pub mod net; +pub mod os; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rand; +pub mod rwlock; +pub mod stack_overflow; +pub mod thread; +pub mod thread_local; +pub mod time; +pub mod stdio; + +pub use crate::sys_common::os_str_bytes as os_str; + +#[cfg(not(test))] +pub fn init() { + // By default, some platforms will send a *signal* when an EPIPE error + // would otherwise be delivered. This runtime doesn't install a SIGPIPE + // handler, causing it to kill the program, which isn't exactly what we + // want! + // + // Hence, we set SIGPIPE to ignore when the program starts up in order + // to prevent this problem. + unsafe { + reset_sigpipe(); + } + + unsafe fn reset_sigpipe() { } +} + +pub use libc::signal; + +pub fn decode_error_kind(errno: i32) -> ErrorKind { + match errno as libc::c_int { + libc::ECONNREFUSED => ErrorKind::ConnectionRefused, + libc::ECONNRESET => ErrorKind::ConnectionReset, + libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, + libc::EPIPE => ErrorKind::BrokenPipe, + libc::ENOTCONN => ErrorKind::NotConnected, + libc::ECONNABORTED => ErrorKind::ConnectionAborted, + libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + libc::EADDRINUSE => ErrorKind::AddrInUse, + libc::ENOENT => ErrorKind::NotFound, + libc::EINTR => ErrorKind::Interrupted, + libc::EINVAL => ErrorKind::InvalidInput, + libc::ETIMEDOUT => ErrorKind::TimedOut, + libc::EEXIST => ErrorKind::AlreadyExists, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => + ErrorKind::WouldBlock, + + _ => ErrorKind::Other, + } +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> { + if t.is_minus_one() { + Err(crate::io::Error::last_os_error()) + } else { + Ok(t) + } +} + +pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T> + where T: IsMinusOne, + F: FnMut() -> T +{ + loop { + match cvt(f()) { + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + other => return other, + } + } +} + +// On Unix-like platforms, libc::abort will unregister signal handlers +// including the SIGABRT handler, preventing the abort from being blocked, and +// fclose streams, with the side effect of flushing them so libc bufferred +// output will be printed. Additionally the shell will generally print a more +// understandable error message like "Abort trap" rather than "Illegal +// instruction" that intrinsics::abort would cause, as intrinsics::abort is +// implemented as an illegal instruction. +pub unsafe fn abort_internal() -> ! { + libc::abort() +} diff --git a/src/libstd/sys/vxworks/mutex.rs b/src/libstd/sys/vxworks/mutex.rs new file mode 100644 index 00000000000..b43af8fdcaa --- /dev/null +++ b/src/libstd/sys/vxworks/mutex.rs @@ -0,0 +1,127 @@ +use crate::cell::UnsafeCell; +use crate::mem::MaybeUninit; + +pub struct Mutex { inner: UnsafeCell<libc::pthread_mutex_t> } + +#[inline] +pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { + m.inner.get() +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +#[allow(dead_code)] // sys isn't exported yet +impl Mutex { + pub const fn new() -> Mutex { + // Might be moved to a different address, so it is better to avoid + // initialization of potentially opaque OS data before it landed. + // Be very careful using this newly constructed `Mutex`, reentrant + // locking is undefined behavior until `init` is called! + Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } + } + #[inline] + pub unsafe fn init(&mut self) { + // Issue #33770 + // + // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have + // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you + // try to re-lock it from the same thread when you already hold a lock. + // + // In practice, glibc takes advantage of this undefined behavior to + // implement hardware lock elision, which uses hardware transactional + // memory to avoid acquiring the lock. While a transaction is in + // progress, the lock appears to be unlocked. This isn't a problem for + // other threads since the transactional memory will abort if a conflict + // is detected, however no abort is generated if re-locking from the + // same thread. + // + // Since locking the same mutex twice will result in two aliasing &mut + // references, we instead create the mutex with type + // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to + // re-lock it from the same thread, thus avoiding undefined behavior. + let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); + let r = libc::pthread_mutexattr_init(attr.as_mut_ptr()); + debug_assert_eq!(r, 0); + let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL); + debug_assert_eq!(r, 0); + let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); + debug_assert_eq!(r, 0); + let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn lock(&self) { + let r = libc::pthread_mutex_lock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn unlock(&self) { + let r = libc::pthread_mutex_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_lock(&self) -> bool { + libc::pthread_mutex_trylock(self.inner.get()) == 0 + } + #[inline] + #[cfg(not(target_os = "dragonfly"))] + pub unsafe fn destroy(&self) { + let r = libc::pthread_mutex_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + #[cfg(target_os = "dragonfly")] + pub unsafe fn destroy(&self) { + let r = libc::pthread_mutex_destroy(self.inner.get()); + // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a + // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. + // Once it is used (locked/unlocked) or pthread_mutex_init() is called, + // this behaviour no longer occurs. + debug_assert!(r == 0 || r == libc::EINVAL); + } +} + +pub struct ReentrantMutex { inner: UnsafeCell<libc::pthread_mutex_t> } + +unsafe impl Send for ReentrantMutex {} +unsafe impl Sync for ReentrantMutex {} + +impl ReentrantMutex { + pub unsafe fn uninitialized() -> ReentrantMutex { + ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } + } + + pub unsafe fn init(&mut self) { + let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); + let result = libc::pthread_mutexattr_init(attr.as_mut_ptr()); + debug_assert_eq!(result, 0); + let result = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), + libc::PTHREAD_MUTEX_RECURSIVE); + debug_assert_eq!(result, 0); + let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); + debug_assert_eq!(result, 0); + let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); + debug_assert_eq!(result, 0); + } + + pub unsafe fn lock(&self) { + let result = libc::pthread_mutex_lock(self.inner.get()); + debug_assert_eq!(result, 0); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + libc::pthread_mutex_trylock(self.inner.get()) == 0 + } + + pub unsafe fn unlock(&self) { + let result = libc::pthread_mutex_unlock(self.inner.get()); + debug_assert_eq!(result, 0); + } + + pub unsafe fn destroy(&self) { + let result = libc::pthread_mutex_destroy(self.inner.get()); + debug_assert_eq!(result, 0); + } +} diff --git a/src/libstd/sys/vxworks/net.rs b/src/libstd/sys/vxworks/net.rs new file mode 100644 index 00000000000..56962e11dcf --- /dev/null +++ b/src/libstd/sys/vxworks/net.rs @@ -0,0 +1,357 @@ +use crate::ffi::CStr; +use crate::io; +use crate::io::{IoSlice, IoSliceMut}; +use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK}; +use crate::mem; +use crate::net::{SocketAddr, Shutdown}; +use crate::str; +use crate::sys::fd::FileDesc; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::time::{Duration, Instant}; +use crate::cmp; + +pub use crate::sys::{cvt, cvt_r}; + +#[allow(unused_extern_crates)] +pub extern crate libc as netc; + +pub type wrlen_t = size_t; + + +const SOCK_CLOEXEC: c_int = 0; +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(()) + } + + // We may need to trigger a glibc workaround. See on_resolver_failure() for details. + on_resolver_failure(); + + 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() + .to_owned() + }; + Err(io::Error::new(io::ErrorKind::Other, + &format!("failed to lookup address information: {}", + detail)[..])) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> { + let fam = match *addr { + SocketAddr::V4(..) => libc::AF_INET, + SocketAddr::V6(..) => libc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { + unsafe { + let fd = cvt(libc::socket(fam, ty, 0))?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + let socket = Socket(fd); + Ok(socket) + } + } + + pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { + unimplemented!(); + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addrp, len) = addr.into_inner(); + cvt(libc::connect(self.0.raw(), addrp, len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = libc::pollfd { + fd: self.0.raw(), + events: libc::POLLOUT, + revents: 0, + }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout.as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::max_value() as u64) as c_int; + + match unsafe { libc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if err.kind() != io::ErrorKind::Interrupted { + return Err(err); + } + } + 0 => {} + _ => { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP rather than read readiness + if pollfd.revents & libc::POLLHUP != 0 { + let e = self.take_error()? + .unwrap_or_else(|| { + io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") + }); + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) + -> io::Result<Socket> { + let fd = cvt_r(|| unsafe { + libc::accept(self.0.raw(), storage, len) + })?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + Ok(Socket(fd)) + } + + pub fn duplicate(&self) -> io::Result<Socket> { + self.0.duplicate().map(Socket) + } + + fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::recv(self.0.raw(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags) + })?; + Ok(ret as usize) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, 0) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, MSG_PEEK) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + + fn recv_from_with_flags(&self, buf: &mut [u8], flags: c_int) + -> io::Result<(usize, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; + + let n = cvt(unsafe { + libc::recvfrom(self.0.raw(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + &mut storage as *mut _ as *mut _, + &mut addrlen) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + + let secs = if dur.as_secs() > libc::time_t::max_value() as u64 { + libc::time_t::max_value() + } else { + dur.as_secs() as libc::time_t + }; + let mut timeout = libc::timeval { + tv_sec: secs, + tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => { + libc::timeval { + tv_sec: 0, + tv_usec: 0, + } + } + }; + setsockopt(self, libc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> { + let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => libc::SHUT_RDWR, + }; + cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; + Ok(()) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result<bool> { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as libc::c_int; + cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(|_| ()) + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + if raw == 0 { + Ok(None) + } else { + Ok(Some(io::Error::from_raw_os_error(raw as i32))) + } + } +} + +impl AsInner<c_int> for Socket { + fn as_inner(&self) -> &c_int { self.0.as_inner() } +} + +impl FromInner<c_int> for Socket { + fn from_inner(fd: c_int) -> Socket { Socket(FileDesc::new(fd)) } +} + +impl IntoInner<c_int> for Socket { + fn into_inner(self) -> c_int { self.0.into_raw() } +} + +// In versions of glibc prior to 2.26, there's a bug where the DNS resolver +// will cache the contents of /etc/resolv.conf, so changes to that file on disk +// can be ignored by a long-running program. That can break DNS lookups on e.g. +// laptops where the network comes and goes. See +// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some +// distros including Debian have patched glibc to fix this for a long time. +// +// A workaround for this bug is to call the res_init libc function, to clear +// the cached configs. Unfortunately, while we believe glibc's implementation +// of res_init is thread-safe, we know that other implementations are not +// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could +// try to synchronize its res_init calls with a Mutex, but that wouldn't +// protect programs that call into libc in other ways. So instead of calling +// res_init unconditionally, we call it only when we detect we're linking +// against glibc version < 2.26. (That is, when we both know its needed and +// believe it's thread-safe). +#[cfg(target_env = "gnu")] +fn on_resolver_failure() { +/* + use crate::sys; + + // If the version fails to parse, we treat it the same as "not glibc". + if let Some(version) = sys::os::glibc_version() { + if version < (2, 26) { + unsafe { libc::res_init() }; + } + } + */ +} + +#[cfg(not(target_env = "gnu"))] +fn on_resolver_failure() {} + +#[cfg(all(test, taget_env = "gnu"))] +mod test { + use super::*; + + #[test] + fn test_res_init() { + // This mostly just tests that the weak linkage doesn't panic wildly... + res_init_if_glibc_before_2_26().unwrap(); + } + + #[test] + fn test_parse_glibc_version() { + let cases = [ + ("0.0", Some((0, 0))), + ("01.+2", Some((1, 2))), + ("3.4.5.six", Some((3, 4))), + ("1", None), + ("1.-2", None), + ("1.foo", None), + ("foo.1", None), + ]; + for &(version_str, parsed) in cases.iter() { + assert_eq!(parsed, parse_glibc_version(version_str)); + } + } +} diff --git a/src/libstd/sys/redox/os.rs b/src/libstd/sys/vxworks/os.rs index 76e43a83b73..5dc35a6176b 100644 --- a/src/libstd/sys/redox/os.rs +++ b/src/libstd/sys/vxworks/os.rs @@ -1,72 +1,136 @@ -//! Implementation of `std::os` functionality for unix systems - -#![allow(unused_imports)] // lots of cfg code here - -use libc::c_char; - -use crate::os::unix::prelude::*; - use crate::error::Error as StdError; -use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::ffi::{CString, CStr, OsString, OsStr}; use crate::fmt; -use crate::io::{self, Read, Write}; +use crate::io; use crate::iter; +use libc::{self, c_int, c_char /*,c_void */}; use crate::marker::PhantomData; use crate::mem; use crate::memchr; -use crate::path::{self, PathBuf}; +use crate::path::{self, PathBuf, Path}; use crate::ptr; use crate::slice; use crate::str; use crate::sys_common::mutex::Mutex; -use crate::sys::{cvt, cvt_libc, fd, syscall}; +use crate::sys::cvt; +/*use sys::fd; this one is probably important */ use crate::vec; -extern { - #[link_name = "__errno_location"] - fn errno_location() -> *mut i32; +const TMPBUF_SZ: usize = 128; +static ENV_LOCK: Mutex = Mutex::new(); + + +// This is a terrible fix +use crate::sys::os_str::Buf; +use crate::sys_common::{FromInner, IntoInner, AsInner}; + +pub trait OsStringExt { + fn from_vec(vec: Vec<u8>) -> Self; + fn into_vec(self) -> Vec<u8>; } -/// Returns the platform-specific value of errno -pub fn errno() -> i32 { - unsafe { - (*errno_location()) +impl OsStringExt for OsString { + fn from_vec(vec: Vec<u8>) -> OsString { + FromInner::from_inner(Buf { inner: vec }) } + fn into_vec(self) -> Vec<u8> { + self.into_inner().inner + } +} + +pub trait OsStrExt { + fn from_bytes(slice: &[u8]) -> &Self; + fn as_bytes(&self) -> &[u8]; +} + +impl OsStrExt for OsStr { + fn from_bytes(slice: &[u8]) -> &OsStr { + unsafe { mem::transmute(slice) } + } + fn as_bytes(&self) -> &[u8] { + &self.as_inner().inner + } +} + +pub fn errno() -> i32 { + unsafe { libc::errnoGet() } +} + +pub fn set_errno(e: i32) { + unsafe { libc::errnoSet(e as c_int); } } /// Gets a detailed string description for the given error number. pub fn error_string(errno: i32) -> String { - if let Some(string) = syscall::STR_ERROR.get(errno as usize) { - string.to_string() - } else { - "unknown error".to_string() + let mut buf = [0 as c_char; TMPBUF_SZ]; + extern { + fn strerror_r( + errnum: c_int, + buf: *mut c_char, + buflen: libc::size_t + ) -> c_int; + } + + let p = buf.as_mut_ptr(); + unsafe { + if strerror_r(errno as c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + let p = p as *const _; + str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() } } pub fn getcwd() -> io::Result<PathBuf> { - let mut buf = [0; 4096]; - let count = cvt(syscall::getcwd(&mut buf))?; - Ok(PathBuf::from(OsString::from_vec(buf[.. count].to_vec()))) + let mut buf = Vec::with_capacity(512); + 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() { + let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); + buf.set_len(len); + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); + } else { + let error = io::Error::last_os_error(); + if error.raw_os_error() != Some(libc::ERANGE) { + return Err(error); + } + } + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + } + } } pub fn chdir(p: &path::Path) -> io::Result<()> { - cvt(syscall::chdir(p.to_str().unwrap())).and(Ok(())) + let p: &OsStr = p.as_ref(); + let p = CString::new(p.as_bytes())?; + unsafe { + match libc::chdir(p.as_ptr()) == (0 as c_int) { + true => Ok(()), + false => Err(io::Error::last_os_error()), + } + } } pub struct SplitPaths<'a> { iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>, - fn(&'a [u8]) -> PathBuf>, + fn(&'a [u8]) -> PathBuf>, } -pub fn split_paths(unparsed: &OsStr) -> SplitPaths { +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { fn bytes_to_path(b: &[u8]) -> PathBuf { PathBuf::from(<OsStr as OsStrExt>::from_bytes(b)) } - fn is_semicolon(b: &u8) -> bool { *b == b';' } + fn is_colon(b: &u8) -> bool { *b == b':' } let unparsed = unparsed.as_bytes(); SplitPaths { - iter: unparsed.split(is_semicolon as fn(&u8) -> bool) - .map(bytes_to_path as fn(&[u8]) -> PathBuf) + iter: unparsed.split(is_colon as fn(&u8) -> bool) + .map(bytes_to_path as fn(&[u8]) -> PathBuf) } } @@ -83,11 +147,13 @@ pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> where I: Iterator<Item=T>, T: AsRef<OsStr> { let mut joined = Vec::new(); - let sep = b';'; + let sep = b':'; for (i, path) in paths.enumerate() { let path = path.as_ref().as_bytes(); - if i > 0 { joined.push(sep) } + if i > 0 { + joined.push(sep) + } if path.contains(&sep) { return Err(JoinPathsError) } @@ -97,7 +163,7 @@ pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> } impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "path segment contains separator `:`".fmt(f) } } @@ -107,22 +173,17 @@ impl StdError for JoinPathsError { } pub fn current_exe() -> io::Result<PathBuf> { - use crate::fs::File; - - let mut file = File::open("sys:exe")?; + #[cfg(test)] + use realstd::env; - let mut path = String::new(); - file.read_to_string(&mut path)?; + #[cfg(not(test))] + use crate::env; - if path.ends_with('\n') { - path.pop(); - } - - Ok(PathBuf::from(path)) + let exe_path = env::args().next().unwrap(); + let path = Path::new(&exe_path); + path.canonicalize() } -pub static ENV_LOCK: Mutex = Mutex::new(); - pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, _dont_send_or_sync_me: PhantomData<*mut ()>, @@ -147,7 +208,7 @@ pub fn env() -> Env { let mut environ = *environ(); if environ == ptr::null() { panic!("os::env() failure getting env string from OS: {}", - io::Error::last_os_error()); + io::Error::last_os_error()); } let mut result = Vec::new(); while *environ != ptr::null() { @@ -200,7 +261,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { unsafe { let _guard = ENV_LOCK.lock(); - cvt_libc(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ()) + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ()) } } @@ -209,12 +270,14 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { unsafe { let _guard = ENV_LOCK.lock(); - cvt_libc(libc::unsetenv(nbuf.as_ptr())).map(|_| ()) + cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ()) } } pub fn page_size() -> usize { - 4096 + unsafe { + libc::sysconf(libc::_SC_PAGESIZE) as usize + } } pub fn temp_dir() -> PathBuf { @@ -224,18 +287,38 @@ pub fn temp_dir() -> PathBuf { } pub fn home_dir() -> Option<PathBuf> { - return crate::env::var_os("HOME").map(PathBuf::from); + return crate::env::var_os("HOME").or_else(|| unsafe { + fallback() + }).map(PathBuf::from); + + unsafe fn fallback() -> Option<OsString> { + let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { + n if n < 0 => 512 as usize, + n => n as usize, + }; + let mut buf = Vec::with_capacity(amt); + let mut passwd: libc::passwd = mem::zeroed(); + let mut result = ptr::null_mut(); + match libc::getpwuid_r(libc::getuid(), &mut passwd, buf.as_mut_ptr(), + buf.capacity(), &mut result) { + 0 if !result.is_null() => { + let ptr = passwd.pw_dir as *const _; + let bytes = CStr::from_ptr(ptr).to_bytes().to_vec(); + Some(OsStringExt::from_vec(bytes)) + }, + _ => None, + } + } } pub fn exit(code: i32) -> ! { - let _ = syscall::exit(code as usize); - unreachable!(); + unsafe { libc::exit(code as c_int) } } pub fn getpid() -> u32 { - syscall::getpid().unwrap() as u32 + unsafe { libc::getpid() as u32 } } pub fn getppid() -> u32 { - syscall::getppid().unwrap() as u32 + unsafe { libc::getppid() as u32 } } diff --git a/src/libstd/sys/vxworks/path.rs b/src/libstd/sys/vxworks/path.rs new file mode 100644 index 00000000000..7a183956107 --- /dev/null +++ b/src/libstd/sys/vxworks/path.rs @@ -0,0 +1,19 @@ +use crate::path::Prefix; +use crate::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: &str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/vxworks/pipe.rs b/src/libstd/sys/vxworks/pipe.rs new file mode 100644 index 00000000000..e09dbe6e99b --- /dev/null +++ b/src/libstd/sys/vxworks/pipe.rs @@ -0,0 +1,95 @@ +use crate::io::{self, IoSlice, IoSliceMut}; +use libc::{self /*, c_int apparently not used? */}; +use crate::mem; +use crate::sync::atomic::{AtomicBool}; +use crate::sys::fd::FileDesc; +use crate::sys::{cvt, cvt_r}; + +pub struct AnonPipe(FileDesc); + +pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + static INVALID: AtomicBool = AtomicBool::new(false); + + let mut fds = [0; 2]; + cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; + + let fd0 = FileDesc::new(fds[0]); + let fd1 = FileDesc::new(fds[1]); + fd0.set_cloexec()?; + fd1.set_cloexec()?; + Ok((AnonPipe(fd0), AnonPipe(fd1))) +} + +impl AnonPipe { + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + pub fn fd(&self) -> &FileDesc { &self.0 } + pub fn into_fd(self) -> FileDesc { self.0 } + pub fn diverge(&self) -> ! { + panic!() + } +} + +pub fn read2(p1: AnonPipe, + v1: &mut Vec<u8>, + p2: AnonPipe, + v2: &mut Vec<u8>) -> io::Result<()> { + + // Set both pipes into nonblocking mode as we're gonna be reading from both + // in the `select` loop below, and we wouldn't want one to block the other! + let p1 = p1.into_fd(); + let p2 = p2.into_fd(); + p1.set_nonblocking_pipe(true)?; + p2.set_nonblocking_pipe(true)?; + + let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; + fds[0].fd = p1.raw(); + fds[0].events = libc::POLLIN; + fds[1].fd = p2.raw(); + fds[1].events = libc::POLLIN; + loop { + // wait for either pipe to become readable using `poll` + cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?; + + if fds[0].revents != 0 && read(&p1, v1)? { + p2.set_nonblocking_pipe(false)?; + return p2.read_to_end(v2).map(|_| ()); + } + if fds[1].revents != 0 && read(&p2, v2)? { + p1.set_nonblocking_pipe(false)?; + return p1.read_to_end(v1).map(|_| ()); + } + } + + // Read as much as we can from each pipe, ignoring EWOULDBLOCK or + // EAGAIN. If we hit EOF, then this will happen because the underlying + // reader will return Ok(0), in which case we'll see `Ok` ourselves. In + // this case we flip the other fd back into blocking mode and read + // whatever's leftover on that file descriptor. + fn read(fd: &FileDesc, dst: &mut Vec<u8>) -> Result<bool, io::Error> { + match fd.read_to_end(dst) { + Ok(_) => Ok(true), + Err(e) => { + if e.raw_os_error() == Some(libc::EWOULDBLOCK) || + e.raw_os_error() == Some(libc::EAGAIN) { + Ok(false) + } else { + Err(e) + } + } + } + } +} diff --git a/src/libstd/sys/vxworks/process/mod.rs b/src/libstd/sys/vxworks/process/mod.rs new file mode 100644 index 00000000000..4dc706006f4 --- /dev/null +++ b/src/libstd/sys/vxworks/process/mod.rs @@ -0,0 +1,7 @@ +pub use self::process_common::{Command, ExitStatus, ExitCode, Stdio, StdioPipes}; +pub use self::process_inner::Process; + +mod process_common; +#[path = "process_vxworks.rs"] +mod process_inner; +mod rtp; diff --git a/src/libstd/sys/vxworks/process/process_common.rs b/src/libstd/sys/vxworks/process/process_common.rs new file mode 100644 index 00000000000..397200c39c2 --- /dev/null +++ b/src/libstd/sys/vxworks/process/process_common.rs @@ -0,0 +1,405 @@ +use crate::os::unix::prelude::*; + +use crate::ffi::{OsString, OsStr, CString, CStr}; +use crate::fmt; +use crate::io; +use crate::ptr; +use crate::sys::fd::FileDesc; +use crate::sys::fs::{File, OpenOptions}; +use crate::sys::pipe::{self, AnonPipe}; +use crate::sys_common::process::{CommandEnv, DefaultEnvKey}; +use crate::collections::BTreeMap; + +use libc::{c_int, gid_t, uid_t, c_char, EXIT_SUCCESS, EXIT_FAILURE}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + // Currently we try hard to ensure that the call to `.exec()` doesn't + // actually allocate any memory. While many platforms try to ensure that + // memory allocation works after a fork in a multithreaded process, it's + // been observed to be buggy and somewhat unreliable, so we do our best to + // just not do it at all! + // + // Along those lines, the `argv` and `envp` raw pointers here are exactly + // what's gonna get passed to `execvp`. The `argv` array starts with the + // `program` and ends with a NULL, and the `envp` pointer, if present, is + // also null-terminated. + // + // Right now we don't support removing arguments, so there's no much fancy + // support there, but we support adding and removing environment variables, + // so a side table is used to track where in the `envp` array each key is + // located. Whenever we add a key we update it in place if it's already + // present, and whenever we remove a key we update the locations of all + // other keys. + program: CString, + args: Vec<CString>, + argv: Argv, + env: CommandEnv<DefaultEnvKey>, + + cwd: Option<CString>, + uid: Option<uid_t>, + gid: Option<gid_t>, + saw_nul: bool, + closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>, + stdin: Option<Stdio>, + stdout: Option<Stdio>, + stderr: Option<Stdio>, +} + +// Create a new type for argv, so that we can make it `Send` +struct Argv(Vec<*const c_char>); + +// It is safe to make Argv Send, because it contains pointers to memory owned by `Command.args` +unsafe impl Send for Argv {} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option<AnonPipe>, + pub stdout: Option<AnonPipe>, + pub stderr: Option<AnonPipe>, +} + +// passed to do_exec() with configuration of what the child stdio should look +// like +pub struct ChildPipes { + pub stdin: ChildStdio, + pub stdout: ChildStdio, + pub stderr: ChildStdio, +} + +pub enum ChildStdio { + Inherit, + Explicit(c_int), + Owned(FileDesc), +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(FileDesc), +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + let mut saw_nul = false; + let program = os2c(program, &mut saw_nul); + Command { + argv: Argv(vec![program.as_ptr(), ptr::null()]), + program, + args: Vec::new(), + env: Default::default(), + cwd: None, + uid: None, + gid: None, + saw_nul, + closures: Vec::new(), + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + // Overwrite the trailing NULL pointer in `argv` and then add a new null + // pointer. + let arg = os2c(arg, &mut self.saw_nul); + self.argv.0[self.args.len() + 1] = arg.as_ptr(); + self.argv.0.push(ptr::null()); + + // Also make sure we keep track of the owned value to schedule a + // destructor for this memory. + self.args.push(arg); + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(os2c(dir, &mut self.saw_nul)); + } + pub fn uid(&mut self, id: uid_t) { + self.uid = Some(id); + } + pub fn gid(&mut self, id: gid_t) { + self.gid = Some(id); + } + + pub fn saw_nul(&self) -> bool { + self.saw_nul + } + pub fn get_argv(&self) -> &Vec<*const c_char> { + &self.argv.0 + } + + #[allow(dead_code)] + pub fn get_cwd(&self) -> &Option<CString> { + &self.cwd + } + #[allow(dead_code)] + pub fn get_uid(&self) -> Option<uid_t> { + self.uid + } + #[allow(dead_code)] + pub fn get_gid(&self) -> Option<gid_t> { + self.gid + } + + pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> { + &mut self.closures + } + + pub unsafe fn pre_exec( + &mut self, + _f: Box<dyn FnMut() -> io::Result<()> + Send + Sync>, + ) { + // Fork() is not supported in vxWorks so no way to run the closure in the new procecss. + unimplemented!();; + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> { + &mut self.env + } + + pub fn capture_env(&mut self) -> Option<CStringArray> { + let maybe_env = self.env.capture_if_changed(); + maybe_env.map(|env| construct_envp(env, &mut self.saw_nul)) + } + #[allow(dead_code)] + pub fn env_saw_path(&self) -> bool { + self.env.have_changed_path() + } + + pub fn setup_io(&self, default: Stdio, needs_stdin: bool) + -> io::Result<(StdioPipes, ChildPipes)> { + let null = Stdio::Null; + let default_stdin = if needs_stdin {&default} else {&null}; + let stdin = self.stdin.as_ref().unwrap_or(default_stdin); + let stdout = self.stdout.as_ref().unwrap_or(&default); + let stderr = self.stderr.as_ref().unwrap_or(&default); + let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; + let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; + let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; + let ours = StdioPipes { + stdin: our_stdin, + stdout: our_stdout, + stderr: our_stderr, + }; + let theirs = ChildPipes { + stdin: their_stdin, + stdout: their_stdout, + stderr: their_stderr, + }; + Ok((ours, theirs)) + } +} + +fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { + CString::new(s.as_bytes()).unwrap_or_else(|_e| { + *saw_nul = true; + CString::new("<string-with-nul>").unwrap() + }) +} + +// Helper type to manage ownership of the strings within a C-style array. +pub struct CStringArray { + items: Vec<CString>, + ptrs: Vec<*const c_char> +} + +impl CStringArray { + pub fn with_capacity(capacity: usize) -> Self { + let mut result = CStringArray { + items: Vec::with_capacity(capacity), + ptrs: Vec::with_capacity(capacity+1) + }; + result.ptrs.push(ptr::null()); + result + } + pub fn push(&mut self, item: CString) { + let l = self.ptrs.len(); + self.ptrs[l-1] = item.as_ptr(); + self.ptrs.push(ptr::null()); + self.items.push(item); + } + pub fn as_ptr(&self) -> *const *const c_char { + self.ptrs.as_ptr() + } +} + +fn construct_envp(env: BTreeMap<DefaultEnvKey, OsString>, saw_nul: &mut bool) -> CStringArray { + let mut result = CStringArray::with_capacity(env.len()); + for (k, v) in env { + let mut k: OsString = k.into(); + + // Reserve additional space for '=' and null terminator + k.reserve_exact(v.len() + 2); + k.push("="); + k.push(&v); + + // Add the new entry into the array + if let Ok(item) = CString::new(k.into_vec()) { + result.push(item); + } else { + *saw_nul = true; + } + } + + result +} + +impl Stdio { + pub fn to_child_stdio(&self, readable: bool) + -> io::Result<(ChildStdio, Option<AnonPipe>)> { + match *self { + Stdio::Inherit => { + Ok((ChildStdio::Inherit, None)) + }, + + // Make sure that the source descriptors are not an stdio + // descriptor, otherwise the order which we set the child's + // descriptors may blow away a descriptor which we are hoping to + // save. For example, suppose we want the child's stderr to be the + // parent's stdout, and the child's stdout to be the parent's + // stderr. No matter which we dup first, the second will get + // overwritten prematurely. + Stdio::Fd(ref fd) => { + if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { + Ok((ChildStdio::Owned(fd.duplicate()?), None)) + } else { + Ok((ChildStdio::Explicit(fd.raw()), None)) + } + } + + Stdio::MakePipe => { + let (reader, writer) = pipe::anon_pipe()?; + let (ours, theirs) = if readable { + (writer, reader) + } else { + (reader, writer) + }; + Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) + } + + Stdio::Null => { + let mut opts = OpenOptions::new(); + opts.read(readable); + opts.write(!readable); + let path = unsafe { + CStr::from_ptr("/null\0".as_ptr() as *const _) + }; + let fd = File::open_c(&path, &opts)?; + Ok((ChildStdio::Owned(fd.into_fd()), None)) + } + } + } +} + +impl From<AnonPipe> for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + Stdio::Fd(pipe.into_fd()) + } +} + +impl From<File> for Stdio { + fn from(file: File) -> Stdio { + Stdio::Fd(file.into_fd()) + } +} + +impl ChildStdio { + pub fn fd(&self) -> Option<c_int> { + match *self { + ChildStdio::Inherit => None, + ChildStdio::Explicit(fd) => Some(fd), + ChildStdio::Owned(ref fd) => Some(fd.raw()), + } + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.program)?; + for arg in &self.args { + write!(f, " {:?}", arg)?; + } + Ok(()) + } +} + +/// Unix exit statuses +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(c_int); + +impl ExitStatus { + pub fn new(status: c_int) -> ExitStatus { + ExitStatus(status) + } + + fn exited(&self) -> bool { + /*unsafe*/ { libc::WIFEXITED(self.0) } + } + + pub fn success(&self) -> bool { + self.code() == Some(0) + } + + pub fn code(&self) -> Option<i32> { + if self.exited() { + Some(/*unsafe*/ { libc::WEXITSTATUS(self.0) }) + } else { + None + } + } + + pub fn signal(&self) -> Option<i32> { + if !self.exited() { + Some(/*unsafe*/ { libc::WTERMSIG(self.0) }) + } else { + None + } + } +} + +impl From<c_int> for ExitStatus { + fn from(a: c_int) -> ExitStatus { + ExitStatus(a) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(code) = self.code() { + write!(f, "exit code: {}", code) + } else { + let signal = self.signal().unwrap(); + write!(f, "signal: {}", signal) + } + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(u8); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); + pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); + + #[inline] + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} diff --git a/src/libstd/sys/vxworks/process/process_vxworks.rs b/src/libstd/sys/vxworks/process/process_vxworks.rs new file mode 100644 index 00000000000..b07966fa206 --- /dev/null +++ b/src/libstd/sys/vxworks/process/process_vxworks.rs @@ -0,0 +1,156 @@ +use crate::io::{self, Error, ErrorKind}; +use libc::{self, c_int, c_char}; +use libc::{RTP_ID}; +use crate::sys; +use crate::sys::cvt; +use crate::sys::process::rtp; +use crate::sys::process::process_common::*; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + use crate::sys::{cvt_r}; + const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + + if self.saw_nul() { + return Err(io::Error::new(ErrorKind::InvalidInput, + "nul byte found in provided data")); + } + let (ours, theirs) = self.setup_io(default, needs_stdin)?; + let mut p = Process { pid: 0, status: None }; + + unsafe { + macro_rules! t { + ($e:expr) => (match $e { + Ok(e) => e, + Err(e) => return Err(e.into()), + }) + } + + let mut orig_stdin = libc::STDIN_FILENO; + let mut orig_stdout = libc::STDOUT_FILENO; + let mut orig_stderr = libc::STDERR_FILENO; + + if let Some(fd) = theirs.stdin.fd() { + orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO))); + t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); + } + if let Some(fd) = theirs.stdout.fd() { + orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO))); + t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); + } + if let Some(fd) = theirs.stderr.fd() { + orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO))); + t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); + } + + if let Some(ref cwd) = *self.get_cwd() { + t!(cvt(libc::chdir(cwd.as_ptr()))); + } + + let ret = rtp::rtpSpawn( + self.get_argv()[0], // executing program + self.get_argv().as_ptr() as *const _, // argv + *sys::os::environ() as *const *const c_char, + 100 as c_int, // initial priority + 0x16000, // initial stack size. 0 defaults + // to 0x4000 in 32 bit and 0x8000 in 64 bit + 0, // options + 0 // task options + ); + + // Because FileDesc was not used, each duplicated file descriptor + // needs to be closed manually + if orig_stdin != libc::STDIN_FILENO { + t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO))); + libc::close(orig_stdin); + } + if orig_stdout != libc::STDOUT_FILENO { + t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO))); + libc::close(orig_stdout); + } + if orig_stderr != libc::STDERR_FILENO { + t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO))); + libc::close(orig_stderr); + } + + if ret != rtp::RTP_ID_ERROR { + p.pid = ret; + Ok((p, ours)) + } else { + Err(io::Error::last_os_error()) + } + } + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + let ret = Command::spawn(self, default, false); + match ret { + Ok(t) => unsafe { + let mut status = 0 as c_int; + libc::waitpid(t.0.pid, &mut status, 0); + libc::exit(0); + }, + Err(e) => e, + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// The unique id of the process (this should never be negative). +pub struct Process { + pid: RTP_ID, + status: Option<ExitStatus>, +} + +impl Process { + pub fn id(&self) -> u32 { + self.pid as u32 + } + + pub fn kill(&mut self) -> io::Result<()> { + // If we've already waited on this process then the pid can be recycled + // and used for another process, and we probably shouldn't be killing + // random processes, so just return an error. + if self.status.is_some() { + Err(Error::new(ErrorKind::InvalidInput, + "invalid argument: can't kill an exited process")) + } else { + cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ()) + } + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + use crate::sys::cvt_r; + if let Some(status) = self.status { + return Ok(status) + } + let mut status = 0 as c_int; + cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; + self.status = Some(ExitStatus::new(status)); + Ok(ExitStatus::new(status)) + } + + pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { + if let Some(status) = self.status { + return Ok(Some(status)) + } + let mut status = 0 as c_int; + let pid = cvt(unsafe { + libc::waitpid(self.pid, &mut status, libc::WNOHANG) + })?; + if pid == 0 { + Ok(None) + } else { + self.status = Some(ExitStatus::new(status)); + Ok(Some(ExitStatus::new(status))) + } + } +} diff --git a/src/libstd/sys/vxworks/process/rtp.rs b/src/libstd/sys/vxworks/process/rtp.rs new file mode 100644 index 00000000000..3e6e0017abc --- /dev/null +++ b/src/libstd/sys/vxworks/process/rtp.rs @@ -0,0 +1,298 @@ +#![allow(non_camel_case_types, unused)] + +use libc::{self, c_int, size_t, c_char, BOOL, RTP_DESC, RTP_ID, TASK_ID}; + + +// Copied directly from rtpLibCommon.h, rtpLib.h, signal.h and taskLibCommon.h (for task options) + +// **** definitions for rtpLibCommon.h **** + +pub const RTP_GLOBAL_SYMBOLS : c_int = 0x01; // register global symbols for RTP +pub const RTP_LOCAL_SYMBOLS : c_int = 0x02; // idem for local symbols +pub const RTP_ALL_SYMBOLS : c_int = (RTP_GLOBAL_SYMBOLS | RTP_LOCAL_SYMBOLS); +pub const RTP_DEBUG : c_int = 0x10; // set RTP in debug mode when created +pub const RTP_BUFFER_VAL_OFF : c_int = 0x20; // disable buffer validation for all + // system calls issued from the RTP +pub const RTP_LOADED_WAIT : c_int = 0x40; // Wait until the RTP is loaded +pub const RTP_CPU_AFFINITY_NONE : c_int = 0x80; // Remove any CPU affinity (SMP) + +// Error Status codes + +pub const M_rtpLib : c_int = 178 << 16; + +pub const S_rtpLib_INVALID_FILE : c_int = (M_rtpLib | 1); +pub const S_rtpLib_INVALID_OPTION : c_int = (M_rtpLib | 2); +pub const S_rtpLib_ACCESS_DENIED : c_int = (M_rtpLib | 3); +pub const S_rtpLib_INVALID_RTP_ID : c_int = (M_rtpLib | 4); +pub const S_rtpLib_NO_SYMBOL_TABLE : c_int = (M_rtpLib | 5); +pub const S_rtpLib_INVALID_SEGMENT_START_ADDRESS : c_int = (M_rtpLib | 6); +pub const S_rtpLib_INVALID_SYMBOL_REGISTR_POLICY : c_int = (M_rtpLib | 7); +pub const S_rtpLib_INSTANTIATE_FAILED : c_int = (M_rtpLib | 8); +pub const S_rtpLib_INVALID_TASK_OPTION : c_int = (M_rtpLib | 9); +pub const S_rtpLib_RTP_NAME_LENGTH_EXCEEDED : c_int = (M_rtpLib | 10); // rtpInfoGet + +pub const VX_RTP_NAME_LENGTH : c_int = 255; // max name length for diplay + + +// The 'status' field (32 bit integer) of a RTP holds the RTP state and status. +// +// NOTE: RTP_STATE_GET() : read the RTP state(s) +// RTP_STATE_PUT() : write the RTP state(s) +// RTP_STATE_SET() : set a RTP state +// RTP_STATE_UNSET() : unset a RTP state +// +// RTP_STATUS_GET() : read the RTP status +// RTP_STATUS_PUT() : write the RTP status +// RTP_STATUS_SET() : set a RTP status +// RTP_STATUS_UNSET() : unset a RTP status +// +// The PUT/SET/UNSET macros are available only in the kernel headers. + + +// RTP states + +pub const RTP_STATE_CREATE : c_int = 0x0001; // RrtpStructTP is under construction +pub const RTP_STATE_NORMAL : c_int = 0x0002; // RrtpStructTP is ready +pub const RTP_STATE_DELETE : c_int = 0x0004; // RrtpStructTP is being deleted + +pub const RTP_STATUS_STOP : c_int = 0x0100; // RTP hrtpStructas recieved stopped signal +pub const RTP_STATUS_ELECTED_DELETER : c_int = 0x0200; // RTP drtpStructelete has started + +pub const RTP_STATE_MASK : c_int = (RTP_STATE_CREATE | RTP_STATE_NORMAL | + RTP_STATE_DELETE); +pub const RTP_STATUS_MASK : c_int = (RTP_STATUS_STOP | RTP_STATUS_ELECTED_DELETER); + +pub fn RTP_STATE_GET (value : c_int) -> c_int { + value & RTP_STATE_MASK +} +pub fn RTP_STATUS_GET (value : c_int) -> c_int { + value & RTP_STATUS_MASK +} + +// Indicates that the RTP_ID returned is not valid. + +// RTP_ID_ERROR is supposed to be set to -1, but you can't set +// an unsigned value to a negative without casting, and you +// can't cast unless the size of the integer types are the same, +// but the size of RTP_ID may differ between kernel and user space. +// Bitwise or-ing min and max should get the same result. +pub const RTP_ID_ERROR : RTP_ID = RTP_ID::min_value() | RTP_ID::max_value(); + +// IS_RTP_ C macros + +pub fn IS_RTP_STATE_NORMAL (value : c_int) -> bool { + (RTP_STATE_GET(value) & RTP_STATE_NORMAL) == RTP_STATE_NORMAL +} +pub fn IS_RTP_STATE_CREATE (value : c_int) -> bool { + (RTP_STATE_GET(value) & RTP_STATE_CREATE) == RTP_STATE_CREATE +} +pub fn IS_RTP_STATE_DELETE (value : c_int) -> bool { + (RTP_STATE_GET(value) & RTP_STATE_DELETE) == RTP_STATE_DELETE +} +pub fn IS_RTP_STATUS_STOP (value : c_int) -> bool { + (RTP_STATUS_GET(value) & RTP_STATUS_STOP ) == RTP_STATUS_STOP +} +pub fn IS_RTP_STATUS_ELECTED_DELETER (value : c_int) -> bool { + (RTP_STATUS_GET(value) & RTP_STATUS_ELECTED_DELETER) == RTP_STATUS_ELECTED_DELETER +} + +// **** end of definitions for rtpLibCommon.h **** + + + + +// **** definitions for rtpLib.h **** + +pub fn rtpExit(exitCode : c_int) -> ! { + unsafe{ libc::exit (exitCode) } +} + +/* rtpLib.h in the kernel +pub const RTP_DEL_VIA_TASK_DELETE : c_int = 0x1; // rtpDelete() via taskDestroy() +pub const RTP_DEL_FORCE : c_int = 0x2; // Forceful rtpDelete() +pub const RTP_ID_ANY : RTP_ID = 0; // used for when a kernel task + // wants to wait for the next + // RTP to finish + + +// Function pointers + +pub type RTP_PRE_CREATE_HOOK = size_t; +pub type RTP_POST_CREATE_HOOK = size_t; +pub type RTP_INIT_COMPLETE_HOOK = size_t; +pub type RTP_DELETE_HOOK = size_t; +*/ + +// **** end of definitions for rtpLib.h **** + + + + + +// **** definitions for signal.h **** +pub fn rtpKill(rtpId : RTP_ID, signo : c_int) -> c_int { + unsafe{ libc::kill(rtpId as c_int, signo) } +} + +pub fn rtpSigqueue(rtpId : RTP_ID, signo : c_int, value : size_t) -> c_int { + unsafe{ libc::sigqueue(rtpId as c_int, signo, value) } +} + +pub fn _rtpSigqueue(rtpId : RTP_ID, signo : c_int, value : *mut size_t, code : c_int) -> c_int { + unsafe{ libc::_sigqueue(rtpId, signo, value, code) } +} + +pub fn taskRaise(signo : c_int) -> c_int { + unsafe{ libc::taskKill(libc::taskIdSelf(), signo) } +} +pub fn rtpRaise(signo : c_int) -> c_int { + unsafe{ libc::raise(signo) } +} + +// **** end of definitions for signal.h **** + + + +// **** definitions for taskLibCommon.h **** +pub const VX_PRIVATE_ENV : c_int = 0x0080; // 1 = private environment variables +pub const VX_NO_STACK_FILL : c_int = 0x0100; // 1 = avoid stack fill of 0xee +pub const VX_PRIVATE_UMASK : c_int = 0x0400; // 1 = private file creation mode mask +pub const VX_TASK_NOACTIVATE : c_int = 0x2000; // taskOpen() does not taskActivate() +pub const VX_NO_STACK_PROTECT : c_int = 0x4000; // no over/underflow stack protection, + // stack space remains executable + +// define for all valid user task options + +pub const VX_USR_TASK_OPTIONS_BASE: c_int = (VX_PRIVATE_ENV | + VX_NO_STACK_FILL | + VX_TASK_NOACTIVATE | + VX_NO_STACK_PROTECT | + VX_PRIVATE_UMASK); + +// **** end of definitions for taskLibCommon.h **** + + + +extern "C" { +// functions in rtpLibCommon.h + +// forward declarations + pub fn rtpSpawn ( + pubrtpFileName : *const c_char, + argv : *const *const c_char, + envp : *const *const c_char, + priority : c_int, + uStackSize : size_t, + options : c_int, + taskOptions : c_int, + ) -> RTP_ID; + + pub fn rtpInfoGet ( + rtpId : RTP_ID, + rtpStruct : *mut RTP_DESC, + ) -> c_int; + +/* functions in rtpLib.h for kernel + + + // function declarations + + pub fn rtpDelete ( + id : RTP_ID, + options : c_int, + status : c_int, + ) -> c_int; + + pub fn rtpDeleteForce ( + rtpId : RTP_ID + ) -> c_int; + + pub fn rtpShow ( + rtpNameOrId : *mut c_char, + level : c_int, + ) -> BOOL; + + // RTP signals are always present when RTPs are included. The public RTP + // signal APIs are declared here. + + + pub fn rtpKill ( + rtpId : RTP_ID, + signo : c_int, + ) -> c_int; + + pub fn rtpSigqueue ( + rtpId : RTP_ID, + signo : c_int, + value : size_t, // Actual type is const union sigval value, + // which is a union of int and void * + ) -> c_int; + + pub fn rtpTaskKill ( + tid : TASK_ID, + signo : c_int, + ) -> c_int; + + pub fn rtpTaskSigqueue ( + tid : TASK_ID, + signo : c_int, + value : const size_t, // Actual type is const union sigval, + // which is a union of int and void * + ) -> c_int; + + pub fn rtpWait ( + rtpWaitId : RTP_ID, + timeout : libc::alloc_jemalloc_Vx_ticks_t, + pRtpId : *mut RTP_ID, + pStatus : *mut c_int, + ) -> c_int; + + // Other public functions + + + pub fn rtpPreCreateHookAdd ( + hook : RTP_PRE_CREATE_HOOK, + addToHead : BOOL, + ) -> c_int; + + pub fn rtpPreCreateHookDelete ( + hook : RTP_POST_CREATE_HOOK, + ) -> c_int; + + pub fn rtpPostCreateHookAdd ( + hook : RTP_POST_CREATE_HOOK, + addToHead : BOOL, + ) -> c_int; + + pub fn rtpPostCreateHookDelete ( + hook : RTP_POST_CREATE_HOOK, + ) -> c_int; + + pub fn rtpInitCompleteHookAdd ( + hook : RTP_INIT_COMPLETE_HOOK, + addToHead : BOOL, + ) -> c_int; + + pub fn rtpInitCompleteHookDelete ( + hook : RTP_INIT_COMPLETE_HOOK, + ) -> c_int; + + pub fn rtpDeleteHookAdd ( + hook : RTP_DELETE_HOOK, + addToHead : BOOL, + ) -> c_int; + + pub fn rtpDeleteHookDelete ( + hook : RTP_DELETE_HOOK, + ) -> c_int; + + pub fn rtpMemShow ( + rtpNameOrId : *mut c_char, + level : c_int, + ) -> c_int; + + pub fn rtpHookShow ( + + ); +*/ +} diff --git a/src/libstd/sys/vxworks/rand.rs b/src/libstd/sys/vxworks/rand.rs new file mode 100644 index 00000000000..1ec0cbe4dcf --- /dev/null +++ b/src/libstd/sys/vxworks/rand.rs @@ -0,0 +1,31 @@ +use crate::mem; +use crate::slice; + +pub fn hashmap_random_keys() -> (u64, u64) { + let mut v = (0, 0); + unsafe { + let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8, + mem::size_of_val(&v)); + imp::fill_bytes(view); + } + return v +} + +mod imp { + use libc; + use crate::io; + + extern "C" { + fn randBytes (randBuf: *mut libc::c_uchar, + numOfBytes: libc::c_int) -> libc::c_int; + } + + pub fn fill_bytes(v: &mut [u8]) { + let ret = unsafe { + randBytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int) + }; + if ret == -1 { + panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); + } + } +} diff --git a/src/libstd/sys/vxworks/rwlock.rs b/src/libstd/sys/vxworks/rwlock.rs new file mode 100644 index 00000000000..718f422ed11 --- /dev/null +++ b/src/libstd/sys/vxworks/rwlock.rs @@ -0,0 +1,113 @@ +use libc; +use crate::cell::UnsafeCell; +use crate::sync::atomic::{AtomicUsize, Ordering}; + +pub struct RWLock { + inner: UnsafeCell<libc::pthread_rwlock_t>, + write_locked: UnsafeCell<bool>, + num_readers: AtomicUsize, +} + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { + inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), + write_locked: UnsafeCell::new(false), + num_readers: AtomicUsize::new(0), + } + } + + #[inline] + pub unsafe fn read(&self) { + let r = libc::pthread_rwlock_rdlock(self.inner.get()); + if r == libc::EAGAIN { + panic!("rwlock maximum reader count exceeded"); + } else if r == libc::EDEADLK || *self.write_locked.get() { + if r == 0 { + self.raw_unlock(); + } + panic!("rwlock read lock would result in deadlock"); + } else { + debug_assert_eq!(r, 0); + self.num_readers.fetch_add(1, Ordering::Relaxed); + } + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); + if r == 0 { + if *self.write_locked.get() { + self.raw_unlock(); + false + } else { + self.num_readers.fetch_add(1, Ordering::Relaxed); + true + } + } else { + false + } + } + + #[inline] + pub unsafe fn write(&self) { + let r = libc::pthread_rwlock_wrlock(self.inner.get()); + // See comments above for why we check for EDEADLK and write_locked. We + // also need to check that num_readers is 0. + if r == libc::EDEADLK || *self.write_locked.get() || + self.num_readers.load(Ordering::Relaxed) != 0 { + if r == 0 { + self.raw_unlock(); + } + panic!("rwlock write lock would result in deadlock"); + } else { + debug_assert_eq!(r, 0); + } + *self.write_locked.get() = true; + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + let r = libc::pthread_rwlock_trywrlock(self.inner.get()); + if r == 0 { + if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { + self.raw_unlock(); + false + } else { + *self.write_locked.get() = true; + true + } + } else { + false + } + } + + #[inline] + unsafe fn raw_unlock(&self) { + let r = libc::pthread_rwlock_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn read_unlock(&self) { + debug_assert!(!*self.write_locked.get()); + self.num_readers.fetch_sub(1, Ordering::Relaxed); + self.raw_unlock(); + } + + #[inline] + pub unsafe fn write_unlock(&self) { + debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0); + debug_assert!(*self.write_locked.get()); + *self.write_locked.get() = false; + self.raw_unlock(); + } + #[inline] + pub unsafe fn destroy(&self) { + let r = libc::pthread_rwlock_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/vxworks/stack_overflow.rs b/src/libstd/sys/vxworks/stack_overflow.rs new file mode 100644 index 00000000000..08e7b310ca1 --- /dev/null +++ b/src/libstd/sys/vxworks/stack_overflow.rs @@ -0,0 +1,41 @@ +#![cfg_attr(test, allow(dead_code))] + +use self::imp::{make_handler, drop_handler}; + +pub use self::imp::cleanup; +pub use self::imp::init; + +pub struct Handler { + _data: *mut libc::c_void +} + +impl Handler { + pub unsafe fn new() -> Handler { + make_handler() + } +} + +impl Drop for Handler { + fn drop(&mut self) { + unsafe { + drop_handler(self); + } + } +} + +mod imp { + use crate::ptr; + + pub unsafe fn init() { + } + + pub unsafe fn cleanup() { + } + + pub unsafe fn make_handler() -> super::Handler { + super::Handler { _data: ptr::null_mut() } + } + + pub unsafe fn drop_handler(_handler: &mut super::Handler) { + } +} diff --git a/src/libstd/sys/redox/stdio.rs b/src/libstd/sys/vxworks/stdio.rs index 33f5bdbb5d3..35f163bbdb1 100644 --- a/src/libstd/sys/redox/stdio.rs +++ b/src/libstd/sys/vxworks/stdio.rs @@ -1,5 +1,4 @@ use crate::io; -use crate::sys::{cvt, syscall}; use crate::sys::fd::FileDesc; pub struct Stdin(()); @@ -12,9 +11,9 @@ impl Stdin { impl io::Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - let fd = FileDesc::new(0); + let fd = FileDesc::new(libc::STDIN_FILENO); let ret = fd.read(buf); - fd.into_raw(); + fd.into_raw(); // do not close this FD ret } } @@ -25,14 +24,14 @@ impl Stdout { impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - let fd = FileDesc::new(1); + let fd = FileDesc::new(libc::STDOUT_FILENO); let ret = fd.write(buf); - fd.into_raw(); + fd.into_raw(); // do not close this FD ret } fn flush(&mut self) -> io::Result<()> { - cvt(syscall::fsync(1)).and(Ok(())) + Ok(()) } } @@ -42,19 +41,19 @@ impl Stderr { impl io::Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - let fd = FileDesc::new(2); + let fd = FileDesc::new(libc::STDERR_FILENO); let ret = fd.write(buf); - fd.into_raw(); + fd.into_raw(); // do not close this FD ret } fn flush(&mut self) -> io::Result<()> { - cvt(syscall::fsync(2)).and(Ok(())) + Ok(()) } } pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(crate::sys::syscall::EBADF as i32) + err.raw_os_error() == Some(libc::EBADF as i32) } pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; diff --git a/src/libstd/sys/vxworks/thread.rs b/src/libstd/sys/vxworks/thread.rs new file mode 100644 index 00000000000..58af8cbe48e --- /dev/null +++ b/src/libstd/sys/vxworks/thread.rs @@ -0,0 +1,143 @@ +use crate::cmp; +use crate::ffi::CStr; +use crate::io; +use crate::mem; +use crate::ptr; +use crate::sys::os; +use crate::time::Duration; + +use crate::sys_common::thread::*; + +pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; + +pub struct Thread { + id: libc::pthread_t, +} + +// Some platforms may have pthread_t as a pointer in which case we still want +// a thread to be Send/Sync +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. +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) +} + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) + -> io::Result<Thread> { + let p = box p; + let mut native: libc::pthread_t = mem::zeroed(); + let mut attr: libc::pthread_attr_t = mem::zeroed(); + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + + let stack_size = cmp::max(stack, min_stack_size(&attr)); + + match pthread_attr_setstacksize(&mut attr, + stack_size) { + 0 => {} + n => { + assert_eq!(n, libc::EINVAL); + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = os::page_size(); + let stack_size = (stack_size + page_size - 1) & + (-(page_size as isize - 1) as usize - 1); + assert_eq!(libc::pthread_attr_setstacksize(&mut attr, + stack_size), 0); + } + }; + + let ret = libc::pthread_create(&mut native, &attr, thread_start, + &*p as *const _ as *mut _); + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + + return if ret != 0 { + Err(io::Error::from_raw_os_error(ret)) + } else { + mem::forget(p); // ownership passed to pthread_create + Ok(Thread { id: native }) + }; + + extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + unsafe { start_thread(main as *mut u8); } + ptr::null_mut() + } + } + + pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); + } + + pub fn set_name(_name: &CStr) { + assert!(false, "FIXME: set_name"); + } + + pub fn sleep(dur: Duration) { + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as _; + + // If we're awoken with a signal then the return value will be -1 and + // nanosleep will fill in `ts` with the remaining time. + unsafe { + while secs > 0 || nsecs > 0 { + let mut ts = libc::timespec { + tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t, + tv_nsec: nsecs, + }; + secs -= ts.tv_sec as u64; + if libc::nanosleep(&ts, &mut ts) == -1 { + assert_eq!(os::errno(), libc::EINTR); + secs += ts.tv_sec as u64; + nsecs = ts.tv_nsec; + } else { + nsecs = 0; + } + } + } + } + + pub fn join(self) { + unsafe { + let ret = libc::pthread_join(self.id, ptr::null_mut()); + mem::forget(self); + assert!(ret == 0, + "failed to join thread: {}", io::Error::from_raw_os_error(ret)); + } + } + + pub fn id(&self) -> libc::pthread_t { self.id } + + pub fn into_id(self) -> libc::pthread_t { + let id = self.id; + mem::forget(self); + id + } +} + +impl Drop for Thread { + fn drop(&mut self) { + let ret = unsafe { libc::pthread_detach(self.id) }; + debug_assert_eq!(ret, 0); + } +} + +#[cfg_attr(test, allow(dead_code))] +pub mod guard { + use crate::ops::Range; + pub type Guard = Range<usize>; + pub unsafe fn current() -> Option<Guard> { None } + pub unsafe fn init() -> Option<Guard> { None } + pub unsafe fn deinit() {} +} + +fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { + libc::PTHREAD_STACK_MIN +} diff --git a/src/libstd/sys/vxworks/thread_local.rs b/src/libstd/sys/vxworks/thread_local.rs new file mode 100644 index 00000000000..ac615b76b36 --- /dev/null +++ b/src/libstd/sys/vxworks/thread_local.rs @@ -0,0 +1,34 @@ +#![allow(dead_code)] // not used on all platforms + +use crate::mem; + +pub type Key = libc::pthread_key_t; + +#[inline] +pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + let mut key = 0; + assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); + key +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = libc::pthread_setspecific(key, value as *mut _); + debug_assert_eq!(r, 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + libc::pthread_getspecific(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(key: Key) { + let r = libc::pthread_key_delete(key); + debug_assert_eq!(r, 0); +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/src/libstd/sys/vxworks/time.rs b/src/libstd/sys/vxworks/time.rs new file mode 100644 index 00000000000..cb3a4241ea6 --- /dev/null +++ b/src/libstd/sys/vxworks/time.rs @@ -0,0 +1,224 @@ +use crate::cmp::Ordering; +use libc; +use crate::time::Duration; +use ::core::hash::{Hash, Hasher}; + +pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; +use crate::convert::TryInto; + +const NSEC_PER_SEC: u64 = 1_000_000_000; + +#[derive(Copy, Clone)] +struct Timespec { + t: libc::timespec, +} + +impl Timespec { + const fn zero() -> Timespec { + Timespec { + t: libc::timespec { tv_sec: 0, tv_nsec: 0 }, + } + } + fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { + if self >= other { + Ok(if self.t.tv_nsec >= other.t.tv_nsec { + Duration::new((self.t.tv_sec - other.t.tv_sec) as u64, + (self.t.tv_nsec - other.t.tv_nsec) as u32) + } else { + Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64, + self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - + other.t.tv_nsec as u32) + }) + } else { + match other.sub_timespec(self) { + Ok(d) => Err(d), + Err(d) => Ok(d), + } + } + } + + fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { + let mut secs = other + .as_secs() + .try_into() // <- target type would be `libc::time_t` + .ok() + .and_then(|secs| self.t.tv_sec.checked_add(secs))?; + + // Nano calculations can't overflow because nanos are <1B which fit + // in a u32. + let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; + if nsec >= NSEC_PER_SEC as u32 { + nsec -= NSEC_PER_SEC as u32; + secs = secs.checked_add(1)?; + } + Some(Timespec { + t: libc::timespec { + tv_sec: secs, + tv_nsec: nsec as _, + }, + }) + } + + fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { + let mut secs = other + .as_secs() + .try_into() // <- target type would be `libc::time_t` + .ok() + .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; + + // Similar to above, nanos can't overflow. + let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; + if nsec < 0 { + nsec += NSEC_PER_SEC as i32; + secs = secs.checked_sub(1)?; + } + Some(Timespec { + t: libc::timespec { + tv_sec: secs, + tv_nsec: nsec as _, + }, + }) + } +} + +impl PartialEq for Timespec { + fn eq(&self, other: &Timespec) -> bool { + self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec + } +} + +impl Eq for Timespec {} + +impl PartialOrd for Timespec { + fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for Timespec { + fn cmp(&self, other: &Timespec) -> Ordering { + let me = (self.t.tv_sec, self.t.tv_nsec); + let other = (other.t.tv_sec, other.t.tv_nsec); + me.cmp(&other) + } +} + +impl Hash for Timespec { + fn hash<H : Hasher>(&self, state: &mut H) { + self.t.tv_sec.hash(state); + self.t.tv_nsec.hash(state); + } +} +mod inner { + use crate::fmt; + use libc; + use crate::sys::cvt; + use crate::time::Duration; + + use super::Timespec; + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Instant { + t: Timespec, + } + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct SystemTime { + t: Timespec, + } + + pub const UNIX_EPOCH: SystemTime = SystemTime { + t: Timespec { + t: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }, + }, + }; + + impl Instant { + pub fn now() -> Instant { + Instant { t: now(libc::CLOCK_MONOTONIC) } + } + + pub const fn zero() -> Instant { + Instant { + t: Timespec::zero(), + } + } + + pub fn actually_monotonic() -> bool { + true + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + self.t.sub_timespec(&other.t).ok() + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_sub_duration(other)? }) + } + } + + impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Instant") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } + } + + impl SystemTime { + pub fn now() -> SystemTime { + SystemTime { t: now(libc::CLOCK_REALTIME) } + } + + pub fn sub_time(&self, other: &SystemTime) + -> Result<Duration, Duration> { + self.t.sub_timespec(&other.t) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + } + } + + impl From<libc::timespec> for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { t: t } } + } + } + + impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SystemTime") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } + } + + pub type clock_t = libc::c_int; + + fn now(clock: clock_t) -> Timespec { + let mut t = Timespec { + t: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + } + }; + cvt(unsafe { + libc::clock_gettime(clock, &mut t.t) + }).unwrap(); + t + } +} diff --git a/src/libstd/sys/vxworks/weak.rs b/src/libstd/sys/vxworks/weak.rs new file mode 100644 index 00000000000..284f2116423 --- /dev/null +++ b/src/libstd/sys/vxworks/weak.rs @@ -0,0 +1,60 @@ +//! Support for "weak linkage" to symbols on Unix +//! +//! Some I/O operations we do in libstd require newer versions of OSes but we +//! need to maintain binary compatibility with older releases for now. In order +//! to use the new functionality when available we use this module for +//! detection. +//! +//! One option to use here is weak linkage, but that is unfortunately only +//! really workable on Linux. Hence, use dlsym to get the symbol value at +//! runtime. This is also done for compatibility with older versions of glibc, +//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that +//! we've been dynamically linked to the library the symbol comes from, but that +//! is currently always the case for things like libpthread/libc. +//! +//! A long time ago this used weak linkage for the __pthread_get_minstack +//! symbol, but that caused Debian to detect an unnecessarily strict versioned +//! dependency on libc6 (#23628). + +use crate::ffi::CStr; +use crate::marker; +use crate::mem; +use crate::sync::atomic::{AtomicUsize, Ordering}; + +pub struct Weak<F> { + name: &'static str, + addr: AtomicUsize, + _marker: marker::PhantomData<F>, +} + +impl<F> Weak<F> { + pub const fn new(name: &'static str) -> Weak<F> { + Weak { + name, + addr: AtomicUsize::new(1), + _marker: marker::PhantomData, + } + } + + pub fn get(&self) -> Option<F> { + assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>()); + unsafe { + if self.addr.load(Ordering::SeqCst) == 1 { + self.addr.store(fetch(self.name), Ordering::SeqCst); + } + match self.addr.load(Ordering::SeqCst) { + 0 => None, + addr => Some(mem::transmute_copy::<usize, F>(&addr)), + } + } + } +} + +unsafe fn fetch(name: &str) -> usize { + let name = match CStr::from_bytes_with_nul(name.as_bytes()) { + Ok(cstr) => cstr, + Err(..) => return 0, + }; + assert!(false, "FIXME: fetch"); + libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize +} diff --git a/src/libstd/sys/wasi/alloc.rs b/src/libstd/sys/wasi/alloc.rs new file mode 100644 index 00000000000..c8529937bbd --- /dev/null +++ b/src/libstd/sys/wasi/alloc.rs @@ -0,0 +1,43 @@ +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ptr; +use crate::sys_common::alloc::{MIN_ALIGN, realloc_fallback}; +use libc; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::malloc(layout.size()) as *mut u8 + } else { + libc::aligned_alloc(layout.size(), layout.align()) as *mut u8 + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::calloc(layout.size(), 1) as *mut u8 + } else { + let ptr = self.alloc(layout.clone()); + if !ptr.is_null() { + ptr::write_bytes(ptr, 0, layout.size()); + } + ptr + } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + libc::free(ptr as *mut libc::c_void) + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + } else { + realloc_fallback(self, ptr, layout, new_size) + } + } +} diff --git a/src/libstd/sys/wasi/args.rs b/src/libstd/sys/wasi/args.rs new file mode 100644 index 00000000000..8b4b354d9fc --- /dev/null +++ b/src/libstd/sys/wasi/args.rs @@ -0,0 +1,76 @@ +use crate::ffi::CStr; +use crate::io; +use crate::sys::cvt_wasi; +use crate::ffi::OsString; +use crate::marker::PhantomData; +use crate::os::wasi::ffi::OsStringExt; +use crate::vec; + +pub unsafe fn init(_argc: isize, _argv: *const *const u8) { +} + +pub unsafe fn cleanup() { +} + +pub struct Args { + iter: vec::IntoIter<OsString>, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +/// Returns the command line arguments +pub fn args() -> Args { + maybe_args().unwrap_or_else(|_| { + Args { + iter: Vec::new().into_iter(), + _dont_send_or_sync_me: PhantomData + } + }) +} + +fn maybe_args() -> io::Result<Args> { + unsafe { + let (mut argc, mut argv_buf_size) = (0, 0); + cvt_wasi(libc::__wasi_args_sizes_get(&mut argc, &mut argv_buf_size))?; + + let mut argc = vec![core::ptr::null_mut::<libc::c_char>(); argc]; + let mut argv_buf = vec![0; argv_buf_size]; + cvt_wasi(libc::__wasi_args_get(argc.as_mut_ptr(), argv_buf.as_mut_ptr()))?; + + let args = argc.into_iter() + .map(|ptr| CStr::from_ptr(ptr).to_bytes().to_vec()) + .map(|bytes| OsString::from_vec(bytes)) + .collect::<Vec<_>>(); + Ok(Args { + iter: args.into_iter(), + _dont_send_or_sync_me: PhantomData, + }) + } +} + +impl Args { + pub fn inner_debug(&self) -> &[OsString] { + self.iter.as_slice() + } +} + +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() + } +} diff --git a/src/libstd/sys/wasi/env.rs b/src/libstd/sys/wasi/env.rs new file mode 100644 index 00000000000..730e356d7fe --- /dev/null +++ b/src/libstd/sys/wasi/env.rs @@ -0,0 +1,9 @@ +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".wasm"; + pub const DLL_EXTENSION: &str = "wasm"; + pub const EXE_SUFFIX: &str = ".wasm"; + pub const EXE_EXTENSION: &str = "wasm"; +} diff --git a/src/libstd/sys/wasi/ext/ffi.rs b/src/libstd/sys/wasi/ext/ffi.rs new file mode 100644 index 00000000000..f71f316d1ba --- /dev/null +++ b/src/libstd/sys/wasi/ext/ffi.rs @@ -0,0 +1,6 @@ +//! WASI-specific extension to the primitives in the `std::ffi` module + +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys_common::os_str_bytes::*; diff --git a/src/libstd/sys/wasi/ext/fs.rs b/src/libstd/sys/wasi/ext/fs.rs new file mode 100644 index 00000000000..0ec4122f385 --- /dev/null +++ b/src/libstd/sys/wasi/ext/fs.rs @@ -0,0 +1,412 @@ +//! WASI-specific extensions to primitives in the `std::fs` module. + +#![unstable(feature = "wasi_ext", issue = "0")] + +use crate::fs::{self, File, Metadata, OpenOptions}; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::os::wasi::ffi::OsStrExt; +use crate::path::{Path, PathBuf}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner}; + +/// WASI-specific extensions to [`File`]. +/// +/// [`File`]: ../../../../std/fs/struct.File.html +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_vectored`], it is not an error to + /// return with a short read. + /// + /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read_vectored + fn read_at(&self, bufs: &mut [IoSliceMut<'_>], 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 appropriately + /// extended and the intermediate bytes are initialized with the value 0. + /// + /// Note that similar to [`File::write_vectored`], it is not an error to return a + /// short write. + /// + /// [`File::write`]: ../../../../std/fs/struct.File.html#method.write_vectored + fn write_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>; + + /// Returns the current position within the file. + /// + /// This corresponds to the `__wasi_fd_tell` syscall and is similar to + /// `seek` where you offset 0 bytes from the current position. + fn tell(&self) -> io::Result<u64>; + + /// Adjust the flags associated with this file. + /// + /// This corresponds to the `__wasi_fd_fdstat_set_flags` syscall. + fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>; + + /// Adjust the rights associated with this file. + /// + /// This corresponds to the `__wasi_fd_fdstat_set_rights` syscall. + fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>; + + /// Provide file advisory information on a file descriptor. + /// + /// This corresponds to the `__wasi_fd_advise` syscall. + fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>; + + /// Force the allocation of space in a file. + /// + /// This corresponds to the `__wasi_fd_allocate` syscall. + fn allocate(&self, offset: u64, len: u64) -> io::Result<()>; + + /// Create a directory. + /// + /// This corresponds to the `__wasi_path_create_directory` syscall. + fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()>; + + /// Read the contents of a symbolic link. + /// + /// This corresponds to the `__wasi_path_readlink` syscall. + fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf>; + + /// Return the attributes of a file or directory. + /// + /// This corresponds to the `__wasi_path_filestat_get` syscall. + fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata>; + + /// Unlink a file. + /// + /// This corresponds to the `__wasi_path_unlink_file` syscall. + fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()>; + + /// Remove a directory. + /// + /// This corresponds to the `__wasi_path_remove_directory` syscall. + fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()>; +} + +// FIXME: bind __wasi_fd_fdstat_get - need to define a custom return type +// FIXME: bind __wasi_fd_readdir - can't return `ReadDir` since we only have entry name +// FIXME: bind __wasi_fd_filestat_set_times maybe? - on crates.io for unix +// FIXME: bind __wasi_path_filestat_set_times maybe? - on crates.io for unix +// FIXME: bind __wasi_poll_oneoff maybe? - probably should wait for I/O to settle +// FIXME: bind __wasi_random_get maybe? - on crates.io for unix + +impl FileExt for fs::File { + fn read_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + self.as_inner().fd().pread(bufs, offset) + } + + fn write_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + self.as_inner().fd().pwrite(bufs, offset) + } + + fn tell(&self) -> io::Result<u64> { + self.as_inner().fd().tell() + } + + fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> { + self.as_inner().fd().set_flags(flags) + } + + fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> { + self.as_inner().fd().set_rights(rights, inheriting) + } + + fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { + self.as_inner().fd().advise(offset, len, advice) + } + + fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { + self.as_inner().fd().allocate(offset, len) + } + + fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()> { + self.as_inner() + .fd() + .create_directory(dir.as_ref().as_os_str().as_bytes()) + } + + fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> { + self.as_inner().read_link(path.as_ref()) + } + + fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata> { + let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?; + Ok(FromInner::from_inner(m)) + } + + fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { + self.as_inner() + .fd() + .unlink_file(path.as_ref().as_os_str().as_bytes()) + } + + fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { + self.as_inner() + .fd() + .remove_directory(path.as_ref().as_os_str().as_bytes()) + } +} + +/// WASI-specific extensions to [`fs::OpenOptions`]. +/// +/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html +pub trait OpenOptionsExt { + /// Pass custom `dirflags` argument to `__wasi_path_open`. + /// + /// This option configures the `dirflags` argument to the + /// `__wasi_path_open` syscall which `OpenOptions` will eventually call. The + /// `dirflags` argument configures how the file is looked up, currently + /// primarily affecting whether symlinks are followed or not. + /// + /// By default this value is `__WASI_LOOKUP_SYMLINK_FOLLOW`, or symlinks are + /// followed. You can call this method with 0 to disable following symlinks + fn lookup_flags(&mut self, flags: u32) -> &mut Self; + + /// Indicates whether `OpenOptions` must open a directory or not. + /// + /// This method will configure whether the `__WASI_O_DIRECTORY` flag is + /// passed when opening a file. When passed it will require that the opened + /// path is a directory. + /// + /// This option is by default `false` + fn directory(&mut self, dir: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags` + /// field of `__wasi_path_open`. + /// + /// This option is by default `false` + fn dsync(&mut self, dsync: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags` + /// field of `__wasi_path_open`. + /// + /// This option is by default `false` + fn nonblock(&mut self, nonblock: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags` + /// field of `__wasi_path_open`. + /// + /// This option is by default `false` + fn rsync(&mut self, rsync: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags` + /// field of `__wasi_path_open`. + /// + /// This option is by default `false` + fn sync(&mut self, sync: bool) -> &mut Self; + + /// Indicates the value that should be passed in for the `fs_rights_base` + /// parameter of `__wasi_path_open`. + /// + /// This option defaults based on the `read` and `write` configuration of + /// this `OpenOptions` builder. If this method is called, however, the + /// exact mask passed in will be used instead. + fn fs_rights_base(&mut self, rights: u64) -> &mut Self; + + /// Indicates the value that should be passed in for the + /// `fs_rights_inheriting` parameter of `__wasi_path_open`. + /// + /// The default for this option is the same value as what will be passed + /// for the `fs_rights_base` parameter but if this method is called then + /// the specified value will be used instead. + fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self; + + /// Open a file or directory. + /// + /// This corresponds to the `__wasi_path_open` syscall. + fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File>; +} + +impl OpenOptionsExt for OpenOptions { + fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions { + self.as_inner_mut().lookup_flags(flags); + self + } + + fn directory(&mut self, dir: bool) -> &mut OpenOptions { + self.as_inner_mut().directory(dir); + self + } + + fn dsync(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().dsync(enabled); + self + } + + fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().nonblock(enabled); + self + } + + fn rsync(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().rsync(enabled); + self + } + + fn sync(&mut self, enabled: bool) -> &mut OpenOptions { + self.as_inner_mut().sync(enabled); + self + } + + fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions { + self.as_inner_mut().fs_rights_base(rights); + self + } + + fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions { + self.as_inner_mut().fs_rights_inheriting(rights); + self + } + + fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File> { + let inner = file.as_inner().open_at(path.as_ref(), self.as_inner())?; + Ok(File::from_inner(inner)) + } +} + +/// WASI-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html +pub trait MetadataExt { + /// Returns the `st_dev` field of the internal `__wasi_filestat_t` + fn dev(&self) -> u64; + /// Returns the `st_ino` field of the internal `__wasi_filestat_t` + fn ino(&self) -> u64; + /// Returns the `st_nlink` field of the internal `__wasi_filestat_t` + fn nlink(&self) -> u32; + /// Returns the `st_atim` field of the internal `__wasi_filestat_t` + fn atim(&self) -> u64; + /// Returns the `st_mtim` field of the internal `__wasi_filestat_t` + fn mtim(&self) -> u64; + /// Returns the `st_ctim` field of the internal `__wasi_filestat_t` + fn ctim(&self) -> u64; +} + +impl MetadataExt for fs::Metadata { + fn dev(&self) -> u64 { + self.as_inner().as_wasi().st_dev + } + fn ino(&self) -> u64 { + self.as_inner().as_wasi().st_ino + } + fn nlink(&self) -> u32 { + self.as_inner().as_wasi().st_nlink + } + fn atim(&self) -> u64 { + self.as_inner().as_wasi().st_atim + } + fn mtim(&self) -> u64 { + self.as_inner().as_wasi().st_mtim + } + fn ctim(&self) -> u64 { + self.as_inner().as_wasi().st_ctim + } +} + +/// WASI-specific extensions for [`FileType`]. +/// +/// Adds support for special WASI file types such as block/character devices, +/// pipes, and sockets. +/// +/// [`FileType`]: ../../../../std/fs/struct.FileType.html +pub trait FileTypeExt { + /// Returns `true` if this file type is a block device. + fn is_block_device(&self) -> bool; + /// Returns `true` if this file type is a character device. + fn is_character_device(&self) -> bool; + /// Returns `true` if this file type is a socket datagram. + fn is_socket_dgram(&self) -> bool; + /// Returns `true` if this file type is a socket stream. + fn is_socket_stream(&self) -> bool; +} + +impl FileTypeExt for fs::FileType { + fn is_block_device(&self) -> bool { + self.as_inner().bits() == libc::__WASI_FILETYPE_BLOCK_DEVICE + } + fn is_character_device(&self) -> bool { + self.as_inner().bits() == libc::__WASI_FILETYPE_CHARACTER_DEVICE + } + fn is_socket_dgram(&self) -> bool { + self.as_inner().bits() == libc::__WASI_FILETYPE_SOCKET_DGRAM + } + fn is_socket_stream(&self) -> bool { + self.as_inner().bits() == libc::__WASI_FILETYPE_SOCKET_STREAM + } +} + +/// WASI-specific extension methods for [`fs::DirEntry`]. +/// +/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html +pub trait DirEntryExt { + /// Returns the underlying `d_ino` field of the `__wasi_dirent_t` + fn ino(&self) -> u64; +} + +impl DirEntryExt for fs::DirEntry { + fn ino(&self) -> u64 { + self.as_inner().ino() + } +} + +/// Create a hard link. +/// +/// This corresponds to the `__wasi_path_link` syscall. +pub fn link<P: AsRef<Path>, U: AsRef<Path>>( + old_fd: &File, + old_flags: u32, + old_path: P, + new_fd: &File, + new_path: U, +) -> io::Result<()> { + old_fd.as_inner().fd().link( + old_flags, + old_path.as_ref().as_os_str().as_bytes(), + new_fd.as_inner().fd(), + new_path.as_ref().as_os_str().as_bytes(), + ) +} + +/// Rename a file or directory. +/// +/// This corresponds to the `__wasi_path_rename` syscall. +pub fn rename<P: AsRef<Path>, U: AsRef<Path>>( + old_fd: &File, + old_path: P, + new_fd: &File, + new_path: U, +) -> io::Result<()> { + old_fd.as_inner().fd().rename( + old_path.as_ref().as_os_str().as_bytes(), + new_fd.as_inner().fd(), + new_path.as_ref().as_os_str().as_bytes(), + ) +} + +/// Create a symbolic link. +/// +/// This corresponds to the `__wasi_path_symlink` syscall. +pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>( + old_path: P, + fd: &File, + new_path: U, +) -> io::Result<()> { + fd.as_inner().fd().symlink( + old_path.as_ref().as_os_str().as_bytes(), + new_path.as_ref().as_os_str().as_bytes(), + ) +} diff --git a/src/libstd/sys/wasi/ext/io.rs b/src/libstd/sys/wasi/ext/io.rs new file mode 100644 index 00000000000..12afd1d42dc --- /dev/null +++ b/src/libstd/sys/wasi/ext/io.rs @@ -0,0 +1,142 @@ +//! WASI-specific extensions to general I/O primitives + +#![unstable(feature = "wasi_ext", issue = "0")] + +use crate::fs; +use crate::io; +use crate::sys; +use crate::net; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// Raw file descriptors. +pub type RawFd = u32; + +/// A trait to extract the raw WASI file descriptor from an underlying +/// object. +pub trait AsRawFd { + /// Extracts the raw file descriptor. + /// + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guaranteed to be valid while + /// the original object has not yet been destroyed. + fn as_raw_fd(&self) -> RawFd; +} + +/// A trait to express the ability to construct an object from a raw file +/// descriptor. +pub trait FromRawFd { + /// Constructs a new instance of `Self` from the given raw file + /// descriptor. + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw file descriptor. +pub trait IntoRawFd { + /// Consumes this object, returning the raw underlying file descriptor. + /// + /// This function **transfers ownership** of the underlying file descriptor + /// to the caller. Callers are then the unique owners of the file descriptor + /// and must close the descriptor once it's no longer needed. + fn into_raw_fd(self) -> RawFd; +} + +impl AsRawFd for net::TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().as_raw() + } +} + +impl FromRawFd for net::TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { + net::TcpStream::from_inner(sys::net::TcpStream::from_inner(fd)) + } +} + +impl IntoRawFd for net::TcpStream { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +impl AsRawFd for net::TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().as_raw() + } +} + +impl FromRawFd for net::TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { + net::TcpListener::from_inner(sys::net::TcpListener::from_inner(fd)) + } +} + +impl IntoRawFd for net::TcpListener { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +impl AsRawFd for net::UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().as_raw() + } +} + +impl FromRawFd for net::UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { + net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(fd)) + } +} + +impl IntoRawFd for net::UdpSocket { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +impl AsRawFd for fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().as_raw() + } +} + +impl FromRawFd for fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> fs::File { + fs::File::from_inner(sys::fs::File::from_inner(fd)) + } +} + +impl IntoRawFd for fs::File { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +impl AsRawFd for io::Stdin { + fn as_raw_fd(&self) -> RawFd { + libc::STDIN_FILENO as u32 + } +} + +impl AsRawFd for io::Stdout { + fn as_raw_fd(&self) -> RawFd { + libc::STDOUT_FILENO as u32 + } +} + +impl AsRawFd for io::Stderr { + fn as_raw_fd(&self) -> RawFd { + libc::STDERR_FILENO as u32 + } +} diff --git a/src/libstd/sys/wasi/ext/mod.rs b/src/libstd/sys/wasi/ext/mod.rs new file mode 100644 index 00000000000..1c24b244b8c --- /dev/null +++ b/src/libstd/sys/wasi/ext/mod.rs @@ -0,0 +1,18 @@ +pub mod ffi; +pub mod fs; +pub mod io; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::ffi::{OsStringExt, OsStrExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::fs::{FileExt, DirEntryExt, MetadataExt, OpenOptionsExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::fs::FileTypeExt; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use crate::sys::ext::io::{AsRawFd, IntoRawFd, FromRawFd}; +} diff --git a/src/libstd/sys/wasi/fd.rs b/src/libstd/sys/wasi/fd.rs new file mode 100644 index 00000000000..25692ec0868 --- /dev/null +++ b/src/libstd/sys/wasi/fd.rs @@ -0,0 +1,350 @@ +#![allow(dead_code)] + +use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::net::Shutdown; +use crate::sys::cvt_wasi; +use libc::{self, c_char, c_void}; + +#[derive(Debug)] +pub struct WasiFd { + fd: libc::__wasi_fd_t, +} + +// FIXME: these should probably all be fancier structs, builders, enums, etc +pub type LookupFlags = u32; +pub type FdFlags = u16; +pub type Advice = u8; +pub type Rights = u64; +pub type Oflags = u16; +pub type DirCookie = u64; +pub type Timestamp = u64; +pub type FstFlags = u16; +pub type RiFlags = u16; +pub type RoFlags = u16; +pub type SiFlags = u16; + +fn iovec(a: &mut [IoSliceMut<'_>]) -> (*const libc::__wasi_iovec_t, usize) { + assert_eq!( + mem::size_of::<IoSliceMut<'_>>(), + mem::size_of::<libc::__wasi_iovec_t>() + ); + assert_eq!( + mem::align_of::<IoSliceMut<'_>>(), + mem::align_of::<libc::__wasi_iovec_t>() + ); + (a.as_ptr() as *const libc::__wasi_iovec_t, a.len()) +} + +fn ciovec(a: &[IoSlice<'_>]) -> (*const libc::__wasi_ciovec_t, usize) { + assert_eq!( + mem::size_of::<IoSlice<'_>>(), + mem::size_of::<libc::__wasi_ciovec_t>() + ); + assert_eq!( + mem::align_of::<IoSlice<'_>>(), + mem::align_of::<libc::__wasi_ciovec_t>() + ); + (a.as_ptr() as *const libc::__wasi_ciovec_t, a.len()) +} + +impl WasiFd { + pub unsafe fn from_raw(fd: libc::__wasi_fd_t) -> WasiFd { + WasiFd { fd } + } + + pub fn into_raw(self) -> libc::__wasi_fd_t { + let ret = self.fd; + mem::forget(self); + ret + } + + pub fn as_raw(&self) -> libc::__wasi_fd_t { + self.fd + } + + pub fn datasync(&self) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_datasync(self.fd) }) + } + + pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + let mut read = 0; + let (ptr, len) = iovec(bufs); + cvt_wasi(unsafe { libc::__wasi_fd_pread(self.fd, ptr, len, offset, &mut read) })?; + Ok(read) + } + + pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + let mut read = 0; + let (ptr, len) = ciovec(bufs); + cvt_wasi(unsafe { libc::__wasi_fd_pwrite(self.fd, ptr, len, offset, &mut read) })?; + Ok(read) + } + + pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + let mut read = 0; + let (ptr, len) = iovec(bufs); + cvt_wasi(unsafe { libc::__wasi_fd_read(self.fd, ptr, len, &mut read) })?; + Ok(read) + } + + pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let mut read = 0; + let (ptr, len) = ciovec(bufs); + cvt_wasi(unsafe { libc::__wasi_fd_write(self.fd, ptr, len, &mut read) })?; + Ok(read) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { + let (whence, offset) = match pos { + SeekFrom::Start(pos) => (libc::__WASI_WHENCE_SET, pos as i64), + SeekFrom::End(pos) => (libc::__WASI_WHENCE_END, pos), + SeekFrom::Current(pos) => (libc::__WASI_WHENCE_CUR, pos), + }; + let mut pos = 0; + cvt_wasi(unsafe { libc::__wasi_fd_seek(self.fd, offset, whence, &mut pos) })?; + Ok(pos) + } + + pub fn tell(&self) -> io::Result<u64> { + let mut pos = 0; + cvt_wasi(unsafe { libc::__wasi_fd_tell(self.fd, &mut pos) })?; + Ok(pos) + } + + // FIXME: __wasi_fd_fdstat_get + + pub fn set_flags(&self, flags: FdFlags) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_fdstat_set_flags(self.fd, flags) }) + } + + pub fn set_rights(&self, base: Rights, inheriting: Rights) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_fdstat_set_rights(self.fd, base, inheriting) }) + } + + pub fn sync(&self) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_sync(self.fd) }) + } + + pub fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_advise(self.fd, offset, len, advice as u8) }) + } + + pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_allocate(self.fd, offset, len) }) + } + + pub fn create_directory(&self, path: &[u8]) -> io::Result<()> { + cvt_wasi(unsafe { + libc::__wasi_path_create_directory(self.fd, path.as_ptr() as *const c_char, path.len()) + }) + } + + pub fn link( + &self, + old_flags: LookupFlags, + old_path: &[u8], + new_fd: &WasiFd, + new_path: &[u8], + ) -> io::Result<()> { + cvt_wasi(unsafe { + libc::__wasi_path_link( + self.fd, + old_flags, + old_path.as_ptr() as *const c_char, + old_path.len(), + new_fd.fd, + new_path.as_ptr() as *const c_char, + new_path.len(), + ) + }) + } + + pub fn open( + &self, + dirflags: LookupFlags, + path: &[u8], + oflags: Oflags, + fs_rights_base: Rights, + fs_rights_inheriting: Rights, + fs_flags: FdFlags, + ) -> io::Result<WasiFd> { + unsafe { + let mut fd = 0; + cvt_wasi(libc::__wasi_path_open( + self.fd, + dirflags, + path.as_ptr() as *const c_char, + path.len(), + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + &mut fd, + ))?; + Ok(WasiFd::from_raw(fd)) + } + } + + pub fn readdir(&self, buf: &mut [u8], cookie: DirCookie) -> io::Result<usize> { + let mut used = 0; + cvt_wasi(unsafe { + libc::__wasi_fd_readdir( + self.fd, + buf.as_mut_ptr() as *mut c_void, + buf.len(), + cookie, + &mut used, + ) + })?; + Ok(used) + } + + pub fn readlink(&self, path: &[u8], buf: &mut [u8]) -> io::Result<usize> { + let mut used = 0; + cvt_wasi(unsafe { + libc::__wasi_path_readlink( + self.fd, + path.as_ptr() as *const c_char, + path.len(), + buf.as_mut_ptr() as *mut c_char, + buf.len(), + &mut used, + ) + })?; + Ok(used) + } + + pub fn rename(&self, old_path: &[u8], new_fd: &WasiFd, new_path: &[u8]) -> io::Result<()> { + cvt_wasi(unsafe { + libc::__wasi_path_rename( + self.fd, + old_path.as_ptr() as *const c_char, + old_path.len(), + new_fd.fd, + new_path.as_ptr() as *const c_char, + new_path.len(), + ) + }) + } + + pub fn filestat_get(&self, buf: *mut libc::__wasi_filestat_t) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_filestat_get(self.fd, buf) }) + } + + pub fn filestat_set_times( + &self, + atim: Timestamp, + mtim: Timestamp, + fstflags: FstFlags, + ) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_filestat_set_times(self.fd, atim, mtim, fstflags) }) + } + + pub fn filestat_set_size(&self, size: u64) -> io::Result<()> { + cvt_wasi(unsafe { libc::__wasi_fd_filestat_set_size(self.fd, size) }) + } + + pub fn path_filestat_get( + &self, + flags: LookupFlags, + path: &[u8], + buf: *mut libc::__wasi_filestat_t, + ) -> io::Result<()> { + cvt_wasi(unsafe { + libc::__wasi_path_filestat_get( + self.fd, + flags, + path.as_ptr() as *const c_char, + path.len(), + buf, + ) + }) + } + + pub fn path_filestat_set_times( + &self, + flags: LookupFlags, + path: &[u8], + atim: Timestamp, + mtim: Timestamp, + fstflags: FstFlags, + ) -> io::Result<()> { + cvt_wasi(unsafe { + libc::__wasi_path_filestat_set_times( + self.fd, + flags, + path.as_ptr() as *const c_char, + path.len(), + atim, + mtim, + fstflags, + ) + }) + } + + pub fn symlink(&self, old_path: &[u8], new_path: &[u8]) -> io::Result<()> { + cvt_wasi(unsafe { + libc::__wasi_path_symlink( + old_path.as_ptr() as *const c_char, + old_path.len(), + self.fd, + new_path.as_ptr() as *const c_char, + new_path.len(), + ) + }) + } + + pub fn unlink_file(&self, path: &[u8]) -> io::Result<()> { + cvt_wasi(unsafe { + libc::__wasi_path_unlink_file(self.fd, path.as_ptr() as *const c_char, path.len()) + }) + } + + pub fn remove_directory(&self, path: &[u8]) -> io::Result<()> { + cvt_wasi(unsafe { + libc::__wasi_path_remove_directory(self.fd, path.as_ptr() as *const c_char, path.len()) + }) + } + + pub fn sock_recv( + &self, + ri_data: &mut [IoSliceMut<'_>], + ri_flags: RiFlags, + ) -> io::Result<(usize, RoFlags)> { + let mut ro_datalen = 0; + let mut ro_flags = 0; + let (ptr, len) = iovec(ri_data); + cvt_wasi(unsafe { + libc::__wasi_sock_recv(self.fd, ptr, len, ri_flags, &mut ro_datalen, &mut ro_flags) + })?; + Ok((ro_datalen, ro_flags)) + } + + pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: SiFlags) -> io::Result<usize> { + let mut so_datalen = 0; + let (ptr, len) = ciovec(si_data); + cvt_wasi(unsafe { libc::__wasi_sock_send(self.fd, ptr, len, si_flags, &mut so_datalen) })?; + Ok(so_datalen) + } + + pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Read => libc::__WASI_SHUT_RD, + Shutdown::Write => libc::__WASI_SHUT_WR, + Shutdown::Both => libc::__WASI_SHUT_WR | libc::__WASI_SHUT_RD, + }; + cvt_wasi(unsafe { libc::__wasi_sock_shutdown(self.fd, how) })?; + Ok(()) + } +} + +impl Drop for WasiFd { + fn drop(&mut self) { + unsafe { + // FIXME: can we handle the return code here even though we can't on + // unix? + libc::__wasi_fd_close(self.fd); + } + } +} diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs new file mode 100644 index 00000000000..172c60385b3 --- /dev/null +++ b/src/libstd/sys/wasi/fs.rs @@ -0,0 +1,691 @@ +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; +use crate::iter; +use crate::mem::{self, ManuallyDrop}; +use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; +use crate::path::{Path, PathBuf}; +use crate::ptr; +use crate::sync::Arc; +use crate::sys::fd::{DirCookie, WasiFd}; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; +use crate::sys_common::FromInner; + +pub use crate::sys_common::fs::copy; +pub use crate::sys_common::fs::remove_dir_all; + +pub struct File { + fd: WasiFd, +} + +#[derive(Clone)] +pub struct FileAttr { + meta: libc::__wasi_filestat_t, +} + +pub struct ReadDir { + inner: Arc<ReadDirInner>, + cookie: Option<DirCookie>, + buf: Vec<u8>, + offset: usize, + cap: usize, +} + +struct ReadDirInner { + root: PathBuf, + dir: File, +} + +pub struct DirEntry { + meta: libc::__wasi_dirent_t, + name: Vec<u8>, + inner: Arc<ReadDirInner>, +} + +#[derive(Clone, Debug, Default)] +pub struct OpenOptions { + read: bool, + write: bool, + dirflags: libc::__wasi_lookupflags_t, + fdflags: libc::__wasi_fdflags_t, + oflags: libc::__wasi_oflags_t, + rights_base: Option<libc::__wasi_rights_t>, + rights_inheriting: Option<libc::__wasi_rights_t>, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + readonly: bool, +} + +#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] +pub struct FileType { + bits: libc::__wasi_filetype_t, +} + +#[derive(Debug)] +pub struct DirBuilder {} + +impl FileAttr { + fn zero() -> FileAttr { + FileAttr { + meta: unsafe { mem::zeroed() }, + } + } + + pub fn size(&self) -> u64 { + self.meta.st_size + } + + pub fn perm(&self) -> FilePermissions { + // not currently implemented in wasi yet + FilePermissions { readonly: false } + } + + pub fn file_type(&self) -> FileType { + FileType { + bits: self.meta.st_filetype, + } + } + + pub fn modified(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from_wasi_timestamp(self.meta.st_mtim)) + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from_wasi_timestamp(self.meta.st_atim)) + } + + pub fn created(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from_wasi_timestamp(self.meta.st_ctim)) + } + + pub fn as_wasi(&self) -> &libc::__wasi_filestat_t { + &self.meta + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.readonly + } + + pub fn set_readonly(&mut self, readonly: bool) { + self.readonly = readonly; + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.bits == libc::__WASI_FILETYPE_DIRECTORY + } + + pub fn is_file(&self) -> bool { + self.bits == libc::__WASI_FILETYPE_REGULAR_FILE + } + + pub fn is_symlink(&self) -> bool { + self.bits == libc::__WASI_FILETYPE_SYMBOLIC_LINK + } + + pub fn bits(&self) -> libc::__wasi_filetype_t { + self.bits + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ReadDir").finish() + } +} + +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; + + fn next(&mut self) -> Option<io::Result<DirEntry>> { + loop { + // If we've reached the capacity of our buffer then we need to read + // some more from the OS, otherwise we pick up at our old offset. + let offset = if self.offset == self.cap { + let cookie = self.cookie.take()?; + match self.inner.dir.fd.readdir(&mut self.buf, cookie) { + Ok(bytes) => self.cap = bytes, + Err(e) => return Some(Err(e)), + } + self.offset = 0; + self.cookie = Some(cookie); + + // If we didn't actually read anything, this is in theory the + // end of the directory. + if self.cap == 0 { + self.cookie = None; + return None; + } + + 0 + } else { + self.offset + }; + let data = &self.buf[offset..self.cap]; + + // If we're not able to read a directory entry then that means it + // must have been truncated at the end of the buffer, so reset our + // offset so we can go back and reread into the buffer, picking up + // where we last left off. + let dirent_size = mem::size_of::<libc::__wasi_dirent_t>(); + if data.len() < dirent_size { + assert!(self.cookie.is_some()); + assert!(self.buf.len() >= dirent_size); + self.offset = self.cap; + continue; + } + let (dirent, data) = data.split_at(dirent_size); + let dirent = + unsafe { ptr::read_unaligned(dirent.as_ptr() as *const libc::__wasi_dirent_t) }; + + // If the file name was truncated, then we need to reinvoke + // `readdir` so we truncate our buffer to start over and reread this + // descriptor. Note that if our offset is 0 that means the file name + // is massive and we need a bigger buffer. + if data.len() < dirent.d_namlen as usize { + if offset == 0 { + let amt_to_add = self.buf.capacity(); + self.buf.extend(iter::repeat(0).take(amt_to_add)); + } + assert!(self.cookie.is_some()); + self.offset = self.cap; + continue; + } + self.cookie = Some(dirent.d_next); + self.offset = offset + dirent_size + dirent.d_namlen as usize; + + let name = &data[..(dirent.d_namlen as usize)]; + + // These names are skipped on all other platforms, so let's skip + // them here too + if name == b"." || name == b".." { + continue; + } + + return Some(Ok(DirEntry { + meta: dirent, + name: name.to_vec(), + inner: self.inner.clone(), + })); + } + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + let name = OsStr::from_bytes(&self.name); + self.inner.root.join(name) + } + + pub fn file_name(&self) -> OsString { + OsString::from_vec(self.name.clone()) + } + + pub fn metadata(&self) -> io::Result<FileAttr> { + metadata_at( + &self.inner.dir.fd, + 0, + OsStr::from_bytes(&self.name).as_ref(), + ) + } + + pub fn file_type(&self) -> io::Result<FileType> { + Ok(FileType { + bits: self.meta.d_type, + }) + } + + pub fn ino(&self) -> libc::__wasi_inode_t { + self.meta.d_ino + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + let mut base = OpenOptions::default(); + base.dirflags = libc::__WASI_LOOKUP_SYMLINK_FOLLOW; + return base; + } + + pub fn read(&mut self, read: bool) { + self.read = read; + } + + pub fn write(&mut self, write: bool) { + self.write = write; + } + + pub fn truncate(&mut self, truncate: bool) { + self.oflag(libc::__WASI_O_TRUNC, truncate); + } + + pub fn create(&mut self, create: bool) { + self.oflag(libc::__WASI_O_CREAT, create); + } + + pub fn create_new(&mut self, create_new: bool) { + self.oflag(libc::__WASI_O_EXCL, create_new); + self.oflag(libc::__WASI_O_CREAT, create_new); + } + + pub fn directory(&mut self, directory: bool) { + self.oflag(libc::__WASI_O_DIRECTORY, directory); + } + + fn oflag(&mut self, bit: libc::__wasi_oflags_t, set: bool) { + if set { + self.oflags |= bit; + } else { + self.oflags &= !bit; + } + } + + pub fn append(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_APPEND, set); + } + + pub fn dsync(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_DSYNC, set); + } + + pub fn nonblock(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_NONBLOCK, set); + } + + pub fn rsync(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_RSYNC, set); + } + + pub fn sync(&mut self, set: bool) { + self.fdflag(libc::__WASI_FDFLAG_SYNC, set); + } + + fn fdflag(&mut self, bit: libc::__wasi_fdflags_t, set: bool) { + if set { + self.fdflags |= bit; + } else { + self.fdflags &= !bit; + } + } + + pub fn fs_rights_base(&mut self, rights: libc::__wasi_rights_t) { + self.rights_base = Some(rights); + } + + pub fn fs_rights_inheriting(&mut self, rights: libc::__wasi_rights_t) { + self.rights_inheriting = Some(rights); + } + + fn rights_base(&self) -> libc::__wasi_rights_t { + if let Some(rights) = self.rights_base { + return rights; + } + + // If rights haven't otherwise been specified try to pick a reasonable + // set. This can always be overridden by users via extension traits, and + // implementations may give us fewer rights silently than we ask for. So + // given that, just look at `read` and `write` and bucket permissions + // based on that. + let mut base = 0; + if self.read { + base |= libc::__WASI_RIGHT_FD_READ; + base |= libc::__WASI_RIGHT_FD_READDIR; + } + if self.write { + base |= libc::__WASI_RIGHT_FD_WRITE; + base |= libc::__WASI_RIGHT_FD_DATASYNC; + base |= libc::__WASI_RIGHT_FD_ALLOCATE; + base |= libc::__WASI_RIGHT_FD_FILESTAT_SET_SIZE; + } + + // FIXME: some of these should probably be read-only or write-only... + base |= libc::__WASI_RIGHT_FD_ADVISE; + base |= libc::__WASI_RIGHT_FD_FDSTAT_SET_FLAGS; + base |= libc::__WASI_RIGHT_FD_FILESTAT_SET_TIMES; + base |= libc::__WASI_RIGHT_FD_SEEK; + base |= libc::__WASI_RIGHT_FD_SYNC; + base |= libc::__WASI_RIGHT_FD_TELL; + base |= libc::__WASI_RIGHT_PATH_CREATE_DIRECTORY; + base |= libc::__WASI_RIGHT_PATH_CREATE_FILE; + base |= libc::__WASI_RIGHT_PATH_FILESTAT_GET; + base |= libc::__WASI_RIGHT_PATH_LINK_SOURCE; + base |= libc::__WASI_RIGHT_PATH_LINK_TARGET; + base |= libc::__WASI_RIGHT_PATH_OPEN; + base |= libc::__WASI_RIGHT_PATH_READLINK; + base |= libc::__WASI_RIGHT_PATH_REMOVE_DIRECTORY; + base |= libc::__WASI_RIGHT_PATH_RENAME_SOURCE; + base |= libc::__WASI_RIGHT_PATH_RENAME_TARGET; + base |= libc::__WASI_RIGHT_PATH_SYMLINK; + base |= libc::__WASI_RIGHT_PATH_UNLINK_FILE; + base |= libc::__WASI_RIGHT_POLL_FD_READWRITE; + + return base; + } + + fn rights_inheriting(&self) -> libc::__wasi_rights_t { + self.rights_inheriting.unwrap_or_else(|| self.rights_base()) + } + + pub fn lookup_flags(&mut self, flags: libc::__wasi_lookupflags_t) { + self.dirflags = flags; + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { + let (dir, file) = open_parent(path, libc::__WASI_RIGHT_PATH_OPEN)?; + open_at(&dir, &file, opts) + } + + pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> { + open_at(&self.fd, path, opts) + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + let mut ret = FileAttr::zero(); + self.fd.filestat_get(&mut ret.meta)?; + Ok(ret) + } + + pub fn metadata_at( + &self, + flags: libc::__wasi_lookupflags_t, + path: &Path, + ) -> io::Result<FileAttr> { + metadata_at(&self.fd, flags, path) + } + + pub fn fsync(&self) -> io::Result<()> { + self.fd.sync() + } + + pub fn datasync(&self) -> io::Result<()> { + self.fd.datasync() + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + self.fd.filestat_set_size(size) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.read_vectored(&mut [IoSliceMut::new(buf)]) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.fd.read(bufs) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.write_vectored(&[IoSlice::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.fd.write(bufs) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { + self.fd.seek(pos) + } + + pub fn duplicate(&self) -> io::Result<File> { + // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup + unsupported() + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + // Permissions haven't been fully figured out in wasi yet, so this is + // likely temporary + unsupported() + } + + pub fn fd(&self) -> &WasiFd { + &self.fd + } + + pub fn into_fd(self) -> WasiFd { + self.fd + } + + pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> { + read_link(&self.fd, file) + } +} + +impl FromInner<u32> for File { + fn from_inner(fd: u32) -> File { + unsafe { + File { + fd: WasiFd::from_raw(fd), + } + } + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_CREATE_DIRECTORY)?; + dir.create_directory(file.as_os_str().as_bytes()) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("File") + .field("fd", &self.fd.as_raw()) + .finish() + } +} + +pub fn readdir(p: &Path) -> io::Result<ReadDir> { + let mut opts = OpenOptions::new(); + opts.directory(true); + opts.read(true); + let dir = File::open(p, &opts)?; + Ok(ReadDir { + cookie: Some(0), + buf: vec![0; 128], + offset: 0, + cap: 0, + inner: Arc::new(ReadDirInner { + dir, + root: p.to_path_buf(), + }), + }) +} + +pub fn unlink(p: &Path) -> io::Result<()> { + let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_UNLINK_FILE)?; + dir.unlink_file(file.as_os_str().as_bytes()) +} + +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let (old, old_file) = open_parent(old, libc::__WASI_RIGHT_PATH_RENAME_SOURCE)?; + let (new, new_file) = open_parent(new, libc::__WASI_RIGHT_PATH_RENAME_TARGET)?; + old.rename( + old_file.as_os_str().as_bytes(), + &new, + new_file.as_os_str().as_bytes(), + ) +} + +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + // Permissions haven't been fully figured out in wasi yet, so this is + // likely temporary + unsupported() +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_REMOVE_DIRECTORY)?; + dir.remove_directory(file.as_os_str().as_bytes()) +} + +pub fn readlink(p: &Path) -> io::Result<PathBuf> { + let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_READLINK)?; + read_link(&dir, &file) +} + +fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> { + // Try to get a best effort initial capacity for the vector we're going to + // fill. Note that if it's not a symlink we don't use a file to avoid + // allocating gigabytes if you read_link a huge movie file by accident. + // Additionally we add 1 to the initial size so if it doesn't change until + // when we call `readlink` the returned length will be less than the + // capacity, guaranteeing that we got all the data. + let meta = metadata_at(fd, 0, file)?; + let initial_size = if meta.file_type().is_symlink() { + (meta.size() as usize).saturating_add(1) + } else { + 1 // this'll fail in just a moment + }; + + // Now that we have an initial guess of how big to make our buffer, call + // `readlink` in a loop until it fails or reports it filled fewer bytes than + // we asked for, indicating we got everything. + let file = file.as_os_str().as_bytes(); + let mut destination = vec![0u8; initial_size]; + loop { + let len = fd.readlink(file, &mut destination)?; + if len < destination.len() { + destination.truncate(len); + destination.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(destination))); + } + let amt_to_add = destination.len(); + destination.extend(iter::repeat(0).take(amt_to_add)); + } +} + +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + let (dst, dst_file) = open_parent(dst, libc::__WASI_RIGHT_PATH_SYMLINK)?; + dst.symlink(src.as_os_str().as_bytes(), dst_file.as_os_str().as_bytes()) +} + +pub fn link(src: &Path, dst: &Path) -> io::Result<()> { + let (src, src_file) = open_parent(src, libc::__WASI_RIGHT_PATH_LINK_SOURCE)?; + let (dst, dst_file) = open_parent(dst, libc::__WASI_RIGHT_PATH_LINK_TARGET)?; + src.link( + libc::__WASI_LOOKUP_SYMLINK_FOLLOW, + src_file.as_os_str().as_bytes(), + &dst, + dst_file.as_os_str().as_bytes(), + ) +} + +pub fn stat(p: &Path) -> io::Result<FileAttr> { + let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_FILESTAT_GET)?; + metadata_at(&dir, libc::__WASI_LOOKUP_SYMLINK_FOLLOW, &file) +} + +pub fn lstat(p: &Path) -> io::Result<FileAttr> { + let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_FILESTAT_GET)?; + metadata_at(&dir, 0, &file) +} + +fn metadata_at( + fd: &WasiFd, + flags: libc::__wasi_lookupflags_t, + path: &Path, +) -> io::Result<FileAttr> { + let mut ret = FileAttr::zero(); + fd.path_filestat_get(flags, path.as_os_str().as_bytes(), &mut ret.meta)?; + Ok(ret) +} + +pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> { + // This seems to not be in wasi's API yet, and we may need to end up + // emulating it ourselves. For now just return an error. + unsupported() +} + +fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> { + let fd = fd.open( + opts.dirflags, + path.as_os_str().as_bytes(), + opts.oflags, + opts.rights_base(), + opts.rights_inheriting(), + opts.fdflags, + )?; + Ok(File { fd }) +} + +/// Attempts to open a bare path `p`. +/// +/// WASI has no fundamental capability to do this. All syscalls and operations +/// are relative to already-open file descriptors. The C library, however, +/// manages a map of preopened file descriptors to their path, and then the C +/// library provides an API to look at this. In other words, when you want to +/// open a path `p`, you have to find a previously opened file descriptor in a +/// global table and then see if `p` is relative to that file descriptor. +/// +/// This function, if successful, will return two items: +/// +/// * The first is a `ManuallyDrop<WasiFd>`. This represents a preopened file +/// descriptor which we don't have ownership of, but we can use. You shouldn't +/// actually drop the `fd`. +/// +/// * The second is a path that should be a part of `p` and represents a +/// relative traversal from the file descriptor specified to the desired +/// location `p`. +/// +/// If successful you can use the returned file descriptor to perform +/// file-descriptor-relative operations on the path returned as well. The +/// `rights` argument indicates what operations are desired on the returned file +/// descriptor, and if successful the returned file descriptor should have the +/// appropriate rights for performing `rights` actions. +/// +/// Note that this can fail if `p` doesn't look like it can be opened relative +/// to any preopened file descriptor. +fn open_parent( + p: &Path, + rights: libc::__wasi_rights_t, +) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> { + let p = CString::new(p.as_os_str().as_bytes())?; + unsafe { + let mut ret = ptr::null(); + let fd = __wasilibc_find_relpath(p.as_ptr(), rights, 0, &mut ret); + if fd == -1 { + let msg = format!( + "failed to find a preopened file descriptor \ + through which {:?} could be opened", + p + ); + return Err(io::Error::new(io::ErrorKind::Other, msg)); + } + let path = Path::new(OsStr::from_bytes(CStr::from_ptr(ret).to_bytes())); + + // FIXME: right now `path` is a pointer into `p`, the `CString` above. + // When we return `p` is deallocated and we can't use it, so we need to + // currently separately allocate `path`. If this becomes an issue though + // we should probably turn this into a closure-taking interface or take + // `&CString` and then pass off `&Path` tied to the same lifetime. + let path = path.to_path_buf(); + + return Ok((ManuallyDrop::new(WasiFd::from_raw(fd as u32)), path)); + } + + // FIXME(rust-lang/libc#1314) use the `libc` crate for this when the API + // there is published + extern "C" { + pub fn __wasilibc_find_relpath( + path: *const libc::c_char, + rights_base: libc::__wasi_rights_t, + rights_inheriting: libc::__wasi_rights_t, + relative_path: *mut *const libc::c_char, + ) -> libc::c_int; + } +} diff --git a/src/libstd/sys/wasi/io.rs b/src/libstd/sys/wasi/io.rs new file mode 100644 index 00000000000..ffecca5d1b6 --- /dev/null +++ b/src/libstd/sys/wasi/io.rs @@ -0,0 +1,87 @@ +use crate::marker::PhantomData; +use crate::slice; + +use libc::{__wasi_ciovec_t, __wasi_iovec_t, c_void}; + +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: __wasi_ciovec_t, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { + vec: __wasi_ciovec_t { + buf: buf.as_ptr() as *const c_void, + buf_len: buf.len(), + }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.buf_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.buf_len -= n; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { + slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) + } + } +} + +#[repr(transparent)] +pub struct IoSliceMut<'a> { + vec: __wasi_iovec_t, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { + vec: __wasi_iovec_t { + buf: buf.as_mut_ptr() as *mut c_void, + buf_len: buf.len() + }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.buf_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.buf_len -= n; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { + slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) + } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { + slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) + } + } +} diff --git a/src/libstd/sys/wasi/mod.rs b/src/libstd/sys/wasi/mod.rs new file mode 100644 index 00000000000..f842869e08e --- /dev/null +++ b/src/libstd/sys/wasi/mod.rs @@ -0,0 +1,128 @@ +//! System bindings for the wasm/web platform +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for wasm. Note that this wasm is *not* the emscripten +//! wasm, so we have no runtime here. +//! +//! This is all super highly experimental and not actually intended for +//! wide/production use yet, it's still all in the experimental category. This +//! will likely change over time. +//! +//! Currently all functions here are basically stubs that immediately return +//! errors. The hope is that with a portability lint we can turn actually just +//! remove all this and just omit parts of the standard library if we're +//! compiling for wasm. That way it's a compile time error for something that's +//! guaranteed to be a runtime error! + +use libc; +use crate::io::{Error, ErrorKind}; +use crate::mem; +use crate::os::raw::c_char; + +pub mod alloc; +pub mod args; +#[path = "../wasm/cmath.rs"] +pub mod cmath; +#[path = "../wasm/condvar.rs"] +pub mod condvar; +pub mod env; +pub mod fd; +pub mod fs; +#[path = "../wasm/memchr.rs"] +pub mod memchr; +#[path = "../wasm/mutex.rs"] +pub mod mutex; +pub mod net; +pub mod io; +pub mod os; +pub use crate::sys_common::os_str_bytes as os_str; +pub mod path; +pub mod pipe; +pub mod process; +#[path = "../wasm/rwlock.rs"] +pub mod rwlock; +#[path = "../wasm/stack_overflow.rs"] +pub mod stack_overflow; +pub mod stdio; +pub mod thread; +#[path = "../wasm/thread_local.rs"] +pub mod thread_local; +#[path = "../wasm/fast_thread_local.rs"] +pub mod fast_thread_local; +pub mod time; +pub mod ext; + +#[cfg(not(test))] +pub fn init() { +} + +pub fn unsupported<T>() -> crate::io::Result<T> { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> Error { + Error::new(ErrorKind::Other, "operation not supported on wasm yet") +} + +pub fn decode_error_kind(_code: i32) -> ErrorKind { + ErrorKind::Other +} + +// This enum is used as the storage for a bunch of types which can't actually +// exist. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Void {} + +pub unsafe fn strlen(mut s: *const c_char) -> usize { + let mut n = 0; + while *s != 0 { + n += 1; + s = s.offset(1); + } + return n +} + +pub unsafe fn abort_internal() -> ! { + libc::abort() +} + +pub fn hashmap_random_keys() -> (u64, u64) { + let mut ret = (0u64, 0u64); + unsafe { + let base = &mut ret as *mut (u64, u64) as *mut libc::c_void; + let len = mem::size_of_val(&ret); + cvt_wasi(libc::__wasi_random_get(base, len)).unwrap(); + } + return ret +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> { + if t.is_minus_one() { + Err(Error::last_os_error()) + } else { + Ok(t) + } +} + +pub fn cvt_wasi(r: u16) -> crate::io::Result<()> { + if r != libc::__WASI_ESUCCESS { + Err(Error::from_raw_os_error(r as i32)) + } else { + Ok(()) + } +} diff --git a/src/libstd/sys/wasi/net.rs b/src/libstd/sys/wasi/net.rs new file mode 100644 index 00000000000..80f633a8e1f --- /dev/null +++ b/src/libstd/sys/wasi/net.rs @@ -0,0 +1,426 @@ +use crate::fmt; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr}; +use crate::time::Duration; +use crate::sys::{unsupported, Void}; +use crate::convert::TryFrom; +use crate::sys::fd::{WasiFd}; +use crate::sys_common::FromInner; + +pub struct TcpStream { + fd: WasiFd, +} + +impl TcpStream { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { + unsupported() + } + + pub fn read(&self, _: &mut [u8]) -> io::Result<usize> { + unsupported() + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + unsupported() + } + + pub fn write(&self, _: &[u8]) -> io::Result<usize> { + unsupported() + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + unsupported() + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + unsupported() + } + + pub fn duplicate(&self) -> io::Result<TcpStream> { + unsupported() + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn nodelay(&self) -> io::Result<bool> { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result<u32> { + unsupported() + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + unsupported() + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn fd(&self) -> &WasiFd { + &self.fd + } + + pub fn into_fd(self) -> WasiFd { + self.fd + } +} + +impl FromInner<u32> for TcpStream { + fn from_inner(fd: u32) -> TcpStream { + unsafe { + TcpStream { + fd: WasiFd::from_raw(fd), + } + } + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TcpStream") + .field("fd", &self.fd.as_raw()) + .finish() + } +} + +pub struct TcpListener { + fd: WasiFd +} + +impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + unsupported() + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + unsupported() + } + + pub fn duplicate(&self) -> io::Result<TcpListener> { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result<u32> { + unsupported() + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn only_v6(&self) -> io::Result<bool> { + unsupported() + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + unsupported() + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn fd(&self) -> &WasiFd { + &self.fd + } + + pub fn into_fd(self) -> WasiFd { + self.fd + } +} + +impl FromInner<u32> for TcpListener { + fn from_inner(fd: u32) -> TcpListener { + unsafe { + TcpListener { + fd: WasiFd::from_raw(fd), + } + } + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TcpListener") + .field("fd", &self.fd.as_raw()) + .finish() + } +} + +pub struct UdpSocket { + fd: WasiFd, +} + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + unsupported() + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + unsupported() + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + unsupported() + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> { + unsupported() + } + + pub fn duplicate(&self) -> io::Result<UdpSocket> { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + unsupported() + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn broadcast(&self) -> io::Result<bool> { + unsupported() + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v4(&self) -> io::Result<bool> { + unsupported() + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn multicast_ttl_v4(&self) -> io::Result<u32> { + unsupported() + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v6(&self) -> io::Result<bool> { + unsupported() + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + unsupported() + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + unsupported() + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + unsupported() + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result<u32> { + unsupported() + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + unsupported() + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { + unsupported() + } + + pub fn send(&self, _: &[u8]) -> io::Result<usize> { + unsupported() + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + unsupported() + } + + pub fn fd(&self) -> &WasiFd { + &self.fd + } + + pub fn into_fd(self) -> WasiFd { + self.fd + } +} + +impl FromInner<u32> for UdpSocket { + fn from_inner(fd: u32) -> UdpSocket { + unsafe { + UdpSocket { + fd: WasiFd::from_raw(fd), + } + } + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UdpSocket") + .field("fd", &self.fd.as_raw()) + .finish() + } +} + +pub struct LookupHost(Void); + +impl LookupHost { + pub fn port(&self) -> u16 { + match self.0 {} + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option<SocketAddr> { + match self.0 {} + } +} + +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result<LookupHost> { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { + unsupported() + } +} + +#[allow(nonstandard_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr { + } + + pub type socklen_t = usize; +} diff --git a/src/libstd/sys/wasi/os.rs b/src/libstd/sys/wasi/os.rs new file mode 100644 index 00000000000..822ea02a11b --- /dev/null +++ b/src/libstd/sys/wasi/os.rs @@ -0,0 +1,184 @@ +use crate::any::Any; +use crate::error::Error as StdError; +use crate::ffi::{OsString, OsStr, CString, CStr}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::os::wasi::prelude::*; +use crate::path::{self, PathBuf}; +use crate::ptr; +use crate::str; +use crate::sys::memchr; +use crate::sys::{cvt, unsupported, Void}; +use crate::vec; + +#[cfg(not(target_feature = "atomics"))] +pub unsafe fn env_lock() -> impl Any { + // No need for a lock if we're single-threaded, but this function will need + // to get implemented for multi-threaded scenarios +} + +pub fn errno() -> i32 { + extern { + #[thread_local] + static errno: libc::c_int; + } + + unsafe { errno as i32 } +} + +pub fn error_string(errno: i32) -> String { + extern { + fn strerror_r(errnum: libc::c_int, buf: *mut libc::c_char, + buflen: libc::size_t) -> libc::c_int; + } + + let mut buf = [0 as libc::c_char; 1024]; + + let p = buf.as_mut_ptr(); + unsafe { + if strerror_r(errno as libc::c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() + } +} + +pub fn getcwd() -> io::Result<PathBuf> { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(&'a Void); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option<PathBuf> { + match *self.0 {} + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError> + where I: Iterator<Item=T>, T: AsRef<OsStr> +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on wasm yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + fn description(&self) -> &str { + "not supported on wasm yet" + } +} + +pub fn current_exe() -> io::Result<PathBuf> { + unsupported() +} + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() } + fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } +} + + +pub fn env() -> Env { + unsafe { + let _guard = env_lock(); + let mut environ = libc::environ; + let mut result = Vec::new(); + while environ != ptr::null_mut() && *environ != ptr::null_mut() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.offset(1); + } + return Env { + iter: result.into_iter(), + _dont_send_or_sync_me: PhantomData, + } + } + + // See src/libstd/sys/unix/os.rs, same as that + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p+1..].to_vec()), + )) + } +} + +pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> { + let k = CString::new(k.as_bytes())?; + unsafe { + let _guard = env_lock(); + let s = libc::getenv(k.as_ptr()) as *const libc::c_char; + let ret = if s.is_null() { + None + } else { + Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) + }; + Ok(ret) + } +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let k = CString::new(k.as_bytes())?; + let v = CString::new(v.as_bytes())?; + + unsafe { + let _guard = env_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ()) + } +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + let nbuf = CString::new(n.as_bytes())?; + + unsafe { + let _guard = env_lock(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ()) + } +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on wasm") +} + +pub fn home_dir() -> Option<PathBuf> { + None +} + +pub fn exit(code: i32) -> ! { + unsafe { + libc::exit(code) + } +} + +pub fn getpid() -> u32 { + panic!("unsupported"); +} diff --git a/src/libstd/sys/wasi/path.rs b/src/libstd/sys/wasi/path.rs new file mode 100644 index 00000000000..7a183956107 --- /dev/null +++ b/src/libstd/sys/wasi/path.rs @@ -0,0 +1,19 @@ +use crate::path::Prefix; +use crate::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: &str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/wasi/pipe.rs b/src/libstd/sys/wasi/pipe.rs new file mode 100644 index 00000000000..9f07f054362 --- /dev/null +++ b/src/libstd/sys/wasi/pipe.rs @@ -0,0 +1,33 @@ +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::sys::Void; + +pub struct AnonPipe(Void); + +impl AnonPipe { + pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +pub fn read2(p1: AnonPipe, + _v1: &mut Vec<u8>, + _p2: AnonPipe, + _v2: &mut Vec<u8>) -> io::Result<()> { + match p1.0 {} +} diff --git a/src/libstd/sys/wasi/process.rs b/src/libstd/sys/wasi/process.rs new file mode 100644 index 00000000000..788b829f4ba --- /dev/null +++ b/src/libstd/sys/wasi/process.rs @@ -0,0 +1,152 @@ +use crate::ffi::OsStr; +use crate::fmt; +use crate::io; +use crate::sys::fs::File; +use crate::sys::pipe::AnonPipe; +use crate::sys::{unsupported, Void}; +use crate::sys_common::process::{CommandEnv, DefaultEnvKey}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + env: CommandEnv<DefaultEnvKey> +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option<AnonPipe>, + pub stdout: Option<AnonPipe>, + pub stderr: Option<AnonPipe>, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(_program: &OsStr) -> Command { + Command { + env: Default::default() + } + } + + pub fn arg(&mut self, _arg: &OsStr) { + } + + pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> { + &mut self.env + } + + pub fn cwd(&mut self, _dir: &OsStr) { + } + + pub fn stdin(&mut self, _stdin: Stdio) { + } + + pub fn stdout(&mut self, _stdout: Stdio) { + } + + pub fn stderr(&mut self, _stderr: Stdio) { + } + + pub fn spawn(&mut self, _default: Stdio, _needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + unsupported() + } +} + +impl From<AnonPipe> for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From<File> for Stdio { + fn from(_file: File) -> Stdio { + panic!("unsupported") + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +pub struct ExitStatus(Void); + +impl ExitStatus { + pub fn success(&self) -> bool { + match self.0 {} + } + + pub fn code(&self) -> Option<i32> { + match self.0 {} + } +} + +impl Clone for ExitStatus { + fn clone(&self) -> ExitStatus { + match self.0 {} + } +} + +impl Copy for ExitStatus {} + +impl PartialEq for ExitStatus { + fn eq(&self, _other: &ExitStatus) -> bool { + match self.0 {} + } +} + +impl Eq for ExitStatus { +} + +impl fmt::Debug for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 {} + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(bool); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(false); + pub const FAILURE: ExitCode = ExitCode(true); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +pub struct Process(Void); + +impl Process { + pub fn id(&self) -> u32 { + match self.0 {} + } + + pub fn kill(&mut self) -> io::Result<()> { + match self.0 {} + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + match self.0 {} + } + + pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { + match self.0 {} + } +} diff --git a/src/libstd/sys/wasi/stdio.rs b/src/libstd/sys/wasi/stdio.rs new file mode 100644 index 00000000000..2bf8d803c01 --- /dev/null +++ b/src/libstd/sys/wasi/stdio.rs @@ -0,0 +1,81 @@ +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::libc; +use crate::mem::ManuallyDrop; +use crate::sys::fd::WasiFd; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub fn new() -> io::Result<Stdin> { + Ok(Stdin) + } + + pub fn read(&self, data: &mut [u8]) -> io::Result<usize> { + self.read_vectored(&mut [IoSliceMut::new(data)]) + } + + pub fn read_vectored(&self, data: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDIN_FILENO as u32) }) + .read(data) + } +} + +impl Stdout { + pub fn new() -> io::Result<Stdout> { + Ok(Stdout) + } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + self.write_vectored(&[IoSlice::new(data)]) + } + + pub fn write_vectored(&self, data: &[IoSlice<'_>]) -> io::Result<usize> { + ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDOUT_FILENO as u32) }) + .write(data) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub fn new() -> io::Result<Stderr> { + Ok(Stderr) + } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + self.write_vectored(&[IoSlice::new(data)]) + } + + pub fn write_vectored(&self, data: &[IoSlice<'_>]) -> io::Result<usize> { + ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDERR_FILENO as u32) }) + .write(data) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } +} + +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + (&*self).write(data) + } + + fn flush(&mut self) -> io::Result<()> { + (&*self).flush() + } +} + +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(libc::__WASI_EBADF as i32) +} + +pub fn panic_output() -> Option<impl io::Write> { + Stderr::new().ok() +} diff --git a/src/libstd/sys/wasi/thread.rs b/src/libstd/sys/wasi/thread.rs new file mode 100644 index 00000000000..5e69e4d948f --- /dev/null +++ b/src/libstd/sys/wasi/thread.rs @@ -0,0 +1,56 @@ +use crate::cmp; +use crate::ffi::CStr; +use crate::io; +use crate::sys::cvt; +use crate::sys::{unsupported, Void}; +use crate::time::Duration; +use libc; + +pub struct Thread(Void); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) + -> io::Result<Thread> + { + unsupported() + } + + pub fn yield_now() { + let ret = unsafe { libc::__wasi_sched_yield() }; + debug_assert_eq!(ret, 0); + } + + pub fn set_name(_name: &CStr) { + // nope + } + + pub fn sleep(dur: Duration) { + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as i32; + + unsafe { + while secs > 0 || nsecs > 0 { + let mut ts = libc::timespec { + tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t, + tv_nsec: nsecs, + }; + secs -= ts.tv_sec as u64; + cvt(libc::nanosleep(&ts, &mut ts)).unwrap(); + nsecs = 0; + } + } + } + + pub fn join(self) { + match self.0 {} + } +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option<Guard> { None } + pub unsafe fn init() -> Option<Guard> { None } +} diff --git a/src/libstd/sys/wasi/time.rs b/src/libstd/sys/wasi/time.rs new file mode 100644 index 00000000000..3f14c80928c --- /dev/null +++ b/src/libstd/sys/wasi/time.rs @@ -0,0 +1,76 @@ +use crate::time::Duration; +use crate::mem; +use crate::sys::cvt_wasi; +use libc; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +fn current_time(clock: u32) -> Duration { + unsafe { + let mut ts = mem::zeroed(); + cvt_wasi(libc::__wasi_clock_time_get( + clock, + 1, // precision... seems ignored though? + &mut ts, + )).unwrap(); + Duration::new( + (ts / 1_000_000_000) as u64, + (ts % 1_000_000_000) as u32, + ) + } +} + +impl Instant { + pub fn now() -> Instant { + Instant(current_time(libc::__WASI_CLOCK_MONOTONIC)) + } + + pub const fn zero() -> Instant { + Instant(Duration::from_secs(0)) + } + + pub fn actually_monotonic() -> bool { + true + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_sub(*other)?)) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + SystemTime(current_time(libc::__WASI_CLOCK_REALTIME)) + } + + pub fn from_wasi_timestamp(ts: libc::__wasi_timestamp_t) -> SystemTime { + SystemTime(Duration::from_nanos(ts)) + } + + pub fn sub_time(&self, other: &SystemTime) + -> Result<Duration, Duration> { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/src/libstd/sys/wasm/alloc.rs b/src/libstd/sys/wasm/alloc.rs index b9098548b9c..c1af6ec1262 100644 --- a/src/libstd/sys/wasm/alloc.rs +++ b/src/libstd/sys/wasm/alloc.rs @@ -49,7 +49,6 @@ unsafe impl GlobalAlloc for System { #[cfg(target_feature = "atomics")] mod lock { - use crate::arch::wasm32; use crate::sync::atomic::{AtomicI32, Ordering::SeqCst}; static LOCKED: AtomicI32 = AtomicI32::new(0); @@ -61,14 +60,76 @@ mod lock { if LOCKED.swap(1, SeqCst) == 0 { return DropLock } - unsafe { - let r = wasm32::i32_atomic_wait( - &LOCKED as *const AtomicI32 as *mut i32, - 1, // expected value - -1, // timeout - ); - debug_assert!(r == 0 || r == 1); - } + // Ok so here's where things get a little depressing. At this point + // in time we need to synchronously acquire a lock, but we're + // contending with some other thread. Typically we'd execute some + // form of `i32.atomic.wait` like so: + // + // unsafe { + // let r = core::arch::wasm32::i32_atomic_wait( + // &LOCKED as *const AtomicI32 as *mut i32, + // 1, // expected value + // -1, // timeout + // ); + // debug_assert!(r == 0 || r == 1); + // } + // + // Unfortunately though in doing so we would cause issues for the + // main thread. The main thread in a web browser *cannot ever + // block*, no exceptions. This means that the main thread can't + // actually execute the `i32.atomic.wait` instruction. + // + // As a result if we want to work within the context of browsers we + // need to figure out some sort of allocation scheme for the main + // thread where when there's contention on the global malloc lock we + // do... something. + // + // Possible ideas include: + // + // 1. Attempt to acquire the global lock. If it fails, fall back to + // memory allocation via `memory.grow`. Later just ... somehow + // ... inject this raw page back into the main allocator as it + // gets sliced up over time. This strategy has the downside of + // forcing allocation of a page to happen whenever the main + // thread contents with other threads, which is unfortunate. + // + // 2. Maintain a form of "two level" allocator scheme where the main + // thread has its own allocator. Somehow this allocator would + // also be balanced with a global allocator, not only to have + // allocations cross between threads but also to ensure that the + // two allocators stay "balanced" in terms of free'd memory and + // such. This, however, seems significantly complicated. + // + // Out of a lack of other ideas, the current strategy implemented + // here is to simply spin. Typical spin loop algorithms have some + // form of "hint" here to the CPU that it's what we're doing to + // ensure that the CPU doesn't get too hot, but wasm doesn't have + // such an instruction. + // + // To be clear, spinning here is not a great solution. + // Another thread with the lock may take quite a long time to wake + // up. For example it could be in `memory.grow` or it could be + // evicted from the CPU for a timeslice like 10ms. For these periods + // of time our thread will "helpfully" sit here and eat CPU time + // until it itself is evicted or the lock holder finishes. This + // means we're just burning and wasting CPU time to no one's + // benefit. + // + // Spinning does have the nice properties, though, of being + // semantically correct, being fair to all threads for memory + // allocation, and being simple enough to implement. + // + // This will surely (hopefully) be replaced in the future with a + // real memory allocator that can handle the restriction of the main + // thread. + // + // + // FIXME: We can also possibly add an optimization here to detect + // when a thread is the main thread or not and block on all + // non-main-thread threads. Currently, however, we have no way + // of knowing which wasm thread is on the browser main thread, but + // if we could figure out we could at least somewhat mitigate the + // cost of this spinning. } } @@ -76,12 +137,16 @@ mod lock { fn drop(&mut self) { let r = LOCKED.swap(0, SeqCst); debug_assert_eq!(r, 1); - unsafe { - wasm32::atomic_notify( - &LOCKED as *const AtomicI32 as *mut i32, - 1, // only one thread - ); - } + + // Note that due to the above logic we don't actually need to wake + // anyone up, but if we did it'd likely look something like this: + // + // unsafe { + // core::arch::wasm32::atomic_notify( + // &LOCKED as *const AtomicI32 as *mut i32, + // 1, // only one thread + // ); + // } } } } diff --git a/src/libstd/sys/wasm/backtrace.rs b/src/libstd/sys/wasm/backtrace.rs deleted file mode 100644 index 7d56b298997..00000000000 --- a/src/libstd/sys/wasm/backtrace.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::io; -use crate::sys::unsupported; -use crate::sys_common::backtrace::Frame; - -pub struct BacktraceContext; - -pub fn unwind_backtrace(_frames: &mut [Frame]) - -> io::Result<(usize, BacktraceContext)> -{ - unsupported() -} - -pub fn resolve_symname<F>(_frame: Frame, - _callback: F, - _: &BacktraceContext) -> io::Result<()> - where F: FnOnce(Option<&str>) -> io::Result<()> -{ - unsupported() -} - -pub fn foreach_symbol_fileline<F>(_: Frame, - _: F, - _: &BacktraceContext) -> io::Result<bool> - where F: FnMut(&[u8], u32) -> io::Result<()> -{ - unsupported() -} diff --git a/src/libstd/sys/wasm/fast_thread_local.rs b/src/libstd/sys/wasm/fast_thread_local.rs new file mode 100644 index 00000000000..ff2198175f0 --- /dev/null +++ b/src/libstd/sys/wasm/fast_thread_local.rs @@ -0,0 +1,9 @@ +#![unstable(feature = "thread_local_internals", issue = "0")] + +pub unsafe fn register_dtor(_t: *mut u8, _dtor: unsafe extern fn(*mut u8)) { + // FIXME: right now there is no concept of "thread exit", but this is likely + // going to show up at some point in the form of an exported symbol that the + // wasm runtime is oging to be expected to call. For now we basically just + // ignore the arguments, but if such a function starts to exist it will + // likely look like the OSX implementation in `unix/fast_thread_local.rs` +} diff --git a/src/libstd/sys/wasm/fs.rs b/src/libstd/sys/wasm/fs.rs index 485d2c87fbd..e9095b375fe 100644 --- a/src/libstd/sys/wasm/fs.rs +++ b/src/libstd/sys/wasm/fs.rs @@ -1,7 +1,7 @@ use crate::ffi::OsString; use crate::fmt; use crate::hash::{Hash, Hasher}; -use crate::io::{self, SeekFrom}; +use crate::io::{self, SeekFrom, IoSlice, IoSliceMut}; use crate::path::{Path, PathBuf}; use crate::sys::time::SystemTime; use crate::sys::{unsupported, Void}; @@ -82,7 +82,7 @@ impl Eq for FilePermissions { } impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -125,13 +125,13 @@ impl Hash for FileType { } impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -200,10 +200,18 @@ impl File { match self.0 {} } + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { match self.0 {} } + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn flush(&self) -> io::Result<()> { match self.0 {} } @@ -236,7 +244,7 @@ impl DirBuilder { } impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } diff --git a/src/libstd/sys/wasm/io.rs b/src/libstd/sys/wasm/io.rs index 8b02d3fd19d..976e122463d 100644 --- a/src/libstd/sys/wasm/io.rs +++ b/src/libstd/sys/wasm/io.rs @@ -1,9 +1,16 @@ -pub struct IoVec<'a>(&'a [u8]); +use crate::mem; + +pub struct IoSlice<'a>(&'a [u8]); + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice(buf) + } -impl<'a> IoVec<'a> { #[inline] - pub fn new(buf: &'a [u8]) -> IoVec<'a> { - IoVec(buf) + pub fn advance(&mut self, n: usize) { + self.0 = &self.0[n..] } #[inline] @@ -12,12 +19,19 @@ impl<'a> IoVec<'a> { } } -pub struct IoVecMut<'a>(&'a mut [u8]); +pub struct IoSliceMut<'a>(&'a mut [u8]); + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut(buf) + } -impl<'a> IoVecMut<'a> { #[inline] - pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> { - IoVecMut(buf) + pub fn advance(&mut self, n: usize) { + let slice = mem::replace(&mut self.0, &mut []); + let (_, remaining) = slice.split_at_mut(n); + self.0 = remaining; } #[inline] diff --git a/src/libstd/sys/wasm/mod.rs b/src/libstd/sys/wasm/mod.rs index 1828cce4e52..56cbafcfdb8 100644 --- a/src/libstd/sys/wasm/mod.rs +++ b/src/libstd/sys/wasm/mod.rs @@ -23,8 +23,6 @@ use crate::time::Duration; pub mod alloc; pub mod args; -#[cfg(feature = "backtrace")] -pub mod backtrace; pub mod cmath; pub mod env; pub mod fs; @@ -32,7 +30,6 @@ pub mod io; pub mod memchr; pub mod net; pub mod os; -pub mod os_str; pub mod path; pub mod pipe; pub mod process; @@ -40,8 +37,12 @@ pub mod stack_overflow; pub mod thread; pub mod time; pub mod stdio; +pub mod thread_local; +pub mod fast_thread_local; -cfg_if! { +pub use crate::sys_common::os_str_bytes as os_str; + +cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { #[path = "condvar_atomics.rs"] pub mod condvar; @@ -49,13 +50,10 @@ cfg_if! { pub mod mutex; #[path = "rwlock_atomics.rs"] pub mod rwlock; - #[path = "thread_local_atomics.rs"] - pub mod thread_local; } else { pub mod condvar; pub mod mutex; pub mod rwlock; - pub mod thread_local; } } diff --git a/src/libstd/sys/wasm/net.rs b/src/libstd/sys/wasm/net.rs index a2ea2dfbbc0..d50f989d2bb 100644 --- a/src/libstd/sys/wasm/net.rs +++ b/src/libstd/sys/wasm/net.rs @@ -1,5 +1,5 @@ use crate::fmt; -use crate::io::{self, IoVec, IoVecMut}; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr}; use crate::time::Duration; use crate::sys::{unsupported, Void}; @@ -40,7 +40,7 @@ impl TcpStream { match self.0 {} } - pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> { + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { match self.0 {} } @@ -48,7 +48,7 @@ impl TcpStream { match self.0 {} } - pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> { + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { match self.0 {} } @@ -94,7 +94,7 @@ impl TcpStream { } impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -144,7 +144,7 @@ impl TcpListener { } impl fmt::Debug for TcpListener { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } @@ -156,6 +156,10 @@ impl UdpSocket { unsupported() } + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + match self.0 {} + } + pub fn socket_addr(&self) -> io::Result<SocketAddr> { match self.0 {} } @@ -278,7 +282,7 @@ impl UdpSocket { } impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } diff --git a/src/libstd/sys/wasm/os.rs b/src/libstd/sys/wasm/os.rs index 145f9ccd73a..5d21999a991 100644 --- a/src/libstd/sys/wasm/os.rs +++ b/src/libstd/sys/wasm/os.rs @@ -24,7 +24,7 @@ pub fn chdir(_: &path::Path) -> io::Result<()> { pub struct SplitPaths<'a>(&'a Void); -pub fn split_paths(_unparsed: &OsStr) -> SplitPaths { +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") } @@ -45,7 +45,7 @@ pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError> } impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "not supported on wasm yet".fmt(f) } } diff --git a/src/libstd/sys/wasm/os_str.rs b/src/libstd/sys/wasm/os_str.rs deleted file mode 100644 index 79b43458d00..00000000000 --- a/src/libstd/sys/wasm/os_str.rs +++ /dev/null @@ -1,180 +0,0 @@ -/// The underlying OsString/OsStr implementation on Unix systems: just -/// a `Vec<u8>`/`[u8]`. - -use crate::borrow::Cow; -use crate::fmt; -use crate::str; -use crate::mem; -use crate::rc::Rc; -use crate::sync::Arc; -use crate::sys_common::{AsInner, IntoInner}; -use crate::sys_common::bytestring::debug_fmt_bytestring; - -use core::str::lossy::Utf8Lossy; - -#[derive(Clone, Hash)] -pub struct Buf { - pub inner: Vec<u8> -} - -pub struct Slice { - pub inner: [u8] -} - -impl fmt::Debug for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - debug_fmt_bytestring(&self.inner, formatter) - } -} - -impl fmt::Display for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) - } -} - -impl fmt::Debug for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self.as_slice(), formatter) - } -} - -impl fmt::Display for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self.as_slice(), formatter) - } -} - -impl IntoInner<Vec<u8>> for Buf { - fn into_inner(self) -> Vec<u8> { - self.inner - } -} - -impl AsInner<[u8]> for Buf { - fn as_inner(&self) -> &[u8] { - &self.inner - } -} - - -impl Buf { - pub fn from_string(s: String) -> Buf { - Buf { inner: s.into_bytes() } - } - - #[inline] - pub fn with_capacity(capacity: usize) -> Buf { - Buf { - inner: Vec::with_capacity(capacity) - } - } - - #[inline] - pub fn clear(&mut self) { - self.inner.clear() - } - - #[inline] - pub fn capacity(&self) -> usize { - self.inner.capacity() - } - - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.inner.reserve(additional) - } - - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.inner.reserve_exact(additional) - } - - #[inline] - pub fn shrink_to_fit(&mut self) { - self.inner.shrink_to_fit() - } - - #[inline] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.inner.shrink_to(min_capacity) - } - - pub fn as_slice(&self) -> &Slice { - unsafe { mem::transmute(&*self.inner) } - } - - pub fn into_string(self) -> Result<String, Buf> { - String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) - } - - pub fn push_slice(&mut self, s: &Slice) { - self.inner.extend_from_slice(&s.inner) - } - - #[inline] - pub fn into_box(self) -> Box<Slice> { - unsafe { mem::transmute(self.inner.into_boxed_slice()) } - } - - #[inline] - pub fn from_box(boxed: Box<Slice>) -> Buf { - let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; - Buf { inner: inner.into_vec() } - } - - #[inline] - pub fn into_arc(&self) -> Arc<Slice> { - self.as_slice().into_arc() - } - - #[inline] - pub fn into_rc(&self) -> Rc<Slice> { - self.as_slice().into_rc() - } -} - -impl Slice { - fn from_u8_slice(s: &[u8]) -> &Slice { - unsafe { mem::transmute(s) } - } - - pub fn from_str(s: &str) -> &Slice { - Slice::from_u8_slice(s.as_bytes()) - } - - pub fn to_str(&self) -> Option<&str> { - str::from_utf8(&self.inner).ok() - } - - pub fn to_string_lossy(&self) -> Cow<str> { - String::from_utf8_lossy(&self.inner) - } - - pub fn to_owned(&self) -> Buf { - Buf { inner: self.inner.to_vec() } - } - - #[inline] - pub fn into_box(&self) -> Box<Slice> { - let boxed: Box<[u8]> = self.inner.into(); - unsafe { mem::transmute(boxed) } - } - - pub fn empty_box() -> Box<Slice> { - let boxed: Box<[u8]> = Default::default(); - unsafe { mem::transmute(boxed) } - } - - #[inline] - pub fn into_arc(&self) -> Arc<Slice> { - let arc: Arc<[u8]> = Arc::from(&self.inner); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } - } - - #[inline] - pub fn into_rc(&self) -> Rc<Slice> { - let rc: Rc<[u8]> = Rc::from(&self.inner); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } - } -} diff --git a/src/libstd/sys/wasm/path.rs b/src/libstd/sys/wasm/path.rs index 5c062e7c97c..7a183956107 100644 --- a/src/libstd/sys/wasm/path.rs +++ b/src/libstd/sys/wasm/path.rs @@ -11,7 +11,7 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'/' } -pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { +pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> { None } diff --git a/src/libstd/sys/wasm/pipe.rs b/src/libstd/sys/wasm/pipe.rs index 2582b993b60..9f07f054362 100644 --- a/src/libstd/sys/wasm/pipe.rs +++ b/src/libstd/sys/wasm/pipe.rs @@ -1,4 +1,4 @@ -use crate::io; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::sys::Void; pub struct AnonPipe(Void); @@ -8,10 +8,18 @@ impl AnonPipe { match self.0 {} } + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { match self.0 {} } + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> { + match self.0 {} + } + pub fn diverge(&self) -> ! { match self.0 {} } diff --git a/src/libstd/sys/wasm/process.rs b/src/libstd/sys/wasm/process.rs index c49daaa1632..a02e009d953 100644 --- a/src/libstd/sys/wasm/process.rs +++ b/src/libstd/sys/wasm/process.rs @@ -73,7 +73,7 @@ impl From<File> for Stdio { } impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { Ok(()) } } @@ -108,13 +108,13 @@ impl Eq for ExitStatus { } impl fmt::Debug for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } impl fmt::Display for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 {} } } diff --git a/src/libstd/sys/wasm/thread.rs b/src/libstd/sys/wasm/thread.rs index a65c413119f..d06965f3278 100644 --- a/src/libstd/sys/wasm/thread.rs +++ b/src/libstd/sys/wasm/thread.rs @@ -1,4 +1,3 @@ -use crate::boxed::FnBox; use crate::ffi::CStr; use crate::io; use crate::sys::{unsupported, Void}; @@ -10,7 +9,7 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box<dyn FnBox()>) + pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> { unsupported() @@ -60,48 +59,40 @@ pub mod guard { pub unsafe fn init() -> Option<Guard> { None } } -cfg_if! { - if #[cfg(all(target_feature = "atomics", feature = "wasm-bindgen-threads"))] { - #[link(wasm_import_module = "__wbindgen_thread_xform__")] - extern { - fn __wbindgen_current_id() -> u32; - fn __wbindgen_tcb_get() -> u32; - fn __wbindgen_tcb_set(ptr: u32); +// This is only used by atomics primitives when the `atomics` feature is +// enabled. In that mode we currently just use our own thread-local to store our +// current thread's ID, and then we lazily initialize it to something allocated +// from a global counter. +#[cfg(target_feature = "atomics")] +pub fn my_id() -> u32 { + use crate::sync::atomic::{AtomicU32, Ordering::SeqCst}; + + static NEXT_ID: AtomicU32 = AtomicU32::new(0); + + #[thread_local] + static mut MY_ID: u32 = 0; + + unsafe { + // If our thread ID isn't set yet then we need to allocate one. Do so + // with with a simple "atomically add to a global counter" strategy. + // This strategy doesn't handled what happens when the counter + // overflows, however, so just abort everything once the counter + // overflows and eventually we could have some sort of recycling scheme + // (or maybe this is all totally irrelevant by that point!). In any case + // though we're using a CAS loop instead of a `fetch_add` to ensure that + // the global counter never overflows. + if MY_ID == 0 { + let mut cur = NEXT_ID.load(SeqCst); + MY_ID = loop { + let next = cur.checked_add(1).unwrap_or_else(|| { + crate::arch::wasm32::unreachable() + }); + match NEXT_ID.compare_exchange(cur, next, SeqCst, SeqCst) { + Ok(_) => break next, + Err(i) => cur = i, + } + }; } - pub fn my_id() -> u32 { - unsafe { __wbindgen_current_id() } - } - - // These are currently only ever used in `thread_local_atomics.rs`, if - // you'd like to use them be sure to update that and make sure everyone - // agrees what's what. - pub fn tcb_get() -> *mut u8 { - use crate::mem; - assert_eq!(mem::size_of::<*mut u8>(), mem::size_of::<u32>()); - unsafe { __wbindgen_tcb_get() as *mut u8 } - } - - pub fn tcb_set(ptr: *mut u8) { - unsafe { __wbindgen_tcb_set(ptr as u32); } - } - - // FIXME: still need something for hooking exiting a thread to free - // data... - - } else if #[cfg(target_feature = "atomics")] { - pub fn my_id() -> u32 { - panic!("thread ids not implemented on wasm with atomics yet") - } - - pub fn tcb_get() -> *mut u8 { - panic!("thread local data not implemented on wasm with atomics yet") - } - - pub fn tcb_set(_ptr: *mut u8) { - panic!("thread local data not implemented on wasm with atomics yet") - } - } else { - // stubbed out because no functions actually access these intrinsics - // unless atomics are enabled + MY_ID } } diff --git a/src/libstd/sys/wasm/thread_local.rs b/src/libstd/sys/wasm/thread_local.rs index 29e9854bcfc..8a0ca6f3d25 100644 --- a/src/libstd/sys/wasm/thread_local.rs +++ b/src/libstd/sys/wasm/thread_local.rs @@ -1,40 +1,26 @@ -use crate::boxed::Box; -use crate::ptr; - pub type Key = usize; -struct Allocated { - value: *mut u8, - dtor: Option<unsafe extern fn(*mut u8)>, -} - #[inline] -pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { - Box::into_raw(Box::new(Allocated { - value: ptr::null_mut(), - dtor, - })) as usize +pub unsafe fn create(_dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + panic!("should not be used on the wasm target"); } #[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - (*(key as *mut Allocated)).value = value; +pub unsafe fn set(_key: Key, _value: *mut u8) { + panic!("should not be used on the wasm target"); } #[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - (*(key as *mut Allocated)).value +pub unsafe fn get(_key: Key) -> *mut u8 { + panic!("should not be used on the wasm target"); } #[inline] -pub unsafe fn destroy(key: Key) { - let key = Box::from_raw(key as *mut Allocated); - if let Some(f) = key.dtor { - f(key.value); - } +pub unsafe fn destroy(_key: Key) { + panic!("should not be used on the wasm target"); } #[inline] pub fn requires_synchronized_create() -> bool { - false + panic!("should not be used on the wasm target"); } diff --git a/src/libstd/sys/wasm/thread_local_atomics.rs b/src/libstd/sys/wasm/thread_local_atomics.rs deleted file mode 100644 index b408ad0d5c1..00000000000 --- a/src/libstd/sys/wasm/thread_local_atomics.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::sys::thread; -use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; - -const MAX_KEYS: usize = 128; -static NEXT_KEY: AtomicUsize = AtomicUsize::new(0); - -struct ThreadControlBlock { - keys: [*mut u8; MAX_KEYS], -} - -impl ThreadControlBlock { - fn new() -> ThreadControlBlock { - ThreadControlBlock { - keys: [0 as *mut u8; MAX_KEYS], - } - } - - fn get() -> *mut ThreadControlBlock { - let ptr = thread::tcb_get(); - if !ptr.is_null() { - return ptr as *mut ThreadControlBlock - } - let tcb = Box::into_raw(Box::new(ThreadControlBlock::new())); - thread::tcb_set(tcb as *mut u8); - tcb - } -} - -pub type Key = usize; - -pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { - drop(dtor); // FIXME: need to figure out how to hook thread exit to run this - let key = NEXT_KEY.fetch_add(1, SeqCst); - if key >= MAX_KEYS { - NEXT_KEY.store(MAX_KEYS, SeqCst); - panic!("cannot allocate space for more TLS keys"); - } - // offset by 1 so we never hand out 0. This is currently required by - // `sys_common/thread_local.rs` where it can't cope with keys of value 0 - // because it messes up the atomic management. - return key + 1 -} - -pub unsafe fn set(key: Key, value: *mut u8) { - (*ThreadControlBlock::get()).keys[key - 1] = value; -} - -pub unsafe fn get(key: Key) -> *mut u8 { - (*ThreadControlBlock::get()).keys[key - 1] -} - -pub unsafe fn destroy(_key: Key) { - // FIXME: should implement this somehow, this isn't typically called but it - // can be called if two threads race to initialize a TLS slot and one ends - // up not being needed. -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - false -} diff --git a/src/libstd/sys/wasm/time.rs b/src/libstd/sys/wasm/time.rs index c1228a1b75e..3f71461eea4 100644 --- a/src/libstd/sys/wasm/time.rs +++ b/src/libstd/sys/wasm/time.rs @@ -22,8 +22,8 @@ impl Instant { false } - pub fn sub_instant(&self, other: &Instant) -> Duration { - self.0 - other.0 + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + self.0.checked_sub(other.0) } pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { diff --git a/src/libstd/sys/windows/args.rs b/src/libstd/sys/windows/args.rs index 3f10e6e5983..b04bb484eed 100644 --- a/src/libstd/sys/windows/args.rs +++ b/src/libstd/sys/windows/args.rs @@ -164,13 +164,13 @@ pub struct ArgsInnerDebug<'a> { } impl<'a> fmt::Debug for ArgsInnerDebug<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.args.parsed_args_list.as_slice().fmt(f) } } impl Args { - pub fn inner_debug(&self) -> ArgsInnerDebug { + pub fn inner_debug(&self) -> ArgsInnerDebug<'_> { ArgsInnerDebug { args: self } diff --git a/src/libstd/sys/windows/backtrace/backtrace_gnu.rs b/src/libstd/sys/windows/backtrace/backtrace_gnu.rs deleted file mode 100644 index 7ac1f8122f7..00000000000 --- a/src/libstd/sys/windows/backtrace/backtrace_gnu.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::io; -use crate::sys::c; -use crate::path::PathBuf; -use crate::fs::{OpenOptions, File}; -use crate::sys::ext::fs::OpenOptionsExt; -use crate::sys::handle::Handle; - -use libc::c_char; -use super::super::{fill_utf16_buf, os2path, to_u16s, wide_char_to_multi_byte}; - -fn query_full_process_image_name() -> io::Result<PathBuf> { - unsafe { - let process_handle = Handle::new(c::OpenProcess(c::PROCESS_QUERY_INFORMATION, - c::FALSE, - c::GetCurrentProcessId())); - fill_utf16_buf(|buf, mut sz| { - if c::QueryFullProcessImageNameW(process_handle.raw(), 0, buf, &mut sz) == 0 { - 0 - } else { - sz - } - }, os2path) - } -} - -fn lock_and_get_executable_filename() -> io::Result<(PathBuf, File)> { - // We query the current image name, open the file without FILE_SHARE_DELETE so it - // can't be moved and then get the current image name again. If the names are the - // same than we have successfully locked the file - let image_name1 = query_full_process_image_name()?; - let file = OpenOptions::new() - .read(true) - .share_mode(c::FILE_SHARE_READ | c::FILE_SHARE_WRITE) - .open(&image_name1)?; - let image_name2 = query_full_process_image_name()?; - - if image_name1 != image_name2 { - return Err(io::Error::new(io::ErrorKind::Other, - "executable moved while trying to lock it")); - } - - Ok((image_name1, file)) -} - -// Get the executable filename for libbacktrace -// This returns the path in the ANSI code page and a File which should remain open -// for as long as the path should remain valid -pub fn get_executable_filename() -> io::Result<(Vec<c_char>, File)> { - let (executable, file) = lock_and_get_executable_filename()?; - let u16_executable = to_u16s(executable.into_os_string())?; - Ok((wide_char_to_multi_byte(c::CP_ACP, c::WC_NO_BEST_FIT_CHARS, - &u16_executable, true)?, file)) -} diff --git a/src/libstd/sys/windows/backtrace/mod.rs b/src/libstd/sys/windows/backtrace/mod.rs deleted file mode 100644 index c5b0cc87210..00000000000 --- a/src/libstd/sys/windows/backtrace/mod.rs +++ /dev/null @@ -1,355 +0,0 @@ -//! As always, windows has something very different than unix, we mainly want -//! to avoid having to depend too much on libunwind for windows. -//! -//! If you google around, you'll find a fair bit of references to built-in -//! functions to get backtraces on windows. It turns out that most of these are -//! in an external library called dbghelp. I was unable to find this library -//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent -//! of it. -//! -//! You'll also find that there's a function called CaptureStackBackTrace -//! mentioned frequently (which is also easy to use), but sadly I didn't have a -//! copy of that function in my mingw install (maybe it was broken?). Instead, -//! this takes the route of using StackWalk64 in order to walk the stack. - -#![allow(deprecated)] // dynamic_lib - -use crate::io; -use crate::mem; -use crate::ptr; -use crate::sys::c; -use crate::sys::dynamic_lib::DynamicLibrary; -use crate::sys_common::backtrace::Frame; - -use libc::c_void; - -macro_rules! sym { - ($lib:expr, $e:expr, $t:ident) => ( - $lib.symbol($e).map(|f| unsafe { - $crate::mem::transmute::<usize, $t>(f) - }) - ) -} - -mod printing; - -#[cfg(target_env = "gnu")] -#[path = "backtrace_gnu.rs"] -pub mod gnu; - -pub use self::printing::{foreach_symbol_fileline, resolve_symname}; -use self::printing::{load_printing_fns_64, load_printing_fns_ex}; - -pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> { - let dbghelp = DynamicLibrary::open("dbghelp.dll")?; - - // Fetch the symbols necessary from dbghelp.dll - let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?; - let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?; - - // StackWalkEx might not be present and we'll fall back to StackWalk64 - let sw_var = match sym!(dbghelp, "StackWalkEx", StackWalkExFn) { - Ok(StackWalkEx) => { - StackWalkVariant::StackWalkEx(StackWalkEx, load_printing_fns_ex(&dbghelp)?) - } - Err(e) => match sym!(dbghelp, "StackWalk64", StackWalk64Fn) { - Ok(StackWalk64) => { - StackWalkVariant::StackWalk64(StackWalk64, load_printing_fns_64(&dbghelp)?) - } - Err(..) => return Err(e), - }, - }; - - // Allocate necessary structures for doing the stack walk - let process = unsafe { c::GetCurrentProcess() }; - - let backtrace_context = BacktraceContext { - handle: process, - SymCleanup, - StackWalkVariant: sw_var, - dbghelp, - }; - - // Initialize this process's symbols - let ret = unsafe { SymInitialize(process, ptr::null_mut(), c::TRUE) }; - if ret != c::TRUE { - return Ok((0, backtrace_context)); - } - - // And now that we're done with all the setup, do the stack walking! - match backtrace_context.StackWalkVariant { - StackWalkVariant::StackWalkEx(StackWalkEx, _) => { - set_frames(StackWalkEx, frames).map(|i| (i, backtrace_context)) - } - - StackWalkVariant::StackWalk64(StackWalk64, _) => { - set_frames(StackWalk64, frames).map(|i| (i, backtrace_context)) - } - } -} - -fn set_frames<W: StackWalker>(StackWalk: W, frames: &mut [Frame]) -> io::Result<usize> { - let process = unsafe { c::GetCurrentProcess() }; - let thread = unsafe { c::GetCurrentProcess() }; - let mut context: c::CONTEXT = unsafe { mem::zeroed() }; - unsafe { c::RtlCaptureContext(&mut context) }; - let mut frame = W::Item::new(); - let image = frame.init(&context); - - let mut i = 0; - while i < frames.len() - && StackWalk.walk(image, process, thread, &mut frame, &mut context) == c::TRUE - { - let addr = frame.get_addr(); - frames[i] = Frame { - symbol_addr: addr, - exact_position: addr, - inline_context: frame.get_inline_context(), - }; - - i += 1 - } - Ok(i) -} - -type SymInitializeFn = unsafe extern "system" fn(c::HANDLE, *mut c_void, c::BOOL) -> c::BOOL; -type SymCleanupFn = unsafe extern "system" fn(c::HANDLE) -> c::BOOL; - -type StackWalkExFn = unsafe extern "system" fn( - c::DWORD, - c::HANDLE, - c::HANDLE, - *mut c::STACKFRAME_EX, - *mut c::CONTEXT, - *mut c_void, - *mut c_void, - *mut c_void, - *mut c_void, - c::DWORD, -) -> c::BOOL; - -type StackWalk64Fn = unsafe extern "system" fn( - c::DWORD, - c::HANDLE, - c::HANDLE, - *mut c::STACKFRAME64, - *mut c::CONTEXT, - *mut c_void, - *mut c_void, - *mut c_void, - *mut c_void, -) -> c::BOOL; - -trait StackWalker { - type Item: StackFrame; - - fn walk( - &self, - _: c::DWORD, - _: c::HANDLE, - _: c::HANDLE, - _: &mut Self::Item, - _: &mut c::CONTEXT - ) -> c::BOOL; -} - -impl StackWalker for StackWalkExFn { - type Item = c::STACKFRAME_EX; - fn walk( - &self, - image: c::DWORD, - process: c::HANDLE, - thread: c::HANDLE, - frame: &mut Self::Item, - context: &mut c::CONTEXT, - ) -> c::BOOL { - unsafe { - self( - image, - process, - thread, - frame, - context, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - 0, - ) - } - } -} - -impl StackWalker for StackWalk64Fn { - type Item = c::STACKFRAME64; - fn walk( - &self, - image: c::DWORD, - process: c::HANDLE, - thread: c::HANDLE, - frame: &mut Self::Item, - context: &mut c::CONTEXT, - ) -> c::BOOL { - unsafe { - self( - image, - process, - thread, - frame, - context, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ) - } - } -} - -trait StackFrame { - fn new() -> Self; - fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD; - fn get_addr(&self) -> *const u8; - fn get_inline_context(&self) -> u32; -} - -impl StackFrame for c::STACKFRAME_EX { - fn new() -> c::STACKFRAME_EX { - unsafe { mem::zeroed() } - } - - #[cfg(target_arch = "x86")] - fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { - self.AddrPC.Offset = ctx.Eip as u64; - self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrStack.Offset = ctx.Esp as u64; - self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrFrame.Offset = ctx.Ebp as u64; - self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; - c::IMAGE_FILE_MACHINE_I386 - } - - #[cfg(target_arch = "x86_64")] - fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { - self.AddrPC.Offset = ctx.Rip as u64; - self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrStack.Offset = ctx.Rsp as u64; - self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrFrame.Offset = ctx.Rbp as u64; - self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; - c::IMAGE_FILE_MACHINE_AMD64 - } - - #[cfg(target_arch = "arm")] - fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { - self.AddrPC.Offset = ctx.Pc as u64; - self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrStack.Offset = ctx.Sp as u64; - self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrFrame.Offset = ctx.R11 as u64; - self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; - c::IMAGE_FILE_MACHINE_ARMNT - } - - #[cfg(target_arch = "aarch64")] - fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { - self.AddrPC.Offset = ctx.Pc as u64; - self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrStack.Offset = ctx.Sp as u64; - self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrFrame.Offset = ctx.Fp as u64; - self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; - c::IMAGE_FILE_MACHINE_ARM64 - } - - fn get_addr(&self) -> *const u8 { - (self.AddrPC.Offset - 1) as *const u8 - } - - fn get_inline_context(&self) -> u32 { - self.InlineFrameContext - } -} - -impl StackFrame for c::STACKFRAME64 { - fn new() -> c::STACKFRAME64 { - unsafe { mem::zeroed() } - } - - #[cfg(target_arch = "x86")] - fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { - self.AddrPC.Offset = ctx.Eip as u64; - self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrStack.Offset = ctx.Esp as u64; - self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrFrame.Offset = ctx.Ebp as u64; - self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; - c::IMAGE_FILE_MACHINE_I386 - } - - #[cfg(target_arch = "x86_64")] - fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { - self.AddrPC.Offset = ctx.Rip as u64; - self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrStack.Offset = ctx.Rsp as u64; - self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrFrame.Offset = ctx.Rbp as u64; - self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; - c::IMAGE_FILE_MACHINE_AMD64 - } - - #[cfg(target_arch = "arm")] - fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { - self.AddrPC.Offset = ctx.Pc as u64; - self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrStack.Offset = ctx.Sp as u64; - self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrFrame.Offset = ctx.R11 as u64; - self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; - c::IMAGE_FILE_MACHINE_ARMNT - } - - #[cfg(target_arch = "aarch64")] - fn init(&mut self, ctx: &c::CONTEXT) -> c::DWORD { - self.AddrPC.Offset = ctx.Pc as u64; - self.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrStack.Offset = ctx.Sp as u64; - self.AddrStack.Mode = c::ADDRESS_MODE::AddrModeFlat; - self.AddrFrame.Offset = ctx.Fp as u64; - self.AddrFrame.Mode = c::ADDRESS_MODE::AddrModeFlat; - c::IMAGE_FILE_MACHINE_ARM64 - } - - fn get_addr(&self) -> *const u8 { - (self.AddrPC.Offset - 1) as *const u8 - } - - fn get_inline_context(&self) -> u32 { - 0 - } -} - -enum StackWalkVariant { - StackWalkEx(StackWalkExFn, printing::PrintingFnsEx), - StackWalk64(StackWalk64Fn, printing::PrintingFns64), -} - -pub struct BacktraceContext { - handle: c::HANDLE, - SymCleanup: SymCleanupFn, - // Only used in printing for msvc and not gnu - // The gnu version is effectively a ZST dummy. - #[allow(dead_code)] - StackWalkVariant: StackWalkVariant, - // keeping DynamycLibrary loaded until its functions no longer needed - #[allow(dead_code)] - dbghelp: DynamicLibrary, -} - -impl Drop for BacktraceContext { - fn drop(&mut self) { - unsafe { - (self.SymCleanup)(self.handle); - } - } -} diff --git a/src/libstd/sys/windows/backtrace/printing/mod.rs b/src/libstd/sys/windows/backtrace/printing/mod.rs deleted file mode 100644 index 9497d51ac17..00000000000 --- a/src/libstd/sys/windows/backtrace/printing/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -#[cfg(target_env = "msvc")] -#[path = "msvc.rs"] -mod printing; - -#[cfg(target_env = "gnu")] -mod printing { - pub use crate::sys_common::gnu::libbacktrace::{foreach_symbol_fileline, resolve_symname}; - - // dummy functions to mirror those present in msvc version. - use crate::sys::dynamic_lib::DynamicLibrary; - use crate::io; - pub struct PrintingFnsEx {} - pub struct PrintingFns64 {} - pub fn load_printing_fns_ex(_: &DynamicLibrary) -> io::Result<PrintingFnsEx> { - Ok(PrintingFnsEx{}) - } - pub fn load_printing_fns_64(_: &DynamicLibrary) -> io::Result<PrintingFns64> { - Ok(PrintingFns64{}) - } -} - -pub use self::printing::{foreach_symbol_fileline, resolve_symname}; -pub use self::printing::{load_printing_fns_ex, load_printing_fns_64, - PrintingFnsEx, PrintingFns64}; diff --git a/src/libstd/sys/windows/backtrace/printing/msvc.rs b/src/libstd/sys/windows/backtrace/printing/msvc.rs deleted file mode 100644 index 13a1512d0eb..00000000000 --- a/src/libstd/sys/windows/backtrace/printing/msvc.rs +++ /dev/null @@ -1,208 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::sys::backtrace::BacktraceContext; -use crate::sys::backtrace::StackWalkVariant; -use crate::sys::c; -use crate::sys::dynamic_lib::DynamicLibrary; -use crate::sys_common::backtrace::Frame; - -use libc::{c_char, c_ulong}; - -// Structs holding printing functions and loaders for them -// Two versions depending on whether dbghelp.dll has StackWalkEx or not -// (the former being in newer Windows versions, the older being in Win7 and before) -pub struct PrintingFnsEx { - resolve_symname: SymFromInlineContextFn, - sym_get_line: SymGetLineFromInlineContextFn, -} -pub struct PrintingFns64 { - resolve_symname: SymFromAddrFn, - sym_get_line: SymGetLineFromAddr64Fn, -} - -pub fn load_printing_fns_ex(dbghelp: &DynamicLibrary) -> io::Result<PrintingFnsEx> { - Ok(PrintingFnsEx { - resolve_symname: sym!(dbghelp, "SymFromInlineContext", SymFromInlineContextFn)?, - sym_get_line: sym!( - dbghelp, - "SymGetLineFromInlineContext", - SymGetLineFromInlineContextFn - )?, - }) -} -pub fn load_printing_fns_64(dbghelp: &DynamicLibrary) -> io::Result<PrintingFns64> { - Ok(PrintingFns64 { - resolve_symname: sym!(dbghelp, "SymFromAddr", SymFromAddrFn)?, - sym_get_line: sym!(dbghelp, "SymGetLineFromAddr64", SymGetLineFromAddr64Fn)?, - }) -} - -type SymFromAddrFn = - unsafe extern "system" fn(c::HANDLE, u64, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL; -type SymFromInlineContextFn = - unsafe extern "system" fn(c::HANDLE, u64, c::ULONG, *mut u64, *mut c::SYMBOL_INFO) -> c::BOOL; - -type SymGetLineFromAddr64Fn = - unsafe extern "system" fn(c::HANDLE, u64, *mut u32, *mut c::IMAGEHLP_LINE64) -> c::BOOL; -type SymGetLineFromInlineContextFn = unsafe extern "system" fn( - c::HANDLE, - u64, - c::ULONG, - u64, - *mut c::DWORD, - *mut c::IMAGEHLP_LINE64, -) -> c::BOOL; - -/// Converts a pointer to symbol to its string value. -pub fn resolve_symname<F>(frame: Frame, callback: F, context: &BacktraceContext) -> io::Result<()> -where - F: FnOnce(Option<&str>) -> io::Result<()>, -{ - match context.StackWalkVariant { - StackWalkVariant::StackWalkEx(_, ref fns) => resolve_symname_internal( - |process: c::HANDLE, - symbol_address: u64, - inline_context: c::ULONG, - info: *mut c::SYMBOL_INFO| unsafe { - let mut displacement = 0u64; - (fns.resolve_symname)( - process, - symbol_address, - inline_context, - &mut displacement, - info, - ) - }, - frame, - callback, - context, - ), - StackWalkVariant::StackWalk64(_, ref fns) => resolve_symname_internal( - |process: c::HANDLE, - symbol_address: u64, - _inline_context: c::ULONG, - info: *mut c::SYMBOL_INFO| unsafe { - let mut displacement = 0u64; - (fns.resolve_symname)(process, symbol_address, &mut displacement, info) - }, - frame, - callback, - context, - ), - } -} - -fn resolve_symname_internal<F, R>( - mut symbol_resolver: R, - frame: Frame, - callback: F, - context: &BacktraceContext, -) -> io::Result<()> -where - F: FnOnce(Option<&str>) -> io::Result<()>, - R: FnMut(c::HANDLE, u64, c::ULONG, *mut c::SYMBOL_INFO) -> c::BOOL, -{ - unsafe { - let mut info: c::SYMBOL_INFO = mem::zeroed(); - info.MaxNameLen = c::MAX_SYM_NAME as c_ulong; - // the struct size in C. the value is different to - // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81) - // due to struct alignment. - info.SizeOfStruct = 88; - - let ret = symbol_resolver( - context.handle, - frame.symbol_addr as u64, - frame.inline_context, - &mut info, - ); - let valid_range = if ret == c::TRUE && frame.symbol_addr as usize >= info.Address as usize { - if info.Size != 0 { - (frame.symbol_addr as usize) < info.Address as usize + info.Size as usize - } else { - true - } - } else { - false - }; - let symname = if valid_range { - let ptr = info.Name.as_ptr() as *const c_char; - CStr::from_ptr(ptr).to_str().ok() - } else { - None - }; - callback(symname) - } -} - -pub fn foreach_symbol_fileline<F>( - frame: Frame, - callback: F, - context: &BacktraceContext, -) -> io::Result<bool> -where - F: FnMut(&[u8], u32) -> io::Result<()>, -{ - match context.StackWalkVariant { - StackWalkVariant::StackWalkEx(_, ref fns) => foreach_symbol_fileline_iternal( - |process: c::HANDLE, - frame_address: u64, - inline_context: c::ULONG, - line: *mut c::IMAGEHLP_LINE64| unsafe { - let mut displacement = 0u32; - (fns.sym_get_line)( - process, - frame_address, - inline_context, - 0, - &mut displacement, - line, - ) - }, - frame, - callback, - context, - ), - StackWalkVariant::StackWalk64(_, ref fns) => foreach_symbol_fileline_iternal( - |process: c::HANDLE, - frame_address: u64, - _inline_context: c::ULONG, - line: *mut c::IMAGEHLP_LINE64| unsafe { - let mut displacement = 0u32; - (fns.sym_get_line)(process, frame_address, &mut displacement, line) - }, - frame, - callback, - context, - ), - } -} - -fn foreach_symbol_fileline_iternal<F, G>( - mut line_getter: G, - frame: Frame, - mut callback: F, - context: &BacktraceContext, -) -> io::Result<bool> -where - F: FnMut(&[u8], u32) -> io::Result<()>, - G: FnMut(c::HANDLE, u64, c::ULONG, *mut c::IMAGEHLP_LINE64) -> c::BOOL, -{ - unsafe { - let mut line: c::IMAGEHLP_LINE64 = mem::zeroed(); - line.SizeOfStruct = mem::size_of::<c::IMAGEHLP_LINE64>() as u32; - - let ret = line_getter( - context.handle, - frame.exact_position as u64, - frame.inline_context, - &mut line, - ); - if ret == c::TRUE { - let name = CStr::from_ptr(line.Filename).to_bytes(); - callback(name, line.LineNumber as u32)?; - } - Ok(false) - } -} diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 518eccf754c..f706709c9cc 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -5,8 +5,6 @@ #![unstable(issue = "0", feature = "windows_c")] use crate::os::raw::{c_int, c_uint, c_ulong, c_long, c_longlong, c_ushort, c_char}; -#[cfg(target_arch = "x86_64")] -use crate::os::raw::c_ulonglong; use crate::ptr; use libc::{wchar_t, size_t, c_void}; @@ -33,16 +31,10 @@ pub type WORD = u16; pub type CHAR = c_char; pub type ULONG_PTR = usize; pub type ULONG = c_ulong; -#[cfg(target_arch = "x86_64")] -pub type ULONGLONG = u64; -#[cfg(target_arch = "x86_64")] -pub type DWORDLONG = ULONGLONG; pub type LPBOOL = *mut BOOL; pub type LPBYTE = *mut BYTE; -pub type LPBY_HANDLE_FILE_INFORMATION = *mut BY_HANDLE_FILE_INFORMATION; pub type LPCSTR = *const CHAR; -pub type LPCVOID = *const c_void; pub type LPCWSTR = *const WCHAR; pub type LPDWORD = *mut DWORD; pub type LPHANDLE = *mut HANDLE; @@ -108,11 +100,6 @@ pub const SECURITY_SQOS_PRESENT: DWORD = 0x00100000; pub const FIONBIO: c_ulong = 0x8004667e; -#[cfg(target_arch = "arm")] -const ARM_MAX_BREAKPOINTS: usize = 8; -#[cfg(target_arch = "arm")] -const ARM_MAX_WATCHPOINTS: usize = 1; - #[repr(C)] #[derive(Copy)] pub struct WIN32_FIND_DATAW { @@ -132,6 +119,7 @@ impl Clone for WIN32_FIND_DATAW { } pub const WSA_FLAG_OVERLAPPED: DWORD = 0x01; +pub const WSA_FLAG_NO_HANDLE_INHERIT: DWORD = 0x80; pub const WSADESCRIPTION_LEN: usize = 256; pub const WSASYS_STATUS_LEN: usize = 128; @@ -141,6 +129,7 @@ pub const INVALID_SOCKET: SOCKET = !0; pub const WSAEACCES: c_int = 10013; pub const WSAEINVAL: c_int = 10022; pub const WSAEWOULDBLOCK: c_int = 10035; +pub const WSAEPROTOTYPE: c_int = 10041; pub const WSAEADDRINUSE: c_int = 10048; pub const WSAEADDRNOTAVAIL: c_int = 10049; pub const WSAECONNABORTED: c_int = 10053; @@ -152,7 +141,6 @@ pub const WSAECONNREFUSED: c_int = 10061; pub const MAX_PROTOCOL_CHAIN: DWORD = 7; -pub const TOKEN_READ: DWORD = 0x20008; pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024; pub const FSCTL_GET_REPARSE_POINT: DWORD = 0x900a8; pub const IO_REPARSE_TAG_SYMLINK: DWORD = 0xa000000c; @@ -168,8 +156,6 @@ pub const STD_INPUT_HANDLE: DWORD = -10i32 as DWORD; pub const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD; pub const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD; -pub const HANDLE_FLAG_INHERIT: DWORD = 0x00000001; - pub const PROGRESS_CONTINUE: DWORD = 0; pub const ERROR_FILE_NOT_FOUND: DWORD = 2; @@ -270,26 +256,6 @@ pub const WAIT_OBJECT_0: DWORD = 0x00000000; pub const WAIT_TIMEOUT: DWORD = 258; pub const WAIT_FAILED: DWORD = 0xFFFFFFFF; -#[cfg(target_env = "msvc")] -#[cfg(feature = "backtrace")] -pub const MAX_SYM_NAME: usize = 2000; -#[cfg(target_arch = "x86")] -#[cfg(feature = "backtrace")] -pub const IMAGE_FILE_MACHINE_I386: DWORD = 0x014c; -#[cfg(target_arch = "x86_64")] -#[cfg(feature = "backtrace")] -pub const IMAGE_FILE_MACHINE_AMD64: DWORD = 0x8664; -#[cfg(target_arch = "aarch64")] -#[cfg(feature = "backtrace")] -pub const IMAGE_FILE_MACHINE_ARM64: DWORD = 0xAA64; -#[cfg(target_arch = "arm")] -#[cfg(feature = "backtrace")] -pub const IMAGE_FILE_MACHINE_ARMNT: DWORD = 0x01c4; - -pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0; -pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; -pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; - pub const PIPE_ACCESS_INBOUND: DWORD = 0x00000001; pub const PIPE_ACCESS_OUTBOUND: DWORD = 0x00000002; pub const FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD = 0x00080000; @@ -370,20 +336,6 @@ pub struct WIN32_FILE_ATTRIBUTE_DATA { } #[repr(C)] -pub struct BY_HANDLE_FILE_INFORMATION { - pub dwFileAttributes: DWORD, - pub ftCreationTime: FILETIME, - pub ftLastAccessTime: FILETIME, - pub ftLastWriteTime: FILETIME, - pub dwVolumeSerialNumber: DWORD, - pub nFileSizeHigh: DWORD, - pub nFileSizeLow: DWORD, - pub nNumberOfLinks: DWORD, - pub nFileIndexHigh: DWORD, - pub nFileIndexLow: DWORD, -} - -#[repr(C)] #[allow(dead_code)] // we only use some variants pub enum FILE_INFO_BY_HANDLE_CLASS { FileBasicInfo = 0, @@ -489,25 +441,6 @@ pub struct REPARSE_MOUNTPOINT_DATA_BUFFER { } #[repr(C)] -pub struct EXCEPTION_RECORD { - pub ExceptionCode: DWORD, - pub ExceptionFlags: DWORD, - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ExceptionAddress: LPVOID, - pub NumberParameters: DWORD, - pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS] -} - -#[repr(C)] -pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ContextRecord: *mut CONTEXT, -} - -pub type PVECTORED_EXCEPTION_HANDLER = extern "system" - fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; - -#[repr(C)] pub struct GUID { pub Data1: DWORD, pub Data2: WORD, @@ -581,41 +514,6 @@ pub struct OVERLAPPED { } #[repr(C)] -#[cfg(target_env = "msvc")] -#[cfg(feature = "backtrace")] -pub struct SYMBOL_INFO { - pub SizeOfStruct: c_ulong, - pub TypeIndex: c_ulong, - pub Reserved: [u64; 2], - pub Index: c_ulong, - pub Size: c_ulong, - pub ModBase: u64, - pub Flags: c_ulong, - pub Value: u64, - pub Address: u64, - pub Register: c_ulong, - pub Scope: c_ulong, - pub Tag: c_ulong, - pub NameLen: c_ulong, - pub MaxNameLen: c_ulong, - // note that windows has this as 1, but it basically just means that - // the name is inline at the end of the struct. For us, we just bump - // the struct size up to MAX_SYM_NAME. - pub Name: [c_char; MAX_SYM_NAME], -} - -#[repr(C)] -#[cfg(target_env = "msvc")] -#[cfg(feature = "backtrace")] -pub struct IMAGEHLP_LINE64 { - pub SizeOfStruct: u32, - pub Key: *const c_void, - pub LineNumber: u32, - pub Filename: *const c_char, - pub Address: u64, -} - -#[repr(C)] #[allow(dead_code)] // we only use some variants pub enum ADDRESS_MODE { AddrMode1616, @@ -625,280 +523,6 @@ pub enum ADDRESS_MODE { } #[repr(C)] -#[cfg(feature = "backtrace")] -pub struct ADDRESS64 { - pub Offset: u64, - pub Segment: u16, - pub Mode: ADDRESS_MODE, -} - -#[repr(C)] -#[cfg(feature = "backtrace")] -pub struct STACKFRAME_EX { - pub AddrPC: ADDRESS64, - pub AddrReturn: ADDRESS64, - pub AddrFrame: ADDRESS64, - pub AddrStack: ADDRESS64, - pub AddrBStore: ADDRESS64, - pub FuncTableEntry: *mut c_void, - pub Params: [u64; 4], - pub Far: BOOL, - pub Virtual: BOOL, - pub Reserved: [u64; 3], - pub KdHelp: KDHELP64, - pub StackFrameSize: DWORD, - pub InlineFrameContext: DWORD, -} - -#[repr(C)] -#[cfg(feature = "backtrace")] -pub struct STACKFRAME64 { - pub AddrPC: ADDRESS64, - pub AddrReturn: ADDRESS64, - pub AddrFrame: ADDRESS64, - pub AddrStack: ADDRESS64, - pub AddrBStore: ADDRESS64, - pub FuncTableEntry: *mut c_void, - pub Params: [u64; 4], - pub Far: BOOL, - pub Virtual: BOOL, - pub Reserved: [u64; 3], - pub KdHelp: KDHELP64, -} - -#[repr(C)] -#[cfg(feature = "backtrace")] -pub struct KDHELP64 { - pub Thread: u64, - pub ThCallbackStack: DWORD, - pub ThCallbackBStore: DWORD, - pub NextCallback: DWORD, - pub FramePointer: DWORD, - pub KiCallUserMode: u64, - pub KeUserCallbackDispatcher: u64, - pub SystemRangeStart: u64, - pub KiUserExceptionDispatcher: u64, - pub StackBase: u64, - pub StackLimit: u64, - pub Reserved: [u64; 5], -} - -#[cfg(target_arch = "x86")] -#[repr(C)] -pub struct CONTEXT { - pub ContextFlags: DWORD, - pub Dr0: DWORD, - pub Dr1: DWORD, - pub Dr2: DWORD, - pub Dr3: DWORD, - pub Dr6: DWORD, - pub Dr7: DWORD, - pub FloatSave: FLOATING_SAVE_AREA, - pub SegGs: DWORD, - pub SegFs: DWORD, - pub SegEs: DWORD, - pub SegDs: DWORD, - pub Edi: DWORD, - pub Esi: DWORD, - pub Ebx: DWORD, - pub Edx: DWORD, - pub Ecx: DWORD, - pub Eax: DWORD, - pub Ebp: DWORD, - pub Eip: DWORD, - pub SegCs: DWORD, - pub EFlags: DWORD, - pub Esp: DWORD, - pub SegSs: DWORD, - pub ExtendedRegisters: [u8; 512], -} - -#[cfg(target_arch = "x86")] -#[repr(C)] -pub struct FLOATING_SAVE_AREA { - pub ControlWord: DWORD, - pub StatusWord: DWORD, - pub TagWord: DWORD, - pub ErrorOffset: DWORD, - pub ErrorSelector: DWORD, - pub DataOffset: DWORD, - pub DataSelector: DWORD, - pub RegisterArea: [u8; 80], - pub Cr0NpxState: DWORD, -} - -#[cfg(target_arch = "x86_64")] -#[repr(C, align(16))] -pub struct CONTEXT { - pub P1Home: DWORDLONG, - pub P2Home: DWORDLONG, - pub P3Home: DWORDLONG, - pub P4Home: DWORDLONG, - pub P5Home: DWORDLONG, - pub P6Home: DWORDLONG, - - pub ContextFlags: DWORD, - pub MxCsr: DWORD, - - pub SegCs: WORD, - pub SegDs: WORD, - pub SegEs: WORD, - pub SegFs: WORD, - pub SegGs: WORD, - pub SegSs: WORD, - pub EFlags: DWORD, - - pub Dr0: DWORDLONG, - pub Dr1: DWORDLONG, - pub Dr2: DWORDLONG, - pub Dr3: DWORDLONG, - pub Dr6: DWORDLONG, - pub Dr7: DWORDLONG, - - pub Rax: DWORDLONG, - pub Rcx: DWORDLONG, - pub Rdx: DWORDLONG, - pub Rbx: DWORDLONG, - pub Rsp: DWORDLONG, - pub Rbp: DWORDLONG, - pub Rsi: DWORDLONG, - pub Rdi: DWORDLONG, - pub R8: DWORDLONG, - pub R9: DWORDLONG, - pub R10: DWORDLONG, - pub R11: DWORDLONG, - pub R12: DWORDLONG, - pub R13: DWORDLONG, - pub R14: DWORDLONG, - pub R15: DWORDLONG, - - pub Rip: DWORDLONG, - - pub FltSave: FLOATING_SAVE_AREA, - - pub VectorRegister: [M128A; 26], - pub VectorControl: DWORDLONG, - - pub DebugControl: DWORDLONG, - pub LastBranchToRip: DWORDLONG, - pub LastBranchFromRip: DWORDLONG, - pub LastExceptionToRip: DWORDLONG, - pub LastExceptionFromRip: DWORDLONG, -} - -#[cfg(target_arch = "x86_64")] -#[repr(C, align(16))] -pub struct M128A { - pub Low: c_ulonglong, - pub High: c_longlong -} - -#[cfg(target_arch = "x86_64")] -#[repr(C, align(16))] -pub struct FLOATING_SAVE_AREA { - _Dummy: [u8; 512] // FIXME: Fill this out -} - -#[cfg(target_arch = "arm")] -#[repr(C)] -pub struct CONTEXT { - pub ContextFlags: ULONG, - pub R0: ULONG, - pub R1: ULONG, - pub R2: ULONG, - pub R3: ULONG, - pub R4: ULONG, - pub R5: ULONG, - pub R6: ULONG, - pub R7: ULONG, - pub R8: ULONG, - pub R9: ULONG, - pub R10: ULONG, - pub R11: ULONG, - pub R12: ULONG, - pub Sp: ULONG, - pub Lr: ULONG, - pub Pc: ULONG, - pub Cpsr: ULONG, - pub Fpscr: ULONG, - pub Padding: ULONG, - pub D: [u64; 32], - pub Bvr: [ULONG; ARM_MAX_BREAKPOINTS], - pub Bcr: [ULONG; ARM_MAX_BREAKPOINTS], - pub Wvr: [ULONG; ARM_MAX_WATCHPOINTS], - pub Wcr: [ULONG; ARM_MAX_WATCHPOINTS], - pub Padding2: [ULONG; 2] -} - -// FIXME(#43348): This structure is used for backtrace only, and a fake -// definition is provided here only to allow rustdoc to pass type-check. This -// will not appear in the final documentation. This should be also defined for -// other architectures supported by Windows such as ARM, and for historical -// interest, maybe MIPS and PowerPC as well. -#[cfg(all(rustdoc, not(any(target_arch = "x86_64", target_arch = "x86", - target_arch = "aarch64", target_arch = "arm"))))] -pub enum CONTEXT {} - -#[cfg(target_arch = "aarch64")] -pub const ARM64_MAX_BREAKPOINTS: usize = 8; - -#[cfg(target_arch = "aarch64")] -pub const ARM64_MAX_WATCHPOINTS: usize = 2; - -#[cfg(target_arch = "aarch64")] -#[repr(C)] -pub struct ARM64_NT_NEON128 { - pub D: [f64; 2], -} - -#[cfg(target_arch = "aarch64")] -#[repr(C, align(16))] -pub struct CONTEXT { - pub ContextFlags: DWORD, - pub Cpsr: DWORD, - pub X0: u64, - pub X1: u64, - pub X2: u64, - pub X3: u64, - pub X4: u64, - pub X5: u64, - pub X6: u64, - pub X7: u64, - pub X8: u64, - pub X9: u64, - pub X10: u64, - pub X11: u64, - pub X12: u64, - pub X13: u64, - pub X14: u64, - pub X15: u64, - pub X16: u64, - pub X17: u64, - pub X18: u64, - pub X19: u64, - pub X20: u64, - pub X21: u64, - pub X22: u64, - pub X23: u64, - pub X24: u64, - pub X25: u64, - pub X26: u64, - pub X27: u64, - pub X28: u64, - pub Fp: u64, - pub Lr: u64, - pub Sp: u64, - pub Pc: u64, - pub V: [ARM64_NT_NEON128; 32], - pub Fpcr: DWORD, - pub Fpsr: DWORD, - pub Bcr: [DWORD; ARM64_MAX_BREAKPOINTS], - pub Bvr: [DWORD; ARM64_MAX_BREAKPOINTS], - pub Wcr: [DWORD; ARM64_MAX_WATCHPOINTS], - pub Wvr: [DWORD; ARM64_MAX_WATCHPOINTS], -} - -#[repr(C)] pub struct SOCKADDR_STORAGE_LH { pub ss_family: ADDRESS_FAMILY, pub __ss_pad1: [CHAR; 6], @@ -960,16 +584,6 @@ pub enum EXCEPTION_DISPOSITION { } #[repr(C)] -#[derive(Copy, Clone)] -pub struct CONSOLE_READCONSOLE_CONTROL { - pub nLength: ULONG, - pub nInitialChars: ULONG, - pub dwCtrlWakeupMask: ULONG, - pub dwControlKeyState: ULONG, -} -pub type PCONSOLE_READCONSOLE_CONTROL = *mut CONSOLE_READCONSOLE_CONTROL; - -#[repr(C)] #[derive(Copy)] pub struct fd_set { pub fd_count: c_uint, @@ -989,6 +603,134 @@ pub struct timeval { pub tv_usec: c_long, } +// Functions forbidden when targeting UWP +cfg_if::cfg_if! { +if #[cfg(not(target_vendor = "uwp"))] { + pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0; + pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; + pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; + + #[repr(C)] + pub struct EXCEPTION_RECORD { + pub ExceptionCode: DWORD, + pub ExceptionFlags: DWORD, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: LPVOID, + pub NumberParameters: DWORD, + pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS] + } + + pub enum CONTEXT {} + + #[repr(C)] + pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, + } + + pub type PVECTORED_EXCEPTION_HANDLER = extern "system" + fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct CONSOLE_READCONSOLE_CONTROL { + pub nLength: ULONG, + pub nInitialChars: ULONG, + pub dwCtrlWakeupMask: ULONG, + pub dwControlKeyState: ULONG, + } + + pub type PCONSOLE_READCONSOLE_CONTROL = *mut CONSOLE_READCONSOLE_CONTROL; + + #[repr(C)] + pub struct BY_HANDLE_FILE_INFORMATION { + pub dwFileAttributes: DWORD, + pub ftCreationTime: FILETIME, + pub ftLastAccessTime: FILETIME, + pub ftLastWriteTime: FILETIME, + pub dwVolumeSerialNumber: DWORD, + pub nFileSizeHigh: DWORD, + pub nFileSizeLow: DWORD, + pub nNumberOfLinks: DWORD, + pub nFileIndexHigh: DWORD, + pub nFileIndexLow: DWORD, + } + + pub type LPBY_HANDLE_FILE_INFORMATION = *mut BY_HANDLE_FILE_INFORMATION; + pub type LPCVOID = *const c_void; + + pub const HANDLE_FLAG_INHERIT: DWORD = 0x00000001; + + pub const TOKEN_READ: DWORD = 0x20008; + + extern "system" { + #[link_name = "SystemFunction036"] + pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN; + + pub fn ReadConsoleW(hConsoleInput: HANDLE, + lpBuffer: LPVOID, + nNumberOfCharsToRead: DWORD, + lpNumberOfCharsRead: LPDWORD, + pInputControl: PCONSOLE_READCONSOLE_CONTROL) -> BOOL; + + pub fn WriteConsoleW(hConsoleOutput: HANDLE, + lpBuffer: LPCVOID, + nNumberOfCharsToWrite: DWORD, + lpNumberOfCharsWritten: LPDWORD, + lpReserved: LPVOID) -> BOOL; + + pub fn GetConsoleMode(hConsoleHandle: HANDLE, + lpMode: LPDWORD) -> BOOL; + // Allowed but unused by UWP + pub fn OpenProcessToken(ProcessHandle: HANDLE, + DesiredAccess: DWORD, + TokenHandle: *mut HANDLE) -> BOOL; + pub fn GetUserProfileDirectoryW(hToken: HANDLE, + lpProfileDir: LPWSTR, + lpcchSize: *mut DWORD) -> BOOL; + pub fn GetFileInformationByHandle(hFile: HANDLE, + lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) + -> BOOL; + pub fn SetHandleInformation(hObject: HANDLE, + dwMask: DWORD, + dwFlags: DWORD) -> BOOL; + pub fn AddVectoredExceptionHandler(FirstHandler: ULONG, + VectoredHandler: PVECTORED_EXCEPTION_HANDLER) + -> LPVOID; + pub fn CreateHardLinkW(lpSymlinkFileName: LPCWSTR, + lpTargetFileName: LPCWSTR, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES) + -> BOOL; + } +} +} + +// UWP specific functions & types +cfg_if::cfg_if! { +if #[cfg(target_vendor = "uwp")] { + pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002; + + #[repr(C)] + pub struct FILE_STANDARD_INFO { + pub AllocationSize: LARGE_INTEGER, + pub EndOfFile: LARGE_INTEGER, + pub NumberOfLink: DWORD, + pub DeletePending: BOOLEAN, + pub Directory: BOOLEAN, + } + + extern "system" { + pub fn GetFileInformationByHandleEx(hFile: HANDLE, + fileInfoClass: FILE_INFO_BY_HANDLE_CLASS, + lpFileInformation: LPVOID, + dwBufferSize: DWORD) -> BOOL; + pub fn BCryptGenRandom(hAlgorithm: LPVOID, pBuffer: *mut u8, + cbBuffer: ULONG, dwFlags: ULONG) -> LONG; + } +} +} + +// Shared between Desktop & UWP extern "system" { pub fn WSAStartup(wVersionRequested: WORD, lpWSAData: LPWSADATA) -> c_int; @@ -1028,34 +770,13 @@ extern "system" { pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION); - pub fn ReadConsoleW(hConsoleInput: HANDLE, - lpBuffer: LPVOID, - nNumberOfCharsToRead: DWORD, - lpNumberOfCharsRead: LPDWORD, - pInputControl: PCONSOLE_READCONSOLE_CONTROL) -> BOOL; - - pub fn WriteConsoleW(hConsoleOutput: HANDLE, - lpBuffer: LPCVOID, - nNumberOfCharsToWrite: DWORD, - lpNumberOfCharsWritten: LPDWORD, - lpReserved: LPVOID) -> BOOL; - - pub fn GetConsoleMode(hConsoleHandle: HANDLE, - lpMode: LPDWORD) -> BOOL; pub fn RemoveDirectoryW(lpPathName: LPCWSTR) -> BOOL; pub fn SetFileAttributesW(lpFileName: LPCWSTR, dwFileAttributes: DWORD) -> BOOL; - pub fn GetFileInformationByHandle(hFile: HANDLE, - lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) - -> BOOL; - pub fn SetLastError(dwErrCode: DWORD); pub fn GetCommandLineW() -> *mut LPCWSTR; pub fn GetTempPathW(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD; - pub fn OpenProcessToken(ProcessHandle: HANDLE, - DesiredAccess: DWORD, - TokenHandle: *mut HANDLE) -> BOOL; pub fn GetCurrentProcess() -> HANDLE; pub fn GetCurrentThread() -> HANDLE; pub fn GetStdHandle(which: DWORD) -> HANDLE; @@ -1080,21 +801,12 @@ extern "system" { pub fn SwitchToThread() -> BOOL; pub fn Sleep(dwMilliseconds: DWORD); pub fn GetProcessId(handle: HANDLE) -> DWORD; - pub fn GetUserProfileDirectoryW(hToken: HANDLE, - lpProfileDir: LPWSTR, - lpcchSize: *mut DWORD) -> BOOL; - pub fn SetHandleInformation(hObject: HANDLE, - dwMask: DWORD, - dwFlags: DWORD) -> BOOL; pub fn CopyFileExW(lpExistingFileName: LPCWSTR, lpNewFileName: LPCWSTR, lpProgressRoutine: LPPROGRESS_ROUTINE, lpData: LPVOID, pbCancel: LPBOOL, dwCopyFlags: DWORD) -> BOOL; - pub fn AddVectoredExceptionHandler(FirstHandler: ULONG, - VectoredHandler: PVECTORED_EXCEPTION_HANDLER) - -> LPVOID; pub fn FormatMessageW(flags: DWORD, lpSrc: LPVOID, msgId: DWORD, @@ -1191,10 +903,6 @@ extern "system" { lpOverlapped: LPOVERLAPPED) -> BOOL; pub fn CloseHandle(hObject: HANDLE) -> BOOL; - pub fn CreateHardLinkW(lpSymlinkFileName: LPCWSTR, - lpTargetFileName: LPCWSTR, - lpSecurityAttributes: LPSECURITY_ATTRIBUTES) - -> BOOL; pub fn MoveFileExW(lpExistingFileName: LPCWSTR, lpNewFileName: LPCWSTR, dwFlags: DWORD) @@ -1220,8 +928,6 @@ extern "system" { pub fn FindNextFileW(findFile: HANDLE, findFileData: LPWIN32_FIND_DATAW) -> BOOL; pub fn FindClose(findFile: HANDLE) -> BOOL; - #[cfg(feature = "backtrace")] - pub fn RtlCaptureContext(ctx: *mut CONTEXT); pub fn getsockopt(s: SOCKET, level: c_int, optname: c_int, @@ -1252,10 +958,6 @@ extern "system" { res: *mut *mut ADDRINFOA) -> c_int; pub fn freeaddrinfo(res: *mut ADDRINFOA); - #[cfg(feature = "backtrace")] - pub fn LoadLibraryW(name: LPCWSTR) -> HMODULE; - #[cfg(feature = "backtrace")] - pub fn FreeLibrary(handle: HMODULE) -> BOOL; pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void; pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; @@ -1290,8 +992,6 @@ extern "system" { exceptfds: *mut fd_set, timeout: *const timeval) -> c_int; - #[link_name = "SystemFunction036"] - pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN; pub fn GetProcessHeap() -> HANDLE; pub fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; @@ -1315,6 +1015,7 @@ compat_fn! { _dwFlags: DWORD) -> DWORD { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 } + #[cfg(not(target_vendor = "uwp"))] pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 } @@ -1361,34 +1062,3 @@ compat_fn! { panic!("rwlocks not available") } } - -#[cfg(all(target_env = "gnu", feature = "backtrace"))] -mod gnu { - use super::*; - - pub const PROCESS_QUERY_INFORMATION: DWORD = 0x0400; - - pub const CP_ACP: UINT = 0; - - pub const WC_NO_BEST_FIT_CHARS: DWORD = 0x00000400; - - extern "system" { - pub fn OpenProcess(dwDesiredAccess: DWORD, - bInheritHandle: BOOL, - dwProcessId: DWORD) -> HANDLE; - } - - compat_fn! { - kernel32: - - pub fn QueryFullProcessImageNameW(_hProcess: HANDLE, - _dwFlags: DWORD, - _lpExeName: LPWSTR, - _lpdwSize: LPDWORD) -> BOOL { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 - } - } -} - -#[cfg(all(target_env = "gnu", feature = "backtrace"))] -pub use self::gnu::*; diff --git a/src/libstd/sys/windows/compat.rs b/src/libstd/sys/windows/compat.rs index 748c1616d1d..544b2087f92 100644 --- a/src/libstd/sys/windows/compat.rs +++ b/src/libstd/sys/windows/compat.rs @@ -37,12 +37,14 @@ pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, macro_rules! compat_fn { ($module:ident: $( + $(#[$meta:meta])* pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty { $($body:expr);* } )*) => ($( #[allow(unused_variables)] + $(#[$meta])* pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::mem; diff --git a/src/libstd/sys/windows/dynamic_lib.rs b/src/libstd/sys/windows/dynamic_lib.rs deleted file mode 100644 index b9d5105cb73..00000000000 --- a/src/libstd/sys/windows/dynamic_lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::os::windows::prelude::*; - -use crate::ffi::{CString, OsStr}; -use crate::io; -use crate::sys::c; - -pub struct DynamicLibrary { - handle: c::HMODULE, -} - -impl DynamicLibrary { - pub fn open(filename: &str) -> io::Result<DynamicLibrary> { - let filename = OsStr::new(filename) - .encode_wide() - .chain(Some(0)) - .collect::<Vec<_>>(); - let result = unsafe { - c::LoadLibraryW(filename.as_ptr()) - }; - if result.is_null() { - Err(io::Error::last_os_error()) - } else { - Ok(DynamicLibrary { handle: result }) - } - } - - pub fn symbol(&self, symbol: &str) -> io::Result<usize> { - let symbol = CString::new(symbol)?; - unsafe { - match c::GetProcAddress(self.handle, symbol.as_ptr()) as usize { - 0 => Err(io::Error::last_os_error()), - n => Ok(n), - } - } - } -} - -impl Drop for DynamicLibrary { - fn drop(&mut self) { - unsafe { - c::FreeLibrary(self.handle); - } - } -} diff --git a/src/libstd/sys/windows/ext/ffi.rs b/src/libstd/sys/windows/ext/ffi.rs index 547b1ef796b..1381825806f 100644 --- a/src/libstd/sys/windows/ext/ffi.rs +++ b/src/libstd/sys/windows/ext/ffi.rs @@ -131,12 +131,12 @@ pub trait OsStrExt { /// /// [`OsString::from_wide`]: ./trait.OsStringExt.html#tymethod.from_wide #[stable(feature = "rust1", since = "1.0.0")] - fn encode_wide(&self) -> EncodeWide; + fn encode_wide(&self) -> EncodeWide<'_>; } #[stable(feature = "rust1", since = "1.0.0")] impl OsStrExt for OsStr { - fn encode_wide(&self) -> EncodeWide { + fn encode_wide(&self) -> EncodeWide<'_> { self.as_inner().inner.encode_wide() } } diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index 268a14ff0aa..23964dc5bd5 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -437,6 +437,33 @@ pub trait MetadataExt { /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn file_size(&self) -> u64; + + /// Returns the value of the `dwVolumeSerialNumber` field of this + /// metadata. + /// + /// This will return `None` if the `Metadata` instance was created from a + /// call to `DirEntry::metadata`. If this `Metadata` was created by using + /// `fs::metadata` or `File::metadata`, then this will return `Some`. + #[unstable(feature = "windows_by_handle", issue = "63010")] + fn volume_serial_number(&self) -> Option<u32>; + + /// Returns the value of the `nNumberOfLinks` field of this + /// metadata. + /// + /// This will return `None` if the `Metadata` instance was created from a + /// call to `DirEntry::metadata`. If this `Metadata` was created by using + /// `fs::metadata` or `File::metadata`, then this will return `Some`. + #[unstable(feature = "windows_by_handle", issue = "63010")] + fn number_of_links(&self) -> Option<u32>; + + /// Returns the value of the `nFileIndex{Low,High}` fields of this + /// metadata. + /// + /// This will return `None` if the `Metadata` instance was created from a + /// call to `DirEntry::metadata`. If this `Metadata` was created by using + /// `fs::metadata` or `File::metadata`, then this will return `Some`. + #[unstable(feature = "windows_by_handle", issue = "63010")] + fn file_index(&self) -> Option<u64>; } #[stable(feature = "metadata_ext", since = "1.1.0")] @@ -446,6 +473,9 @@ impl MetadataExt for Metadata { fn last_access_time(&self) -> u64 { self.as_inner().accessed_u64() } fn last_write_time(&self) -> u64 { self.as_inner().modified_u64() } fn file_size(&self) -> u64 { self.as_inner().size() } + fn volume_serial_number(&self) -> Option<u32> { self.as_inner().volume_serial_number() } + fn number_of_links(&self) -> Option<u32> { self.as_inner().number_of_links() } + fn file_index(&self) -> Option<u64> { self.as_inner().file_index() } } /// Windows-specific extensions to [`FileType`]. diff --git a/src/libstd/sys/windows/ext/io.rs b/src/libstd/sys/windows/ext/io.rs index 1a7d734b89e..ec47c2e9d5a 100644 --- a/src/libstd/sys/windows/ext/io.rs +++ b/src/libstd/sys/windows/ext/io.rs @@ -83,6 +83,27 @@ impl AsRawHandle for io::Stderr { } } +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawHandle for io::StdinLock<'a> { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle } + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawHandle for io::StdoutLock<'a> { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle } + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawHandle for io::StderrLock<'a> { + fn as_raw_handle(&self) -> RawHandle { + unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle } + } +} + #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawHandle for fs::File { unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { diff --git a/src/libstd/sys/windows/fast_thread_local.rs b/src/libstd/sys/windows/fast_thread_local.rs index 0ccc67e3fd5..31d0bd1e72e 100644 --- a/src/libstd/sys/windows/fast_thread_local.rs +++ b/src/libstd/sys/windows/fast_thread_local.rs @@ -2,7 +2,3 @@ #![cfg(target_thread_local)] pub use crate::sys_common::thread_local::register_dtor_fallback as register_dtor; - -pub fn requires_move_before_drop() -> bool { - false -} diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index daf8aae24dd..5bae6ba4749 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -2,7 +2,7 @@ use crate::os::windows::prelude::*; use crate::ffi::OsString; use crate::fmt; -use crate::io::{self, Error, SeekFrom}; +use crate::io::{self, Error, SeekFrom, IoSlice, IoSliceMut}; use crate::mem; use crate::path::{Path, PathBuf}; use crate::ptr; @@ -25,6 +25,9 @@ pub struct FileAttr { last_write_time: c::FILETIME, file_size: u64, reparse_tag: c::DWORD, + volume_serial_number: Option<u32>, + number_of_links: Option<u32>, + file_index: Option<u64>, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -74,7 +77,7 @@ pub struct FilePermissions { attrs: c::DWORD } pub struct DirBuilder; impl fmt::Debug for ReadDir { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + 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) @@ -156,6 +159,9 @@ impl DirEntry { } else { 0 }, + volume_serial_number: None, + number_of_links: None, + file_index: None, }) } } @@ -287,19 +293,70 @@ impl File { Ok(()) } + #[cfg(not(target_vendor = "uwp"))] pub fn file_attr(&self) -> io::Result<FileAttr> { unsafe { let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); - cvt(c::GetFileInformationByHandle(self.handle.raw(), - &mut info))?; - let mut attr = FileAttr { + cvt(c::GetFileInformationByHandle(self.handle.raw(), &mut info))?; + let mut reparse_tag = 0; + if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + if let Ok((_, buf)) = self.reparse_point(&mut b) { + reparse_tag = buf.ReparseTag; + } + } + Ok(FileAttr { attributes: info.dwFileAttributes, creation_time: info.ftCreationTime, last_access_time: info.ftLastAccessTime, last_write_time: info.ftLastWriteTime, - file_size: ((info.nFileSizeHigh as u64) << 32) | (info.nFileSizeLow as u64), + file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32), + reparse_tag, + volume_serial_number: Some(info.dwVolumeSerialNumber), + number_of_links: Some(info.nNumberOfLinks), + file_index: Some((info.nFileIndexLow as u64) | + ((info.nFileIndexHigh as u64) << 32)), + }) + } + } + + #[cfg(target_vendor = "uwp")] + pub fn file_attr(&self) -> io::Result<FileAttr> { + unsafe { + let mut info: c::FILE_BASIC_INFO = mem::zeroed(); + let size = mem::size_of_val(&info); + cvt(c::GetFileInformationByHandleEx(self.handle.raw(), + c::FileBasicInfo, + &mut info as *mut _ as *mut libc::c_void, + size as c::DWORD))?; + let mut attr = FileAttr { + attributes: info.FileAttributes, + creation_time: c::FILETIME { + dwLowDateTime: info.CreationTime as c::DWORD, + dwHighDateTime: (info.CreationTime >> 32) as c::DWORD, + }, + last_access_time: c::FILETIME { + dwLowDateTime: info.LastAccessTime as c::DWORD, + dwHighDateTime: (info.LastAccessTime >> 32) as c::DWORD, + }, + last_write_time: c::FILETIME { + dwLowDateTime: info.LastWriteTime as c::DWORD, + dwHighDateTime: (info.LastWriteTime >> 32) as c::DWORD, + }, + file_size: 0, reparse_tag: 0, + volume_serial_number: None, + number_of_links: None, + file_index: None, }; + let mut info: c::FILE_STANDARD_INFO = mem::zeroed(); + let size = mem::size_of_val(&info); + cvt(c::GetFileInformationByHandleEx(self.handle.raw(), + c::FileStandardInfo, + &mut info as *mut _ as *mut libc::c_void, + size as c::DWORD))?; + attr.file_size = info.AllocationSize as u64; + attr.number_of_links = Some(info.NumberOfLinks); if attr.is_reparse_point() { let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; if let Ok((_, buf)) = self.reparse_point(&mut b) { @@ -314,6 +371,10 @@ impl File { self.handle.read(buf) } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.handle.read_vectored(bufs) + } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { self.handle.read_at(buf, offset) } @@ -322,6 +383,10 @@ impl File { self.handle.write(buf) } + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.handle.write_vectored(bufs) + } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { self.handle.write_at(buf, offset) } @@ -435,7 +500,7 @@ impl FromInner<c::HANDLE> for File { } impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME(#24570): add more info here (e.g., mode) let mut b = f.debug_struct("File"); b.field("handle", &self.handle.raw()); @@ -455,7 +520,9 @@ impl FileAttr { FilePermissions { attrs: self.attributes } } - pub fn attrs(&self) -> u32 { self.attributes as u32 } + pub fn attrs(&self) -> u32 { + self.attributes + } pub fn file_type(&self) -> FileType { FileType::new(self.attributes, self.reparse_tag) @@ -485,8 +552,16 @@ impl FileAttr { to_u64(&self.creation_time) } - fn is_reparse_point(&self) -> bool { - self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 + pub fn volume_serial_number(&self) -> Option<u32> { + self.volume_serial_number + } + + pub fn number_of_links(&self) -> Option<u32> { + self.number_of_links + } + + pub fn file_index(&self) -> Option<u64> { + self.file_index } } @@ -662,6 +737,7 @@ pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> { Ok(()) } +#[cfg(not(target_vendor = "uwp"))] pub fn link(src: &Path, dst: &Path) -> io::Result<()> { let src = to_u16s(src)?; let dst = to_u16s(dst)?; @@ -671,6 +747,12 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> { Ok(()) } +#[cfg(target_vendor = "uwp")] +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + return Err(io::Error::new(io::ErrorKind::Other, + "hard link are not supported on UWP")); +} + pub fn stat(path: &Path) -> io::Result<FileAttr> { let mut opts = OpenOptions::new(); // No read or write permissions are necessary diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index 02549088c87..3e5aa693354 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -1,7 +1,7 @@ #![unstable(issue = "0", feature = "windows_handle")] use crate::cmp; -use crate::io::{self, ErrorKind, Read}; +use crate::io::{self, ErrorKind, Read, IoSlice, IoSliceMut}; use crate::mem; use crate::ops::Deref; use crate::ptr; @@ -89,6 +89,10 @@ impl RawHandle { } } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + crate::io::default_read_vectored(|buf| self.read(buf), bufs) + } + 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; @@ -169,6 +173,10 @@ impl RawHandle { Ok(amt as usize) } + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + crate::io::default_write_vectored(|buf| self.write(buf), bufs) + } + 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; @@ -199,4 +207,8 @@ impl<'a> Read for &'a RawHandle { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + (**self).read_vectored(bufs) + } } diff --git a/src/libstd/sys/windows/io.rs b/src/libstd/sys/windows/io.rs index 54dd271c9d6..e44dcbe164d 100644 --- a/src/libstd/sys/windows/io.rs +++ b/src/libstd/sys/windows/io.rs @@ -3,16 +3,16 @@ use crate::slice; use crate::sys::c; #[repr(transparent)] -pub struct IoVec<'a> { +pub struct IoSlice<'a> { vec: c::WSABUF, _p: PhantomData<&'a [u8]>, } -impl<'a> IoVec<'a> { +impl<'a> IoSlice<'a> { #[inline] - pub fn new(buf: &'a [u8]) -> IoVec<'a> { + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { assert!(buf.len() <= c::ULONG::max_value() as usize); - IoVec { + IoSlice { vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_ptr() as *mut u8 as *mut c::CHAR, @@ -22,6 +22,18 @@ impl<'a> IoVec<'a> { } #[inline] + pub fn advance(&mut self, n: usize) { + if (self.vec.len as usize) < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.len -= n as c::ULONG; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize) @@ -29,16 +41,17 @@ impl<'a> IoVec<'a> { } } -pub struct IoVecMut<'a> { +#[repr(transparent)] +pub struct IoSliceMut<'a> { vec: c::WSABUF, _p: PhantomData<&'a mut [u8]>, } -impl<'a> IoVecMut<'a> { +impl<'a> IoSliceMut<'a> { #[inline] - pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> { + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { assert!(buf.len() <= c::ULONG::max_value() as usize); - IoVecMut { + IoSliceMut { vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_mut_ptr() as *mut c::CHAR, @@ -48,6 +61,18 @@ impl<'a> IoVecMut<'a> { } #[inline] + pub fn advance(&mut self, n: usize) { + if (self.vec.len as usize) < n { + panic!("advancing IoSliceMut beyond its length"); + } + + unsafe { + self.vec.len -= n as c::ULONG; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.vec.buf as *mut u8, self.vec.len as usize) diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 1425254a2e1..d59ac5959a6 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -14,13 +14,9 @@ pub use self::rand::hashmap_random_keys; pub mod alloc; pub mod args; -#[cfg(feature = "backtrace")] -pub mod backtrace; pub mod c; pub mod cmath; pub mod condvar; -#[cfg(feature = "backtrace")] -pub mod dynamic_lib; pub mod env; pub mod ext; pub mod fast_thread_local; @@ -37,11 +33,20 @@ pub mod pipe; pub mod process; pub mod rand; pub mod rwlock; -pub mod stack_overflow; pub mod thread; pub mod thread_local; pub mod time; -pub mod stdio; +cfg_if::cfg_if! { + if #[cfg(not(target_vendor = "uwp"))] { + pub mod stdio; + pub mod stack_overflow; + } else { + pub mod stdio_uwp; + pub mod stack_overflow_uwp; + pub use self::stdio_uwp as stdio; + pub use self::stack_overflow_uwp as stack_overflow; + } +} #[cfg(not(test))] pub fn init() { @@ -199,7 +204,7 @@ fn wide_char_to_multi_byte(code_page: u32, } } -pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { +pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] { match v.iter().position(|c| *c == 0) { // don't include the 0 Some(i) => &v[..i], diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs index 1aa910f05c9..37cbdcefced 100644 --- a/src/libstd/sys/windows/mutex.rs +++ b/src/libstd/sys/windows/mutex.rs @@ -154,7 +154,7 @@ unsafe impl Sync for ReentrantMutex {} impl ReentrantMutex { pub fn uninitialized() -> ReentrantMutex { - ReentrantMutex { inner: UnsafeCell::new(MaybeUninit::uninitialized()) } + ReentrantMutex { inner: UnsafeCell::new(MaybeUninit::uninit()) } } pub unsafe fn init(&mut self) { diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs index 1231fd55e25..32f4011fb32 100644 --- a/src/libstd/sys/windows/net.rs +++ b/src/libstd/sys/windows/net.rs @@ -1,7 +1,7 @@ #![unstable(issue = "0", feature = "windows_net")] use crate::cmp; -use crate::io::{self, Read, IoVec, IoVecMut}; +use crate::io::{self, Read, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{SocketAddr, Shutdown}; use crate::ptr; @@ -97,12 +97,26 @@ impl Socket { }; let socket = unsafe { match c::WSASocketW(fam, ty, 0, ptr::null_mut(), 0, - c::WSA_FLAG_OVERLAPPED) { - c::INVALID_SOCKET => Err(last_error()), + c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT) { + c::INVALID_SOCKET => { + match c::WSAGetLastError() { + c::WSAEPROTOTYPE => { + match c::WSASocketW(fam, ty, 0, ptr::null_mut(), 0, + c::WSA_FLAG_OVERLAPPED) { + c::INVALID_SOCKET => Err(last_error()), + n => { + let s = Socket(n); + s.set_no_inherit()?; + Ok(s) + }, + } + }, + n => Err(io::Error::from_raw_os_error(n)), + } + }, n => Ok(Socket(n)), } }?; - socket.set_no_inherit()?; Ok(socket) } @@ -168,7 +182,6 @@ impl Socket { n => Ok(Socket(n)), } }?; - socket.set_no_inherit()?; Ok(socket) } @@ -178,16 +191,34 @@ impl Socket { cvt(c::WSADuplicateSocketW(self.0, c::GetCurrentProcessId(), &mut info))?; + match c::WSASocketW(info.iAddressFamily, info.iSocketType, info.iProtocol, &mut info, 0, - c::WSA_FLAG_OVERLAPPED) { - c::INVALID_SOCKET => Err(last_error()), + c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT) { + c::INVALID_SOCKET => { + match c::WSAGetLastError() { + c::WSAEPROTOTYPE => { + match c::WSASocketW(info.iAddressFamily, + info.iSocketType, + info.iProtocol, + &mut info, 0, + c::WSA_FLAG_OVERLAPPED) { + c::INVALID_SOCKET => Err(last_error()), + n => { + let s = Socket(n); + s.set_no_inherit()?; + Ok(s) + }, + } + }, + n => Err(io::Error::from_raw_os_error(n)), + } + }, n => Ok(Socket(n)), } }?; - socket.set_no_inherit()?; Ok(socket) } @@ -208,7 +239,7 @@ impl Socket { self.recv_with_flags(buf, 0) } - pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD; @@ -268,7 +299,7 @@ impl Socket { self.recv_from_with_flags(buf, c::MSG_PEEK) } - pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD; let mut nwritten = 0; unsafe { @@ -312,6 +343,7 @@ impl Socket { } } + #[cfg(not(target_vendor = "uwp"))] fn set_no_inherit(&self) -> io::Result<()> { sys::cvt(unsafe { c::SetHandleInformation(self.0 as c::HANDLE, @@ -319,6 +351,11 @@ impl Socket { }).map(|_| ()) } + #[cfg(target_vendor = "uwp")] + fn set_no_inherit(&self) -> io::Result<()> { + Err(io::Error::new(io::ErrorKind::Other, "Unavailable on UWP")) + } + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { let how = match how { Shutdown::Write => c::SD_SEND, diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index 5b433ddfb4a..7c400dce686 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -13,7 +13,6 @@ use crate::path::{self, PathBuf}; use crate::ptr; use crate::slice; use crate::sys::{c, cvt}; -use crate::sys::handle::Handle; use super::to_u16s; @@ -136,7 +135,7 @@ pub struct SplitPaths<'a> { must_yield: bool, } -pub fn split_paths(unparsed: &OsStr) -> SplitPaths { +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { SplitPaths { data: unparsed.encode_wide(), must_yield: true, @@ -212,7 +211,7 @@ pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> } impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "path segment contains `\"`".fmt(f) } } @@ -284,10 +283,11 @@ pub fn temp_dir() -> PathBuf { }, super::os2path).unwrap() } -pub fn home_dir() -> Option<PathBuf> { - crate::env::var_os("HOME").or_else(|| { - crate::env::var_os("USERPROFILE") - }).map(PathBuf::from).or_else(|| unsafe { +#[cfg(not(target_vendor = "uwp"))] +fn home_dir_crt() -> Option<PathBuf> { + unsafe { + use crate::sys::handle::Handle; + let me = c::GetCurrentProcess(); let mut token = ptr::null_mut(); if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 { @@ -301,7 +301,18 @@ pub fn home_dir() -> Option<PathBuf> { _ => sz - 1, // sz includes the null terminator } }, super::os2path).ok() - }) + } +} + +#[cfg(target_vendor = "uwp")] +fn home_dir_crt() -> Option<PathBuf> { + None +} + +pub fn home_dir() -> Option<PathBuf> { + crate::env::var_os("HOME").or_else(|| { + crate::env::var_os("USERPROFILE") + }).map(PathBuf::from).or_else(|| home_dir_crt()) } pub fn exit(code: i32) -> ! { diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs index 8befa66ecdc..c7a82e09252 100644 --- a/src/libstd/sys/windows/os_str.rs +++ b/src/libstd/sys/windows/os_str.rs @@ -33,13 +33,13 @@ impl AsInner<Wtf8> for Buf { } impl fmt::Debug for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.as_slice(), formatter) } } impl fmt::Display for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self.as_slice(), formatter) } } @@ -49,13 +49,13 @@ pub struct Slice { } impl fmt::Debug for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.inner, formatter) } } impl fmt::Display for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.inner, formatter) } } @@ -139,7 +139,7 @@ impl Slice { self.inner.as_str() } - pub fn to_string_lossy(&self) -> Cow<str> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { self.inner.to_string_lossy() } diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs index b8532ca9b0d..7eae28cb14f 100644 --- a/src/libstd/sys/windows/path.rs +++ b/src/libstd/sys/windows/path.rs @@ -19,7 +19,7 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'\\' } -pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> { +pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> { use crate::path::Prefix::*; unsafe { // The unsafety here stems from converting between &OsStr and &[u8] diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index 07f4f5f0e58..041d5385eb6 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -1,7 +1,7 @@ use crate::os::windows::prelude::*; use crate::ffi::OsStr; -use crate::io; +use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; use crate::path::Path; use crate::ptr; @@ -37,15 +37,15 @@ pub struct Pipes { /// /// The ours/theirs pipes are *not* specifically readable or writable. Each /// one only supports a read or a write, but which is which depends on the -/// boolean flag given. If `ours_readable` is true then `ours` is readable where -/// `theirs` is writable. Conversely if `ours_readable` is false then `ours` is -/// writable where `theirs` is readable. +/// boolean flag given. If `ours_readable` is `true`, then `ours` is readable and +/// `theirs` is writable. Conversely, if `ours_readable` is `false`, then `ours` +/// is writable and `theirs` is readable. /// /// Also note that the `ours` pipe is always a handle opened up in overlapped /// mode. This means that technically speaking it should only ever be used /// with `OVERLAPPED` instances, but also works out ok if it's only ever used /// once at a time (which we do indeed guarantee). -pub fn anon_pipe(ours_readable: bool) -> io::Result<Pipes> { +pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Result<Pipes> { // Note that we specifically do *not* use `CreatePipe` here because // unfortunately the anonymous pipes returned do not support overlapped // operations. Instead, we create a "hopefully unique" name and create a @@ -137,6 +137,13 @@ pub fn anon_pipe(ours_readable: bool) -> io::Result<Pipes> { opts.write(ours_readable); opts.read(!ours_readable); opts.share_mode(0); + let size = mem::size_of::<c::SECURITY_ATTRIBUTES>(); + let mut sa = c::SECURITY_ATTRIBUTES { + nLength: size as c::DWORD, + lpSecurityDescriptor: ptr::null_mut(), + bInheritHandle: their_handle_inheritable as i32, + }; + opts.security_attributes(&mut sa); let theirs = File::open(Path::new(&name), &opts)?; let theirs = AnonPipe { inner: theirs.into_handle() }; @@ -166,9 +173,17 @@ impl AnonPipe { self.inner.read(buf) } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.inner.read_vectored(bufs) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.inner.write(buf) } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.inner.write_vectored(bufs) + } } pub fn read2(p1: AnonPipe, @@ -334,7 +349,7 @@ impl<'a> Drop for AsyncPipe<'a> { // If anything here fails, there's not really much we can do, so we leak // the buffer/OVERLAPPED pointers to ensure we're at least memory safe. if self.pipe.cancel_io().is_err() || self.result().is_err() { - let buf = mem::replace(self.dst, Vec::new()); + let buf = mem::take(self.dst); let overlapped = Box::new(unsafe { mem::zeroed() }); let overlapped = mem::replace(&mut self.overlapped, overlapped); mem::forget((buf, overlapped)); diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 95f061d22bd..05e0ca67064 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -219,7 +219,7 @@ impl Command { } impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.program)?; for arg in &self.args { write!(f, " {:?}", arg)?; @@ -267,13 +267,8 @@ impl Stdio { Stdio::MakePipe => { let ours_readable = stdio_id != c::STD_INPUT_HANDLE; - let pipes = pipe::anon_pipe(ours_readable)?; + let pipes = pipe::anon_pipe(ours_readable, true)?; *pipe = Some(pipes.ours); - cvt(unsafe { - c::SetHandleInformation(pipes.theirs.handle().raw(), - c::HANDLE_FLAG_INHERIT, - c::HANDLE_FLAG_INHERIT) - })?; Ok(pipes.theirs.into_handle()) } @@ -393,7 +388,7 @@ impl From<c::DWORD> for ExitStatus { } impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Windows exit codes with the high bit set typically mean some form of // unhandled exception or warning. In this scenario printing the exit // code in decimal doesn't always make sense because it's a very large diff --git a/src/libstd/sys/windows/rand.rs b/src/libstd/sys/windows/rand.rs index 0193f4defa1..c9bcb5d7415 100644 --- a/src/libstd/sys/windows/rand.rs +++ b/src/libstd/sys/windows/rand.rs @@ -2,6 +2,7 @@ use crate::io; use crate::mem; use crate::sys::c; +#[cfg(not(target_vendor = "uwp"))] pub fn hashmap_random_keys() -> (u64, u64) { let mut v = (0, 0); let ret = unsafe { @@ -14,3 +15,20 @@ pub fn hashmap_random_keys() -> (u64, u64) { } return v } + +#[cfg(target_vendor = "uwp")] +pub fn hashmap_random_keys() -> (u64, u64) { + use crate::ptr; + + let mut v = (0, 0); + let ret = unsafe { + c::BCryptGenRandom(ptr::null_mut(), &mut v as *mut _ as *mut u8, + mem::size_of_val(&v) as c::ULONG, + c::BCRYPT_USE_SYSTEM_PREFERRED_RNG) + }; + if ret != 0 { + panic!("couldn't generate random bytes: {}", + io::Error::last_os_error()); + } + return v +} diff --git a/src/libstd/sys/redox/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow_uwp.rs index cf01d323d45..e7236cf359c 100644 --- a/src/libstd/sys/redox/stack_overflow.rs +++ b/src/libstd/sys/windows/stack_overflow_uwp.rs @@ -3,15 +3,11 @@ pub struct Handler; impl Handler { - pub unsafe fn new() -> Handler { + pub fn new() -> Handler { Handler } } -pub unsafe fn init() { +pub unsafe fn init() {} -} - -pub unsafe fn cleanup() { - -} +pub unsafe fn cleanup() {} diff --git a/src/libstd/sys/windows/stdio_uwp.rs b/src/libstd/sys/windows/stdio_uwp.rs new file mode 100644 index 00000000000..489d3df2860 --- /dev/null +++ b/src/libstd/sys/windows/stdio_uwp.rs @@ -0,0 +1,85 @@ +#![unstable(issue = "0", feature = "windows_stdio")] + +use crate::io; +use crate::sys::c; +use crate::sys::handle::Handle; +use crate::mem::ManuallyDrop; + +pub struct Stdin { +} +pub struct Stdout; +pub struct Stderr; + +const MAX_BUFFER_SIZE: usize = 8192; +pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3; + +pub fn get_handle(handle_id: c::DWORD) -> io::Result<c::HANDLE> { + let handle = unsafe { c::GetStdHandle(handle_id) }; + if handle == c::INVALID_HANDLE_VALUE { + Err(io::Error::last_os_error()) + } else if handle.is_null() { + Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32)) + } else { + Ok(handle) + } +} + +fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result<usize> { + let handle = get_handle(handle_id)?; + let handle = Handle::new(handle); + ManuallyDrop::new(handle).write(data) +} + +impl Stdin { + pub fn new() -> io::Result<Stdin> { + Ok(Stdin { }) + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let handle = get_handle(c::STD_INPUT_HANDLE)?; + let handle = Handle::new(handle); + ManuallyDrop::new(handle).read(buf) + } +} + +impl Stdout { + pub fn new() -> io::Result<Stdout> { + Ok(Stdout) + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + write(c::STD_OUTPUT_HANDLE, buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub fn new() -> io::Result<Stderr> { + Ok(Stderr) + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + write(c::STD_ERROR_HANDLE, buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32) +} + +pub fn panic_output() -> Option<impl io::Write> { + Stderr::new().ok() +} diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index 1b0a811f13b..ebdf3612e06 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -1,4 +1,3 @@ -use crate::boxed::FnBox; use crate::io; use crate::ffi::CStr; use crate::mem; @@ -20,7 +19,7 @@ pub struct Thread { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>) + pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { let p = box p; diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs index 2c99bca7009..e0f0e3a1a45 100644 --- a/src/libstd/sys/windows/time.rs +++ b/src/libstd/sys/windows/time.rs @@ -49,17 +49,17 @@ impl Instant { Instant { t: Duration::from_secs(0) } } - pub fn sub_instant(&self, other: &Instant) -> Duration { + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { // On windows there's a threshold below which we consider two timestamps // equivalent due to measurement error. For more details + doc link, // check the docs on epsilon. let epsilon = perf_counter::PerformanceCounterInstant::epsilon(); if other.t > self.t && other.t - self.t <= epsilon { - return Duration::new(0, 0) + Some(Duration::new(0, 0)) + } else { + self.t.checked_sub(other.t) } - self.t.checked_sub(other.t) - .expect("specified instant was later than self") } pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { @@ -139,7 +139,7 @@ impl Ord for SystemTime { } impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("intervals", &self.intervals()) .finish() @@ -173,7 +173,7 @@ fn intervals2dur(intervals: u64) -> Duration { mod perf_counter { use super::{NANOS_PER_SEC}; - use crate::sync::Once; + use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use crate::sys_common::mul_div_u64; use crate::sys::c; use crate::sys::cvt; @@ -210,13 +210,25 @@ mod perf_counter { fn frequency() -> c::LARGE_INTEGER { static mut FREQUENCY: c::LARGE_INTEGER = 0; - static ONCE: Once = Once::new(); + static STATE: AtomicUsize = AtomicUsize::new(0); unsafe { - ONCE.call_once(|| { - cvt(c::QueryPerformanceFrequency(&mut FREQUENCY)).unwrap(); - }); - FREQUENCY + // If a previous thread has filled in this global state, use that. + if STATE.load(SeqCst) == 2 { + return FREQUENCY; + } + + // ... otherwise learn for ourselves ... + let mut frequency = 0; + cvt(c::QueryPerformanceFrequency(&mut frequency)).unwrap(); + + // ... and attempt to be the one thread that stores it globally for + // all other threads + if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { + FREQUENCY = frequency; + STATE.store(2, SeqCst); + } + return frequency; } } diff --git a/src/libstd/sys_common/alloc.rs b/src/libstd/sys_common/alloc.rs index 978a70bee09..1cfc7ed17f2 100644 --- a/src/libstd/sys_common/alloc.rs +++ b/src/libstd/sys_common/alloc.rs @@ -12,7 +12,8 @@ use crate::ptr; target_arch = "powerpc", target_arch = "powerpc64", target_arch = "asmjs", - target_arch = "wasm32")))] + target_arch = "wasm32", + target_arch = "hexagon")))] pub const MIN_ALIGN: usize = 8; #[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64", diff --git a/src/libstd/sys_common/at_exit_imp.rs b/src/libstd/sys_common/at_exit_imp.rs index 1181b861611..cdb72ee872e 100644 --- a/src/libstd/sys_common/at_exit_imp.rs +++ b/src/libstd/sys_common/at_exit_imp.rs @@ -2,12 +2,11 @@ //! //! Documentation can be found on the `rt::at_exit` function. -use crate::boxed::FnBox; use crate::ptr; use crate::mem; use crate::sys_common::mutex::Mutex; -type Queue = Vec<Box<dyn FnBox()>>; +type Queue = Vec<Box<dyn FnOnce()>>; // NB these are specifically not types from `std::sync` as they currently rely // on poisoning and this module needs to operate at a lower level than requiring @@ -61,7 +60,7 @@ pub fn cleanup() { } } -pub fn push(f: Box<dyn FnBox()>) -> bool { +pub fn push(f: Box<dyn FnOnce()>) -> bool { unsafe { let _guard = LOCK.lock(); if init() { diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs index 1a80908779e..bf37ff7ddbd 100644 --- a/src/libstd/sys_common/backtrace.rs +++ b/src/libstd/sys_common/backtrace.rs @@ -2,40 +2,17 @@ /// supported platforms. use crate::env; -use crate::io::prelude::*; use crate::io; +use crate::io::prelude::*; +use crate::mem; use crate::path::{self, Path}; use crate::ptr; -use crate::str; use crate::sync::atomic::{self, Ordering}; use crate::sys::mutex::Mutex; -use rustc_demangle::demangle; - -pub use crate::sys::backtrace::{ - unwind_backtrace, - resolve_symname, - foreach_symbol_fileline, - BacktraceContext -}; - -#[cfg(target_pointer_width = "64")] -pub const HEX_WIDTH: usize = 18; - -#[cfg(target_pointer_width = "32")] -pub const HEX_WIDTH: usize = 10; - -/// Represents an item in the backtrace list. See `unwind_backtrace` for how -/// it is created. -#[derive(Debug, Copy, Clone)] -pub struct Frame { - /// Exact address of the call that failed. - pub exact_position: *const u8, - /// Address of the enclosing function. - pub symbol_addr: *const u8, - /// Which inlined function is this frame referring to - pub inline_context: u32, -} +use backtrace::{BytesOrWideString, Frame, Symbol}; + +pub const HEX_WIDTH: usize = 2 + 2 * mem::size_of::<usize>(); /// Max number of frames to print. const MAX_NB_FRAMES: usize = 100; @@ -49,7 +26,7 @@ pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> { // test mode immediately return here to optimize away any references to the // libbacktrace symbols if cfg!(test) { - return Ok(()) + return Ok(()); } // Use a lock to prevent mixed output in multithreading context. @@ -63,75 +40,39 @@ pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> { } fn _print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> { - let mut frames = [Frame { - exact_position: ptr::null(), - symbol_addr: ptr::null(), - inline_context: 0, - }; MAX_NB_FRAMES]; - let (nb_frames, context) = unwind_backtrace(&mut frames)?; - let (skipped_before, skipped_after) = - filter_frames(&frames[..nb_frames], format, &context); - if skipped_before + skipped_after > 0 { - writeln!(w, "note: Some details are omitted, \ - run with `RUST_BACKTRACE=full` for a verbose backtrace.")?; - } writeln!(w, "stack backtrace:")?; - let filtered_frames = &frames[..nb_frames - skipped_after]; - for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() { - resolve_symname(*frame, |symname| { - output(w, index, *frame, symname, format) - }, &context)?; - let has_more_filenames = foreach_symbol_fileline(*frame, |file, line| { - output_fileline(w, file, line, format) - }, &context)?; - if has_more_filenames { - w.write_all(b" <... and possibly more>")?; - } - } - - Ok(()) -} - -/// Returns a number of frames to remove at the beginning and at the end of the -/// backtrace, according to the backtrace format. -fn filter_frames(frames: &[Frame], - format: PrintFormat, - context: &BacktraceContext) -> (usize, usize) -{ - if format == PrintFormat::Full { - return (0, 0); - } - - let skipped_before = 0; - - let skipped_after = frames.len() - frames.iter().position(|frame| { - let mut is_marker = false; - let _ = resolve_symname(*frame, |symname| { - if let Some(mangled_symbol_name) = symname { - // Use grep to find the concerned functions - if mangled_symbol_name.contains("__rust_begin_short_backtrace") { - is_marker = true; - } + let mut printer = Printer::new(format, w); + unsafe { + backtrace::trace_unsynchronized(|frame| { + let mut hit = false; + backtrace::resolve_frame_unsynchronized(frame, |symbol| { + hit = true; + printer.output(frame, Some(symbol)); + }); + if !hit { + printer.output(frame, None); } - Ok(()) - }, context); - is_marker - }).unwrap_or(frames.len()); - - if skipped_before + skipped_after >= frames.len() { - // Avoid showing completely empty backtraces - return (0, 0); + !printer.done + }); } - - (skipped_before, skipped_after) + if printer.skipped { + writeln!( + w, + "note: Some details are omitted, \ + run with `RUST_BACKTRACE=full` for a verbose backtrace." + )?; + } + Ok(()) } - /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. #[inline(never)] pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T - where F: FnOnce() -> T, F: Send, T: Send +where + F: FnOnce() -> T, + F: Send, + T: Send, { f() } @@ -156,7 +97,7 @@ pub fn log_enabled() -> Option<PrintFormat> { _ => return Some(PrintFormat::Full), } - let val = env::var_os("RUST_BACKTRACE").and_then(|x| + let val = env::var_os("RUST_BACKTRACE").and_then(|x| { if &x == "0" { None } else if &x == "full" { @@ -164,81 +105,141 @@ pub fn log_enabled() -> Option<PrintFormat> { } else { Some(PrintFormat::Short) } + }); + ENABLED.store( + match val { + Some(v) => v as isize, + None => 1, + }, + Ordering::SeqCst, ); - ENABLED.store(match val { - Some(v) => v as isize, - None => 1, - }, Ordering::SeqCst); val } -/// Prints the symbol of the backtrace frame. -/// -/// These output functions should now be used everywhere to ensure consistency. -/// You may want to also use `output_fileline`. -fn output(w: &mut dyn Write, idx: usize, frame: Frame, - s: Option<&str>, format: PrintFormat) -> io::Result<()> { - // Remove the `17: 0x0 - <unknown>` line. - if format == PrintFormat::Short && frame.exact_position == ptr::null() { - return Ok(()); +struct Printer<'a, 'b> { + format: PrintFormat, + done: bool, + skipped: bool, + idx: usize, + out: &'a mut (dyn Write + 'b), +} + +impl<'a, 'b> Printer<'a, 'b> { + fn new(format: PrintFormat, out: &'a mut (dyn Write + 'b)) -> Printer<'a, 'b> { + Printer { format, done: false, skipped: false, idx: 0, out } } - match format { - PrintFormat::Full => write!(w, - " {:2}: {:2$?} - ", - idx, - frame.exact_position, - HEX_WIDTH)?, - PrintFormat::Short => write!(w, " {:2}: ", idx)?, + + /// Prints the symbol of the backtrace frame. + /// + /// These output functions should now be used everywhere to ensure consistency. + /// You may want to also use `output_fileline`. + fn output(&mut self, frame: &Frame, symbol: Option<&Symbol>) { + if self.idx > MAX_NB_FRAMES { + self.done = true; + self.skipped = true; + return; + } + if self._output(frame, symbol).is_err() { + self.done = true; + } + self.idx += 1; } - match s { - Some(string) => { - let symbol = demangle(string); - match format { - PrintFormat::Full => write!(w, "{}", symbol)?, - // strip the trailing hash if short mode - PrintFormat::Short => write!(w, "{:#}", symbol)?, + + fn _output(&mut self, frame: &Frame, symbol: Option<&Symbol>) -> io::Result<()> { + if self.format == PrintFormat::Short { + if let Some(sym) = symbol.and_then(|s| s.name()).and_then(|s| s.as_str()) { + if sym.contains("__rust_begin_short_backtrace") { + self.skipped = true; + self.done = true; + return Ok(()); + } + } + + // Remove the `17: 0x0 - <unknown>` line. + if self.format == PrintFormat::Short && frame.ip() == ptr::null_mut() { + self.skipped = true; + return Ok(()); } } - None => w.write_all(b"<unknown>")?, - } - w.write_all(b"\n") -} -/// Prints the filename and line number of the backtrace frame. -/// -/// See also `output`. -#[allow(dead_code)] -fn output_fileline(w: &mut dyn Write, - file: &[u8], - line: u32, - format: PrintFormat) -> io::Result<()> { - // prior line: " ##: {:2$} - func" - w.write_all(b"")?; - match format { - PrintFormat::Full => write!(w, - " {:1$}", - "", - HEX_WIDTH)?, - PrintFormat::Short => write!(w, " ")?, - } + match self.format { + PrintFormat::Full => { + write!(self.out, " {:2}: {:2$?} - ", self.idx, frame.ip(), HEX_WIDTH)? + } + PrintFormat::Short => write!(self.out, " {:2}: ", self.idx)?, + } - let file = str::from_utf8(file).unwrap_or("<unknown>"); - let file_path = Path::new(file); - let mut already_printed = false; - if format == PrintFormat::Short && file_path.is_absolute() { - if let Ok(cwd) = env::current_dir() { - if let Ok(stripped) = file_path.strip_prefix(&cwd) { - if let Some(s) = stripped.to_str() { - write!(w, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?; - already_printed = true; + match symbol.and_then(|s| s.name()) { + Some(symbol) => { + match self.format { + PrintFormat::Full => write!(self.out, "{}", symbol)?, + // Strip the trailing hash if short mode. + PrintFormat::Short => write!(self.out, "{:#}", symbol)?, } } + None => self.out.write_all(b"<unknown>")?, } - } - if !already_printed { - write!(w, " at {}:{}", file, line)?; + self.out.write_all(b"\n")?; + if let Some(sym) = symbol { + self.output_fileline(sym)?; + } + Ok(()) } - w.write_all(b"\n") -} + /// Prints the filename and line number of the backtrace frame. + /// + /// See also `output`. + fn output_fileline(&mut self, symbol: &Symbol) -> io::Result<()> { + #[cfg(windows)] + let path_buf; + let file = match symbol.filename_raw() { + #[cfg(unix)] + Some(BytesOrWideString::Bytes(bytes)) => { + use crate::os::unix::prelude::*; + Path::new(crate::ffi::OsStr::from_bytes(bytes)) + } + #[cfg(not(unix))] + Some(BytesOrWideString::Bytes(bytes)) => { + Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")) + } + #[cfg(windows)] + Some(BytesOrWideString::Wide(wide)) => { + use crate::os::windows::prelude::*; + path_buf = crate::ffi::OsString::from_wide(wide); + Path::new(&path_buf) + } + #[cfg(not(windows))] + Some(BytesOrWideString::Wide(_wide)) => { + Path::new("<unknown>") + } + None => return Ok(()), + }; + let line = match symbol.lineno() { + Some(line) => line, + None => return Ok(()), + }; + // prior line: " ##: {:2$} - func" + self.out.write_all(b"")?; + match self.format { + PrintFormat::Full => write!(self.out, " {:1$}", "", HEX_WIDTH)?, + PrintFormat::Short => write!(self.out, " ")?, + } + let mut already_printed = false; + if self.format == PrintFormat::Short && file.is_absolute() { + if let Ok(cwd) = env::current_dir() { + if let Ok(stripped) = file.strip_prefix(&cwd) { + if let Some(s) = stripped.to_str() { + write!(self.out, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?; + already_printed = true; + } + } + } + } + if !already_printed { + write!(self.out, " at {}:{}", file.display(), line)?; + } + + self.out.write_all(b"\n") + } +} diff --git a/src/libstd/sys_common/bytestring.rs b/src/libstd/sys_common/bytestring.rs index 273d586a5a0..429ecf6281b 100644 --- a/src/libstd/sys_common/bytestring.rs +++ b/src/libstd/sys_common/bytestring.rs @@ -3,9 +3,9 @@ use crate::fmt::{Formatter, Result, Write}; use core::str::lossy::{Utf8Lossy, Utf8LossyChunk}; -pub fn debug_fmt_bytestring(slice: &[u8], f: &mut Formatter) -> Result { +pub fn debug_fmt_bytestring(slice: &[u8], f: &mut Formatter<'_>) -> Result { // Writes out a valid unicode string with the correct escape sequences - fn write_str_escaped(f: &mut Formatter, s: &str) -> Result { + fn write_str_escaped(f: &mut Formatter<'_>, s: &str) -> Result { for c in s.chars().flat_map(|c| c.escape_debug()) { f.write_char(c)? } @@ -32,7 +32,7 @@ mod tests { struct Helper<'a>(&'a [u8]); impl Debug for Helper<'_> { - fn fmt(&self, f: &mut Formatter) -> Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { debug_fmt_bytestring(self.0, f) } } diff --git a/src/libstd/sys_common/fs.rs b/src/libstd/sys_common/fs.rs new file mode 100644 index 00000000000..7152fcd215c --- /dev/null +++ b/src/libstd/sys_common/fs.rs @@ -0,0 +1,41 @@ +#![allow(dead_code)] // not used on all platforms + +use crate::path::Path; +use crate::fs; +use crate::io::{self, Error, ErrorKind}; + +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + if !from.is_file() { + return Err(Error::new(ErrorKind::InvalidInput, + "the source path is not an existing regular file")) + } + + let mut reader = fs::File::open(from)?; + let mut writer = fs::File::create(to)?; + let perm = reader.metadata()?.permissions(); + + let ret = io::copy(&mut reader, &mut writer)?; + fs::set_permissions(to, perm)?; + Ok(ret) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let filetype = fs::symlink_metadata(path)?.file_type(); + if filetype.is_symlink() { + fs::remove_file(path) + } else { + remove_dir_all_recursive(path) + } +} + +fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { + for child in fs::read_dir(path)? { + let child = child?; + if child.file_type()?.is_dir() { + remove_dir_all_recursive(&child.path())?; + } else { + fs::remove_file(&child.path())?; + } + } + fs::remove_dir(path) +} diff --git a/src/libstd/sys_common/gnu/libbacktrace.rs b/src/libstd/sys_common/gnu/libbacktrace.rs deleted file mode 100644 index 6cd050242dd..00000000000 --- a/src/libstd/sys_common/gnu/libbacktrace.rs +++ /dev/null @@ -1,175 +0,0 @@ -use backtrace_sys::backtrace_state; - -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::ptr; -use crate::sys::backtrace::BacktraceContext; -use crate::sys_common::backtrace::Frame; - -pub fn foreach_symbol_fileline<F>(frame: Frame, - mut f: F, - _: &BacktraceContext) -> io::Result<bool> -where F: FnMut(&[u8], u32) -> io::Result<()> -{ - // pcinfo may return an arbitrary number of file:line pairs, - // in the order of stack trace (i.e., inlined calls first). - // in order to avoid allocation, we stack-allocate a fixed size of entries. - const FILELINE_SIZE: usize = 32; - let mut fileline_buf = [(ptr::null(), !0); FILELINE_SIZE]; - let ret; - let fileline_count = { - let state = unsafe { init_state() }; - if state.is_null() { - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to allocate libbacktrace state") - ) - } - let mut fileline_win: &mut [FileLine] = &mut fileline_buf; - let fileline_addr = &mut fileline_win as *mut &mut [FileLine]; - ret = unsafe { - backtrace_sys::backtrace_pcinfo( - state, - frame.exact_position as libc::uintptr_t, - pcinfo_cb, - error_cb, - fileline_addr as *mut libc::c_void, - ) - }; - FILELINE_SIZE - fileline_win.len() - }; - if ret == 0 { - for &(file, line) in &fileline_buf[..fileline_count] { - if file.is_null() { continue; } // just to be sure - let file = unsafe { CStr::from_ptr(file).to_bytes() }; - f(file, line)?; - } - Ok(fileline_count == FILELINE_SIZE) - } else { - Ok(false) - } -} - -/// Converts a pointer to symbol to its string value. -pub fn resolve_symname<F>(frame: Frame, - callback: F, - _: &BacktraceContext) -> io::Result<()> - where F: FnOnce(Option<&str>) -> io::Result<()> -{ - let symname = { - let state = unsafe { init_state() }; - if state.is_null() { - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to allocate libbacktrace state") - ) - } - let mut data: *const libc::c_char = ptr::null(); - let data_addr = &mut data as *mut *const libc::c_char; - let ret = unsafe { - backtrace_sys::backtrace_syminfo( - state, - frame.symbol_addr as libc::uintptr_t, - syminfo_cb, - error_cb, - data_addr as *mut libc::c_void, - ) - }; - if ret == 0 || data.is_null() { - None - } else { - unsafe { - CStr::from_ptr(data).to_str().ok() - } - } - }; - callback(symname) -} - -//////////////////////////////////////////////////////////////////////// -// helper callbacks -//////////////////////////////////////////////////////////////////////// - -type FileLine = (*const libc::c_char, u32); - -extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, - _errnum: libc::c_int) { - // do nothing for now -} -extern fn syminfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - symname: *const libc::c_char, - _symval: libc::uintptr_t, - _symsize: libc::uintptr_t) { - let slot = data as *mut *const libc::c_char; - unsafe { *slot = symname; } -} -extern fn pcinfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - filename: *const libc::c_char, - lineno: libc::c_int, - _function: *const libc::c_char) -> libc::c_int { - if !filename.is_null() { - let slot = data as *mut &mut [FileLine]; - let buffer = unsafe {ptr::read(slot)}; - - // if the buffer is not full, add file:line to the buffer - // and adjust the buffer for next possible calls to pcinfo_cb. - if !buffer.is_empty() { - buffer[0] = (filename, lineno as u32); - unsafe { ptr::write(slot, &mut buffer[1..]); } - } - } - - 0 -} - -// The libbacktrace API supports creating a state, but it does not -// support destroying a state. I personally take this to mean that a -// state is meant to be created and then live forever. -// -// I would love to register an at_exit() handler which cleans up this -// state, but libbacktrace provides no way to do so. -// -// With these constraints, this function has a statically cached state -// that is calculated the first time this is requested. Remember that -// backtracing all happens serially (one global lock). -// -// Things don't work so well on not-Linux since libbacktrace can't track -// down that executable this is. We at one point used env::current_exe but -// it turns out that there are some serious security issues with that -// approach. -// -// Specifically, on certain platforms like BSDs, a malicious actor can cause -// an arbitrary file to be placed at the path returned by current_exe. -// libbacktrace does not behave defensively in the presence of ill-formed -// DWARF information, and has been demonstrated to segfault in at least one -// case. There is no evidence at the moment to suggest that a more carefully -// constructed file can't cause arbitrary code execution. As a result of all -// of this, we don't hint libbacktrace with the path to the current process. -unsafe fn init_state() -> *mut backtrace_state { - static mut STATE: *mut backtrace_state = ptr::null_mut(); - if !STATE.is_null() { return STATE } - - let filename = match crate::sys::backtrace::gnu::get_executable_filename() { - Ok((filename, file)) => { - // filename is purposely leaked here since libbacktrace requires - // it to stay allocated permanently, file is also leaked so that - // the file stays locked - let filename_ptr = filename.as_ptr(); - mem::forget(filename); - mem::forget(file); - filename_ptr - }, - Err(_) => ptr::null(), - }; - - STATE = backtrace_sys::backtrace_create_state( - filename, - 0, - error_cb, - ptr::null_mut(), - ); - STATE -} diff --git a/src/libstd/sys_common/gnu/mod.rs b/src/libstd/sys_common/gnu/mod.rs deleted file mode 100644 index d6959697f2a..00000000000 --- a/src/libstd/sys_common/gnu/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![allow(missing_docs)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -pub mod libbacktrace; diff --git a/src/libstd/sys_common/io.rs b/src/libstd/sys_common/io.rs index 44b0963302d..8789abe55c3 100644 --- a/src/libstd/sys_common/io.rs +++ b/src/libstd/sys_common/io.rs @@ -16,7 +16,7 @@ pub mod test { p.join(path) } - pub fn path<'a>(&'a self) -> &'a Path { + pub fn path(&self) -> &Path { let TempDir(ref p) = *self; p } diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index 1fc32365408..9190a3b0d5f 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -28,6 +28,17 @@ macro_rules! rtassert { }) } +#[allow(unused_macros)] // not used on all platforms +macro_rules! rtunwrap { + ($ok:ident, $e:expr) => (match $e { + $ok(v) => v, + ref err => { + let err = err.as_ref().map(|_|()); // map Ok/Some which might not be Debug + rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err) + }, + }) +} + pub mod alloc; pub mod at_exit_imp; #[cfg(feature = "backtrace")] @@ -35,6 +46,13 @@ pub mod backtrace; pub mod condvar; pub mod io; pub mod mutex; +#[cfg(any(rustdoc, // see `mod os`, docs are generated for multiple platforms + unix, + target_os = "redox", + target_os = "cloudabi", + target_arch = "wasm32", + all(target_vendor = "fortanix", target_env = "sgx")))] +pub mod os_str_bytes; pub mod poison; pub mod remutex; pub mod rwlock; @@ -45,11 +63,11 @@ pub mod util; pub mod wtf8; pub mod bytestring; pub mod process; +pub mod fs; -cfg_if! { +cfg_if::cfg_if! { if #[cfg(any(target_os = "cloudabi", target_os = "l4re", - target_os = "redox", all(target_arch = "wasm32", not(target_os = "emscripten")), all(target_vendor = "fortanix", target_env = "sgx")))] { pub use crate::sys::net; @@ -58,12 +76,6 @@ cfg_if! { } } -#[cfg(feature = "backtrace")] -#[cfg(any(all(unix, not(target_os = "emscripten")), - all(windows, target_env = "gnu"), - target_os = "redox"))] -pub mod gnu; - // common error constructors /// A trait for viewing representations from std types diff --git a/src/libstd/sys_common/mutex.rs b/src/libstd/sys_common/mutex.rs index 4b58cac7941..28d85949ffa 100644 --- a/src/libstd/sys_common/mutex.rs +++ b/src/libstd/sys_common/mutex.rs @@ -38,7 +38,7 @@ impl Mutex { /// Calls raw_lock() and then returns an RAII guard to guarantee the mutex /// will be unlocked. #[inline] - pub unsafe fn lock(&self) -> MutexGuard { + pub unsafe fn lock(&self) -> MutexGuard<'_> { self.raw_lock(); MutexGuard(&self.0) } diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs index b9505aaa69b..391f670346f 100644 --- a/src/libstd/sys_common/net.rs +++ b/src/libstd/sys_common/net.rs @@ -1,7 +1,7 @@ use crate::cmp; use crate::ffi::CString; use crate::fmt; -use crate::io::{self, Error, ErrorKind, IoVec, IoVecMut}; +use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr}; use crate::ptr; @@ -37,12 +37,12 @@ use crate::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"))] + target_os = "haiku"))] 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")))] + target_os = "haiku")))] const MSG_NOSIGNAL: c_int = 0x0; //////////////////////////////////////////////////////////////////////////////// @@ -256,7 +256,7 @@ impl TcpStream { self.inner.read(buf) } - pub fn read_vectored(&self, bufs: &mut [IoVecMut<'_>]) -> io::Result<usize> { + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { self.inner.read_vectored(bufs) } @@ -271,7 +271,7 @@ impl TcpStream { Ok(ret as usize) } - pub fn write_vectored(&self, bufs: &[IoVec<'_>]) -> io::Result<usize> { + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { self.inner.write_vectored(bufs) } @@ -328,7 +328,7 @@ impl FromInner<Socket> for TcpStream { } impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut res = f.debug_struct("TcpStream"); if let Ok(addr) = self.socket_addr() { @@ -435,7 +435,7 @@ impl FromInner<Socket> for TcpListener { } impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut res = f.debug_struct("TcpListener"); if let Ok(addr) = self.socket_addr() { @@ -472,6 +472,12 @@ impl UdpSocket { pub fn into_socket(self) -> Socket { self.inner } + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + sockname(|buf, len| unsafe { + c::getpeername(*self.inner.as_inner(), buf, len) + }) + } + pub fn socket_addr(&self) -> io::Result<SocketAddr> { sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) @@ -638,7 +644,7 @@ impl FromInner<Socket> for UdpSocket { } impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut res = f.debug_struct("UdpSocket"); if let Ok(addr) = self.socket_addr() { diff --git a/src/libstd/sys/unix/os_str.rs b/src/libstd/sys_common/os_str_bytes.rs index 79b43458d00..a4961974d89 100644 --- a/src/libstd/sys/unix/os_str.rs +++ b/src/libstd/sys_common/os_str_bytes.rs @@ -1,46 +1,47 @@ -/// The underlying OsString/OsStr implementation on Unix systems: just -/// a `Vec<u8>`/`[u8]`. +//! The underlying OsString/OsStr implementation on Unix and many other +//! systems: just a `Vec<u8>`/`[u8]`. use crate::borrow::Cow; +use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::str; use crate::mem; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::{AsInner, IntoInner}; +use crate::sys_common::{FromInner, IntoInner, AsInner}; use crate::sys_common::bytestring::debug_fmt_bytestring; use core::str::lossy::Utf8Lossy; #[derive(Clone, Hash)] -pub struct Buf { +pub(crate) struct Buf { pub inner: Vec<u8> } -pub struct Slice { +pub(crate) struct Slice { pub inner: [u8] } impl fmt::Debug for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { debug_fmt_bytestring(&self.inner, formatter) } } impl fmt::Display for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) } } impl fmt::Debug for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.as_slice(), formatter) } } impl fmt::Display for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self.as_slice(), formatter) } } @@ -147,7 +148,7 @@ impl Slice { str::from_utf8(&self.inner).ok() } - pub fn to_string_lossy(&self) -> Cow<str> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { String::from_utf8_lossy(&self.inner) } @@ -178,3 +179,69 @@ impl Slice { unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } } } + +/// Platform-specific extensions to [`OsString`]. +/// +/// [`OsString`]: ../../../../std/ffi/struct.OsString.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an [`OsString`] from a byte vector. + /// + /// See the module docmentation for an example. + /// + /// [`OsString`]: ../../../ffi/struct.OsString.html + #[stable(feature = "rust1", since = "1.0.0")] + fn from_vec(vec: Vec<u8>) -> Self; + + /// Yields the underlying byte vector of this [`OsString`]. + /// + /// See the module docmentation for an example. + /// + /// [`OsString`]: ../../../ffi/struct.OsString.html + #[stable(feature = "rust1", since = "1.0.0")] + fn into_vec(self) -> Vec<u8>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_vec(vec: Vec<u8>) -> OsString { + FromInner::from_inner(Buf { inner: vec }) + } + fn into_vec(self) -> Vec<u8> { + self.into_inner().inner + } +} + +/// Platform-specific extensions to [`OsStr`]. +/// +/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + #[stable(feature = "rust1", since = "1.0.0")] + /// Creates an [`OsStr`] from a byte slice. + /// + /// See the module docmentation for an example. + /// + /// [`OsStr`]: ../../../ffi/struct.OsStr.html + fn from_bytes(slice: &[u8]) -> &Self; + + /// Gets the underlying byte view of the [`OsStr`] slice. + /// + /// See the module docmentation for an example. + /// + /// [`OsStr`]: ../../../ffi/struct.OsStr.html + #[stable(feature = "rust1", since = "1.0.0")] + fn as_bytes(&self) -> &[u8]; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + #[inline] + fn from_bytes(slice: &[u8]) -> &OsStr { + unsafe { mem::transmute(slice) } + } + #[inline] + fn as_bytes(&self) -> &[u8] { + &self.as_inner().inner + } +} diff --git a/src/libstd/sys_common/poison.rs b/src/libstd/sys_common/poison.rs index d2294235666..adac45f2d59 100644 --- a/src/libstd/sys_common/poison.rs +++ b/src/libstd/sys_common/poison.rs @@ -136,14 +136,14 @@ pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>; #[stable(feature = "rust1", since = "1.0.0")] impl<T> fmt::Debug for PoisonError<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "PoisonError { inner: .. }".fmt(f) } } #[stable(feature = "rust1", since = "1.0.0")] impl<T> fmt::Display for PoisonError<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { "poisoned lock: another task failed inside".fmt(f) } } @@ -214,7 +214,7 @@ impl<T> From<PoisonError<T>> for TryLockError<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> fmt::Debug for TryLockError<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f), TryLockError::WouldBlock => "WouldBlock".fmt(f) @@ -224,7 +224,7 @@ impl<T> fmt::Debug for TryLockError<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> fmt::Display for TryLockError<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { TryLockError::Poisoned(..) => "poisoned lock: another task failed inside", TryLockError::WouldBlock => "try_lock failed because the operation would block" diff --git a/src/libstd/sys_common/remutex.rs b/src/libstd/sys_common/remutex.rs index 2aec361d7a4..f08b13c4aa2 100644 --- a/src/libstd/sys_common/remutex.rs +++ b/src/libstd/sys_common/remutex.rs @@ -72,7 +72,7 @@ impl<T> ReentrantMutex<T> { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn lock(&self) -> LockResult<ReentrantMutexGuard<T>> { + pub fn lock(&self) -> LockResult<ReentrantMutexGuard<'_, T>> { unsafe { self.inner.lock() } ReentrantMutexGuard::new(&self) } @@ -89,7 +89,7 @@ impl<T> ReentrantMutex<T> { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn try_lock(&self) -> TryLockResult<ReentrantMutexGuard<T>> { + pub fn try_lock(&self) -> TryLockResult<ReentrantMutexGuard<'_, T>> { if unsafe { self.inner.try_lock() } { Ok(ReentrantMutexGuard::new(&self)?) } else { @@ -108,7 +108,7 @@ impl<T> Drop for ReentrantMutex<T> { } impl<T: fmt::Debug + 'static> fmt::Debug for ReentrantMutex<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.try_lock() { Ok(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(), Err(TryLockError::Poisoned(err)) => { @@ -117,7 +117,9 @@ impl<T: fmt::Debug + 'static> fmt::Debug for ReentrantMutex<T> { Err(TryLockError::WouldBlock) => { struct LockedPlaceholder; impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("<locked>") } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("<locked>") + } } f.debug_struct("ReentrantMutex").field("data", &LockedPlaceholder).finish() diff --git a/src/libstd/sys_common/thread.rs b/src/libstd/sys_common/thread.rs index b2142e75308..6ab0d5cbe9c 100644 --- a/src/libstd/sys_common/thread.rs +++ b/src/libstd/sys_common/thread.rs @@ -1,4 +1,3 @@ -use crate::boxed::FnBox; use crate::env; use crate::sync::atomic::{self, Ordering}; use crate::sys::stack_overflow; @@ -11,7 +10,7 @@ pub unsafe fn start_thread(main: *mut u8) { let _handler = stack_overflow::Handler::new(); // Finally, let's run some code. - Box::from_raw(main as *mut Box<dyn FnBox()>)() + Box::from_raw(main as *mut Box<dyn FnOnce()>)() } pub fn min_stack() -> usize { diff --git a/src/libstd/sys_common/util.rs b/src/libstd/sys_common/util.rs index 206443a6736..7936dd35999 100644 --- a/src/libstd/sys_common/util.rs +++ b/src/libstd/sys_common/util.rs @@ -3,7 +3,7 @@ use crate::io::prelude::*; use crate::sys::stdio::panic_output; use crate::thread; -pub fn dumb_print(args: fmt::Arguments) { +pub fn dumb_print(args: fmt::Arguments<'_>) { if let Some(mut out) = panic_output() { let _ = out.write_fmt(args); } @@ -14,7 +14,7 @@ pub fn dumb_print(args: fmt::Arguments) { // crate::intrinsics::abort() may be used instead. The above implementations cover // all targets currently supported by libstd. -pub fn abort(args: fmt::Arguments) -> ! { +pub fn abort(args: fmt::Arguments<'_>) -> ! { dumb_print(format_args!("fatal runtime error: {}\n", args)); unsafe { crate::sys::abort_internal(); } } diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index b15239e8d87..81e606fc165 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -46,7 +46,7 @@ pub struct CodePoint { /// Example: `U+1F4A9` impl fmt::Debug for CodePoint { #[inline] - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "U+{:04X}", self.value) } } @@ -134,7 +134,7 @@ impl ops::DerefMut for Wtf8Buf { /// Example: `"a\u{D800}"` for a string with code points [U+0061, U+D800] impl fmt::Debug for Wtf8Buf { #[inline] - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, formatter) } } @@ -146,10 +146,10 @@ impl Wtf8Buf { Wtf8Buf { bytes: Vec::new() } } - /// Creates a new, empty WTF-8 string with pre-allocated capacity for `n` bytes. + /// Creates a new, empty WTF-8 string with pre-allocated capacity for `capacity` bytes. #[inline] - pub fn with_capacity(n: usize) -> Wtf8Buf { - Wtf8Buf { bytes: Vec::with_capacity(n) } + pub fn with_capacity(capacity: usize) -> Wtf8Buf { + Wtf8Buf { bytes: Vec::with_capacity(capacity) } } /// Creates a WTF-8 string from a UTF-8 `String`. @@ -388,9 +388,7 @@ impl Extend<CodePoint> for Wtf8Buf { let (low, _high) = iterator.size_hint(); // Lower bound of one byte per code point (ASCII only) self.bytes.reserve(low); - for code_point in iterator { - self.push(code_point); - } + iterator.for_each(move |code_point| self.push(code_point)); } } @@ -411,8 +409,8 @@ impl AsInner<[u8]> for Wtf8 { /// and surrogates as `\u` followed by four hexadecimal digits. /// Example: `"a\u{D800}"` for a slice with code points [U+0061, U+D800] impl fmt::Debug for Wtf8 { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - fn write_str_escaped(f: &mut fmt::Formatter, s: &str) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fn write_str_escaped(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result { use crate::fmt::Write; for c in s.chars().flat_map(|c| c.escape_debug()) { f.write_char(c)? @@ -441,7 +439,7 @@ impl fmt::Debug for Wtf8 { } impl fmt::Display for Wtf8 { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { let wtf8_bytes = &self.bytes; let mut pos = 0; loop { @@ -522,7 +520,7 @@ impl Wtf8 { /// Returns an iterator for the string’s code points. #[inline] - pub fn code_points(&self) -> Wtf8CodePoints { + pub fn code_points(&self) -> Wtf8CodePoints<'_> { Wtf8CodePoints { bytes: self.bytes.iter() } } @@ -547,7 +545,7 @@ impl Wtf8 { /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”). /// /// This only copies the data if necessary (if it contains any surrogate). - pub fn to_string_lossy(&self) -> Cow<str> { + pub fn to_string_lossy(&self) -> Cow<'_, str> { let surrogate_pos = match self.next_surrogate(0) { None => return Cow::Borrowed(unsafe { str::from_utf8_unchecked(&self.bytes) }), Some((pos, _)) => pos, @@ -579,7 +577,7 @@ impl Wtf8 { /// calling `Wtf8Buf::from_ill_formed_utf16` on the resulting code units /// would always return the original WTF-8 string. #[inline] - pub fn encode_wide(&self) -> EncodeWide { + pub fn encode_wide(&self) -> EncodeWide<'_> { EncodeWide { code_points: self.code_points(), extra: 0 } } @@ -1228,7 +1226,7 @@ mod tests { assert_eq!(Wtf8::from_str("aé 💩").to_string_lossy(), Cow::Borrowed("aé 💩")); let mut string = Wtf8Buf::from_str("aé 💩"); string.push(CodePoint::from_u32(0xD800).unwrap()); - let expected: Cow<str> = Cow::Owned(String::from("aé 💩�")); + let expected: Cow<'_, str> = Cow::Owned(String::from("aé 💩�")); assert_eq!(string.to_string_lossy(), expected); } diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 7ad6b124e3a..f85b5d632f1 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -2,10 +2,8 @@ #![unstable(feature = "thread_local_internals", issue = "0")] -use crate::cell::UnsafeCell; +use crate::error::Error; use crate::fmt; -use crate::hint; -use crate::mem; /// A thread local storage key which owns its contents. /// @@ -92,15 +90,12 @@ pub struct LocalKey<T: 'static> { // trivially devirtualizable by LLVM because the value of `inner` never // changes and the constant should be readonly within a crate. This mainly // only runs into problems when TLS statics are exported across crates. - inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>, - - // initialization routine to invoke to create a value - init: fn() -> T, + inner: unsafe fn() -> Option<&'static T>, } #[stable(feature = "std_debug", since = "1.16.0")] impl<T: 'static> fmt::Debug for LocalKey<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("LocalKey { .. }") } } @@ -159,10 +154,7 @@ macro_rules! __thread_local_inner { #[inline] fn __init() -> $t { $init } - unsafe fn __getit() -> $crate::option::Option< - &'static $crate::cell::UnsafeCell< - $crate::option::Option<$t>>> - { + unsafe fn __getit() -> $crate::option::Option<&'static $t> { #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] static __KEY: $crate::thread::__StaticLocalKeyInner<$t> = $crate::thread::__StaticLocalKeyInner::new(); @@ -182,11 +174,11 @@ macro_rules! __thread_local_inner { static __KEY: $crate::thread::__OsLocalKeyInner<$t> = $crate::thread::__OsLocalKeyInner::new(); - __KEY.get() + __KEY.get(__init) } unsafe { - $crate::thread::LocalKey::new(__getit, __init) + $crate::thread::LocalKey::new(__getit) } } }; @@ -198,34 +190,36 @@ macro_rules! __thread_local_inner { /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). #[stable(feature = "thread_local_try_with", since = "1.26.0")] +#[derive(Clone, Copy, Eq, PartialEq)] pub struct AccessError { _private: (), } #[stable(feature = "thread_local_try_with", since = "1.26.0")] impl fmt::Debug for AccessError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AccessError").finish() } } #[stable(feature = "thread_local_try_with", since = "1.26.0")] impl fmt::Display for AccessError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt("already destroyed", f) } } +#[stable(feature = "thread_local_try_with", since = "1.26.0")] +impl Error for AccessError {} + impl<T: 'static> LocalKey<T> { #[doc(hidden)] #[unstable(feature = "thread_local_internals", reason = "recently added to create a key", issue = "0")] - pub const unsafe fn new(inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>, - init: fn() -> T) -> LocalKey<T> { + pub const unsafe fn new(inner: unsafe fn() -> Option<&'static T>) -> LocalKey<T> { LocalKey { inner, - init, } } @@ -246,37 +240,6 @@ impl<T: 'static> LocalKey<T> { after it is destroyed") } - unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T { - // Execute the initialization up front, *then* move it into our slot, - // just in case initialization fails. - let value = (self.init)(); - let ptr = slot.get(); - - // note that this can in theory just be `*ptr = Some(value)`, but due to - // the compiler will currently codegen that pattern with something like: - // - // ptr::drop_in_place(ptr) - // ptr::write(ptr, Some(value)) - // - // Due to this pattern it's possible for the destructor of the value in - // `ptr` (e.g., if this is being recursively initialized) to re-access - // TLS, in which case there will be a `&` and `&mut` pointer to the same - // value (an aliasing violation). To avoid setting the "I'm running a - // destructor" flag we just use `mem::replace` which should sequence the - // operations a little differently and make this safe to call. - mem::replace(&mut *ptr, Some(value)); - - // After storing `Some` we want to get a reference to the contents of - // what we just stored. While we could use `unwrap` here and it should - // always work it empirically doesn't seem to always get optimized away, - // which means that using something like `try_with` can pull in - // panicking code and cause a large size bloat. - match *ptr { - Some(ref x) => x, - None => hint::unreachable_unchecked(), - } - } - /// Acquires a reference to the value in this TLS key. /// /// This will lazily initialize the value if this thread has not referenced @@ -293,13 +256,68 @@ impl<T: 'static> LocalKey<T> { F: FnOnce(&T) -> R, { unsafe { - let slot = (self.inner)().ok_or(AccessError { + let thread_local = (self.inner)().ok_or(AccessError { _private: (), })?; - Ok(f(match *slot.get() { - Some(ref inner) => inner, - None => self.init(slot), - })) + Ok(f(thread_local)) + } + } +} + +mod lazy { + use crate::cell::UnsafeCell; + use crate::mem; + use crate::hint; + + pub struct LazyKeyInner<T> { + inner: UnsafeCell<Option<T>>, + } + + impl<T> LazyKeyInner<T> { + pub const fn new() -> LazyKeyInner<T> { + LazyKeyInner { + inner: UnsafeCell::new(None), + } + } + + pub unsafe fn get(&self) -> Option<&'static T> { + (*self.inner.get()).as_ref() + } + + pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T { + // Execute the initialization up front, *then* move it into our slot, + // just in case initialization fails. + let value = init(); + let ptr = self.inner.get(); + + // note that this can in theory just be `*ptr = Some(value)`, but due to + // the compiler will currently codegen that pattern with something like: + // + // ptr::drop_in_place(ptr) + // ptr::write(ptr, Some(value)) + // + // Due to this pattern it's possible for the destructor of the value in + // `ptr` (e.g., if this is being recursively initialized) to re-access + // TLS, in which case there will be a `&` and `&mut` pointer to the same + // value (an aliasing violation). To avoid setting the "I'm running a + // destructor" flag we just use `mem::replace` which should sequence the + // operations a little differently and make this safe to call. + mem::replace(&mut *ptr, Some(value)); + + // After storing `Some` we want to get a reference to the contents of + // what we just stored. While we could use `unwrap` here and it should + // always work it empirically doesn't seem to always get optimized away, + // which means that using something like `try_with` can pull in + // panicking code and cause a large size bloat. + match *ptr { + Some(ref x) => x, + None => hint::unreachable_unchecked(), + } + } + + #[allow(unused)] + pub unsafe fn take(&mut self) -> Option<T> { + (*self.inner.get()).take() } } } @@ -309,17 +327,17 @@ impl<T: 'static> LocalKey<T> { #[doc(hidden)] #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] pub mod statik { - use crate::cell::UnsafeCell; + use super::lazy::LazyKeyInner; use crate::fmt; pub struct Key<T> { - inner: UnsafeCell<Option<T>>, + inner: LazyKeyInner<T>, } unsafe impl<T> Sync for Key<T> { } impl<T> fmt::Debug for Key<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Key { .. }") } } @@ -327,12 +345,16 @@ pub mod statik { impl<T> Key<T> { pub const fn new() -> Key<T> { Key { - inner: UnsafeCell::new(None), + inner: LazyKeyInner::new(), } } - pub unsafe fn get(&self) -> Option<&'static UnsafeCell<Option<T>>> { - Some(&*(&self.inner as *const _)) + pub unsafe fn get(&self, init: fn() -> T) -> Option<&'static T> { + let value = match self.inner.get() { + Some(ref value) => value, + None => self.inner.initialize(init), + }; + Some(value) } } } @@ -340,23 +362,42 @@ pub mod statik { #[doc(hidden)] #[cfg(target_thread_local)] pub mod fast { - use crate::cell::{Cell, UnsafeCell}; + use super::lazy::LazyKeyInner; + use crate::cell::Cell; use crate::fmt; use crate::mem; - use crate::ptr; - use crate::sys::fast_thread_local::{register_dtor, requires_move_before_drop}; + use crate::sys::fast_thread_local::register_dtor; + + #[derive(Copy, Clone)] + enum DtorState { + Unregistered, + Registered, + RunningOrHasRun, + } + // This data structure has been carefully constructed so that the fast path + // only contains one branch on x86. That optimization is necessary to avoid + // duplicated tls lookups on OSX. + // + // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 pub struct Key<T> { - inner: UnsafeCell<Option<T>>, + // If `LazyKeyInner::get` returns `None`, that indicates either: + // * The value has never been initialized + // * The value is being recursively initialized + // * The value has already been destroyed or is being destroyed + // To determine which kind of `None`, check `dtor_state`. + // + // This is very optimizer friendly for the fast path - initialized but + // not yet dropped. + inner: LazyKeyInner<T>, // Metadata to keep track of the state of the destructor. Remember that - // these variables are thread-local, not global. - dtor_registered: Cell<bool>, - dtor_running: Cell<bool>, + // this variable is thread-local, not global. + dtor_state: Cell<DtorState>, } impl<T> fmt::Debug for Key<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Key { .. }") } } @@ -364,54 +405,75 @@ pub mod fast { impl<T> Key<T> { pub const fn new() -> Key<T> { Key { - inner: UnsafeCell::new(None), - dtor_registered: Cell::new(false), - dtor_running: Cell::new(false) + inner: LazyKeyInner::new(), + dtor_state: Cell::new(DtorState::Unregistered), } } - pub unsafe fn get(&self) -> Option<&'static UnsafeCell<Option<T>>> { - if mem::needs_drop::<T>() && self.dtor_running.get() { - return None + pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { + match self.inner.get() { + Some(val) => Some(val), + None => self.try_initialize(init), } - self.register_dtor(); - Some(&*(&self.inner as *const _)) } - unsafe fn register_dtor(&self) { - if !mem::needs_drop::<T>() || self.dtor_registered.get() { - return + // `try_initialize` is only called once per fast thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + // + // Macos: Inlining this function can cause two `tlv_get_addr` calls to + // be performed for every call to `Key::get`. The #[cold] hint makes + // that less likely. + // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722 + #[cold] + unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { + if !mem::needs_drop::<T>() || self.try_register_dtor() { + Some(self.inner.initialize(init)) + } else { + None } + } - register_dtor(self as *const _ as *mut u8, - destroy_value::<T>); - self.dtor_registered.set(true); + // `try_register_dtor` is only called once per fast thread local + // variable, except in corner cases where thread_local dtors reference + // other thread_local's, or it is being recursively initialized. + unsafe fn try_register_dtor(&self) -> bool { + match self.dtor_state.get() { + DtorState::Unregistered => { + // dtor registration happens before initialization. + register_dtor(self as *const _ as *mut u8, + destroy_value::<T>); + self.dtor_state.set(DtorState::Registered); + true + } + DtorState::Registered => { + // recursively initialized + true + } + DtorState::RunningOrHasRun => { + false + } + } } } 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 - // destructor as running for this thread so calls to `get` will return - // `None`. - (*ptr).dtor_running.set(true); - // Some implementations may require us to move the value before we drop - // it as it could get re-initialized in-place during destruction. - // - // Hence, we use `ptr::read` on those platforms (to move to a "safe" - // location) instead of drop_in_place. - if requires_move_before_drop() { - ptr::read((*ptr).inner.get()); - } else { - ptr::drop_in_place((*ptr).inner.get()); - } + // Right before we run the user destructor be sure to set the + // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This + // causes future calls to `get` to run `try_initialize_drop` again, + // which will now fail, and return `None`. + let value = (*ptr).inner.take(); + (*ptr).dtor_state.set(DtorState::RunningOrHasRun); + drop(value); } } #[doc(hidden)] pub mod os { - use crate::cell::{Cell, UnsafeCell}; + use super::lazy::LazyKeyInner; + use crate::cell::Cell; use crate::fmt; use crate::marker; use crate::ptr; @@ -424,7 +486,7 @@ pub mod os { } impl<T> fmt::Debug for Key<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("Key { .. }") } } @@ -432,8 +494,8 @@ pub mod os { unsafe impl<T> Sync for Key<T> { } struct Value<T: 'static> { + inner: LazyKeyInner<T>, key: &'static Key<T>, - value: UnsafeCell<Option<T>>, } impl<T: 'static> Key<T> { @@ -444,24 +506,43 @@ pub mod os { } } - pub unsafe fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { + pub unsafe fn get(&'static self, init: fn() -> T) -> Option<&'static T> { let ptr = self.os.get() as *mut Value<T>; - if !ptr.is_null() { - if ptr as usize == 1 { - return None + if ptr as usize > 1 { + match (*ptr).inner.get() { + Some(ref value) => return Some(value), + None => {}, } - return Some(&(*ptr).value); } + self.try_initialize(init) + } - // If the lookup returned null, we haven't initialized our own - // local copy, so do that now. - let ptr: Box<Value<T>> = box Value { - key: self, - value: UnsafeCell::new(None), + // `try_initialize` is only called once per os thread local variable, + // except in corner cases where thread_local dtors reference other + // thread_local's, or it is being recursively initialized. + unsafe fn try_initialize(&'static self, init: fn() -> T) -> Option<&'static T> { + let ptr = self.os.get() as *mut Value<T>; + if ptr as usize == 1 { + // destructor is running + return None + } + + let ptr = if ptr.is_null() { + // If the lookup returned null, we haven't initialized our own + // local copy, so do that now. + let ptr: Box<Value<T>> = box Value { + inner: LazyKeyInner::new(), + key: self, + }; + let ptr = Box::into_raw(ptr); + self.os.set(ptr as *mut u8); + ptr + } else { + // recursive initialization + ptr }; - let ptr = Box::into_raw(ptr); - self.os.set(ptr as *mut u8); - Some(&(*ptr).value) + + Some((*ptr).inner.initialize(init)) } } @@ -530,7 +611,7 @@ mod tests { thread::spawn(|| { assert!(FOO.try_with(|_| ()).is_ok()); - }).join().ok().unwrap(); + }).join().ok().expect("thread panicked"); } #[test] @@ -584,7 +665,7 @@ mod tests { thread::spawn(move|| { drop(S1); - }).join().ok().unwrap(); + }).join().ok().expect("thread panicked"); } #[test] @@ -600,7 +681,7 @@ mod tests { thread::spawn(move|| unsafe { K1.with(|s| *s.get() = Some(S1)); - }).join().ok().unwrap(); + }).join().ok().expect("thread panicked"); } // Note that this test will deadlock if TLS destructors aren't run (this diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 08f0aa2f0d2..764041d2f42 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -157,12 +157,12 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::any::Any; -use crate::boxed::FnBox; use crate::cell::UnsafeCell; use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; use crate::mem; +use crate::num::NonZeroU64; use crate::panic; use crate::panicking; use crate::str; @@ -269,7 +269,7 @@ impl Builder { /// /// let builder = thread::Builder::new() /// .name("foo".into()) - /// .stack_size(10); + /// .stack_size(32 * 1024); /// /// let handler = builder.spawn(|| { /// // thread code @@ -443,6 +443,7 @@ impl Builder { /// [`Builder::spawn`]: ../../std/thread/struct.Builder.html#method.spawn /// [`io::Result`]: ../../std/io/type.Result.html /// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html + /// [`JoinHandle::join`]: ../../std/thread/struct.JoinHandle.html#method.join #[unstable(feature = "thread_spawn_unchecked", issue = "55132")] pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result<JoinHandle<T>> where F: FnOnce() -> T, F: Send + 'a, T: Send + 'a @@ -487,7 +488,9 @@ impl Builder { // returning. native: Some(imp::Thread::new( stack_size, - mem::transmute::<Box<dyn FnBox() + 'a>, Box<dyn FnBox() + 'static>>(Box::new(main)) + mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(Box::new( + main, + )), )?), thread: my_thread, packet: Packet(my_packet), @@ -1036,7 +1039,7 @@ pub fn park_timeout(dur: Duration) { /// [`Thread`]: ../../std/thread/struct.Thread.html #[stable(feature = "thread_id", since = "1.19.0")] #[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -pub struct ThreadId(u64); +pub struct ThreadId(NonZeroU64); impl ThreadId { // Generate a new unique thread ID. @@ -1044,7 +1047,7 @@ impl ThreadId { // We never call `GUARD.init()`, so it is UB to attempt to // acquire this mutex reentrantly! static GUARD: mutex::Mutex = mutex::Mutex::new(); - static mut COUNTER: u64 = 0; + static mut COUNTER: u64 = 1; unsafe { let _guard = GUARD.lock(); @@ -1058,7 +1061,7 @@ impl ThreadId { let id = COUNTER; COUNTER += 1; - ThreadId(id) + ThreadId(NonZeroU64::new(id).unwrap()) } } } @@ -1255,8 +1258,11 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Thread { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.name(), f) + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Thread") + .field("id", &self.id()) + .field("name", &self.name()) + .finish() } } @@ -1465,7 +1471,7 @@ impl<T> IntoInner<imp::Thread> for JoinHandle<T> { #[stable(feature = "std_debug", since = "1.16.0")] impl<T> fmt::Debug for JoinHandle<T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("JoinHandle { .. }") } } @@ -1484,9 +1490,10 @@ fn _assert_sync_and_send() { mod tests { use super::Builder; use crate::any::Any; + use crate::mem; use crate::sync::mpsc::{channel, Sender}; use crate::result; - use crate::thread; + use crate::thread::{self, ThreadId}; use crate::time::Duration; use crate::u32; @@ -1497,7 +1504,7 @@ mod tests { fn test_unnamed_thread() { thread::spawn(move|| { assert!(thread::current().name().is_none()); - }).join().ok().unwrap(); + }).join().ok().expect("thread panicked"); } #[test] @@ -1691,6 +1698,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn test_park_timeout_unpark_not_called() { for _ in 0..10 { thread::park_timeout(Duration::from_millis(10)); @@ -1698,6 +1706,7 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn test_park_timeout_unpark_called_other_thread() { for _ in 0..10 { let th = thread::current(); @@ -1712,11 +1721,17 @@ mod tests { } #[test] + #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn sleep_ms_smoke() { thread::sleep(Duration::from_millis(2)); } #[test] + fn test_size_of_option_thread_id() { + assert_eq!(mem::size_of::<Option<ThreadId>>(), mem::size_of::<ThreadId>()); + } + + #[test] fn test_thread_id_equal() { assert!(thread::current().id() == thread::current().id()); } @@ -1727,6 +1742,6 @@ mod tests { assert!(thread::current().id() != spawned_id); } - // NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due + // NOTE: the corresponding test for stderr is in ui/thread-stderr, due // to the test harness apparently interfering with stderr configuration. } diff --git a/src/libstd/time.rs b/src/libstd/time.rs index 6d7093ac33e..98371b9ba3d 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -212,7 +212,7 @@ impl Instant { /// ``` #[stable(feature = "time2", since = "1.8.0")] pub fn duration_since(&self, earlier: Instant) -> Duration { - self.0.sub_instant(&earlier.0) + self.0.checked_sub_instant(&earlier.0).expect("supplied instant is later than self") } /// Returns the amount of time elapsed from another instant to this one, @@ -233,11 +233,7 @@ impl Instant { /// ``` #[unstable(feature = "checked_duration_since", issue = "58402")] pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> { - if self >= &earlier { - Some(self.0.sub_instant(&earlier.0)) - } else { - None - } + self.0.checked_sub_instant(&earlier.0) } /// Returns the amount of time elapsed from another instant to this one, @@ -353,7 +349,7 @@ impl Sub<Instant> for Instant { #[stable(feature = "time2", since = "1.8.0")] impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } @@ -400,6 +396,7 @@ impl SystemTime { /// This function may fail because measurements taken earlier are not /// guaranteed to always be before later measurements (due to anomalies such /// as the system clock being adjusted either forwards or backwards). + /// [`Instant`] can be used to measure elapsed time without this risk of failure. /// /// If successful, [`Ok`]`(`[`Duration`]`)` is returned where the duration represents /// the amount of time elapsed from the specified measurement to this one. @@ -410,6 +407,7 @@ impl SystemTime { /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok /// [`Duration`]: ../../std/time/struct.Duration.html /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// [`Instant`]: ../../std/time/struct.Instant.html /// /// # Examples /// @@ -418,7 +416,7 @@ impl SystemTime { /// /// let sys_time = SystemTime::now(); /// let difference = sys_time.duration_since(sys_time) - /// .expect("SystemTime::duration_since failed"); + /// .expect("Clock may have gone backwards"); /// println!("{:?}", difference); /// ``` #[stable(feature = "time2", since = "1.8.0")] @@ -427,7 +425,8 @@ impl SystemTime { self.0.sub_time(&earlier.0).map_err(SystemTimeError) } - /// Returns the amount of time elapsed since this system time was created. + /// Returns the difference between the clock time when this + /// system time was created, and the current clock time. /// /// This function may fail as the underlying system clock is susceptible to /// drift and updates (e.g., the system clock could go backwards), so this @@ -435,12 +434,15 @@ impl SystemTime { /// returned where the duration represents the amount of time elapsed from /// this time measurement to the current time. /// + /// To measure elapsed time reliably, use [`Instant`] instead. + /// /// Returns an [`Err`] if `self` is later than the current system time, and /// the error contains how far from the current system time `self` is. /// /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok /// [`Duration`]: ../../std/time/struct.Duration.html /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// [`Instant`]: ../../std/time/struct.Instant.html /// /// # Examples /// @@ -517,7 +519,7 @@ impl SubAssign<Duration> for SystemTime { #[stable(feature = "time2", since = "1.8.0")] impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } @@ -585,7 +587,7 @@ impl Error for SystemTimeError { #[stable(feature = "time2", since = "1.8.0")] impl fmt::Display for SystemTimeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "second time provided was later than self") } } @@ -664,20 +666,23 @@ mod tests { #[test] #[should_panic] - fn instant_duration_panic() { + fn instant_duration_since_panic() { let a = Instant::now(); (a - Duration::new(1, 0)).duration_since(a); } #[test] - fn checked_instant_duration_nopanic() { - let a = Instant::now(); - let ret = (a - Duration::new(1, 0)).checked_duration_since(a); - assert_eq!(ret, None); + fn instant_checked_duration_since_nopanic() { + let now = Instant::now(); + let earlier = now - Duration::new(1, 0); + let later = now + Duration::new(1, 0); + assert_eq!(earlier.checked_duration_since(now), None); + assert_eq!(later.checked_duration_since(now), Some(Duration::new(1, 0))); + assert_eq!(now.checked_duration_since(now), Some(Duration::new(0, 0))); } #[test] - fn saturating_instant_duration_nopanic() { + fn instant_saturating_duration_since_nopanic() { let a = Instant::now(); let ret = (a - Duration::new(1, 0)).saturating_duration_since(a); assert_eq!(ret, Duration::new(0,0)); @@ -712,13 +717,6 @@ mod tests { assert_almost_eq!(a - second + second, a); assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); - // A difference of 80 and 800 years cannot fit inside a 32-bit time_t - if !(cfg!(unix) && crate::mem::size_of::<libc::time_t>() <= 4) { - let eighty_years = second * 60 * 60 * 24 * 365 * 80; - assert_almost_eq!(a - eighty_years + eighty_years, a); - assert_almost_eq!(a - (eighty_years * 10) + (eighty_years * 10), a); - } - let one_second_from_epoch = UNIX_EPOCH + Duration::new(1, 0); let one_second_from_epoch2 = UNIX_EPOCH + Duration::new(0, 500_000_000) + Duration::new(0, 500_000_000); @@ -747,8 +745,8 @@ mod tests { #[test] fn since_epoch() { let ts = SystemTime::now(); - let a = ts.duration_since(UNIX_EPOCH).unwrap(); - let b = ts.duration_since(UNIX_EPOCH - Duration::new(1, 0)).unwrap(); + let a = ts.duration_since(UNIX_EPOCH + Duration::new(1, 0)).unwrap(); + let b = ts.duration_since(UNIX_EPOCH).unwrap(); assert!(b > a); assert_eq!(b - a, Duration::new(1, 0)); |
