Importing rustc-1.48.0
Bug: 173721343
Change-Id: Ie8184d9a685086ca8a77266d6c608843f40dc9e1
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index ef0ef41..b27b056 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -16,11 +16,11 @@
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core" }
-libc = { version = "0.2.74", default-features = false, features = ['rustc-dep-of-std'] }
-compiler_builtins = { version = "0.1.32" }
+libc = { version = "0.2.77", default-features = false, features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "0.1.35" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
-hashbrown = { version = "0.8.1", default-features = false, features = ['rustc-dep-of-std'] }
+hashbrown = { version = "0.9.0", default-features = false, features = ['rustc-dep-of-std'] }
# Dependencies of the `backtrace` crate
addr2line = { version = "0.13.0", optional = true, default-features = false }
@@ -59,6 +59,7 @@
panic-unwind = ["panic_unwind"]
profiler = ["profiler_builtins"]
compiler-builtins-c = ["alloc/compiler-builtins-c"]
+compiler-builtins-mem = ["alloc/compiler-builtins-mem"]
llvm-libunwind = ["unwind/llvm-libunwind"]
# Make panics and failed asserts immediately abort without formatting any message
diff --git a/library/std/build.rs b/library/std/build.rs
index 04bfed1..f2ed755 100644
--- a/library/std/build.rs
+++ b/library/std/build.rs
@@ -8,10 +8,6 @@
println!("cargo:rustc-link-lib=dl");
println!("cargo:rustc-link-lib=log");
println!("cargo:rustc-link-lib=gcc");
- } else if !target.contains("musl") {
- println!("cargo:rustc-link-lib=dl");
- println!("cargo:rustc-link-lib=rt");
- println!("cargo:rustc-link-lib=pthread");
}
} else if target.contains("freebsd") {
println!("cargo:rustc-link-lib=execinfo");
@@ -83,7 +79,7 @@
// - os=none ("bare metal" targets)
// - mipsel-sony-psp
// - nvptx64-nvidia-cuda
- // - avr-unknown-unknown
+ // - arch=avr
// - tvos (aarch64-apple-tvos, x86_64-apple-tvos)
// - uefi (x86_64-unknown-uefi, i686-unknown-uefi)
// - JSON targets
diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs
index b4009c8..dd76006 100644
--- a/library/std/src/alloc.rs
+++ b/library/std/src/alloc.rs
@@ -133,7 +133,7 @@
impl System {
#[inline]
- fn alloc_impl(&mut self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocErr> {
+ fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocError> {
match layout.size() {
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)),
// SAFETY: `layout` is non-zero in size,
@@ -143,47 +143,56 @@
} else {
GlobalAlloc::alloc(self, layout)
};
- let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
+ let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
Ok(NonNull::slice_from_raw_parts(ptr, size))
},
}
}
- // Safety: Same as `AllocRef::grow`
+ // SAFETY: Same as `AllocRef::grow`
#[inline]
unsafe fn grow_impl(
- &mut self,
+ &self,
ptr: NonNull<u8>,
- layout: Layout,
- new_size: usize,
+ old_layout: Layout,
+ new_layout: Layout,
zeroed: bool,
- ) -> Result<NonNull<[u8]>, AllocErr> {
+ ) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(
- new_size >= layout.size(),
- "`new_size` must be greater than or equal to `layout.size()`"
+ new_layout.size() >= old_layout.size(),
+ "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
);
- match layout.size() {
- // SAFETY: the caller must ensure that the `new_size` does not overflow.
- // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
- 0 => unsafe {
- let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
- self.alloc_impl(new_layout, zeroed)
- },
+ match old_layout.size() {
+ 0 => self.alloc_impl(new_layout, zeroed),
// SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size`
// as required by safety conditions. Other conditions must be upheld by the caller
- old_size => unsafe {
- // `realloc` probably checks for `new_size >= size` or something similar.
- intrinsics::assume(new_size >= layout.size());
+ old_size if old_layout.align() == new_layout.align() => unsafe {
+ let new_size = new_layout.size();
- let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
- let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
+ // `realloc` probably checks for `new_size >= old_layout.size()` or something similar.
+ intrinsics::assume(new_size >= old_layout.size());
+
+ let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
+ let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
if zeroed {
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
}
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
},
+
+ // SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
+ // both the old and new memory allocation are valid for reads and writes for `old_size`
+ // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+ // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+ // for `dealloc` must be upheld by the caller.
+ old_size => unsafe {
+ let new_ptr = self.alloc_impl(new_layout, zeroed)?;
+ ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
+ AllocRef::dealloc(&self, ptr, old_layout);
+ Ok(new_ptr)
+ },
}
}
}
@@ -193,17 +202,17 @@
#[unstable(feature = "allocator_api", issue = "32838")]
unsafe impl AllocRef for System {
#[inline]
- fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
+ fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
self.alloc_impl(layout, false)
}
#[inline]
- fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
+ fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
self.alloc_impl(layout, true)
}
#[inline]
- unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
+ unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() != 0 {
// SAFETY: `layout` is non-zero in size,
// other conditions must be upheld by the caller
@@ -213,54 +222,66 @@
#[inline]
unsafe fn grow(
- &mut self,
+ &self,
ptr: NonNull<u8>,
- layout: Layout,
- new_size: usize,
- ) -> Result<NonNull<[u8]>, AllocErr> {
+ old_layout: Layout,
+ new_layout: Layout,
+ ) -> Result<NonNull<[u8]>, AllocError> {
// SAFETY: all conditions must be upheld by the caller
- unsafe { self.grow_impl(ptr, layout, new_size, false) }
+ unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
}
#[inline]
unsafe fn grow_zeroed(
- &mut self,
+ &self,
ptr: NonNull<u8>,
- layout: Layout,
- new_size: usize,
- ) -> Result<NonNull<[u8]>, AllocErr> {
+ old_layout: Layout,
+ new_layout: Layout,
+ ) -> Result<NonNull<[u8]>, AllocError> {
// SAFETY: all conditions must be upheld by the caller
- unsafe { self.grow_impl(ptr, layout, new_size, true) }
+ unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
}
#[inline]
unsafe fn shrink(
- &mut self,
+ &self,
ptr: NonNull<u8>,
- layout: Layout,
- new_size: usize,
- ) -> Result<NonNull<[u8]>, AllocErr> {
+ old_layout: Layout,
+ new_layout: Layout,
+ ) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(
- new_size <= layout.size(),
- "`new_size` must be smaller than or equal to `layout.size()`"
+ new_layout.size() <= old_layout.size(),
+ "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
);
- match new_size {
+ match new_layout.size() {
// SAFETY: conditions must be upheld by the caller
0 => unsafe {
- self.dealloc(ptr, layout);
- Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0))
+ AllocRef::dealloc(&self, ptr, old_layout);
+ Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0))
},
// SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
- new_size => unsafe {
- // `realloc` probably checks for `new_size <= size` or something similar.
- intrinsics::assume(new_size <= layout.size());
+ new_size if old_layout.align() == new_layout.align() => unsafe {
+ // `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
+ intrinsics::assume(new_size <= old_layout.size());
- let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
- let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
+ let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
+ let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
},
+
+ // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
+ // both the old and new memory allocation are valid for reads and writes for `new_size`
+ // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
+ // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
+ // for `dealloc` must be upheld by the caller.
+ new_size => unsafe {
+ let new_ptr = AllocRef::alloc(&self, new_layout)?;
+ ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
+ AllocRef::dealloc(&self, ptr, old_layout);
+ Ok(new_ptr)
+ },
}
}
}
diff --git a/library/std/src/ascii.rs b/library/std/src/ascii.rs
index c910613..035cd9f 100644
--- a/library/std/src/ascii.rs
+++ b/library/std/src/ascii.rs
@@ -70,7 +70,6 @@
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
///
/// [`make_ascii_uppercase`]: AsciiExt::make_ascii_uppercase
- /// [`str::to_uppercase`]: ../primitive.str.html#method.to_uppercase
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
fn to_ascii_uppercase(&self) -> Self::Owned;
@@ -91,7 +90,6 @@
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
///
/// [`make_ascii_lowercase`]: AsciiExt::make_ascii_lowercase
- /// [`str::to_lowercase`]: ../primitive.str.html#method.to_lowercase
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated)]
fn to_ascii_lowercase(&self) -> Self::Owned;
diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs
index 09f83ea..cc29e1c 100644
--- a/library/std/src/backtrace.rs
+++ b/library/std/src/backtrace.rs
@@ -66,6 +66,9 @@
#![unstable(feature = "backtrace", issue = "53487")]
+#[cfg(test)]
+mod tests;
+
// NB: A note on resolution of a backtrace:
//
// Backtraces primarily happen in two steps, one is where we actually capture
@@ -438,55 +441,3 @@
}
}
}
-
-#[test]
-fn test_debug() {
- let backtrace = Backtrace {
- inner: Inner::Captured(Mutex::new(Capture {
- actual_start: 1,
- resolved: true,
- frames: vec![
- BacktraceFrame {
- frame: RawFrame::Fake,
- symbols: vec![BacktraceSymbol {
- name: Some(b"std::backtrace::Backtrace::create".to_vec()),
- filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())),
- lineno: Some(100),
- }],
- },
- BacktraceFrame {
- frame: RawFrame::Fake,
- symbols: vec![BacktraceSymbol {
- name: Some(b"__rust_maybe_catch_panic".to_vec()),
- filename: None,
- lineno: None,
- }],
- },
- BacktraceFrame {
- frame: RawFrame::Fake,
- symbols: vec![
- BacktraceSymbol {
- name: Some(b"std::rt::lang_start_internal".to_vec()),
- filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
- lineno: Some(300),
- },
- BacktraceSymbol {
- name: Some(b"std::rt::lang_start".to_vec()),
- filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
- lineno: Some(400),
- },
- ],
- },
- ],
- })),
- };
-
- #[rustfmt::skip]
- let expected = "Backtrace [\
- \n { fn: \"__rust_maybe_catch_panic\" },\
- \n { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\
- \n { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\
- \n]";
-
- assert_eq!(format!("{:#?}", backtrace), expected);
-}
diff --git a/library/std/src/backtrace/tests.rs b/library/std/src/backtrace/tests.rs
new file mode 100644
index 0000000..287359c
--- /dev/null
+++ b/library/std/src/backtrace/tests.rs
@@ -0,0 +1,53 @@
+use super::*;
+
+#[test]
+fn test_debug() {
+ let backtrace = Backtrace {
+ inner: Inner::Captured(Mutex::new(Capture {
+ actual_start: 1,
+ resolved: true,
+ frames: vec![
+ BacktraceFrame {
+ frame: RawFrame::Fake,
+ symbols: vec![BacktraceSymbol {
+ name: Some(b"std::backtrace::Backtrace::create".to_vec()),
+ filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())),
+ lineno: Some(100),
+ }],
+ },
+ BacktraceFrame {
+ frame: RawFrame::Fake,
+ symbols: vec![BacktraceSymbol {
+ name: Some(b"__rust_maybe_catch_panic".to_vec()),
+ filename: None,
+ lineno: None,
+ }],
+ },
+ BacktraceFrame {
+ frame: RawFrame::Fake,
+ symbols: vec![
+ BacktraceSymbol {
+ name: Some(b"std::rt::lang_start_internal".to_vec()),
+ filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
+ lineno: Some(300),
+ },
+ BacktraceSymbol {
+ name: Some(b"std::rt::lang_start".to_vec()),
+ filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
+ lineno: Some(400),
+ },
+ ],
+ },
+ ],
+ })),
+ };
+
+ #[rustfmt::skip]
+ let expected = "Backtrace [\
+ \n { fn: \"__rust_maybe_catch_panic\" },\
+ \n { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\
+ \n { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\
+ \n]";
+
+ assert_eq!(format!("{:#?}", backtrace), expected);
+}
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index 70f7214..f12ceff 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -1,4 +1,5 @@
-// ignore-tidy-filelength
+#[cfg(test)]
+mod tests;
use self::Entry::*;
@@ -496,6 +497,50 @@
Drain { base: self.base.drain() }
}
+ /// Creates an iterator which uses a closure to determine if an element should be removed.
+ ///
+ /// If the closure returns true, the element is removed from the map and yielded.
+ /// If the closure returns false, or panics, the element remains in the map and will not be
+ /// yielded.
+ ///
+ /// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of
+ /// whether you choose to keep or remove it.
+ ///
+ /// If the iterator is only partially consumed or not consumed at all, each of the remaining
+ /// elements will still be subjected to the closure and removed and dropped if it returns true.
+ ///
+ /// It is unspecified how many more elements will be subjected to the closure
+ /// if a panic occurs in the closure, or a panic occurs while dropping an element,
+ /// or if the `DrainFilter` value is leaked.
+ ///
+ /// # Examples
+ ///
+ /// Splitting a map into even and odd keys, reusing the original map:
+ ///
+ /// ```
+ /// #![feature(hash_drain_filter)]
+ /// use std::collections::HashMap;
+ ///
+ /// let mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x)).collect();
+ /// let drained: HashMap<i32, i32> = map.drain_filter(|k, _v| k % 2 == 0).collect();
+ ///
+ /// let mut evens = drained.keys().copied().collect::<Vec<_>>();
+ /// let mut odds = map.keys().copied().collect::<Vec<_>>();
+ /// evens.sort();
+ /// odds.sort();
+ ///
+ /// assert_eq!(evens, vec![0, 2, 4, 6]);
+ /// assert_eq!(odds, vec![1, 3, 5, 7]);
+ /// ```
+ #[inline]
+ #[unstable(feature = "hash_drain_filter", issue = "59618")]
+ pub fn drain_filter<F>(&mut self, pred: F) -> DrainFilter<'_, K, V, F>
+ where
+ F: FnMut(&K, &mut V) -> bool,
+ {
+ DrainFilter { base: self.base.drain_filter(pred) }
+ }
+
/// Clears the map, removing all key-value pairs. Keeps the allocated memory
/// for reuse.
///
@@ -1057,6 +1102,16 @@
/// documentation for more.
///
/// [`iter`]: HashMap::iter
+///
+/// # Example
+///
+/// ```
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter = map.iter();
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Iter<'a, K: 'a, V: 'a> {
base: base::Iter<'a, K, V>,
@@ -1084,6 +1139,16 @@
/// documentation for more.
///
/// [`iter_mut`]: HashMap::iter_mut
+///
+/// # Example
+///
+/// ```
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter = map.iter_mut();
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct IterMut<'a, K: 'a, V: 'a> {
base: base::IterMut<'a, K, V>,
@@ -1103,6 +1168,16 @@
/// (provided by the `IntoIterator` trait). See its documentation for more.
///
/// [`into_iter`]: IntoIterator::into_iter
+///
+/// # Example
+///
+/// ```
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter = map.into_iter();
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct IntoIter<K, V> {
base: base::IntoIter<K, V>,
@@ -1122,6 +1197,16 @@
/// documentation for more.
///
/// [`keys`]: HashMap::keys
+///
+/// # Example
+///
+/// ```
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter_keys = map.keys();
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Keys<'a, K: 'a, V: 'a> {
inner: Iter<'a, K, V>,
@@ -1149,6 +1234,16 @@
/// documentation for more.
///
/// [`values`]: HashMap::values
+///
+/// # Example
+///
+/// ```
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter_values = map.values();
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Values<'a, K: 'a, V: 'a> {
inner: Iter<'a, K, V>,
@@ -1176,6 +1271,16 @@
/// documentation for more.
///
/// [`drain`]: HashMap::drain
+///
+/// # Example
+///
+/// ```
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter = map.drain();
+/// ```
#[stable(feature = "drain", since = "1.6.0")]
pub struct Drain<'a, K: 'a, V: 'a> {
base: base::Drain<'a, K, V>,
@@ -1189,12 +1294,47 @@
}
}
+/// A draining, filtering iterator over the entries of a `HashMap`.
+///
+/// This `struct` is created by the [`drain_filter`] method on [`HashMap`].
+///
+/// [`drain_filter`]: HashMap::drain_filter
+///
+/// # Example
+///
+/// ```
+/// #![feature(hash_drain_filter)]
+///
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter = map.drain_filter(|_k, v| *v % 2 == 0);
+/// ```
+#[unstable(feature = "hash_drain_filter", issue = "59618")]
+pub struct DrainFilter<'a, K, V, F>
+where
+ F: FnMut(&K, &mut V) -> bool,
+{
+ base: base::DrainFilter<'a, K, V, F>,
+}
+
/// A mutable iterator over the values of a `HashMap`.
///
/// This `struct` is created by the [`values_mut`] method on [`HashMap`]. See its
/// documentation for more.
///
/// [`values_mut`]: HashMap::values_mut
+///
+/// # Example
+///
+/// ```
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter_values = map.values_mut();
+/// ```
#[stable(feature = "map_values_mut", since = "1.10.0")]
pub struct ValuesMut<'a, K: 'a, V: 'a> {
inner: IterMut<'a, K, V>,
@@ -1206,6 +1346,18 @@
/// See its documentation for more.
///
/// [`into_keys`]: HashMap::into_keys
+///
+/// # Example
+///
+/// ```
+/// #![feature(map_into_keys_values)]
+///
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter_keys = map.into_keys();
+/// ```
#[unstable(feature = "map_into_keys_values", issue = "75294")]
pub struct IntoKeys<K, V> {
inner: IntoIter<K, V>,
@@ -1217,6 +1369,18 @@
/// See its documentation for more.
///
/// [`into_values`]: HashMap::into_values
+///
+/// # Example
+///
+/// ```
+/// #![feature(map_into_keys_values)]
+///
+/// use std::collections::HashMap;
+///
+/// let mut map = HashMap::new();
+/// map.insert("a", 1);
+/// let iter_keys = map.into_values();
+/// ```
#[unstable(feature = "map_into_keys_values", issue = "75294")]
pub struct IntoValues<K, V> {
inner: IntoIter<K, V>,
@@ -1227,7 +1391,6 @@
/// See the [`HashMap::raw_entry_mut`] docs for usage examples.
///
/// [`HashMap::raw_entry_mut`]: HashMap::raw_entry_mut
-
#[unstable(feature = "hash_raw_entry", issue = "56167")]
pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> {
map: &'a mut HashMap<K, V, S>,
@@ -1240,13 +1403,11 @@
/// This `enum` is constructed through the [`raw_entry_mut`] method on [`HashMap`],
/// then calling one of the methods of that [`RawEntryBuilderMut`].
///
-/// [`Entry`]: enum.Entry.html
/// [`raw_entry_mut`]: HashMap::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.
- Occupied(RawOccupiedEntryMut<'a, K, V>),
+ Occupied(RawOccupiedEntryMut<'a, K, V, S>),
/// A vacant entry.
Vacant(RawVacantEntryMut<'a, K, V, S>),
}
@@ -1254,8 +1415,8 @@
/// A view into an occupied entry in a `HashMap`.
/// It is part of the [`RawEntryMut`] enum.
#[unstable(feature = "hash_raw_entry", issue = "56167")]
-pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a> {
- base: base::RawOccupiedEntryMut<'a, K, V>,
+pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a, S: 'a> {
+ base: base::RawOccupiedEntryMut<'a, K, V, S>,
}
/// A view into a vacant entry in a `HashMap`.
@@ -1456,7 +1617,7 @@
}
}
-impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> {
+impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> {
/// Gets a reference to the key in the entry.
#[inline]
#[unstable(feature = "hash_raw_entry", issue = "56167")]
@@ -1596,7 +1757,7 @@
}
#[unstable(feature = "hash_raw_entry", issue = "56167")]
-impl<K: Debug, V: Debug> Debug for RawOccupiedEntryMut<'_, K, V> {
+impl<K: Debug, V: Debug, S> Debug for RawOccupiedEntryMut<'_, K, V, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawOccupiedEntryMut")
.field("key", self.key())
@@ -1647,8 +1808,6 @@
/// A view into an occupied entry in a `HashMap`.
/// It is part of the [`Entry`] enum.
-///
-/// [`Entry`]: enum.Entry.html
#[stable(feature = "rust1", since = "1.0.0")]
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
base: base::RustcOccupiedEntry<'a, K, V>,
@@ -1663,8 +1822,6 @@
/// A view into a vacant entry in a `HashMap`.
/// It is part of the [`Entry`] enum.
-///
-/// [`Entry`]: enum.Entry.html
#[stable(feature = "rust1", since = "1.0.0")]
pub struct VacantEntry<'a, K: 'a, V: 'a> {
base: base::RustcVacantEntry<'a, K, V>,
@@ -1885,13 +2042,9 @@
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,
-{
+impl<K, V: fmt::Debug> fmt::Debug for ValuesMut<'_, K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_list().entries(self.inner.iter()).finish()
+ f.debug_list().entries(self.inner.iter().map(|(_, val)| val)).finish()
}
}
@@ -1919,7 +2072,7 @@
impl<K, V> FusedIterator for IntoKeys<K, V> {}
#[unstable(feature = "map_into_keys_values", issue = "75294")]
-impl<K: Debug, V: Debug> fmt::Debug for IntoKeys<K, V> {
+impl<K: Debug, V> fmt::Debug for IntoKeys<K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish()
}
@@ -1949,7 +2102,7 @@
impl<K, V> FusedIterator for IntoValues<K, V> {}
#[unstable(feature = "map_into_keys_values", issue = "75294")]
-impl<K: Debug, V: Debug> fmt::Debug for IntoValues<K, V> {
+impl<K, V: Debug> fmt::Debug for IntoValues<K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish()
}
@@ -1989,6 +2142,36 @@
}
}
+#[unstable(feature = "hash_drain_filter", issue = "59618")]
+impl<K, V, F> Iterator for DrainFilter<'_, K, V, F>
+where
+ F: FnMut(&K, &mut V) -> bool,
+{
+ type Item = (K, V);
+
+ #[inline]
+ fn next(&mut self) -> Option<(K, V)> {
+ self.base.next()
+ }
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.base.size_hint()
+ }
+}
+
+#[unstable(feature = "hash_drain_filter", issue = "59618")]
+impl<K, V, F> FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {}
+
+#[unstable(feature = "hash_drain_filter", issue = "59618")]
+impl<'a, K, V, F> fmt::Debug for DrainFilter<'a, K, V, F>
+where
+ F: FnMut(&K, &mut V) -> bool,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad("DrainFilter { .. }")
+ }
+}
+
impl<'a, K, V> Entry<'a, K, V> {
#[stable(feature = "rust1", since = "1.0.0")]
/// Ensures a value is in the entry by inserting the default if empty, and returns
@@ -2381,7 +2564,7 @@
/// use std::rc::Rc;
///
/// let mut map: HashMap<Rc<String>, u32> = HashMap::new();
- /// let mut known_strings: Vec<Rc<String>> = Vec::new();
+ /// let known_strings: Vec<Rc<String>> = Vec::new();
///
/// // Initialise known strings, run program, etc.
///
@@ -2389,7 +2572,7 @@
///
/// fn reclaim_memory(map: &mut HashMap<Rc<String>, u32>, known_strings: &[Rc<String>] ) {
/// for s in known_strings {
- /// if let Entry::Occupied(entry) = map.entry(s.clone()) {
+ /// if let Entry::Occupied(entry) = map.entry(Rc::clone(s)) {
/// // Replaces the entry's key with our version of it in `known_strings`.
/// entry.replace_key();
/// }
@@ -2649,11 +2832,10 @@
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
impl Default for DefaultHasher {
- // 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`.
+ /// Creates a new `DefaultHasher` using [`new`].
/// See its documentation for more.
+ ///
+ /// [`new`]: DefaultHasher::new
fn default() -> DefaultHasher {
DefaultHasher::new()
}
@@ -2697,7 +2879,7 @@
}
#[inline]
-fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError {
+pub(super) fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError {
match err {
hashbrown::TryReserveError::CapacityOverflow => TryReserveError::CapacityOverflow,
hashbrown::TryReserveError::AllocError { layout } => {
@@ -2754,933 +2936,3 @@
d
}
}
-
-#[cfg(test)]
-mod test_map {
- use super::Entry::{Occupied, Vacant};
- use super::HashMap;
- use super::RandomState;
- use crate::cell::RefCell;
- use rand::{thread_rng, Rng};
- use realstd::collections::TryReserveError::*;
-
- // 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>;
-
- let m = HM::new();
- assert_eq!(m.capacity(), 0);
-
- let m = HM::default();
- assert_eq!(m.capacity(), 0);
-
- let m = HM::with_hasher(RandomState::new());
- assert_eq!(m.capacity(), 0);
-
- let m = HM::with_capacity(0);
- assert_eq!(m.capacity(), 0);
-
- let m = HM::with_capacity_and_hasher(0, RandomState::new());
- assert_eq!(m.capacity(), 0);
-
- let mut m = HM::new();
- m.insert(1, 1);
- m.insert(2, 2);
- m.remove(&1);
- m.remove(&2);
- m.shrink_to_fit();
- assert_eq!(m.capacity(), 0);
-
- let mut m = HM::new();
- m.reserve(0);
- assert_eq!(m.capacity(), 0);
- }
-
- #[test]
- fn test_create_capacity_zero() {
- let mut m = HashMap::with_capacity(0);
-
- assert!(m.insert(1, 1).is_none());
-
- assert!(m.contains_key(&1));
- assert!(!m.contains_key(&0));
- }
-
- #[test]
- fn test_insert() {
- let mut m = HashMap::new();
- assert_eq!(m.len(), 0);
- assert!(m.insert(1, 2).is_none());
- assert_eq!(m.len(), 1);
- assert!(m.insert(2, 4).is_none());
- assert_eq!(m.len(), 2);
- assert_eq!(*m.get(&1).unwrap(), 2);
- assert_eq!(*m.get(&2).unwrap(), 4);
- }
-
- #[test]
- fn test_clone() {
- let mut m = HashMap::new();
- assert_eq!(m.len(), 0);
- assert!(m.insert(1, 2).is_none());
- assert_eq!(m.len(), 1);
- assert!(m.insert(2, 4).is_none());
- assert_eq!(m.len(), 2);
- let m2 = m.clone();
- assert_eq!(*m2.get(&1).unwrap(), 2);
- assert_eq!(*m2.get(&2).unwrap(), 4);
- assert_eq!(m2.len(), 2);
- }
-
- thread_local! { static DROP_VECTOR: RefCell<Vec<i32>> = RefCell::new(Vec::new()) }
-
- #[derive(Hash, PartialEq, Eq)]
- struct Droppable {
- k: usize,
- }
-
- impl Droppable {
- fn new(k: usize) -> Droppable {
- DROP_VECTOR.with(|slot| {
- slot.borrow_mut()[k] += 1;
- });
-
- Droppable { k }
- }
- }
-
- impl Drop for Droppable {
- fn drop(&mut self) {
- DROP_VECTOR.with(|slot| {
- slot.borrow_mut()[self.k] -= 1;
- });
- }
- }
-
- impl Clone for Droppable {
- fn clone(&self) -> Droppable {
- Droppable::new(self.k)
- }
- }
-
- #[test]
- fn test_drops() {
- DROP_VECTOR.with(|slot| {
- *slot.borrow_mut() = vec![0; 200];
- });
-
- {
- let mut m = HashMap::new();
-
- DROP_VECTOR.with(|v| {
- for i in 0..200 {
- assert_eq!(v.borrow()[i], 0);
- }
- });
-
- for i in 0..100 {
- let d1 = Droppable::new(i);
- let d2 = Droppable::new(i + 100);
- m.insert(d1, d2);
- }
-
- DROP_VECTOR.with(|v| {
- for i in 0..200 {
- assert_eq!(v.borrow()[i], 1);
- }
- });
-
- for i in 0..50 {
- let k = Droppable::new(i);
- let v = m.remove(&k);
-
- assert!(v.is_some());
-
- DROP_VECTOR.with(|v| {
- assert_eq!(v.borrow()[i], 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);
- }
-
- for i in 50..100 {
- assert_eq!(v.borrow()[i], 1);
- assert_eq!(v.borrow()[i + 100], 1);
- }
- });
- }
-
- DROP_VECTOR.with(|v| {
- for i in 0..200 {
- assert_eq!(v.borrow()[i], 0);
- }
- });
- }
-
- #[test]
- fn test_into_iter_drops() {
- DROP_VECTOR.with(|v| {
- *v.borrow_mut() = vec![0; 200];
- });
-
- let hm = {
- let mut hm = HashMap::new();
-
- DROP_VECTOR.with(|v| {
- for i in 0..200 {
- assert_eq!(v.borrow()[i], 0);
- }
- });
-
- for i in 0..100 {
- let d1 = Droppable::new(i);
- let d2 = Droppable::new(i + 100);
- hm.insert(d1, d2);
- }
-
- DROP_VECTOR.with(|v| {
- for i in 0..200 {
- assert_eq!(v.borrow()[i], 1);
- }
- });
-
- hm
- };
-
- // By the way, ensure that cloning doesn't screw up the dropping.
- drop(hm.clone());
-
- {
- let mut half = hm.into_iter().take(50);
-
- DROP_VECTOR.with(|v| {
- for i in 0..200 {
- assert_eq!(v.borrow()[i], 1);
- }
- });
-
- for _ in half.by_ref() {}
-
- DROP_VECTOR.with(|v| {
- let nk = (0..100).filter(|&i| v.borrow()[i] == 1).count();
-
- let nv = (0..100).filter(|&i| v.borrow()[i + 100] == 1).count();
-
- assert_eq!(nk, 50);
- assert_eq!(nv, 50);
- });
- };
-
- DROP_VECTOR.with(|v| {
- for i in 0..200 {
- assert_eq!(v.borrow()[i], 0);
- }
- });
- }
-
- #[test]
- fn test_empty_remove() {
- let mut m: HashMap<i32, bool> = HashMap::new();
- assert_eq!(m.remove(&0), None);
- }
-
- #[test]
- fn test_empty_entry() {
- let mut m: HashMap<i32, bool> = HashMap::new();
- match m.entry(0) {
- Occupied(_) => panic!(),
- Vacant(_) => {}
- }
- assert!(*m.entry(0).or_insert(true));
- assert_eq!(m.len(), 1);
- }
-
- #[test]
- fn test_empty_iter() {
- let mut m: HashMap<i32, bool> = HashMap::new();
- assert_eq!(m.drain().next(), None);
- assert_eq!(m.keys().next(), None);
- assert_eq!(m.values().next(), None);
- assert_eq!(m.values_mut().next(), None);
- assert_eq!(m.iter().next(), None);
- assert_eq!(m.iter_mut().next(), None);
- assert_eq!(m.len(), 0);
- assert!(m.is_empty());
- assert_eq!(m.into_iter().next(), None);
- }
-
- #[test]
- fn test_lots_of_insertions() {
- let mut m = HashMap::new();
-
- // Try this a few times to make sure we never screw up the hashmap's
- // internal state.
- for _ in 0..10 {
- assert!(m.is_empty());
-
- for i in 1..1001 {
- assert!(m.insert(i, i).is_none());
-
- for j in 1..=i {
- let r = m.get(&j);
- assert_eq!(r, Some(&j));
- }
-
- for j in i + 1..1001 {
- let r = m.get(&j);
- assert_eq!(r, None);
- }
- }
-
- for i in 1001..2001 {
- assert!(!m.contains_key(&i));
- }
-
- // remove forwards
- for i in 1..1001 {
- assert!(m.remove(&i).is_some());
-
- for j in 1..=i {
- assert!(!m.contains_key(&j));
- }
-
- for j in i + 1..1001 {
- assert!(m.contains_key(&j));
- }
- }
-
- for i in 1..1001 {
- assert!(!m.contains_key(&i));
- }
-
- for i in 1..1001 {
- assert!(m.insert(i, i).is_none());
- }
-
- // remove backwards
- for i in (1..1001).rev() {
- assert!(m.remove(&i).is_some());
-
- for j in i..1001 {
- assert!(!m.contains_key(&j));
- }
-
- for j in 1..i {
- assert!(m.contains_key(&j));
- }
- }
- }
- }
-
- #[test]
- fn test_find_mut() {
- let mut m = HashMap::new();
- assert!(m.insert(1, 12).is_none());
- assert!(m.insert(2, 8).is_none());
- assert!(m.insert(5, 14).is_none());
- let new = 100;
- match m.get_mut(&5) {
- None => panic!(),
- Some(x) => *x = new,
- }
- assert_eq!(m.get(&5), Some(&new));
- }
-
- #[test]
- fn test_insert_overwrite() {
- let mut m = HashMap::new();
- assert!(m.insert(1, 2).is_none());
- assert_eq!(*m.get(&1).unwrap(), 2);
- assert!(!m.insert(1, 3).is_none());
- assert_eq!(*m.get(&1).unwrap(), 3);
- }
-
- #[test]
- fn test_insert_conflicts() {
- let mut m = HashMap::with_capacity(4);
- assert!(m.insert(1, 2).is_none());
- assert!(m.insert(5, 3).is_none());
- assert!(m.insert(9, 4).is_none());
- assert_eq!(*m.get(&9).unwrap(), 4);
- assert_eq!(*m.get(&5).unwrap(), 3);
- assert_eq!(*m.get(&1).unwrap(), 2);
- }
-
- #[test]
- fn test_conflict_remove() {
- let mut m = HashMap::with_capacity(4);
- assert!(m.insert(1, 2).is_none());
- assert_eq!(*m.get(&1).unwrap(), 2);
- assert!(m.insert(5, 3).is_none());
- assert_eq!(*m.get(&1).unwrap(), 2);
- assert_eq!(*m.get(&5).unwrap(), 3);
- assert!(m.insert(9, 4).is_none());
- assert_eq!(*m.get(&1).unwrap(), 2);
- assert_eq!(*m.get(&5).unwrap(), 3);
- assert_eq!(*m.get(&9).unwrap(), 4);
- assert!(m.remove(&1).is_some());
- assert_eq!(*m.get(&9).unwrap(), 4);
- assert_eq!(*m.get(&5).unwrap(), 3);
- }
-
- #[test]
- fn test_is_empty() {
- let mut m = HashMap::with_capacity(4);
- assert!(m.insert(1, 2).is_none());
- assert!(!m.is_empty());
- assert!(m.remove(&1).is_some());
- assert!(m.is_empty());
- }
-
- #[test]
- fn test_remove() {
- let mut m = HashMap::new();
- m.insert(1, 2);
- assert_eq!(m.remove(&1), Some(2));
- assert_eq!(m.remove(&1), None);
- }
-
- #[test]
- fn test_remove_entry() {
- let mut m = HashMap::new();
- m.insert(1, 2);
- assert_eq!(m.remove_entry(&1), Some((1, 2)));
- assert_eq!(m.remove(&1), None);
- }
-
- #[test]
- fn test_iterate() {
- let mut m = HashMap::with_capacity(4);
- for i in 0..32 {
- assert!(m.insert(i, i * 2).is_none());
- }
- assert_eq!(m.len(), 32);
-
- let mut observed: u32 = 0;
-
- for (k, v) in &m {
- assert_eq!(*v, *k * 2);
- observed |= 1 << *k;
- }
- assert_eq!(observed, 0xFFFF_FFFF);
- }
-
- #[test]
- fn test_keys() {
- let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
- let map: HashMap<_, _> = vec.into_iter().collect();
- let keys: Vec<_> = map.keys().cloned().collect();
- assert_eq!(keys.len(), 3);
- assert!(keys.contains(&1));
- assert!(keys.contains(&2));
- assert!(keys.contains(&3));
- }
-
- #[test]
- fn test_values() {
- let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
- let map: HashMap<_, _> = vec.into_iter().collect();
- let values: Vec<_> = map.values().cloned().collect();
- assert_eq!(values.len(), 3);
- assert!(values.contains(&'a'));
- assert!(values.contains(&'b'));
- assert!(values.contains(&'c'));
- }
-
- #[test]
- fn test_values_mut() {
- let vec = vec![(1, 1), (2, 2), (3, 3)];
- let mut map: HashMap<_, _> = vec.into_iter().collect();
- for value in map.values_mut() {
- *value = (*value) * 2
- }
- let values: Vec<_> = map.values().cloned().collect();
- assert_eq!(values.len(), 3);
- assert!(values.contains(&2));
- assert!(values.contains(&4));
- assert!(values.contains(&6));
- }
-
- #[test]
- fn test_into_keys() {
- let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
- let map: HashMap<_, _> = vec.into_iter().collect();
- let keys: Vec<_> = map.into_keys().collect();
-
- assert_eq!(keys.len(), 3);
- assert!(keys.contains(&1));
- assert!(keys.contains(&2));
- assert!(keys.contains(&3));
- }
-
- #[test]
- fn test_into_values() {
- let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
- let map: HashMap<_, _> = vec.into_iter().collect();
- let values: Vec<_> = map.into_values().collect();
-
- assert_eq!(values.len(), 3);
- assert!(values.contains(&'a'));
- assert!(values.contains(&'b'));
- assert!(values.contains(&'c'));
- }
-
- #[test]
- fn test_find() {
- let mut m = HashMap::new();
- assert!(m.get(&1).is_none());
- m.insert(1, 2);
- match m.get(&1) {
- None => panic!(),
- Some(v) => assert_eq!(*v, 2),
- }
- }
-
- #[test]
- fn test_eq() {
- let mut m1 = HashMap::new();
- m1.insert(1, 2);
- m1.insert(2, 3);
- m1.insert(3, 4);
-
- let mut m2 = HashMap::new();
- m2.insert(1, 2);
- m2.insert(2, 3);
-
- assert!(m1 != m2);
-
- m2.insert(3, 4);
-
- assert_eq!(m1, m2);
- }
-
- #[test]
- fn test_show() {
- let mut map = HashMap::new();
- let empty: HashMap<i32, i32> = HashMap::new();
-
- map.insert(1, 2);
- map.insert(3, 4);
-
- let map_str = format!("{:?}", map);
-
- assert!(map_str == "{1: 2, 3: 4}" || map_str == "{3: 4, 1: 2}");
- assert_eq!(format!("{:?}", empty), "{}");
- }
-
- #[test]
- fn test_reserve_shrink_to_fit() {
- let mut m = HashMap::new();
- m.insert(0, 0);
- m.remove(&0);
- assert!(m.capacity() >= m.len());
- for i in 0..128 {
- m.insert(i, i);
- }
- m.reserve(256);
-
- let usable_cap = m.capacity();
- for i in 128..(128 + 256) {
- m.insert(i, i);
- assert_eq!(m.capacity(), usable_cap);
- }
-
- for i in 100..(128 + 256) {
- assert_eq!(m.remove(&i), Some(i));
- }
- m.shrink_to_fit();
-
- assert_eq!(m.len(), 100);
- assert!(!m.is_empty());
- assert!(m.capacity() >= m.len());
-
- for i in 0..100 {
- assert_eq!(m.remove(&i), Some(i));
- }
- m.shrink_to_fit();
- m.insert(0, 0);
-
- assert_eq!(m.len(), 1);
- assert!(m.capacity() >= m.len());
- assert_eq!(m.remove(&0), Some(0));
- }
-
- #[test]
- fn test_from_iter() {
- 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]
- fn test_size_hint() {
- let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
-
- let map: HashMap<_, _> = xs.iter().cloned().collect();
-
- let mut iter = map.iter();
-
- for _ in iter.by_ref().take(3) {}
-
- assert_eq!(iter.size_hint(), (3, Some(3)));
- }
-
- #[test]
- fn test_iter_len() {
- let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
-
- let map: HashMap<_, _> = xs.iter().cloned().collect();
-
- let mut iter = map.iter();
-
- for _ in iter.by_ref().take(3) {}
-
- assert_eq!(iter.len(), 3);
- }
-
- #[test]
- fn test_mut_size_hint() {
- let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
-
- let mut map: HashMap<_, _> = xs.iter().cloned().collect();
-
- let mut iter = map.iter_mut();
-
- for _ in iter.by_ref().take(3) {}
-
- assert_eq!(iter.size_hint(), (3, Some(3)));
- }
-
- #[test]
- fn test_iter_mut_len() {
- let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
-
- let mut map: HashMap<_, _> = xs.iter().cloned().collect();
-
- let mut iter = map.iter_mut();
-
- for _ in iter.by_ref().take(3) {}
-
- assert_eq!(iter.len(), 3);
- }
-
- #[test]
- fn test_index() {
- let mut map = HashMap::new();
-
- map.insert(1, 2);
- map.insert(2, 1);
- map.insert(3, 4);
-
- assert_eq!(map[&2], 1);
- }
-
- #[test]
- #[should_panic]
- fn test_index_nonexistent() {
- let mut map = HashMap::new();
-
- map.insert(1, 2);
- map.insert(2, 1);
- map.insert(3, 4);
-
- map[&4];
- }
-
- #[test]
- fn test_entry() {
- let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
-
- let mut map: HashMap<_, _> = xs.iter().cloned().collect();
-
- // Existing key (insert)
- match map.entry(1) {
- Vacant(_) => unreachable!(),
- Occupied(mut view) => {
- assert_eq!(view.get(), &10);
- assert_eq!(view.insert(100), 10);
- }
- }
- assert_eq!(map.get(&1).unwrap(), &100);
- assert_eq!(map.len(), 6);
-
- // Existing key (update)
- match map.entry(2) {
- Vacant(_) => unreachable!(),
- Occupied(mut view) => {
- let v = view.get_mut();
- let new_v = (*v) * 10;
- *v = new_v;
- }
- }
- assert_eq!(map.get(&2).unwrap(), &200);
- assert_eq!(map.len(), 6);
-
- // Existing key (take)
- match map.entry(3) {
- Vacant(_) => unreachable!(),
- Occupied(view) => {
- assert_eq!(view.remove(), 30);
- }
- }
- assert_eq!(map.get(&3), None);
- assert_eq!(map.len(), 5);
-
- // Inexistent key (insert)
- match map.entry(10) {
- Occupied(_) => unreachable!(),
- Vacant(view) => {
- assert_eq!(*view.insert(1000), 1000);
- }
- }
- assert_eq!(map.get(&10).unwrap(), &1000);
- assert_eq!(map.len(), 6);
- }
-
- #[test]
- fn test_entry_take_doesnt_corrupt() {
- #![allow(deprecated)] //rand
- // 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);
- }
- }
-
- let mut m = HashMap::new();
- let mut rng = thread_rng();
-
- // Populate the map with some items.
- for _ in 0..50 {
- let x = rng.gen_range(-10, 10);
- m.insert(x, ());
- }
-
- for _ in 0..1000 {
- let x = rng.gen_range(-10, 10);
- match m.entry(x) {
- Vacant(_) => {}
- Occupied(e) => {
- e.remove();
- }
- }
-
- check(&m);
- }
- }
-
- #[test]
- fn test_extend_ref() {
- let mut a = HashMap::new();
- a.insert(1, "one");
- let mut b = HashMap::new();
- b.insert(2, "two");
- b.insert(3, "three");
-
- a.extend(&b);
-
- assert_eq!(a.len(), 3);
- assert_eq!(a[&1], "one");
- assert_eq!(a[&2], "two");
- assert_eq!(a[&3], "three");
- }
-
- #[test]
- fn test_capacity_not_less_than_len() {
- let mut a = HashMap::new();
- let mut item = 0;
-
- for _ in 0..116 {
- a.insert(item, 0);
- item += 1;
- }
-
- assert!(a.capacity() > a.len());
-
- let free = a.capacity() - a.len();
- for _ in 0..free {
- a.insert(item, 0);
- item += 1;
- }
-
- assert_eq!(a.len(), a.capacity());
-
- // Insert at capacity should cause allocation.
- a.insert(item, 0);
- assert!(a.capacity() > a.len());
- }
-
- #[test]
- fn test_occupied_entry_key() {
- let mut a = HashMap::new();
- let key = "hello there";
- let value = "value goes here";
- assert!(a.is_empty());
- a.insert(key.clone(), value.clone());
- assert_eq!(a.len(), 1);
- assert_eq!(a[key], value);
-
- match a.entry(key.clone()) {
- Vacant(_) => panic!(),
- Occupied(e) => assert_eq!(key, *e.key()),
- }
- assert_eq!(a.len(), 1);
- assert_eq!(a[key], value);
- }
-
- #[test]
- fn test_vacant_entry_key() {
- let mut a = HashMap::new();
- let key = "hello there";
- let value = "value goes here";
-
- assert!(a.is_empty());
- match a.entry(key.clone()) {
- Occupied(_) => panic!(),
- Vacant(e) => {
- assert_eq!(key, *e.key());
- e.insert(value.clone());
- }
- }
- assert_eq!(a.len(), 1);
- assert_eq!(a[key], value);
- }
-
- #[test]
- fn test_retain() {
- 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);
- assert_eq!(map[&2], 20);
- assert_eq!(map[&4], 40);
- assert_eq!(map[&6], 60);
- }
-
- #[test]
- fn test_try_reserve() {
- let mut empty_bytes: HashMap<u8, u8> = HashMap::new();
-
- const MAX_USIZE: usize = usize::MAX;
-
- if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) {
- } else {
- panic!("usize::MAX should trigger an overflow!");
- }
-
- if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE / 8) {
- } else {
- panic!("usize::MAX / 8 should trigger an OOM!")
- }
- }
-
- #[test]
- fn test_raw_entry() {
- use super::RawEntryMut::{Occupied, Vacant};
-
- let xs = [(1i32, 10i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
-
- let mut map: HashMap<_, _> = xs.iter().cloned().collect();
-
- let compute_hash = |map: &HashMap<i32, i32>, k: i32| -> u64 {
- use core::hash::{BuildHasher, Hash, Hasher};
-
- let mut hasher = map.hasher().build_hasher();
- k.hash(&mut hasher);
- hasher.finish()
- };
-
- // Existing key (insert)
- match map.raw_entry_mut().from_key(&1) {
- Vacant(_) => unreachable!(),
- Occupied(mut view) => {
- assert_eq!(view.get(), &10);
- assert_eq!(view.insert(100), 10);
- }
- }
- 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.len(), 6);
-
- // Existing key (update)
- match map.raw_entry_mut().from_key(&2) {
- Vacant(_) => unreachable!(),
- Occupied(mut view) => {
- let v = view.get_mut();
- let new_v = (*v) * 10;
- *v = new_v;
- }
- }
- 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.len(), 6);
-
- // Existing key (take)
- let hash3 = compute_hash(&map, 3);
- match map.raw_entry_mut().from_key_hashed_nocheck(hash3, &3) {
- Vacant(_) => unreachable!(),
- Occupied(view) => {
- assert_eq!(view.remove_entry(), (3, 30));
- }
- }
- 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.len(), 5);
-
- // Nonexistent key (insert)
- match map.raw_entry_mut().from_key(&10) {
- Occupied(_) => unreachable!(),
- Vacant(view) => {
- assert_eq!(view.insert(10, 1000), (&mut 10, &mut 1000));
- }
- }
- assert_eq!(map.raw_entry().from_key(&10).unwrap(), (&10, &1000));
- assert_eq!(map.len(), 6);
-
- // Ensure all lookup methods produce equivalent results.
- for k in 0..12 {
- let hash = compute_hash(&map, k);
- let v = map.get(&k).cloned();
- let kv = v.as_ref().map(|v| (&k, v));
-
- 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);
-
- match map.raw_entry_mut().from_key(&k) {
- Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv),
- Vacant(_) => assert_eq!(v, None),
- }
- match map.raw_entry_mut().from_key_hashed_nocheck(hash, &k) {
- Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv),
- Vacant(_) => assert_eq!(v, None),
- }
- match map.raw_entry_mut().from_hash(hash, |q| *q == k) {
- Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv),
- Vacant(_) => assert_eq!(v, None),
- }
- }
- }
-}
diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs
new file mode 100644
index 0000000..4679683
--- /dev/null
+++ b/library/std/src/collections/hash/map/tests.rs
@@ -0,0 +1,1087 @@
+use super::Entry::{Occupied, Vacant};
+use super::HashMap;
+use super::RandomState;
+use crate::cell::RefCell;
+use rand::{thread_rng, Rng};
+use realstd::collections::TryReserveError::*;
+
+// 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>;
+
+ let m = HM::new();
+ assert_eq!(m.capacity(), 0);
+
+ let m = HM::default();
+ assert_eq!(m.capacity(), 0);
+
+ let m = HM::with_hasher(RandomState::new());
+ assert_eq!(m.capacity(), 0);
+
+ let m = HM::with_capacity(0);
+ assert_eq!(m.capacity(), 0);
+
+ let m = HM::with_capacity_and_hasher(0, RandomState::new());
+ assert_eq!(m.capacity(), 0);
+
+ let mut m = HM::new();
+ m.insert(1, 1);
+ m.insert(2, 2);
+ m.remove(&1);
+ m.remove(&2);
+ m.shrink_to_fit();
+ assert_eq!(m.capacity(), 0);
+
+ let mut m = HM::new();
+ m.reserve(0);
+ assert_eq!(m.capacity(), 0);
+}
+
+#[test]
+fn test_create_capacity_zero() {
+ let mut m = HashMap::with_capacity(0);
+
+ assert!(m.insert(1, 1).is_none());
+
+ assert!(m.contains_key(&1));
+ assert!(!m.contains_key(&0));
+}
+
+#[test]
+fn test_insert() {
+ let mut m = HashMap::new();
+ assert_eq!(m.len(), 0);
+ assert!(m.insert(1, 2).is_none());
+ assert_eq!(m.len(), 1);
+ assert!(m.insert(2, 4).is_none());
+ assert_eq!(m.len(), 2);
+ assert_eq!(*m.get(&1).unwrap(), 2);
+ assert_eq!(*m.get(&2).unwrap(), 4);
+}
+
+#[test]
+fn test_clone() {
+ let mut m = HashMap::new();
+ assert_eq!(m.len(), 0);
+ assert!(m.insert(1, 2).is_none());
+ assert_eq!(m.len(), 1);
+ assert!(m.insert(2, 4).is_none());
+ assert_eq!(m.len(), 2);
+ let m2 = m.clone();
+ assert_eq!(*m2.get(&1).unwrap(), 2);
+ assert_eq!(*m2.get(&2).unwrap(), 4);
+ assert_eq!(m2.len(), 2);
+}
+
+thread_local! { static DROP_VECTOR: RefCell<Vec<i32>> = RefCell::new(Vec::new()) }
+
+#[derive(Hash, PartialEq, Eq)]
+struct Droppable {
+ k: usize,
+}
+
+impl Droppable {
+ fn new(k: usize) -> Droppable {
+ DROP_VECTOR.with(|slot| {
+ slot.borrow_mut()[k] += 1;
+ });
+
+ Droppable { k }
+ }
+}
+
+impl Drop for Droppable {
+ fn drop(&mut self) {
+ DROP_VECTOR.with(|slot| {
+ slot.borrow_mut()[self.k] -= 1;
+ });
+ }
+}
+
+impl Clone for Droppable {
+ fn clone(&self) -> Droppable {
+ Droppable::new(self.k)
+ }
+}
+
+#[test]
+fn test_drops() {
+ DROP_VECTOR.with(|slot| {
+ *slot.borrow_mut() = vec![0; 200];
+ });
+
+ {
+ let mut m = HashMap::new();
+
+ DROP_VECTOR.with(|v| {
+ for i in 0..200 {
+ assert_eq!(v.borrow()[i], 0);
+ }
+ });
+
+ for i in 0..100 {
+ let d1 = Droppable::new(i);
+ let d2 = Droppable::new(i + 100);
+ m.insert(d1, d2);
+ }
+
+ DROP_VECTOR.with(|v| {
+ for i in 0..200 {
+ assert_eq!(v.borrow()[i], 1);
+ }
+ });
+
+ for i in 0..50 {
+ let k = Droppable::new(i);
+ let v = m.remove(&k);
+
+ assert!(v.is_some());
+
+ DROP_VECTOR.with(|v| {
+ assert_eq!(v.borrow()[i], 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);
+ }
+
+ for i in 50..100 {
+ assert_eq!(v.borrow()[i], 1);
+ assert_eq!(v.borrow()[i + 100], 1);
+ }
+ });
+ }
+
+ DROP_VECTOR.with(|v| {
+ for i in 0..200 {
+ assert_eq!(v.borrow()[i], 0);
+ }
+ });
+}
+
+#[test]
+fn test_into_iter_drops() {
+ DROP_VECTOR.with(|v| {
+ *v.borrow_mut() = vec![0; 200];
+ });
+
+ let hm = {
+ let mut hm = HashMap::new();
+
+ DROP_VECTOR.with(|v| {
+ for i in 0..200 {
+ assert_eq!(v.borrow()[i], 0);
+ }
+ });
+
+ for i in 0..100 {
+ let d1 = Droppable::new(i);
+ let d2 = Droppable::new(i + 100);
+ hm.insert(d1, d2);
+ }
+
+ DROP_VECTOR.with(|v| {
+ for i in 0..200 {
+ assert_eq!(v.borrow()[i], 1);
+ }
+ });
+
+ hm
+ };
+
+ // By the way, ensure that cloning doesn't screw up the dropping.
+ drop(hm.clone());
+
+ {
+ let mut half = hm.into_iter().take(50);
+
+ DROP_VECTOR.with(|v| {
+ for i in 0..200 {
+ assert_eq!(v.borrow()[i], 1);
+ }
+ });
+
+ for _ in half.by_ref() {}
+
+ DROP_VECTOR.with(|v| {
+ let nk = (0..100).filter(|&i| v.borrow()[i] == 1).count();
+
+ let nv = (0..100).filter(|&i| v.borrow()[i + 100] == 1).count();
+
+ assert_eq!(nk, 50);
+ assert_eq!(nv, 50);
+ });
+ };
+
+ DROP_VECTOR.with(|v| {
+ for i in 0..200 {
+ assert_eq!(v.borrow()[i], 0);
+ }
+ });
+}
+
+#[test]
+fn test_empty_remove() {
+ let mut m: HashMap<i32, bool> = HashMap::new();
+ assert_eq!(m.remove(&0), None);
+}
+
+#[test]
+fn test_empty_entry() {
+ let mut m: HashMap<i32, bool> = HashMap::new();
+ match m.entry(0) {
+ Occupied(_) => panic!(),
+ Vacant(_) => {}
+ }
+ assert!(*m.entry(0).or_insert(true));
+ assert_eq!(m.len(), 1);
+}
+
+#[test]
+fn test_empty_iter() {
+ let mut m: HashMap<i32, bool> = HashMap::new();
+ assert_eq!(m.drain().next(), None);
+ assert_eq!(m.keys().next(), None);
+ assert_eq!(m.values().next(), None);
+ assert_eq!(m.values_mut().next(), None);
+ assert_eq!(m.iter().next(), None);
+ assert_eq!(m.iter_mut().next(), None);
+ assert_eq!(m.len(), 0);
+ assert!(m.is_empty());
+ assert_eq!(m.into_iter().next(), None);
+}
+
+#[test]
+fn test_lots_of_insertions() {
+ let mut m = HashMap::new();
+
+ // Try this a few times to make sure we never screw up the hashmap's
+ // internal state.
+ for _ in 0..10 {
+ assert!(m.is_empty());
+
+ for i in 1..1001 {
+ assert!(m.insert(i, i).is_none());
+
+ for j in 1..=i {
+ let r = m.get(&j);
+ assert_eq!(r, Some(&j));
+ }
+
+ for j in i + 1..1001 {
+ let r = m.get(&j);
+ assert_eq!(r, None);
+ }
+ }
+
+ for i in 1001..2001 {
+ assert!(!m.contains_key(&i));
+ }
+
+ // remove forwards
+ for i in 1..1001 {
+ assert!(m.remove(&i).is_some());
+
+ for j in 1..=i {
+ assert!(!m.contains_key(&j));
+ }
+
+ for j in i + 1..1001 {
+ assert!(m.contains_key(&j));
+ }
+ }
+
+ for i in 1..1001 {
+ assert!(!m.contains_key(&i));
+ }
+
+ for i in 1..1001 {
+ assert!(m.insert(i, i).is_none());
+ }
+
+ // remove backwards
+ for i in (1..1001).rev() {
+ assert!(m.remove(&i).is_some());
+
+ for j in i..1001 {
+ assert!(!m.contains_key(&j));
+ }
+
+ for j in 1..i {
+ assert!(m.contains_key(&j));
+ }
+ }
+ }
+}
+
+#[test]
+fn test_find_mut() {
+ let mut m = HashMap::new();
+ assert!(m.insert(1, 12).is_none());
+ assert!(m.insert(2, 8).is_none());
+ assert!(m.insert(5, 14).is_none());
+ let new = 100;
+ match m.get_mut(&5) {
+ None => panic!(),
+ Some(x) => *x = new,
+ }
+ assert_eq!(m.get(&5), Some(&new));
+}
+
+#[test]
+fn test_insert_overwrite() {
+ let mut m = HashMap::new();
+ assert!(m.insert(1, 2).is_none());
+ assert_eq!(*m.get(&1).unwrap(), 2);
+ assert!(!m.insert(1, 3).is_none());
+ assert_eq!(*m.get(&1).unwrap(), 3);
+}
+
+#[test]
+fn test_insert_conflicts() {
+ let mut m = HashMap::with_capacity(4);
+ assert!(m.insert(1, 2).is_none());
+ assert!(m.insert(5, 3).is_none());
+ assert!(m.insert(9, 4).is_none());
+ assert_eq!(*m.get(&9).unwrap(), 4);
+ assert_eq!(*m.get(&5).unwrap(), 3);
+ assert_eq!(*m.get(&1).unwrap(), 2);
+}
+
+#[test]
+fn test_conflict_remove() {
+ let mut m = HashMap::with_capacity(4);
+ assert!(m.insert(1, 2).is_none());
+ assert_eq!(*m.get(&1).unwrap(), 2);
+ assert!(m.insert(5, 3).is_none());
+ assert_eq!(*m.get(&1).unwrap(), 2);
+ assert_eq!(*m.get(&5).unwrap(), 3);
+ assert!(m.insert(9, 4).is_none());
+ assert_eq!(*m.get(&1).unwrap(), 2);
+ assert_eq!(*m.get(&5).unwrap(), 3);
+ assert_eq!(*m.get(&9).unwrap(), 4);
+ assert!(m.remove(&1).is_some());
+ assert_eq!(*m.get(&9).unwrap(), 4);
+ assert_eq!(*m.get(&5).unwrap(), 3);
+}
+
+#[test]
+fn test_is_empty() {
+ let mut m = HashMap::with_capacity(4);
+ assert!(m.insert(1, 2).is_none());
+ assert!(!m.is_empty());
+ assert!(m.remove(&1).is_some());
+ assert!(m.is_empty());
+}
+
+#[test]
+fn test_remove() {
+ let mut m = HashMap::new();
+ m.insert(1, 2);
+ assert_eq!(m.remove(&1), Some(2));
+ assert_eq!(m.remove(&1), None);
+}
+
+#[test]
+fn test_remove_entry() {
+ let mut m = HashMap::new();
+ m.insert(1, 2);
+ assert_eq!(m.remove_entry(&1), Some((1, 2)));
+ assert_eq!(m.remove(&1), None);
+}
+
+#[test]
+fn test_iterate() {
+ let mut m = HashMap::with_capacity(4);
+ for i in 0..32 {
+ assert!(m.insert(i, i * 2).is_none());
+ }
+ assert_eq!(m.len(), 32);
+
+ let mut observed: u32 = 0;
+
+ for (k, v) in &m {
+ assert_eq!(*v, *k * 2);
+ observed |= 1 << *k;
+ }
+ assert_eq!(observed, 0xFFFF_FFFF);
+}
+
+#[test]
+fn test_keys() {
+ let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+ let map: HashMap<_, _> = vec.into_iter().collect();
+ let keys: Vec<_> = map.keys().cloned().collect();
+ assert_eq!(keys.len(), 3);
+ assert!(keys.contains(&1));
+ assert!(keys.contains(&2));
+ assert!(keys.contains(&3));
+}
+
+#[test]
+fn test_values() {
+ let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+ let map: HashMap<_, _> = vec.into_iter().collect();
+ let values: Vec<_> = map.values().cloned().collect();
+ assert_eq!(values.len(), 3);
+ assert!(values.contains(&'a'));
+ assert!(values.contains(&'b'));
+ assert!(values.contains(&'c'));
+}
+
+#[test]
+fn test_values_mut() {
+ let vec = vec![(1, 1), (2, 2), (3, 3)];
+ let mut map: HashMap<_, _> = vec.into_iter().collect();
+ for value in map.values_mut() {
+ *value = (*value) * 2
+ }
+ let values: Vec<_> = map.values().cloned().collect();
+ assert_eq!(values.len(), 3);
+ assert!(values.contains(&2));
+ assert!(values.contains(&4));
+ assert!(values.contains(&6));
+}
+
+#[test]
+fn test_into_keys() {
+ let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+ let map: HashMap<_, _> = vec.into_iter().collect();
+ let keys: Vec<_> = map.into_keys().collect();
+
+ assert_eq!(keys.len(), 3);
+ assert!(keys.contains(&1));
+ assert!(keys.contains(&2));
+ assert!(keys.contains(&3));
+}
+
+#[test]
+fn test_into_values() {
+ let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+ let map: HashMap<_, _> = vec.into_iter().collect();
+ let values: Vec<_> = map.into_values().collect();
+
+ assert_eq!(values.len(), 3);
+ assert!(values.contains(&'a'));
+ assert!(values.contains(&'b'));
+ assert!(values.contains(&'c'));
+}
+
+#[test]
+fn test_find() {
+ let mut m = HashMap::new();
+ assert!(m.get(&1).is_none());
+ m.insert(1, 2);
+ match m.get(&1) {
+ None => panic!(),
+ Some(v) => assert_eq!(*v, 2),
+ }
+}
+
+#[test]
+fn test_eq() {
+ let mut m1 = HashMap::new();
+ m1.insert(1, 2);
+ m1.insert(2, 3);
+ m1.insert(3, 4);
+
+ let mut m2 = HashMap::new();
+ m2.insert(1, 2);
+ m2.insert(2, 3);
+
+ assert!(m1 != m2);
+
+ m2.insert(3, 4);
+
+ assert_eq!(m1, m2);
+}
+
+#[test]
+fn test_show() {
+ let mut map = HashMap::new();
+ let empty: HashMap<i32, i32> = HashMap::new();
+
+ map.insert(1, 2);
+ map.insert(3, 4);
+
+ let map_str = format!("{:?}", map);
+
+ assert!(map_str == "{1: 2, 3: 4}" || map_str == "{3: 4, 1: 2}");
+ assert_eq!(format!("{:?}", empty), "{}");
+}
+
+#[test]
+fn test_reserve_shrink_to_fit() {
+ let mut m = HashMap::new();
+ m.insert(0, 0);
+ m.remove(&0);
+ assert!(m.capacity() >= m.len());
+ for i in 0..128 {
+ m.insert(i, i);
+ }
+ m.reserve(256);
+
+ let usable_cap = m.capacity();
+ for i in 128..(128 + 256) {
+ m.insert(i, i);
+ assert_eq!(m.capacity(), usable_cap);
+ }
+
+ for i in 100..(128 + 256) {
+ assert_eq!(m.remove(&i), Some(i));
+ }
+ m.shrink_to_fit();
+
+ assert_eq!(m.len(), 100);
+ assert!(!m.is_empty());
+ assert!(m.capacity() >= m.len());
+
+ for i in 0..100 {
+ assert_eq!(m.remove(&i), Some(i));
+ }
+ m.shrink_to_fit();
+ m.insert(0, 0);
+
+ assert_eq!(m.len(), 1);
+ assert!(m.capacity() >= m.len());
+ assert_eq!(m.remove(&0), Some(0));
+}
+
+#[test]
+fn test_from_iter() {
+ 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]
+fn test_size_hint() {
+ let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
+
+ let map: HashMap<_, _> = xs.iter().cloned().collect();
+
+ let mut iter = map.iter();
+
+ for _ in iter.by_ref().take(3) {}
+
+ assert_eq!(iter.size_hint(), (3, Some(3)));
+}
+
+#[test]
+fn test_iter_len() {
+ let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
+
+ let map: HashMap<_, _> = xs.iter().cloned().collect();
+
+ let mut iter = map.iter();
+
+ for _ in iter.by_ref().take(3) {}
+
+ assert_eq!(iter.len(), 3);
+}
+
+#[test]
+fn test_mut_size_hint() {
+ let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
+
+ let mut map: HashMap<_, _> = xs.iter().cloned().collect();
+
+ let mut iter = map.iter_mut();
+
+ for _ in iter.by_ref().take(3) {}
+
+ assert_eq!(iter.size_hint(), (3, Some(3)));
+}
+
+#[test]
+fn test_iter_mut_len() {
+ let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
+
+ let mut map: HashMap<_, _> = xs.iter().cloned().collect();
+
+ let mut iter = map.iter_mut();
+
+ for _ in iter.by_ref().take(3) {}
+
+ assert_eq!(iter.len(), 3);
+}
+
+#[test]
+fn test_index() {
+ let mut map = HashMap::new();
+
+ map.insert(1, 2);
+ map.insert(2, 1);
+ map.insert(3, 4);
+
+ assert_eq!(map[&2], 1);
+}
+
+#[test]
+#[should_panic]
+fn test_index_nonexistent() {
+ let mut map = HashMap::new();
+
+ map.insert(1, 2);
+ map.insert(2, 1);
+ map.insert(3, 4);
+
+ map[&4];
+}
+
+#[test]
+fn test_entry() {
+ let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
+
+ let mut map: HashMap<_, _> = xs.iter().cloned().collect();
+
+ // Existing key (insert)
+ match map.entry(1) {
+ Vacant(_) => unreachable!(),
+ Occupied(mut view) => {
+ assert_eq!(view.get(), &10);
+ assert_eq!(view.insert(100), 10);
+ }
+ }
+ assert_eq!(map.get(&1).unwrap(), &100);
+ assert_eq!(map.len(), 6);
+
+ // Existing key (update)
+ match map.entry(2) {
+ Vacant(_) => unreachable!(),
+ Occupied(mut view) => {
+ let v = view.get_mut();
+ let new_v = (*v) * 10;
+ *v = new_v;
+ }
+ }
+ assert_eq!(map.get(&2).unwrap(), &200);
+ assert_eq!(map.len(), 6);
+
+ // Existing key (take)
+ match map.entry(3) {
+ Vacant(_) => unreachable!(),
+ Occupied(view) => {
+ assert_eq!(view.remove(), 30);
+ }
+ }
+ assert_eq!(map.get(&3), None);
+ assert_eq!(map.len(), 5);
+
+ // Inexistent key (insert)
+ match map.entry(10) {
+ Occupied(_) => unreachable!(),
+ Vacant(view) => {
+ assert_eq!(*view.insert(1000), 1000);
+ }
+ }
+ assert_eq!(map.get(&10).unwrap(), &1000);
+ assert_eq!(map.len(), 6);
+}
+
+#[test]
+fn test_entry_take_doesnt_corrupt() {
+ #![allow(deprecated)] //rand
+ // 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);
+ }
+ }
+
+ let mut m = HashMap::new();
+ let mut rng = thread_rng();
+
+ // Populate the map with some items.
+ for _ in 0..50 {
+ let x = rng.gen_range(-10, 10);
+ m.insert(x, ());
+ }
+
+ for _ in 0..1000 {
+ let x = rng.gen_range(-10, 10);
+ match m.entry(x) {
+ Vacant(_) => {}
+ Occupied(e) => {
+ e.remove();
+ }
+ }
+
+ check(&m);
+ }
+}
+
+#[test]
+fn test_extend_ref() {
+ let mut a = HashMap::new();
+ a.insert(1, "one");
+ let mut b = HashMap::new();
+ b.insert(2, "two");
+ b.insert(3, "three");
+
+ a.extend(&b);
+
+ assert_eq!(a.len(), 3);
+ assert_eq!(a[&1], "one");
+ assert_eq!(a[&2], "two");
+ assert_eq!(a[&3], "three");
+}
+
+#[test]
+fn test_capacity_not_less_than_len() {
+ let mut a = HashMap::new();
+ let mut item = 0;
+
+ for _ in 0..116 {
+ a.insert(item, 0);
+ item += 1;
+ }
+
+ assert!(a.capacity() > a.len());
+
+ let free = a.capacity() - a.len();
+ for _ in 0..free {
+ a.insert(item, 0);
+ item += 1;
+ }
+
+ assert_eq!(a.len(), a.capacity());
+
+ // Insert at capacity should cause allocation.
+ a.insert(item, 0);
+ assert!(a.capacity() > a.len());
+}
+
+#[test]
+fn test_occupied_entry_key() {
+ let mut a = HashMap::new();
+ let key = "hello there";
+ let value = "value goes here";
+ assert!(a.is_empty());
+ a.insert(key.clone(), value.clone());
+ assert_eq!(a.len(), 1);
+ assert_eq!(a[key], value);
+
+ match a.entry(key.clone()) {
+ Vacant(_) => panic!(),
+ Occupied(e) => assert_eq!(key, *e.key()),
+ }
+ assert_eq!(a.len(), 1);
+ assert_eq!(a[key], value);
+}
+
+#[test]
+fn test_vacant_entry_key() {
+ let mut a = HashMap::new();
+ let key = "hello there";
+ let value = "value goes here";
+
+ assert!(a.is_empty());
+ match a.entry(key.clone()) {
+ Occupied(_) => panic!(),
+ Vacant(e) => {
+ assert_eq!(key, *e.key());
+ e.insert(value.clone());
+ }
+ }
+ assert_eq!(a.len(), 1);
+ assert_eq!(a[key], value);
+}
+
+#[test]
+fn test_retain() {
+ 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);
+ assert_eq!(map[&2], 20);
+ assert_eq!(map[&4], 40);
+ assert_eq!(map[&6], 60);
+}
+
+#[test]
+fn test_try_reserve() {
+ let mut empty_bytes: HashMap<u8, u8> = HashMap::new();
+
+ const MAX_USIZE: usize = usize::MAX;
+
+ if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) {
+ } else {
+ panic!("usize::MAX should trigger an overflow!");
+ }
+
+ if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE / 8) {
+ } else {
+ panic!("usize::MAX / 8 should trigger an OOM!")
+ }
+}
+
+#[test]
+fn test_raw_entry() {
+ use super::RawEntryMut::{Occupied, Vacant};
+
+ let xs = [(1i32, 10i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
+
+ let mut map: HashMap<_, _> = xs.iter().cloned().collect();
+
+ let compute_hash = |map: &HashMap<i32, i32>, k: i32| -> u64 {
+ use core::hash::{BuildHasher, Hash, Hasher};
+
+ let mut hasher = map.hasher().build_hasher();
+ k.hash(&mut hasher);
+ hasher.finish()
+ };
+
+ // Existing key (insert)
+ match map.raw_entry_mut().from_key(&1) {
+ Vacant(_) => unreachable!(),
+ Occupied(mut view) => {
+ assert_eq!(view.get(), &10);
+ assert_eq!(view.insert(100), 10);
+ }
+ }
+ 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.len(), 6);
+
+ // Existing key (update)
+ match map.raw_entry_mut().from_key(&2) {
+ Vacant(_) => unreachable!(),
+ Occupied(mut view) => {
+ let v = view.get_mut();
+ let new_v = (*v) * 10;
+ *v = new_v;
+ }
+ }
+ 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.len(), 6);
+
+ // Existing key (take)
+ let hash3 = compute_hash(&map, 3);
+ match map.raw_entry_mut().from_key_hashed_nocheck(hash3, &3) {
+ Vacant(_) => unreachable!(),
+ Occupied(view) => {
+ assert_eq!(view.remove_entry(), (3, 30));
+ }
+ }
+ 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.len(), 5);
+
+ // Nonexistent key (insert)
+ match map.raw_entry_mut().from_key(&10) {
+ Occupied(_) => unreachable!(),
+ Vacant(view) => {
+ assert_eq!(view.insert(10, 1000), (&mut 10, &mut 1000));
+ }
+ }
+ assert_eq!(map.raw_entry().from_key(&10).unwrap(), (&10, &1000));
+ assert_eq!(map.len(), 6);
+
+ // Ensure all lookup methods produce equivalent results.
+ for k in 0..12 {
+ let hash = compute_hash(&map, k);
+ let v = map.get(&k).cloned();
+ let kv = v.as_ref().map(|v| (&k, v));
+
+ 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);
+
+ match map.raw_entry_mut().from_key(&k) {
+ Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv),
+ Vacant(_) => assert_eq!(v, None),
+ }
+ match map.raw_entry_mut().from_key_hashed_nocheck(hash, &k) {
+ Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv),
+ Vacant(_) => assert_eq!(v, None),
+ }
+ match map.raw_entry_mut().from_hash(hash, |q| *q == k) {
+ Occupied(mut o) => assert_eq!(Some(o.get_key_value()), kv),
+ Vacant(_) => assert_eq!(v, None),
+ }
+ }
+}
+
+mod test_drain_filter {
+ use super::*;
+
+ use crate::panic::{catch_unwind, AssertUnwindSafe};
+ use crate::sync::atomic::{AtomicUsize, Ordering};
+
+ trait EqSorted: Iterator {
+ fn eq_sorted<I: IntoIterator<Item = Self::Item>>(self, other: I) -> bool;
+ }
+
+ impl<T: Iterator> EqSorted for T
+ where
+ T::Item: Eq + Ord,
+ {
+ fn eq_sorted<I: IntoIterator<Item = Self::Item>>(self, other: I) -> bool {
+ let mut v: Vec<_> = self.collect();
+ v.sort_unstable();
+ v.into_iter().eq(other)
+ }
+ }
+
+ #[test]
+ fn empty() {
+ let mut map: HashMap<i32, i32> = HashMap::new();
+ map.drain_filter(|_, _| unreachable!("there's nothing to decide on"));
+ assert!(map.is_empty());
+ }
+
+ #[test]
+ fn consuming_nothing() {
+ let pairs = (0..3).map(|i| (i, i));
+ let mut map: HashMap<_, _> = pairs.collect();
+ assert!(map.drain_filter(|_, _| false).eq_sorted(crate::iter::empty()));
+ assert_eq!(map.len(), 3);
+ }
+
+ #[test]
+ fn consuming_all() {
+ let pairs = (0..3).map(|i| (i, i));
+ let mut map: HashMap<_, _> = pairs.clone().collect();
+ assert!(map.drain_filter(|_, _| true).eq_sorted(pairs));
+ assert!(map.is_empty());
+ }
+
+ #[test]
+ fn mutating_and_keeping() {
+ let pairs = (0..3).map(|i| (i, i));
+ let mut map: HashMap<_, _> = pairs.collect();
+ assert!(
+ map.drain_filter(|_, v| {
+ *v += 6;
+ false
+ })
+ .eq_sorted(crate::iter::empty())
+ );
+ assert!(map.keys().copied().eq_sorted(0..3));
+ assert!(map.values().copied().eq_sorted(6..9));
+ }
+
+ #[test]
+ fn mutating_and_removing() {
+ let pairs = (0..3).map(|i| (i, i));
+ let mut map: HashMap<_, _> = pairs.collect();
+ assert!(
+ map.drain_filter(|_, v| {
+ *v += 6;
+ true
+ })
+ .eq_sorted((0..3).map(|i| (i, i + 6)))
+ );
+ assert!(map.is_empty());
+ }
+
+ #[test]
+ fn drop_panic_leak() {
+ static PREDS: AtomicUsize = AtomicUsize::new(0);
+ static DROPS: AtomicUsize = AtomicUsize::new(0);
+
+ struct D;
+ impl Drop for D {
+ fn drop(&mut self) {
+ if DROPS.fetch_add(1, Ordering::SeqCst) == 1 {
+ panic!("panic in `drop`");
+ }
+ }
+ }
+
+ let mut map = (0..3).map(|i| (i, D)).collect::<HashMap<_, _>>();
+
+ catch_unwind(move || {
+ drop(map.drain_filter(|_, _| {
+ PREDS.fetch_add(1, Ordering::SeqCst);
+ true
+ }))
+ })
+ .unwrap_err();
+
+ assert_eq!(PREDS.load(Ordering::SeqCst), 3);
+ assert_eq!(DROPS.load(Ordering::SeqCst), 3);
+ }
+
+ #[test]
+ fn pred_panic_leak() {
+ static PREDS: AtomicUsize = AtomicUsize::new(0);
+ static DROPS: AtomicUsize = AtomicUsize::new(0);
+
+ struct D;
+ impl Drop for D {
+ fn drop(&mut self) {
+ DROPS.fetch_add(1, Ordering::SeqCst);
+ }
+ }
+
+ let mut map = (0..3).map(|i| (i, D)).collect::<HashMap<_, _>>();
+
+ catch_unwind(AssertUnwindSafe(|| {
+ drop(map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) {
+ 0 => true,
+ _ => panic!(),
+ }))
+ }))
+ .unwrap_err();
+
+ assert_eq!(PREDS.load(Ordering::SeqCst), 2);
+ assert_eq!(DROPS.load(Ordering::SeqCst), 1);
+ assert_eq!(map.len(), 2);
+ }
+
+ // Same as above, but attempt to use the iterator again after the panic in the predicate
+ #[test]
+ fn pred_panic_reuse() {
+ static PREDS: AtomicUsize = AtomicUsize::new(0);
+ static DROPS: AtomicUsize = AtomicUsize::new(0);
+
+ struct D;
+ impl Drop for D {
+ fn drop(&mut self) {
+ DROPS.fetch_add(1, Ordering::SeqCst);
+ }
+ }
+
+ let mut map = (0..3).map(|i| (i, D)).collect::<HashMap<_, _>>();
+
+ {
+ let mut it = map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) {
+ 0 => true,
+ _ => panic!(),
+ });
+ catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
+ // Iterator behaviour after a panic is explicitly unspecified,
+ // so this is just the current implementation:
+ let result = catch_unwind(AssertUnwindSafe(|| it.next()));
+ assert!(result.is_err());
+ }
+
+ assert_eq!(PREDS.load(Ordering::SeqCst), 3);
+ assert_eq!(DROPS.load(Ordering::SeqCst), 1);
+ assert_eq!(map.len(), 2);
+ }
+}
diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs
index 10bf917..a0c3985 100644
--- a/library/std/src/collections/hash/set.rs
+++ b/library/std/src/collections/hash/set.rs
@@ -1,3 +1,8 @@
+#[cfg(test)]
+mod tests;
+
+use hashbrown::hash_set as base;
+
use crate::borrow::Borrow;
use crate::collections::TryReserveError;
use crate::fmt;
@@ -5,7 +10,7 @@
use crate::iter::{Chain, FromIterator, FusedIterator};
use crate::ops::{BitAnd, BitOr, BitXor, Sub};
-use super::map::{self, HashMap, Keys, RandomState};
+use super::map::{map_try_reserve_error, RandomState};
// Future Optimization (FIXME!)
// ============================
@@ -98,13 +103,14 @@
/// // use the values stored in the set
/// ```
///
+/// [`HashMap`]: crate::collections::HashMap
/// [`RefCell`]: crate::cell::RefCell
/// [`Cell`]: crate::cell::Cell
#[derive(Clone)]
#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_type")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct HashSet<T, S = RandomState> {
- map: HashMap<T, (), S>,
+ base: base::HashSet<T, S>,
}
impl<T> HashSet<T, RandomState> {
@@ -122,7 +128,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new() -> HashSet<T, RandomState> {
- HashSet { map: HashMap::new() }
+ Default::default()
}
/// Creates an empty `HashSet` with the specified capacity.
@@ -140,7 +146,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(capacity: usize) -> HashSet<T, RandomState> {
- HashSet { map: HashMap::with_capacity(capacity) }
+ HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, Default::default()) }
}
}
@@ -157,7 +163,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn capacity(&self) -> usize {
- self.map.capacity()
+ self.base.capacity()
}
/// An iterator visiting all elements in arbitrary order.
@@ -179,7 +185,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn iter(&self) -> Iter<'_, T> {
- Iter { iter: self.map.keys() }
+ Iter { base: self.base.iter() }
}
/// Returns the number of elements in the set.
@@ -197,7 +203,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn len(&self) -> usize {
- self.map.len()
+ self.base.len()
}
/// Returns `true` if the set contains no elements.
@@ -215,7 +221,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn is_empty(&self) -> bool {
- self.map.is_empty()
+ self.base.is_empty()
}
/// Clears the set, returning all elements in an iterator.
@@ -238,7 +244,48 @@
#[inline]
#[stable(feature = "drain", since = "1.6.0")]
pub fn drain(&mut self) -> Drain<'_, T> {
- Drain { iter: self.map.drain() }
+ Drain { base: self.base.drain() }
+ }
+
+ /// Creates an iterator which uses a closure to determine if a value should be removed.
+ ///
+ /// If the closure returns true, then the value is removed and yielded.
+ /// If the closure returns false, the value will remain in the list and will not be yielded
+ /// by the iterator.
+ ///
+ /// If the iterator is only partially consumed or not consumed at all, each of the remaining
+ /// values will still be subjected to the closure and removed and dropped if it returns true.
+ ///
+ /// It is unspecified how many more values will be subjected to the closure
+ /// if a panic occurs in the closure, or if a panic occurs while dropping a value, or if the
+ /// `DrainFilter` itself is leaked.
+ ///
+ /// # Examples
+ ///
+ /// Splitting a set into even and odd values, reusing the original set:
+ ///
+ /// ```
+ /// #![feature(hash_drain_filter)]
+ /// use std::collections::HashSet;
+ ///
+ /// let mut set: HashSet<i32> = (0..8).collect();
+ /// let drained: HashSet<i32> = set.drain_filter(|v| v % 2 == 0).collect();
+ ///
+ /// let mut evens = drained.into_iter().collect::<Vec<_>>();
+ /// let mut odds = set.into_iter().collect::<Vec<_>>();
+ /// evens.sort();
+ /// odds.sort();
+ ///
+ /// assert_eq!(evens, vec![0, 2, 4, 6]);
+ /// assert_eq!(odds, vec![1, 3, 5, 7]);
+ /// ```
+ #[inline]
+ #[unstable(feature = "hash_drain_filter", issue = "59618")]
+ pub fn drain_filter<F>(&mut self, pred: F) -> DrainFilter<'_, T, F>
+ where
+ F: FnMut(&T) -> bool,
+ {
+ DrainFilter { base: self.base.drain_filter(pred) }
}
/// Clears the set, removing all values.
@@ -256,7 +303,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn clear(&mut self) {
- self.map.clear()
+ self.base.clear()
}
/// Creates a new empty hash set which will use the given hasher to hash
@@ -285,7 +332,7 @@
#[inline]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
pub fn with_hasher(hasher: S) -> HashSet<T, S> {
- HashSet { map: HashMap::with_hasher(hasher) }
+ HashSet { base: base::HashSet::with_hasher(hasher) }
}
/// Creates an empty `HashSet` with the specified capacity, using
@@ -315,7 +362,7 @@
#[inline]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet<T, S> {
- HashSet { map: HashMap::with_capacity_and_hasher(capacity, hasher) }
+ HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) }
}
/// Returns a reference to the set's [`BuildHasher`].
@@ -333,7 +380,7 @@
#[inline]
#[stable(feature = "hashmap_public_hasher", since = "1.9.0")]
pub fn hasher(&self) -> &S {
- self.map.hasher()
+ self.base.hasher()
}
}
@@ -361,7 +408,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn reserve(&mut self, additional: usize) {
- self.map.reserve(additional)
+ self.base.reserve(additional)
}
/// Tries to reserve capacity for at least `additional` more elements to be inserted
@@ -384,7 +431,7 @@
#[inline]
#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
- self.map.try_reserve(additional)
+ self.base.try_reserve(additional).map_err(map_try_reserve_error)
}
/// Shrinks the capacity of the set as much as possible. It will drop
@@ -406,7 +453,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn shrink_to_fit(&mut self) {
- self.map.shrink_to_fit()
+ self.base.shrink_to_fit()
}
/// Shrinks the capacity of the set with a lower limit. It will drop
@@ -434,7 +481,7 @@
#[inline]
#[unstable(feature = "shrink_to", reason = "new API", issue = "56431")]
pub fn shrink_to(&mut self, min_capacity: usize) {
- self.map.shrink_to(min_capacity)
+ self.base.shrink_to(min_capacity)
}
/// Visits the values representing the difference,
@@ -574,7 +621,7 @@
T: Borrow<Q>,
Q: Hash + Eq,
{
- self.map.contains_key(value)
+ self.base.contains(value)
}
/// Returns a reference to the value in the set, if any, that is equal to the given value.
@@ -599,7 +646,7 @@
T: Borrow<Q>,
Q: Hash + Eq,
{
- self.map.get_key_value(value).map(|(k, _)| k)
+ self.base.get(value)
}
/// Inserts the given `value` into the set if it is not present, then
@@ -623,7 +670,7 @@
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
+ self.base.get_or_insert(value)
}
/// Inserts an owned copy of the given `value` into the set if it is not
@@ -655,7 +702,7 @@
{
// 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(|| (value.to_owned(), ())).0
+ self.base.get_or_insert_owned(value)
}
/// Inserts a value computed from `f` into the set if the given `value` is
@@ -688,7 +735,7 @@
{
// 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
+ self.base.get_or_insert_with(value, f)
}
/// Returns `true` if `self` has no elements in common with `other`.
@@ -785,7 +832,7 @@
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn insert(&mut self, value: T) -> bool {
- self.map.insert(value, ()).is_none()
+ self.base.insert(value)
}
/// Adds a value to the set, replacing the existing value, if any, that is equal to the given
@@ -806,13 +853,7 @@
#[inline]
#[stable(feature = "set_recovery", since = "1.9.0")]
pub fn replace(&mut self, value: T) -> Option<T> {
- match self.map.entry(value) {
- map::Entry::Occupied(occupied) => Some(occupied.replace_key()),
- map::Entry::Vacant(vacant) => {
- vacant.insert(());
- None
- }
- }
+ self.base.replace(value)
}
/// Removes a value from the set. Returns whether the value was
@@ -840,7 +881,7 @@
T: Borrow<Q>,
Q: Hash + Eq,
{
- self.map.remove(value).is_some()
+ self.base.remove(value)
}
/// Removes and returns the value in the set, if any, that is equal to the given one.
@@ -865,7 +906,7 @@
T: Borrow<Q>,
Q: Hash + Eq,
{
- self.map.remove_entry(value).map(|(k, _)| k)
+ self.base.take(value)
}
/// Retains only the elements specified by the predicate.
@@ -883,11 +924,11 @@
/// assert_eq!(set.len(), 3);
/// ```
#[stable(feature = "retain_hash_collection", since = "1.18.0")]
- pub fn retain<F>(&mut self, mut f: F)
+ pub fn retain<F>(&mut self, f: F)
where
F: FnMut(&T) -> bool,
{
- self.map.retain(|k, _| f(k));
+ self.base.retain(f)
}
}
@@ -946,17 +987,17 @@
{
#[inline]
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
- self.map.extend(iter.into_iter().map(|k| (k, ())));
+ self.base.extend(iter);
}
#[inline]
fn extend_one(&mut self, item: T) {
- self.map.insert(item, ());
+ self.base.insert(item);
}
#[inline]
fn extend_reserve(&mut self, additional: usize) {
- self.map.extend_reserve(additional);
+ self.base.extend_reserve(additional);
}
}
@@ -973,7 +1014,7 @@
#[inline]
fn extend_one(&mut self, &item: &'a T) {
- self.map.insert(item, ());
+ self.base.insert(item);
}
#[inline]
@@ -990,7 +1031,7 @@
/// Creates an empty `HashSet<T, S>` with the `Default` value for the hasher.
#[inline]
fn default() -> HashSet<T, S> {
- HashSet { map: HashMap::default() }
+ HashSet { base: Default::default() }
}
}
@@ -1132,9 +1173,19 @@
/// See its documentation for more.
///
/// [`iter`]: HashSet::iter
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::HashSet;
+///
+/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
+///
+/// let mut iter = a.iter();
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Iter<'a, K: 'a> {
- iter: Keys<'a, K, ()>,
+ base: base::Iter<'a, K>,
}
/// An owning iterator over the items of a `HashSet`.
@@ -1143,9 +1194,19 @@
/// (provided by the `IntoIterator` trait). See its documentation for more.
///
/// [`into_iter`]: IntoIterator::into_iter
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::HashSet;
+///
+/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
+///
+/// let mut iter = a.into_iter();
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct IntoIter<K> {
- iter: map::IntoIter<K, ()>,
+ base: base::IntoIter<K>,
}
/// A draining iterator over the items of a `HashSet`.
@@ -1154,9 +1215,44 @@
/// See its documentation for more.
///
/// [`drain`]: HashSet::drain
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::HashSet;
+///
+/// let mut a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
+///
+/// let mut drain = a.drain();
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Drain<'a, K: 'a> {
- iter: map::Drain<'a, K, ()>,
+ base: base::Drain<'a, K>,
+}
+
+/// A draining, filtering iterator over the items of a `HashSet`.
+///
+/// This `struct` is created by the [`drain_filter`] method on [`HashSet`].
+///
+/// [`drain_filter`]: HashSet::drain_filter
+///
+/// # Examples
+///
+/// ```
+/// #![feature(hash_drain_filter)]
+///
+/// use std::collections::HashSet;
+///
+/// let mut a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
+///
+/// let mut drain_filtered = a.drain_filter(|v| v % 2 == 0);
+/// ```
+#[unstable(feature = "hash_drain_filter", issue = "59618")]
+pub struct DrainFilter<'a, K, F>
+where
+ F: FnMut(&K) -> bool,
+{
+ base: base::DrainFilter<'a, K, F>,
}
/// A lazy iterator producing elements in the intersection of `HashSet`s.
@@ -1165,6 +1261,17 @@
/// See its documentation for more.
///
/// [`intersection`]: HashSet::intersection
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::HashSet;
+///
+/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
+/// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect();
+///
+/// let mut intersection = a.intersection(&b);
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Intersection<'a, T: 'a, S: 'a> {
// iterator of the first set
@@ -1179,6 +1286,17 @@
/// See its documentation for more.
///
/// [`difference`]: HashSet::difference
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::HashSet;
+///
+/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
+/// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect();
+///
+/// let mut difference = a.difference(&b);
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Difference<'a, T: 'a, S: 'a> {
// iterator of the first set
@@ -1193,6 +1311,17 @@
/// [`HashSet`]. See its documentation for more.
///
/// [`symmetric_difference`]: HashSet::symmetric_difference
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::HashSet;
+///
+/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
+/// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect();
+///
+/// let mut intersection = a.symmetric_difference(&b);
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct SymmetricDifference<'a, T: 'a, S: 'a> {
iter: Chain<Difference<'a, T, S>, Difference<'a, T, S>>,
@@ -1204,6 +1333,17 @@
/// See its documentation for more.
///
/// [`union`]: HashSet::union
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::HashSet;
+///
+/// let a: HashSet<u32> = vec![1, 2, 3].into_iter().collect();
+/// let b: HashSet<_> = [4, 2, 3, 4].iter().cloned().collect();
+///
+/// let mut union_iter = a.union(&b);
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Union<'a, T: 'a, S: 'a> {
iter: Chain<Iter<'a, T>, Difference<'a, T, S>>,
@@ -1247,7 +1387,7 @@
/// ```
#[inline]
fn into_iter(self) -> IntoIter<T> {
- IntoIter { iter: self.map.into_iter() }
+ IntoIter { base: self.base.into_iter() }
}
}
@@ -1255,7 +1395,7 @@
impl<K> Clone for Iter<'_, K> {
#[inline]
fn clone(&self) -> Self {
- Iter { iter: self.iter.clone() }
+ Iter { base: self.base.clone() }
}
}
#[stable(feature = "rust1", since = "1.0.0")]
@@ -1264,18 +1404,18 @@
#[inline]
fn next(&mut self) -> Option<&'a K> {
- self.iter.next()
+ self.base.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
+ self.base.size_hint()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<K> ExactSizeIterator for Iter<'_, K> {
#[inline]
fn len(&self) -> usize {
- self.iter.len()
+ self.base.len()
}
}
#[stable(feature = "fused", since = "1.26.0")]
@@ -1294,18 +1434,18 @@
#[inline]
fn next(&mut self) -> Option<K> {
- self.iter.next().map(|(k, _)| k)
+ self.base.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
+ self.base.size_hint()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<K> ExactSizeIterator for IntoIter<K> {
#[inline]
fn len(&self) -> usize {
- self.iter.len()
+ self.base.len()
}
}
#[stable(feature = "fused", since = "1.26.0")]
@@ -1314,8 +1454,7 @@
#[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 {
- let entries_iter = self.iter.iter().map(|(k, _)| k);
- f.debug_list().entries(entries_iter).finish()
+ fmt::Debug::fmt(&self.base, f)
}
}
@@ -1325,18 +1464,18 @@
#[inline]
fn next(&mut self) -> Option<K> {
- self.iter.next().map(|(k, _)| k)
+ self.base.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
+ self.base.size_hint()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<K> ExactSizeIterator for Drain<'_, K> {
#[inline]
fn len(&self) -> usize {
- self.iter.len()
+ self.base.len()
}
}
#[stable(feature = "fused", since = "1.26.0")]
@@ -1345,8 +1484,37 @@
#[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 {
- let entries_iter = self.iter.iter().map(|(k, _)| k);
- f.debug_list().entries(entries_iter).finish()
+ fmt::Debug::fmt(&self.base, f)
+ }
+}
+
+#[unstable(feature = "hash_drain_filter", issue = "59618")]
+impl<K, F> Iterator for DrainFilter<'_, K, F>
+where
+ F: FnMut(&K) -> bool,
+{
+ type Item = K;
+
+ #[inline]
+ fn next(&mut self) -> Option<K> {
+ self.base.next()
+ }
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.base.size_hint()
+ }
+}
+
+#[unstable(feature = "hash_drain_filter", issue = "59618")]
+impl<K, F> FusedIterator for DrainFilter<'_, K, F> where F: FnMut(&K) -> bool {}
+
+#[unstable(feature = "hash_drain_filter", issue = "59618")]
+impl<'a, K, F> fmt::Debug for DrainFilter<'a, K, F>
+where
+ F: FnMut(&K) -> bool,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad("DrainFilter { .. }")
}
}
@@ -1579,422 +1747,3 @@
d
}
}
-
-#[cfg(test)]
-mod test_set {
- use super::super::map::RandomState;
- use super::HashSet;
-
- #[test]
- fn test_zero_capacities() {
- type HS = HashSet<i32>;
-
- let s = HS::new();
- assert_eq!(s.capacity(), 0);
-
- let s = HS::default();
- assert_eq!(s.capacity(), 0);
-
- let s = HS::with_hasher(RandomState::new());
- assert_eq!(s.capacity(), 0);
-
- let s = HS::with_capacity(0);
- assert_eq!(s.capacity(), 0);
-
- let s = HS::with_capacity_and_hasher(0, RandomState::new());
- assert_eq!(s.capacity(), 0);
-
- let mut s = HS::new();
- s.insert(1);
- s.insert(2);
- s.remove(&1);
- s.remove(&2);
- s.shrink_to_fit();
- assert_eq!(s.capacity(), 0);
-
- let mut s = HS::new();
- s.reserve(0);
- assert_eq!(s.capacity(), 0);
- }
-
- #[test]
- fn test_disjoint() {
- let mut xs = HashSet::new();
- let mut ys = HashSet::new();
- assert!(xs.is_disjoint(&ys));
- assert!(ys.is_disjoint(&xs));
- assert!(xs.insert(5));
- assert!(ys.insert(11));
- assert!(xs.is_disjoint(&ys));
- assert!(ys.is_disjoint(&xs));
- assert!(xs.insert(7));
- assert!(xs.insert(19));
- assert!(xs.insert(4));
- assert!(ys.insert(2));
- assert!(ys.insert(-11));
- assert!(xs.is_disjoint(&ys));
- assert!(ys.is_disjoint(&xs));
- assert!(ys.insert(7));
- assert!(!xs.is_disjoint(&ys));
- assert!(!ys.is_disjoint(&xs));
- }
-
- #[test]
- fn test_subset_and_superset() {
- let mut a = HashSet::new();
- assert!(a.insert(0));
- assert!(a.insert(5));
- assert!(a.insert(11));
- assert!(a.insert(7));
-
- let mut b = HashSet::new();
- assert!(b.insert(0));
- assert!(b.insert(7));
- assert!(b.insert(19));
- assert!(b.insert(250));
- assert!(b.insert(11));
- assert!(b.insert(200));
-
- assert!(!a.is_subset(&b));
- assert!(!a.is_superset(&b));
- assert!(!b.is_subset(&a));
- assert!(!b.is_superset(&a));
-
- assert!(b.insert(5));
-
- assert!(a.is_subset(&b));
- assert!(!a.is_superset(&b));
- assert!(!b.is_subset(&a));
- assert!(b.is_superset(&a));
- }
-
- #[test]
- fn test_iterate() {
- let mut a = HashSet::new();
- for i in 0..32 {
- assert!(a.insert(i));
- }
- let mut observed: u32 = 0;
- for k in &a {
- observed |= 1 << *k;
- }
- assert_eq!(observed, 0xFFFF_FFFF);
- }
-
- #[test]
- fn test_intersection() {
- let mut a = HashSet::new();
- let mut b = HashSet::new();
- assert!(a.intersection(&b).next().is_none());
-
- assert!(a.insert(11));
- assert!(a.insert(1));
- assert!(a.insert(3));
- assert!(a.insert(77));
- assert!(a.insert(103));
- assert!(a.insert(5));
- assert!(a.insert(-5));
-
- assert!(b.insert(2));
- assert!(b.insert(11));
- assert!(b.insert(77));
- assert!(b.insert(-9));
- assert!(b.insert(-42));
- assert!(b.insert(5));
- assert!(b.insert(3));
-
- let mut i = 0;
- let expected = [3, 5, 11, 77];
- for x in a.intersection(&b) {
- assert!(expected.contains(x));
- i += 1
- }
- assert_eq!(i, expected.len());
-
- assert!(a.insert(9)); // make a bigger than b
-
- i = 0;
- for x in a.intersection(&b) {
- assert!(expected.contains(x));
- i += 1
- }
- assert_eq!(i, expected.len());
-
- i = 0;
- for x in b.intersection(&a) {
- assert!(expected.contains(x));
- i += 1
- }
- assert_eq!(i, expected.len());
- }
-
- #[test]
- fn test_difference() {
- let mut a = HashSet::new();
- let mut b = HashSet::new();
-
- assert!(a.insert(1));
- assert!(a.insert(3));
- assert!(a.insert(5));
- assert!(a.insert(9));
- assert!(a.insert(11));
-
- assert!(b.insert(3));
- assert!(b.insert(9));
-
- let mut i = 0;
- let expected = [1, 5, 11];
- for x in a.difference(&b) {
- assert!(expected.contains(x));
- i += 1
- }
- assert_eq!(i, expected.len());
- }
-
- #[test]
- fn test_symmetric_difference() {
- let mut a = HashSet::new();
- let mut b = HashSet::new();
-
- assert!(a.insert(1));
- assert!(a.insert(3));
- assert!(a.insert(5));
- assert!(a.insert(9));
- assert!(a.insert(11));
-
- assert!(b.insert(-2));
- assert!(b.insert(3));
- assert!(b.insert(9));
- assert!(b.insert(14));
- assert!(b.insert(22));
-
- let mut i = 0;
- let expected = [-2, 1, 5, 11, 14, 22];
- for x in a.symmetric_difference(&b) {
- assert!(expected.contains(x));
- i += 1
- }
- assert_eq!(i, expected.len());
- }
-
- #[test]
- fn test_union() {
- let mut a = HashSet::new();
- let mut b = HashSet::new();
- assert!(a.union(&b).next().is_none());
- assert!(b.union(&a).next().is_none());
-
- assert!(a.insert(1));
- assert!(a.insert(3));
- assert!(a.insert(11));
- assert!(a.insert(16));
- assert!(a.insert(19));
- assert!(a.insert(24));
-
- assert!(b.insert(-2));
- assert!(b.insert(1));
- assert!(b.insert(5));
- assert!(b.insert(9));
- assert!(b.insert(13));
- assert!(b.insert(19));
-
- let mut i = 0;
- let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24];
- for x in a.union(&b) {
- assert!(expected.contains(x));
- i += 1
- }
- assert_eq!(i, expected.len());
-
- assert!(a.insert(9)); // make a bigger than b
- assert!(a.insert(5));
-
- i = 0;
- for x in a.union(&b) {
- assert!(expected.contains(x));
- i += 1
- }
- assert_eq!(i, expected.len());
-
- i = 0;
- for x in b.union(&a) {
- assert!(expected.contains(x));
- i += 1
- }
- assert_eq!(i, expected.len());
- }
-
- #[test]
- fn test_from_iter() {
- 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]
- fn test_move_iter() {
- let hs = {
- let mut hs = HashSet::new();
-
- hs.insert('a');
- hs.insert('b');
-
- hs
- };
-
- let v = hs.into_iter().collect::<Vec<char>>();
- assert!(v == ['a', 'b'] || v == ['b', 'a']);
- }
-
- #[test]
- fn test_eq() {
- // These constants once happened to expose a bug in insert().
- // I'm keeping them around to prevent a regression.
- let mut s1 = HashSet::new();
-
- s1.insert(1);
- s1.insert(2);
- s1.insert(3);
-
- let mut s2 = HashSet::new();
-
- s2.insert(1);
- s2.insert(2);
-
- assert!(s1 != s2);
-
- s2.insert(3);
-
- assert_eq!(s1, s2);
- }
-
- #[test]
- fn test_show() {
- let mut set = HashSet::new();
- let empty = HashSet::<i32>::new();
-
- set.insert(1);
- set.insert(2);
-
- let set_str = format!("{:?}", set);
-
- assert!(set_str == "{1, 2}" || set_str == "{2, 1}");
- assert_eq!(format!("{:?}", empty), "{}");
- }
-
- #[test]
- fn test_trivial_drain() {
- let mut s = HashSet::<i32>::new();
- for _ in s.drain() {}
- assert!(s.is_empty());
- drop(s);
-
- let mut s = HashSet::<i32>::new();
- drop(s.drain());
- assert!(s.is_empty());
- }
-
- #[test]
- fn test_drain() {
- let mut s: HashSet<_> = (1..100).collect();
-
- // try this a bunch of times to make sure we don't screw up internal state.
- for _ in 0..20 {
- assert_eq!(s.len(), 99);
-
- {
- let mut last_i = 0;
- let mut d = s.drain();
- for (i, x) in d.by_ref().take(50).enumerate() {
- last_i = i;
- assert!(x != 0);
- }
- assert_eq!(last_i, 49);
- }
-
- for _ in &s {
- panic!("s should be empty!");
- }
-
- // reset to try again.
- s.extend(1..100);
- }
- }
-
- #[test]
- fn test_replace() {
- use crate::hash;
-
- #[derive(Debug)]
- struct Foo(&'static str, i32);
-
- impl PartialEq for Foo {
- fn eq(&self, other: &Self) -> bool {
- self.0 == other.0
- }
- }
-
- impl Eq for Foo {}
-
- impl hash::Hash for Foo {
- fn hash<H: hash::Hasher>(&self, h: &mut H) {
- self.0.hash(h);
- }
- }
-
- let mut s = HashSet::new();
- assert_eq!(s.replace(Foo("a", 1)), None);
- assert_eq!(s.len(), 1);
- assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1)));
- assert_eq!(s.len(), 1);
-
- let mut it = s.iter();
- assert_eq!(it.next(), Some(&Foo("a", 2)));
- assert_eq!(it.next(), None);
- }
-
- #[test]
- fn test_extend_ref() {
- let mut a = HashSet::new();
- a.insert(1);
-
- a.extend(&[2, 3, 4]);
-
- assert_eq!(a.len(), 4);
- assert!(a.contains(&1));
- assert!(a.contains(&2));
- assert!(a.contains(&3));
- assert!(a.contains(&4));
-
- let mut b = HashSet::new();
- b.insert(5);
- b.insert(6);
-
- a.extend(&b);
-
- assert_eq!(a.len(), 6);
- assert!(a.contains(&1));
- assert!(a.contains(&2));
- assert!(a.contains(&3));
- assert!(a.contains(&4));
- assert!(a.contains(&5));
- assert!(a.contains(&6));
- }
-
- #[test]
- fn test_retain() {
- let xs = [1, 2, 3, 4, 5, 6];
- let mut set: HashSet<i32> = xs.iter().cloned().collect();
- set.retain(|&k| k % 2 == 0);
- assert_eq!(set.len(), 3);
- assert!(set.contains(&2));
- assert!(set.contains(&4));
- assert!(set.contains(&6));
- }
-}
diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs
new file mode 100644
index 0000000..40f8467
--- /dev/null
+++ b/library/std/src/collections/hash/set/tests.rs
@@ -0,0 +1,486 @@
+use super::super::map::RandomState;
+use super::HashSet;
+
+use crate::panic::{catch_unwind, AssertUnwindSafe};
+use crate::sync::atomic::{AtomicU32, Ordering};
+
+#[test]
+fn test_zero_capacities() {
+ type HS = HashSet<i32>;
+
+ let s = HS::new();
+ assert_eq!(s.capacity(), 0);
+
+ let s = HS::default();
+ assert_eq!(s.capacity(), 0);
+
+ let s = HS::with_hasher(RandomState::new());
+ assert_eq!(s.capacity(), 0);
+
+ let s = HS::with_capacity(0);
+ assert_eq!(s.capacity(), 0);
+
+ let s = HS::with_capacity_and_hasher(0, RandomState::new());
+ assert_eq!(s.capacity(), 0);
+
+ let mut s = HS::new();
+ s.insert(1);
+ s.insert(2);
+ s.remove(&1);
+ s.remove(&2);
+ s.shrink_to_fit();
+ assert_eq!(s.capacity(), 0);
+
+ let mut s = HS::new();
+ s.reserve(0);
+ assert_eq!(s.capacity(), 0);
+}
+
+#[test]
+fn test_disjoint() {
+ let mut xs = HashSet::new();
+ let mut ys = HashSet::new();
+ assert!(xs.is_disjoint(&ys));
+ assert!(ys.is_disjoint(&xs));
+ assert!(xs.insert(5));
+ assert!(ys.insert(11));
+ assert!(xs.is_disjoint(&ys));
+ assert!(ys.is_disjoint(&xs));
+ assert!(xs.insert(7));
+ assert!(xs.insert(19));
+ assert!(xs.insert(4));
+ assert!(ys.insert(2));
+ assert!(ys.insert(-11));
+ assert!(xs.is_disjoint(&ys));
+ assert!(ys.is_disjoint(&xs));
+ assert!(ys.insert(7));
+ assert!(!xs.is_disjoint(&ys));
+ assert!(!ys.is_disjoint(&xs));
+}
+
+#[test]
+fn test_subset_and_superset() {
+ let mut a = HashSet::new();
+ assert!(a.insert(0));
+ assert!(a.insert(5));
+ assert!(a.insert(11));
+ assert!(a.insert(7));
+
+ let mut b = HashSet::new();
+ assert!(b.insert(0));
+ assert!(b.insert(7));
+ assert!(b.insert(19));
+ assert!(b.insert(250));
+ assert!(b.insert(11));
+ assert!(b.insert(200));
+
+ assert!(!a.is_subset(&b));
+ assert!(!a.is_superset(&b));
+ assert!(!b.is_subset(&a));
+ assert!(!b.is_superset(&a));
+
+ assert!(b.insert(5));
+
+ assert!(a.is_subset(&b));
+ assert!(!a.is_superset(&b));
+ assert!(!b.is_subset(&a));
+ assert!(b.is_superset(&a));
+}
+
+#[test]
+fn test_iterate() {
+ let mut a = HashSet::new();
+ for i in 0..32 {
+ assert!(a.insert(i));
+ }
+ let mut observed: u32 = 0;
+ for k in &a {
+ observed |= 1 << *k;
+ }
+ assert_eq!(observed, 0xFFFF_FFFF);
+}
+
+#[test]
+fn test_intersection() {
+ let mut a = HashSet::new();
+ let mut b = HashSet::new();
+ assert!(a.intersection(&b).next().is_none());
+
+ assert!(a.insert(11));
+ assert!(a.insert(1));
+ assert!(a.insert(3));
+ assert!(a.insert(77));
+ assert!(a.insert(103));
+ assert!(a.insert(5));
+ assert!(a.insert(-5));
+
+ assert!(b.insert(2));
+ assert!(b.insert(11));
+ assert!(b.insert(77));
+ assert!(b.insert(-9));
+ assert!(b.insert(-42));
+ assert!(b.insert(5));
+ assert!(b.insert(3));
+
+ let mut i = 0;
+ let expected = [3, 5, 11, 77];
+ for x in a.intersection(&b) {
+ assert!(expected.contains(x));
+ i += 1
+ }
+ assert_eq!(i, expected.len());
+
+ assert!(a.insert(9)); // make a bigger than b
+
+ i = 0;
+ for x in a.intersection(&b) {
+ assert!(expected.contains(x));
+ i += 1
+ }
+ assert_eq!(i, expected.len());
+
+ i = 0;
+ for x in b.intersection(&a) {
+ assert!(expected.contains(x));
+ i += 1
+ }
+ assert_eq!(i, expected.len());
+}
+
+#[test]
+fn test_difference() {
+ let mut a = HashSet::new();
+ let mut b = HashSet::new();
+
+ assert!(a.insert(1));
+ assert!(a.insert(3));
+ assert!(a.insert(5));
+ assert!(a.insert(9));
+ assert!(a.insert(11));
+
+ assert!(b.insert(3));
+ assert!(b.insert(9));
+
+ let mut i = 0;
+ let expected = [1, 5, 11];
+ for x in a.difference(&b) {
+ assert!(expected.contains(x));
+ i += 1
+ }
+ assert_eq!(i, expected.len());
+}
+
+#[test]
+fn test_symmetric_difference() {
+ let mut a = HashSet::new();
+ let mut b = HashSet::new();
+
+ assert!(a.insert(1));
+ assert!(a.insert(3));
+ assert!(a.insert(5));
+ assert!(a.insert(9));
+ assert!(a.insert(11));
+
+ assert!(b.insert(-2));
+ assert!(b.insert(3));
+ assert!(b.insert(9));
+ assert!(b.insert(14));
+ assert!(b.insert(22));
+
+ let mut i = 0;
+ let expected = [-2, 1, 5, 11, 14, 22];
+ for x in a.symmetric_difference(&b) {
+ assert!(expected.contains(x));
+ i += 1
+ }
+ assert_eq!(i, expected.len());
+}
+
+#[test]
+fn test_union() {
+ let mut a = HashSet::new();
+ let mut b = HashSet::new();
+ assert!(a.union(&b).next().is_none());
+ assert!(b.union(&a).next().is_none());
+
+ assert!(a.insert(1));
+ assert!(a.insert(3));
+ assert!(a.insert(11));
+ assert!(a.insert(16));
+ assert!(a.insert(19));
+ assert!(a.insert(24));
+
+ assert!(b.insert(-2));
+ assert!(b.insert(1));
+ assert!(b.insert(5));
+ assert!(b.insert(9));
+ assert!(b.insert(13));
+ assert!(b.insert(19));
+
+ let mut i = 0;
+ let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24];
+ for x in a.union(&b) {
+ assert!(expected.contains(x));
+ i += 1
+ }
+ assert_eq!(i, expected.len());
+
+ assert!(a.insert(9)); // make a bigger than b
+ assert!(a.insert(5));
+
+ i = 0;
+ for x in a.union(&b) {
+ assert!(expected.contains(x));
+ i += 1
+ }
+ assert_eq!(i, expected.len());
+
+ i = 0;
+ for x in b.union(&a) {
+ assert!(expected.contains(x));
+ i += 1
+ }
+ assert_eq!(i, expected.len());
+}
+
+#[test]
+fn test_from_iter() {
+ 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]
+fn test_move_iter() {
+ let hs = {
+ let mut hs = HashSet::new();
+
+ hs.insert('a');
+ hs.insert('b');
+
+ hs
+ };
+
+ let v = hs.into_iter().collect::<Vec<char>>();
+ assert!(v == ['a', 'b'] || v == ['b', 'a']);
+}
+
+#[test]
+fn test_eq() {
+ // These constants once happened to expose a bug in insert().
+ // I'm keeping them around to prevent a regression.
+ let mut s1 = HashSet::new();
+
+ s1.insert(1);
+ s1.insert(2);
+ s1.insert(3);
+
+ let mut s2 = HashSet::new();
+
+ s2.insert(1);
+ s2.insert(2);
+
+ assert!(s1 != s2);
+
+ s2.insert(3);
+
+ assert_eq!(s1, s2);
+}
+
+#[test]
+fn test_show() {
+ let mut set = HashSet::new();
+ let empty = HashSet::<i32>::new();
+
+ set.insert(1);
+ set.insert(2);
+
+ let set_str = format!("{:?}", set);
+
+ assert!(set_str == "{1, 2}" || set_str == "{2, 1}");
+ assert_eq!(format!("{:?}", empty), "{}");
+}
+
+#[test]
+fn test_trivial_drain() {
+ let mut s = HashSet::<i32>::new();
+ for _ in s.drain() {}
+ assert!(s.is_empty());
+ drop(s);
+
+ let mut s = HashSet::<i32>::new();
+ drop(s.drain());
+ assert!(s.is_empty());
+}
+
+#[test]
+fn test_drain() {
+ let mut s: HashSet<_> = (1..100).collect();
+
+ // try this a bunch of times to make sure we don't screw up internal state.
+ for _ in 0..20 {
+ assert_eq!(s.len(), 99);
+
+ {
+ let mut last_i = 0;
+ let mut d = s.drain();
+ for (i, x) in d.by_ref().take(50).enumerate() {
+ last_i = i;
+ assert!(x != 0);
+ }
+ assert_eq!(last_i, 49);
+ }
+
+ for _ in &s {
+ panic!("s should be empty!");
+ }
+
+ // reset to try again.
+ s.extend(1..100);
+ }
+}
+
+#[test]
+fn test_replace() {
+ use crate::hash;
+
+ #[derive(Debug)]
+ struct Foo(&'static str, i32);
+
+ impl PartialEq for Foo {
+ fn eq(&self, other: &Self) -> bool {
+ self.0 == other.0
+ }
+ }
+
+ impl Eq for Foo {}
+
+ impl hash::Hash for Foo {
+ fn hash<H: hash::Hasher>(&self, h: &mut H) {
+ self.0.hash(h);
+ }
+ }
+
+ let mut s = HashSet::new();
+ assert_eq!(s.replace(Foo("a", 1)), None);
+ assert_eq!(s.len(), 1);
+ assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1)));
+ assert_eq!(s.len(), 1);
+
+ let mut it = s.iter();
+ assert_eq!(it.next(), Some(&Foo("a", 2)));
+ assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_extend_ref() {
+ let mut a = HashSet::new();
+ a.insert(1);
+
+ a.extend(&[2, 3, 4]);
+
+ assert_eq!(a.len(), 4);
+ assert!(a.contains(&1));
+ assert!(a.contains(&2));
+ assert!(a.contains(&3));
+ assert!(a.contains(&4));
+
+ let mut b = HashSet::new();
+ b.insert(5);
+ b.insert(6);
+
+ a.extend(&b);
+
+ assert_eq!(a.len(), 6);
+ assert!(a.contains(&1));
+ assert!(a.contains(&2));
+ assert!(a.contains(&3));
+ assert!(a.contains(&4));
+ assert!(a.contains(&5));
+ assert!(a.contains(&6));
+}
+
+#[test]
+fn test_retain() {
+ let xs = [1, 2, 3, 4, 5, 6];
+ let mut set: HashSet<i32> = xs.iter().cloned().collect();
+ set.retain(|&k| k % 2 == 0);
+ assert_eq!(set.len(), 3);
+ assert!(set.contains(&2));
+ assert!(set.contains(&4));
+ assert!(set.contains(&6));
+}
+
+#[test]
+fn test_drain_filter() {
+ let mut x: HashSet<_> = [1].iter().copied().collect();
+ let mut y: HashSet<_> = [1].iter().copied().collect();
+
+ x.drain_filter(|_| true);
+ y.drain_filter(|_| false);
+ assert_eq!(x.len(), 0);
+ assert_eq!(y.len(), 1);
+}
+
+#[test]
+fn test_drain_filter_drop_panic_leak() {
+ static PREDS: AtomicU32 = AtomicU32::new(0);
+ static DROPS: AtomicU32 = AtomicU32::new(0);
+
+ #[derive(PartialEq, Eq, PartialOrd, Hash)]
+ struct D(i32);
+ impl Drop for D {
+ fn drop(&mut self) {
+ if DROPS.fetch_add(1, Ordering::SeqCst) == 1 {
+ panic!("panic in `drop`");
+ }
+ }
+ }
+
+ let mut set = (0..3).map(|i| D(i)).collect::<HashSet<_>>();
+
+ catch_unwind(move || {
+ drop(set.drain_filter(|_| {
+ PREDS.fetch_add(1, Ordering::SeqCst);
+ true
+ }))
+ })
+ .ok();
+
+ assert_eq!(PREDS.load(Ordering::SeqCst), 3);
+ assert_eq!(DROPS.load(Ordering::SeqCst), 3);
+}
+
+#[test]
+fn test_drain_filter_pred_panic_leak() {
+ static PREDS: AtomicU32 = AtomicU32::new(0);
+ static DROPS: AtomicU32 = AtomicU32::new(0);
+
+ #[derive(PartialEq, Eq, PartialOrd, Hash)]
+ struct D;
+ impl Drop for D {
+ fn drop(&mut self) {
+ DROPS.fetch_add(1, Ordering::SeqCst);
+ }
+ }
+
+ let mut set: HashSet<_> = (0..3).map(|_| D).collect();
+
+ catch_unwind(AssertUnwindSafe(|| {
+ drop(set.drain_filter(|_| match PREDS.fetch_add(1, Ordering::SeqCst) {
+ 0 => true,
+ _ => panic!(),
+ }))
+ }))
+ .ok();
+
+ assert_eq!(PREDS.load(Ordering::SeqCst), 1);
+ assert_eq!(DROPS.load(Ordering::SeqCst), 3);
+ assert_eq!(set.len(), 0);
+}
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 387c588..b0fceb9 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -10,6 +10,9 @@
#![stable(feature = "env", since = "1.0.0")]
+#[cfg(test)]
+mod tests;
+
use crate::error::Error;
use crate::ffi::{OsStr, OsString};
use crate::fmt;
@@ -65,10 +68,9 @@
/// An iterator over a snapshot of the environment variables of this process.
///
-/// This structure is created by the [`std::env::vars`] function. See its
-/// documentation for more.
+/// This structure is created by [`env::vars()`]. See its documentation for more.
///
-/// [`std::env::vars`]: vars
+/// [`env::vars()`]: vars
#[stable(feature = "env", since = "1.0.0")]
pub struct Vars {
inner: VarsOs,
@@ -76,10 +78,9 @@
/// An iterator over a snapshot of the environment variables of this process.
///
-/// This structure is created by the [`std::env::vars_os`] function. See
-/// its documentation for more.
+/// This structure is created by [`env::vars_os()`]. See its documentation for more.
///
-/// [`std::env::vars_os`]: vars_os
+/// [`env::vars_os()`]: vars_os
#[stable(feature = "env", since = "1.0.0")]
pub struct VarsOs {
inner: os_imp::Env,
@@ -95,10 +96,8 @@
/// # Panics
///
/// While iterating, the returned iterator will panic if any key or value in the
-/// environment is not valid unicode. If this is not desired, consider using the
-/// [`env::vars_os`] function.
-///
-/// [`env::vars_os`]: vars_os
+/// environment is not valid unicode. If this is not desired, consider using
+/// [`env::vars_os()`].
///
/// # Examples
///
@@ -111,6 +110,8 @@
/// println!("{}: {}", key, value);
/// }
/// ```
+///
+/// [`env::vars_os()`]: vars_os
#[stable(feature = "env", since = "1.0.0")]
pub fn vars() -> Vars {
Vars { inner: vars_os() }
@@ -242,9 +243,9 @@
}
/// The error type for operations interacting with environment variables.
-/// Possibly returned from the [`env::var`] function.
+/// Possibly returned from [`env::var()`].
///
-/// [`env::var`]: var
+/// [`env::var()`]: var
#[derive(Debug, PartialEq, Eq, Clone)]
#[stable(feature = "env", since = "1.0.0")]
pub enum VarError {
@@ -369,10 +370,10 @@
///
/// The iterator element type is [`PathBuf`].
///
-/// This structure is created by the [`std::env::split_paths`] function. See its
+/// This structure is created by [`env::split_paths()`]. See its
/// documentation for more.
///
-/// [`std::env::split_paths`]: split_paths
+/// [`env::split_paths()`]: split_paths
#[stable(feature = "env", since = "1.0.0")]
pub struct SplitPaths<'a> {
inner: os_imp::SplitPaths<'a>,
@@ -423,9 +424,9 @@
}
/// The error type for operations on the `PATH` variable. Possibly returned from
-/// the [`env::join_paths`] function.
+/// [`env::join_paths()`].
///
-/// [`env::join_paths`]: join_paths
+/// [`env::join_paths()`]: join_paths
#[derive(Debug)]
#[stable(feature = "env", since = "1.0.0")]
pub struct JoinPathsError {
@@ -460,7 +461,8 @@
/// }
/// ```
///
-/// Joining a path containing a colon on a Unix-like platform results in an error:
+/// Joining a path containing a colon on a Unix-like platform results in an
+/// error:
///
/// ```
/// # if cfg!(unix) {
@@ -472,8 +474,8 @@
/// # }
/// ```
///
-/// Using `env::join_paths` with [`env::split_paths`] to append an item to the `PATH` environment
-/// variable:
+/// Using `env::join_paths()` with [`env::split_paths()`] to append an item to
+/// the `PATH` environment variable:
///
/// ```
/// use std::env;
@@ -491,7 +493,7 @@
/// }
/// ```
///
-/// [`env::split_paths`]: split_paths
+/// [`env::split_paths()`]: split_paths
#[stable(feature = "env", since = "1.0.0")]
pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
where
@@ -664,14 +666,14 @@
/// An iterator over the arguments of a process, yielding a [`String`] value for
/// each argument.
///
-/// This struct is created by the [`std::env::args`] function. See its
-/// documentation for more.
+/// This struct is created by [`env::args()`]. See its documentation
+/// for more.
///
/// The first element is traditionally the path of the executable, but it can be
/// set to arbitrary text, and may not even exist. This means this property
/// should not be relied upon for security purposes.
///
-/// [`std::env::args`]: args
+/// [`env::args()`]: args
#[stable(feature = "env", since = "1.0.0")]
pub struct Args {
inner: ArgsOs,
@@ -680,34 +682,34 @@
/// An iterator over the arguments of a process, yielding an [`OsString`] value
/// for each argument.
///
-/// This struct is created by the [`std::env::args_os`] function. See its
-/// documentation for more.
+/// This struct is created by [`env::args_os()`]. See its documentation
+/// for more.
///
/// The first element is traditionally the path of the executable, but it can be
/// set to arbitrary text, and may not even exist. This means this property
/// should not be relied upon for security purposes.
///
-/// [`std::env::args_os`]: args_os
+/// [`env::args_os()`]: args_os
#[stable(feature = "env", since = "1.0.0")]
pub struct ArgsOs {
inner: sys::args::Args,
}
-/// Returns the arguments which this program was started with (normally passed
+/// Returns the arguments that this program was started with (normally passed
/// via the command line).
///
/// The first element is traditionally the path of the executable, but it can be
/// set to arbitrary text, and may not even exist. This means this property should
/// not be relied upon for security purposes.
///
-/// On Unix systems shell usually expands unquoted arguments with glob patterns
+/// On Unix systems the shell usually expands unquoted arguments with glob patterns
/// (such as `*` and `?`). On Windows this is not done, and such arguments are
/// passed as-is.
///
-/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array".
-/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension.
-/// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS
-/// and Windows.
+/// On glibc Linux systems, arguments are retrieved by placing a function in `.init_array`.
+/// Glibc passes `argc`, `argv`, and `envp` to functions in `.init_array`, as a non-standard
+/// extension. This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it
+/// does on macOS and Windows.
///
/// # Panics
///
@@ -944,112 +946,3 @@
#[stable(feature = "env", since = "1.0.0")]
pub const EXE_EXTENSION: &str = os::EXE_EXTENSION;
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- use crate::path::Path;
-
- #[test]
- #[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)]
- fn test_self_exe_path() {
- let path = current_exe();
- assert!(path.is_ok());
- let path = path.unwrap();
-
- // Hard to test this function
- assert!(path.is_absolute());
- }
-
- #[test]
- fn test() {
- assert!((!Path::new("test-path").is_absolute()));
-
- #[cfg(not(target_env = "sgx"))]
- current_dir().unwrap();
- }
-
- #[test]
- #[cfg(windows)]
- fn split_paths_windows() {
- use crate::path::PathBuf;
-
- fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
- split_paths(unparsed).collect::<Vec<_>>()
- == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>()
- }
-
- assert!(check_parse("", &mut [""]));
- assert!(check_parse(r#""""#, &mut [""]));
- assert!(check_parse(";;", &mut ["", "", ""]));
- assert!(check_parse(r"c:\", &mut [r"c:\"]));
- assert!(check_parse(r"c:\;", &mut [r"c:\", ""]));
- assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"]));
- assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"]));
- assert!(check_parse(
- r#"c:\;c:\"foo;bar"\;c:\baz"#,
- &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"]
- ));
- }
-
- #[test]
- #[cfg(unix)]
- fn split_paths_unix() {
- use crate::path::PathBuf;
-
- fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
- split_paths(unparsed).collect::<Vec<_>>()
- == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>()
- }
-
- assert!(check_parse("", &mut [""]));
- assert!(check_parse("::", &mut ["", "", ""]));
- assert!(check_parse("/", &mut ["/"]));
- assert!(check_parse("/:", &mut ["/", ""]));
- assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"]));
- }
-
- #[test]
- #[cfg(unix)]
- fn join_paths_unix() {
- use crate::ffi::OsStr;
-
- fn test_eq(input: &[&str], output: &str) -> bool {
- &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output)
- }
-
- assert!(test_eq(&[], ""));
- assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin"));
- assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:"));
- assert!(join_paths(["/te:st"].iter().cloned()).is_err());
- }
-
- #[test]
- #[cfg(windows)]
- fn join_paths_windows() {
- use crate::ffi::OsStr;
-
- fn test_eq(input: &[&str], output: &str) -> bool {
- &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output)
- }
-
- assert!(test_eq(&[], ""));
- assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\"));
- assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;"));
- assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#));
- assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err());
- }
-
- #[test]
- fn args_debug() {
- assert_eq!(
- format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()),
- format!("{:?}", args())
- );
- assert_eq!(
- format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()),
- format!("{:?}", args_os())
- );
- }
-}
diff --git a/library/std/src/env/tests.rs b/library/std/src/env/tests.rs
new file mode 100644
index 0000000..94cace0
--- /dev/null
+++ b/library/std/src/env/tests.rs
@@ -0,0 +1,102 @@
+use super::*;
+
+use crate::path::Path;
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)]
+fn test_self_exe_path() {
+ let path = current_exe();
+ assert!(path.is_ok());
+ let path = path.unwrap();
+
+ // Hard to test this function
+ assert!(path.is_absolute());
+}
+
+#[test]
+fn test() {
+ assert!((!Path::new("test-path").is_absolute()));
+
+ #[cfg(not(target_env = "sgx"))]
+ current_dir().unwrap();
+}
+
+#[test]
+#[cfg(windows)]
+fn split_paths_windows() {
+ use crate::path::PathBuf;
+
+ fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
+ split_paths(unparsed).collect::<Vec<_>>()
+ == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>()
+ }
+
+ assert!(check_parse("", &mut [""]));
+ assert!(check_parse(r#""""#, &mut [""]));
+ assert!(check_parse(";;", &mut ["", "", ""]));
+ assert!(check_parse(r"c:\", &mut [r"c:\"]));
+ assert!(check_parse(r"c:\;", &mut [r"c:\", ""]));
+ assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"]));
+ assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"]));
+ assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"]));
+}
+
+#[test]
+#[cfg(unix)]
+fn split_paths_unix() {
+ use crate::path::PathBuf;
+
+ fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
+ split_paths(unparsed).collect::<Vec<_>>()
+ == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>()
+ }
+
+ assert!(check_parse("", &mut [""]));
+ assert!(check_parse("::", &mut ["", "", ""]));
+ assert!(check_parse("/", &mut ["/"]));
+ assert!(check_parse("/:", &mut ["/", ""]));
+ assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"]));
+}
+
+#[test]
+#[cfg(unix)]
+fn join_paths_unix() {
+ use crate::ffi::OsStr;
+
+ fn test_eq(input: &[&str], output: &str) -> bool {
+ &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output)
+ }
+
+ assert!(test_eq(&[], ""));
+ assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin"));
+ assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:"));
+ assert!(join_paths(["/te:st"].iter().cloned()).is_err());
+}
+
+#[test]
+#[cfg(windows)]
+fn join_paths_windows() {
+ use crate::ffi::OsStr;
+
+ fn test_eq(input: &[&str], output: &str) -> bool {
+ &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output)
+ }
+
+ assert!(test_eq(&[], ""));
+ assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\"));
+ assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;"));
+ assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#));
+ assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err());
+}
+
+#[test]
+fn args_debug() {
+ assert_eq!(
+ format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()),
+ format!("{:?}", args())
+ );
+ assert_eq!(
+ format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()),
+ format!("{:?}", args_os())
+ );
+}
diff --git a/library/std/src/error.rs b/library/std/src/error.rs
index 84e686c..5771ca7 100644
--- a/library/std/src/error.rs
+++ b/library/std/src/error.rs
@@ -13,10 +13,13 @@
// coherence challenge (e.g., specialization, neg impls, etc) we can
// reconsider what crate these items belong in.
+#[cfg(test)]
+mod tests;
+
use core::array;
use core::convert::Infallible;
-use crate::alloc::{AllocErr, LayoutErr};
+use crate::alloc::{AllocError, LayoutErr};
use crate::any::TypeId;
use crate::backtrace::Backtrace;
use crate::borrow::Cow;
@@ -33,15 +36,14 @@
/// themselves through the [`Display`] and [`Debug`] traits, and may provide
/// cause chain information:
///
-/// The [`source`] method is generally used when errors cross "abstraction
-/// boundaries". If one module must report an error that is caused by an error
-/// from a lower-level module, it can allow access to that error via the
-/// [`source`] method. This makes it possible for the high-level module to
-/// provide its own errors while also revealing some of the implementation for
-/// debugging via [`source`] chains.
+/// [`Error::source()`] is generally used when errors cross
+/// "abstraction boundaries". If one module must report an error that is caused
+/// by an error from a lower-level module, it can allow accessing that error
+/// via [`Error::source()`]. This makes it possible for the high-level
+/// module to provide its own errors while also revealing some of the
+/// implementation for debugging via `source` chains.
///
/// [`Result<T, E>`]: Result
-/// [`source`]: Error::source
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Error: Debug + Display {
/// The lower-level source of this error, if any.
@@ -385,13 +387,9 @@
reason = "the precise API and guarantees it provides may be tweaked.",
issue = "32838"
)]
-impl Error for AllocErr {}
+impl Error for AllocError {}
-#[unstable(
- feature = "allocator_api",
- reason = "the precise API and guarantees it provides may be tweaked.",
- issue = "32838"
-)]
+#[stable(feature = "alloc_layout", since = "1.28.0")]
impl Error for LayoutErr {}
#[stable(feature = "rust1", since = "1.0.0")]
@@ -636,7 +634,7 @@
}
/// Returns an iterator starting with the current error and continuing with
- /// recursively calling [`source`].
+ /// recursively calling [`Error::source`].
///
/// If you want to omit the current error and only use its sources,
/// use `skip(1)`.
@@ -686,8 +684,6 @@
/// assert!(iter.next().is_none());
/// assert!(iter.next().is_none());
/// ```
- ///
- /// [`source`]: Error::source
#[unstable(feature = "error_iter", issue = "58520")]
#[inline]
pub fn chain(&self) -> Chain<'_> {
@@ -741,44 +737,3 @@
})
}
}
-
-#[cfg(test)]
-mod tests {
- use super::Error;
- use crate::fmt;
-
- #[derive(Debug, PartialEq)]
- struct A;
- #[derive(Debug, PartialEq)]
- struct B;
-
- impl fmt::Display for A {
- 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 {
- write!(f, "B")
- }
- }
-
- impl Error for A {}
- impl Error for B {}
-
- #[test]
- fn downcasting() {
- let mut a = A;
- let a = &mut a as &mut (dyn Error + 'static);
- assert_eq!(a.downcast_ref::<A>(), Some(&A));
- assert_eq!(a.downcast_ref::<B>(), None);
- assert_eq!(a.downcast_mut::<A>(), Some(&mut A));
- assert_eq!(a.downcast_mut::<B>(), None);
-
- let a: Box<dyn Error> = Box::new(A);
- match a.downcast::<B>() {
- Ok(..) => panic!("expected error"),
- Err(e) => assert_eq!(*e.downcast::<A>().unwrap(), A),
- }
- }
-}
diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs
new file mode 100644
index 0000000..66d6924
--- /dev/null
+++ b/library/std/src/error/tests.rs
@@ -0,0 +1,37 @@
+use super::Error;
+use crate::fmt;
+
+#[derive(Debug, PartialEq)]
+struct A;
+#[derive(Debug, PartialEq)]
+struct B;
+
+impl fmt::Display for A {
+ 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 {
+ write!(f, "B")
+ }
+}
+
+impl Error for A {}
+impl Error for B {}
+
+#[test]
+fn downcasting() {
+ let mut a = A;
+ let a = &mut a as &mut (dyn Error + 'static);
+ assert_eq!(a.downcast_ref::<A>(), Some(&A));
+ assert_eq!(a.downcast_ref::<B>(), None);
+ assert_eq!(a.downcast_mut::<A>(), Some(&mut A));
+ assert_eq!(a.downcast_mut::<B>(), None);
+
+ let a: Box<dyn Error> = Box::new(A);
+ match a.downcast::<B>() {
+ Ok(..) => panic!("expected error"),
+ Err(e) => assert_eq!(*e.downcast::<A>().unwrap(), A),
+ }
+}
diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs
index c905bcf..59c2da5 100644
--- a/library/std/src/f32.rs
+++ b/library/std/src/f32.rs
@@ -11,6 +11,9 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
+#[cfg(test)]
+mod tests;
+
#[cfg(not(test))]
use crate::intrinsics;
#[cfg(not(test))]
@@ -909,766 +912,3 @@
x
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::f32::consts;
- use crate::num::FpCategory as Fp;
- use crate::num::*;
-
- #[test]
- fn test_num_f32() {
- test_num(10f32, 2f32);
- }
-
- #[test]
- fn test_min_nan() {
- assert_eq!(f32::NAN.min(2.0), 2.0);
- assert_eq!(2.0f32.min(f32::NAN), 2.0);
- }
-
- #[test]
- fn test_max_nan() {
- assert_eq!(f32::NAN.max(2.0), 2.0);
- assert_eq!(2.0f32.max(f32::NAN), 2.0);
- }
-
- #[test]
- fn test_nan() {
- let nan: f32 = f32::NAN;
- assert!(nan.is_nan());
- assert!(!nan.is_infinite());
- assert!(!nan.is_finite());
- assert!(!nan.is_normal());
- assert!(nan.is_sign_positive());
- assert!(!nan.is_sign_negative());
- assert_eq!(Fp::Nan, nan.classify());
- }
-
- #[test]
- fn test_infinity() {
- let inf: f32 = f32::INFINITY;
- assert!(inf.is_infinite());
- assert!(!inf.is_finite());
- assert!(inf.is_sign_positive());
- assert!(!inf.is_sign_negative());
- assert!(!inf.is_nan());
- assert!(!inf.is_normal());
- assert_eq!(Fp::Infinite, inf.classify());
- }
-
- #[test]
- fn test_neg_infinity() {
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert!(neg_inf.is_infinite());
- assert!(!neg_inf.is_finite());
- assert!(!neg_inf.is_sign_positive());
- assert!(neg_inf.is_sign_negative());
- assert!(!neg_inf.is_nan());
- assert!(!neg_inf.is_normal());
- assert_eq!(Fp::Infinite, neg_inf.classify());
- }
-
- #[test]
- fn test_zero() {
- let zero: f32 = 0.0f32;
- assert_eq!(0.0, zero);
- assert!(!zero.is_infinite());
- assert!(zero.is_finite());
- assert!(zero.is_sign_positive());
- assert!(!zero.is_sign_negative());
- assert!(!zero.is_nan());
- assert!(!zero.is_normal());
- assert_eq!(Fp::Zero, zero.classify());
- }
-
- #[test]
- fn test_neg_zero() {
- let neg_zero: f32 = -0.0;
- assert_eq!(0.0, neg_zero);
- assert!(!neg_zero.is_infinite());
- assert!(neg_zero.is_finite());
- assert!(!neg_zero.is_sign_positive());
- assert!(neg_zero.is_sign_negative());
- assert!(!neg_zero.is_nan());
- assert!(!neg_zero.is_normal());
- assert_eq!(Fp::Zero, neg_zero.classify());
- }
-
- #[test]
- fn test_one() {
- let one: f32 = 1.0f32;
- assert_eq!(1.0, one);
- assert!(!one.is_infinite());
- assert!(one.is_finite());
- assert!(one.is_sign_positive());
- assert!(!one.is_sign_negative());
- assert!(!one.is_nan());
- assert!(one.is_normal());
- assert_eq!(Fp::Normal, one.classify());
- }
-
- #[test]
- fn test_is_nan() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert!(nan.is_nan());
- assert!(!0.0f32.is_nan());
- assert!(!5.3f32.is_nan());
- assert!(!(-10.732f32).is_nan());
- assert!(!inf.is_nan());
- assert!(!neg_inf.is_nan());
- }
-
- #[test]
- fn test_is_infinite() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert!(!nan.is_infinite());
- assert!(inf.is_infinite());
- assert!(neg_inf.is_infinite());
- assert!(!0.0f32.is_infinite());
- assert!(!42.8f32.is_infinite());
- assert!(!(-109.2f32).is_infinite());
- }
-
- #[test]
- fn test_is_finite() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert!(!nan.is_finite());
- assert!(!inf.is_finite());
- assert!(!neg_inf.is_finite());
- assert!(0.0f32.is_finite());
- assert!(42.8f32.is_finite());
- assert!((-109.2f32).is_finite());
- }
-
- #[test]
- fn test_is_normal() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- let zero: f32 = 0.0f32;
- let neg_zero: f32 = -0.0;
- assert!(!nan.is_normal());
- assert!(!inf.is_normal());
- assert!(!neg_inf.is_normal());
- assert!(!zero.is_normal());
- assert!(!neg_zero.is_normal());
- assert!(1f32.is_normal());
- assert!(1e-37f32.is_normal());
- assert!(!1e-38f32.is_normal());
- }
-
- #[test]
- fn test_classify() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- let zero: f32 = 0.0f32;
- let neg_zero: f32 = -0.0;
- assert_eq!(nan.classify(), Fp::Nan);
- assert_eq!(inf.classify(), Fp::Infinite);
- assert_eq!(neg_inf.classify(), Fp::Infinite);
- assert_eq!(zero.classify(), Fp::Zero);
- assert_eq!(neg_zero.classify(), Fp::Zero);
- assert_eq!(1f32.classify(), Fp::Normal);
- assert_eq!(1e-37f32.classify(), Fp::Normal);
- assert_eq!(1e-38f32.classify(), Fp::Subnormal);
- }
-
- #[test]
- fn test_floor() {
- assert_approx_eq!(1.0f32.floor(), 1.0f32);
- assert_approx_eq!(1.3f32.floor(), 1.0f32);
- assert_approx_eq!(1.5f32.floor(), 1.0f32);
- assert_approx_eq!(1.7f32.floor(), 1.0f32);
- assert_approx_eq!(0.0f32.floor(), 0.0f32);
- assert_approx_eq!((-0.0f32).floor(), -0.0f32);
- assert_approx_eq!((-1.0f32).floor(), -1.0f32);
- assert_approx_eq!((-1.3f32).floor(), -2.0f32);
- assert_approx_eq!((-1.5f32).floor(), -2.0f32);
- assert_approx_eq!((-1.7f32).floor(), -2.0f32);
- }
-
- #[test]
- fn test_ceil() {
- assert_approx_eq!(1.0f32.ceil(), 1.0f32);
- assert_approx_eq!(1.3f32.ceil(), 2.0f32);
- assert_approx_eq!(1.5f32.ceil(), 2.0f32);
- assert_approx_eq!(1.7f32.ceil(), 2.0f32);
- assert_approx_eq!(0.0f32.ceil(), 0.0f32);
- assert_approx_eq!((-0.0f32).ceil(), -0.0f32);
- assert_approx_eq!((-1.0f32).ceil(), -1.0f32);
- assert_approx_eq!((-1.3f32).ceil(), -1.0f32);
- assert_approx_eq!((-1.5f32).ceil(), -1.0f32);
- assert_approx_eq!((-1.7f32).ceil(), -1.0f32);
- }
-
- #[test]
- fn test_round() {
- assert_approx_eq!(1.0f32.round(), 1.0f32);
- assert_approx_eq!(1.3f32.round(), 1.0f32);
- assert_approx_eq!(1.5f32.round(), 2.0f32);
- assert_approx_eq!(1.7f32.round(), 2.0f32);
- assert_approx_eq!(0.0f32.round(), 0.0f32);
- assert_approx_eq!((-0.0f32).round(), -0.0f32);
- assert_approx_eq!((-1.0f32).round(), -1.0f32);
- assert_approx_eq!((-1.3f32).round(), -1.0f32);
- assert_approx_eq!((-1.5f32).round(), -2.0f32);
- assert_approx_eq!((-1.7f32).round(), -2.0f32);
- }
-
- #[test]
- fn test_trunc() {
- assert_approx_eq!(1.0f32.trunc(), 1.0f32);
- assert_approx_eq!(1.3f32.trunc(), 1.0f32);
- assert_approx_eq!(1.5f32.trunc(), 1.0f32);
- assert_approx_eq!(1.7f32.trunc(), 1.0f32);
- assert_approx_eq!(0.0f32.trunc(), 0.0f32);
- assert_approx_eq!((-0.0f32).trunc(), -0.0f32);
- assert_approx_eq!((-1.0f32).trunc(), -1.0f32);
- assert_approx_eq!((-1.3f32).trunc(), -1.0f32);
- assert_approx_eq!((-1.5f32).trunc(), -1.0f32);
- assert_approx_eq!((-1.7f32).trunc(), -1.0f32);
- }
-
- #[test]
- fn test_fract() {
- assert_approx_eq!(1.0f32.fract(), 0.0f32);
- assert_approx_eq!(1.3f32.fract(), 0.3f32);
- assert_approx_eq!(1.5f32.fract(), 0.5f32);
- assert_approx_eq!(1.7f32.fract(), 0.7f32);
- assert_approx_eq!(0.0f32.fract(), 0.0f32);
- assert_approx_eq!((-0.0f32).fract(), -0.0f32);
- assert_approx_eq!((-1.0f32).fract(), -0.0f32);
- assert_approx_eq!((-1.3f32).fract(), -0.3f32);
- assert_approx_eq!((-1.5f32).fract(), -0.5f32);
- assert_approx_eq!((-1.7f32).fract(), -0.7f32);
- }
-
- #[test]
- fn test_abs() {
- assert_eq!(f32::INFINITY.abs(), f32::INFINITY);
- assert_eq!(1f32.abs(), 1f32);
- assert_eq!(0f32.abs(), 0f32);
- assert_eq!((-0f32).abs(), 0f32);
- assert_eq!((-1f32).abs(), 1f32);
- assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY);
- assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32);
- assert!(f32::NAN.abs().is_nan());
- }
-
- #[test]
- fn test_signum() {
- assert_eq!(f32::INFINITY.signum(), 1f32);
- assert_eq!(1f32.signum(), 1f32);
- assert_eq!(0f32.signum(), 1f32);
- assert_eq!((-0f32).signum(), -1f32);
- assert_eq!((-1f32).signum(), -1f32);
- assert_eq!(f32::NEG_INFINITY.signum(), -1f32);
- assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32);
- assert!(f32::NAN.signum().is_nan());
- }
-
- #[test]
- fn test_is_sign_positive() {
- assert!(f32::INFINITY.is_sign_positive());
- assert!(1f32.is_sign_positive());
- assert!(0f32.is_sign_positive());
- assert!(!(-0f32).is_sign_positive());
- assert!(!(-1f32).is_sign_positive());
- assert!(!f32::NEG_INFINITY.is_sign_positive());
- assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive());
- assert!(f32::NAN.is_sign_positive());
- assert!(!(-f32::NAN).is_sign_positive());
- }
-
- #[test]
- fn test_is_sign_negative() {
- assert!(!f32::INFINITY.is_sign_negative());
- assert!(!1f32.is_sign_negative());
- assert!(!0f32.is_sign_negative());
- assert!((-0f32).is_sign_negative());
- assert!((-1f32).is_sign_negative());
- assert!(f32::NEG_INFINITY.is_sign_negative());
- assert!((1f32 / f32::NEG_INFINITY).is_sign_negative());
- assert!(!f32::NAN.is_sign_negative());
- assert!((-f32::NAN).is_sign_negative());
- }
-
- #[test]
- fn test_mul_add() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_approx_eq!(12.3f32.mul_add(4.5, 6.7), 62.05);
- assert_approx_eq!((-12.3f32).mul_add(-4.5, -6.7), 48.65);
- assert_approx_eq!(0.0f32.mul_add(8.9, 1.2), 1.2);
- assert_approx_eq!(3.4f32.mul_add(-0.0, 5.6), 5.6);
- assert!(nan.mul_add(7.8, 9.0).is_nan());
- assert_eq!(inf.mul_add(7.8, 9.0), inf);
- assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
- assert_eq!(8.9f32.mul_add(inf, 3.2), inf);
- assert_eq!((-3.2f32).mul_add(2.4, neg_inf), neg_inf);
- }
-
- #[test]
- fn test_recip() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_eq!(1.0f32.recip(), 1.0);
- assert_eq!(2.0f32.recip(), 0.5);
- assert_eq!((-0.4f32).recip(), -2.5);
- assert_eq!(0.0f32.recip(), inf);
- assert!(nan.recip().is_nan());
- assert_eq!(inf.recip(), 0.0);
- assert_eq!(neg_inf.recip(), 0.0);
- }
-
- #[test]
- fn test_powi() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_eq!(1.0f32.powi(1), 1.0);
- assert_approx_eq!((-3.1f32).powi(2), 9.61);
- assert_approx_eq!(5.9f32.powi(-2), 0.028727);
- assert_eq!(8.3f32.powi(0), 1.0);
- assert!(nan.powi(2).is_nan());
- assert_eq!(inf.powi(3), inf);
- assert_eq!(neg_inf.powi(2), inf);
- }
-
- #[test]
- fn test_powf() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_eq!(1.0f32.powf(1.0), 1.0);
- assert_approx_eq!(3.4f32.powf(4.5), 246.408218);
- assert_approx_eq!(2.7f32.powf(-3.2), 0.041652);
- assert_approx_eq!((-3.1f32).powf(2.0), 9.61);
- assert_approx_eq!(5.9f32.powf(-2.0), 0.028727);
- assert_eq!(8.3f32.powf(0.0), 1.0);
- assert!(nan.powf(2.0).is_nan());
- assert_eq!(inf.powf(2.0), inf);
- assert_eq!(neg_inf.powf(3.0), neg_inf);
- }
-
- #[test]
- fn test_sqrt_domain() {
- assert!(f32::NAN.sqrt().is_nan());
- assert!(f32::NEG_INFINITY.sqrt().is_nan());
- assert!((-1.0f32).sqrt().is_nan());
- assert_eq!((-0.0f32).sqrt(), -0.0);
- assert_eq!(0.0f32.sqrt(), 0.0);
- assert_eq!(1.0f32.sqrt(), 1.0);
- assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY);
- }
-
- #[test]
- fn test_exp() {
- assert_eq!(1.0, 0.0f32.exp());
- assert_approx_eq!(2.718282, 1.0f32.exp());
- assert_approx_eq!(148.413162, 5.0f32.exp());
-
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- let nan: f32 = f32::NAN;
- assert_eq!(inf, inf.exp());
- assert_eq!(0.0, neg_inf.exp());
- assert!(nan.exp().is_nan());
- }
-
- #[test]
- fn test_exp2() {
- assert_eq!(32.0, 5.0f32.exp2());
- assert_eq!(1.0, 0.0f32.exp2());
-
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- let nan: f32 = f32::NAN;
- assert_eq!(inf, inf.exp2());
- assert_eq!(0.0, neg_inf.exp2());
- assert!(nan.exp2().is_nan());
- }
-
- #[test]
- fn test_ln() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_approx_eq!(1.0f32.exp().ln(), 1.0);
- assert!(nan.ln().is_nan());
- assert_eq!(inf.ln(), inf);
- assert!(neg_inf.ln().is_nan());
- assert!((-2.3f32).ln().is_nan());
- assert_eq!((-0.0f32).ln(), neg_inf);
- assert_eq!(0.0f32.ln(), neg_inf);
- assert_approx_eq!(4.0f32.ln(), 1.386294);
- }
-
- #[test]
- fn test_log() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_eq!(10.0f32.log(10.0), 1.0);
- assert_approx_eq!(2.3f32.log(3.5), 0.664858);
- assert_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0);
- assert!(1.0f32.log(1.0).is_nan());
- assert!(1.0f32.log(-13.9).is_nan());
- assert!(nan.log(2.3).is_nan());
- assert_eq!(inf.log(10.0), inf);
- assert!(neg_inf.log(8.8).is_nan());
- assert!((-2.3f32).log(0.1).is_nan());
- assert_eq!((-0.0f32).log(2.0), neg_inf);
- assert_eq!(0.0f32.log(7.0), neg_inf);
- }
-
- #[test]
- fn test_log2() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_approx_eq!(10.0f32.log2(), 3.321928);
- assert_approx_eq!(2.3f32.log2(), 1.201634);
- assert_approx_eq!(1.0f32.exp().log2(), 1.442695);
- assert!(nan.log2().is_nan());
- assert_eq!(inf.log2(), inf);
- assert!(neg_inf.log2().is_nan());
- assert!((-2.3f32).log2().is_nan());
- assert_eq!((-0.0f32).log2(), neg_inf);
- assert_eq!(0.0f32.log2(), neg_inf);
- }
-
- #[test]
- fn test_log10() {
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_eq!(10.0f32.log10(), 1.0);
- assert_approx_eq!(2.3f32.log10(), 0.361728);
- assert_approx_eq!(1.0f32.exp().log10(), 0.434294);
- assert_eq!(1.0f32.log10(), 0.0);
- assert!(nan.log10().is_nan());
- assert_eq!(inf.log10(), inf);
- assert!(neg_inf.log10().is_nan());
- assert!((-2.3f32).log10().is_nan());
- assert_eq!((-0.0f32).log10(), neg_inf);
- assert_eq!(0.0f32.log10(), neg_inf);
- }
-
- #[test]
- fn test_to_degrees() {
- let pi: f32 = consts::PI;
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_eq!(0.0f32.to_degrees(), 0.0);
- assert_approx_eq!((-5.8f32).to_degrees(), -332.315521);
- assert_eq!(pi.to_degrees(), 180.0);
- assert!(nan.to_degrees().is_nan());
- assert_eq!(inf.to_degrees(), inf);
- assert_eq!(neg_inf.to_degrees(), neg_inf);
- assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703);
- }
-
- #[test]
- fn test_to_radians() {
- let pi: f32 = consts::PI;
- let nan: f32 = f32::NAN;
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- assert_eq!(0.0f32.to_radians(), 0.0);
- assert_approx_eq!(154.6f32.to_radians(), 2.698279);
- assert_approx_eq!((-332.31f32).to_radians(), -5.799903);
- assert_eq!(180.0f32.to_radians(), pi);
- assert!(nan.to_radians().is_nan());
- assert_eq!(inf.to_radians(), inf);
- assert_eq!(neg_inf.to_radians(), neg_inf);
- }
-
- #[test]
- fn test_asinh() {
- assert_eq!(0.0f32.asinh(), 0.0f32);
- assert_eq!((-0.0f32).asinh(), -0.0f32);
-
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- let nan: f32 = f32::NAN;
- assert_eq!(inf.asinh(), inf);
- assert_eq!(neg_inf.asinh(), neg_inf);
- assert!(nan.asinh().is_nan());
- assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271
- assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32);
- assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32);
- // regression test for the catastrophic cancellation fixed in 72486
- assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32);
- }
-
- #[test]
- fn test_acosh() {
- assert_eq!(1.0f32.acosh(), 0.0f32);
- assert!(0.999f32.acosh().is_nan());
-
- let inf: f32 = f32::INFINITY;
- let neg_inf: f32 = f32::NEG_INFINITY;
- let nan: f32 = f32::NAN;
- assert_eq!(inf.acosh(), inf);
- assert!(neg_inf.acosh().is_nan());
- assert!(nan.acosh().is_nan());
- assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32);
- assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32);
- }
-
- #[test]
- fn test_atanh() {
- assert_eq!(0.0f32.atanh(), 0.0f32);
- assert_eq!((-0.0f32).atanh(), -0.0f32);
-
- let inf32: f32 = f32::INFINITY;
- let neg_inf32: f32 = f32::NEG_INFINITY;
- assert_eq!(1.0f32.atanh(), inf32);
- assert_eq!((-1.0f32).atanh(), neg_inf32);
-
- assert!(2f64.atanh().atanh().is_nan());
- assert!((-2f64).atanh().atanh().is_nan());
-
- let inf64: f32 = f32::INFINITY;
- let neg_inf64: f32 = f32::NEG_INFINITY;
- let nan32: f32 = f32::NAN;
- assert!(inf64.atanh().is_nan());
- assert!(neg_inf64.atanh().is_nan());
- assert!(nan32.atanh().is_nan());
-
- assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32);
- assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32);
- }
-
- #[test]
- fn test_real_consts() {
- use super::consts;
-
- let pi: f32 = consts::PI;
- let frac_pi_2: f32 = consts::FRAC_PI_2;
- let frac_pi_3: f32 = consts::FRAC_PI_3;
- let frac_pi_4: f32 = consts::FRAC_PI_4;
- let frac_pi_6: f32 = consts::FRAC_PI_6;
- let frac_pi_8: f32 = consts::FRAC_PI_8;
- let frac_1_pi: f32 = consts::FRAC_1_PI;
- let frac_2_pi: f32 = consts::FRAC_2_PI;
- let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI;
- let sqrt2: f32 = consts::SQRT_2;
- let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2;
- let e: f32 = consts::E;
- let log2_e: f32 = consts::LOG2_E;
- let log10_e: f32 = consts::LOG10_E;
- let ln_2: f32 = consts::LN_2;
- let ln_10: f32 = consts::LN_10;
-
- assert_approx_eq!(frac_pi_2, pi / 2f32);
- assert_approx_eq!(frac_pi_3, pi / 3f32);
- assert_approx_eq!(frac_pi_4, pi / 4f32);
- assert_approx_eq!(frac_pi_6, pi / 6f32);
- assert_approx_eq!(frac_pi_8, pi / 8f32);
- assert_approx_eq!(frac_1_pi, 1f32 / pi);
- assert_approx_eq!(frac_2_pi, 2f32 / pi);
- assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt());
- assert_approx_eq!(sqrt2, 2f32.sqrt());
- assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt());
- assert_approx_eq!(log2_e, e.log2());
- assert_approx_eq!(log10_e, e.log10());
- assert_approx_eq!(ln_2, 2f32.ln());
- assert_approx_eq!(ln_10, 10f32.ln());
- }
-
- #[test]
- fn test_float_bits_conv() {
- assert_eq!((1f32).to_bits(), 0x3f800000);
- assert_eq!((12.5f32).to_bits(), 0x41480000);
- assert_eq!((1337f32).to_bits(), 0x44a72000);
- assert_eq!((-14.25f32).to_bits(), 0xc1640000);
- assert_approx_eq!(f32::from_bits(0x3f800000), 1.0);
- assert_approx_eq!(f32::from_bits(0x41480000), 12.5);
- assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0);
- assert_approx_eq!(f32::from_bits(0xc1640000), -14.25);
-
- // Check that NaNs roundtrip their bits regardless of signaling-ness
- // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
- let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA;
- let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555;
- assert!(f32::from_bits(masked_nan1).is_nan());
- assert!(f32::from_bits(masked_nan2).is_nan());
-
- 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() {
- let _ = 1.0f32.clamp(3.0, 1.0);
- }
-
- #[test]
- #[should_panic]
- fn test_clamp_min_is_nan() {
- let _ = 1.0f32.clamp(f32::NAN, 1.0);
- }
-
- #[test]
- #[should_panic]
- fn test_clamp_max_is_nan() {
- let _ = 1.0f32.clamp(3.0, f32::NAN);
- }
-
- #[test]
- fn test_total_cmp() {
- use core::cmp::Ordering;
-
- fn quiet_bit_mask() -> u32 {
- 1 << (f32::MANTISSA_DIGITS - 2)
- }
-
- fn min_subnorm() -> f32 {
- f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0)
- }
-
- fn max_subnorm() -> f32 {
- f32::MIN_POSITIVE - min_subnorm()
- }
-
- fn q_nan() -> f32 {
- f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask())
- }
-
- fn s_nan() -> f32 {
- f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42)
- }
-
- assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
- assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
- assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY));
- assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX));
- assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5));
- assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0));
- assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5));
- assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5));
- assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE));
- assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0));
- assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0));
- assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE));
- assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5));
- assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0));
- assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5));
- assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5));
- assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX));
- assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY));
- assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
- assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
-
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
- assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX));
- assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5));
- assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5));
- assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0));
- assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5));
- assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
- assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0));
- assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE));
- assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5));
- assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0));
- assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5));
- assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5));
- assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX));
- assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY));
- assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan()));
- assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
-
- assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
- assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan()));
- assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY));
- assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX));
- assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5));
- assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5));
- assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0));
- assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5));
- assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE));
- assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0));
- assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
- assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE));
- assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5));
- assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0));
- assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5));
- assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5));
- assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX));
- assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY));
- assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
-
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
-
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
- }
-}
diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs
new file mode 100644
index 0000000..0d4b865
--- /dev/null
+++ b/library/std/src/f32/tests.rs
@@ -0,0 +1,759 @@
+use crate::f32::consts;
+use crate::num::FpCategory as Fp;
+use crate::num::*;
+
+#[test]
+fn test_num_f32() {
+ test_num(10f32, 2f32);
+}
+
+#[test]
+fn test_min_nan() {
+ assert_eq!(f32::NAN.min(2.0), 2.0);
+ assert_eq!(2.0f32.min(f32::NAN), 2.0);
+}
+
+#[test]
+fn test_max_nan() {
+ assert_eq!(f32::NAN.max(2.0), 2.0);
+ assert_eq!(2.0f32.max(f32::NAN), 2.0);
+}
+
+#[test]
+fn test_nan() {
+ let nan: f32 = f32::NAN;
+ assert!(nan.is_nan());
+ assert!(!nan.is_infinite());
+ assert!(!nan.is_finite());
+ assert!(!nan.is_normal());
+ assert!(nan.is_sign_positive());
+ assert!(!nan.is_sign_negative());
+ assert_eq!(Fp::Nan, nan.classify());
+}
+
+#[test]
+fn test_infinity() {
+ let inf: f32 = f32::INFINITY;
+ assert!(inf.is_infinite());
+ assert!(!inf.is_finite());
+ assert!(inf.is_sign_positive());
+ assert!(!inf.is_sign_negative());
+ assert!(!inf.is_nan());
+ assert!(!inf.is_normal());
+ assert_eq!(Fp::Infinite, inf.classify());
+}
+
+#[test]
+fn test_neg_infinity() {
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert!(neg_inf.is_infinite());
+ assert!(!neg_inf.is_finite());
+ assert!(!neg_inf.is_sign_positive());
+ assert!(neg_inf.is_sign_negative());
+ assert!(!neg_inf.is_nan());
+ assert!(!neg_inf.is_normal());
+ assert_eq!(Fp::Infinite, neg_inf.classify());
+}
+
+#[test]
+fn test_zero() {
+ let zero: f32 = 0.0f32;
+ assert_eq!(0.0, zero);
+ assert!(!zero.is_infinite());
+ assert!(zero.is_finite());
+ assert!(zero.is_sign_positive());
+ assert!(!zero.is_sign_negative());
+ assert!(!zero.is_nan());
+ assert!(!zero.is_normal());
+ assert_eq!(Fp::Zero, zero.classify());
+}
+
+#[test]
+fn test_neg_zero() {
+ let neg_zero: f32 = -0.0;
+ assert_eq!(0.0, neg_zero);
+ assert!(!neg_zero.is_infinite());
+ assert!(neg_zero.is_finite());
+ assert!(!neg_zero.is_sign_positive());
+ assert!(neg_zero.is_sign_negative());
+ assert!(!neg_zero.is_nan());
+ assert!(!neg_zero.is_normal());
+ assert_eq!(Fp::Zero, neg_zero.classify());
+}
+
+#[test]
+fn test_one() {
+ let one: f32 = 1.0f32;
+ assert_eq!(1.0, one);
+ assert!(!one.is_infinite());
+ assert!(one.is_finite());
+ assert!(one.is_sign_positive());
+ assert!(!one.is_sign_negative());
+ assert!(!one.is_nan());
+ assert!(one.is_normal());
+ assert_eq!(Fp::Normal, one.classify());
+}
+
+#[test]
+fn test_is_nan() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert!(nan.is_nan());
+ assert!(!0.0f32.is_nan());
+ assert!(!5.3f32.is_nan());
+ assert!(!(-10.732f32).is_nan());
+ assert!(!inf.is_nan());
+ assert!(!neg_inf.is_nan());
+}
+
+#[test]
+fn test_is_infinite() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert!(!nan.is_infinite());
+ assert!(inf.is_infinite());
+ assert!(neg_inf.is_infinite());
+ assert!(!0.0f32.is_infinite());
+ assert!(!42.8f32.is_infinite());
+ assert!(!(-109.2f32).is_infinite());
+}
+
+#[test]
+fn test_is_finite() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert!(!nan.is_finite());
+ assert!(!inf.is_finite());
+ assert!(!neg_inf.is_finite());
+ assert!(0.0f32.is_finite());
+ assert!(42.8f32.is_finite());
+ assert!((-109.2f32).is_finite());
+}
+
+#[test]
+fn test_is_normal() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ let zero: f32 = 0.0f32;
+ let neg_zero: f32 = -0.0;
+ assert!(!nan.is_normal());
+ assert!(!inf.is_normal());
+ assert!(!neg_inf.is_normal());
+ assert!(!zero.is_normal());
+ assert!(!neg_zero.is_normal());
+ assert!(1f32.is_normal());
+ assert!(1e-37f32.is_normal());
+ assert!(!1e-38f32.is_normal());
+}
+
+#[test]
+fn test_classify() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ let zero: f32 = 0.0f32;
+ let neg_zero: f32 = -0.0;
+ assert_eq!(nan.classify(), Fp::Nan);
+ assert_eq!(inf.classify(), Fp::Infinite);
+ assert_eq!(neg_inf.classify(), Fp::Infinite);
+ assert_eq!(zero.classify(), Fp::Zero);
+ assert_eq!(neg_zero.classify(), Fp::Zero);
+ assert_eq!(1f32.classify(), Fp::Normal);
+ assert_eq!(1e-37f32.classify(), Fp::Normal);
+ assert_eq!(1e-38f32.classify(), Fp::Subnormal);
+}
+
+#[test]
+fn test_floor() {
+ assert_approx_eq!(1.0f32.floor(), 1.0f32);
+ assert_approx_eq!(1.3f32.floor(), 1.0f32);
+ assert_approx_eq!(1.5f32.floor(), 1.0f32);
+ assert_approx_eq!(1.7f32.floor(), 1.0f32);
+ assert_approx_eq!(0.0f32.floor(), 0.0f32);
+ assert_approx_eq!((-0.0f32).floor(), -0.0f32);
+ assert_approx_eq!((-1.0f32).floor(), -1.0f32);
+ assert_approx_eq!((-1.3f32).floor(), -2.0f32);
+ assert_approx_eq!((-1.5f32).floor(), -2.0f32);
+ assert_approx_eq!((-1.7f32).floor(), -2.0f32);
+}
+
+#[test]
+fn test_ceil() {
+ assert_approx_eq!(1.0f32.ceil(), 1.0f32);
+ assert_approx_eq!(1.3f32.ceil(), 2.0f32);
+ assert_approx_eq!(1.5f32.ceil(), 2.0f32);
+ assert_approx_eq!(1.7f32.ceil(), 2.0f32);
+ assert_approx_eq!(0.0f32.ceil(), 0.0f32);
+ assert_approx_eq!((-0.0f32).ceil(), -0.0f32);
+ assert_approx_eq!((-1.0f32).ceil(), -1.0f32);
+ assert_approx_eq!((-1.3f32).ceil(), -1.0f32);
+ assert_approx_eq!((-1.5f32).ceil(), -1.0f32);
+ assert_approx_eq!((-1.7f32).ceil(), -1.0f32);
+}
+
+#[test]
+fn test_round() {
+ assert_approx_eq!(1.0f32.round(), 1.0f32);
+ assert_approx_eq!(1.3f32.round(), 1.0f32);
+ assert_approx_eq!(1.5f32.round(), 2.0f32);
+ assert_approx_eq!(1.7f32.round(), 2.0f32);
+ assert_approx_eq!(0.0f32.round(), 0.0f32);
+ assert_approx_eq!((-0.0f32).round(), -0.0f32);
+ assert_approx_eq!((-1.0f32).round(), -1.0f32);
+ assert_approx_eq!((-1.3f32).round(), -1.0f32);
+ assert_approx_eq!((-1.5f32).round(), -2.0f32);
+ assert_approx_eq!((-1.7f32).round(), -2.0f32);
+}
+
+#[test]
+fn test_trunc() {
+ assert_approx_eq!(1.0f32.trunc(), 1.0f32);
+ assert_approx_eq!(1.3f32.trunc(), 1.0f32);
+ assert_approx_eq!(1.5f32.trunc(), 1.0f32);
+ assert_approx_eq!(1.7f32.trunc(), 1.0f32);
+ assert_approx_eq!(0.0f32.trunc(), 0.0f32);
+ assert_approx_eq!((-0.0f32).trunc(), -0.0f32);
+ assert_approx_eq!((-1.0f32).trunc(), -1.0f32);
+ assert_approx_eq!((-1.3f32).trunc(), -1.0f32);
+ assert_approx_eq!((-1.5f32).trunc(), -1.0f32);
+ assert_approx_eq!((-1.7f32).trunc(), -1.0f32);
+}
+
+#[test]
+fn test_fract() {
+ assert_approx_eq!(1.0f32.fract(), 0.0f32);
+ assert_approx_eq!(1.3f32.fract(), 0.3f32);
+ assert_approx_eq!(1.5f32.fract(), 0.5f32);
+ assert_approx_eq!(1.7f32.fract(), 0.7f32);
+ assert_approx_eq!(0.0f32.fract(), 0.0f32);
+ assert_approx_eq!((-0.0f32).fract(), -0.0f32);
+ assert_approx_eq!((-1.0f32).fract(), -0.0f32);
+ assert_approx_eq!((-1.3f32).fract(), -0.3f32);
+ assert_approx_eq!((-1.5f32).fract(), -0.5f32);
+ assert_approx_eq!((-1.7f32).fract(), -0.7f32);
+}
+
+#[test]
+fn test_abs() {
+ assert_eq!(f32::INFINITY.abs(), f32::INFINITY);
+ assert_eq!(1f32.abs(), 1f32);
+ assert_eq!(0f32.abs(), 0f32);
+ assert_eq!((-0f32).abs(), 0f32);
+ assert_eq!((-1f32).abs(), 1f32);
+ assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY);
+ assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32);
+ assert!(f32::NAN.abs().is_nan());
+}
+
+#[test]
+fn test_signum() {
+ assert_eq!(f32::INFINITY.signum(), 1f32);
+ assert_eq!(1f32.signum(), 1f32);
+ assert_eq!(0f32.signum(), 1f32);
+ assert_eq!((-0f32).signum(), -1f32);
+ assert_eq!((-1f32).signum(), -1f32);
+ assert_eq!(f32::NEG_INFINITY.signum(), -1f32);
+ assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32);
+ assert!(f32::NAN.signum().is_nan());
+}
+
+#[test]
+fn test_is_sign_positive() {
+ assert!(f32::INFINITY.is_sign_positive());
+ assert!(1f32.is_sign_positive());
+ assert!(0f32.is_sign_positive());
+ assert!(!(-0f32).is_sign_positive());
+ assert!(!(-1f32).is_sign_positive());
+ assert!(!f32::NEG_INFINITY.is_sign_positive());
+ assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive());
+ assert!(f32::NAN.is_sign_positive());
+ assert!(!(-f32::NAN).is_sign_positive());
+}
+
+#[test]
+fn test_is_sign_negative() {
+ assert!(!f32::INFINITY.is_sign_negative());
+ assert!(!1f32.is_sign_negative());
+ assert!(!0f32.is_sign_negative());
+ assert!((-0f32).is_sign_negative());
+ assert!((-1f32).is_sign_negative());
+ assert!(f32::NEG_INFINITY.is_sign_negative());
+ assert!((1f32 / f32::NEG_INFINITY).is_sign_negative());
+ assert!(!f32::NAN.is_sign_negative());
+ assert!((-f32::NAN).is_sign_negative());
+}
+
+#[test]
+fn test_mul_add() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_approx_eq!(12.3f32.mul_add(4.5, 6.7), 62.05);
+ assert_approx_eq!((-12.3f32).mul_add(-4.5, -6.7), 48.65);
+ assert_approx_eq!(0.0f32.mul_add(8.9, 1.2), 1.2);
+ assert_approx_eq!(3.4f32.mul_add(-0.0, 5.6), 5.6);
+ assert!(nan.mul_add(7.8, 9.0).is_nan());
+ assert_eq!(inf.mul_add(7.8, 9.0), inf);
+ assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
+ assert_eq!(8.9f32.mul_add(inf, 3.2), inf);
+ assert_eq!((-3.2f32).mul_add(2.4, neg_inf), neg_inf);
+}
+
+#[test]
+fn test_recip() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_eq!(1.0f32.recip(), 1.0);
+ assert_eq!(2.0f32.recip(), 0.5);
+ assert_eq!((-0.4f32).recip(), -2.5);
+ assert_eq!(0.0f32.recip(), inf);
+ assert!(nan.recip().is_nan());
+ assert_eq!(inf.recip(), 0.0);
+ assert_eq!(neg_inf.recip(), 0.0);
+}
+
+#[test]
+fn test_powi() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_eq!(1.0f32.powi(1), 1.0);
+ assert_approx_eq!((-3.1f32).powi(2), 9.61);
+ assert_approx_eq!(5.9f32.powi(-2), 0.028727);
+ assert_eq!(8.3f32.powi(0), 1.0);
+ assert!(nan.powi(2).is_nan());
+ assert_eq!(inf.powi(3), inf);
+ assert_eq!(neg_inf.powi(2), inf);
+}
+
+#[test]
+fn test_powf() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_eq!(1.0f32.powf(1.0), 1.0);
+ assert_approx_eq!(3.4f32.powf(4.5), 246.408218);
+ assert_approx_eq!(2.7f32.powf(-3.2), 0.041652);
+ assert_approx_eq!((-3.1f32).powf(2.0), 9.61);
+ assert_approx_eq!(5.9f32.powf(-2.0), 0.028727);
+ assert_eq!(8.3f32.powf(0.0), 1.0);
+ assert!(nan.powf(2.0).is_nan());
+ assert_eq!(inf.powf(2.0), inf);
+ assert_eq!(neg_inf.powf(3.0), neg_inf);
+}
+
+#[test]
+fn test_sqrt_domain() {
+ assert!(f32::NAN.sqrt().is_nan());
+ assert!(f32::NEG_INFINITY.sqrt().is_nan());
+ assert!((-1.0f32).sqrt().is_nan());
+ assert_eq!((-0.0f32).sqrt(), -0.0);
+ assert_eq!(0.0f32.sqrt(), 0.0);
+ assert_eq!(1.0f32.sqrt(), 1.0);
+ assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY);
+}
+
+#[test]
+fn test_exp() {
+ assert_eq!(1.0, 0.0f32.exp());
+ assert_approx_eq!(2.718282, 1.0f32.exp());
+ assert_approx_eq!(148.413162, 5.0f32.exp());
+
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ let nan: f32 = f32::NAN;
+ assert_eq!(inf, inf.exp());
+ assert_eq!(0.0, neg_inf.exp());
+ assert!(nan.exp().is_nan());
+}
+
+#[test]
+fn test_exp2() {
+ assert_eq!(32.0, 5.0f32.exp2());
+ assert_eq!(1.0, 0.0f32.exp2());
+
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ let nan: f32 = f32::NAN;
+ assert_eq!(inf, inf.exp2());
+ assert_eq!(0.0, neg_inf.exp2());
+ assert!(nan.exp2().is_nan());
+}
+
+#[test]
+fn test_ln() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_approx_eq!(1.0f32.exp().ln(), 1.0);
+ assert!(nan.ln().is_nan());
+ assert_eq!(inf.ln(), inf);
+ assert!(neg_inf.ln().is_nan());
+ assert!((-2.3f32).ln().is_nan());
+ assert_eq!((-0.0f32).ln(), neg_inf);
+ assert_eq!(0.0f32.ln(), neg_inf);
+ assert_approx_eq!(4.0f32.ln(), 1.386294);
+}
+
+#[test]
+fn test_log() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_eq!(10.0f32.log(10.0), 1.0);
+ assert_approx_eq!(2.3f32.log(3.5), 0.664858);
+ assert_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0);
+ assert!(1.0f32.log(1.0).is_nan());
+ assert!(1.0f32.log(-13.9).is_nan());
+ assert!(nan.log(2.3).is_nan());
+ assert_eq!(inf.log(10.0), inf);
+ assert!(neg_inf.log(8.8).is_nan());
+ assert!((-2.3f32).log(0.1).is_nan());
+ assert_eq!((-0.0f32).log(2.0), neg_inf);
+ assert_eq!(0.0f32.log(7.0), neg_inf);
+}
+
+#[test]
+fn test_log2() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_approx_eq!(10.0f32.log2(), 3.321928);
+ assert_approx_eq!(2.3f32.log2(), 1.201634);
+ assert_approx_eq!(1.0f32.exp().log2(), 1.442695);
+ assert!(nan.log2().is_nan());
+ assert_eq!(inf.log2(), inf);
+ assert!(neg_inf.log2().is_nan());
+ assert!((-2.3f32).log2().is_nan());
+ assert_eq!((-0.0f32).log2(), neg_inf);
+ assert_eq!(0.0f32.log2(), neg_inf);
+}
+
+#[test]
+fn test_log10() {
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_eq!(10.0f32.log10(), 1.0);
+ assert_approx_eq!(2.3f32.log10(), 0.361728);
+ assert_approx_eq!(1.0f32.exp().log10(), 0.434294);
+ assert_eq!(1.0f32.log10(), 0.0);
+ assert!(nan.log10().is_nan());
+ assert_eq!(inf.log10(), inf);
+ assert!(neg_inf.log10().is_nan());
+ assert!((-2.3f32).log10().is_nan());
+ assert_eq!((-0.0f32).log10(), neg_inf);
+ assert_eq!(0.0f32.log10(), neg_inf);
+}
+
+#[test]
+fn test_to_degrees() {
+ let pi: f32 = consts::PI;
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_eq!(0.0f32.to_degrees(), 0.0);
+ assert_approx_eq!((-5.8f32).to_degrees(), -332.315521);
+ assert_eq!(pi.to_degrees(), 180.0);
+ assert!(nan.to_degrees().is_nan());
+ assert_eq!(inf.to_degrees(), inf);
+ assert_eq!(neg_inf.to_degrees(), neg_inf);
+ assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703);
+}
+
+#[test]
+fn test_to_radians() {
+ let pi: f32 = consts::PI;
+ let nan: f32 = f32::NAN;
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ assert_eq!(0.0f32.to_radians(), 0.0);
+ assert_approx_eq!(154.6f32.to_radians(), 2.698279);
+ assert_approx_eq!((-332.31f32).to_radians(), -5.799903);
+ assert_eq!(180.0f32.to_radians(), pi);
+ assert!(nan.to_radians().is_nan());
+ assert_eq!(inf.to_radians(), inf);
+ assert_eq!(neg_inf.to_radians(), neg_inf);
+}
+
+#[test]
+fn test_asinh() {
+ assert_eq!(0.0f32.asinh(), 0.0f32);
+ assert_eq!((-0.0f32).asinh(), -0.0f32);
+
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ let nan: f32 = f32::NAN;
+ assert_eq!(inf.asinh(), inf);
+ assert_eq!(neg_inf.asinh(), neg_inf);
+ assert!(nan.asinh().is_nan());
+ assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271
+ assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32);
+ assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32);
+ // regression test for the catastrophic cancellation fixed in 72486
+ assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32);
+}
+
+#[test]
+fn test_acosh() {
+ assert_eq!(1.0f32.acosh(), 0.0f32);
+ assert!(0.999f32.acosh().is_nan());
+
+ let inf: f32 = f32::INFINITY;
+ let neg_inf: f32 = f32::NEG_INFINITY;
+ let nan: f32 = f32::NAN;
+ assert_eq!(inf.acosh(), inf);
+ assert!(neg_inf.acosh().is_nan());
+ assert!(nan.acosh().is_nan());
+ assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32);
+ assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32);
+}
+
+#[test]
+fn test_atanh() {
+ assert_eq!(0.0f32.atanh(), 0.0f32);
+ assert_eq!((-0.0f32).atanh(), -0.0f32);
+
+ let inf32: f32 = f32::INFINITY;
+ let neg_inf32: f32 = f32::NEG_INFINITY;
+ assert_eq!(1.0f32.atanh(), inf32);
+ assert_eq!((-1.0f32).atanh(), neg_inf32);
+
+ assert!(2f64.atanh().atanh().is_nan());
+ assert!((-2f64).atanh().atanh().is_nan());
+
+ let inf64: f32 = f32::INFINITY;
+ let neg_inf64: f32 = f32::NEG_INFINITY;
+ let nan32: f32 = f32::NAN;
+ assert!(inf64.atanh().is_nan());
+ assert!(neg_inf64.atanh().is_nan());
+ assert!(nan32.atanh().is_nan());
+
+ assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32);
+ assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32);
+}
+
+#[test]
+fn test_real_consts() {
+ use super::consts;
+
+ let pi: f32 = consts::PI;
+ let frac_pi_2: f32 = consts::FRAC_PI_2;
+ let frac_pi_3: f32 = consts::FRAC_PI_3;
+ let frac_pi_4: f32 = consts::FRAC_PI_4;
+ let frac_pi_6: f32 = consts::FRAC_PI_6;
+ let frac_pi_8: f32 = consts::FRAC_PI_8;
+ let frac_1_pi: f32 = consts::FRAC_1_PI;
+ let frac_2_pi: f32 = consts::FRAC_2_PI;
+ let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI;
+ let sqrt2: f32 = consts::SQRT_2;
+ let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2;
+ let e: f32 = consts::E;
+ let log2_e: f32 = consts::LOG2_E;
+ let log10_e: f32 = consts::LOG10_E;
+ let ln_2: f32 = consts::LN_2;
+ let ln_10: f32 = consts::LN_10;
+
+ assert_approx_eq!(frac_pi_2, pi / 2f32);
+ assert_approx_eq!(frac_pi_3, pi / 3f32);
+ assert_approx_eq!(frac_pi_4, pi / 4f32);
+ assert_approx_eq!(frac_pi_6, pi / 6f32);
+ assert_approx_eq!(frac_pi_8, pi / 8f32);
+ assert_approx_eq!(frac_1_pi, 1f32 / pi);
+ assert_approx_eq!(frac_2_pi, 2f32 / pi);
+ assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt());
+ assert_approx_eq!(sqrt2, 2f32.sqrt());
+ assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt());
+ assert_approx_eq!(log2_e, e.log2());
+ assert_approx_eq!(log10_e, e.log10());
+ assert_approx_eq!(ln_2, 2f32.ln());
+ assert_approx_eq!(ln_10, 10f32.ln());
+}
+
+#[test]
+fn test_float_bits_conv() {
+ assert_eq!((1f32).to_bits(), 0x3f800000);
+ assert_eq!((12.5f32).to_bits(), 0x41480000);
+ assert_eq!((1337f32).to_bits(), 0x44a72000);
+ assert_eq!((-14.25f32).to_bits(), 0xc1640000);
+ assert_approx_eq!(f32::from_bits(0x3f800000), 1.0);
+ assert_approx_eq!(f32::from_bits(0x41480000), 12.5);
+ assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0);
+ assert_approx_eq!(f32::from_bits(0xc1640000), -14.25);
+
+ // Check that NaNs roundtrip their bits regardless of signaling-ness
+ // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+ let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA;
+ let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555;
+ assert!(f32::from_bits(masked_nan1).is_nan());
+ assert!(f32::from_bits(masked_nan2).is_nan());
+
+ 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() {
+ let _ = 1.0f32.clamp(3.0, 1.0);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_min_is_nan() {
+ let _ = 1.0f32.clamp(f32::NAN, 1.0);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_max_is_nan() {
+ let _ = 1.0f32.clamp(3.0, f32::NAN);
+}
+
+#[test]
+fn test_total_cmp() {
+ use core::cmp::Ordering;
+
+ fn quiet_bit_mask() -> u32 {
+ 1 << (f32::MANTISSA_DIGITS - 2)
+ }
+
+ fn min_subnorm() -> f32 {
+ f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0)
+ }
+
+ fn max_subnorm() -> f32 {
+ f32::MIN_POSITIVE - min_subnorm()
+ }
+
+ fn q_nan() -> f32 {
+ f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask())
+ }
+
+ fn s_nan() -> f32 {
+ f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42)
+ }
+
+ assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
+ assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
+ assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY));
+ assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX));
+ assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5));
+ assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0));
+ assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5));
+ assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5));
+ assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0));
+ assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0));
+ assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5));
+ assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0));
+ assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5));
+ assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5));
+ assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX));
+ assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY));
+ assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
+ assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
+
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
+ assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX));
+ assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5));
+ assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5));
+ assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0));
+ assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5));
+ assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
+ assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0));
+ assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5));
+ assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0));
+ assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5));
+ assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5));
+ assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX));
+ assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY));
+ assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan()));
+ assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
+
+ assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
+ assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan()));
+ assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY));
+ assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX));
+ assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5));
+ assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5));
+ assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0));
+ assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5));
+ assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0));
+ assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
+ assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5));
+ assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0));
+ assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5));
+ assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5));
+ assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX));
+ assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY));
+ assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
+
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
+
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
+}
diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs
index f09fc8d..bd094bd 100644
--- a/library/std/src/f64.rs
+++ b/library/std/src/f64.rs
@@ -11,6 +11,9 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
+#[cfg(test)]
+mod tests;
+
#[cfg(not(test))]
use crate::intrinsics;
#[cfg(not(test))]
@@ -936,762 +939,3 @@
}
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::f64::consts;
- use crate::num::FpCategory as Fp;
- use crate::num::*;
-
- #[test]
- fn test_num_f64() {
- test_num(10f64, 2f64);
- }
-
- #[test]
- fn test_min_nan() {
- assert_eq!(f64::NAN.min(2.0), 2.0);
- assert_eq!(2.0f64.min(f64::NAN), 2.0);
- }
-
- #[test]
- fn test_max_nan() {
- assert_eq!(f64::NAN.max(2.0), 2.0);
- assert_eq!(2.0f64.max(f64::NAN), 2.0);
- }
-
- #[test]
- fn test_nan() {
- let nan: f64 = f64::NAN;
- assert!(nan.is_nan());
- assert!(!nan.is_infinite());
- assert!(!nan.is_finite());
- assert!(!nan.is_normal());
- assert!(nan.is_sign_positive());
- assert!(!nan.is_sign_negative());
- assert_eq!(Fp::Nan, nan.classify());
- }
-
- #[test]
- fn test_infinity() {
- let inf: f64 = f64::INFINITY;
- assert!(inf.is_infinite());
- assert!(!inf.is_finite());
- assert!(inf.is_sign_positive());
- assert!(!inf.is_sign_negative());
- assert!(!inf.is_nan());
- assert!(!inf.is_normal());
- assert_eq!(Fp::Infinite, inf.classify());
- }
-
- #[test]
- fn test_neg_infinity() {
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert!(neg_inf.is_infinite());
- assert!(!neg_inf.is_finite());
- assert!(!neg_inf.is_sign_positive());
- assert!(neg_inf.is_sign_negative());
- assert!(!neg_inf.is_nan());
- assert!(!neg_inf.is_normal());
- assert_eq!(Fp::Infinite, neg_inf.classify());
- }
-
- #[test]
- fn test_zero() {
- let zero: f64 = 0.0f64;
- assert_eq!(0.0, zero);
- assert!(!zero.is_infinite());
- assert!(zero.is_finite());
- assert!(zero.is_sign_positive());
- assert!(!zero.is_sign_negative());
- assert!(!zero.is_nan());
- assert!(!zero.is_normal());
- assert_eq!(Fp::Zero, zero.classify());
- }
-
- #[test]
- fn test_neg_zero() {
- let neg_zero: f64 = -0.0;
- assert_eq!(0.0, neg_zero);
- assert!(!neg_zero.is_infinite());
- assert!(neg_zero.is_finite());
- assert!(!neg_zero.is_sign_positive());
- assert!(neg_zero.is_sign_negative());
- assert!(!neg_zero.is_nan());
- assert!(!neg_zero.is_normal());
- assert_eq!(Fp::Zero, neg_zero.classify());
- }
-
- #[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630
- #[test]
- fn test_one() {
- let one: f64 = 1.0f64;
- assert_eq!(1.0, one);
- assert!(!one.is_infinite());
- assert!(one.is_finite());
- assert!(one.is_sign_positive());
- assert!(!one.is_sign_negative());
- assert!(!one.is_nan());
- assert!(one.is_normal());
- assert_eq!(Fp::Normal, one.classify());
- }
-
- #[test]
- fn test_is_nan() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert!(nan.is_nan());
- assert!(!0.0f64.is_nan());
- assert!(!5.3f64.is_nan());
- assert!(!(-10.732f64).is_nan());
- assert!(!inf.is_nan());
- assert!(!neg_inf.is_nan());
- }
-
- #[test]
- fn test_is_infinite() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert!(!nan.is_infinite());
- assert!(inf.is_infinite());
- assert!(neg_inf.is_infinite());
- assert!(!0.0f64.is_infinite());
- assert!(!42.8f64.is_infinite());
- assert!(!(-109.2f64).is_infinite());
- }
-
- #[test]
- fn test_is_finite() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert!(!nan.is_finite());
- assert!(!inf.is_finite());
- assert!(!neg_inf.is_finite());
- assert!(0.0f64.is_finite());
- assert!(42.8f64.is_finite());
- assert!((-109.2f64).is_finite());
- }
-
- #[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630
- #[test]
- fn test_is_normal() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- let zero: f64 = 0.0f64;
- let neg_zero: f64 = -0.0;
- assert!(!nan.is_normal());
- assert!(!inf.is_normal());
- assert!(!neg_inf.is_normal());
- assert!(!zero.is_normal());
- assert!(!neg_zero.is_normal());
- assert!(1f64.is_normal());
- assert!(1e-307f64.is_normal());
- assert!(!1e-308f64.is_normal());
- }
-
- #[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630
- #[test]
- fn test_classify() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- let zero: f64 = 0.0f64;
- let neg_zero: f64 = -0.0;
- assert_eq!(nan.classify(), Fp::Nan);
- assert_eq!(inf.classify(), Fp::Infinite);
- assert_eq!(neg_inf.classify(), Fp::Infinite);
- assert_eq!(zero.classify(), Fp::Zero);
- assert_eq!(neg_zero.classify(), Fp::Zero);
- assert_eq!(1e-307f64.classify(), Fp::Normal);
- assert_eq!(1e-308f64.classify(), Fp::Subnormal);
- }
-
- #[test]
- fn test_floor() {
- assert_approx_eq!(1.0f64.floor(), 1.0f64);
- assert_approx_eq!(1.3f64.floor(), 1.0f64);
- assert_approx_eq!(1.5f64.floor(), 1.0f64);
- assert_approx_eq!(1.7f64.floor(), 1.0f64);
- assert_approx_eq!(0.0f64.floor(), 0.0f64);
- assert_approx_eq!((-0.0f64).floor(), -0.0f64);
- assert_approx_eq!((-1.0f64).floor(), -1.0f64);
- assert_approx_eq!((-1.3f64).floor(), -2.0f64);
- assert_approx_eq!((-1.5f64).floor(), -2.0f64);
- assert_approx_eq!((-1.7f64).floor(), -2.0f64);
- }
-
- #[test]
- fn test_ceil() {
- assert_approx_eq!(1.0f64.ceil(), 1.0f64);
- assert_approx_eq!(1.3f64.ceil(), 2.0f64);
- assert_approx_eq!(1.5f64.ceil(), 2.0f64);
- assert_approx_eq!(1.7f64.ceil(), 2.0f64);
- assert_approx_eq!(0.0f64.ceil(), 0.0f64);
- assert_approx_eq!((-0.0f64).ceil(), -0.0f64);
- assert_approx_eq!((-1.0f64).ceil(), -1.0f64);
- assert_approx_eq!((-1.3f64).ceil(), -1.0f64);
- assert_approx_eq!((-1.5f64).ceil(), -1.0f64);
- assert_approx_eq!((-1.7f64).ceil(), -1.0f64);
- }
-
- #[test]
- fn test_round() {
- assert_approx_eq!(1.0f64.round(), 1.0f64);
- assert_approx_eq!(1.3f64.round(), 1.0f64);
- assert_approx_eq!(1.5f64.round(), 2.0f64);
- assert_approx_eq!(1.7f64.round(), 2.0f64);
- assert_approx_eq!(0.0f64.round(), 0.0f64);
- assert_approx_eq!((-0.0f64).round(), -0.0f64);
- assert_approx_eq!((-1.0f64).round(), -1.0f64);
- assert_approx_eq!((-1.3f64).round(), -1.0f64);
- assert_approx_eq!((-1.5f64).round(), -2.0f64);
- assert_approx_eq!((-1.7f64).round(), -2.0f64);
- }
-
- #[test]
- fn test_trunc() {
- assert_approx_eq!(1.0f64.trunc(), 1.0f64);
- assert_approx_eq!(1.3f64.trunc(), 1.0f64);
- assert_approx_eq!(1.5f64.trunc(), 1.0f64);
- assert_approx_eq!(1.7f64.trunc(), 1.0f64);
- assert_approx_eq!(0.0f64.trunc(), 0.0f64);
- assert_approx_eq!((-0.0f64).trunc(), -0.0f64);
- assert_approx_eq!((-1.0f64).trunc(), -1.0f64);
- assert_approx_eq!((-1.3f64).trunc(), -1.0f64);
- assert_approx_eq!((-1.5f64).trunc(), -1.0f64);
- assert_approx_eq!((-1.7f64).trunc(), -1.0f64);
- }
-
- #[test]
- fn test_fract() {
- assert_approx_eq!(1.0f64.fract(), 0.0f64);
- assert_approx_eq!(1.3f64.fract(), 0.3f64);
- assert_approx_eq!(1.5f64.fract(), 0.5f64);
- assert_approx_eq!(1.7f64.fract(), 0.7f64);
- assert_approx_eq!(0.0f64.fract(), 0.0f64);
- assert_approx_eq!((-0.0f64).fract(), -0.0f64);
- assert_approx_eq!((-1.0f64).fract(), -0.0f64);
- assert_approx_eq!((-1.3f64).fract(), -0.3f64);
- assert_approx_eq!((-1.5f64).fract(), -0.5f64);
- assert_approx_eq!((-1.7f64).fract(), -0.7f64);
- }
-
- #[test]
- fn test_abs() {
- assert_eq!(f64::INFINITY.abs(), f64::INFINITY);
- assert_eq!(1f64.abs(), 1f64);
- assert_eq!(0f64.abs(), 0f64);
- assert_eq!((-0f64).abs(), 0f64);
- assert_eq!((-1f64).abs(), 1f64);
- assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY);
- assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64);
- assert!(f64::NAN.abs().is_nan());
- }
-
- #[test]
- fn test_signum() {
- assert_eq!(f64::INFINITY.signum(), 1f64);
- assert_eq!(1f64.signum(), 1f64);
- assert_eq!(0f64.signum(), 1f64);
- assert_eq!((-0f64).signum(), -1f64);
- assert_eq!((-1f64).signum(), -1f64);
- assert_eq!(f64::NEG_INFINITY.signum(), -1f64);
- assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64);
- assert!(f64::NAN.signum().is_nan());
- }
-
- #[test]
- fn test_is_sign_positive() {
- assert!(f64::INFINITY.is_sign_positive());
- assert!(1f64.is_sign_positive());
- assert!(0f64.is_sign_positive());
- assert!(!(-0f64).is_sign_positive());
- assert!(!(-1f64).is_sign_positive());
- assert!(!f64::NEG_INFINITY.is_sign_positive());
- assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive());
- assert!(f64::NAN.is_sign_positive());
- assert!(!(-f64::NAN).is_sign_positive());
- }
-
- #[test]
- fn test_is_sign_negative() {
- assert!(!f64::INFINITY.is_sign_negative());
- assert!(!1f64.is_sign_negative());
- assert!(!0f64.is_sign_negative());
- assert!((-0f64).is_sign_negative());
- assert!((-1f64).is_sign_negative());
- assert!(f64::NEG_INFINITY.is_sign_negative());
- assert!((1f64 / f64::NEG_INFINITY).is_sign_negative());
- assert!(!f64::NAN.is_sign_negative());
- assert!((-f64::NAN).is_sign_negative());
- }
-
- #[test]
- fn test_mul_add() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05);
- assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65);
- assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2);
- assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6);
- assert!(nan.mul_add(7.8, 9.0).is_nan());
- assert_eq!(inf.mul_add(7.8, 9.0), inf);
- assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
- assert_eq!(8.9f64.mul_add(inf, 3.2), inf);
- assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf);
- }
-
- #[test]
- fn test_recip() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_eq!(1.0f64.recip(), 1.0);
- assert_eq!(2.0f64.recip(), 0.5);
- assert_eq!((-0.4f64).recip(), -2.5);
- assert_eq!(0.0f64.recip(), inf);
- assert!(nan.recip().is_nan());
- assert_eq!(inf.recip(), 0.0);
- assert_eq!(neg_inf.recip(), 0.0);
- }
-
- #[test]
- fn test_powi() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_eq!(1.0f64.powi(1), 1.0);
- assert_approx_eq!((-3.1f64).powi(2), 9.61);
- assert_approx_eq!(5.9f64.powi(-2), 0.028727);
- assert_eq!(8.3f64.powi(0), 1.0);
- assert!(nan.powi(2).is_nan());
- assert_eq!(inf.powi(3), inf);
- assert_eq!(neg_inf.powi(2), inf);
- }
-
- #[test]
- fn test_powf() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_eq!(1.0f64.powf(1.0), 1.0);
- assert_approx_eq!(3.4f64.powf(4.5), 246.408183);
- assert_approx_eq!(2.7f64.powf(-3.2), 0.041652);
- assert_approx_eq!((-3.1f64).powf(2.0), 9.61);
- assert_approx_eq!(5.9f64.powf(-2.0), 0.028727);
- assert_eq!(8.3f64.powf(0.0), 1.0);
- assert!(nan.powf(2.0).is_nan());
- assert_eq!(inf.powf(2.0), inf);
- assert_eq!(neg_inf.powf(3.0), neg_inf);
- }
-
- #[test]
- fn test_sqrt_domain() {
- assert!(f64::NAN.sqrt().is_nan());
- assert!(f64::NEG_INFINITY.sqrt().is_nan());
- assert!((-1.0f64).sqrt().is_nan());
- assert_eq!((-0.0f64).sqrt(), -0.0);
- assert_eq!(0.0f64.sqrt(), 0.0);
- assert_eq!(1.0f64.sqrt(), 1.0);
- assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY);
- }
-
- #[test]
- fn test_exp() {
- assert_eq!(1.0, 0.0f64.exp());
- assert_approx_eq!(2.718282, 1.0f64.exp());
- assert_approx_eq!(148.413159, 5.0f64.exp());
-
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- let nan: f64 = f64::NAN;
- assert_eq!(inf, inf.exp());
- assert_eq!(0.0, neg_inf.exp());
- assert!(nan.exp().is_nan());
- }
-
- #[test]
- fn test_exp2() {
- assert_eq!(32.0, 5.0f64.exp2());
- assert_eq!(1.0, 0.0f64.exp2());
-
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- let nan: f64 = f64::NAN;
- assert_eq!(inf, inf.exp2());
- assert_eq!(0.0, neg_inf.exp2());
- assert!(nan.exp2().is_nan());
- }
-
- #[test]
- fn test_ln() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_approx_eq!(1.0f64.exp().ln(), 1.0);
- assert!(nan.ln().is_nan());
- assert_eq!(inf.ln(), inf);
- assert!(neg_inf.ln().is_nan());
- assert!((-2.3f64).ln().is_nan());
- assert_eq!((-0.0f64).ln(), neg_inf);
- assert_eq!(0.0f64.ln(), neg_inf);
- assert_approx_eq!(4.0f64.ln(), 1.386294);
- }
-
- #[test]
- fn test_log() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_eq!(10.0f64.log(10.0), 1.0);
- assert_approx_eq!(2.3f64.log(3.5), 0.664858);
- assert_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0);
- assert!(1.0f64.log(1.0).is_nan());
- assert!(1.0f64.log(-13.9).is_nan());
- assert!(nan.log(2.3).is_nan());
- assert_eq!(inf.log(10.0), inf);
- assert!(neg_inf.log(8.8).is_nan());
- assert!((-2.3f64).log(0.1).is_nan());
- assert_eq!((-0.0f64).log(2.0), neg_inf);
- assert_eq!(0.0f64.log(7.0), neg_inf);
- }
-
- #[test]
- fn test_log2() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_approx_eq!(10.0f64.log2(), 3.321928);
- assert_approx_eq!(2.3f64.log2(), 1.201634);
- assert_approx_eq!(1.0f64.exp().log2(), 1.442695);
- assert!(nan.log2().is_nan());
- assert_eq!(inf.log2(), inf);
- assert!(neg_inf.log2().is_nan());
- assert!((-2.3f64).log2().is_nan());
- assert_eq!((-0.0f64).log2(), neg_inf);
- assert_eq!(0.0f64.log2(), neg_inf);
- }
-
- #[test]
- fn test_log10() {
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_eq!(10.0f64.log10(), 1.0);
- assert_approx_eq!(2.3f64.log10(), 0.361728);
- assert_approx_eq!(1.0f64.exp().log10(), 0.434294);
- assert_eq!(1.0f64.log10(), 0.0);
- assert!(nan.log10().is_nan());
- assert_eq!(inf.log10(), inf);
- assert!(neg_inf.log10().is_nan());
- assert!((-2.3f64).log10().is_nan());
- assert_eq!((-0.0f64).log10(), neg_inf);
- assert_eq!(0.0f64.log10(), neg_inf);
- }
-
- #[test]
- fn test_to_degrees() {
- let pi: f64 = consts::PI;
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_eq!(0.0f64.to_degrees(), 0.0);
- assert_approx_eq!((-5.8f64).to_degrees(), -332.315521);
- assert_eq!(pi.to_degrees(), 180.0);
- assert!(nan.to_degrees().is_nan());
- assert_eq!(inf.to_degrees(), inf);
- assert_eq!(neg_inf.to_degrees(), neg_inf);
- }
-
- #[test]
- fn test_to_radians() {
- let pi: f64 = consts::PI;
- let nan: f64 = f64::NAN;
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- assert_eq!(0.0f64.to_radians(), 0.0);
- assert_approx_eq!(154.6f64.to_radians(), 2.698279);
- assert_approx_eq!((-332.31f64).to_radians(), -5.799903);
- assert_eq!(180.0f64.to_radians(), pi);
- assert!(nan.to_radians().is_nan());
- assert_eq!(inf.to_radians(), inf);
- assert_eq!(neg_inf.to_radians(), neg_inf);
- }
-
- #[test]
- fn test_asinh() {
- assert_eq!(0.0f64.asinh(), 0.0f64);
- assert_eq!((-0.0f64).asinh(), -0.0f64);
-
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- let nan: f64 = f64::NAN;
- assert_eq!(inf.asinh(), inf);
- assert_eq!(neg_inf.asinh(), neg_inf);
- assert!(nan.asinh().is_nan());
- assert!((-0.0f64).asinh().is_sign_negative());
- // issue 63271
- assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64);
- assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64);
- // regression test for the catastrophic cancellation fixed in 72486
- assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083);
- }
-
- #[test]
- fn test_acosh() {
- assert_eq!(1.0f64.acosh(), 0.0f64);
- assert!(0.999f64.acosh().is_nan());
-
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- let nan: f64 = f64::NAN;
- assert_eq!(inf.acosh(), inf);
- assert!(neg_inf.acosh().is_nan());
- assert!(nan.acosh().is_nan());
- assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64);
- assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64);
- }
-
- #[test]
- fn test_atanh() {
- assert_eq!(0.0f64.atanh(), 0.0f64);
- assert_eq!((-0.0f64).atanh(), -0.0f64);
-
- let inf: f64 = f64::INFINITY;
- let neg_inf: f64 = f64::NEG_INFINITY;
- let nan: f64 = f64::NAN;
- assert_eq!(1.0f64.atanh(), inf);
- assert_eq!((-1.0f64).atanh(), neg_inf);
- assert!(2f64.atanh().atanh().is_nan());
- assert!((-2f64).atanh().atanh().is_nan());
- assert!(inf.atanh().is_nan());
- assert!(neg_inf.atanh().is_nan());
- assert!(nan.atanh().is_nan());
- assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64);
- assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64);
- }
-
- #[test]
- fn test_real_consts() {
- use super::consts;
- let pi: f64 = consts::PI;
- let frac_pi_2: f64 = consts::FRAC_PI_2;
- let frac_pi_3: f64 = consts::FRAC_PI_3;
- let frac_pi_4: f64 = consts::FRAC_PI_4;
- let frac_pi_6: f64 = consts::FRAC_PI_6;
- let frac_pi_8: f64 = consts::FRAC_PI_8;
- let frac_1_pi: f64 = consts::FRAC_1_PI;
- let frac_2_pi: f64 = consts::FRAC_2_PI;
- let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI;
- let sqrt2: f64 = consts::SQRT_2;
- let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2;
- let e: f64 = consts::E;
- let log2_e: f64 = consts::LOG2_E;
- let log10_e: f64 = consts::LOG10_E;
- let ln_2: f64 = consts::LN_2;
- let ln_10: f64 = consts::LN_10;
-
- assert_approx_eq!(frac_pi_2, pi / 2f64);
- assert_approx_eq!(frac_pi_3, pi / 3f64);
- assert_approx_eq!(frac_pi_4, pi / 4f64);
- assert_approx_eq!(frac_pi_6, pi / 6f64);
- assert_approx_eq!(frac_pi_8, pi / 8f64);
- assert_approx_eq!(frac_1_pi, 1f64 / pi);
- assert_approx_eq!(frac_2_pi, 2f64 / pi);
- assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt());
- assert_approx_eq!(sqrt2, 2f64.sqrt());
- assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt());
- assert_approx_eq!(log2_e, e.log2());
- assert_approx_eq!(log10_e, e.log10());
- assert_approx_eq!(ln_2, 2f64.ln());
- assert_approx_eq!(ln_10, 10f64.ln());
- }
-
- #[test]
- fn test_float_bits_conv() {
- assert_eq!((1f64).to_bits(), 0x3ff0000000000000);
- assert_eq!((12.5f64).to_bits(), 0x4029000000000000);
- assert_eq!((1337f64).to_bits(), 0x4094e40000000000);
- assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000);
- assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0);
- assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5);
- assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0);
- assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25);
-
- // Check that NaNs roundtrip their bits regardless of signaling-ness
- // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
- let masked_nan1 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
- let masked_nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
- assert!(f64::from_bits(masked_nan1).is_nan());
- assert!(f64::from_bits(masked_nan2).is_nan());
-
- 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() {
- let _ = 1.0f64.clamp(3.0, 1.0);
- }
-
- #[test]
- #[should_panic]
- fn test_clamp_min_is_nan() {
- let _ = 1.0f64.clamp(f64::NAN, 1.0);
- }
-
- #[test]
- #[should_panic]
- fn test_clamp_max_is_nan() {
- let _ = 1.0f64.clamp(3.0, f64::NAN);
- }
-
- #[test]
- fn test_total_cmp() {
- use core::cmp::Ordering;
-
- fn quiet_bit_mask() -> u64 {
- 1 << (f64::MANTISSA_DIGITS - 2)
- }
-
- fn min_subnorm() -> f64 {
- f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0)
- }
-
- fn max_subnorm() -> f64 {
- f64::MIN_POSITIVE - min_subnorm()
- }
-
- fn q_nan() -> f64 {
- f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask())
- }
-
- fn s_nan() -> f64 {
- f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42)
- }
-
- assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
- assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
- assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY));
- assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX));
- assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5));
- assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0));
- assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5));
- assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5));
- assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE));
- assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0));
- assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0));
- assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE));
- assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5));
- assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0));
- assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5));
- assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5));
- assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX));
- assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY));
- assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
- assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
-
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
- assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX));
- assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5));
- assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5));
- assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0));
- assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5));
- assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
- assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0));
- assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE));
- assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5));
- assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0));
- assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5));
- assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5));
- assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX));
- assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY));
- assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan()));
- assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
-
- assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
- assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan()));
- assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY));
- assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX));
- assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5));
- assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5));
- assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0));
- assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5));
- assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE));
- assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0));
- assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
- assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE));
- assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5));
- assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0));
- assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5));
- assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5));
- assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX));
- assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY));
- assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
-
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY));
- assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
-
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY));
- assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
- }
-}
diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs
new file mode 100644
index 0000000..5c163cf
--- /dev/null
+++ b/library/std/src/f64/tests.rs
@@ -0,0 +1,755 @@
+use crate::f64::consts;
+use crate::num::FpCategory as Fp;
+use crate::num::*;
+
+#[test]
+fn test_num_f64() {
+ test_num(10f64, 2f64);
+}
+
+#[test]
+fn test_min_nan() {
+ assert_eq!(f64::NAN.min(2.0), 2.0);
+ assert_eq!(2.0f64.min(f64::NAN), 2.0);
+}
+
+#[test]
+fn test_max_nan() {
+ assert_eq!(f64::NAN.max(2.0), 2.0);
+ assert_eq!(2.0f64.max(f64::NAN), 2.0);
+}
+
+#[test]
+fn test_nan() {
+ let nan: f64 = f64::NAN;
+ assert!(nan.is_nan());
+ assert!(!nan.is_infinite());
+ assert!(!nan.is_finite());
+ assert!(!nan.is_normal());
+ assert!(nan.is_sign_positive());
+ assert!(!nan.is_sign_negative());
+ assert_eq!(Fp::Nan, nan.classify());
+}
+
+#[test]
+fn test_infinity() {
+ let inf: f64 = f64::INFINITY;
+ assert!(inf.is_infinite());
+ assert!(!inf.is_finite());
+ assert!(inf.is_sign_positive());
+ assert!(!inf.is_sign_negative());
+ assert!(!inf.is_nan());
+ assert!(!inf.is_normal());
+ assert_eq!(Fp::Infinite, inf.classify());
+}
+
+#[test]
+fn test_neg_infinity() {
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert!(neg_inf.is_infinite());
+ assert!(!neg_inf.is_finite());
+ assert!(!neg_inf.is_sign_positive());
+ assert!(neg_inf.is_sign_negative());
+ assert!(!neg_inf.is_nan());
+ assert!(!neg_inf.is_normal());
+ assert_eq!(Fp::Infinite, neg_inf.classify());
+}
+
+#[test]
+fn test_zero() {
+ let zero: f64 = 0.0f64;
+ assert_eq!(0.0, zero);
+ assert!(!zero.is_infinite());
+ assert!(zero.is_finite());
+ assert!(zero.is_sign_positive());
+ assert!(!zero.is_sign_negative());
+ assert!(!zero.is_nan());
+ assert!(!zero.is_normal());
+ assert_eq!(Fp::Zero, zero.classify());
+}
+
+#[test]
+fn test_neg_zero() {
+ let neg_zero: f64 = -0.0;
+ assert_eq!(0.0, neg_zero);
+ assert!(!neg_zero.is_infinite());
+ assert!(neg_zero.is_finite());
+ assert!(!neg_zero.is_sign_positive());
+ assert!(neg_zero.is_sign_negative());
+ assert!(!neg_zero.is_nan());
+ assert!(!neg_zero.is_normal());
+ assert_eq!(Fp::Zero, neg_zero.classify());
+}
+
+#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630
+#[test]
+fn test_one() {
+ let one: f64 = 1.0f64;
+ assert_eq!(1.0, one);
+ assert!(!one.is_infinite());
+ assert!(one.is_finite());
+ assert!(one.is_sign_positive());
+ assert!(!one.is_sign_negative());
+ assert!(!one.is_nan());
+ assert!(one.is_normal());
+ assert_eq!(Fp::Normal, one.classify());
+}
+
+#[test]
+fn test_is_nan() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert!(nan.is_nan());
+ assert!(!0.0f64.is_nan());
+ assert!(!5.3f64.is_nan());
+ assert!(!(-10.732f64).is_nan());
+ assert!(!inf.is_nan());
+ assert!(!neg_inf.is_nan());
+}
+
+#[test]
+fn test_is_infinite() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert!(!nan.is_infinite());
+ assert!(inf.is_infinite());
+ assert!(neg_inf.is_infinite());
+ assert!(!0.0f64.is_infinite());
+ assert!(!42.8f64.is_infinite());
+ assert!(!(-109.2f64).is_infinite());
+}
+
+#[test]
+fn test_is_finite() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert!(!nan.is_finite());
+ assert!(!inf.is_finite());
+ assert!(!neg_inf.is_finite());
+ assert!(0.0f64.is_finite());
+ assert!(42.8f64.is_finite());
+ assert!((-109.2f64).is_finite());
+}
+
+#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630
+#[test]
+fn test_is_normal() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ let zero: f64 = 0.0f64;
+ let neg_zero: f64 = -0.0;
+ assert!(!nan.is_normal());
+ assert!(!inf.is_normal());
+ assert!(!neg_inf.is_normal());
+ assert!(!zero.is_normal());
+ assert!(!neg_zero.is_normal());
+ assert!(1f64.is_normal());
+ assert!(1e-307f64.is_normal());
+ assert!(!1e-308f64.is_normal());
+}
+
+#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630
+#[test]
+fn test_classify() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ let zero: f64 = 0.0f64;
+ let neg_zero: f64 = -0.0;
+ assert_eq!(nan.classify(), Fp::Nan);
+ assert_eq!(inf.classify(), Fp::Infinite);
+ assert_eq!(neg_inf.classify(), Fp::Infinite);
+ assert_eq!(zero.classify(), Fp::Zero);
+ assert_eq!(neg_zero.classify(), Fp::Zero);
+ assert_eq!(1e-307f64.classify(), Fp::Normal);
+ assert_eq!(1e-308f64.classify(), Fp::Subnormal);
+}
+
+#[test]
+fn test_floor() {
+ assert_approx_eq!(1.0f64.floor(), 1.0f64);
+ assert_approx_eq!(1.3f64.floor(), 1.0f64);
+ assert_approx_eq!(1.5f64.floor(), 1.0f64);
+ assert_approx_eq!(1.7f64.floor(), 1.0f64);
+ assert_approx_eq!(0.0f64.floor(), 0.0f64);
+ assert_approx_eq!((-0.0f64).floor(), -0.0f64);
+ assert_approx_eq!((-1.0f64).floor(), -1.0f64);
+ assert_approx_eq!((-1.3f64).floor(), -2.0f64);
+ assert_approx_eq!((-1.5f64).floor(), -2.0f64);
+ assert_approx_eq!((-1.7f64).floor(), -2.0f64);
+}
+
+#[test]
+fn test_ceil() {
+ assert_approx_eq!(1.0f64.ceil(), 1.0f64);
+ assert_approx_eq!(1.3f64.ceil(), 2.0f64);
+ assert_approx_eq!(1.5f64.ceil(), 2.0f64);
+ assert_approx_eq!(1.7f64.ceil(), 2.0f64);
+ assert_approx_eq!(0.0f64.ceil(), 0.0f64);
+ assert_approx_eq!((-0.0f64).ceil(), -0.0f64);
+ assert_approx_eq!((-1.0f64).ceil(), -1.0f64);
+ assert_approx_eq!((-1.3f64).ceil(), -1.0f64);
+ assert_approx_eq!((-1.5f64).ceil(), -1.0f64);
+ assert_approx_eq!((-1.7f64).ceil(), -1.0f64);
+}
+
+#[test]
+fn test_round() {
+ assert_approx_eq!(1.0f64.round(), 1.0f64);
+ assert_approx_eq!(1.3f64.round(), 1.0f64);
+ assert_approx_eq!(1.5f64.round(), 2.0f64);
+ assert_approx_eq!(1.7f64.round(), 2.0f64);
+ assert_approx_eq!(0.0f64.round(), 0.0f64);
+ assert_approx_eq!((-0.0f64).round(), -0.0f64);
+ assert_approx_eq!((-1.0f64).round(), -1.0f64);
+ assert_approx_eq!((-1.3f64).round(), -1.0f64);
+ assert_approx_eq!((-1.5f64).round(), -2.0f64);
+ assert_approx_eq!((-1.7f64).round(), -2.0f64);
+}
+
+#[test]
+fn test_trunc() {
+ assert_approx_eq!(1.0f64.trunc(), 1.0f64);
+ assert_approx_eq!(1.3f64.trunc(), 1.0f64);
+ assert_approx_eq!(1.5f64.trunc(), 1.0f64);
+ assert_approx_eq!(1.7f64.trunc(), 1.0f64);
+ assert_approx_eq!(0.0f64.trunc(), 0.0f64);
+ assert_approx_eq!((-0.0f64).trunc(), -0.0f64);
+ assert_approx_eq!((-1.0f64).trunc(), -1.0f64);
+ assert_approx_eq!((-1.3f64).trunc(), -1.0f64);
+ assert_approx_eq!((-1.5f64).trunc(), -1.0f64);
+ assert_approx_eq!((-1.7f64).trunc(), -1.0f64);
+}
+
+#[test]
+fn test_fract() {
+ assert_approx_eq!(1.0f64.fract(), 0.0f64);
+ assert_approx_eq!(1.3f64.fract(), 0.3f64);
+ assert_approx_eq!(1.5f64.fract(), 0.5f64);
+ assert_approx_eq!(1.7f64.fract(), 0.7f64);
+ assert_approx_eq!(0.0f64.fract(), 0.0f64);
+ assert_approx_eq!((-0.0f64).fract(), -0.0f64);
+ assert_approx_eq!((-1.0f64).fract(), -0.0f64);
+ assert_approx_eq!((-1.3f64).fract(), -0.3f64);
+ assert_approx_eq!((-1.5f64).fract(), -0.5f64);
+ assert_approx_eq!((-1.7f64).fract(), -0.7f64);
+}
+
+#[test]
+fn test_abs() {
+ assert_eq!(f64::INFINITY.abs(), f64::INFINITY);
+ assert_eq!(1f64.abs(), 1f64);
+ assert_eq!(0f64.abs(), 0f64);
+ assert_eq!((-0f64).abs(), 0f64);
+ assert_eq!((-1f64).abs(), 1f64);
+ assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY);
+ assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64);
+ assert!(f64::NAN.abs().is_nan());
+}
+
+#[test]
+fn test_signum() {
+ assert_eq!(f64::INFINITY.signum(), 1f64);
+ assert_eq!(1f64.signum(), 1f64);
+ assert_eq!(0f64.signum(), 1f64);
+ assert_eq!((-0f64).signum(), -1f64);
+ assert_eq!((-1f64).signum(), -1f64);
+ assert_eq!(f64::NEG_INFINITY.signum(), -1f64);
+ assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64);
+ assert!(f64::NAN.signum().is_nan());
+}
+
+#[test]
+fn test_is_sign_positive() {
+ assert!(f64::INFINITY.is_sign_positive());
+ assert!(1f64.is_sign_positive());
+ assert!(0f64.is_sign_positive());
+ assert!(!(-0f64).is_sign_positive());
+ assert!(!(-1f64).is_sign_positive());
+ assert!(!f64::NEG_INFINITY.is_sign_positive());
+ assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive());
+ assert!(f64::NAN.is_sign_positive());
+ assert!(!(-f64::NAN).is_sign_positive());
+}
+
+#[test]
+fn test_is_sign_negative() {
+ assert!(!f64::INFINITY.is_sign_negative());
+ assert!(!1f64.is_sign_negative());
+ assert!(!0f64.is_sign_negative());
+ assert!((-0f64).is_sign_negative());
+ assert!((-1f64).is_sign_negative());
+ assert!(f64::NEG_INFINITY.is_sign_negative());
+ assert!((1f64 / f64::NEG_INFINITY).is_sign_negative());
+ assert!(!f64::NAN.is_sign_negative());
+ assert!((-f64::NAN).is_sign_negative());
+}
+
+#[test]
+fn test_mul_add() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05);
+ assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65);
+ assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2);
+ assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6);
+ assert!(nan.mul_add(7.8, 9.0).is_nan());
+ assert_eq!(inf.mul_add(7.8, 9.0), inf);
+ assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
+ assert_eq!(8.9f64.mul_add(inf, 3.2), inf);
+ assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf);
+}
+
+#[test]
+fn test_recip() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_eq!(1.0f64.recip(), 1.0);
+ assert_eq!(2.0f64.recip(), 0.5);
+ assert_eq!((-0.4f64).recip(), -2.5);
+ assert_eq!(0.0f64.recip(), inf);
+ assert!(nan.recip().is_nan());
+ assert_eq!(inf.recip(), 0.0);
+ assert_eq!(neg_inf.recip(), 0.0);
+}
+
+#[test]
+fn test_powi() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_eq!(1.0f64.powi(1), 1.0);
+ assert_approx_eq!((-3.1f64).powi(2), 9.61);
+ assert_approx_eq!(5.9f64.powi(-2), 0.028727);
+ assert_eq!(8.3f64.powi(0), 1.0);
+ assert!(nan.powi(2).is_nan());
+ assert_eq!(inf.powi(3), inf);
+ assert_eq!(neg_inf.powi(2), inf);
+}
+
+#[test]
+fn test_powf() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_eq!(1.0f64.powf(1.0), 1.0);
+ assert_approx_eq!(3.4f64.powf(4.5), 246.408183);
+ assert_approx_eq!(2.7f64.powf(-3.2), 0.041652);
+ assert_approx_eq!((-3.1f64).powf(2.0), 9.61);
+ assert_approx_eq!(5.9f64.powf(-2.0), 0.028727);
+ assert_eq!(8.3f64.powf(0.0), 1.0);
+ assert!(nan.powf(2.0).is_nan());
+ assert_eq!(inf.powf(2.0), inf);
+ assert_eq!(neg_inf.powf(3.0), neg_inf);
+}
+
+#[test]
+fn test_sqrt_domain() {
+ assert!(f64::NAN.sqrt().is_nan());
+ assert!(f64::NEG_INFINITY.sqrt().is_nan());
+ assert!((-1.0f64).sqrt().is_nan());
+ assert_eq!((-0.0f64).sqrt(), -0.0);
+ assert_eq!(0.0f64.sqrt(), 0.0);
+ assert_eq!(1.0f64.sqrt(), 1.0);
+ assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY);
+}
+
+#[test]
+fn test_exp() {
+ assert_eq!(1.0, 0.0f64.exp());
+ assert_approx_eq!(2.718282, 1.0f64.exp());
+ assert_approx_eq!(148.413159, 5.0f64.exp());
+
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ let nan: f64 = f64::NAN;
+ assert_eq!(inf, inf.exp());
+ assert_eq!(0.0, neg_inf.exp());
+ assert!(nan.exp().is_nan());
+}
+
+#[test]
+fn test_exp2() {
+ assert_eq!(32.0, 5.0f64.exp2());
+ assert_eq!(1.0, 0.0f64.exp2());
+
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ let nan: f64 = f64::NAN;
+ assert_eq!(inf, inf.exp2());
+ assert_eq!(0.0, neg_inf.exp2());
+ assert!(nan.exp2().is_nan());
+}
+
+#[test]
+fn test_ln() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_approx_eq!(1.0f64.exp().ln(), 1.0);
+ assert!(nan.ln().is_nan());
+ assert_eq!(inf.ln(), inf);
+ assert!(neg_inf.ln().is_nan());
+ assert!((-2.3f64).ln().is_nan());
+ assert_eq!((-0.0f64).ln(), neg_inf);
+ assert_eq!(0.0f64.ln(), neg_inf);
+ assert_approx_eq!(4.0f64.ln(), 1.386294);
+}
+
+#[test]
+fn test_log() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_eq!(10.0f64.log(10.0), 1.0);
+ assert_approx_eq!(2.3f64.log(3.5), 0.664858);
+ assert_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0);
+ assert!(1.0f64.log(1.0).is_nan());
+ assert!(1.0f64.log(-13.9).is_nan());
+ assert!(nan.log(2.3).is_nan());
+ assert_eq!(inf.log(10.0), inf);
+ assert!(neg_inf.log(8.8).is_nan());
+ assert!((-2.3f64).log(0.1).is_nan());
+ assert_eq!((-0.0f64).log(2.0), neg_inf);
+ assert_eq!(0.0f64.log(7.0), neg_inf);
+}
+
+#[test]
+fn test_log2() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_approx_eq!(10.0f64.log2(), 3.321928);
+ assert_approx_eq!(2.3f64.log2(), 1.201634);
+ assert_approx_eq!(1.0f64.exp().log2(), 1.442695);
+ assert!(nan.log2().is_nan());
+ assert_eq!(inf.log2(), inf);
+ assert!(neg_inf.log2().is_nan());
+ assert!((-2.3f64).log2().is_nan());
+ assert_eq!((-0.0f64).log2(), neg_inf);
+ assert_eq!(0.0f64.log2(), neg_inf);
+}
+
+#[test]
+fn test_log10() {
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_eq!(10.0f64.log10(), 1.0);
+ assert_approx_eq!(2.3f64.log10(), 0.361728);
+ assert_approx_eq!(1.0f64.exp().log10(), 0.434294);
+ assert_eq!(1.0f64.log10(), 0.0);
+ assert!(nan.log10().is_nan());
+ assert_eq!(inf.log10(), inf);
+ assert!(neg_inf.log10().is_nan());
+ assert!((-2.3f64).log10().is_nan());
+ assert_eq!((-0.0f64).log10(), neg_inf);
+ assert_eq!(0.0f64.log10(), neg_inf);
+}
+
+#[test]
+fn test_to_degrees() {
+ let pi: f64 = consts::PI;
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_eq!(0.0f64.to_degrees(), 0.0);
+ assert_approx_eq!((-5.8f64).to_degrees(), -332.315521);
+ assert_eq!(pi.to_degrees(), 180.0);
+ assert!(nan.to_degrees().is_nan());
+ assert_eq!(inf.to_degrees(), inf);
+ assert_eq!(neg_inf.to_degrees(), neg_inf);
+}
+
+#[test]
+fn test_to_radians() {
+ let pi: f64 = consts::PI;
+ let nan: f64 = f64::NAN;
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ assert_eq!(0.0f64.to_radians(), 0.0);
+ assert_approx_eq!(154.6f64.to_radians(), 2.698279);
+ assert_approx_eq!((-332.31f64).to_radians(), -5.799903);
+ assert_eq!(180.0f64.to_radians(), pi);
+ assert!(nan.to_radians().is_nan());
+ assert_eq!(inf.to_radians(), inf);
+ assert_eq!(neg_inf.to_radians(), neg_inf);
+}
+
+#[test]
+fn test_asinh() {
+ assert_eq!(0.0f64.asinh(), 0.0f64);
+ assert_eq!((-0.0f64).asinh(), -0.0f64);
+
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ let nan: f64 = f64::NAN;
+ assert_eq!(inf.asinh(), inf);
+ assert_eq!(neg_inf.asinh(), neg_inf);
+ assert!(nan.asinh().is_nan());
+ assert!((-0.0f64).asinh().is_sign_negative());
+ // issue 63271
+ assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64);
+ assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64);
+ // regression test for the catastrophic cancellation fixed in 72486
+ assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083);
+}
+
+#[test]
+fn test_acosh() {
+ assert_eq!(1.0f64.acosh(), 0.0f64);
+ assert!(0.999f64.acosh().is_nan());
+
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ let nan: f64 = f64::NAN;
+ assert_eq!(inf.acosh(), inf);
+ assert!(neg_inf.acosh().is_nan());
+ assert!(nan.acosh().is_nan());
+ assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64);
+ assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64);
+}
+
+#[test]
+fn test_atanh() {
+ assert_eq!(0.0f64.atanh(), 0.0f64);
+ assert_eq!((-0.0f64).atanh(), -0.0f64);
+
+ let inf: f64 = f64::INFINITY;
+ let neg_inf: f64 = f64::NEG_INFINITY;
+ let nan: f64 = f64::NAN;
+ assert_eq!(1.0f64.atanh(), inf);
+ assert_eq!((-1.0f64).atanh(), neg_inf);
+ assert!(2f64.atanh().atanh().is_nan());
+ assert!((-2f64).atanh().atanh().is_nan());
+ assert!(inf.atanh().is_nan());
+ assert!(neg_inf.atanh().is_nan());
+ assert!(nan.atanh().is_nan());
+ assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64);
+ assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64);
+}
+
+#[test]
+fn test_real_consts() {
+ use super::consts;
+ let pi: f64 = consts::PI;
+ let frac_pi_2: f64 = consts::FRAC_PI_2;
+ let frac_pi_3: f64 = consts::FRAC_PI_3;
+ let frac_pi_4: f64 = consts::FRAC_PI_4;
+ let frac_pi_6: f64 = consts::FRAC_PI_6;
+ let frac_pi_8: f64 = consts::FRAC_PI_8;
+ let frac_1_pi: f64 = consts::FRAC_1_PI;
+ let frac_2_pi: f64 = consts::FRAC_2_PI;
+ let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI;
+ let sqrt2: f64 = consts::SQRT_2;
+ let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2;
+ let e: f64 = consts::E;
+ let log2_e: f64 = consts::LOG2_E;
+ let log10_e: f64 = consts::LOG10_E;
+ let ln_2: f64 = consts::LN_2;
+ let ln_10: f64 = consts::LN_10;
+
+ assert_approx_eq!(frac_pi_2, pi / 2f64);
+ assert_approx_eq!(frac_pi_3, pi / 3f64);
+ assert_approx_eq!(frac_pi_4, pi / 4f64);
+ assert_approx_eq!(frac_pi_6, pi / 6f64);
+ assert_approx_eq!(frac_pi_8, pi / 8f64);
+ assert_approx_eq!(frac_1_pi, 1f64 / pi);
+ assert_approx_eq!(frac_2_pi, 2f64 / pi);
+ assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt());
+ assert_approx_eq!(sqrt2, 2f64.sqrt());
+ assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt());
+ assert_approx_eq!(log2_e, e.log2());
+ assert_approx_eq!(log10_e, e.log10());
+ assert_approx_eq!(ln_2, 2f64.ln());
+ assert_approx_eq!(ln_10, 10f64.ln());
+}
+
+#[test]
+fn test_float_bits_conv() {
+ assert_eq!((1f64).to_bits(), 0x3ff0000000000000);
+ assert_eq!((12.5f64).to_bits(), 0x4029000000000000);
+ assert_eq!((1337f64).to_bits(), 0x4094e40000000000);
+ assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000);
+ assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0);
+ assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5);
+ assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0);
+ assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25);
+
+ // Check that NaNs roundtrip their bits regardless of signaling-ness
+ // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+ let masked_nan1 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
+ let masked_nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
+ assert!(f64::from_bits(masked_nan1).is_nan());
+ assert!(f64::from_bits(masked_nan2).is_nan());
+
+ 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() {
+ let _ = 1.0f64.clamp(3.0, 1.0);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_min_is_nan() {
+ let _ = 1.0f64.clamp(f64::NAN, 1.0);
+}
+
+#[test]
+#[should_panic]
+fn test_clamp_max_is_nan() {
+ let _ = 1.0f64.clamp(3.0, f64::NAN);
+}
+
+#[test]
+fn test_total_cmp() {
+ use core::cmp::Ordering;
+
+ fn quiet_bit_mask() -> u64 {
+ 1 << (f64::MANTISSA_DIGITS - 2)
+ }
+
+ fn min_subnorm() -> f64 {
+ f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0)
+ }
+
+ fn max_subnorm() -> f64 {
+ f64::MIN_POSITIVE - min_subnorm()
+ }
+
+ fn q_nan() -> f64 {
+ f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask())
+ }
+
+ fn s_nan() -> f64 {
+ f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42)
+ }
+
+ assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
+ assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
+ assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY));
+ assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX));
+ assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5));
+ assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0));
+ assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5));
+ assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5));
+ assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0));
+ assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0));
+ assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5));
+ assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0));
+ assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5));
+ assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5));
+ assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX));
+ assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY));
+ assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
+ assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
+
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
+ assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX));
+ assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5));
+ assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5));
+ assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0));
+ assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5));
+ assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
+ assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0));
+ assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5));
+ assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0));
+ assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5));
+ assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5));
+ assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX));
+ assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY));
+ assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan()));
+ assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
+
+ assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
+ assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan()));
+ assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY));
+ assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX));
+ assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5));
+ assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5));
+ assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0));
+ assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5));
+ assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0));
+ assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
+ assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5));
+ assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0));
+ assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5));
+ assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5));
+ assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX));
+ assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY));
+ assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
+
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY));
+ assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
+
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY));
+ assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
+}
diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs
index 717967f..1302173 100644
--- a/library/std/src/ffi/c_str.rs
+++ b/library/std/src/ffi/c_str.rs
@@ -1,4 +1,8 @@
#![deny(unsafe_op_in_unsafe_fn)]
+
+#[cfg(test)]
+mod tests;
+
use crate::ascii;
use crate::borrow::{Borrow, Cow};
use crate::cmp::Ordering;
@@ -877,13 +881,13 @@
unsafe {
// Transmute `Vec<NonZeroU8>` to `Vec<u8>`.
let v: Vec<u8> = {
- // Safety:
+ // SAFETY:
// - transmuting between `NonZeroU8` and `u8` is sound;
// - `alloc::Layout<NonZeroU8> == alloc::Layout<u8>`.
let (ptr, len, cap): (*mut NonZeroU8, _, _) = Vec::into_raw_parts(v);
Vec::from_raw_parts(ptr.cast::<u8>(), len, cap)
};
- // Safety: `v` cannot contain null bytes, given the type-level
+ // SAFETY: `v` cannot contain null bytes, given the type-level
// invariant of `NonZeroU8`.
CString::from_vec_unchecked(v)
}
@@ -1522,202 +1526,3 @@
self
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::borrow::Cow::{Borrowed, Owned};
- use crate::collections::hash_map::DefaultHasher;
- use crate::hash::{Hash, Hasher};
- use crate::os::raw::c_char;
- use crate::rc::Rc;
- use crate::sync::Arc;
-
- #[test]
- fn c_to_rust() {
- let data = b"123\0";
- let ptr = data.as_ptr() as *const c_char;
- unsafe {
- assert_eq!(CStr::from_ptr(ptr).to_bytes(), b"123");
- assert_eq!(CStr::from_ptr(ptr).to_bytes_with_nul(), b"123\0");
- }
- }
-
- #[test]
- fn simple() {
- let s = CString::new("1234").unwrap();
- assert_eq!(s.as_bytes(), b"1234");
- assert_eq!(s.as_bytes_with_nul(), b"1234\0");
- }
-
- #[test]
- fn build_with_zero1() {
- assert!(CString::new(&b"\0"[..]).is_err());
- }
- #[test]
- fn build_with_zero2() {
- assert!(CString::new(vec![0]).is_err());
- }
-
- #[test]
- fn build_with_zero3() {
- unsafe {
- let s = CString::from_vec_unchecked(vec![0]);
- assert_eq!(s.as_bytes(), b"\0");
- }
- }
-
- #[test]
- fn formatted() {
- let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap();
- assert_eq!(format!("{:?}", s), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#);
- }
-
- #[test]
- fn borrowed() {
- unsafe {
- let s = CStr::from_ptr(b"12\0".as_ptr() as *const _);
- assert_eq!(s.to_bytes(), b"12");
- assert_eq!(s.to_bytes_with_nul(), b"12\0");
- }
- }
-
- #[test]
- fn to_str() {
- let data = b"123\xE2\x80\xA6\0";
- let ptr = data.as_ptr() as *const c_char;
- unsafe {
- assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…"));
- assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…"));
- }
- let data = b"123\xE2\0";
- let ptr = data.as_ptr() as *const c_char;
- unsafe {
- assert!(CStr::from_ptr(ptr).to_str().is_err());
- assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::<str>(format!("123\u{FFFD}")));
- }
- }
-
- #[test]
- fn to_owned() {
- let data = b"123\0";
- let ptr = data.as_ptr() as *const c_char;
-
- let owned = unsafe { CStr::from_ptr(ptr).to_owned() };
- assert_eq!(owned.as_bytes_with_nul(), data);
- }
-
- #[test]
- fn equal_hash() {
- let data = b"123\xE2\xFA\xA6\0";
- let ptr = data.as_ptr() as *const c_char;
- let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) };
-
- let mut s = DefaultHasher::new();
- cstr.hash(&mut s);
- let cstr_hash = s.finish();
- let mut s = DefaultHasher::new();
- CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s);
- let cstring_hash = s.finish();
-
- assert_eq!(cstr_hash, cstring_hash);
- }
-
- #[test]
- fn from_bytes_with_nul() {
- let data = b"123\0";
- let cstr = CStr::from_bytes_with_nul(data);
- assert_eq!(cstr.map(CStr::to_bytes), Ok(&b"123"[..]));
- let cstr = CStr::from_bytes_with_nul(data);
- assert_eq!(cstr.map(CStr::to_bytes_with_nul), Ok(&b"123\0"[..]));
-
- unsafe {
- let cstr = CStr::from_bytes_with_nul(data);
- let cstr_unchecked = CStr::from_bytes_with_nul_unchecked(data);
- assert_eq!(cstr, Ok(cstr_unchecked));
- }
- }
-
- #[test]
- fn from_bytes_with_nul_unterminated() {
- let data = b"123";
- let cstr = CStr::from_bytes_with_nul(data);
- assert!(cstr.is_err());
- }
-
- #[test]
- fn from_bytes_with_nul_interior() {
- let data = b"1\023\0";
- let cstr = CStr::from_bytes_with_nul(data);
- assert!(cstr.is_err());
- }
-
- #[test]
- fn into_boxed() {
- let orig: &[u8] = b"Hello, world!\0";
- let cstr = CStr::from_bytes_with_nul(orig).unwrap();
- let boxed: Box<CStr> = Box::from(cstr);
- let cstring = cstr.to_owned().into_boxed_c_str().into_c_string();
- assert_eq!(cstr, &*boxed);
- assert_eq!(&*boxed, &*cstring);
- assert_eq!(&*cstring, cstr);
- }
-
- #[test]
- fn boxed_default() {
- let boxed = <Box<CStr>>::default();
- assert_eq!(boxed.to_bytes_with_nul(), &[0]);
- }
-
- #[test]
- fn test_c_str_clone_into() {
- let mut c_string = CString::new("lorem").unwrap();
- let c_ptr = c_string.as_ptr();
- let c_str = CStr::from_bytes_with_nul(b"ipsum\0").unwrap();
- c_str.clone_into(&mut c_string);
- assert_eq!(c_str, c_string.as_c_str());
- // The exact same size shouldn't have needed to move its allocation
- assert_eq!(c_ptr, c_string.as_ptr());
- }
-
- #[test]
- fn into_rc() {
- let orig: &[u8] = b"Hello, world!\0";
- let cstr = CStr::from_bytes_with_nul(orig).unwrap();
- let rc: Rc<CStr> = Rc::from(cstr);
- let arc: Arc<CStr> = Arc::from(cstr);
-
- assert_eq!(&*rc, cstr);
- assert_eq!(&*arc, cstr);
-
- let rc2: Rc<CStr> = Rc::from(cstr.to_owned());
- let arc2: Arc<CStr> = Arc::from(cstr.to_owned());
-
- assert_eq!(&*rc2, cstr);
- assert_eq!(&*arc2, cstr);
- }
-
- #[test]
- fn cstr_const_constructor() {
- const CSTR: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"Hello, world!\0") };
-
- assert_eq!(CSTR.to_str().unwrap(), "Hello, world!");
- }
-
- #[test]
- fn cstr_index_from() {
- let original = b"Hello, world!\0";
- let cstr = CStr::from_bytes_with_nul(original).unwrap();
- let result = CStr::from_bytes_with_nul(&original[7..]).unwrap();
-
- assert_eq!(&cstr[7..], result);
- }
-
- #[test]
- #[should_panic]
- fn cstr_index_from_empty() {
- let original = b"Hello, world!\0";
- let cstr = CStr::from_bytes_with_nul(original).unwrap();
- let _ = &cstr[original.len()..];
- }
-}
diff --git a/library/std/src/ffi/c_str/tests.rs b/library/std/src/ffi/c_str/tests.rs
new file mode 100644
index 0000000..4dff3df
--- /dev/null
+++ b/library/std/src/ffi/c_str/tests.rs
@@ -0,0 +1,195 @@
+use super::*;
+use crate::borrow::Cow::{Borrowed, Owned};
+use crate::collections::hash_map::DefaultHasher;
+use crate::hash::{Hash, Hasher};
+use crate::os::raw::c_char;
+use crate::rc::Rc;
+use crate::sync::Arc;
+
+#[test]
+fn c_to_rust() {
+ let data = b"123\0";
+ let ptr = data.as_ptr() as *const c_char;
+ unsafe {
+ assert_eq!(CStr::from_ptr(ptr).to_bytes(), b"123");
+ assert_eq!(CStr::from_ptr(ptr).to_bytes_with_nul(), b"123\0");
+ }
+}
+
+#[test]
+fn simple() {
+ let s = CString::new("1234").unwrap();
+ assert_eq!(s.as_bytes(), b"1234");
+ assert_eq!(s.as_bytes_with_nul(), b"1234\0");
+}
+
+#[test]
+fn build_with_zero1() {
+ assert!(CString::new(&b"\0"[..]).is_err());
+}
+#[test]
+fn build_with_zero2() {
+ assert!(CString::new(vec![0]).is_err());
+}
+
+#[test]
+fn build_with_zero3() {
+ unsafe {
+ let s = CString::from_vec_unchecked(vec![0]);
+ assert_eq!(s.as_bytes(), b"\0");
+ }
+}
+
+#[test]
+fn formatted() {
+ let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap();
+ assert_eq!(format!("{:?}", s), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#);
+}
+
+#[test]
+fn borrowed() {
+ unsafe {
+ let s = CStr::from_ptr(b"12\0".as_ptr() as *const _);
+ assert_eq!(s.to_bytes(), b"12");
+ assert_eq!(s.to_bytes_with_nul(), b"12\0");
+ }
+}
+
+#[test]
+fn to_str() {
+ let data = b"123\xE2\x80\xA6\0";
+ let ptr = data.as_ptr() as *const c_char;
+ unsafe {
+ assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…"));
+ assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…"));
+ }
+ let data = b"123\xE2\0";
+ let ptr = data.as_ptr() as *const c_char;
+ unsafe {
+ assert!(CStr::from_ptr(ptr).to_str().is_err());
+ assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::<str>(format!("123\u{FFFD}")));
+ }
+}
+
+#[test]
+fn to_owned() {
+ let data = b"123\0";
+ let ptr = data.as_ptr() as *const c_char;
+
+ let owned = unsafe { CStr::from_ptr(ptr).to_owned() };
+ assert_eq!(owned.as_bytes_with_nul(), data);
+}
+
+#[test]
+fn equal_hash() {
+ let data = b"123\xE2\xFA\xA6\0";
+ let ptr = data.as_ptr() as *const c_char;
+ let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) };
+
+ let mut s = DefaultHasher::new();
+ cstr.hash(&mut s);
+ let cstr_hash = s.finish();
+ let mut s = DefaultHasher::new();
+ CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s);
+ let cstring_hash = s.finish();
+
+ assert_eq!(cstr_hash, cstring_hash);
+}
+
+#[test]
+fn from_bytes_with_nul() {
+ let data = b"123\0";
+ let cstr = CStr::from_bytes_with_nul(data);
+ assert_eq!(cstr.map(CStr::to_bytes), Ok(&b"123"[..]));
+ let cstr = CStr::from_bytes_with_nul(data);
+ assert_eq!(cstr.map(CStr::to_bytes_with_nul), Ok(&b"123\0"[..]));
+
+ unsafe {
+ let cstr = CStr::from_bytes_with_nul(data);
+ let cstr_unchecked = CStr::from_bytes_with_nul_unchecked(data);
+ assert_eq!(cstr, Ok(cstr_unchecked));
+ }
+}
+
+#[test]
+fn from_bytes_with_nul_unterminated() {
+ let data = b"123";
+ let cstr = CStr::from_bytes_with_nul(data);
+ assert!(cstr.is_err());
+}
+
+#[test]
+fn from_bytes_with_nul_interior() {
+ let data = b"1\023\0";
+ let cstr = CStr::from_bytes_with_nul(data);
+ assert!(cstr.is_err());
+}
+
+#[test]
+fn into_boxed() {
+ let orig: &[u8] = b"Hello, world!\0";
+ let cstr = CStr::from_bytes_with_nul(orig).unwrap();
+ let boxed: Box<CStr> = Box::from(cstr);
+ let cstring = cstr.to_owned().into_boxed_c_str().into_c_string();
+ assert_eq!(cstr, &*boxed);
+ assert_eq!(&*boxed, &*cstring);
+ assert_eq!(&*cstring, cstr);
+}
+
+#[test]
+fn boxed_default() {
+ let boxed = <Box<CStr>>::default();
+ assert_eq!(boxed.to_bytes_with_nul(), &[0]);
+}
+
+#[test]
+fn test_c_str_clone_into() {
+ let mut c_string = CString::new("lorem").unwrap();
+ let c_ptr = c_string.as_ptr();
+ let c_str = CStr::from_bytes_with_nul(b"ipsum\0").unwrap();
+ c_str.clone_into(&mut c_string);
+ assert_eq!(c_str, c_string.as_c_str());
+ // The exact same size shouldn't have needed to move its allocation
+ assert_eq!(c_ptr, c_string.as_ptr());
+}
+
+#[test]
+fn into_rc() {
+ let orig: &[u8] = b"Hello, world!\0";
+ let cstr = CStr::from_bytes_with_nul(orig).unwrap();
+ let rc: Rc<CStr> = Rc::from(cstr);
+ let arc: Arc<CStr> = Arc::from(cstr);
+
+ assert_eq!(&*rc, cstr);
+ assert_eq!(&*arc, cstr);
+
+ let rc2: Rc<CStr> = Rc::from(cstr.to_owned());
+ let arc2: Arc<CStr> = Arc::from(cstr.to_owned());
+
+ assert_eq!(&*rc2, cstr);
+ assert_eq!(&*arc2, cstr);
+}
+
+#[test]
+fn cstr_const_constructor() {
+ const CSTR: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"Hello, world!\0") };
+
+ assert_eq!(CSTR.to_str().unwrap(), "Hello, world!");
+}
+
+#[test]
+fn cstr_index_from() {
+ let original = b"Hello, world!\0";
+ let cstr = CStr::from_bytes_with_nul(original).unwrap();
+ let result = CStr::from_bytes_with_nul(&original[7..]).unwrap();
+
+ assert_eq!(&cstr[7..], result);
+}
+
+#[test]
+#[should_panic]
+fn cstr_index_from_empty() {
+ let original = b"Hello, world!\0";
+ let cstr = CStr::from_bytes_with_nul(original).unwrap();
+ let _ = &cstr[original.len()..];
+}
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index 262d39d..2663f68 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -1,3 +1,6 @@
+#[cfg(test)]
+mod tests;
+
use crate::borrow::{Borrow, Cow};
use crate::cmp;
use crate::fmt;
@@ -91,7 +94,7 @@
// `OsStr::from_inner` current implementation relies
// on `OsStr` being layout-compatible with `Slice`.
// When attribute privacy is implemented, `OsStr` should be annotated as `#[repr(transparent)]`.
-// Anyway, `OsStr` representation and layout are considered implementation detail, are
+// Anyway, `OsStr` representation and layout are considered implementation details, are
// not documented and must not be relied upon.
pub struct OsStr {
inner: Slice,
@@ -507,14 +510,14 @@
#[inline]
fn from_inner(inner: &Slice) -> &OsStr {
- // Safety: OsStr is just a wrapper of Slice,
+ // SAFETY: OsStr is just a wrapper of Slice,
// therefore converting &Slice to &OsStr is safe.
unsafe { &*(inner as *const Slice as *const OsStr) }
}
#[inline]
fn from_inner_mut(inner: &mut Slice) -> &mut OsStr {
- // Safety: OsStr is just a wrapper of Slice,
+ // SAFETY: OsStr is just a wrapper of Slice,
// therefore converting &mut Slice to &mut OsStr is safe.
// Any method that mutates OsStr must be careful not to
// break platform-specific encoding, in particular Wtf8 on Windows.
@@ -1145,172 +1148,3 @@
Ok(OsString::from(s))
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::sys_common::{AsInner, IntoInner};
-
- use crate::rc::Rc;
- use crate::sync::Arc;
-
- #[test]
- fn test_os_string_with_capacity() {
- let os_string = OsString::with_capacity(0);
- assert_eq!(0, os_string.inner.into_inner().capacity());
-
- let os_string = OsString::with_capacity(10);
- assert_eq!(10, os_string.inner.into_inner().capacity());
-
- let mut os_string = OsString::with_capacity(0);
- os_string.push("abc");
- assert!(os_string.inner.into_inner().capacity() >= 3);
- }
-
- #[test]
- fn test_os_string_clear() {
- let mut os_string = OsString::from("abc");
- assert_eq!(3, os_string.inner.as_inner().len());
-
- os_string.clear();
- assert_eq!(&os_string, "");
- assert_eq!(0, os_string.inner.as_inner().len());
- }
-
- #[test]
- fn test_os_string_capacity() {
- let os_string = OsString::with_capacity(0);
- assert_eq!(0, os_string.capacity());
-
- let os_string = OsString::with_capacity(10);
- assert_eq!(10, os_string.capacity());
-
- let mut os_string = OsString::with_capacity(0);
- os_string.push("abc");
- assert!(os_string.capacity() >= 3);
- }
-
- #[test]
- fn test_os_string_reserve() {
- let mut os_string = OsString::new();
- assert_eq!(os_string.capacity(), 0);
-
- os_string.reserve(2);
- assert!(os_string.capacity() >= 2);
-
- for _ in 0..16 {
- os_string.push("a");
- }
-
- assert!(os_string.capacity() >= 16);
- os_string.reserve(16);
- assert!(os_string.capacity() >= 32);
-
- os_string.push("a");
-
- os_string.reserve(16);
- assert!(os_string.capacity() >= 33)
- }
-
- #[test]
- fn test_os_string_reserve_exact() {
- let mut os_string = OsString::new();
- assert_eq!(os_string.capacity(), 0);
-
- os_string.reserve_exact(2);
- assert!(os_string.capacity() >= 2);
-
- for _ in 0..16 {
- os_string.push("a");
- }
-
- assert!(os_string.capacity() >= 16);
- os_string.reserve_exact(16);
- assert!(os_string.capacity() >= 32);
-
- os_string.push("a");
-
- os_string.reserve_exact(16);
- assert!(os_string.capacity() >= 33)
- }
-
- #[test]
- fn test_os_string_default() {
- let os_string: OsString = Default::default();
- assert_eq!("", &os_string);
- }
-
- #[test]
- fn test_os_str_is_empty() {
- let mut os_string = OsString::new();
- assert!(os_string.is_empty());
-
- os_string.push("abc");
- assert!(!os_string.is_empty());
-
- os_string.clear();
- assert!(os_string.is_empty());
- }
-
- #[test]
- fn test_os_str_len() {
- let mut os_string = OsString::new();
- assert_eq!(0, os_string.len());
-
- os_string.push("abc");
- assert_eq!(3, os_string.len());
-
- os_string.clear();
- assert_eq!(0, os_string.len());
- }
-
- #[test]
- fn test_os_str_default() {
- let os_str: &OsStr = Default::default();
- assert_eq!("", os_str);
- }
-
- #[test]
- fn into_boxed() {
- let orig = "Hello, world!";
- let os_str = OsStr::new(orig);
- let boxed: Box<OsStr> = Box::from(os_str);
- let os_string = os_str.to_owned().into_boxed_os_str().into_os_string();
- assert_eq!(os_str, &*boxed);
- assert_eq!(&*boxed, &*os_string);
- assert_eq!(&*os_string, os_str);
- }
-
- #[test]
- fn boxed_default() {
- let boxed = <Box<OsStr>>::default();
- assert!(boxed.is_empty());
- }
-
- #[test]
- fn test_os_str_clone_into() {
- let mut os_string = OsString::with_capacity(123);
- os_string.push("hello");
- let os_str = OsStr::new("bonjour");
- os_str.clone_into(&mut os_string);
- assert_eq!(os_str, os_string);
- assert!(os_string.capacity() >= 123);
- }
-
- #[test]
- fn into_rc() {
- let orig = "Hello, world!";
- let os_str = OsStr::new(orig);
- let rc: Rc<OsStr> = Rc::from(os_str);
- let arc: Arc<OsStr> = Arc::from(os_str);
-
- assert_eq!(&*rc, os_str);
- assert_eq!(&*arc, os_str);
-
- let rc2: Rc<OsStr> = Rc::from(os_str.to_owned());
- let arc2: Arc<OsStr> = Arc::from(os_str.to_owned());
-
- assert_eq!(&*rc2, os_str);
- assert_eq!(&*arc2, os_str);
- }
-}
diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs
new file mode 100644
index 0000000..283f2b5
--- /dev/null
+++ b/library/std/src/ffi/os_str/tests.rs
@@ -0,0 +1,165 @@
+use super::*;
+use crate::sys_common::{AsInner, IntoInner};
+
+use crate::rc::Rc;
+use crate::sync::Arc;
+
+#[test]
+fn test_os_string_with_capacity() {
+ let os_string = OsString::with_capacity(0);
+ assert_eq!(0, os_string.inner.into_inner().capacity());
+
+ let os_string = OsString::with_capacity(10);
+ assert_eq!(10, os_string.inner.into_inner().capacity());
+
+ let mut os_string = OsString::with_capacity(0);
+ os_string.push("abc");
+ assert!(os_string.inner.into_inner().capacity() >= 3);
+}
+
+#[test]
+fn test_os_string_clear() {
+ let mut os_string = OsString::from("abc");
+ assert_eq!(3, os_string.inner.as_inner().len());
+
+ os_string.clear();
+ assert_eq!(&os_string, "");
+ assert_eq!(0, os_string.inner.as_inner().len());
+}
+
+#[test]
+fn test_os_string_capacity() {
+ let os_string = OsString::with_capacity(0);
+ assert_eq!(0, os_string.capacity());
+
+ let os_string = OsString::with_capacity(10);
+ assert_eq!(10, os_string.capacity());
+
+ let mut os_string = OsString::with_capacity(0);
+ os_string.push("abc");
+ assert!(os_string.capacity() >= 3);
+}
+
+#[test]
+fn test_os_string_reserve() {
+ let mut os_string = OsString::new();
+ assert_eq!(os_string.capacity(), 0);
+
+ os_string.reserve(2);
+ assert!(os_string.capacity() >= 2);
+
+ for _ in 0..16 {
+ os_string.push("a");
+ }
+
+ assert!(os_string.capacity() >= 16);
+ os_string.reserve(16);
+ assert!(os_string.capacity() >= 32);
+
+ os_string.push("a");
+
+ os_string.reserve(16);
+ assert!(os_string.capacity() >= 33)
+}
+
+#[test]
+fn test_os_string_reserve_exact() {
+ let mut os_string = OsString::new();
+ assert_eq!(os_string.capacity(), 0);
+
+ os_string.reserve_exact(2);
+ assert!(os_string.capacity() >= 2);
+
+ for _ in 0..16 {
+ os_string.push("a");
+ }
+
+ assert!(os_string.capacity() >= 16);
+ os_string.reserve_exact(16);
+ assert!(os_string.capacity() >= 32);
+
+ os_string.push("a");
+
+ os_string.reserve_exact(16);
+ assert!(os_string.capacity() >= 33)
+}
+
+#[test]
+fn test_os_string_default() {
+ let os_string: OsString = Default::default();
+ assert_eq!("", &os_string);
+}
+
+#[test]
+fn test_os_str_is_empty() {
+ let mut os_string = OsString::new();
+ assert!(os_string.is_empty());
+
+ os_string.push("abc");
+ assert!(!os_string.is_empty());
+
+ os_string.clear();
+ assert!(os_string.is_empty());
+}
+
+#[test]
+fn test_os_str_len() {
+ let mut os_string = OsString::new();
+ assert_eq!(0, os_string.len());
+
+ os_string.push("abc");
+ assert_eq!(3, os_string.len());
+
+ os_string.clear();
+ assert_eq!(0, os_string.len());
+}
+
+#[test]
+fn test_os_str_default() {
+ let os_str: &OsStr = Default::default();
+ assert_eq!("", os_str);
+}
+
+#[test]
+fn into_boxed() {
+ let orig = "Hello, world!";
+ let os_str = OsStr::new(orig);
+ let boxed: Box<OsStr> = Box::from(os_str);
+ let os_string = os_str.to_owned().into_boxed_os_str().into_os_string();
+ assert_eq!(os_str, &*boxed);
+ assert_eq!(&*boxed, &*os_string);
+ assert_eq!(&*os_string, os_str);
+}
+
+#[test]
+fn boxed_default() {
+ let boxed = <Box<OsStr>>::default();
+ assert!(boxed.is_empty());
+}
+
+#[test]
+fn test_os_str_clone_into() {
+ let mut os_string = OsString::with_capacity(123);
+ os_string.push("hello");
+ let os_str = OsStr::new("bonjour");
+ os_str.clone_into(&mut os_string);
+ assert_eq!(os_str, os_string);
+ assert!(os_string.capacity() >= 123);
+}
+
+#[test]
+fn into_rc() {
+ let orig = "Hello, world!";
+ let os_str = OsStr::new(orig);
+ let rc: Rc<OsStr> = Rc::from(os_str);
+ let arc: Arc<OsStr> = Arc::from(os_str);
+
+ assert_eq!(&*rc, os_str);
+ assert_eq!(&*arc, os_str);
+
+ let rc2: Rc<OsStr> = Rc::from(os_str.to_owned());
+ let arc2: Arc<OsStr> = Arc::from(os_str.to_owned());
+
+ assert_eq!(&*rc2, os_str);
+ assert_eq!(&*arc2, os_str);
+}
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index b1630f8..161bfe3 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -1,5 +1,3 @@
-// ignore-tidy-filelength
-
//! Filesystem manipulation operations.
//!
//! This module contains basic methods to manipulate the contents of the local
@@ -10,6 +8,9 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![deny(unsafe_op_in_unsafe_fn)]
+#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))]
+mod tests;
+
use crate::ffi::OsString;
use crate::fmt;
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
@@ -552,7 +553,7 @@
/// the `SetFileInformationByHandle` function on Windows. Note that, this
/// [may change in the future][changes].
///
- /// [changes]: ../io/index.html#platform-specific-behavior
+ /// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1502,7 +1503,7 @@
/// and the `DeleteFile` function on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1540,7 +1541,7 @@
/// and the `GetFileAttributesEx` function on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1574,7 +1575,7 @@
/// and the `GetFileAttributesEx` function on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1617,7 +1618,7 @@
///
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1668,7 +1669,7 @@
/// `fcopyfile`.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1706,7 +1707,7 @@
/// and the `CreateHardLink` function on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1771,7 +1772,7 @@
/// `FILE_FLAG_BACKUP_SEMANTICS` flags on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1811,7 +1812,7 @@
/// with other applications (if passed to the application on the command-line,
/// or written to a file another application may read).
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
/// [path]: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
///
/// # Errors
@@ -1845,7 +1846,7 @@
/// and the `CreateDirectory` function on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// **NOTE**: If a parent of the given path doesn't exist, this function will
/// return an error. To create a directory and all its missing parents at the
@@ -1886,7 +1887,7 @@
/// and the `CreateDirectory` function on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1929,7 +1930,7 @@
/// and the `RemoveDirectory` function on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -1969,7 +1970,7 @@
/// on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -2005,7 +2006,7 @@
/// currently corresponds to `readdir` on Unix and `FindNextFile` on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// The order in which this iterator returns entries is platform and filesystem
/// dependent.
@@ -2074,7 +2075,7 @@
/// and the `SetFileAttributes` function on Windows.
/// Note that, this [may change in the future][changes].
///
-/// [changes]: ../io/index.html#platform-specific-behavior
+/// [changes]: io#platform-specific-behavior
///
/// # Errors
///
@@ -2194,1349 +2195,3 @@
&mut self.inner
}
}
-
-#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))]
-mod tests {
- use crate::io::prelude::*;
-
- use crate::fs::{self, File, OpenOptions};
- use crate::io::{ErrorKind, SeekFrom};
- use crate::path::Path;
- use crate::str;
- use crate::sys_common::io::test::{tmpdir, TempDir};
- use crate::thread;
-
- use rand::{rngs::StdRng, RngCore, SeedableRng};
-
- #[cfg(unix)]
- use crate::os::unix::fs::symlink as symlink_dir;
- #[cfg(unix)]
- use crate::os::unix::fs::symlink as symlink_file;
- #[cfg(unix)]
- use crate::os::unix::fs::symlink as symlink_junction;
- #[cfg(windows)]
- use crate::os::windows::fs::{symlink_dir, symlink_file};
- #[cfg(windows)]
- use crate::sys::fs::symlink_junction;
-
- macro_rules! check {
- ($e:expr) => {
- match $e {
- Ok(t) => t,
- Err(e) => panic!("{} failed with: {}", stringify!($e), e),
- }
- };
- }
-
- #[cfg(windows)]
- macro_rules! error {
- ($e:expr, $s:expr) => {
- match $e {
- Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
- Err(ref err) => assert!(
- err.raw_os_error() == Some($s),
- format!("`{}` did not have a code of `{}`", err, $s)
- ),
- }
- };
- }
-
- #[cfg(unix)]
- macro_rules! error {
- ($e:expr, $s:expr) => {
- error_contains!($e, $s)
- };
- }
-
- macro_rules! error_contains {
- ($e:expr, $s:expr) => {
- match $e {
- Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
- Err(ref err) => assert!(
- err.to_string().contains($s),
- format!("`{}` did not contain `{}`", err, $s)
- ),
- }
- };
- }
-
- // Several test fail on windows if the user does not have permission to
- // create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of
- // disabling these test on Windows, use this function to test whether we
- // have permission, and return otherwise. This way, we still don't run these
- // tests most of the time, but at least we do if the user has the right
- // permissions.
- pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
- if cfg!(unix) {
- return true;
- }
- let link = tmpdir.join("some_hopefully_unique_link_name");
-
- match symlink_file(r"nonexisting_target", link) {
- Ok(_) => true,
- // ERROR_PRIVILEGE_NOT_HELD = 1314
- Err(ref err) if err.raw_os_error() == Some(1314) => false,
- Err(_) => true,
- }
- }
-
- #[test]
- fn file_test_io_smoke_test() {
- let message = "it's alright. have a good time";
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("file_rt_io_file_test.txt");
- {
- let mut write_stream = check!(File::create(filename));
- check!(write_stream.write(message.as_bytes()));
- }
- {
- let mut read_stream = check!(File::open(filename));
- let mut read_buf = [0; 1028];
- let read_str = match check!(read_stream.read(&mut read_buf)) {
- 0 => panic!("shouldn't happen"),
- n => str::from_utf8(&read_buf[..n]).unwrap().to_string(),
- };
- assert_eq!(read_str, message);
- }
- check!(fs::remove_file(filename));
- }
-
- #[test]
- fn invalid_path_raises() {
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("file_that_does_not_exist.txt");
- let result = File::open(filename);
-
- #[cfg(all(unix, not(target_os = "vxworks")))]
- error!(result, "No such file or directory");
- #[cfg(target_os = "vxworks")]
- error!(result, "no such file or directory");
- #[cfg(windows)]
- error!(result, 2); // ERROR_FILE_NOT_FOUND
- }
-
- #[test]
- fn file_test_iounlinking_invalid_path_should_raise_condition() {
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
-
- let result = fs::remove_file(filename);
-
- #[cfg(all(unix, not(target_os = "vxworks")))]
- error!(result, "No such file or directory");
- #[cfg(target_os = "vxworks")]
- error!(result, "no such file or directory");
- #[cfg(windows)]
- error!(result, 2); // ERROR_FILE_NOT_FOUND
- }
-
- #[test]
- fn file_test_io_non_positional_read() {
- let message: &str = "ten-four";
- let mut read_mem = [0; 8];
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("file_rt_io_file_test_positional.txt");
- {
- let mut rw_stream = check!(File::create(filename));
- check!(rw_stream.write(message.as_bytes()));
- }
- {
- let mut read_stream = check!(File::open(filename));
- {
- let read_buf = &mut read_mem[0..4];
- check!(read_stream.read(read_buf));
- }
- {
- let read_buf = &mut read_mem[4..8];
- check!(read_stream.read(read_buf));
- }
- }
- check!(fs::remove_file(filename));
- let read_str = str::from_utf8(&read_mem).unwrap();
- assert_eq!(read_str, message);
- }
-
- #[test]
- fn file_test_io_seek_and_tell_smoke_test() {
- let message = "ten-four";
- let mut read_mem = [0; 4];
- let set_cursor = 4 as u64;
- let tell_pos_pre_read;
- let tell_pos_post_read;
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
- {
- let mut rw_stream = check!(File::create(filename));
- check!(rw_stream.write(message.as_bytes()));
- }
- {
- let mut read_stream = check!(File::open(filename));
- check!(read_stream.seek(SeekFrom::Start(set_cursor)));
- tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0)));
- check!(read_stream.read(&mut read_mem));
- tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0)));
- }
- check!(fs::remove_file(filename));
- let read_str = str::from_utf8(&read_mem).unwrap();
- assert_eq!(read_str, &message[4..8]);
- assert_eq!(tell_pos_pre_read, set_cursor);
- assert_eq!(tell_pos_post_read, message.len() as u64);
- }
-
- #[test]
- fn file_test_io_seek_and_write() {
- let initial_msg = "food-is-yummy";
- let overwrite_msg = "-the-bar!!";
- let final_msg = "foo-the-bar!!";
- let seek_idx = 3;
- let mut read_mem = [0; 13];
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt");
- {
- let mut rw_stream = check!(File::create(filename));
- check!(rw_stream.write(initial_msg.as_bytes()));
- check!(rw_stream.seek(SeekFrom::Start(seek_idx)));
- check!(rw_stream.write(overwrite_msg.as_bytes()));
- }
- {
- let mut read_stream = check!(File::open(filename));
- check!(read_stream.read(&mut read_mem));
- }
- check!(fs::remove_file(filename));
- let read_str = str::from_utf8(&read_mem).unwrap();
- assert!(read_str == final_msg);
- }
-
- #[test]
- fn file_test_io_seek_shakedown() {
- // 01234567890123
- let initial_msg = "qwer-asdf-zxcv";
- let chunk_one: &str = "qwer";
- let chunk_two: &str = "asdf";
- let chunk_three: &str = "zxcv";
- let mut read_mem = [0; 4];
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt");
- {
- let mut rw_stream = check!(File::create(filename));
- check!(rw_stream.write(initial_msg.as_bytes()));
- }
- {
- let mut read_stream = check!(File::open(filename));
-
- check!(read_stream.seek(SeekFrom::End(-4)));
- check!(read_stream.read(&mut read_mem));
- assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three);
-
- check!(read_stream.seek(SeekFrom::Current(-9)));
- check!(read_stream.read(&mut read_mem));
- assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two);
-
- check!(read_stream.seek(SeekFrom::Start(0)));
- check!(read_stream.read(&mut read_mem));
- assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one);
- }
- check!(fs::remove_file(filename));
- }
-
- #[test]
- fn file_test_io_eof() {
- let tmpdir = tmpdir();
- let filename = tmpdir.join("file_rt_io_file_test_eof.txt");
- let mut buf = [0; 256];
- {
- let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
- let mut rw = check!(oo.open(&filename));
- assert_eq!(check!(rw.read(&mut buf)), 0);
- assert_eq!(check!(rw.read(&mut buf)), 0);
- }
- check!(fs::remove_file(&filename));
- }
-
- #[test]
- #[cfg(unix)]
- fn file_test_io_read_write_at() {
- use crate::os::unix::fs::FileExt;
-
- let tmpdir = tmpdir();
- let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt");
- let mut buf = [0; 256];
- let write1 = "asdf";
- let write2 = "qwer-";
- let write3 = "-zxcv";
- let content = "qwer-asdf-zxcv";
- {
- let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
- let mut rw = check!(oo.open(&filename));
- assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len());
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
- assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len());
- assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
- assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
- assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0"));
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
- assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
- assert_eq!(check!(rw.read(&mut buf)), write1.len());
- assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
- assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
- assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
- assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len());
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
- }
- {
- let mut read = check!(File::open(&filename));
- assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
- assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
- assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0);
- assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
- assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
- assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
- assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9);
- assert_eq!(check!(read.read(&mut buf)), write3.len());
- assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
- assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
- assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
- assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
- assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
- assert_eq!(check!(read.read_at(&mut buf, 14)), 0);
- assert_eq!(check!(read.read_at(&mut buf, 15)), 0);
- assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
- }
- check!(fs::remove_file(&filename));
- }
-
- #[test]
- #[cfg(unix)]
- fn set_get_unix_permissions() {
- use crate::os::unix::fs::PermissionsExt;
-
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("set_get_unix_permissions");
- check!(fs::create_dir(filename));
- let mask = 0o7777;
-
- check!(fs::set_permissions(filename, fs::Permissions::from_mode(0)));
- let metadata0 = check!(fs::metadata(filename));
- assert_eq!(mask & metadata0.permissions().mode(), 0);
-
- check!(fs::set_permissions(filename, fs::Permissions::from_mode(0o1777)));
- let metadata1 = check!(fs::metadata(filename));
- #[cfg(all(unix, not(target_os = "vxworks")))]
- assert_eq!(mask & metadata1.permissions().mode(), 0o1777);
- #[cfg(target_os = "vxworks")]
- assert_eq!(mask & metadata1.permissions().mode(), 0o0777);
- }
-
- #[test]
- #[cfg(windows)]
- fn file_test_io_seek_read_write() {
- use crate::os::windows::fs::FileExt;
-
- let tmpdir = tmpdir();
- let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt");
- let mut buf = [0; 256];
- let write1 = "asdf";
- let write2 = "qwer-";
- let write3 = "-zxcv";
- let content = "qwer-asdf-zxcv";
- {
- let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
- let mut rw = check!(oo.open(&filename));
- assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len());
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
- assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len());
- assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
- assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0);
- assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
- assert_eq!(check!(rw.read(&mut buf)), write1.len());
- assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
- assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len());
- assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
- assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len());
- assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14);
- }
- {
- let mut read = check!(File::open(&filename));
- assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
- assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
- assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
- assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
- assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
- assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
- assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
- assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
- assert_eq!(check!(read.read(&mut buf)), write3.len());
- assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
- assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
- assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
- assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
- assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
- assert_eq!(check!(read.seek_read(&mut buf, 14)), 0);
- assert_eq!(check!(read.seek_read(&mut buf, 15)), 0);
- }
- check!(fs::remove_file(&filename));
- }
-
- #[test]
- fn file_test_stat_is_correct_on_is_file() {
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
- {
- let mut opts = OpenOptions::new();
- let mut fs = check!(opts.read(true).write(true).create(true).open(filename));
- let msg = "hw";
- fs.write(msg.as_bytes()).unwrap();
-
- let fstat_res = check!(fs.metadata());
- assert!(fstat_res.is_file());
- }
- let stat_res_fn = check!(fs::metadata(filename));
- assert!(stat_res_fn.is_file());
- let stat_res_meth = check!(filename.metadata());
- assert!(stat_res_meth.is_file());
- check!(fs::remove_file(filename));
- }
-
- #[test]
- fn file_test_stat_is_correct_on_is_dir() {
- let tmpdir = tmpdir();
- let filename = &tmpdir.join("file_stat_correct_on_is_dir");
- check!(fs::create_dir(filename));
- let stat_res_fn = check!(fs::metadata(filename));
- assert!(stat_res_fn.is_dir());
- let stat_res_meth = check!(filename.metadata());
- assert!(stat_res_meth.is_dir());
- check!(fs::remove_dir(filename));
- }
-
- #[test]
- fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
- let tmpdir = tmpdir();
- let dir = &tmpdir.join("fileinfo_false_on_dir");
- check!(fs::create_dir(dir));
- assert!(!dir.is_file());
- check!(fs::remove_dir(dir));
- }
-
- #[test]
- fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
- let tmpdir = tmpdir();
- let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt");
- check!(check!(File::create(file)).write(b"foo"));
- assert!(file.exists());
- check!(fs::remove_file(file));
- assert!(!file.exists());
- }
-
- #[test]
- fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
- let tmpdir = tmpdir();
- let dir = &tmpdir.join("before_and_after_dir");
- assert!(!dir.exists());
- check!(fs::create_dir(dir));
- assert!(dir.exists());
- assert!(dir.is_dir());
- check!(fs::remove_dir(dir));
- assert!(!dir.exists());
- }
-
- #[test]
- fn file_test_directoryinfo_readdir() {
- let tmpdir = tmpdir();
- let dir = &tmpdir.join("di_readdir");
- check!(fs::create_dir(dir));
- let prefix = "foo";
- for n in 0..3 {
- let f = dir.join(&format!("{}.txt", n));
- let mut w = check!(File::create(&f));
- let msg_str = format!("{}{}", prefix, n.to_string());
- let msg = msg_str.as_bytes();
- check!(w.write(msg));
- }
- let files = check!(fs::read_dir(dir));
- let mut mem = [0; 4];
- for f in files {
- let f = f.unwrap().path();
- {
- let n = f.file_stem().unwrap();
- check!(check!(File::open(&f)).read(&mut mem));
- let read_str = str::from_utf8(&mem).unwrap();
- let expected = format!("{}{}", prefix, n.to_str().unwrap());
- assert_eq!(expected, read_str);
- }
- check!(fs::remove_file(&f));
- }
- check!(fs::remove_dir(dir));
- }
-
- #[test]
- fn file_create_new_already_exists_error() {
- let tmpdir = tmpdir();
- let file = &tmpdir.join("file_create_new_error_exists");
- check!(fs::File::create(file));
- let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err();
- assert_eq!(e.kind(), ErrorKind::AlreadyExists);
- }
-
- #[test]
- fn mkdir_path_already_exists_error() {
- let tmpdir = tmpdir();
- let dir = &tmpdir.join("mkdir_error_twice");
- check!(fs::create_dir(dir));
- let e = fs::create_dir(dir).unwrap_err();
- assert_eq!(e.kind(), ErrorKind::AlreadyExists);
- }
-
- #[test]
- fn recursive_mkdir() {
- let tmpdir = tmpdir();
- let dir = tmpdir.join("d1/d2");
- check!(fs::create_dir_all(&dir));
- assert!(dir.is_dir())
- }
-
- #[test]
- fn recursive_mkdir_failure() {
- let tmpdir = tmpdir();
- let dir = tmpdir.join("d1");
- let file = dir.join("f1");
-
- check!(fs::create_dir_all(&dir));
- check!(File::create(&file));
-
- let result = fs::create_dir_all(&file);
-
- assert!(result.is_err());
- }
-
- #[test]
- fn concurrent_recursive_mkdir() {
- for _ in 0..100 {
- let dir = tmpdir();
- let mut dir = dir.join("a");
- for _ in 0..40 {
- dir = dir.join("a");
- }
- let mut join = vec![];
- for _ in 0..8 {
- let dir = dir.clone();
- join.push(thread::spawn(move || {
- check!(fs::create_dir_all(&dir));
- }))
- }
-
- // No `Display` on result of `join()`
- join.drain(..).map(|join| join.join().unwrap()).count();
- }
- }
-
- #[test]
- fn recursive_mkdir_slash() {
- check!(fs::create_dir_all(Path::new("/")));
- }
-
- #[test]
- fn recursive_mkdir_dot() {
- check!(fs::create_dir_all(Path::new(".")));
- }
-
- #[test]
- fn recursive_mkdir_empty() {
- check!(fs::create_dir_all(Path::new("")));
- }
-
- #[test]
- fn recursive_rmdir() {
- let tmpdir = tmpdir();
- let d1 = tmpdir.join("d1");
- let dt = d1.join("t");
- let dtt = dt.join("t");
- let d2 = tmpdir.join("d2");
- let canary = d2.join("do_not_delete");
- check!(fs::create_dir_all(&dtt));
- check!(fs::create_dir_all(&d2));
- check!(check!(File::create(&canary)).write(b"foo"));
- check!(symlink_junction(&d2, &dt.join("d2")));
- let _ = symlink_file(&canary, &d1.join("canary"));
- check!(fs::remove_dir_all(&d1));
-
- assert!(!d1.is_dir());
- assert!(canary.exists());
- }
-
- #[test]
- fn recursive_rmdir_of_symlink() {
- // test we do not recursively delete a symlink but only dirs.
- let tmpdir = tmpdir();
- let link = tmpdir.join("d1");
- let dir = tmpdir.join("d2");
- let canary = dir.join("do_not_delete");
- check!(fs::create_dir_all(&dir));
- check!(check!(File::create(&canary)).write(b"foo"));
- check!(symlink_junction(&dir, &link));
- check!(fs::remove_dir_all(&link));
-
- assert!(!link.is_dir());
- assert!(canary.exists());
- }
-
- #[test]
- // only Windows makes a distinction between file and directory symlinks.
- #[cfg(windows)]
- fn recursive_rmdir_of_file_symlink() {
- let tmpdir = tmpdir();
- if !got_symlink_permission(&tmpdir) {
- return;
- };
-
- let f1 = tmpdir.join("f1");
- let f2 = tmpdir.join("f2");
- check!(check!(File::create(&f1)).write(b"foo"));
- check!(symlink_file(&f1, &f2));
- match fs::remove_dir_all(&f2) {
- Ok(..) => panic!("wanted a failure"),
- Err(..) => {}
- }
- }
-
- #[test]
- fn unicode_path_is_dir() {
- assert!(Path::new(".").is_dir());
- assert!(!Path::new("test/stdtest/fs.rs").is_dir());
-
- let tmpdir = tmpdir();
-
- let mut dirpath = tmpdir.path().to_path_buf();
- dirpath.push("test-ź°äøć¼ä½ 儽");
- check!(fs::create_dir(&dirpath));
- assert!(dirpath.is_dir());
-
- let mut filepath = dirpath;
- filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs");
- check!(File::create(&filepath)); // ignore return; touch only
- assert!(!filepath.is_dir());
- assert!(filepath.exists());
- }
-
- #[test]
- fn unicode_path_exists() {
- assert!(Path::new(".").exists());
- assert!(!Path::new("test/nonexistent-bogus-path").exists());
-
- let tmpdir = tmpdir();
- let unicode = tmpdir.path();
- let unicode = unicode.join("test-ź°äøć¼åč§");
- check!(fs::create_dir(&unicode));
- assert!(unicode.exists());
- assert!(!Path::new("test/unicode-bogus-path-ź°äøć¼åč§").exists());
- }
-
- #[test]
- fn copy_file_does_not_exist() {
- let from = Path::new("test/nonexistent-bogus-path");
- let to = Path::new("test/other-bogus-path");
-
- match fs::copy(&from, &to) {
- Ok(..) => panic!(),
- Err(..) => {
- assert!(!from.exists());
- assert!(!to.exists());
- }
- }
- }
-
- #[test]
- fn copy_src_does_not_exist() {
- let tmpdir = tmpdir();
- let from = Path::new("test/nonexistent-bogus-path");
- let to = tmpdir.join("out.txt");
- check!(check!(File::create(&to)).write(b"hello"));
- assert!(fs::copy(&from, &to).is_err());
- assert!(!from.exists());
- let mut v = Vec::new();
- check!(check!(File::open(&to)).read_to_end(&mut v));
- assert_eq!(v, b"hello");
- }
-
- #[test]
- fn copy_file_ok() {
- let tmpdir = tmpdir();
- let input = tmpdir.join("in.txt");
- let out = tmpdir.join("out.txt");
-
- check!(check!(File::create(&input)).write(b"hello"));
- check!(fs::copy(&input, &out));
- let mut v = Vec::new();
- check!(check!(File::open(&out)).read_to_end(&mut v));
- assert_eq!(v, b"hello");
-
- assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions());
- }
-
- #[test]
- fn copy_file_dst_dir() {
- let tmpdir = tmpdir();
- let out = tmpdir.join("out");
-
- check!(File::create(&out));
- match fs::copy(&*out, tmpdir.path()) {
- Ok(..) => panic!(),
- Err(..) => {}
- }
- }
-
- #[test]
- fn copy_file_dst_exists() {
- let tmpdir = tmpdir();
- let input = tmpdir.join("in");
- let output = tmpdir.join("out");
-
- check!(check!(File::create(&input)).write("foo".as_bytes()));
- check!(check!(File::create(&output)).write("bar".as_bytes()));
- check!(fs::copy(&input, &output));
-
- let mut v = Vec::new();
- check!(check!(File::open(&output)).read_to_end(&mut v));
- assert_eq!(v, b"foo".to_vec());
- }
-
- #[test]
- fn copy_file_src_dir() {
- let tmpdir = tmpdir();
- let out = tmpdir.join("out");
-
- match fs::copy(tmpdir.path(), &out) {
- Ok(..) => panic!(),
- Err(..) => {}
- }
- assert!(!out.exists());
- }
-
- #[test]
- fn copy_file_preserves_perm_bits() {
- let tmpdir = tmpdir();
- let input = tmpdir.join("in.txt");
- let out = tmpdir.join("out.txt");
-
- let attr = check!(check!(File::create(&input)).metadata());
- let mut p = attr.permissions();
- p.set_readonly(true);
- check!(fs::set_permissions(&input, p));
- check!(fs::copy(&input, &out));
- assert!(check!(out.metadata()).permissions().readonly());
- check!(fs::set_permissions(&input, attr.permissions()));
- check!(fs::set_permissions(&out, attr.permissions()));
- }
-
- #[test]
- #[cfg(windows)]
- fn copy_file_preserves_streams() {
- let tmp = tmpdir();
- check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes()));
- assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0);
- assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0);
- let mut v = Vec::new();
- check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v));
- assert_eq!(v, b"carrot".to_vec());
- }
-
- #[test]
- fn copy_file_returns_metadata_len() {
- let tmp = tmpdir();
- let in_path = tmp.join("in.txt");
- let out_path = tmp.join("out.txt");
- check!(check!(File::create(&in_path)).write(b"lettuce"));
- #[cfg(windows)]
- check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot"));
- let copied_len = check!(fs::copy(&in_path, &out_path));
- assert_eq!(check!(out_path.metadata()).len(), copied_len);
- }
-
- #[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;
- };
-
- let input = tmpdir.join("in.txt");
- let out = tmpdir.join("out.txt");
-
- check!(check!(File::create(&input)).write("foobar".as_bytes()));
- check!(symlink_file(&input, &out));
- assert!(check!(out.symlink_metadata()).file_type().is_symlink());
- assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
- let mut v = Vec::new();
- check!(check!(File::open(&out)).read_to_end(&mut v));
- assert_eq!(v, b"foobar".to_vec());
- }
-
- #[test]
- fn symlink_noexist() {
- // Symlinks can point to things that don't exist
- let tmpdir = tmpdir();
- if !got_symlink_permission(&tmpdir) {
- return;
- };
-
- // Use a relative path for testing. Symlinks get normalized by Windows,
- // so we may not get the same path back for absolute paths
- check!(symlink_file(&"foo", &tmpdir.join("bar")));
- assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo");
- }
-
- #[test]
- fn read_link() {
- if cfg!(windows) {
- // directory symlink
- assert_eq!(
- check!(fs::read_link(r"C:\Users\All Users")).to_str().unwrap(),
- r"C:\ProgramData"
- );
- // junction
- assert_eq!(
- check!(fs::read_link(r"C:\Users\Default User")).to_str().unwrap(),
- r"C:\Users\Default"
- );
- // junction with special permissions
- assert_eq!(
- check!(fs::read_link(r"C:\Documents and Settings\")).to_str().unwrap(),
- r"C:\Users"
- );
- }
- let tmpdir = tmpdir();
- let link = tmpdir.join("link");
- if !got_symlink_permission(&tmpdir) {
- return;
- };
- check!(symlink_file(&"foo", &link));
- assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo");
- }
-
- #[test]
- fn readlink_not_symlink() {
- let tmpdir = tmpdir();
- match fs::read_link(tmpdir.path()) {
- Ok(..) => panic!("wanted a failure"),
- Err(..) => {}
- }
- }
-
- #[test]
- fn links_work() {
- let tmpdir = tmpdir();
- let input = tmpdir.join("in.txt");
- let out = tmpdir.join("out.txt");
-
- check!(check!(File::create(&input)).write("foobar".as_bytes()));
- check!(fs::hard_link(&input, &out));
- assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
- assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len());
- let mut v = Vec::new();
- check!(check!(File::open(&out)).read_to_end(&mut v));
- assert_eq!(v, b"foobar".to_vec());
-
- // can't link to yourself
- match fs::hard_link(&input, &input) {
- Ok(..) => panic!("wanted a failure"),
- Err(..) => {}
- }
- // can't link to something that doesn't exist
- match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
- Ok(..) => panic!("wanted a failure"),
- Err(..) => {}
- }
- }
-
- #[test]
- fn chmod_works() {
- let tmpdir = tmpdir();
- let file = tmpdir.join("in.txt");
-
- check!(File::create(&file));
- let attr = check!(fs::metadata(&file));
- assert!(!attr.permissions().readonly());
- let mut p = attr.permissions();
- p.set_readonly(true);
- check!(fs::set_permissions(&file, p.clone()));
- let attr = check!(fs::metadata(&file));
- assert!(attr.permissions().readonly());
-
- match fs::set_permissions(&tmpdir.join("foo"), p.clone()) {
- Ok(..) => panic!("wanted an error"),
- Err(..) => {}
- }
-
- p.set_readonly(false);
- check!(fs::set_permissions(&file, p));
- }
-
- #[test]
- fn fchmod_works() {
- let tmpdir = tmpdir();
- let path = tmpdir.join("in.txt");
-
- let file = check!(File::create(&path));
- let attr = check!(fs::metadata(&path));
- assert!(!attr.permissions().readonly());
- let mut p = attr.permissions();
- p.set_readonly(true);
- check!(file.set_permissions(p.clone()));
- let attr = check!(fs::metadata(&path));
- assert!(attr.permissions().readonly());
-
- p.set_readonly(false);
- check!(file.set_permissions(p));
- }
-
- #[test]
- fn sync_doesnt_kill_anything() {
- let tmpdir = tmpdir();
- let path = tmpdir.join("in.txt");
-
- let mut file = check!(File::create(&path));
- check!(file.sync_all());
- check!(file.sync_data());
- check!(file.write(b"foo"));
- check!(file.sync_all());
- check!(file.sync_data());
- }
-
- #[test]
- fn truncate_works() {
- let tmpdir = tmpdir();
- let path = tmpdir.join("in.txt");
-
- let mut file = check!(File::create(&path));
- check!(file.write(b"foo"));
- check!(file.sync_all());
-
- // Do some simple things with truncation
- assert_eq!(check!(file.metadata()).len(), 3);
- check!(file.set_len(10));
- assert_eq!(check!(file.metadata()).len(), 10);
- check!(file.write(b"bar"));
- check!(file.sync_all());
- assert_eq!(check!(file.metadata()).len(), 10);
-
- let mut v = Vec::new();
- check!(check!(File::open(&path)).read_to_end(&mut v));
- assert_eq!(v, b"foobar\0\0\0\0".to_vec());
-
- // Truncate to a smaller length, don't seek, and then write something.
- // Ensure that the intermediate zeroes are all filled in (we have `seek`ed
- // past the end of the file).
- check!(file.set_len(2));
- assert_eq!(check!(file.metadata()).len(), 2);
- check!(file.write(b"wut"));
- check!(file.sync_all());
- assert_eq!(check!(file.metadata()).len(), 9);
- let mut v = Vec::new();
- check!(check!(File::open(&path)).read_to_end(&mut v));
- assert_eq!(v, b"fo\0\0\0\0wut".to_vec());
- }
-
- #[test]
- fn open_flavors() {
- use crate::fs::OpenOptions as OO;
- fn c<T: Clone>(t: &T) -> T {
- t.clone()
- }
-
- let tmpdir = tmpdir();
-
- let mut r = OO::new();
- r.read(true);
- let mut w = OO::new();
- w.write(true);
- let mut rw = OO::new();
- rw.read(true).write(true);
- let mut a = OO::new();
- a.append(true);
- let mut ra = OO::new();
- ra.read(true).append(true);
-
- #[cfg(windows)]
- let invalid_options = 87; // ERROR_INVALID_PARAMETER
- #[cfg(all(unix, not(target_os = "vxworks")))]
- let invalid_options = "Invalid argument";
- #[cfg(target_os = "vxworks")]
- let invalid_options = "invalid argument";
-
- // Test various combinations of creation modes and access modes.
- //
- // Allowed:
- // creation mode | read | write | read-write | append | read-append |
- // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:|
- // not set (open existing) | X | X | X | X | X |
- // create | | X | X | X | X |
- // truncate | | X | X | | |
- // create and truncate | | X | X | | |
- // create_new | | X | X | X | X |
- //
- // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
-
- // write-only
- check!(c(&w).create_new(true).open(&tmpdir.join("a")));
- check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a")));
- check!(c(&w).truncate(true).open(&tmpdir.join("a")));
- check!(c(&w).create(true).open(&tmpdir.join("a")));
- check!(c(&w).open(&tmpdir.join("a")));
-
- // read-only
- error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options);
- error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options);
- error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options);
- error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options);
- check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only
-
- // read-write
- check!(c(&rw).create_new(true).open(&tmpdir.join("c")));
- check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c")));
- check!(c(&rw).truncate(true).open(&tmpdir.join("c")));
- check!(c(&rw).create(true).open(&tmpdir.join("c")));
- check!(c(&rw).open(&tmpdir.join("c")));
-
- // append
- check!(c(&a).create_new(true).open(&tmpdir.join("d")));
- error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options);
- error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options);
- check!(c(&a).create(true).open(&tmpdir.join("d")));
- check!(c(&a).open(&tmpdir.join("d")));
-
- // read-append
- check!(c(&ra).create_new(true).open(&tmpdir.join("e")));
- error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options);
- error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options);
- check!(c(&ra).create(true).open(&tmpdir.join("e")));
- check!(c(&ra).open(&tmpdir.join("e")));
-
- // Test opening a file without setting an access mode
- let mut blank = OO::new();
- error!(blank.create(true).open(&tmpdir.join("f")), invalid_options);
-
- // Test write works
- check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
-
- // Test write fails for read-only
- check!(r.open(&tmpdir.join("h")));
- {
- let mut f = check!(r.open(&tmpdir.join("h")));
- assert!(f.write("wut".as_bytes()).is_err());
- }
-
- // Test write overwrites
- {
- let mut f = check!(c(&w).open(&tmpdir.join("h")));
- check!(f.write("baz".as_bytes()));
- }
- {
- let mut f = check!(c(&r).open(&tmpdir.join("h")));
- let mut b = vec![0; 6];
- check!(f.read(&mut b));
- assert_eq!(b, "bazbar".as_bytes());
- }
-
- // Test truncate works
- {
- let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
- check!(f.write("foo".as_bytes()));
- }
- assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
-
- // Test append works
- assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
- {
- let mut f = check!(c(&a).open(&tmpdir.join("h")));
- check!(f.write("bar".as_bytes()));
- }
- assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
-
- // Test .append(true) equals .write(true).append(true)
- {
- let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
- check!(f.write("baz".as_bytes()));
- }
- assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9);
- }
-
- #[test]
- fn _assert_send_sync() {
- fn _assert_send_sync<T: Send + Sync>() {}
- _assert_send_sync::<OpenOptions>();
- }
-
- #[test]
- fn binary_file() {
- let mut bytes = [0; 1024];
- StdRng::from_entropy().fill_bytes(&mut bytes);
-
- let tmpdir = tmpdir();
-
- check!(check!(File::create(&tmpdir.join("test"))).write(&bytes));
- let mut v = Vec::new();
- check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v));
- assert!(v == &bytes[..]);
- }
-
- #[test]
- fn write_then_read() {
- let mut bytes = [0; 1024];
- StdRng::from_entropy().fill_bytes(&mut bytes);
-
- let tmpdir = tmpdir();
-
- check!(fs::write(&tmpdir.join("test"), &bytes[..]));
- let v = check!(fs::read(&tmpdir.join("test")));
- assert!(v == &bytes[..]);
-
- check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
- error_contains!(
- fs::read_to_string(&tmpdir.join("not-utf8")),
- "stream did not contain valid UTF-8"
- );
-
- let s = "ššš š“š";
- check!(fs::write(&tmpdir.join("utf8"), s.as_bytes()));
- let string = check!(fs::read_to_string(&tmpdir.join("utf8")));
- assert_eq!(string, s);
- }
-
- #[test]
- fn file_try_clone() {
- let tmpdir = tmpdir();
-
- let mut f1 = check!(
- OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test"))
- );
- let mut f2 = check!(f1.try_clone());
-
- check!(f1.write_all(b"hello world"));
- check!(f1.seek(SeekFrom::Start(2)));
-
- let mut buf = vec![];
- check!(f2.read_to_end(&mut buf));
- assert_eq!(buf, b"llo world");
- drop(f2);
-
- check!(f1.write_all(b"!"));
- }
-
- #[test]
- #[cfg(not(windows))]
- fn unlink_readonly() {
- let tmpdir = tmpdir();
- let path = tmpdir.join("file");
- check!(File::create(&path));
- let mut perm = check!(fs::metadata(&path)).permissions();
- perm.set_readonly(true);
- check!(fs::set_permissions(&path, perm));
- check!(fs::remove_file(&path));
- }
-
- #[test]
- fn mkdir_trailing_slash() {
- let tmpdir = tmpdir();
- let path = tmpdir.join("file");
- check!(fs::create_dir_all(&path.join("a/")));
- }
-
- #[test]
- fn canonicalize_works_simple() {
- let tmpdir = tmpdir();
- let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
- let file = tmpdir.join("test");
- File::create(&file).unwrap();
- assert_eq!(fs::canonicalize(&file).unwrap(), file);
- }
-
- #[test]
- fn realpath_works() {
- let tmpdir = tmpdir();
- if !got_symlink_permission(&tmpdir) {
- return;
- };
-
- let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
- let file = tmpdir.join("test");
- let dir = tmpdir.join("test2");
- let link = dir.join("link");
- let linkdir = tmpdir.join("test3");
-
- File::create(&file).unwrap();
- fs::create_dir(&dir).unwrap();
- symlink_file(&file, &link).unwrap();
- symlink_dir(&dir, &linkdir).unwrap();
-
- assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
-
- assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir);
- assert_eq!(fs::canonicalize(&file).unwrap(), file);
- assert_eq!(fs::canonicalize(&link).unwrap(), file);
- assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir);
- assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file);
- }
-
- #[test]
- fn realpath_works_tricky() {
- let tmpdir = tmpdir();
- if !got_symlink_permission(&tmpdir) {
- return;
- };
-
- let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
- let a = tmpdir.join("a");
- let b = a.join("b");
- let c = b.join("c");
- let d = a.join("d");
- let e = d.join("e");
- let f = a.join("f");
-
- fs::create_dir_all(&b).unwrap();
- fs::create_dir_all(&d).unwrap();
- File::create(&f).unwrap();
- if cfg!(not(windows)) {
- symlink_file("../d/e", &c).unwrap();
- symlink_file("../f", &e).unwrap();
- }
- if cfg!(windows) {
- symlink_file(r"..\d\e", &c).unwrap();
- symlink_file(r"..\f", &e).unwrap();
- }
-
- assert_eq!(fs::canonicalize(&c).unwrap(), f);
- assert_eq!(fs::canonicalize(&e).unwrap(), f);
- }
-
- #[test]
- fn dir_entry_methods() {
- let tmpdir = tmpdir();
-
- fs::create_dir_all(&tmpdir.join("a")).unwrap();
- File::create(&tmpdir.join("b")).unwrap();
-
- for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) {
- let fname = file.file_name();
- match fname.to_str() {
- Some("a") => {
- assert!(file.file_type().unwrap().is_dir());
- assert!(file.metadata().unwrap().is_dir());
- }
- Some("b") => {
- assert!(file.file_type().unwrap().is_file());
- assert!(file.metadata().unwrap().is_file());
- }
- f => panic!("unknown file name: {:?}", f),
- }
- }
- }
-
- #[test]
- fn dir_entry_debug() {
- let tmpdir = tmpdir();
- File::create(&tmpdir.join("b")).unwrap();
- let mut read_dir = tmpdir.path().read_dir().unwrap();
- let dir_entry = read_dir.next().unwrap().unwrap();
- let actual = format!("{:?}", dir_entry);
- let expected = format!("DirEntry({:?})", dir_entry.0.path());
- assert_eq!(actual, expected);
- }
-
- #[test]
- fn read_dir_not_found() {
- let res = fs::read_dir("/path/that/does/not/exist");
- assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
- }
-
- #[test]
- fn create_dir_all_with_junctions() {
- let tmpdir = tmpdir();
- let target = tmpdir.join("target");
-
- let junction = tmpdir.join("junction");
- let b = junction.join("a/b");
-
- let link = tmpdir.join("link");
- let d = link.join("c/d");
-
- fs::create_dir(&target).unwrap();
-
- check!(symlink_junction(&target, &junction));
- check!(fs::create_dir_all(&b));
- // the junction itself is not a directory, but `is_dir()` on a Path
- // follows links
- assert!(junction.is_dir());
- assert!(b.exists());
-
- if !got_symlink_permission(&tmpdir) {
- return;
- };
- check!(symlink_dir(&target, &link));
- check!(fs::create_dir_all(&d));
- assert!(link.is_dir());
- assert!(d.exists());
- }
-
- #[test]
- fn metadata_access_times() {
- let tmpdir = tmpdir();
-
- let b = tmpdir.join("b");
- File::create(&b).unwrap();
-
- let a = check!(fs::metadata(&tmpdir.path()));
- let b = check!(fs::metadata(&b));
-
- assert_eq!(check!(a.accessed()), check!(a.accessed()));
- assert_eq!(check!(a.modified()), check!(a.modified()));
- assert_eq!(check!(b.accessed()), check!(b.modified()));
-
- if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
- check!(a.created());
- check!(b.created());
- }
-
- if cfg!(target_os = "linux") {
- // Not always available
- match (a.created(), b.created()) {
- (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
- (Err(e1), Err(e2))
- if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other => {}
- (a, b) => panic!(
- "creation time must be always supported or not supported: {:?} {:?}",
- a, b,
- ),
- }
- }
- }
-}
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
new file mode 100644
index 0000000..65a2907
--- /dev/null
+++ b/library/std/src/fs/tests.rs
@@ -0,0 +1,1339 @@
+use crate::io::prelude::*;
+
+use crate::fs::{self, File, OpenOptions};
+use crate::io::{ErrorKind, SeekFrom};
+use crate::path::Path;
+use crate::str;
+use crate::sys_common::io::test::{tmpdir, TempDir};
+use crate::thread;
+
+use rand::{rngs::StdRng, RngCore, SeedableRng};
+
+#[cfg(unix)]
+use crate::os::unix::fs::symlink as symlink_dir;
+#[cfg(unix)]
+use crate::os::unix::fs::symlink as symlink_file;
+#[cfg(unix)]
+use crate::os::unix::fs::symlink as symlink_junction;
+#[cfg(windows)]
+use crate::os::windows::fs::{symlink_dir, symlink_file};
+#[cfg(windows)]
+use crate::sys::fs::symlink_junction;
+
+macro_rules! check {
+ ($e:expr) => {
+ match $e {
+ Ok(t) => t,
+ Err(e) => panic!("{} failed with: {}", stringify!($e), e),
+ }
+ };
+}
+
+#[cfg(windows)]
+macro_rules! error {
+ ($e:expr, $s:expr) => {
+ match $e {
+ Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
+ Err(ref err) => assert!(
+ err.raw_os_error() == Some($s),
+ format!("`{}` did not have a code of `{}`", err, $s)
+ ),
+ }
+ };
+}
+
+#[cfg(unix)]
+macro_rules! error {
+ ($e:expr, $s:expr) => {
+ error_contains!($e, $s)
+ };
+}
+
+macro_rules! error_contains {
+ ($e:expr, $s:expr) => {
+ match $e {
+ Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
+ Err(ref err) => {
+ assert!(err.to_string().contains($s), format!("`{}` did not contain `{}`", err, $s))
+ }
+ }
+ };
+}
+
+// Several test fail on windows if the user does not have permission to
+// create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of
+// disabling these test on Windows, use this function to test whether we
+// have permission, and return otherwise. This way, we still don't run these
+// tests most of the time, but at least we do if the user has the right
+// permissions.
+pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
+ if cfg!(unix) {
+ return true;
+ }
+ let link = tmpdir.join("some_hopefully_unique_link_name");
+
+ match symlink_file(r"nonexisting_target", link) {
+ Ok(_) => true,
+ // ERROR_PRIVILEGE_NOT_HELD = 1314
+ Err(ref err) if err.raw_os_error() == Some(1314) => false,
+ Err(_) => true,
+ }
+}
+
+#[test]
+fn file_test_io_smoke_test() {
+ let message = "it's alright. have a good time";
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("file_rt_io_file_test.txt");
+ {
+ let mut write_stream = check!(File::create(filename));
+ check!(write_stream.write(message.as_bytes()));
+ }
+ {
+ let mut read_stream = check!(File::open(filename));
+ let mut read_buf = [0; 1028];
+ let read_str = match check!(read_stream.read(&mut read_buf)) {
+ 0 => panic!("shouldn't happen"),
+ n => str::from_utf8(&read_buf[..n]).unwrap().to_string(),
+ };
+ assert_eq!(read_str, message);
+ }
+ check!(fs::remove_file(filename));
+}
+
+#[test]
+fn invalid_path_raises() {
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("file_that_does_not_exist.txt");
+ let result = File::open(filename);
+
+ #[cfg(all(unix, not(target_os = "vxworks")))]
+ error!(result, "No such file or directory");
+ #[cfg(target_os = "vxworks")]
+ error!(result, "no such file or directory");
+ #[cfg(windows)]
+ error!(result, 2); // ERROR_FILE_NOT_FOUND
+}
+
+#[test]
+fn file_test_iounlinking_invalid_path_should_raise_condition() {
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
+
+ let result = fs::remove_file(filename);
+
+ #[cfg(all(unix, not(target_os = "vxworks")))]
+ error!(result, "No such file or directory");
+ #[cfg(target_os = "vxworks")]
+ error!(result, "no such file or directory");
+ #[cfg(windows)]
+ error!(result, 2); // ERROR_FILE_NOT_FOUND
+}
+
+#[test]
+fn file_test_io_non_positional_read() {
+ let message: &str = "ten-four";
+ let mut read_mem = [0; 8];
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("file_rt_io_file_test_positional.txt");
+ {
+ let mut rw_stream = check!(File::create(filename));
+ check!(rw_stream.write(message.as_bytes()));
+ }
+ {
+ let mut read_stream = check!(File::open(filename));
+ {
+ let read_buf = &mut read_mem[0..4];
+ check!(read_stream.read(read_buf));
+ }
+ {
+ let read_buf = &mut read_mem[4..8];
+ check!(read_stream.read(read_buf));
+ }
+ }
+ check!(fs::remove_file(filename));
+ let read_str = str::from_utf8(&read_mem).unwrap();
+ assert_eq!(read_str, message);
+}
+
+#[test]
+fn file_test_io_seek_and_tell_smoke_test() {
+ let message = "ten-four";
+ let mut read_mem = [0; 4];
+ let set_cursor = 4 as u64;
+ let tell_pos_pre_read;
+ let tell_pos_post_read;
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
+ {
+ let mut rw_stream = check!(File::create(filename));
+ check!(rw_stream.write(message.as_bytes()));
+ }
+ {
+ let mut read_stream = check!(File::open(filename));
+ check!(read_stream.seek(SeekFrom::Start(set_cursor)));
+ tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0)));
+ check!(read_stream.read(&mut read_mem));
+ tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0)));
+ }
+ check!(fs::remove_file(filename));
+ let read_str = str::from_utf8(&read_mem).unwrap();
+ assert_eq!(read_str, &message[4..8]);
+ assert_eq!(tell_pos_pre_read, set_cursor);
+ assert_eq!(tell_pos_post_read, message.len() as u64);
+}
+
+#[test]
+fn file_test_io_seek_and_write() {
+ let initial_msg = "food-is-yummy";
+ let overwrite_msg = "-the-bar!!";
+ let final_msg = "foo-the-bar!!";
+ let seek_idx = 3;
+ let mut read_mem = [0; 13];
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt");
+ {
+ let mut rw_stream = check!(File::create(filename));
+ check!(rw_stream.write(initial_msg.as_bytes()));
+ check!(rw_stream.seek(SeekFrom::Start(seek_idx)));
+ check!(rw_stream.write(overwrite_msg.as_bytes()));
+ }
+ {
+ let mut read_stream = check!(File::open(filename));
+ check!(read_stream.read(&mut read_mem));
+ }
+ check!(fs::remove_file(filename));
+ let read_str = str::from_utf8(&read_mem).unwrap();
+ assert!(read_str == final_msg);
+}
+
+#[test]
+fn file_test_io_seek_shakedown() {
+ // 01234567890123
+ let initial_msg = "qwer-asdf-zxcv";
+ let chunk_one: &str = "qwer";
+ let chunk_two: &str = "asdf";
+ let chunk_three: &str = "zxcv";
+ let mut read_mem = [0; 4];
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt");
+ {
+ let mut rw_stream = check!(File::create(filename));
+ check!(rw_stream.write(initial_msg.as_bytes()));
+ }
+ {
+ let mut read_stream = check!(File::open(filename));
+
+ check!(read_stream.seek(SeekFrom::End(-4)));
+ check!(read_stream.read(&mut read_mem));
+ assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three);
+
+ check!(read_stream.seek(SeekFrom::Current(-9)));
+ check!(read_stream.read(&mut read_mem));
+ assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two);
+
+ check!(read_stream.seek(SeekFrom::Start(0)));
+ check!(read_stream.read(&mut read_mem));
+ assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one);
+ }
+ check!(fs::remove_file(filename));
+}
+
+#[test]
+fn file_test_io_eof() {
+ let tmpdir = tmpdir();
+ let filename = tmpdir.join("file_rt_io_file_test_eof.txt");
+ let mut buf = [0; 256];
+ {
+ let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
+ let mut rw = check!(oo.open(&filename));
+ assert_eq!(check!(rw.read(&mut buf)), 0);
+ assert_eq!(check!(rw.read(&mut buf)), 0);
+ }
+ check!(fs::remove_file(&filename));
+}
+
+#[test]
+#[cfg(unix)]
+fn file_test_io_read_write_at() {
+ use crate::os::unix::fs::FileExt;
+
+ let tmpdir = tmpdir();
+ let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt");
+ let mut buf = [0; 256];
+ let write1 = "asdf";
+ let write2 = "qwer-";
+ let write3 = "-zxcv";
+ let content = "qwer-asdf-zxcv";
+ {
+ let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
+ let mut rw = check!(oo.open(&filename));
+ assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len());
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
+ assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len());
+ assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
+ assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
+ assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0"));
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
+ assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
+ assert_eq!(check!(rw.read(&mut buf)), write1.len());
+ assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
+ assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
+ assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
+ assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len());
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
+ }
+ {
+ let mut read = check!(File::open(&filename));
+ assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
+ assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
+ assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0);
+ assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
+ assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
+ assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
+ assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9);
+ assert_eq!(check!(read.read(&mut buf)), write3.len());
+ assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
+ assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
+ assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
+ assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
+ assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
+ assert_eq!(check!(read.read_at(&mut buf, 14)), 0);
+ assert_eq!(check!(read.read_at(&mut buf, 15)), 0);
+ assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
+ }
+ check!(fs::remove_file(&filename));
+}
+
+#[test]
+#[cfg(unix)]
+fn set_get_unix_permissions() {
+ use crate::os::unix::fs::PermissionsExt;
+
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("set_get_unix_permissions");
+ check!(fs::create_dir(filename));
+ let mask = 0o7777;
+
+ check!(fs::set_permissions(filename, fs::Permissions::from_mode(0)));
+ let metadata0 = check!(fs::metadata(filename));
+ assert_eq!(mask & metadata0.permissions().mode(), 0);
+
+ check!(fs::set_permissions(filename, fs::Permissions::from_mode(0o1777)));
+ let metadata1 = check!(fs::metadata(filename));
+ #[cfg(all(unix, not(target_os = "vxworks")))]
+ assert_eq!(mask & metadata1.permissions().mode(), 0o1777);
+ #[cfg(target_os = "vxworks")]
+ assert_eq!(mask & metadata1.permissions().mode(), 0o0777);
+}
+
+#[test]
+#[cfg(windows)]
+fn file_test_io_seek_read_write() {
+ use crate::os::windows::fs::FileExt;
+
+ let tmpdir = tmpdir();
+ let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt");
+ let mut buf = [0; 256];
+ let write1 = "asdf";
+ let write2 = "qwer-";
+ let write3 = "-zxcv";
+ let content = "qwer-asdf-zxcv";
+ {
+ let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
+ let mut rw = check!(oo.open(&filename));
+ assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len());
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
+ assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len());
+ assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
+ assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0);
+ assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
+ assert_eq!(check!(rw.read(&mut buf)), write1.len());
+ assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
+ assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len());
+ assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
+ assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len());
+ assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14);
+ }
+ {
+ let mut read = check!(File::open(&filename));
+ assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
+ assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
+ assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
+ assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
+ assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
+ assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
+ assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
+ assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
+ assert_eq!(check!(read.read(&mut buf)), write3.len());
+ assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
+ assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
+ assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
+ assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
+ assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
+ assert_eq!(check!(read.seek_read(&mut buf, 14)), 0);
+ assert_eq!(check!(read.seek_read(&mut buf, 15)), 0);
+ }
+ check!(fs::remove_file(&filename));
+}
+
+#[test]
+fn file_test_stat_is_correct_on_is_file() {
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
+ {
+ let mut opts = OpenOptions::new();
+ let mut fs = check!(opts.read(true).write(true).create(true).open(filename));
+ let msg = "hw";
+ fs.write(msg.as_bytes()).unwrap();
+
+ let fstat_res = check!(fs.metadata());
+ assert!(fstat_res.is_file());
+ }
+ let stat_res_fn = check!(fs::metadata(filename));
+ assert!(stat_res_fn.is_file());
+ let stat_res_meth = check!(filename.metadata());
+ assert!(stat_res_meth.is_file());
+ check!(fs::remove_file(filename));
+}
+
+#[test]
+fn file_test_stat_is_correct_on_is_dir() {
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("file_stat_correct_on_is_dir");
+ check!(fs::create_dir(filename));
+ let stat_res_fn = check!(fs::metadata(filename));
+ assert!(stat_res_fn.is_dir());
+ let stat_res_meth = check!(filename.metadata());
+ assert!(stat_res_meth.is_dir());
+ check!(fs::remove_dir(filename));
+}
+
+#[test]
+fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
+ let tmpdir = tmpdir();
+ let dir = &tmpdir.join("fileinfo_false_on_dir");
+ check!(fs::create_dir(dir));
+ assert!(!dir.is_file());
+ check!(fs::remove_dir(dir));
+}
+
+#[test]
+fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
+ let tmpdir = tmpdir();
+ let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt");
+ check!(check!(File::create(file)).write(b"foo"));
+ assert!(file.exists());
+ check!(fs::remove_file(file));
+ assert!(!file.exists());
+}
+
+#[test]
+fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
+ let tmpdir = tmpdir();
+ let dir = &tmpdir.join("before_and_after_dir");
+ assert!(!dir.exists());
+ check!(fs::create_dir(dir));
+ assert!(dir.exists());
+ assert!(dir.is_dir());
+ check!(fs::remove_dir(dir));
+ assert!(!dir.exists());
+}
+
+#[test]
+fn file_test_directoryinfo_readdir() {
+ let tmpdir = tmpdir();
+ let dir = &tmpdir.join("di_readdir");
+ check!(fs::create_dir(dir));
+ let prefix = "foo";
+ for n in 0..3 {
+ let f = dir.join(&format!("{}.txt", n));
+ let mut w = check!(File::create(&f));
+ let msg_str = format!("{}{}", prefix, n.to_string());
+ let msg = msg_str.as_bytes();
+ check!(w.write(msg));
+ }
+ let files = check!(fs::read_dir(dir));
+ let mut mem = [0; 4];
+ for f in files {
+ let f = f.unwrap().path();
+ {
+ let n = f.file_stem().unwrap();
+ check!(check!(File::open(&f)).read(&mut mem));
+ let read_str = str::from_utf8(&mem).unwrap();
+ let expected = format!("{}{}", prefix, n.to_str().unwrap());
+ assert_eq!(expected, read_str);
+ }
+ check!(fs::remove_file(&f));
+ }
+ check!(fs::remove_dir(dir));
+}
+
+#[test]
+fn file_create_new_already_exists_error() {
+ let tmpdir = tmpdir();
+ let file = &tmpdir.join("file_create_new_error_exists");
+ check!(fs::File::create(file));
+ let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err();
+ assert_eq!(e.kind(), ErrorKind::AlreadyExists);
+}
+
+#[test]
+fn mkdir_path_already_exists_error() {
+ let tmpdir = tmpdir();
+ let dir = &tmpdir.join("mkdir_error_twice");
+ check!(fs::create_dir(dir));
+ let e = fs::create_dir(dir).unwrap_err();
+ assert_eq!(e.kind(), ErrorKind::AlreadyExists);
+}
+
+#[test]
+fn recursive_mkdir() {
+ let tmpdir = tmpdir();
+ let dir = tmpdir.join("d1/d2");
+ check!(fs::create_dir_all(&dir));
+ assert!(dir.is_dir())
+}
+
+#[test]
+fn recursive_mkdir_failure() {
+ let tmpdir = tmpdir();
+ let dir = tmpdir.join("d1");
+ let file = dir.join("f1");
+
+ check!(fs::create_dir_all(&dir));
+ check!(File::create(&file));
+
+ let result = fs::create_dir_all(&file);
+
+ assert!(result.is_err());
+}
+
+#[test]
+fn concurrent_recursive_mkdir() {
+ for _ in 0..100 {
+ let dir = tmpdir();
+ let mut dir = dir.join("a");
+ for _ in 0..40 {
+ dir = dir.join("a");
+ }
+ let mut join = vec![];
+ for _ in 0..8 {
+ let dir = dir.clone();
+ join.push(thread::spawn(move || {
+ check!(fs::create_dir_all(&dir));
+ }))
+ }
+
+ // No `Display` on result of `join()`
+ join.drain(..).map(|join| join.join().unwrap()).count();
+ }
+}
+
+#[test]
+fn recursive_mkdir_slash() {
+ check!(fs::create_dir_all(Path::new("/")));
+}
+
+#[test]
+fn recursive_mkdir_dot() {
+ check!(fs::create_dir_all(Path::new(".")));
+}
+
+#[test]
+fn recursive_mkdir_empty() {
+ check!(fs::create_dir_all(Path::new("")));
+}
+
+#[test]
+fn recursive_rmdir() {
+ let tmpdir = tmpdir();
+ let d1 = tmpdir.join("d1");
+ let dt = d1.join("t");
+ let dtt = dt.join("t");
+ let d2 = tmpdir.join("d2");
+ let canary = d2.join("do_not_delete");
+ check!(fs::create_dir_all(&dtt));
+ check!(fs::create_dir_all(&d2));
+ check!(check!(File::create(&canary)).write(b"foo"));
+ check!(symlink_junction(&d2, &dt.join("d2")));
+ let _ = symlink_file(&canary, &d1.join("canary"));
+ check!(fs::remove_dir_all(&d1));
+
+ assert!(!d1.is_dir());
+ assert!(canary.exists());
+}
+
+#[test]
+fn recursive_rmdir_of_symlink() {
+ // test we do not recursively delete a symlink but only dirs.
+ let tmpdir = tmpdir();
+ let link = tmpdir.join("d1");
+ let dir = tmpdir.join("d2");
+ let canary = dir.join("do_not_delete");
+ check!(fs::create_dir_all(&dir));
+ check!(check!(File::create(&canary)).write(b"foo"));
+ check!(symlink_junction(&dir, &link));
+ check!(fs::remove_dir_all(&link));
+
+ assert!(!link.is_dir());
+ assert!(canary.exists());
+}
+
+#[test]
+// only Windows makes a distinction between file and directory symlinks.
+#[cfg(windows)]
+fn recursive_rmdir_of_file_symlink() {
+ let tmpdir = tmpdir();
+ if !got_symlink_permission(&tmpdir) {
+ return;
+ };
+
+ let f1 = tmpdir.join("f1");
+ let f2 = tmpdir.join("f2");
+ check!(check!(File::create(&f1)).write(b"foo"));
+ check!(symlink_file(&f1, &f2));
+ match fs::remove_dir_all(&f2) {
+ Ok(..) => panic!("wanted a failure"),
+ Err(..) => {}
+ }
+}
+
+#[test]
+fn unicode_path_is_dir() {
+ assert!(Path::new(".").is_dir());
+ assert!(!Path::new("test/stdtest/fs.rs").is_dir());
+
+ let tmpdir = tmpdir();
+
+ let mut dirpath = tmpdir.path().to_path_buf();
+ dirpath.push("test-ź°äøć¼ä½ 儽");
+ check!(fs::create_dir(&dirpath));
+ assert!(dirpath.is_dir());
+
+ let mut filepath = dirpath;
+ filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs");
+ check!(File::create(&filepath)); // ignore return; touch only
+ assert!(!filepath.is_dir());
+ assert!(filepath.exists());
+}
+
+#[test]
+fn unicode_path_exists() {
+ assert!(Path::new(".").exists());
+ assert!(!Path::new("test/nonexistent-bogus-path").exists());
+
+ let tmpdir = tmpdir();
+ let unicode = tmpdir.path();
+ let unicode = unicode.join("test-ź°äøć¼åč§");
+ check!(fs::create_dir(&unicode));
+ assert!(unicode.exists());
+ assert!(!Path::new("test/unicode-bogus-path-ź°äøć¼åč§").exists());
+}
+
+#[test]
+fn copy_file_does_not_exist() {
+ let from = Path::new("test/nonexistent-bogus-path");
+ let to = Path::new("test/other-bogus-path");
+
+ match fs::copy(&from, &to) {
+ Ok(..) => panic!(),
+ Err(..) => {
+ assert!(!from.exists());
+ assert!(!to.exists());
+ }
+ }
+}
+
+#[test]
+fn copy_src_does_not_exist() {
+ let tmpdir = tmpdir();
+ let from = Path::new("test/nonexistent-bogus-path");
+ let to = tmpdir.join("out.txt");
+ check!(check!(File::create(&to)).write(b"hello"));
+ assert!(fs::copy(&from, &to).is_err());
+ assert!(!from.exists());
+ let mut v = Vec::new();
+ check!(check!(File::open(&to)).read_to_end(&mut v));
+ assert_eq!(v, b"hello");
+}
+
+#[test]
+fn copy_file_ok() {
+ let tmpdir = tmpdir();
+ let input = tmpdir.join("in.txt");
+ let out = tmpdir.join("out.txt");
+
+ check!(check!(File::create(&input)).write(b"hello"));
+ check!(fs::copy(&input, &out));
+ let mut v = Vec::new();
+ check!(check!(File::open(&out)).read_to_end(&mut v));
+ assert_eq!(v, b"hello");
+
+ assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions());
+}
+
+#[test]
+fn copy_file_dst_dir() {
+ let tmpdir = tmpdir();
+ let out = tmpdir.join("out");
+
+ check!(File::create(&out));
+ match fs::copy(&*out, tmpdir.path()) {
+ Ok(..) => panic!(),
+ Err(..) => {}
+ }
+}
+
+#[test]
+fn copy_file_dst_exists() {
+ let tmpdir = tmpdir();
+ let input = tmpdir.join("in");
+ let output = tmpdir.join("out");
+
+ check!(check!(File::create(&input)).write("foo".as_bytes()));
+ check!(check!(File::create(&output)).write("bar".as_bytes()));
+ check!(fs::copy(&input, &output));
+
+ let mut v = Vec::new();
+ check!(check!(File::open(&output)).read_to_end(&mut v));
+ assert_eq!(v, b"foo".to_vec());
+}
+
+#[test]
+fn copy_file_src_dir() {
+ let tmpdir = tmpdir();
+ let out = tmpdir.join("out");
+
+ match fs::copy(tmpdir.path(), &out) {
+ Ok(..) => panic!(),
+ Err(..) => {}
+ }
+ assert!(!out.exists());
+}
+
+#[test]
+fn copy_file_preserves_perm_bits() {
+ let tmpdir = tmpdir();
+ let input = tmpdir.join("in.txt");
+ let out = tmpdir.join("out.txt");
+
+ let attr = check!(check!(File::create(&input)).metadata());
+ let mut p = attr.permissions();
+ p.set_readonly(true);
+ check!(fs::set_permissions(&input, p));
+ check!(fs::copy(&input, &out));
+ assert!(check!(out.metadata()).permissions().readonly());
+ check!(fs::set_permissions(&input, attr.permissions()));
+ check!(fs::set_permissions(&out, attr.permissions()));
+}
+
+#[test]
+#[cfg(windows)]
+fn copy_file_preserves_streams() {
+ let tmp = tmpdir();
+ check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes()));
+ assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0);
+ assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0);
+ let mut v = Vec::new();
+ check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v));
+ assert_eq!(v, b"carrot".to_vec());
+}
+
+#[test]
+fn copy_file_returns_metadata_len() {
+ let tmp = tmpdir();
+ let in_path = tmp.join("in.txt");
+ let out_path = tmp.join("out.txt");
+ check!(check!(File::create(&in_path)).write(b"lettuce"));
+ #[cfg(windows)]
+ check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot"));
+ let copied_len = check!(fs::copy(&in_path, &out_path));
+ assert_eq!(check!(out_path.metadata()).len(), copied_len);
+}
+
+#[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;
+ };
+
+ let input = tmpdir.join("in.txt");
+ let out = tmpdir.join("out.txt");
+
+ check!(check!(File::create(&input)).write("foobar".as_bytes()));
+ check!(symlink_file(&input, &out));
+ assert!(check!(out.symlink_metadata()).file_type().is_symlink());
+ assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
+ let mut v = Vec::new();
+ check!(check!(File::open(&out)).read_to_end(&mut v));
+ assert_eq!(v, b"foobar".to_vec());
+}
+
+#[test]
+fn symlink_noexist() {
+ // Symlinks can point to things that don't exist
+ let tmpdir = tmpdir();
+ if !got_symlink_permission(&tmpdir) {
+ return;
+ };
+
+ // Use a relative path for testing. Symlinks get normalized by Windows,
+ // so we may not get the same path back for absolute paths
+ check!(symlink_file(&"foo", &tmpdir.join("bar")));
+ assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo");
+}
+
+#[test]
+fn read_link() {
+ if cfg!(windows) {
+ // directory symlink
+ assert_eq!(
+ check!(fs::read_link(r"C:\Users\All Users")).to_str().unwrap(),
+ r"C:\ProgramData"
+ );
+ // junction
+ assert_eq!(
+ check!(fs::read_link(r"C:\Users\Default User")).to_str().unwrap(),
+ r"C:\Users\Default"
+ );
+ // junction with special permissions
+ assert_eq!(
+ check!(fs::read_link(r"C:\Documents and Settings\")).to_str().unwrap(),
+ r"C:\Users"
+ );
+ }
+ let tmpdir = tmpdir();
+ let link = tmpdir.join("link");
+ if !got_symlink_permission(&tmpdir) {
+ return;
+ };
+ check!(symlink_file(&"foo", &link));
+ assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo");
+}
+
+#[test]
+fn readlink_not_symlink() {
+ let tmpdir = tmpdir();
+ match fs::read_link(tmpdir.path()) {
+ Ok(..) => panic!("wanted a failure"),
+ Err(..) => {}
+ }
+}
+
+#[test]
+fn links_work() {
+ let tmpdir = tmpdir();
+ let input = tmpdir.join("in.txt");
+ let out = tmpdir.join("out.txt");
+
+ check!(check!(File::create(&input)).write("foobar".as_bytes()));
+ check!(fs::hard_link(&input, &out));
+ assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
+ assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len());
+ let mut v = Vec::new();
+ check!(check!(File::open(&out)).read_to_end(&mut v));
+ assert_eq!(v, b"foobar".to_vec());
+
+ // can't link to yourself
+ match fs::hard_link(&input, &input) {
+ Ok(..) => panic!("wanted a failure"),
+ Err(..) => {}
+ }
+ // can't link to something that doesn't exist
+ match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
+ Ok(..) => panic!("wanted a failure"),
+ Err(..) => {}
+ }
+}
+
+#[test]
+fn chmod_works() {
+ let tmpdir = tmpdir();
+ let file = tmpdir.join("in.txt");
+
+ check!(File::create(&file));
+ let attr = check!(fs::metadata(&file));
+ assert!(!attr.permissions().readonly());
+ let mut p = attr.permissions();
+ p.set_readonly(true);
+ check!(fs::set_permissions(&file, p.clone()));
+ let attr = check!(fs::metadata(&file));
+ assert!(attr.permissions().readonly());
+
+ match fs::set_permissions(&tmpdir.join("foo"), p.clone()) {
+ Ok(..) => panic!("wanted an error"),
+ Err(..) => {}
+ }
+
+ p.set_readonly(false);
+ check!(fs::set_permissions(&file, p));
+}
+
+#[test]
+fn fchmod_works() {
+ let tmpdir = tmpdir();
+ let path = tmpdir.join("in.txt");
+
+ let file = check!(File::create(&path));
+ let attr = check!(fs::metadata(&path));
+ assert!(!attr.permissions().readonly());
+ let mut p = attr.permissions();
+ p.set_readonly(true);
+ check!(file.set_permissions(p.clone()));
+ let attr = check!(fs::metadata(&path));
+ assert!(attr.permissions().readonly());
+
+ p.set_readonly(false);
+ check!(file.set_permissions(p));
+}
+
+#[test]
+fn sync_doesnt_kill_anything() {
+ let tmpdir = tmpdir();
+ let path = tmpdir.join("in.txt");
+
+ let mut file = check!(File::create(&path));
+ check!(file.sync_all());
+ check!(file.sync_data());
+ check!(file.write(b"foo"));
+ check!(file.sync_all());
+ check!(file.sync_data());
+}
+
+#[test]
+fn truncate_works() {
+ let tmpdir = tmpdir();
+ let path = tmpdir.join("in.txt");
+
+ let mut file = check!(File::create(&path));
+ check!(file.write(b"foo"));
+ check!(file.sync_all());
+
+ // Do some simple things with truncation
+ assert_eq!(check!(file.metadata()).len(), 3);
+ check!(file.set_len(10));
+ assert_eq!(check!(file.metadata()).len(), 10);
+ check!(file.write(b"bar"));
+ check!(file.sync_all());
+ assert_eq!(check!(file.metadata()).len(), 10);
+
+ let mut v = Vec::new();
+ check!(check!(File::open(&path)).read_to_end(&mut v));
+ assert_eq!(v, b"foobar\0\0\0\0".to_vec());
+
+ // Truncate to a smaller length, don't seek, and then write something.
+ // Ensure that the intermediate zeroes are all filled in (we have `seek`ed
+ // past the end of the file).
+ check!(file.set_len(2));
+ assert_eq!(check!(file.metadata()).len(), 2);
+ check!(file.write(b"wut"));
+ check!(file.sync_all());
+ assert_eq!(check!(file.metadata()).len(), 9);
+ let mut v = Vec::new();
+ check!(check!(File::open(&path)).read_to_end(&mut v));
+ assert_eq!(v, b"fo\0\0\0\0wut".to_vec());
+}
+
+#[test]
+fn open_flavors() {
+ use crate::fs::OpenOptions as OO;
+ fn c<T: Clone>(t: &T) -> T {
+ t.clone()
+ }
+
+ let tmpdir = tmpdir();
+
+ let mut r = OO::new();
+ r.read(true);
+ let mut w = OO::new();
+ w.write(true);
+ let mut rw = OO::new();
+ rw.read(true).write(true);
+ let mut a = OO::new();
+ a.append(true);
+ let mut ra = OO::new();
+ ra.read(true).append(true);
+
+ #[cfg(windows)]
+ let invalid_options = 87; // ERROR_INVALID_PARAMETER
+ #[cfg(all(unix, not(target_os = "vxworks")))]
+ let invalid_options = "Invalid argument";
+ #[cfg(target_os = "vxworks")]
+ let invalid_options = "invalid argument";
+
+ // Test various combinations of creation modes and access modes.
+ //
+ // Allowed:
+ // creation mode | read | write | read-write | append | read-append |
+ // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:|
+ // not set (open existing) | X | X | X | X | X |
+ // create | | X | X | X | X |
+ // truncate | | X | X | | |
+ // create and truncate | | X | X | | |
+ // create_new | | X | X | X | X |
+ //
+ // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
+
+ // write-only
+ check!(c(&w).create_new(true).open(&tmpdir.join("a")));
+ check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a")));
+ check!(c(&w).truncate(true).open(&tmpdir.join("a")));
+ check!(c(&w).create(true).open(&tmpdir.join("a")));
+ check!(c(&w).open(&tmpdir.join("a")));
+
+ // read-only
+ error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options);
+ error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options);
+ error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options);
+ error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options);
+ check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only
+
+ // read-write
+ check!(c(&rw).create_new(true).open(&tmpdir.join("c")));
+ check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c")));
+ check!(c(&rw).truncate(true).open(&tmpdir.join("c")));
+ check!(c(&rw).create(true).open(&tmpdir.join("c")));
+ check!(c(&rw).open(&tmpdir.join("c")));
+
+ // append
+ check!(c(&a).create_new(true).open(&tmpdir.join("d")));
+ error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options);
+ error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options);
+ check!(c(&a).create(true).open(&tmpdir.join("d")));
+ check!(c(&a).open(&tmpdir.join("d")));
+
+ // read-append
+ check!(c(&ra).create_new(true).open(&tmpdir.join("e")));
+ error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options);
+ error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options);
+ check!(c(&ra).create(true).open(&tmpdir.join("e")));
+ check!(c(&ra).open(&tmpdir.join("e")));
+
+ // Test opening a file without setting an access mode
+ let mut blank = OO::new();
+ error!(blank.create(true).open(&tmpdir.join("f")), invalid_options);
+
+ // Test write works
+ check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
+
+ // Test write fails for read-only
+ check!(r.open(&tmpdir.join("h")));
+ {
+ let mut f = check!(r.open(&tmpdir.join("h")));
+ assert!(f.write("wut".as_bytes()).is_err());
+ }
+
+ // Test write overwrites
+ {
+ let mut f = check!(c(&w).open(&tmpdir.join("h")));
+ check!(f.write("baz".as_bytes()));
+ }
+ {
+ let mut f = check!(c(&r).open(&tmpdir.join("h")));
+ let mut b = vec![0; 6];
+ check!(f.read(&mut b));
+ assert_eq!(b, "bazbar".as_bytes());
+ }
+
+ // Test truncate works
+ {
+ let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
+ check!(f.write("foo".as_bytes()));
+ }
+ assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
+
+ // Test append works
+ assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
+ {
+ let mut f = check!(c(&a).open(&tmpdir.join("h")));
+ check!(f.write("bar".as_bytes()));
+ }
+ assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
+
+ // Test .append(true) equals .write(true).append(true)
+ {
+ let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
+ check!(f.write("baz".as_bytes()));
+ }
+ assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9);
+}
+
+#[test]
+fn _assert_send_sync() {
+ fn _assert_send_sync<T: Send + Sync>() {}
+ _assert_send_sync::<OpenOptions>();
+}
+
+#[test]
+fn binary_file() {
+ let mut bytes = [0; 1024];
+ StdRng::from_entropy().fill_bytes(&mut bytes);
+
+ let tmpdir = tmpdir();
+
+ check!(check!(File::create(&tmpdir.join("test"))).write(&bytes));
+ let mut v = Vec::new();
+ check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v));
+ assert!(v == &bytes[..]);
+}
+
+#[test]
+fn write_then_read() {
+ let mut bytes = [0; 1024];
+ StdRng::from_entropy().fill_bytes(&mut bytes);
+
+ let tmpdir = tmpdir();
+
+ check!(fs::write(&tmpdir.join("test"), &bytes[..]));
+ let v = check!(fs::read(&tmpdir.join("test")));
+ assert!(v == &bytes[..]);
+
+ check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
+ error_contains!(
+ fs::read_to_string(&tmpdir.join("not-utf8")),
+ "stream did not contain valid UTF-8"
+ );
+
+ let s = "ššš š“š";
+ check!(fs::write(&tmpdir.join("utf8"), s.as_bytes()));
+ let string = check!(fs::read_to_string(&tmpdir.join("utf8")));
+ assert_eq!(string, s);
+}
+
+#[test]
+fn file_try_clone() {
+ let tmpdir = tmpdir();
+
+ let mut f1 =
+ check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test")));
+ let mut f2 = check!(f1.try_clone());
+
+ check!(f1.write_all(b"hello world"));
+ check!(f1.seek(SeekFrom::Start(2)));
+
+ let mut buf = vec![];
+ check!(f2.read_to_end(&mut buf));
+ assert_eq!(buf, b"llo world");
+ drop(f2);
+
+ check!(f1.write_all(b"!"));
+}
+
+#[test]
+#[cfg(not(windows))]
+fn unlink_readonly() {
+ let tmpdir = tmpdir();
+ let path = tmpdir.join("file");
+ check!(File::create(&path));
+ let mut perm = check!(fs::metadata(&path)).permissions();
+ perm.set_readonly(true);
+ check!(fs::set_permissions(&path, perm));
+ check!(fs::remove_file(&path));
+}
+
+#[test]
+fn mkdir_trailing_slash() {
+ let tmpdir = tmpdir();
+ let path = tmpdir.join("file");
+ check!(fs::create_dir_all(&path.join("a/")));
+}
+
+#[test]
+fn canonicalize_works_simple() {
+ let tmpdir = tmpdir();
+ let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
+ let file = tmpdir.join("test");
+ File::create(&file).unwrap();
+ assert_eq!(fs::canonicalize(&file).unwrap(), file);
+}
+
+#[test]
+fn realpath_works() {
+ let tmpdir = tmpdir();
+ if !got_symlink_permission(&tmpdir) {
+ return;
+ };
+
+ let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
+ let file = tmpdir.join("test");
+ let dir = tmpdir.join("test2");
+ let link = dir.join("link");
+ let linkdir = tmpdir.join("test3");
+
+ File::create(&file).unwrap();
+ fs::create_dir(&dir).unwrap();
+ symlink_file(&file, &link).unwrap();
+ symlink_dir(&dir, &linkdir).unwrap();
+
+ assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
+
+ assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir);
+ assert_eq!(fs::canonicalize(&file).unwrap(), file);
+ assert_eq!(fs::canonicalize(&link).unwrap(), file);
+ assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir);
+ assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file);
+}
+
+#[test]
+fn realpath_works_tricky() {
+ let tmpdir = tmpdir();
+ if !got_symlink_permission(&tmpdir) {
+ return;
+ };
+
+ let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
+ let a = tmpdir.join("a");
+ let b = a.join("b");
+ let c = b.join("c");
+ let d = a.join("d");
+ let e = d.join("e");
+ let f = a.join("f");
+
+ fs::create_dir_all(&b).unwrap();
+ fs::create_dir_all(&d).unwrap();
+ File::create(&f).unwrap();
+ if cfg!(not(windows)) {
+ symlink_file("../d/e", &c).unwrap();
+ symlink_file("../f", &e).unwrap();
+ }
+ if cfg!(windows) {
+ symlink_file(r"..\d\e", &c).unwrap();
+ symlink_file(r"..\f", &e).unwrap();
+ }
+
+ assert_eq!(fs::canonicalize(&c).unwrap(), f);
+ assert_eq!(fs::canonicalize(&e).unwrap(), f);
+}
+
+#[test]
+fn dir_entry_methods() {
+ let tmpdir = tmpdir();
+
+ fs::create_dir_all(&tmpdir.join("a")).unwrap();
+ File::create(&tmpdir.join("b")).unwrap();
+
+ for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) {
+ let fname = file.file_name();
+ match fname.to_str() {
+ Some("a") => {
+ assert!(file.file_type().unwrap().is_dir());
+ assert!(file.metadata().unwrap().is_dir());
+ }
+ Some("b") => {
+ assert!(file.file_type().unwrap().is_file());
+ assert!(file.metadata().unwrap().is_file());
+ }
+ f => panic!("unknown file name: {:?}", f),
+ }
+ }
+}
+
+#[test]
+fn dir_entry_debug() {
+ let tmpdir = tmpdir();
+ File::create(&tmpdir.join("b")).unwrap();
+ let mut read_dir = tmpdir.path().read_dir().unwrap();
+ let dir_entry = read_dir.next().unwrap().unwrap();
+ let actual = format!("{:?}", dir_entry);
+ let expected = format!("DirEntry({:?})", dir_entry.0.path());
+ assert_eq!(actual, expected);
+}
+
+#[test]
+fn read_dir_not_found() {
+ let res = fs::read_dir("/path/that/does/not/exist");
+ assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
+}
+
+#[test]
+fn create_dir_all_with_junctions() {
+ let tmpdir = tmpdir();
+ let target = tmpdir.join("target");
+
+ let junction = tmpdir.join("junction");
+ let b = junction.join("a/b");
+
+ let link = tmpdir.join("link");
+ let d = link.join("c/d");
+
+ fs::create_dir(&target).unwrap();
+
+ check!(symlink_junction(&target, &junction));
+ check!(fs::create_dir_all(&b));
+ // the junction itself is not a directory, but `is_dir()` on a Path
+ // follows links
+ assert!(junction.is_dir());
+ assert!(b.exists());
+
+ if !got_symlink_permission(&tmpdir) {
+ return;
+ };
+ check!(symlink_dir(&target, &link));
+ check!(fs::create_dir_all(&d));
+ assert!(link.is_dir());
+ assert!(d.exists());
+}
+
+#[test]
+fn metadata_access_times() {
+ let tmpdir = tmpdir();
+
+ let b = tmpdir.join("b");
+ File::create(&b).unwrap();
+
+ let a = check!(fs::metadata(&tmpdir.path()));
+ let b = check!(fs::metadata(&b));
+
+ assert_eq!(check!(a.accessed()), check!(a.accessed()));
+ assert_eq!(check!(a.modified()), check!(a.modified()));
+ assert_eq!(check!(b.accessed()), check!(b.modified()));
+
+ if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
+ check!(a.created());
+ check!(b.created());
+ }
+
+ if cfg!(target_os = "linux") {
+ // Not always available
+ match (a.created(), b.created()) {
+ (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
+ (Err(e1), Err(e2))
+ if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other => {}
+ (a, b) => {
+ panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,)
+ }
+ }
+ }
+}
diff --git a/library/std/src/future.rs b/library/std/src/future.rs
index 89dd9fb..9d9c36e 100644
--- a/library/std/src/future.rs
+++ b/library/std/src/future.rs
@@ -9,7 +9,7 @@
pub use core::future::{from_generator, get_context, ResumeTy};
#[doc(inline)]
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.48.0")]
pub use core::future::{pending, ready, Pending, Ready};
#[doc(inline)]
diff --git a/library/std/src/io/buffered.rs b/library/std/src/io/buffered.rs
index f3aadf29b..97c4b87 100644
--- a/library/std/src/io/buffered.rs
+++ b/library/std/src/io/buffered.rs
@@ -1,5 +1,8 @@
//! Buffering wrappers for I/O traits
+#[cfg(test)]
+mod tests;
+
use crate::io::prelude::*;
use crate::cmp;
@@ -383,6 +386,51 @@
self.discard_buffer();
Ok(result)
}
+
+ /// Returns the current seek position from the start of the stream.
+ ///
+ /// The value returned is equivalent to `self.seek(SeekFrom::Current(0))`
+ /// but does not flush the internal buffer. Due to this optimization the
+ /// function does not guarantee that calling `.into_inner()` immediately
+ /// afterwards will yield the underlying reader at the same position. Use
+ /// [`BufReader::seek`] instead if you require that guarantee.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if the position of the inner reader is smaller
+ /// than the amount of buffered data. That can happen if the inner reader
+ /// has an incorrect implementation of [`Seek::stream_position`], or if the
+ /// position has gone out of sync due to calling [`Seek::seek`] directly on
+ /// the underlying reader.
+ ///
+ /// # 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(())
+ /// }
+ /// ```
+ fn stream_position(&mut self) -> io::Result<u64> {
+ let remainder = (self.cap - self.pos) as u64;
+ self.inner.stream_position().map(|pos| {
+ pos.checked_sub(remainder).expect(
+ "overflow when subtracting remaining buffer size from inner stream position",
+ )
+ })
+ }
}
/// Wraps a writer and buffers its output.
@@ -517,33 +565,81 @@
BufWriter { inner: Some(inner), buf: Vec::with_capacity(capacity), panicked: false }
}
+ /// Send data in our local buffer into the inner writer, looping as
+ /// necessary until either it's all been sent or an error occurs.
+ ///
+ /// Because all the data in the buffer has been reported to our owner as
+ /// "successfully written" (by returning nonzero success values from
+ /// `write`), any 0-length writes from `inner` must be reported as i/o
+ /// errors from this method.
fn flush_buf(&mut self) -> io::Result<()> {
- let mut written = 0;
- let len = self.buf.len();
- let mut ret = Ok(());
- while written < len {
+ /// Helper struct to ensure the buffer is updated after all the writes
+ /// are complete. It tracks the number of written bytes and drains them
+ /// all from the front of the buffer when dropped.
+ struct BufGuard<'a> {
+ buffer: &'a mut Vec<u8>,
+ written: usize,
+ }
+
+ impl<'a> BufGuard<'a> {
+ fn new(buffer: &'a mut Vec<u8>) -> Self {
+ Self { buffer, written: 0 }
+ }
+
+ /// The unwritten part of the buffer
+ fn remaining(&self) -> &[u8] {
+ &self.buffer[self.written..]
+ }
+
+ /// Flag some bytes as removed from the front of the buffer
+ fn consume(&mut self, amt: usize) {
+ self.written += amt;
+ }
+
+ /// true if all of the bytes have been written
+ fn done(&self) -> bool {
+ self.written >= self.buffer.len()
+ }
+ }
+
+ impl Drop for BufGuard<'_> {
+ fn drop(&mut self) {
+ if self.written > 0 {
+ self.buffer.drain(..self.written);
+ }
+ }
+ }
+
+ let mut guard = BufGuard::new(&mut self.buf);
+ let inner = self.inner.as_mut().unwrap();
+ while !guard.done() {
self.panicked = true;
- let r = self.inner.as_mut().unwrap().write(&self.buf[written..]);
+ let r = inner.write(guard.remaining());
self.panicked = false;
match r {
Ok(0) => {
- ret =
- Err(Error::new(ErrorKind::WriteZero, "failed to write the buffered data"));
- break;
+ return Err(Error::new(
+ ErrorKind::WriteZero,
+ "failed to write the buffered data",
+ ));
}
- Ok(n) => written += n,
+ Ok(n) => guard.consume(n),
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
- Err(e) => {
- ret = Err(e);
- break;
- }
+ Err(e) => return Err(e),
}
}
- if written > 0 {
- self.buf.drain(..written);
- }
- ret
+ Ok(())
+ }
+
+ /// Buffer some data without flushing it, regardless of the size of the
+ /// data. Writes as much as possible without exceeding capacity. Returns
+ /// the number of bytes written.
+ fn write_to_buf(&mut self, buf: &[u8]) -> usize {
+ let available = self.buf.capacity() - self.buf.len();
+ let amt_to_buffer = available.min(buf.len());
+ self.buf.extend_from_slice(&buf[..amt_to_buffer]);
+ amt_to_buffer
}
/// Gets a reference to the underlying writer.
@@ -656,13 +752,35 @@
if self.buf.len() + buf.len() > self.buf.capacity() {
self.flush_buf()?;
}
+ // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
if buf.len() >= self.buf.capacity() {
self.panicked = true;
let r = self.get_mut().write(buf);
self.panicked = false;
r
} else {
- self.buf.write(buf)
+ self.buf.extend_from_slice(buf);
+ Ok(buf.len())
+ }
+ }
+
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ // Normally, `write_all` just calls `write` in a loop. We can do better
+ // by calling `self.get_mut().write_all()` directly, which avoids
+ // round trips through the buffer in the event of a series of partial
+ // writes in some circumstances.
+ if self.buf.len() + buf.len() > self.buf.capacity() {
+ self.flush_buf()?;
+ }
+ // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
+ if buf.len() >= self.buf.capacity() {
+ self.panicked = true;
+ let r = self.get_mut().write_all(buf);
+ self.panicked = false;
+ r
+ } else {
+ self.buf.extend_from_slice(buf);
+ Ok(())
}
}
@@ -671,13 +789,15 @@
if self.buf.len() + total_len > self.buf.capacity() {
self.flush_buf()?;
}
+ // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919
if total_len >= self.buf.capacity() {
self.panicked = true;
let r = self.get_mut().write_vectored(bufs);
self.panicked = false;
r
} else {
- self.buf.write_vectored(bufs)
+ bufs.iter().for_each(|b| self.buf.extend_from_slice(b));
+ Ok(total_len)
}
}
@@ -709,7 +829,8 @@
///
/// Seeking always writes out the internal buffer before seeking.
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
- self.flush_buf().and_then(|_| self.get_mut().seek(pos))
+ self.flush_buf()?;
+ self.get_mut().seek(pos)
}
}
@@ -816,6 +937,274 @@
}
}
+/// Private helper struct for implementing the line-buffered writing logic.
+/// This shim temporarily wraps a BufWriter, and uses its internals to
+/// implement a line-buffered writer (specifically by using the internal
+/// methods like write_to_buf and flush_buf). In this way, a more
+/// efficient abstraction can be created than one that only had access to
+/// `write` and `flush`, without needlessly duplicating a lot of the
+/// implementation details of BufWriter. This also allows existing
+/// `BufWriters` to be temporarily given line-buffering logic; this is what
+/// enables Stdout to be alternately in line-buffered or block-buffered mode.
+#[derive(Debug)]
+pub(super) struct LineWriterShim<'a, W: Write> {
+ buffer: &'a mut BufWriter<W>,
+}
+
+impl<'a, W: Write> LineWriterShim<'a, W> {
+ pub fn new(buffer: &'a mut BufWriter<W>) -> Self {
+ Self { buffer }
+ }
+
+ /// Get a mutable reference to the inner writer (that is, the writer
+ /// wrapped by the BufWriter). Be careful with this writer, as writes to
+ /// it will bypass the buffer.
+ fn inner_mut(&mut self) -> &mut W {
+ self.buffer.get_mut()
+ }
+
+ /// Get the content currently buffered in self.buffer
+ fn buffered(&self) -> &[u8] {
+ self.buffer.buffer()
+ }
+
+ /// Flush the buffer iff the last byte is a newline (indicating that an
+ /// earlier write only succeeded partially, and we want to retry flushing
+ /// the buffered line before continuing with a subsequent write)
+ fn flush_if_completed_line(&mut self) -> io::Result<()> {
+ match self.buffered().last().copied() {
+ Some(b'\n') => self.buffer.flush_buf(),
+ _ => Ok(()),
+ }
+ }
+}
+
+impl<'a, W: Write> Write for LineWriterShim<'a, W> {
+ /// Write some data into this BufReader with line buffering. This means
+ /// that, if any newlines are present in the data, the data up to the last
+ /// newline is sent directly to the underlying writer, and data after it
+ /// is buffered. Returns the number of bytes written.
+ ///
+ /// This function operates on a "best effort basis"; in keeping with the
+ /// convention of `Write::write`, it makes at most one attempt to write
+ /// new data to the underlying writer. If that write only reports a partial
+ /// success, the remaining data will be buffered.
+ ///
+ /// Because this function attempts to send completed lines to the underlying
+ /// writer, it will also flush the existing buffer if it ends with a
+ /// newline, even if the incoming data does not contain any newlines.
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let newline_idx = match memchr::memrchr(b'\n', buf) {
+ // If there are no new newlines (that is, if this write is less than
+ // one line), just do a regular buffered write (which may flush if
+ // we exceed the inner buffer's size)
+ None => {
+ self.flush_if_completed_line()?;
+ return self.buffer.write(buf);
+ }
+ // Otherwise, arrange for the lines to be written directly to the
+ // inner writer.
+ Some(newline_idx) => newline_idx + 1,
+ };
+
+ // Flush existing content to prepare for our write. We have to do this
+ // before attempting to write `buf` in order to maintain consistency;
+ // if we add `buf` to the buffer then try to flush it all at once,
+ // we're obligated to return Ok(), which would mean suppressing any
+ // errors that occur during flush.
+ self.buffer.flush_buf()?;
+
+ // This is what we're going to try to write directly to the inner
+ // writer. The rest will be buffered, if nothing goes wrong.
+ let lines = &buf[..newline_idx];
+
+ // Write `lines` directly to the inner writer. In keeping with the
+ // `write` convention, make at most one attempt to add new (unbuffered)
+ // data. Because this write doesn't touch the BufWriter state directly,
+ // and the buffer is known to be empty, we don't need to worry about
+ // self.buffer.panicked here.
+ let flushed = self.inner_mut().write(lines)?;
+
+ // If buffer returns Ok(0), propagate that to the caller without
+ // doing additional buffering; otherwise we're just guaranteeing
+ // an "ErrorKind::WriteZero" later.
+ if flushed == 0 {
+ return Ok(0);
+ }
+
+ // Now that the write has succeeded, buffer the rest (or as much of
+ // the rest as possible). If there were any unwritten newlines, we
+ // only buffer out to the last unwritten newline that fits in the
+ // buffer; this helps prevent flushing partial lines on subsequent
+ // calls to LineWriterShim::write.
+
+ // Handle the cases in order of most-common to least-common, under
+ // the presumption that most writes succeed in totality, and that most
+ // writes are smaller than the buffer.
+ // - Is this a partial line (ie, no newlines left in the unwritten tail)
+ // - If not, does the data out to the last unwritten newline fit in
+ // the buffer?
+ // - If not, scan for the last newline that *does* fit in the buffer
+ let tail = if flushed >= newline_idx {
+ &buf[flushed..]
+ } else if newline_idx - flushed <= self.buffer.capacity() {
+ &buf[flushed..newline_idx]
+ } else {
+ let scan_area = &buf[flushed..];
+ let scan_area = &scan_area[..self.buffer.capacity()];
+ match memchr::memrchr(b'\n', scan_area) {
+ Some(newline_idx) => &scan_area[..newline_idx + 1],
+ None => scan_area,
+ }
+ };
+
+ let buffered = self.buffer.write_to_buf(tail);
+ Ok(flushed + buffered)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.buffer.flush()
+ }
+
+ /// Write some vectored data into this BufReader with line buffering. This
+ /// means that, if any newlines are present in the data, the data up to
+ /// and including the buffer containing the last newline is sent directly
+ /// to the inner writer, and the data after it is buffered. Returns the
+ /// number of bytes written.
+ ///
+ /// This function operates on a "best effort basis"; in keeping with the
+ /// convention of `Write::write`, it makes at most one attempt to write
+ /// new data to the underlying writer.
+ ///
+ /// Because this function attempts to send completed lines to the underlying
+ /// writer, it will also flush the existing buffer if it contains any
+ /// newlines.
+ ///
+ /// Because sorting through an array of `IoSlice` can be a bit convoluted,
+ /// This method differs from write in the following ways:
+ ///
+ /// - It attempts to write the full content of all the buffers up to and
+ /// including the one containing the last newline. This means that it
+ /// may attempt to write a partial line, that buffer has data past the
+ /// newline.
+ /// - If the write only reports partial success, it does not attempt to
+ /// find the precise location of the written bytes and buffer the rest.
+ ///
+ /// If the underlying vector doesn't support vectored writing, we instead
+ /// simply write the first non-empty buffer with `write`. This way, we
+ /// get the benefits of more granular partial-line handling without losing
+ /// anything in efficiency
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ // If there's no specialized behavior for write_vectored, just use
+ // write. This has the benefit of more granular partial-line handling.
+ if !self.is_write_vectored() {
+ return match bufs.iter().find(|buf| !buf.is_empty()) {
+ Some(buf) => self.write(buf),
+ None => Ok(0),
+ };
+ }
+
+ // Find the buffer containing the last newline
+ let last_newline_buf_idx = bufs
+ .iter()
+ .enumerate()
+ .rev()
+ .find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i));
+
+ // If there are no new newlines (that is, if this write is less than
+ // one line), just do a regular buffered write
+ let last_newline_buf_idx = match last_newline_buf_idx {
+ // No newlines; just do a normal buffered write
+ None => {
+ self.flush_if_completed_line()?;
+ return self.buffer.write_vectored(bufs);
+ }
+ Some(i) => i,
+ };
+
+ // Flush existing content to prepare for our write
+ self.buffer.flush_buf()?;
+
+ // This is what we're going to try to write directly to the inner
+ // writer. The rest will be buffered, if nothing goes wrong.
+ let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1);
+
+ // Write `lines` directly to the inner writer. In keeping with the
+ // `write` convention, make at most one attempt to add new (unbuffered)
+ // data. Because this write doesn't touch the BufWriter state directly,
+ // and the buffer is known to be empty, we don't need to worry about
+ // self.panicked here.
+ let flushed = self.inner_mut().write_vectored(lines)?;
+
+ // If inner returns Ok(0), propagate that to the caller without
+ // doing additional buffering; otherwise we're just guaranteeing
+ // an "ErrorKind::WriteZero" later.
+ if flushed == 0 {
+ return Ok(0);
+ }
+
+ // Don't try to reconstruct the exact amount written; just bail
+ // in the event of a partial write
+ let lines_len = lines.iter().map(|buf| buf.len()).sum();
+ if flushed < lines_len {
+ return Ok(flushed);
+ }
+
+ // Now that the write has succeeded, buffer the rest (or as much of the
+ // rest as possible)
+ let buffered: usize = tail
+ .iter()
+ .filter(|buf| !buf.is_empty())
+ .map(|buf| self.buffer.write_to_buf(buf))
+ .take_while(|&n| n > 0)
+ .sum();
+
+ Ok(flushed + buffered)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ self.buffer.is_write_vectored()
+ }
+
+ /// Write some data into this BufReader with line buffering. This means
+ /// that, if any newlines are present in the data, the data up to the last
+ /// newline is sent directly to the underlying writer, and data after it
+ /// is buffered.
+ ///
+ /// Because this function attempts to send completed lines to the underlying
+ /// writer, it will also flush the existing buffer if it contains any
+ /// newlines, even if the incoming data does not contain any newlines.
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ match memchr::memrchr(b'\n', buf) {
+ // If there are no new newlines (that is, if this write is less than
+ // one line), just do a regular buffered write (which may flush if
+ // we exceed the inner buffer's size)
+ None => {
+ self.flush_if_completed_line()?;
+ self.buffer.write_all(buf)
+ }
+ Some(newline_idx) => {
+ let (lines, tail) = buf.split_at(newline_idx + 1);
+
+ if self.buffered().is_empty() {
+ self.inner_mut().write_all(lines)?;
+ } else {
+ // If there is any buffered data, we add the incoming lines
+ // to that buffer before flushing, which saves us at least
+ // one write call. We can't really do this with `write`,
+ // since we can't do this *and* not suppress errors *and*
+ // report a consistent state to the caller in a return
+ // value, but here in write_all it's fine.
+ self.buffer.write_all(lines)?;
+ self.buffer.flush_buf()?;
+ }
+
+ self.buffer.write_all(tail)
+ }
+ }
+ }
+}
+
/// Wraps a writer and buffers output to it, flushing whenever a newline
/// (`0x0a`, `'\n'`) is detected.
///
@@ -881,7 +1270,6 @@
#[stable(feature = "rust1", since = "1.0.0")]
pub struct LineWriter<W: Write> {
inner: BufWriter<W>,
- need_flush: bool,
}
impl<W: Write> LineWriter<W> {
@@ -922,7 +1310,7 @@
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(capacity: usize, inner: W) -> LineWriter<W> {
- LineWriter { inner: BufWriter::with_capacity(capacity, inner), need_flush: false }
+ LineWriter { inner: BufWriter::with_capacity(capacity, inner) }
}
/// Gets a reference to the underlying writer.
@@ -996,110 +1384,40 @@
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
- self.inner.into_inner().map_err(|IntoInnerError(buf, e)| {
- IntoInnerError(LineWriter { inner: buf, need_flush: false }, e)
- })
+ self.inner
+ .into_inner()
+ .map_err(|IntoInnerError(buf, e)| IntoInnerError(LineWriter { inner: buf }, e))
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<W: Write> Write for LineWriter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- if self.need_flush {
- self.flush()?;
- }
-
- // Find the last newline character in the buffer provided. If found then
- // we're going to write all the data up to that point and then flush,
- // otherwise we just write the whole block to the underlying writer.
- let i = match memchr::memrchr(b'\n', buf) {
- Some(i) => i,
- None => return self.inner.write(buf),
- };
-
- // Ok, we're going to write a partial amount of the data given first
- // followed by flushing the newline. After we've successfully written
- // some data then we *must* report that we wrote that data, so future
- // errors are ignored. We set our internal `need_flush` flag, though, in
- // case flushing fails and we need to try it first next time.
- let n = self.inner.write(&buf[..=i])?;
- self.need_flush = true;
- if self.flush().is_err() || n != i + 1 {
- return Ok(n);
- }
-
- // At this point we successfully wrote `i + 1` bytes and flushed it out,
- // meaning that the entire line is now flushed out on the screen. While
- // we can attempt to finish writing the rest of the data provided.
- // Remember though that we ignore errors here as we've successfully
- // written data, so we need to report that.
- match self.inner.write(&buf[i + 1..]) {
- Ok(i) => Ok(n + i),
- Err(_) => Ok(n),
- }
- }
-
- // Vectored writes are very similar to the writes above, but adjusted for
- // the list of buffers that we have to write.
- fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
- if self.need_flush {
- self.flush()?;
- }
-
- // Find the last newline, and failing that write the whole buffer
- let last_newline = bufs.iter().enumerate().rev().find_map(|(i, buf)| {
- let pos = memchr::memrchr(b'\n', buf)?;
- Some((i, pos))
- });
- let (i, j) = match last_newline {
- Some(pair) => pair,
- None => return self.inner.write_vectored(bufs),
- };
- let (prefix, suffix) = bufs.split_at(i);
- let (buf, suffix) = suffix.split_at(1);
- let buf = &buf[0];
-
- // Write everything up to the last newline, flushing afterwards. Note
- // that only if we finished our entire `write_vectored` do we try the
- // subsequent
- // `write`
- let mut n = 0;
- let prefix_amt = prefix.iter().map(|i| i.len()).sum();
- if prefix_amt > 0 {
- n += self.inner.write_vectored(prefix)?;
- self.need_flush = true;
- }
- if n == prefix_amt {
- match self.inner.write(&buf[..=j]) {
- Ok(m) => n += m,
- Err(e) if n == 0 => return Err(e),
- Err(_) => return Ok(n),
- }
- self.need_flush = true;
- }
- if self.flush().is_err() || n != j + 1 + prefix_amt {
- return Ok(n);
- }
-
- // ... and now write out everything remaining
- match self.inner.write(&buf[j + 1..]) {
- Ok(i) => n += i,
- Err(_) => return Ok(n),
- }
-
- if suffix.iter().map(|s| s.len()).sum::<usize>() == 0 {
- return Ok(n);
- }
- match self.inner.write_vectored(suffix) {
- Ok(i) => Ok(n + i),
- Err(_) => Ok(n),
- }
+ LineWriterShim::new(&mut self.inner).write(buf)
}
fn flush(&mut self) -> io::Result<()> {
- self.inner.flush()?;
- self.need_flush = false;
- Ok(())
+ self.inner.flush()
+ }
+
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ LineWriterShim::new(&mut self.inner).write_vectored(bufs)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ self.inner.is_write_vectored()
+ }
+
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ LineWriterShim::new(&mut self.inner).write_all(buf)
+ }
+
+ fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+ LineWriterShim::new(&mut self.inner).write_all_vectored(bufs)
+ }
+
+ fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
+ LineWriterShim::new(&mut self.inner).write_fmt(fmt)
}
}
@@ -1118,584 +1436,3 @@
.finish()
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::io::prelude::*;
- use crate::io::{self, BufReader, BufWriter, IoSlice, LineWriter, SeekFrom};
- use crate::sync::atomic::{AtomicUsize, Ordering};
- use crate::thread;
-
- /// A dummy reader intended at testing short-reads propagation.
- pub struct ShortReader {
- lengths: Vec<usize>,
- }
-
- impl Read for ShortReader {
- fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
- if self.lengths.is_empty() { Ok(0) } else { Ok(self.lengths.remove(0)) }
- }
- }
-
- #[test]
- fn test_buffered_reader() {
- let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
- let mut reader = BufReader::with_capacity(2, inner);
-
- let mut buf = [0, 0, 0];
- let nread = reader.read(&mut buf);
- assert_eq!(nread.unwrap(), 3);
- assert_eq!(buf, [5, 6, 7]);
- assert_eq!(reader.buffer(), []);
-
- let mut buf = [0, 0];
- let nread = reader.read(&mut buf);
- assert_eq!(nread.unwrap(), 2);
- assert_eq!(buf, [0, 1]);
- assert_eq!(reader.buffer(), []);
-
- let mut buf = [0];
- let nread = reader.read(&mut buf);
- assert_eq!(nread.unwrap(), 1);
- assert_eq!(buf, [2]);
- assert_eq!(reader.buffer(), [3]);
-
- let mut buf = [0, 0, 0];
- let nread = reader.read(&mut buf);
- assert_eq!(nread.unwrap(), 1);
- assert_eq!(buf, [3, 0, 0]);
- assert_eq!(reader.buffer(), []);
-
- let nread = reader.read(&mut buf);
- assert_eq!(nread.unwrap(), 1);
- assert_eq!(buf, [4, 0, 0]);
- assert_eq!(reader.buffer(), []);
-
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- }
-
- #[test]
- fn test_buffered_reader_seek() {
- let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
- let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner));
-
- assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3));
- assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
- assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(3));
- assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
- assert_eq!(reader.seek(SeekFrom::Current(1)).ok(), Some(4));
- assert_eq!(reader.fill_buf().ok(), Some(&[1, 2][..]));
- reader.consume(1);
- assert_eq!(reader.seek(SeekFrom::Current(-2)).ok(), Some(3));
- }
-
- #[test]
- fn test_buffered_reader_seek_relative() {
- let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
- let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner));
-
- assert!(reader.seek_relative(3).is_ok());
- assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
- assert!(reader.seek_relative(0).is_ok());
- assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
- assert!(reader.seek_relative(1).is_ok());
- assert_eq!(reader.fill_buf().ok(), Some(&[1][..]));
- assert!(reader.seek_relative(-1).is_ok());
- assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
- assert!(reader.seek_relative(2).is_ok());
- assert_eq!(reader.fill_buf().ok(), Some(&[2, 3][..]));
- }
-
- #[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 {
- pos: u64,
- }
- impl Read for PositionReader {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- let len = buf.len();
- for x in buf {
- *x = self.pos as u8;
- self.pos = self.pos.wrapping_add(1);
- }
- Ok(len)
- }
- }
- impl Seek for PositionReader {
- fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
- match pos {
- SeekFrom::Start(n) => {
- self.pos = n;
- }
- SeekFrom::Current(n) => {
- self.pos = self.pos.wrapping_add(n as u64);
- }
- SeekFrom::End(n) => {
- self.pos = u64::MAX.wrapping_add(n as u64);
- }
- }
- Ok(self.pos)
- }
- }
-
- let mut reader = BufReader::with_capacity(5, PositionReader { pos: 0 });
- assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2, 3, 4][..]));
- assert_eq!(reader.seek(SeekFrom::End(-5)).ok(), Some(u64::MAX - 5));
- assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5));
- // the following seek will require two underlying seeks
- let expected = 9223372036854775802;
- assert_eq!(reader.seek(SeekFrom::Current(i64::MIN)).ok(), Some(expected));
- assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5));
- // seeking to 0 should empty the buffer.
- assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(expected));
- assert_eq!(reader.get_ref().pos, expected);
- }
-
- #[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)).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);
-
- writer.write(&[0, 1]).unwrap();
- assert_eq!(writer.buffer(), []);
- assert_eq!(*writer.get_ref(), [0, 1]);
-
- writer.write(&[2]).unwrap();
- assert_eq!(writer.buffer(), [2]);
- assert_eq!(*writer.get_ref(), [0, 1]);
-
- writer.write(&[3]).unwrap();
- assert_eq!(writer.buffer(), [2, 3]);
- assert_eq!(*writer.get_ref(), [0, 1]);
-
- writer.flush().unwrap();
- assert_eq!(writer.buffer(), []);
- assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
-
- writer.write(&[4]).unwrap();
- writer.write(&[5]).unwrap();
- assert_eq!(writer.buffer(), [4, 5]);
- assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
-
- writer.write(&[6]).unwrap();
- assert_eq!(writer.buffer(), [6]);
- assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]);
-
- writer.write(&[7, 8]).unwrap();
- assert_eq!(writer.buffer(), []);
- assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]);
-
- writer.write(&[9, 10, 11]).unwrap();
- assert_eq!(writer.buffer(), []);
- assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
-
- writer.flush().unwrap();
- assert_eq!(writer.buffer(), []);
- assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
- }
-
- #[test]
- fn test_buffered_writer_inner_flushes() {
- let mut w = BufWriter::with_capacity(3, Vec::new());
- w.write(&[0, 1]).unwrap();
- assert_eq!(*w.get_ref(), []);
- let w = w.into_inner().unwrap();
- assert_eq!(w, [0, 1]);
- }
-
- #[test]
- fn test_buffered_writer_seek() {
- let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new()));
- w.write_all(&[0, 1, 2, 3, 4, 5]).unwrap();
- w.write_all(&[6, 7]).unwrap();
- assert_eq!(w.seek(SeekFrom::Current(0)).ok(), Some(8));
- assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]);
- assert_eq!(w.seek(SeekFrom::Start(2)).ok(), Some(2));
- w.write_all(&[8, 9]).unwrap();
- assert_eq!(&w.into_inner().unwrap().into_inner()[..], &[0, 1, 8, 9, 4, 5, 6, 7]);
- }
-
- #[test]
- fn test_read_until() {
- let inner: &[u8] = &[0, 1, 2, 1, 0];
- let mut reader = BufReader::with_capacity(2, inner);
- let mut v = Vec::new();
- reader.read_until(0, &mut v).unwrap();
- assert_eq!(v, [0]);
- v.truncate(0);
- reader.read_until(2, &mut v).unwrap();
- assert_eq!(v, [1, 2]);
- v.truncate(0);
- reader.read_until(1, &mut v).unwrap();
- assert_eq!(v, [1]);
- v.truncate(0);
- reader.read_until(8, &mut v).unwrap();
- assert_eq!(v, [0]);
- v.truncate(0);
- reader.read_until(9, &mut v).unwrap();
- assert_eq!(v, []);
- }
-
- #[test]
- fn test_line_buffer_fail_flush() {
- // Issue #32085
- struct FailFlushWriter<'a>(&'a mut Vec<u8>);
-
- impl Write for FailFlushWriter<'_> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- self.0.extend_from_slice(buf);
- Ok(buf.len())
- }
- fn flush(&mut self) -> io::Result<()> {
- Err(io::Error::new(io::ErrorKind::Other, "flush failed"))
- }
- }
-
- let mut buf = Vec::new();
- {
- let mut writer = LineWriter::new(FailFlushWriter(&mut buf));
- let to_write = b"abc\ndef";
- if let Ok(written) = writer.write(to_write) {
- assert!(written < to_write.len(), "didn't flush on new line");
- // PASS
- return;
- }
- }
- assert!(buf.is_empty(), "write returned an error but wrote data");
- }
-
- #[test]
- fn test_line_buffer() {
- let mut writer = LineWriter::new(Vec::new());
- writer.write(&[0]).unwrap();
- assert_eq!(*writer.get_ref(), []);
- writer.write(&[1]).unwrap();
- assert_eq!(*writer.get_ref(), []);
- writer.flush().unwrap();
- assert_eq!(*writer.get_ref(), [0, 1]);
- writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap();
- assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']);
- writer.flush().unwrap();
- assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]);
- writer.write(&[3, b'\n']).unwrap();
- assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']);
- }
-
- #[test]
- fn test_read_line() {
- let in_buf: &[u8] = b"a\nb\nc";
- let mut reader = BufReader::with_capacity(2, in_buf);
- let mut s = String::new();
- reader.read_line(&mut s).unwrap();
- assert_eq!(s, "a\n");
- s.truncate(0);
- reader.read_line(&mut s).unwrap();
- assert_eq!(s, "b\n");
- s.truncate(0);
- reader.read_line(&mut s).unwrap();
- assert_eq!(s, "c");
- s.truncate(0);
- reader.read_line(&mut s).unwrap();
- assert_eq!(s, "");
- }
-
- #[test]
- fn test_lines() {
- let in_buf: &[u8] = b"a\nb\nc";
- let reader = BufReader::with_capacity(2, in_buf);
- let mut it = reader.lines();
- assert_eq!(it.next().unwrap().unwrap(), "a".to_string());
- assert_eq!(it.next().unwrap().unwrap(), "b".to_string());
- assert_eq!(it.next().unwrap().unwrap(), "c".to_string());
- assert!(it.next().is_none());
- }
-
- #[test]
- fn test_short_reads() {
- let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] };
- let mut reader = BufReader::new(inner);
- let mut buf = [0, 0];
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- assert_eq!(reader.read(&mut buf).unwrap(), 1);
- assert_eq!(reader.read(&mut buf).unwrap(), 2);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- assert_eq!(reader.read(&mut buf).unwrap(), 1);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- }
-
- #[test]
- #[should_panic]
- fn dont_panic_in_drop_on_panicked_flush() {
- struct FailFlushWriter;
-
- impl Write for FailFlushWriter {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- Ok(buf.len())
- }
- fn flush(&mut self) -> io::Result<()> {
- Err(io::Error::last_os_error())
- }
- }
-
- let writer = FailFlushWriter;
- let _writer = BufWriter::new(writer);
-
- // If writer panics *again* due to the flush error then the process will
- // abort.
- panic!();
- }
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn panic_in_write_doesnt_flush_in_drop() {
- static WRITES: AtomicUsize = AtomicUsize::new(0);
-
- struct PanicWriter;
-
- impl Write for PanicWriter {
- fn write(&mut self, _: &[u8]) -> io::Result<usize> {
- WRITES.fetch_add(1, Ordering::SeqCst);
- panic!();
- }
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
- }
-
- thread::spawn(|| {
- let mut writer = BufWriter::new(PanicWriter);
- let _ = writer.write(b"hello world");
- let _ = writer.flush();
- })
- .join()
- .unwrap_err();
-
- assert_eq!(WRITES.load(Ordering::SeqCst), 1);
- }
-
- #[bench]
- fn bench_buffered_reader(b: &mut test::Bencher) {
- b.iter(|| BufReader::new(io::empty()));
- }
-
- #[bench]
- fn bench_buffered_writer(b: &mut test::Bencher) {
- b.iter(|| BufWriter::new(io::sink()));
- }
-
- struct AcceptOneThenFail {
- written: bool,
- flushed: bool,
- }
-
- impl Write for AcceptOneThenFail {
- fn write(&mut self, data: &[u8]) -> io::Result<usize> {
- if !self.written {
- assert_eq!(data, b"a\nb\n");
- self.written = true;
- Ok(data.len())
- } else {
- Err(io::Error::new(io::ErrorKind::NotFound, "test"))
- }
- }
-
- fn flush(&mut self) -> io::Result<()> {
- assert!(self.written);
- assert!(!self.flushed);
- self.flushed = true;
- Err(io::Error::new(io::ErrorKind::Other, "test"))
- }
- }
-
- #[test]
- fn erroneous_flush_retried() {
- let a = AcceptOneThenFail { written: false, flushed: false };
-
- let mut l = LineWriter::new(a);
- assert_eq!(l.write(b"a\nb\na").unwrap(), 4);
- assert!(l.get_ref().written);
- assert!(l.get_ref().flushed);
- l.get_mut().flushed = false;
-
- assert_eq!(l.write(b"a").unwrap_err().kind(), io::ErrorKind::Other)
- }
-
- #[test]
- fn line_vectored() {
- let mut a = LineWriter::new(Vec::new());
- assert_eq!(
- a.write_vectored(&[
- IoSlice::new(&[]),
- IoSlice::new(b"\n"),
- IoSlice::new(&[]),
- IoSlice::new(b"a"),
- ])
- .unwrap(),
- 2,
- );
- assert_eq!(a.get_ref(), b"\n");
-
- assert_eq!(
- a.write_vectored(&[
- IoSlice::new(&[]),
- IoSlice::new(b"b"),
- IoSlice::new(&[]),
- IoSlice::new(b"a"),
- IoSlice::new(&[]),
- IoSlice::new(b"c"),
- ])
- .unwrap(),
- 3,
- );
- assert_eq!(a.get_ref(), b"\n");
- a.flush().unwrap();
- assert_eq!(a.get_ref(), b"\nabac");
- assert_eq!(a.write_vectored(&[]).unwrap(), 0);
- assert_eq!(
- a.write_vectored(&[
- IoSlice::new(&[]),
- IoSlice::new(&[]),
- IoSlice::new(&[]),
- IoSlice::new(&[]),
- ])
- .unwrap(),
- 0,
- );
- assert_eq!(a.write_vectored(&[IoSlice::new(b"a\nb"),]).unwrap(), 3);
- assert_eq!(a.get_ref(), b"\nabaca\n");
- }
-
- #[test]
- fn line_vectored_partial_and_errors() {
- enum Call {
- Write { inputs: Vec<&'static [u8]>, output: io::Result<usize> },
- Flush { output: io::Result<()> },
- }
- struct Writer {
- calls: Vec<Call>,
- }
-
- impl Write for Writer {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- self.write_vectored(&[IoSlice::new(buf)])
- }
-
- fn write_vectored(&mut self, buf: &[IoSlice<'_>]) -> io::Result<usize> {
- match self.calls.pop().unwrap() {
- Call::Write { inputs, output } => {
- assert_eq!(inputs, buf.iter().map(|b| &**b).collect::<Vec<_>>());
- output
- }
- _ => panic!("unexpected call to write"),
- }
- }
-
- fn flush(&mut self) -> io::Result<()> {
- match self.calls.pop().unwrap() {
- Call::Flush { output } => output,
- _ => panic!("unexpected call to flush"),
- }
- }
- }
-
- impl Drop for Writer {
- fn drop(&mut self) {
- if !thread::panicking() {
- assert_eq!(self.calls.len(), 0);
- }
- }
- }
-
- // partial writes keep going
- let mut a = LineWriter::new(Writer { calls: Vec::new() });
- a.write_vectored(&[IoSlice::new(&[]), IoSlice::new(b"abc")]).unwrap();
- a.get_mut().calls.push(Call::Flush { output: Ok(()) });
- a.get_mut().calls.push(Call::Write { inputs: vec![b"bcx\n"], output: Ok(4) });
- a.get_mut().calls.push(Call::Write { inputs: vec![b"abcx\n"], output: Ok(1) });
- a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\n")]).unwrap();
- a.get_mut().calls.push(Call::Flush { output: Ok(()) });
- a.flush().unwrap();
-
- // erroneous writes stop and don't write more
- a.get_mut().calls.push(Call::Write { inputs: vec![b"x\n"], output: Err(err()) });
- assert_eq!(a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\na")]).unwrap(), 2);
- a.get_mut().calls.push(Call::Flush { output: Ok(()) });
- a.get_mut().calls.push(Call::Write { inputs: vec![b"x\n"], output: Ok(2) });
- a.flush().unwrap();
-
- fn err() -> io::Error {
- io::Error::new(io::ErrorKind::Other, "x")
- }
- }
-}
diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs
new file mode 100644
index 0000000..66a64f6
--- /dev/null
+++ b/library/std/src/io/buffered/tests.rs
@@ -0,0 +1,958 @@
+use crate::io::prelude::*;
+use crate::io::{self, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom};
+use crate::panic;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::thread;
+
+/// A dummy reader intended at testing short-reads propagation.
+pub struct ShortReader {
+ lengths: Vec<usize>,
+}
+
+// FIXME: rustfmt and tidy disagree about the correct formatting of this
+// function. This leads to issues for users with editors configured to
+// rustfmt-on-save.
+impl Read for ShortReader {
+ fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
+ if self.lengths.is_empty() { Ok(0) } else { Ok(self.lengths.remove(0)) }
+ }
+}
+
+#[test]
+fn test_buffered_reader() {
+ let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
+ let mut reader = BufReader::with_capacity(2, inner);
+
+ let mut buf = [0, 0, 0];
+ let nread = reader.read(&mut buf);
+ assert_eq!(nread.unwrap(), 3);
+ assert_eq!(buf, [5, 6, 7]);
+ assert_eq!(reader.buffer(), []);
+
+ let mut buf = [0, 0];
+ let nread = reader.read(&mut buf);
+ assert_eq!(nread.unwrap(), 2);
+ assert_eq!(buf, [0, 1]);
+ assert_eq!(reader.buffer(), []);
+
+ let mut buf = [0];
+ let nread = reader.read(&mut buf);
+ assert_eq!(nread.unwrap(), 1);
+ assert_eq!(buf, [2]);
+ assert_eq!(reader.buffer(), [3]);
+
+ let mut buf = [0, 0, 0];
+ let nread = reader.read(&mut buf);
+ assert_eq!(nread.unwrap(), 1);
+ assert_eq!(buf, [3, 0, 0]);
+ assert_eq!(reader.buffer(), []);
+
+ let nread = reader.read(&mut buf);
+ assert_eq!(nread.unwrap(), 1);
+ assert_eq!(buf, [4, 0, 0]);
+ assert_eq!(reader.buffer(), []);
+
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+}
+
+#[test]
+fn test_buffered_reader_seek() {
+ let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
+ let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner));
+
+ assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3));
+ assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
+ assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(3));
+ assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
+ assert_eq!(reader.seek(SeekFrom::Current(1)).ok(), Some(4));
+ assert_eq!(reader.fill_buf().ok(), Some(&[1, 2][..]));
+ reader.consume(1);
+ assert_eq!(reader.seek(SeekFrom::Current(-2)).ok(), Some(3));
+}
+
+#[test]
+fn test_buffered_reader_seek_relative() {
+ let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
+ let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner));
+
+ assert!(reader.seek_relative(3).is_ok());
+ assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
+ assert!(reader.seek_relative(0).is_ok());
+ assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
+ assert!(reader.seek_relative(1).is_ok());
+ assert_eq!(reader.fill_buf().ok(), Some(&[1][..]));
+ assert!(reader.seek_relative(-1).is_ok());
+ assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
+ assert!(reader.seek_relative(2).is_ok());
+ assert_eq!(reader.fill_buf().ok(), Some(&[2, 3][..]));
+}
+
+#[test]
+fn test_buffered_reader_stream_position() {
+ let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
+ let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner));
+
+ assert_eq!(reader.stream_position().ok(), Some(0));
+ assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3));
+ assert_eq!(reader.stream_position().ok(), Some(3));
+ // relative seeking within the buffer and reading position should keep the buffer
+ assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..]));
+ assert!(reader.seek_relative(0).is_ok());
+ assert_eq!(reader.stream_position().ok(), Some(3));
+ assert_eq!(reader.buffer(), &[0, 1][..]);
+ assert!(reader.seek_relative(1).is_ok());
+ assert_eq!(reader.stream_position().ok(), Some(4));
+ assert_eq!(reader.buffer(), &[1][..]);
+ assert!(reader.seek_relative(-1).is_ok());
+ assert_eq!(reader.stream_position().ok(), Some(3));
+ assert_eq!(reader.buffer(), &[0, 1][..]);
+ // relative seeking outside the buffer will discard it
+ assert!(reader.seek_relative(2).is_ok());
+ assert_eq!(reader.stream_position().ok(), Some(5));
+ assert_eq!(reader.buffer(), &[][..]);
+}
+
+#[test]
+fn test_buffered_reader_stream_position_panic() {
+ let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
+ let mut reader = BufReader::with_capacity(4, io::Cursor::new(inner));
+
+ // cause internal buffer to be filled but read only partially
+ let mut buffer = [0, 0];
+ assert!(reader.read_exact(&mut buffer).is_ok());
+ // rewinding the internal reader will cause buffer to loose sync
+ let inner = reader.get_mut();
+ assert!(inner.seek(SeekFrom::Start(0)).is_ok());
+ // overflow when subtracting the remaining buffer size from current position
+ let result = panic::catch_unwind(panic::AssertUnwindSafe(|| reader.stream_position().ok()));
+ assert!(result.is_err());
+}
+
+#[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 {
+ pos: u64,
+ }
+ impl Read for PositionReader {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let len = buf.len();
+ for x in buf {
+ *x = self.pos as u8;
+ self.pos = self.pos.wrapping_add(1);
+ }
+ Ok(len)
+ }
+ }
+ impl Seek for PositionReader {
+ fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+ match pos {
+ SeekFrom::Start(n) => {
+ self.pos = n;
+ }
+ SeekFrom::Current(n) => {
+ self.pos = self.pos.wrapping_add(n as u64);
+ }
+ SeekFrom::End(n) => {
+ self.pos = u64::MAX.wrapping_add(n as u64);
+ }
+ }
+ Ok(self.pos)
+ }
+ }
+
+ let mut reader = BufReader::with_capacity(5, PositionReader { pos: 0 });
+ assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2, 3, 4][..]));
+ assert_eq!(reader.seek(SeekFrom::End(-5)).ok(), Some(u64::MAX - 5));
+ assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5));
+ // the following seek will require two underlying seeks
+ let expected = 9223372036854775802;
+ assert_eq!(reader.seek(SeekFrom::Current(i64::MIN)).ok(), Some(expected));
+ assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5));
+ // seeking to 0 should empty the buffer.
+ assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(expected));
+ assert_eq!(reader.get_ref().pos, expected);
+}
+
+#[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)).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);
+
+ writer.write(&[0, 1]).unwrap();
+ assert_eq!(writer.buffer(), []);
+ assert_eq!(*writer.get_ref(), [0, 1]);
+
+ writer.write(&[2]).unwrap();
+ assert_eq!(writer.buffer(), [2]);
+ assert_eq!(*writer.get_ref(), [0, 1]);
+
+ writer.write(&[3]).unwrap();
+ assert_eq!(writer.buffer(), [2, 3]);
+ assert_eq!(*writer.get_ref(), [0, 1]);
+
+ writer.flush().unwrap();
+ assert_eq!(writer.buffer(), []);
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
+
+ writer.write(&[4]).unwrap();
+ writer.write(&[5]).unwrap();
+ assert_eq!(writer.buffer(), [4, 5]);
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
+
+ writer.write(&[6]).unwrap();
+ assert_eq!(writer.buffer(), [6]);
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]);
+
+ writer.write(&[7, 8]).unwrap();
+ assert_eq!(writer.buffer(), []);
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]);
+
+ writer.write(&[9, 10, 11]).unwrap();
+ assert_eq!(writer.buffer(), []);
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
+
+ writer.flush().unwrap();
+ assert_eq!(writer.buffer(), []);
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
+}
+
+#[test]
+fn test_buffered_writer_inner_flushes() {
+ let mut w = BufWriter::with_capacity(3, Vec::new());
+ w.write(&[0, 1]).unwrap();
+ assert_eq!(*w.get_ref(), []);
+ let w = w.into_inner().unwrap();
+ assert_eq!(w, [0, 1]);
+}
+
+#[test]
+fn test_buffered_writer_seek() {
+ let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new()));
+ w.write_all(&[0, 1, 2, 3, 4, 5]).unwrap();
+ w.write_all(&[6, 7]).unwrap();
+ assert_eq!(w.seek(SeekFrom::Current(0)).ok(), Some(8));
+ assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]);
+ assert_eq!(w.seek(SeekFrom::Start(2)).ok(), Some(2));
+ w.write_all(&[8, 9]).unwrap();
+ assert_eq!(&w.into_inner().unwrap().into_inner()[..], &[0, 1, 8, 9, 4, 5, 6, 7]);
+}
+
+#[test]
+fn test_read_until() {
+ let inner: &[u8] = &[0, 1, 2, 1, 0];
+ let mut reader = BufReader::with_capacity(2, inner);
+ let mut v = Vec::new();
+ reader.read_until(0, &mut v).unwrap();
+ assert_eq!(v, [0]);
+ v.truncate(0);
+ reader.read_until(2, &mut v).unwrap();
+ assert_eq!(v, [1, 2]);
+ v.truncate(0);
+ reader.read_until(1, &mut v).unwrap();
+ assert_eq!(v, [1]);
+ v.truncate(0);
+ reader.read_until(8, &mut v).unwrap();
+ assert_eq!(v, [0]);
+ v.truncate(0);
+ reader.read_until(9, &mut v).unwrap();
+ assert_eq!(v, []);
+}
+
+#[test]
+fn test_line_buffer() {
+ let mut writer = LineWriter::new(Vec::new());
+ writer.write(&[0]).unwrap();
+ assert_eq!(*writer.get_ref(), []);
+ writer.write(&[1]).unwrap();
+ assert_eq!(*writer.get_ref(), []);
+ writer.flush().unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1]);
+ writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']);
+ writer.flush().unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]);
+ writer.write(&[3, b'\n']).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']);
+}
+
+#[test]
+fn test_read_line() {
+ let in_buf: &[u8] = b"a\nb\nc";
+ let mut reader = BufReader::with_capacity(2, in_buf);
+ let mut s = String::new();
+ reader.read_line(&mut s).unwrap();
+ assert_eq!(s, "a\n");
+ s.truncate(0);
+ reader.read_line(&mut s).unwrap();
+ assert_eq!(s, "b\n");
+ s.truncate(0);
+ reader.read_line(&mut s).unwrap();
+ assert_eq!(s, "c");
+ s.truncate(0);
+ reader.read_line(&mut s).unwrap();
+ assert_eq!(s, "");
+}
+
+#[test]
+fn test_lines() {
+ let in_buf: &[u8] = b"a\nb\nc";
+ let reader = BufReader::with_capacity(2, in_buf);
+ let mut it = reader.lines();
+ assert_eq!(it.next().unwrap().unwrap(), "a".to_string());
+ assert_eq!(it.next().unwrap().unwrap(), "b".to_string());
+ assert_eq!(it.next().unwrap().unwrap(), "c".to_string());
+ assert!(it.next().is_none());
+}
+
+#[test]
+fn test_short_reads() {
+ let inner = ShortReader { lengths: vec![0, 1, 2, 0, 1, 0] };
+ let mut reader = BufReader::new(inner);
+ let mut buf = [0, 0];
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+ assert_eq!(reader.read(&mut buf).unwrap(), 1);
+ assert_eq!(reader.read(&mut buf).unwrap(), 2);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+ assert_eq!(reader.read(&mut buf).unwrap(), 1);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+}
+
+#[test]
+#[should_panic]
+fn dont_panic_in_drop_on_panicked_flush() {
+ struct FailFlushWriter;
+
+ impl Write for FailFlushWriter {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ Ok(buf.len())
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ Err(io::Error::last_os_error())
+ }
+ }
+
+ let writer = FailFlushWriter;
+ let _writer = BufWriter::new(writer);
+
+ // If writer panics *again* due to the flush error then the process will
+ // abort.
+ panic!();
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn panic_in_write_doesnt_flush_in_drop() {
+ static WRITES: AtomicUsize = AtomicUsize::new(0);
+
+ struct PanicWriter;
+
+ impl Write for PanicWriter {
+ fn write(&mut self, _: &[u8]) -> io::Result<usize> {
+ WRITES.fetch_add(1, Ordering::SeqCst);
+ panic!();
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+ }
+
+ thread::spawn(|| {
+ let mut writer = BufWriter::new(PanicWriter);
+ let _ = writer.write(b"hello world");
+ let _ = writer.flush();
+ })
+ .join()
+ .unwrap_err();
+
+ assert_eq!(WRITES.load(Ordering::SeqCst), 1);
+}
+
+#[bench]
+fn bench_buffered_reader(b: &mut test::Bencher) {
+ b.iter(|| BufReader::new(io::empty()));
+}
+
+#[bench]
+fn bench_buffered_writer(b: &mut test::Bencher) {
+ b.iter(|| BufWriter::new(io::sink()));
+}
+
+/// A simple `Write` target, designed to be wrapped by `LineWriter` /
+/// `BufWriter` / etc, that can have its `write` & `flush` behavior
+/// configured
+#[derive(Default, Clone)]
+struct ProgrammableSink {
+ // Writes append to this slice
+ pub buffer: Vec<u8>,
+
+ // Flush sets this flag
+ pub flushed: bool,
+
+ // If true, writes will always be an error
+ pub always_write_error: bool,
+
+ // If true, flushes will always be an error
+ pub always_flush_error: bool,
+
+ // If set, only up to this number of bytes will be written in a single
+ // call to `write`
+ pub accept_prefix: Option<usize>,
+
+ // If set, counts down with each write, and writes return an error
+ // when it hits 0
+ pub max_writes: Option<usize>,
+
+ // If set, attempting to write when max_writes == Some(0) will be an
+ // error; otherwise, it will return Ok(0).
+ pub error_after_max_writes: bool,
+}
+
+impl Write for ProgrammableSink {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ if self.always_write_error {
+ return Err(io::Error::new(io::ErrorKind::Other, "test - always_write_error"));
+ }
+
+ match self.max_writes {
+ Some(0) if self.error_after_max_writes => {
+ return Err(io::Error::new(io::ErrorKind::Other, "test - max_writes"));
+ }
+ Some(0) => return Ok(0),
+ Some(ref mut count) => *count -= 1,
+ None => {}
+ }
+
+ let len = match self.accept_prefix {
+ None => data.len(),
+ Some(prefix) => data.len().min(prefix),
+ };
+
+ let data = &data[..len];
+ self.buffer.extend_from_slice(data);
+
+ Ok(len)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ if self.always_flush_error {
+ Err(io::Error::new(io::ErrorKind::Other, "test - always_flush_error"))
+ } else {
+ self.flushed = true;
+ Ok(())
+ }
+ }
+}
+
+/// Previously the `LineWriter` could successfully write some bytes but
+/// then fail to report that it has done so. Additionally, an erroneous
+/// flush after a successful write was permanently ignored.
+///
+/// Test that a line writer correctly reports the number of written bytes,
+/// and that it attempts to flush buffered lines from previous writes
+/// before processing new data
+///
+/// Regression test for #37807
+#[test]
+fn erroneous_flush_retried() {
+ let writer = ProgrammableSink {
+ // Only write up to 4 bytes at a time
+ accept_prefix: Some(4),
+
+ // Accept the first two writes, then error the others
+ max_writes: Some(2),
+ error_after_max_writes: true,
+
+ ..Default::default()
+ };
+
+ // This should write the first 4 bytes. The rest will be buffered, out
+ // to the last newline.
+ let mut writer = LineWriter::new(writer);
+ assert_eq!(writer.write(b"a\nb\nc\nd\ne").unwrap(), 8);
+
+ // This write should attempt to flush "c\nd\n", then buffer "e". No
+ // errors should happen here because no further writes should be
+ // attempted against `writer`.
+ assert_eq!(writer.write(b"e").unwrap(), 1);
+ assert_eq!(&writer.get_ref().buffer, b"a\nb\nc\nd\n");
+}
+
+#[test]
+fn line_vectored() {
+ let mut a = LineWriter::new(Vec::new());
+ assert_eq!(
+ a.write_vectored(&[
+ IoSlice::new(&[]),
+ IoSlice::new(b"\n"),
+ IoSlice::new(&[]),
+ IoSlice::new(b"a"),
+ ])
+ .unwrap(),
+ 2,
+ );
+ assert_eq!(a.get_ref(), b"\n");
+
+ assert_eq!(
+ a.write_vectored(&[
+ IoSlice::new(&[]),
+ IoSlice::new(b"b"),
+ IoSlice::new(&[]),
+ IoSlice::new(b"a"),
+ IoSlice::new(&[]),
+ IoSlice::new(b"c"),
+ ])
+ .unwrap(),
+ 3,
+ );
+ assert_eq!(a.get_ref(), b"\n");
+ a.flush().unwrap();
+ assert_eq!(a.get_ref(), b"\nabac");
+ assert_eq!(a.write_vectored(&[]).unwrap(), 0);
+ assert_eq!(
+ a.write_vectored(&[
+ IoSlice::new(&[]),
+ IoSlice::new(&[]),
+ IoSlice::new(&[]),
+ IoSlice::new(&[]),
+ ])
+ .unwrap(),
+ 0,
+ );
+ assert_eq!(a.write_vectored(&[IoSlice::new(b"a\nb"),]).unwrap(), 3);
+ assert_eq!(a.get_ref(), b"\nabaca\nb");
+}
+
+#[test]
+fn line_vectored_partial_and_errors() {
+ use crate::collections::VecDeque;
+
+ enum Call {
+ Write { inputs: Vec<&'static [u8]>, output: io::Result<usize> },
+ Flush { output: io::Result<()> },
+ }
+
+ #[derive(Default)]
+ struct Writer {
+ calls: VecDeque<Call>,
+ }
+
+ impl Write for Writer {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(buf)])
+ }
+
+ fn write_vectored(&mut self, buf: &[IoSlice<'_>]) -> io::Result<usize> {
+ match self.calls.pop_front().expect("unexpected call to write") {
+ Call::Write { inputs, output } => {
+ assert_eq!(inputs, buf.iter().map(|b| &**b).collect::<Vec<_>>());
+ output
+ }
+ Call::Flush { .. } => panic!("unexpected call to write; expected a flush"),
+ }
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ match self.calls.pop_front().expect("Unexpected call to flush") {
+ Call::Flush { output } => output,
+ Call::Write { .. } => panic!("unexpected call to flush; expected a write"),
+ }
+ }
+ }
+
+ impl Drop for Writer {
+ fn drop(&mut self) {
+ if !thread::panicking() {
+ assert_eq!(self.calls.len(), 0);
+ }
+ }
+ }
+
+ // partial writes keep going
+ let mut a = LineWriter::new(Writer::default());
+ a.write_vectored(&[IoSlice::new(&[]), IoSlice::new(b"abc")]).unwrap();
+
+ a.get_mut().calls.push_back(Call::Write { inputs: vec![b"abc"], output: Ok(1) });
+ a.get_mut().calls.push_back(Call::Write { inputs: vec![b"bc"], output: Ok(2) });
+ a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\n"], output: Ok(2) });
+
+ a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\n")]).unwrap();
+
+ a.get_mut().calls.push_back(Call::Flush { output: Ok(()) });
+ a.flush().unwrap();
+
+ // erroneous writes stop and don't write more
+ a.get_mut().calls.push_back(Call::Write { inputs: vec![b"x", b"\na"], output: Err(err()) });
+ a.get_mut().calls.push_back(Call::Flush { output: Ok(()) });
+ assert!(a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\na")]).is_err());
+ a.flush().unwrap();
+
+ fn err() -> io::Error {
+ io::Error::new(io::ErrorKind::Other, "x")
+ }
+}
+
+/// Test that, in cases where vectored writing is not enabled, the
+/// LineWriter uses the normal `write` call, which more-correctly handles
+/// partial lines
+#[test]
+fn line_vectored_ignored() {
+ let writer = ProgrammableSink::default();
+ let mut writer = LineWriter::new(writer);
+
+ let content = [
+ IoSlice::new(&[]),
+ IoSlice::new(b"Line 1\nLine"),
+ IoSlice::new(b" 2\nLine 3\nL"),
+ IoSlice::new(&[]),
+ IoSlice::new(&[]),
+ IoSlice::new(b"ine 4"),
+ IoSlice::new(b"\nLine 5\n"),
+ ];
+
+ let count = writer.write_vectored(&content).unwrap();
+ assert_eq!(count, 11);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+
+ let count = writer.write_vectored(&content[2..]).unwrap();
+ assert_eq!(count, 11);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n");
+
+ let count = writer.write_vectored(&content[5..]).unwrap();
+ assert_eq!(count, 5);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n");
+
+ let count = writer.write_vectored(&content[6..]).unwrap();
+ assert_eq!(count, 8);
+ assert_eq!(
+ writer.get_ref().buffer.as_slice(),
+ b"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n".as_ref()
+ );
+}
+
+/// Test that, given this input:
+///
+/// Line 1\n
+/// Line 2\n
+/// Line 3\n
+/// Line 4
+///
+/// And given a result that only writes to midway through Line 2
+///
+/// That only up to the end of Line 3 is buffered
+///
+/// This behavior is desirable because it prevents flushing partial lines
+#[test]
+fn partial_write_buffers_line() {
+ let writer = ProgrammableSink { accept_prefix: Some(13), ..Default::default() };
+ let mut writer = LineWriter::new(writer);
+
+ assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3\nLine4").unwrap(), 21);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2");
+
+ assert_eq!(writer.write(b"Line 4").unwrap(), 6);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\n");
+}
+
+/// Test that, given this input:
+///
+/// Line 1\n
+/// Line 2\n
+/// Line 3
+///
+/// And given that the full write of lines 1 and 2 was successful
+/// That data up to Line 3 is buffered
+#[test]
+fn partial_line_buffered_after_line_write() {
+ let writer = ProgrammableSink::default();
+ let mut writer = LineWriter::new(writer);
+
+ assert_eq!(writer.write(b"Line 1\nLine 2\nLine 3").unwrap(), 20);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\n");
+
+ assert!(writer.flush().is_ok());
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3");
+}
+
+/// Test that, given a partial line that exceeds the length of
+/// LineBuffer's buffer (that is, without a trailing newline), that that
+/// line is written to the inner writer
+#[test]
+fn long_line_flushed() {
+ let writer = ProgrammableSink::default();
+ let mut writer = LineWriter::with_capacity(5, writer);
+
+ assert_eq!(writer.write(b"0123456789").unwrap(), 10);
+ assert_eq!(&writer.get_ref().buffer, b"0123456789");
+}
+
+/// Test that, given a very long partial line *after* successfully
+/// flushing a complete line, that that line is buffered unconditionally,
+/// and no additional writes take place. This assures the property that
+/// `write` should make at-most-one attempt to write new data.
+#[test]
+fn line_long_tail_not_flushed() {
+ let writer = ProgrammableSink::default();
+ let mut writer = LineWriter::with_capacity(5, writer);
+
+ // Assert that Line 1\n is flushed, and 01234 is buffered
+ assert_eq!(writer.write(b"Line 1\n0123456789").unwrap(), 12);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+
+ // Because the buffer is full, this subsequent write will flush it
+ assert_eq!(writer.write(b"5").unwrap(), 1);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\n01234");
+}
+
+/// Test that, if an attempt to pre-flush buffered data returns Ok(0),
+/// this is propagated as an error.
+#[test]
+fn line_buffer_write0_error() {
+ let writer = ProgrammableSink {
+ // Accept one write, then return Ok(0) on subsequent ones
+ max_writes: Some(1),
+
+ ..Default::default()
+ };
+ let mut writer = LineWriter::new(writer);
+
+ // This should write "Line 1\n" and buffer "Partial"
+ assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+
+ // This will attempt to flush "partial", which will return Ok(0), which
+ // needs to be an error, because we've already informed the client
+ // that we accepted the write.
+ let err = writer.write(b" Line End\n").unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::WriteZero);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+}
+
+/// Test that, if a write returns Ok(0) after a successful pre-flush, this
+/// is propagated as Ok(0)
+#[test]
+fn line_buffer_write0_normal() {
+ let writer = ProgrammableSink {
+ // Accept two writes, then return Ok(0) on subsequent ones
+ max_writes: Some(2),
+
+ ..Default::default()
+ };
+ let mut writer = LineWriter::new(writer);
+
+ // This should write "Line 1\n" and buffer "Partial"
+ assert_eq!(writer.write(b"Line 1\nPartial").unwrap(), 14);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\n");
+
+ // This will flush partial, which will succeed, but then return Ok(0)
+ // when flushing " Line End\n"
+ assert_eq!(writer.write(b" Line End\n").unwrap(), 0);
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\nPartial");
+}
+
+/// LineWriter has a custom `write_all`; make sure it works correctly
+#[test]
+fn line_write_all() {
+ let writer = ProgrammableSink {
+ // Only write 5 bytes at a time
+ accept_prefix: Some(5),
+ ..Default::default()
+ };
+ let mut writer = LineWriter::new(writer);
+
+ writer.write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial").unwrap();
+ assert_eq!(&writer.get_ref().buffer, b"Line 1\nLine 2\nLine 3\nLine 4\n");
+ writer.write_all(b" Line 5\n").unwrap();
+ assert_eq!(
+ writer.get_ref().buffer.as_slice(),
+ b"Line 1\nLine 2\nLine 3\nLine 4\nPartial Line 5\n".as_ref(),
+ );
+}
+
+#[test]
+fn line_write_all_error() {
+ let writer = ProgrammableSink {
+ // Only accept up to 3 writes of up to 5 bytes each
+ accept_prefix: Some(5),
+ max_writes: Some(3),
+ ..Default::default()
+ };
+
+ let mut writer = LineWriter::new(writer);
+ let res = writer.write_all(b"Line 1\nLine 2\nLine 3\nLine 4\nPartial");
+ assert!(res.is_err());
+ // An error from write_all leaves everything in an indeterminate state,
+ // so there's nothing else to test here
+}
+
+/// Under certain circumstances, the old implementation of LineWriter
+/// would try to buffer "to the last newline" but be forced to buffer
+/// less than that, leading to inappropriate partial line writes.
+/// Regression test for that issue.
+#[test]
+fn partial_multiline_buffering() {
+ let writer = ProgrammableSink {
+ // Write only up to 5 bytes at a time
+ accept_prefix: Some(5),
+ ..Default::default()
+ };
+
+ let mut writer = LineWriter::with_capacity(10, writer);
+
+ let content = b"AAAAABBBBB\nCCCCDDDDDD\nEEE";
+
+ // When content is written, LineWriter will try to write blocks A, B,
+ // C, and D. Only block A will succeed. Under the old behavior, LineWriter
+ // would then try to buffer B, C and D, but because its capacity is 10,
+ // it will only be able to buffer B and C. We don't want to buffer
+ // partial lines concurrent with whole lines, so the correct behavior
+ // is to buffer only block B (out to the newline)
+ assert_eq!(writer.write(content).unwrap(), 11);
+ assert_eq!(writer.get_ref().buffer, *b"AAAAA");
+
+ writer.flush().unwrap();
+ assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB\n");
+}
+
+/// Same as test_partial_multiline_buffering, but in the event NO full lines
+/// fit in the buffer, just buffer as much as possible
+#[test]
+fn partial_multiline_buffering_without_full_line() {
+ let writer = ProgrammableSink {
+ // Write only up to 5 bytes at a time
+ accept_prefix: Some(5),
+ ..Default::default()
+ };
+
+ let mut writer = LineWriter::with_capacity(5, writer);
+
+ let content = b"AAAAABBBBBBBBBB\nCCCCC\nDDDDD";
+
+ // When content is written, LineWriter will try to write blocks A, B,
+ // and C. Only block A will succeed. Under the old behavior, LineWriter
+ // would then try to buffer B and C, but because its capacity is 5,
+ // it will only be able to buffer part of B. Because it's not possible
+ // for it to buffer any complete lines, it should buffer as much of B as
+ // possible
+ assert_eq!(writer.write(content).unwrap(), 10);
+ assert_eq!(writer.get_ref().buffer, *b"AAAAA");
+
+ writer.flush().unwrap();
+ assert_eq!(writer.get_ref().buffer, *b"AAAAABBBBB");
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+enum RecordedEvent {
+ Write(String),
+ Flush,
+}
+
+#[derive(Debug, Clone, Default)]
+struct WriteRecorder {
+ pub events: Vec<RecordedEvent>,
+}
+
+impl Write for WriteRecorder {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ use crate::str::from_utf8;
+
+ self.events.push(RecordedEvent::Write(from_utf8(buf).unwrap().to_string()));
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.events.push(RecordedEvent::Flush);
+ Ok(())
+ }
+}
+
+/// Test that a normal, formatted writeln only results in a single write
+/// call to the underlying writer. A naive implementation of
+/// LineWriter::write_all results in two writes: one of the buffered data,
+/// and another of the final substring in the formatted set
+#[test]
+fn single_formatted_write() {
+ let writer = WriteRecorder::default();
+ let mut writer = LineWriter::new(writer);
+
+ // Under a naive implementation of LineWriter, this will result in two
+ // writes: "hello, world" and "!\n", because write() has to flush the
+ // buffer before attempting to write the last "!\n". write_all shouldn't
+ // have this limitation.
+ writeln!(&mut writer, "{}, {}!", "hello", "world").unwrap();
+ assert_eq!(writer.get_ref().events, [RecordedEvent::Write("hello, world!\n".to_string())]);
+}
diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs
index 58343f6..5733735 100644
--- a/library/std/src/io/cursor.rs
+++ b/library/std/src/io/cursor.rs
@@ -1,3 +1,6 @@
+#[cfg(test)]
+mod tests;
+
use crate::io::prelude::*;
use crate::cmp;
@@ -447,531 +450,3 @@
Ok(())
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::io::prelude::*;
- use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom};
-
- #[test]
- fn test_vec_writer() {
- let mut writer = Vec::new();
- assert_eq!(writer.write(&[0]).unwrap(), 1);
- assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
- assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
- assert_eq!(
- writer
- .write_vectored(&[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);
- }
-
- #[test]
- fn test_mem_writer() {
- let mut writer = Cursor::new(Vec::new());
- assert_eq!(writer.write(&[0]).unwrap(), 1);
- assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
- assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
- assert_eq!(
- writer
- .write_vectored(&[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);
- }
-
- #[test]
- fn test_mem_mut_writer() {
- let mut vec = Vec::new();
- let mut writer = Cursor::new(&mut vec);
- assert_eq!(writer.write(&[0]).unwrap(), 1);
- assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
- assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
- assert_eq!(
- writer
- .write_vectored(&[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);
- }
-
- #[test]
- fn test_box_slice_writer() {
- let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
- assert_eq!(writer.position(), 0);
- assert_eq!(writer.write(&[0]).unwrap(), 1);
- assert_eq!(writer.position(), 1);
- assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
- assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
- assert_eq!(writer.position(), 8);
- assert_eq!(writer.write(&[]).unwrap(), 0);
- assert_eq!(writer.position(), 8);
-
- assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
- assert_eq!(writer.write(&[10]).unwrap(), 0);
- let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
- assert_eq!(&**writer.get_ref(), b);
- }
-
- #[test]
- 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(&[IoSlice::new(&[0])]).unwrap(), 1);
- assert_eq!(writer.position(), 1);
- assert_eq!(
- 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(&[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);
- }
-
- #[test]
- fn test_buf_writer() {
- let mut buf = [0 as u8; 9];
- {
- let mut writer = Cursor::new(&mut buf[..]);
- assert_eq!(writer.position(), 0);
- assert_eq!(writer.write(&[0]).unwrap(), 1);
- assert_eq!(writer.position(), 1);
- assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
- assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
- assert_eq!(writer.position(), 8);
- assert_eq!(writer.write(&[]).unwrap(), 0);
- assert_eq!(writer.position(), 8);
-
- assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
- assert_eq!(writer.write(&[10]).unwrap(), 0);
- }
- let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
- assert_eq!(buf, b);
- }
-
- #[test]
- fn test_buf_writer_vectored() {
- let mut buf = [0 as u8; 9];
- {
- let mut writer = Cursor::new(&mut buf[..]);
- assert_eq!(writer.position(), 0);
- assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
- assert_eq!(writer.position(), 1);
- assert_eq!(
- 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(&[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);
- }
-
- #[test]
- fn test_buf_writer_seek() {
- let mut buf = [0 as u8; 8];
- {
- let mut writer = Cursor::new(&mut buf[..]);
- assert_eq!(writer.position(), 0);
- assert_eq!(writer.write(&[1]).unwrap(), 1);
- assert_eq!(writer.position(), 1);
-
- assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2);
- assert_eq!(writer.position(), 2);
- assert_eq!(writer.write(&[2]).unwrap(), 1);
- assert_eq!(writer.position(), 3);
-
- assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1);
- assert_eq!(writer.position(), 1);
- assert_eq!(writer.write(&[3]).unwrap(), 1);
- assert_eq!(writer.position(), 2);
-
- assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
- assert_eq!(writer.position(), 7);
- assert_eq!(writer.write(&[4]).unwrap(), 1);
- assert_eq!(writer.position(), 8);
- }
- let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
- assert_eq!(buf, b);
- }
-
- #[test]
- fn test_buf_writer_error() {
- let mut buf = [0 as u8; 2];
- let mut writer = Cursor::new(&mut buf[..]);
- assert_eq!(writer.write(&[0]).unwrap(), 1);
- assert_eq!(writer.write(&[0, 0]).unwrap(), 1);
- assert_eq!(writer.write(&[0, 0]).unwrap(), 0);
- }
-
- #[test]
- fn test_mem_reader() {
- let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
- let mut buf = [];
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- assert_eq!(reader.position(), 0);
- let mut buf = [0];
- assert_eq!(reader.read(&mut buf).unwrap(), 1);
- assert_eq!(reader.position(), 1);
- let b: &[_] = &[0];
- assert_eq!(buf, b);
- let mut buf = [0; 4];
- assert_eq!(reader.read(&mut buf).unwrap(), 4);
- assert_eq!(reader.position(), 5);
- let b: &[_] = &[1, 2, 3, 4];
- assert_eq!(buf, b);
- assert_eq!(reader.read(&mut buf).unwrap(), 3);
- let b: &[_] = &[5, 6, 7];
- assert_eq!(&buf[..3], b);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- }
-
- #[test]
- 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 [IoSliceMut::new(&mut buf)]).unwrap(), 0);
- assert_eq!(reader.position(), 0);
- let mut buf = [0];
- assert_eq!(
- reader
- .read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),])
- .unwrap(),
- 1,
- );
- assert_eq!(reader.position(), 1);
- let b: &[_] = &[0];
- assert_eq!(buf, b);
- let mut buf1 = [0; 4];
- let mut buf2 = [0; 4];
- assert_eq!(
- reader
- .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),])
- .unwrap(),
- 7,
- );
- let b1: &[_] = &[1, 2, 3, 4];
- let b2: &[_] = &[5, 6, 7];
- assert_eq!(buf1, b1);
- assert_eq!(&buf2[..3], b2);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- }
-
- #[test]
- fn test_boxed_slice_reader() {
- let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
- let mut buf = [];
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- assert_eq!(reader.position(), 0);
- let mut buf = [0];
- assert_eq!(reader.read(&mut buf).unwrap(), 1);
- assert_eq!(reader.position(), 1);
- let b: &[_] = &[0];
- assert_eq!(buf, b);
- let mut buf = [0; 4];
- assert_eq!(reader.read(&mut buf).unwrap(), 4);
- assert_eq!(reader.position(), 5);
- let b: &[_] = &[1, 2, 3, 4];
- assert_eq!(buf, b);
- assert_eq!(reader.read(&mut buf).unwrap(), 3);
- let b: &[_] = &[5, 6, 7];
- assert_eq!(&buf[..3], b);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- }
-
- #[test]
- 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 [IoSliceMut::new(&mut buf)]).unwrap(), 0);
- assert_eq!(reader.position(), 0);
- let mut buf = [0];
- assert_eq!(
- reader
- .read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),])
- .unwrap(),
- 1,
- );
- assert_eq!(reader.position(), 1);
- let b: &[_] = &[0];
- assert_eq!(buf, b);
- let mut buf1 = [0; 4];
- let mut buf2 = [0; 4];
- assert_eq!(
- reader
- .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
- .unwrap(),
- 7,
- );
- let b1: &[_] = &[1, 2, 3, 4];
- let b2: &[_] = &[5, 6, 7];
- assert_eq!(buf1, b1);
- assert_eq!(&buf2[..3], b2);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- }
-
- #[test]
- fn read_to_end() {
- let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
- let mut v = Vec::new();
- reader.read_to_end(&mut v).unwrap();
- assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]);
- }
-
- #[test]
- fn test_slice_reader() {
- let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
- let reader = &mut &in_buf[..];
- let mut buf = [];
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- let mut buf = [0];
- assert_eq!(reader.read(&mut buf).unwrap(), 1);
- assert_eq!(reader.len(), 7);
- let b: &[_] = &[0];
- assert_eq!(&buf[..], b);
- let mut buf = [0; 4];
- assert_eq!(reader.read(&mut buf).unwrap(), 4);
- assert_eq!(reader.len(), 3);
- let b: &[_] = &[1, 2, 3, 4];
- assert_eq!(&buf[..], b);
- assert_eq!(reader.read(&mut buf).unwrap(), 3);
- let b: &[_] = &[5, 6, 7];
- assert_eq!(&buf[..3], b);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- }
-
- #[test]
- fn test_slice_reader_vectored() {
- 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 [IoSliceMut::new(&mut buf)]).unwrap(), 0);
- let mut buf = [0];
- assert_eq!(
- reader
- .read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),])
- .unwrap(),
- 1,
- );
- assert_eq!(reader.len(), 7);
- let b: &[_] = &[0];
- assert_eq!(buf, b);
- let mut buf1 = [0; 4];
- let mut buf2 = [0; 4];
- assert_eq!(
- reader
- .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
- .unwrap(),
- 7,
- );
- let b1: &[_] = &[1, 2, 3, 4];
- let b2: &[_] = &[5, 6, 7];
- assert_eq!(buf1, b1);
- assert_eq!(&buf2[..3], b2);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- }
-
- #[test]
- fn test_read_exact() {
- let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
- let reader = &mut &in_buf[..];
- let mut buf = [];
- assert!(reader.read_exact(&mut buf).is_ok());
- let mut buf = [8];
- assert!(reader.read_exact(&mut buf).is_ok());
- assert_eq!(buf[0], 0);
- assert_eq!(reader.len(), 7);
- let mut buf = [0, 0, 0, 0, 0, 0, 0];
- assert!(reader.read_exact(&mut buf).is_ok());
- assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]);
- assert_eq!(reader.len(), 0);
- let mut buf = [0];
- assert!(reader.read_exact(&mut buf).is_err());
- }
-
- #[test]
- fn test_buf_reader() {
- let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
- let mut reader = Cursor::new(&in_buf[..]);
- let mut buf = [];
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- assert_eq!(reader.position(), 0);
- let mut buf = [0];
- assert_eq!(reader.read(&mut buf).unwrap(), 1);
- assert_eq!(reader.position(), 1);
- let b: &[_] = &[0];
- assert_eq!(buf, b);
- let mut buf = [0; 4];
- assert_eq!(reader.read(&mut buf).unwrap(), 4);
- assert_eq!(reader.position(), 5);
- let b: &[_] = &[1, 2, 3, 4];
- assert_eq!(buf, b);
- assert_eq!(reader.read(&mut buf).unwrap(), 3);
- let b: &[_] = &[5, 6, 7];
- assert_eq!(&buf[..3], b);
- assert_eq!(reader.read(&mut buf).unwrap(), 0);
- }
-
- #[test]
- fn seek_past_end() {
- let buf = [0xff];
- let mut r = Cursor::new(&buf[..]);
- assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
- assert_eq!(r.read(&mut [0]).unwrap(), 0);
-
- let mut r = Cursor::new(vec![10]);
- assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
- assert_eq!(r.read(&mut [0]).unwrap(), 0);
-
- let mut buf = [0];
- let mut r = Cursor::new(&mut buf[..]);
- assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
- assert_eq!(r.write(&[3]).unwrap(), 0);
-
- let mut r = Cursor::new(vec![10].into_boxed_slice());
- assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
- assert_eq!(r.write(&[3]).unwrap(), 0);
- }
-
- #[test]
- fn seek_past_i64() {
- let buf = [0xff];
- let mut r = Cursor::new(&buf[..]);
- assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
- assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
- assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
- assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
- assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
- assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
-
- let mut r = Cursor::new(vec![10]);
- assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
- assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
- assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
- assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
- assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
- assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
-
- let mut buf = [0];
- let mut r = Cursor::new(&mut buf[..]);
- assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
- assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
- assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
- assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
- assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
- assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
-
- let mut r = Cursor::new(vec![10].into_boxed_slice());
- assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
- assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
- assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
- assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
- assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
- assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
- }
-
- #[test]
- fn seek_before_0() {
- let buf = [0xff];
- let mut r = Cursor::new(&buf[..]);
- assert!(r.seek(SeekFrom::End(-2)).is_err());
-
- let mut r = Cursor::new(vec![10]);
- assert!(r.seek(SeekFrom::End(-2)).is_err());
-
- let mut buf = [0];
- let mut r = Cursor::new(&mut buf[..]);
- assert!(r.seek(SeekFrom::End(-2)).is_err());
-
- let mut r = Cursor::new(vec![10].into_boxed_slice());
- assert!(r.seek(SeekFrom::End(-2)).is_err());
- }
-
- #[test]
- fn test_seekable_mem_writer() {
- let mut writer = Cursor::new(Vec::<u8>::new());
- assert_eq!(writer.position(), 0);
- assert_eq!(writer.write(&[0]).unwrap(), 1);
- assert_eq!(writer.position(), 1);
- assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
- assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
- assert_eq!(writer.position(), 8);
- let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
- assert_eq!(&writer.get_ref()[..], b);
-
- assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0);
- assert_eq!(writer.position(), 0);
- assert_eq!(writer.write(&[3, 4]).unwrap(), 2);
- let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7];
- assert_eq!(&writer.get_ref()[..], b);
-
- assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3);
- assert_eq!(writer.write(&[0, 1]).unwrap(), 2);
- let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7];
- assert_eq!(&writer.get_ref()[..], b);
-
- assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
- assert_eq!(writer.write(&[1, 2]).unwrap(), 2);
- let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2];
- assert_eq!(&writer.get_ref()[..], b);
-
- assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10);
- assert_eq!(writer.write(&[1]).unwrap(), 1);
- let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1];
- assert_eq!(&writer.get_ref()[..], b);
- }
-
- #[test]
- fn vec_seek_past_end() {
- let mut r = Cursor::new(Vec::new());
- assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
- assert_eq!(r.write(&[3]).unwrap(), 1);
- }
-
- #[test]
- fn vec_seek_before_0() {
- let mut r = Cursor::new(Vec::new());
- assert!(r.seek(SeekFrom::End(-2)).is_err());
- }
-
- #[test]
- #[cfg(target_pointer_width = "32")]
- fn vec_seek_and_write_past_usize_max() {
- let mut c = Cursor::new(Vec::new());
- c.set_position(usize::MAX as u64 + 1);
- assert!(c.write_all(&[1, 2, 3]).is_err());
- }
-
- #[test]
- fn test_partial_eq() {
- assert_eq!(Cursor::new(Vec::<u8>::new()), Cursor::new(Vec::<u8>::new()));
- }
-
- #[test]
- fn test_eq() {
- struct AssertEq<T: Eq>(pub T);
-
- let _: AssertEq<Cursor<Vec<u8>>> = AssertEq(Cursor::new(Vec::new()));
- }
-}
diff --git a/library/std/src/io/cursor/tests.rs b/library/std/src/io/cursor/tests.rs
new file mode 100644
index 0000000..80d88ca
--- /dev/null
+++ b/library/std/src/io/cursor/tests.rs
@@ -0,0 +1,516 @@
+use crate::io::prelude::*;
+use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom};
+
+#[test]
+fn test_vec_writer() {
+ let mut writer = Vec::new();
+ assert_eq!(writer.write(&[0]).unwrap(), 1);
+ assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
+ assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
+ assert_eq!(
+ writer
+ .write_vectored(&[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);
+}
+
+#[test]
+fn test_mem_writer() {
+ let mut writer = Cursor::new(Vec::new());
+ assert_eq!(writer.write(&[0]).unwrap(), 1);
+ assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
+ assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
+ assert_eq!(
+ writer
+ .write_vectored(&[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);
+}
+
+#[test]
+fn test_mem_mut_writer() {
+ let mut vec = Vec::new();
+ let mut writer = Cursor::new(&mut vec);
+ assert_eq!(writer.write(&[0]).unwrap(), 1);
+ assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
+ assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
+ assert_eq!(
+ writer
+ .write_vectored(&[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);
+}
+
+#[test]
+fn test_box_slice_writer() {
+ let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write(&[0]).unwrap(), 1);
+ assert_eq!(writer.position(), 1);
+ assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
+ assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
+ assert_eq!(writer.position(), 8);
+ assert_eq!(writer.write(&[]).unwrap(), 0);
+ assert_eq!(writer.position(), 8);
+
+ assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
+ assert_eq!(writer.write(&[10]).unwrap(), 0);
+ let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
+ assert_eq!(&**writer.get_ref(), b);
+}
+
+#[test]
+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(&[IoSlice::new(&[0])]).unwrap(), 1);
+ assert_eq!(writer.position(), 1);
+ assert_eq!(
+ 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(&[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);
+}
+
+#[test]
+fn test_buf_writer() {
+ let mut buf = [0 as u8; 9];
+ {
+ let mut writer = Cursor::new(&mut buf[..]);
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write(&[0]).unwrap(), 1);
+ assert_eq!(writer.position(), 1);
+ assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
+ assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
+ assert_eq!(writer.position(), 8);
+ assert_eq!(writer.write(&[]).unwrap(), 0);
+ assert_eq!(writer.position(), 8);
+
+ assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
+ assert_eq!(writer.write(&[10]).unwrap(), 0);
+ }
+ let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
+ assert_eq!(buf, b);
+}
+
+#[test]
+fn test_buf_writer_vectored() {
+ let mut buf = [0 as u8; 9];
+ {
+ let mut writer = Cursor::new(&mut buf[..]);
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
+ assert_eq!(writer.position(), 1);
+ assert_eq!(
+ 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(&[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);
+}
+
+#[test]
+fn test_buf_writer_seek() {
+ let mut buf = [0 as u8; 8];
+ {
+ let mut writer = Cursor::new(&mut buf[..]);
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write(&[1]).unwrap(), 1);
+ assert_eq!(writer.position(), 1);
+
+ assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2);
+ assert_eq!(writer.position(), 2);
+ assert_eq!(writer.write(&[2]).unwrap(), 1);
+ assert_eq!(writer.position(), 3);
+
+ assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1);
+ assert_eq!(writer.position(), 1);
+ assert_eq!(writer.write(&[3]).unwrap(), 1);
+ assert_eq!(writer.position(), 2);
+
+ assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
+ assert_eq!(writer.position(), 7);
+ assert_eq!(writer.write(&[4]).unwrap(), 1);
+ assert_eq!(writer.position(), 8);
+ }
+ let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
+ assert_eq!(buf, b);
+}
+
+#[test]
+fn test_buf_writer_error() {
+ let mut buf = [0 as u8; 2];
+ let mut writer = Cursor::new(&mut buf[..]);
+ assert_eq!(writer.write(&[0]).unwrap(), 1);
+ assert_eq!(writer.write(&[0, 0]).unwrap(), 1);
+ assert_eq!(writer.write(&[0, 0]).unwrap(), 0);
+}
+
+#[test]
+fn test_mem_reader() {
+ let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
+ let mut buf = [];
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+ assert_eq!(reader.position(), 0);
+ let mut buf = [0];
+ assert_eq!(reader.read(&mut buf).unwrap(), 1);
+ assert_eq!(reader.position(), 1);
+ let b: &[_] = &[0];
+ assert_eq!(buf, b);
+ let mut buf = [0; 4];
+ assert_eq!(reader.read(&mut buf).unwrap(), 4);
+ assert_eq!(reader.position(), 5);
+ let b: &[_] = &[1, 2, 3, 4];
+ assert_eq!(buf, b);
+ assert_eq!(reader.read(&mut buf).unwrap(), 3);
+ let b: &[_] = &[5, 6, 7];
+ assert_eq!(&buf[..3], b);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+}
+
+#[test]
+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 [IoSliceMut::new(&mut buf)]).unwrap(), 0);
+ assert_eq!(reader.position(), 0);
+ let mut buf = [0];
+ assert_eq!(
+ reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
+ 1,
+ );
+ assert_eq!(reader.position(), 1);
+ let b: &[_] = &[0];
+ assert_eq!(buf, b);
+ let mut buf1 = [0; 4];
+ let mut buf2 = [0; 4];
+ assert_eq!(
+ reader
+ .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),])
+ .unwrap(),
+ 7,
+ );
+ let b1: &[_] = &[1, 2, 3, 4];
+ let b2: &[_] = &[5, 6, 7];
+ assert_eq!(buf1, b1);
+ assert_eq!(&buf2[..3], b2);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+}
+
+#[test]
+fn test_boxed_slice_reader() {
+ let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
+ let mut buf = [];
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+ assert_eq!(reader.position(), 0);
+ let mut buf = [0];
+ assert_eq!(reader.read(&mut buf).unwrap(), 1);
+ assert_eq!(reader.position(), 1);
+ let b: &[_] = &[0];
+ assert_eq!(buf, b);
+ let mut buf = [0; 4];
+ assert_eq!(reader.read(&mut buf).unwrap(), 4);
+ assert_eq!(reader.position(), 5);
+ let b: &[_] = &[1, 2, 3, 4];
+ assert_eq!(buf, b);
+ assert_eq!(reader.read(&mut buf).unwrap(), 3);
+ let b: &[_] = &[5, 6, 7];
+ assert_eq!(&buf[..3], b);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+}
+
+#[test]
+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 [IoSliceMut::new(&mut buf)]).unwrap(), 0);
+ assert_eq!(reader.position(), 0);
+ let mut buf = [0];
+ assert_eq!(
+ reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
+ 1,
+ );
+ assert_eq!(reader.position(), 1);
+ let b: &[_] = &[0];
+ assert_eq!(buf, b);
+ let mut buf1 = [0; 4];
+ let mut buf2 = [0; 4];
+ assert_eq!(
+ reader
+ .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
+ .unwrap(),
+ 7,
+ );
+ let b1: &[_] = &[1, 2, 3, 4];
+ let b2: &[_] = &[5, 6, 7];
+ assert_eq!(buf1, b1);
+ assert_eq!(&buf2[..3], b2);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+}
+
+#[test]
+fn read_to_end() {
+ let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
+ let mut v = Vec::new();
+ reader.read_to_end(&mut v).unwrap();
+ assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]);
+}
+
+#[test]
+fn test_slice_reader() {
+ let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
+ let reader = &mut &in_buf[..];
+ let mut buf = [];
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+ let mut buf = [0];
+ assert_eq!(reader.read(&mut buf).unwrap(), 1);
+ assert_eq!(reader.len(), 7);
+ let b: &[_] = &[0];
+ assert_eq!(&buf[..], b);
+ let mut buf = [0; 4];
+ assert_eq!(reader.read(&mut buf).unwrap(), 4);
+ assert_eq!(reader.len(), 3);
+ let b: &[_] = &[1, 2, 3, 4];
+ assert_eq!(&buf[..], b);
+ assert_eq!(reader.read(&mut buf).unwrap(), 3);
+ let b: &[_] = &[5, 6, 7];
+ assert_eq!(&buf[..3], b);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+}
+
+#[test]
+fn test_slice_reader_vectored() {
+ 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 [IoSliceMut::new(&mut buf)]).unwrap(), 0);
+ let mut buf = [0];
+ assert_eq!(
+ reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
+ 1,
+ );
+ assert_eq!(reader.len(), 7);
+ let b: &[_] = &[0];
+ assert_eq!(buf, b);
+ let mut buf1 = [0; 4];
+ let mut buf2 = [0; 4];
+ assert_eq!(
+ reader
+ .read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
+ .unwrap(),
+ 7,
+ );
+ let b1: &[_] = &[1, 2, 3, 4];
+ let b2: &[_] = &[5, 6, 7];
+ assert_eq!(buf1, b1);
+ assert_eq!(&buf2[..3], b2);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+}
+
+#[test]
+fn test_read_exact() {
+ let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
+ let reader = &mut &in_buf[..];
+ let mut buf = [];
+ assert!(reader.read_exact(&mut buf).is_ok());
+ let mut buf = [8];
+ assert!(reader.read_exact(&mut buf).is_ok());
+ assert_eq!(buf[0], 0);
+ assert_eq!(reader.len(), 7);
+ let mut buf = [0, 0, 0, 0, 0, 0, 0];
+ assert!(reader.read_exact(&mut buf).is_ok());
+ assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]);
+ assert_eq!(reader.len(), 0);
+ let mut buf = [0];
+ assert!(reader.read_exact(&mut buf).is_err());
+}
+
+#[test]
+fn test_buf_reader() {
+ let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
+ let mut reader = Cursor::new(&in_buf[..]);
+ let mut buf = [];
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+ assert_eq!(reader.position(), 0);
+ let mut buf = [0];
+ assert_eq!(reader.read(&mut buf).unwrap(), 1);
+ assert_eq!(reader.position(), 1);
+ let b: &[_] = &[0];
+ assert_eq!(buf, b);
+ let mut buf = [0; 4];
+ assert_eq!(reader.read(&mut buf).unwrap(), 4);
+ assert_eq!(reader.position(), 5);
+ let b: &[_] = &[1, 2, 3, 4];
+ assert_eq!(buf, b);
+ assert_eq!(reader.read(&mut buf).unwrap(), 3);
+ let b: &[_] = &[5, 6, 7];
+ assert_eq!(&buf[..3], b);
+ assert_eq!(reader.read(&mut buf).unwrap(), 0);
+}
+
+#[test]
+fn seek_past_end() {
+ let buf = [0xff];
+ let mut r = Cursor::new(&buf[..]);
+ assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
+ assert_eq!(r.read(&mut [0]).unwrap(), 0);
+
+ let mut r = Cursor::new(vec![10]);
+ assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
+ assert_eq!(r.read(&mut [0]).unwrap(), 0);
+
+ let mut buf = [0];
+ let mut r = Cursor::new(&mut buf[..]);
+ assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
+ assert_eq!(r.write(&[3]).unwrap(), 0);
+
+ let mut r = Cursor::new(vec![10].into_boxed_slice());
+ assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
+ assert_eq!(r.write(&[3]).unwrap(), 0);
+}
+
+#[test]
+fn seek_past_i64() {
+ let buf = [0xff];
+ let mut r = Cursor::new(&buf[..]);
+ assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
+ assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
+ assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
+ assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
+ assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
+ assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
+
+ let mut r = Cursor::new(vec![10]);
+ assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
+ assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
+ assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
+ assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
+ assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
+ assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
+
+ let mut buf = [0];
+ let mut r = Cursor::new(&mut buf[..]);
+ assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
+ assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
+ assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
+ assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
+ assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
+ assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
+
+ let mut r = Cursor::new(vec![10].into_boxed_slice());
+ assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
+ assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
+ assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
+ assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
+ assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
+ assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
+}
+
+#[test]
+fn seek_before_0() {
+ let buf = [0xff];
+ let mut r = Cursor::new(&buf[..]);
+ assert!(r.seek(SeekFrom::End(-2)).is_err());
+
+ let mut r = Cursor::new(vec![10]);
+ assert!(r.seek(SeekFrom::End(-2)).is_err());
+
+ let mut buf = [0];
+ let mut r = Cursor::new(&mut buf[..]);
+ assert!(r.seek(SeekFrom::End(-2)).is_err());
+
+ let mut r = Cursor::new(vec![10].into_boxed_slice());
+ assert!(r.seek(SeekFrom::End(-2)).is_err());
+}
+
+#[test]
+fn test_seekable_mem_writer() {
+ let mut writer = Cursor::new(Vec::<u8>::new());
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write(&[0]).unwrap(), 1);
+ assert_eq!(writer.position(), 1);
+ assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
+ assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
+ assert_eq!(writer.position(), 8);
+ let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
+ assert_eq!(&writer.get_ref()[..], b);
+
+ assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0);
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write(&[3, 4]).unwrap(), 2);
+ let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7];
+ assert_eq!(&writer.get_ref()[..], b);
+
+ assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3);
+ assert_eq!(writer.write(&[0, 1]).unwrap(), 2);
+ let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7];
+ assert_eq!(&writer.get_ref()[..], b);
+
+ assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
+ assert_eq!(writer.write(&[1, 2]).unwrap(), 2);
+ let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2];
+ assert_eq!(&writer.get_ref()[..], b);
+
+ assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10);
+ assert_eq!(writer.write(&[1]).unwrap(), 1);
+ let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1];
+ assert_eq!(&writer.get_ref()[..], b);
+}
+
+#[test]
+fn vec_seek_past_end() {
+ let mut r = Cursor::new(Vec::new());
+ assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
+ assert_eq!(r.write(&[3]).unwrap(), 1);
+}
+
+#[test]
+fn vec_seek_before_0() {
+ let mut r = Cursor::new(Vec::new());
+ assert!(r.seek(SeekFrom::End(-2)).is_err());
+}
+
+#[test]
+#[cfg(target_pointer_width = "32")]
+fn vec_seek_and_write_past_usize_max() {
+ let mut c = Cursor::new(Vec::new());
+ c.set_position(usize::MAX as u64 + 1);
+ assert!(c.write_all(&[1, 2, 3]).is_err());
+}
+
+#[test]
+fn test_partial_eq() {
+ assert_eq!(Cursor::new(Vec::<u8>::new()), Cursor::new(Vec::<u8>::new()));
+}
+
+#[test]
+fn test_eq() {
+ struct AssertEq<T: Eq>(pub T);
+
+ let _: AssertEq<Cursor<Vec<u8>>> = AssertEq(Cursor::new(Vec::new()));
+}
diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs
index e6eda2c..ba0f0a0 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -1,3 +1,6 @@
+#[cfg(test)]
+mod tests;
+
use crate::convert::From;
use crate::error;
use crate::fmt;
@@ -574,60 +577,3 @@
fn _is_sync_send<T: Sync + Send>() {}
_is_sync_send::<Error>();
}
-
-#[cfg(test)]
-mod test {
- use super::{Custom, Error, ErrorKind, Repr};
- use crate::error;
- use crate::fmt;
- use crate::sys::decode_error_kind;
- use crate::sys::os::error_string;
-
- #[test]
- fn test_debug_error() {
- let code = 6;
- let msg = error_string(code);
- let kind = decode_error_kind(code);
- let err = Error {
- repr: Repr::Custom(box Custom {
- kind: ErrorKind::InvalidInput,
- error: box Error { repr: super::Repr::Os(code) },
- }),
- };
- let expected = format!(
- "Custom {{ \
- kind: InvalidInput, \
- error: Os {{ \
- code: {:?}, \
- kind: {:?}, \
- message: {:?} \
- }} \
- }}",
- code, kind, msg
- );
- assert_eq!(format!("{:?}", err), expected);
- }
-
- #[test]
- fn test_downcasting() {
- #[derive(Debug)]
- struct TestError;
-
- impl fmt::Display for TestError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("asdf")
- }
- }
-
- impl error::Error for TestError {}
-
- // we have to call all of these UFCS style right now since method
- // resolution won't implicitly drop the Send+Sync bounds
- let mut err = Error::new(ErrorKind::Other, TestError);
- assert!(err.get_ref().unwrap().is::<TestError>());
- assert_eq!("asdf", err.get_ref().unwrap().to_string());
- assert!(err.get_mut().unwrap().is::<TestError>());
- let extracted = err.into_inner().unwrap();
- extracted.downcast::<TestError>().unwrap();
- }
-}
diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs
new file mode 100644
index 0000000..0cce936
--- /dev/null
+++ b/library/std/src/io/error/tests.rs
@@ -0,0 +1,53 @@
+use super::{Custom, Error, ErrorKind, Repr};
+use crate::error;
+use crate::fmt;
+use crate::sys::decode_error_kind;
+use crate::sys::os::error_string;
+
+#[test]
+fn test_debug_error() {
+ let code = 6;
+ let msg = error_string(code);
+ let kind = decode_error_kind(code);
+ let err = Error {
+ repr: Repr::Custom(box Custom {
+ kind: ErrorKind::InvalidInput,
+ error: box Error { repr: super::Repr::Os(code) },
+ }),
+ };
+ let expected = format!(
+ "Custom {{ \
+ kind: InvalidInput, \
+ error: Os {{ \
+ code: {:?}, \
+ kind: {:?}, \
+ message: {:?} \
+ }} \
+ }}",
+ code, kind, msg
+ );
+ assert_eq!(format!("{:?}", err), expected);
+}
+
+#[test]
+fn test_downcasting() {
+ #[derive(Debug)]
+ struct TestError;
+
+ impl fmt::Display for TestError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("asdf")
+ }
+ }
+
+ impl error::Error for TestError {}
+
+ // we have to call all of these UFCS style right now since method
+ // resolution won't implicitly drop the Send+Sync bounds
+ let mut err = Error::new(ErrorKind::Other, TestError);
+ assert!(err.get_ref().unwrap().is::<TestError>());
+ assert_eq!("asdf", err.get_ref().unwrap().to_string());
+ assert!(err.get_mut().unwrap().is::<TestError>());
+ let extracted = err.into_inner().unwrap();
+ extracted.downcast::<TestError>().unwrap();
+}
diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs
index 01dff0b..e09e7ba 100644
--- a/library/std/src/io/impls.rs
+++ b/library/std/src/io/impls.rs
@@ -1,3 +1,6 @@
+#[cfg(test)]
+mod tests;
+
use crate::cmp;
use crate::fmt;
use crate::io::{
@@ -397,64 +400,3 @@
Ok(())
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::io::prelude::*;
-
- #[bench]
- fn bench_read_slice(b: &mut test::Bencher) {
- let buf = [5; 1024];
- let mut dst = [0; 128];
-
- b.iter(|| {
- let mut rd = &buf[..];
- for _ in 0..8 {
- let _ = rd.read(&mut dst);
- test::black_box(&dst);
- }
- })
- }
-
- #[bench]
- fn bench_write_slice(b: &mut test::Bencher) {
- let mut buf = [0; 1024];
- let src = [5; 128];
-
- b.iter(|| {
- let mut wr = &mut buf[..];
- for _ in 0..8 {
- let _ = wr.write_all(&src);
- test::black_box(&wr);
- }
- })
- }
-
- #[bench]
- fn bench_read_vec(b: &mut test::Bencher) {
- let buf = vec![5; 1024];
- let mut dst = [0; 128];
-
- b.iter(|| {
- let mut rd = &buf[..];
- for _ in 0..8 {
- let _ = rd.read(&mut dst);
- test::black_box(&dst);
- }
- })
- }
-
- #[bench]
- fn bench_write_vec(b: &mut test::Bencher) {
- let mut buf = Vec::with_capacity(1024);
- let src = [5; 128];
-
- b.iter(|| {
- let mut wr = &mut buf[..];
- for _ in 0..8 {
- let _ = wr.write_all(&src);
- test::black_box(&wr);
- }
- })
- }
-}
diff --git a/library/std/src/io/impls/tests.rs b/library/std/src/io/impls/tests.rs
new file mode 100644
index 0000000..d1cd84a
--- /dev/null
+++ b/library/std/src/io/impls/tests.rs
@@ -0,0 +1,57 @@
+use crate::io::prelude::*;
+
+#[bench]
+fn bench_read_slice(b: &mut test::Bencher) {
+ let buf = [5; 1024];
+ let mut dst = [0; 128];
+
+ b.iter(|| {
+ let mut rd = &buf[..];
+ for _ in 0..8 {
+ let _ = rd.read(&mut dst);
+ test::black_box(&dst);
+ }
+ })
+}
+
+#[bench]
+fn bench_write_slice(b: &mut test::Bencher) {
+ let mut buf = [0; 1024];
+ let src = [5; 128];
+
+ b.iter(|| {
+ let mut wr = &mut buf[..];
+ for _ in 0..8 {
+ let _ = wr.write_all(&src);
+ test::black_box(&wr);
+ }
+ })
+}
+
+#[bench]
+fn bench_read_vec(b: &mut test::Bencher) {
+ let buf = vec![5; 1024];
+ let mut dst = [0; 128];
+
+ b.iter(|| {
+ let mut rd = &buf[..];
+ for _ in 0..8 {
+ let _ = rd.read(&mut dst);
+ test::black_box(&dst);
+ }
+ })
+}
+
+#[bench]
+fn bench_write_vec(b: &mut test::Bencher) {
+ let mut buf = Vec::with_capacity(1024);
+ let src = [5; 128];
+
+ b.iter(|| {
+ let mut wr = &mut buf[..];
+ for _ in 0..8 {
+ let _ = wr.write_all(&src);
+ test::black_box(&wr);
+ }
+ })
+}
diff --git a/library/std/src/io/lazy.rs b/library/std/src/io/lazy.rs
deleted file mode 100644
index 1968d49..0000000
--- a/library/std/src/io/lazy.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-use crate::cell::Cell;
-use crate::ptr;
-use crate::sync::Arc;
-use crate::sys_common;
-use crate::sys_common::mutex::Mutex;
-
-pub struct Lazy<T> {
- // We never call `lock.init()`, so it is UB to attempt to acquire this mutex reentrantly!
- lock: Mutex,
- ptr: Cell<*mut Arc<T>>,
-}
-
-#[inline]
-const fn done<T>() -> *mut Arc<T> {
- 1_usize as *mut _
-}
-
-unsafe impl<T> Sync for Lazy<T> {}
-
-impl<T> Lazy<T> {
- pub const fn new() -> Lazy<T> {
- Lazy { lock: Mutex::new(), ptr: Cell::new(ptr::null_mut()) }
- }
-}
-
-impl<T: Send + Sync + 'static> Lazy<T> {
- /// Safety: `init` must not call `get` on the variable that is being
- /// initialized.
- pub unsafe fn get(&'static self, init: fn() -> Arc<T>) -> Option<Arc<T>> {
- let _guard = self.lock.lock();
- let ptr = self.ptr.get();
- if ptr.is_null() {
- Some(self.init(init))
- } else if ptr == done() {
- None
- } else {
- Some((*ptr).clone())
- }
- }
-
- // Must only be called with `lock` held
- unsafe fn init(&'static self, init: fn() -> Arc<T>) -> Arc<T> {
- // If we successfully register an at exit handler, then we cache the
- // `Arc` allocation in our own internal box (it will get deallocated by
- // the at exit handler). Otherwise we just return the freshly allocated
- // `Arc`.
- let registered = sys_common::at_exit(move || {
- let ptr = {
- let _guard = self.lock.lock();
- self.ptr.replace(done())
- };
- drop(Box::from_raw(ptr))
- });
- // This could reentrantly call `init` again, which is a problem
- // because our `lock` allows reentrancy!
- // That's why `get` is unsafe and requires the caller to ensure no reentrancy happens.
- let ret = init();
- if registered.is_ok() {
- self.ptr.set(Box::into_raw(Box::new(ret.clone())));
- }
- ret
- }
-}
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 462b696..d9d0380 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -249,6 +249,9 @@
#![stable(feature = "rust1", since = "1.0.0")]
+#[cfg(test)]
+mod tests;
+
use crate::cmp;
use crate::fmt;
use crate::memchr;
@@ -282,7 +285,6 @@
mod cursor;
mod error;
mod impls;
-mod lazy;
pub mod prelude;
mod stdio;
mod util;
@@ -2481,501 +2483,3 @@
}
}
}
-
-#[cfg(test)]
-mod tests {
- use super::{repeat, Cursor, SeekFrom};
- use crate::cmp::{self, min};
- use crate::io::prelude::*;
- use crate::io::{self, IoSlice, IoSliceMut};
- use crate::ops::Deref;
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn read_until() {
- let mut buf = Cursor::new(&b"12"[..]);
- let mut v = Vec::new();
- assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2);
- assert_eq!(v, b"12");
-
- let mut buf = Cursor::new(&b"1233"[..]);
- let mut v = Vec::new();
- assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3);
- assert_eq!(v, b"123");
- v.truncate(0);
- assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1);
- assert_eq!(v, b"3");
- v.truncate(0);
- assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0);
- assert_eq!(v, []);
- }
-
- #[test]
- fn split() {
- let buf = Cursor::new(&b"12"[..]);
- let mut s = buf.split(b'3');
- assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
- assert!(s.next().is_none());
-
- let buf = Cursor::new(&b"1233"[..]);
- let mut s = buf.split(b'3');
- assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
- assert_eq!(s.next().unwrap().unwrap(), vec![]);
- assert!(s.next().is_none());
- }
-
- #[test]
- fn read_line() {
- let mut buf = Cursor::new(&b"12"[..]);
- let mut v = String::new();
- assert_eq!(buf.read_line(&mut v).unwrap(), 2);
- assert_eq!(v, "12");
-
- let mut buf = Cursor::new(&b"12\n\n"[..]);
- let mut v = String::new();
- assert_eq!(buf.read_line(&mut v).unwrap(), 3);
- assert_eq!(v, "12\n");
- v.truncate(0);
- assert_eq!(buf.read_line(&mut v).unwrap(), 1);
- assert_eq!(v, "\n");
- v.truncate(0);
- assert_eq!(buf.read_line(&mut v).unwrap(), 0);
- assert_eq!(v, "");
- }
-
- #[test]
- fn lines() {
- let buf = Cursor::new(&b"12\r"[..]);
- let mut s = buf.lines();
- assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string());
- assert!(s.next().is_none());
-
- let buf = Cursor::new(&b"12\r\n\n"[..]);
- let mut s = buf.lines();
- assert_eq!(s.next().unwrap().unwrap(), "12".to_string());
- assert_eq!(s.next().unwrap().unwrap(), "".to_string());
- assert!(s.next().is_none());
- }
-
- #[test]
- fn read_to_end() {
- let mut c = Cursor::new(&b""[..]);
- let mut v = Vec::new();
- assert_eq!(c.read_to_end(&mut v).unwrap(), 0);
- assert_eq!(v, []);
-
- let mut c = Cursor::new(&b"1"[..]);
- let mut v = Vec::new();
- assert_eq!(c.read_to_end(&mut v).unwrap(), 1);
- assert_eq!(v, b"1");
-
- let cap = 1024 * 1024;
- let data = (0..cap).map(|i| (i / 3) as u8).collect::<Vec<_>>();
- let mut v = Vec::new();
- let (a, b) = data.split_at(data.len() / 2);
- assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len());
- assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len());
- assert_eq!(v, data);
- }
-
- #[test]
- fn read_to_string() {
- let mut c = Cursor::new(&b""[..]);
- let mut v = String::new();
- assert_eq!(c.read_to_string(&mut v).unwrap(), 0);
- assert_eq!(v, "");
-
- let mut c = Cursor::new(&b"1"[..]);
- let mut v = String::new();
- assert_eq!(c.read_to_string(&mut v).unwrap(), 1);
- assert_eq!(v, "1");
-
- let mut c = Cursor::new(&b"\xff"[..]);
- let mut v = String::new();
- assert!(c.read_to_string(&mut v).is_err());
- }
-
- #[test]
- fn read_exact() {
- let mut buf = [0; 4];
-
- let mut c = Cursor::new(&b""[..]);
- assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
-
- let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..]));
- c.read_exact(&mut buf).unwrap();
- assert_eq!(&buf, b"1234");
- c.read_exact(&mut buf).unwrap();
- assert_eq!(&buf, b"5678");
- assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
- }
-
- #[test]
- fn read_exact_slice() {
- let mut buf = [0; 4];
-
- let mut c = &b""[..];
- assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
-
- let mut c = &b"123"[..];
- assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
- // make sure the optimized (early returning) method is being used
- assert_eq!(&buf, &[0; 4]);
-
- let mut c = &b"1234"[..];
- c.read_exact(&mut buf).unwrap();
- assert_eq!(&buf, b"1234");
-
- let mut c = &b"56789"[..];
- c.read_exact(&mut buf).unwrap();
- assert_eq!(&buf, b"5678");
- assert_eq!(c, b"9");
- }
-
- #[test]
- fn take_eof() {
- struct R;
-
- impl Read for R {
- fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
- Err(io::Error::new(io::ErrorKind::Other, ""))
- }
- }
- impl BufRead for R {
- fn fill_buf(&mut self) -> io::Result<&[u8]> {
- Err(io::Error::new(io::ErrorKind::Other, ""))
- }
- fn consume(&mut self, _amt: usize) {}
- }
-
- let mut buf = [0; 1];
- assert_eq!(0, R.take(0).read(&mut buf).unwrap());
- assert_eq!(b"", R.take(0).fill_buf().unwrap());
- }
-
- fn cmp_bufread<Br1: BufRead, Br2: BufRead>(mut br1: Br1, mut br2: Br2, exp: &[u8]) {
- let mut cat = Vec::new();
- loop {
- let consume = {
- let buf1 = br1.fill_buf().unwrap();
- let buf2 = br2.fill_buf().unwrap();
- let minlen = if buf1.len() < buf2.len() { buf1.len() } else { buf2.len() };
- assert_eq!(buf1[..minlen], buf2[..minlen]);
- cat.extend_from_slice(&buf1[..minlen]);
- minlen
- };
- if consume == 0 {
- break;
- }
- br1.consume(consume);
- br2.consume(consume);
- }
- assert_eq!(br1.fill_buf().unwrap().len(), 0);
- assert_eq!(br2.fill_buf().unwrap().len(), 0);
- assert_eq!(&cat[..], &exp[..])
- }
-
- #[test]
- fn chain_bufread() {
- let testdata = b"ABCDEFGHIJKL";
- let chain1 =
- (&testdata[..3]).chain(&testdata[3..6]).chain(&testdata[6..9]).chain(&testdata[9..]);
- let chain2 = (&testdata[..4]).chain(&testdata[4..8]).chain(&testdata[8..]);
- cmp_bufread(chain1, chain2, &testdata[..]);
- }
-
- #[test]
- fn chain_zero_length_read_is_not_eof() {
- let a = b"A";
- let b = b"B";
- let mut s = String::new();
- let mut chain = (&a[..]).chain(&b[..]);
- chain.read(&mut []).unwrap();
- chain.read_to_string(&mut s).unwrap();
- assert_eq!("AB", s);
- }
-
- #[bench]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn bench_read_to_end(b: &mut test::Bencher) {
- b.iter(|| {
- let mut lr = repeat(1).take(10000000);
- let mut vec = Vec::with_capacity(1024);
- 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(())
- }
-
- // A simple example reader which uses the default implementation of
- // read_to_end.
- struct ExampleSliceReader<'a> {
- slice: &'a [u8],
- }
-
- impl<'a> Read for ExampleSliceReader<'a> {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- let len = cmp::min(self.slice.len(), buf.len());
- buf[..len].copy_from_slice(&self.slice[..len]);
- self.slice = &self.slice[len..];
- Ok(len)
- }
- }
-
- #[test]
- fn test_read_to_end_capacity() -> io::Result<()> {
- let input = &b"foo"[..];
-
- // read_to_end() generally needs to over-allocate, both for efficiency
- // and so that it can distinguish EOF. Assert that this is the case
- // with this simple ExampleSliceReader struct, which uses the default
- // implementation of read_to_end. Even though vec1 is allocated with
- // exactly enough capacity for the read, read_to_end will allocate more
- // space here.
- let mut vec1 = Vec::with_capacity(input.len());
- ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?;
- assert_eq!(vec1.len(), input.len());
- assert!(vec1.capacity() > input.len(), "allocated more");
-
- // However, std::io::Take includes an implementation of read_to_end
- // that will not allocate when the limit has already been reached. In
- // this case, vec2 never grows.
- let mut vec2 = Vec::with_capacity(input.len());
- ExampleSliceReader { slice: input }.take(input.len() as u64).read_to_end(&mut vec2)?;
- assert_eq!(vec2.len(), input.len());
- assert_eq!(vec2.capacity(), input.len(), "did not allocate more");
-
- 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(bufs, 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(bufs, 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(bufs, 18);
- assert_eq!(bufs[0].deref(), [3; 6].as_ref());
- }
-
- #[test]
- fn io_slice_mut_advance_empty_slice() {
- let empty_bufs = &mut [][..];
- // Shouldn't panic.
- IoSliceMut::advance(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(bufs, 9);
- assert!(bufs.is_empty());
- }
-
- #[test]
- fn io_slice_advance() {
- let buf1 = [1; 8];
- let buf2 = [2; 16];
- let buf3 = [3; 8];
- let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..];
-
- // Only in a single buffer..
- bufs = IoSlice::advance(bufs, 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(bufs, 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(bufs, 18);
- assert_eq!(bufs[0].deref(), [3; 6].as_ref());
- }
-
- #[test]
- fn io_slice_advance_empty_slice() {
- let empty_bufs = &mut [][..];
- // Shouldn't panic.
- IoSlice::advance(empty_bufs, 1);
- }
-
- #[test]
- fn io_slice_advance_beyond_total_length() {
- let buf1 = [1; 8];
- let mut bufs = &mut [IoSlice::new(&buf1)][..];
-
- // Going beyond the total length should be ok.
- bufs = IoSlice::advance(bufs, 9);
- assert!(bufs.is_empty());
- }
-
- /// Create a new writer that reads from at most `n_bufs` and reads
- /// `per_call` bytes (in total) per call to write.
- fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter {
- TestWriter { n_bufs, per_call, written: Vec::new() }
- }
-
- struct TestWriter {
- n_bufs: usize,
- per_call: usize,
- written: Vec<u8>,
- }
-
- impl Write for TestWriter {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- self.write_vectored(&[IoSlice::new(buf)])
- }
-
- fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
- let mut left = self.per_call;
- let mut written = 0;
- for buf in bufs.iter().take(self.n_bufs) {
- let n = min(left, buf.len());
- self.written.extend_from_slice(&buf[0..n]);
- left -= n;
- written += n;
- }
- Ok(written)
- }
-
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
- }
-
- #[test]
- fn test_writer_read_from_one_buf() {
- let mut writer = test_writer(1, 2);
-
- assert_eq!(writer.write(&[]).unwrap(), 0);
- assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
-
- // Read at most 2 bytes.
- assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2);
- let bufs = &[IoSlice::new(&[2, 2, 2])];
- assert_eq!(writer.write_vectored(bufs).unwrap(), 2);
-
- // Only read from first buf.
- let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])];
- assert_eq!(writer.write_vectored(bufs).unwrap(), 1);
-
- assert_eq!(writer.written, &[1, 1, 2, 2, 3]);
- }
-
- #[test]
- fn test_writer_read_from_multiple_bufs() {
- let mut writer = test_writer(3, 3);
-
- // Read at most 3 bytes from two buffers.
- let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])];
- assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
-
- // Read at most 3 bytes from three buffers.
- let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])];
- assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
-
- assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]);
- }
-
- #[test]
- fn test_write_all_vectored() {
- #[rustfmt::skip] // Becomes unreadable otherwise.
- let tests: Vec<(_, &'static [u8])> = vec![
- (vec![], &[]),
- (vec![IoSlice::new(&[]), IoSlice::new(&[])], &[]),
- (vec![IoSlice::new(&[1])], &[1]),
- (vec![IoSlice::new(&[1, 2])], &[1, 2]),
- (vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]),
- (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]),
- (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]),
- (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]),
- (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]),
- (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]),
- (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
- (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
- (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]),
- (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]),
- (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]),
- (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]),
- (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]),
- (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]),
- (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]),
- ];
-
- let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)];
-
- for (n_bufs, per_call) in writer_configs.iter().copied() {
- for (mut input, wanted) in tests.clone().into_iter() {
- let mut writer = test_writer(n_bufs, per_call);
- assert!(writer.write_all_vectored(&mut *input).is_ok());
- assert_eq!(&*writer.written, &*wanted);
- }
- }
- }
-}
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index 3943c66..b68e538 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -1,30 +1,50 @@
#![cfg_attr(test, allow(unused))]
+#[cfg(test)]
+mod tests;
+
use crate::io::prelude::*;
use crate::cell::RefCell;
use crate::fmt;
-use crate::io::lazy::Lazy;
use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter};
-use crate::sync::{Arc, Mutex, MutexGuard, Once};
+use crate::lazy::SyncOnceCell;
+use crate::pin::Pin;
+use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sync::{Mutex, MutexGuard};
use crate::sys::stdio;
+use crate::sys_common;
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
use crate::thread::LocalKey;
thread_local! {
- /// Stdout used by print! and println! macros
+ /// Used by the test crate to capture the output of the print! and println! macros.
static LOCAL_STDOUT: RefCell<Option<Box<dyn Write + Send>>> = {
RefCell::new(None)
}
}
thread_local! {
- /// Stderr used by eprint! and eprintln! macros, and panics
+ /// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics.
static LOCAL_STDERR: RefCell<Option<Box<dyn Write + Send>>> = {
RefCell::new(None)
}
}
+/// Flag to indicate LOCAL_STDOUT and/or LOCAL_STDERR is used.
+///
+/// If both are None and were never set on any thread, this flag is set to
+/// false, and both LOCAL_STDOUT and LOCAL_STDOUT can be safely ignored on all
+/// threads, saving some time and memory registering an unused thread local.
+///
+/// Note about memory ordering: This contains information about whether two
+/// thread local variables might be in use. Although this is a global flag, the
+/// memory ordering between threads does not matter: we only want this flag to
+/// have a consistent order between set_print/set_panic and print_to *within
+/// the same thread*. Within the same thread, things always have a perfectly
+/// consistent order. So Ordering::Relaxed is fine.
+static LOCAL_STREAMS: AtomicBool = AtomicBool::new(false);
+
/// A handle to a raw instance of the standard input stream of this process.
///
/// This handle is not synchronized or buffered in any fashion. Constructed via
@@ -214,7 +234,7 @@
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Stdin {
- inner: Arc<Mutex<BufReader<StdinRaw>>>,
+ inner: &'static Mutex<BufReader<StdinRaw>>,
}
/// A locked reference to the `Stdin` handle.
@@ -289,15 +309,11 @@
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stdin() -> Stdin {
- static INSTANCE: Lazy<Mutex<BufReader<StdinRaw>>> = Lazy::new();
- return Stdin {
- inner: unsafe { INSTANCE.get(stdin_init).expect("cannot access stdin during shutdown") },
- };
-
- fn stdin_init() -> Arc<Mutex<BufReader<StdinRaw>>> {
- // This must not reentrantly access `INSTANCE`
- let stdin = stdin_raw();
- Arc::new(Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin)))
+ static INSTANCE: SyncOnceCell<Mutex<BufReader<StdinRaw>>> = SyncOnceCell::new();
+ Stdin {
+ inner: INSTANCE.get_or_init(|| {
+ Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin_raw()))
+ }),
}
}
@@ -473,7 +489,7 @@
// FIXME: this should be LineWriter or BufWriter depending on the state of
// stdout (tty or not). Note that if this is not line buffered it
// should also flush-on-panic or some form of flush-on-abort.
- inner: Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,
+ inner: Pin<&'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,
}
/// A locked reference to the `Stdout` handle.
@@ -531,20 +547,32 @@
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stdout() -> Stdout {
- static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = Lazy::new();
- return Stdout {
- inner: unsafe { INSTANCE.get(stdout_init).expect("cannot access stdout during shutdown") },
- };
+ static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
+ SyncOnceCell::new();
- fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> {
- // This must not reentrantly access `INSTANCE`
- let stdout = stdout_raw();
- unsafe {
- let ret = Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout))));
- ret.init();
- ret
+ fn cleanup() {
+ if let Some(instance) = INSTANCE.get() {
+ // Flush the data and disable buffering during shutdown
+ // by replacing the line writer by one with zero
+ // buffering capacity.
+ // We use try_lock() instead of lock(), because someone
+ // might have leaked a StdoutLock, which would
+ // otherwise cause a deadlock here.
+ if let Some(lock) = Pin::static_ref(instance).try_lock() {
+ *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
+ }
}
}
+
+ Stdout {
+ inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
+ || unsafe {
+ let _ = sys_common::at_exit(cleanup);
+ ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))
+ },
+ |mutex| unsafe { mutex.init() },
+ ),
+ }
}
impl Stdout {
@@ -584,6 +612,32 @@
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ (&*self).write(buf)
+ }
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ (&*self).write_vectored(bufs)
+ }
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ io::Write::is_write_vectored(&&*self)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ (&*self).flush()
+ }
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ (&*self).write_all(buf)
+ }
+ fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+ (&*self).write_all_vectored(bufs)
+ }
+ fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
+ (&*self).write_fmt(args)
+ }
+}
+
+#[stable(feature = "write_mt", since = "1.48.0")]
+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> {
@@ -606,6 +660,7 @@
self.lock().write_fmt(args)
}
}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for StdoutLock<'_> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@@ -648,7 +703,7 @@
/// an error.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Stderr {
- inner: &'static ReentrantMutex<RefCell<StderrRaw>>,
+ inner: Pin<&'static ReentrantMutex<RefCell<StderrRaw>>>,
}
/// A locked reference to the `Stderr` handle.
@@ -704,23 +759,17 @@
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stderr() -> Stderr {
- // Note that unlike `stdout()` we don't use `Lazy` here which registers a
- // destructor. Stderr is not buffered nor does the `stderr_raw` type consume
- // any owned resources, so there's no need to run any destructors at some
- // point in the future.
- //
- // This has the added benefit of allowing `stderr` to be usable during
- // process shutdown as well!
- static INSTANCE: ReentrantMutex<RefCell<StderrRaw>> =
- unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) };
+ // Note that unlike `stdout()` we don't use `at_exit` here to register a
+ // destructor. Stderr is not buffered , so there's no need to run a
+ // destructor for flushing the buffer
+ static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<StderrRaw>>> = SyncOnceCell::new();
- // When accessing stderr we need one-time initialization of the reentrant
- // mutex. Afterwards we can just always use the now-filled-in `INSTANCE` value.
- static INIT: Once = Once::new();
- INIT.call_once(|| unsafe {
- INSTANCE.init();
- });
- Stderr { inner: &INSTANCE }
+ Stderr {
+ inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
+ || unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) },
+ |mutex| unsafe { mutex.init() },
+ ),
+ }
}
impl Stderr {
@@ -760,6 +809,32 @@
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ (&*self).write(buf)
+ }
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ (&*self).write_vectored(bufs)
+ }
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ io::Write::is_write_vectored(&&*self)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ (&*self).flush()
+ }
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ (&*self).write_all(buf)
+ }
+ fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+ (&*self).write_all_vectored(bufs)
+ }
+ fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
+ (&*self).write_fmt(args)
+ }
+}
+
+#[stable(feature = "write_mt", since = "1.48.0")]
+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> {
@@ -782,6 +857,7 @@
self.lock().write_fmt(args)
}
}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for StderrLock<'_> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@@ -829,10 +905,18 @@
#[doc(hidden)]
pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
use crate::mem;
- LOCAL_STDERR.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(|mut s| {
- let _ = s.flush();
- Some(s)
- })
+ if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
+ // LOCAL_STDERR is definitely None since LOCAL_STREAMS is false.
+ return None;
+ }
+ let s = LOCAL_STDERR.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(
+ |mut s| {
+ let _ = s.flush();
+ Some(s)
+ },
+ );
+ LOCAL_STREAMS.store(true, Ordering::Relaxed);
+ s
}
/// Resets the thread-local stdout handle to the specified writer
@@ -852,10 +936,18 @@
#[doc(hidden)]
pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
use crate::mem;
- LOCAL_STDOUT.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(|mut s| {
- let _ = s.flush();
- Some(s)
- })
+ if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
+ // LOCAL_STDOUT is definitely None since LOCAL_STREAMS is false.
+ return None;
+ }
+ let s = LOCAL_STDOUT.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(
+ |mut s| {
+ let _ = s.flush();
+ Some(s)
+ },
+ );
+ LOCAL_STREAMS.store(true, Ordering::Relaxed);
+ s
}
/// Write `args` to output stream `local_s` if possible, `global_s`
@@ -876,20 +968,26 @@
) where
T: Write,
{
- let result = local_s
- .try_with(|s| {
- // Note that we completely remove a local sink to write to in case
- // our printing recursively panics/prints, so the recursive
- // panic/print goes to the global sink instead of our local sink.
- let prev = s.borrow_mut().take();
- if let Some(mut w) = prev {
- let result = w.write_fmt(args);
- *s.borrow_mut() = Some(w);
- return result;
- }
- global_s().write_fmt(args)
+ let result = LOCAL_STREAMS
+ .load(Ordering::Relaxed)
+ .then(|| {
+ local_s
+ .try_with(|s| {
+ // Note that we completely remove a local sink to write to in case
+ // our printing recursively panics/prints, so the recursive
+ // panic/print goes to the global sink instead of our local sink.
+ let prev = s.borrow_mut().take();
+ if let Some(mut w) = prev {
+ let result = w.write_fmt(args);
+ *s.borrow_mut() = Some(w);
+ return result;
+ }
+ global_s().write_fmt(args)
+ })
+ .ok()
})
- .unwrap_or_else(|_| global_s().write_fmt(args));
+ .flatten()
+ .unwrap_or_else(|| global_s().write_fmt(args));
if let Err(e) = result {
panic!("failed printing to {}: {}", label, e);
@@ -920,54 +1018,3 @@
#[cfg(test)]
pub use realstd::io::{_eprint, _print};
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::panic::{RefUnwindSafe, UnwindSafe};
- use crate::thread;
-
- #[test]
- fn stdout_unwind_safe() {
- assert_unwind_safe::<Stdout>();
- }
- #[test]
- fn stdoutlock_unwind_safe() {
- assert_unwind_safe::<StdoutLock<'_>>();
- assert_unwind_safe::<StdoutLock<'static>>();
- }
- #[test]
- fn stderr_unwind_safe() {
- assert_unwind_safe::<Stderr>();
- }
- #[test]
- fn stderrlock_unwind_safe() {
- assert_unwind_safe::<StderrLock<'_>>();
- assert_unwind_safe::<StderrLock<'static>>();
- }
-
- fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn panic_doesnt_poison() {
- thread::spawn(|| {
- let _a = stdin();
- let _a = _a.lock();
- let _a = stdout();
- let _a = _a.lock();
- let _a = stderr();
- let _a = _a.lock();
- panic!();
- })
- .join()
- .unwrap_err();
-
- let _a = stdin();
- let _a = _a.lock();
- let _a = stdout();
- let _a = _a.lock();
- let _a = stderr();
- let _a = _a.lock();
- }
-}
diff --git a/library/std/src/io/stdio/tests.rs b/library/std/src/io/stdio/tests.rs
new file mode 100644
index 0000000..04af500
--- /dev/null
+++ b/library/std/src/io/stdio/tests.rs
@@ -0,0 +1,47 @@
+use super::*;
+use crate::panic::{RefUnwindSafe, UnwindSafe};
+use crate::thread;
+
+#[test]
+fn stdout_unwind_safe() {
+ assert_unwind_safe::<Stdout>();
+}
+#[test]
+fn stdoutlock_unwind_safe() {
+ assert_unwind_safe::<StdoutLock<'_>>();
+ assert_unwind_safe::<StdoutLock<'static>>();
+}
+#[test]
+fn stderr_unwind_safe() {
+ assert_unwind_safe::<Stderr>();
+}
+#[test]
+fn stderrlock_unwind_safe() {
+ assert_unwind_safe::<StderrLock<'_>>();
+ assert_unwind_safe::<StderrLock<'static>>();
+}
+
+fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn panic_doesnt_poison() {
+ thread::spawn(|| {
+ let _a = stdin();
+ let _a = _a.lock();
+ let _a = stdout();
+ let _a = _a.lock();
+ let _a = stderr();
+ let _a = _a.lock();
+ panic!();
+ })
+ .join()
+ .unwrap_err();
+
+ let _a = stdin();
+ let _a = _a.lock();
+ let _a = stdout();
+ let _a = _a.lock();
+ let _a = stderr();
+ let _a = _a.lock();
+}
diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs
new file mode 100644
index 0000000..913b285
--- /dev/null
+++ b/library/std/src/io/tests.rs
@@ -0,0 +1,494 @@
+use super::{repeat, Cursor, SeekFrom};
+use crate::cmp::{self, min};
+use crate::io::prelude::*;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::ops::Deref;
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn read_until() {
+ let mut buf = Cursor::new(&b"12"[..]);
+ let mut v = Vec::new();
+ assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2);
+ assert_eq!(v, b"12");
+
+ let mut buf = Cursor::new(&b"1233"[..]);
+ let mut v = Vec::new();
+ assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3);
+ assert_eq!(v, b"123");
+ v.truncate(0);
+ assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1);
+ assert_eq!(v, b"3");
+ v.truncate(0);
+ assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0);
+ assert_eq!(v, []);
+}
+
+#[test]
+fn split() {
+ let buf = Cursor::new(&b"12"[..]);
+ let mut s = buf.split(b'3');
+ assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
+ assert!(s.next().is_none());
+
+ let buf = Cursor::new(&b"1233"[..]);
+ let mut s = buf.split(b'3');
+ assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
+ assert_eq!(s.next().unwrap().unwrap(), vec![]);
+ assert!(s.next().is_none());
+}
+
+#[test]
+fn read_line() {
+ let mut buf = Cursor::new(&b"12"[..]);
+ let mut v = String::new();
+ assert_eq!(buf.read_line(&mut v).unwrap(), 2);
+ assert_eq!(v, "12");
+
+ let mut buf = Cursor::new(&b"12\n\n"[..]);
+ let mut v = String::new();
+ assert_eq!(buf.read_line(&mut v).unwrap(), 3);
+ assert_eq!(v, "12\n");
+ v.truncate(0);
+ assert_eq!(buf.read_line(&mut v).unwrap(), 1);
+ assert_eq!(v, "\n");
+ v.truncate(0);
+ assert_eq!(buf.read_line(&mut v).unwrap(), 0);
+ assert_eq!(v, "");
+}
+
+#[test]
+fn lines() {
+ let buf = Cursor::new(&b"12\r"[..]);
+ let mut s = buf.lines();
+ assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string());
+ assert!(s.next().is_none());
+
+ let buf = Cursor::new(&b"12\r\n\n"[..]);
+ let mut s = buf.lines();
+ assert_eq!(s.next().unwrap().unwrap(), "12".to_string());
+ assert_eq!(s.next().unwrap().unwrap(), "".to_string());
+ assert!(s.next().is_none());
+}
+
+#[test]
+fn read_to_end() {
+ let mut c = Cursor::new(&b""[..]);
+ let mut v = Vec::new();
+ assert_eq!(c.read_to_end(&mut v).unwrap(), 0);
+ assert_eq!(v, []);
+
+ let mut c = Cursor::new(&b"1"[..]);
+ let mut v = Vec::new();
+ assert_eq!(c.read_to_end(&mut v).unwrap(), 1);
+ assert_eq!(v, b"1");
+
+ let cap = 1024 * 1024;
+ let data = (0..cap).map(|i| (i / 3) as u8).collect::<Vec<_>>();
+ let mut v = Vec::new();
+ let (a, b) = data.split_at(data.len() / 2);
+ assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len());
+ assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len());
+ assert_eq!(v, data);
+}
+
+#[test]
+fn read_to_string() {
+ let mut c = Cursor::new(&b""[..]);
+ let mut v = String::new();
+ assert_eq!(c.read_to_string(&mut v).unwrap(), 0);
+ assert_eq!(v, "");
+
+ let mut c = Cursor::new(&b"1"[..]);
+ let mut v = String::new();
+ assert_eq!(c.read_to_string(&mut v).unwrap(), 1);
+ assert_eq!(v, "1");
+
+ let mut c = Cursor::new(&b"\xff"[..]);
+ let mut v = String::new();
+ assert!(c.read_to_string(&mut v).is_err());
+}
+
+#[test]
+fn read_exact() {
+ let mut buf = [0; 4];
+
+ let mut c = Cursor::new(&b""[..]);
+ assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
+
+ let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..]));
+ c.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b"1234");
+ c.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b"5678");
+ assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
+}
+
+#[test]
+fn read_exact_slice() {
+ let mut buf = [0; 4];
+
+ let mut c = &b""[..];
+ assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
+
+ let mut c = &b"123"[..];
+ assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
+ // make sure the optimized (early returning) method is being used
+ assert_eq!(&buf, &[0; 4]);
+
+ let mut c = &b"1234"[..];
+ c.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b"1234");
+
+ let mut c = &b"56789"[..];
+ c.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b"5678");
+ assert_eq!(c, b"9");
+}
+
+#[test]
+fn take_eof() {
+ struct R;
+
+ impl Read for R {
+ fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
+ Err(io::Error::new(io::ErrorKind::Other, ""))
+ }
+ }
+ impl BufRead for R {
+ fn fill_buf(&mut self) -> io::Result<&[u8]> {
+ Err(io::Error::new(io::ErrorKind::Other, ""))
+ }
+ fn consume(&mut self, _amt: usize) {}
+ }
+
+ let mut buf = [0; 1];
+ assert_eq!(0, R.take(0).read(&mut buf).unwrap());
+ assert_eq!(b"", R.take(0).fill_buf().unwrap());
+}
+
+fn cmp_bufread<Br1: BufRead, Br2: BufRead>(mut br1: Br1, mut br2: Br2, exp: &[u8]) {
+ let mut cat = Vec::new();
+ loop {
+ let consume = {
+ let buf1 = br1.fill_buf().unwrap();
+ let buf2 = br2.fill_buf().unwrap();
+ let minlen = if buf1.len() < buf2.len() { buf1.len() } else { buf2.len() };
+ assert_eq!(buf1[..minlen], buf2[..minlen]);
+ cat.extend_from_slice(&buf1[..minlen]);
+ minlen
+ };
+ if consume == 0 {
+ break;
+ }
+ br1.consume(consume);
+ br2.consume(consume);
+ }
+ assert_eq!(br1.fill_buf().unwrap().len(), 0);
+ assert_eq!(br2.fill_buf().unwrap().len(), 0);
+ assert_eq!(&cat[..], &exp[..])
+}
+
+#[test]
+fn chain_bufread() {
+ let testdata = b"ABCDEFGHIJKL";
+ let chain1 =
+ (&testdata[..3]).chain(&testdata[3..6]).chain(&testdata[6..9]).chain(&testdata[9..]);
+ let chain2 = (&testdata[..4]).chain(&testdata[4..8]).chain(&testdata[8..]);
+ cmp_bufread(chain1, chain2, &testdata[..]);
+}
+
+#[test]
+fn chain_zero_length_read_is_not_eof() {
+ let a = b"A";
+ let b = b"B";
+ let mut s = String::new();
+ let mut chain = (&a[..]).chain(&b[..]);
+ chain.read(&mut []).unwrap();
+ chain.read_to_string(&mut s).unwrap();
+ assert_eq!("AB", s);
+}
+
+#[bench]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn bench_read_to_end(b: &mut test::Bencher) {
+ b.iter(|| {
+ let mut lr = repeat(1).take(10000000);
+ let mut vec = Vec::with_capacity(1024);
+ 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(())
+}
+
+// A simple example reader which uses the default implementation of
+// read_to_end.
+struct ExampleSliceReader<'a> {
+ slice: &'a [u8],
+}
+
+impl<'a> Read for ExampleSliceReader<'a> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let len = cmp::min(self.slice.len(), buf.len());
+ buf[..len].copy_from_slice(&self.slice[..len]);
+ self.slice = &self.slice[len..];
+ Ok(len)
+ }
+}
+
+#[test]
+fn test_read_to_end_capacity() -> io::Result<()> {
+ let input = &b"foo"[..];
+
+ // read_to_end() generally needs to over-allocate, both for efficiency
+ // and so that it can distinguish EOF. Assert that this is the case
+ // with this simple ExampleSliceReader struct, which uses the default
+ // implementation of read_to_end. Even though vec1 is allocated with
+ // exactly enough capacity for the read, read_to_end will allocate more
+ // space here.
+ let mut vec1 = Vec::with_capacity(input.len());
+ ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?;
+ assert_eq!(vec1.len(), input.len());
+ assert!(vec1.capacity() > input.len(), "allocated more");
+
+ // However, std::io::Take includes an implementation of read_to_end
+ // that will not allocate when the limit has already been reached. In
+ // this case, vec2 never grows.
+ let mut vec2 = Vec::with_capacity(input.len());
+ ExampleSliceReader { slice: input }.take(input.len() as u64).read_to_end(&mut vec2)?;
+ assert_eq!(vec2.len(), input.len());
+ assert_eq!(vec2.capacity(), input.len(), "did not allocate more");
+
+ 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(bufs, 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(bufs, 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(bufs, 18);
+ assert_eq!(bufs[0].deref(), [3; 6].as_ref());
+}
+
+#[test]
+fn io_slice_mut_advance_empty_slice() {
+ let empty_bufs = &mut [][..];
+ // Shouldn't panic.
+ IoSliceMut::advance(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(bufs, 9);
+ assert!(bufs.is_empty());
+}
+
+#[test]
+fn io_slice_advance() {
+ let buf1 = [1; 8];
+ let buf2 = [2; 16];
+ let buf3 = [3; 8];
+ let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..];
+
+ // Only in a single buffer..
+ bufs = IoSlice::advance(bufs, 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(bufs, 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(bufs, 18);
+ assert_eq!(bufs[0].deref(), [3; 6].as_ref());
+}
+
+#[test]
+fn io_slice_advance_empty_slice() {
+ let empty_bufs = &mut [][..];
+ // Shouldn't panic.
+ IoSlice::advance(empty_bufs, 1);
+}
+
+#[test]
+fn io_slice_advance_beyond_total_length() {
+ let buf1 = [1; 8];
+ let mut bufs = &mut [IoSlice::new(&buf1)][..];
+
+ // Going beyond the total length should be ok.
+ bufs = IoSlice::advance(bufs, 9);
+ assert!(bufs.is_empty());
+}
+
+/// Create a new writer that reads from at most `n_bufs` and reads
+/// `per_call` bytes (in total) per call to write.
+fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter {
+ TestWriter { n_bufs, per_call, written: Vec::new() }
+}
+
+struct TestWriter {
+ n_bufs: usize,
+ per_call: usize,
+ written: Vec<u8>,
+}
+
+impl Write for TestWriter {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(buf)])
+ }
+
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ let mut left = self.per_call;
+ let mut written = 0;
+ for buf in bufs.iter().take(self.n_bufs) {
+ let n = min(left, buf.len());
+ self.written.extend_from_slice(&buf[0..n]);
+ left -= n;
+ written += n;
+ }
+ Ok(written)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+#[test]
+fn test_writer_read_from_one_buf() {
+ let mut writer = test_writer(1, 2);
+
+ assert_eq!(writer.write(&[]).unwrap(), 0);
+ assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
+
+ // Read at most 2 bytes.
+ assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2);
+ let bufs = &[IoSlice::new(&[2, 2, 2])];
+ assert_eq!(writer.write_vectored(bufs).unwrap(), 2);
+
+ // Only read from first buf.
+ let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])];
+ assert_eq!(writer.write_vectored(bufs).unwrap(), 1);
+
+ assert_eq!(writer.written, &[1, 1, 2, 2, 3]);
+}
+
+#[test]
+fn test_writer_read_from_multiple_bufs() {
+ let mut writer = test_writer(3, 3);
+
+ // Read at most 3 bytes from two buffers.
+ let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])];
+ assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
+
+ // Read at most 3 bytes from three buffers.
+ let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])];
+ assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
+
+ assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]);
+}
+
+#[test]
+fn test_write_all_vectored() {
+ #[rustfmt::skip] // Becomes unreadable otherwise.
+ let tests: Vec<(_, &'static [u8])> = vec![
+ (vec![], &[]),
+ (vec![IoSlice::new(&[]), IoSlice::new(&[])], &[]),
+ (vec![IoSlice::new(&[1])], &[1]),
+ (vec![IoSlice::new(&[1, 2])], &[1, 2]),
+ (vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]),
+ (vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]),
+ (vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]),
+ (vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]),
+ (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]),
+ (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]),
+ (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
+ (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
+ (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]),
+ (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]),
+ (vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]),
+ (vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]),
+ (vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]),
+ (vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]),
+ (vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]),
+ ];
+
+ let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)];
+
+ for (n_bufs, per_call) in writer_configs.iter().copied() {
+ for (mut input, wanted) in tests.clone().into_iter() {
+ let mut writer = test_writer(n_bufs, per_call);
+ assert!(writer.write_all_vectored(&mut *input).is_ok());
+ assert_eq!(&*writer.written, &*wanted);
+ }
+ }
+}
diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs
index a093b74..dc05b964 100644
--- a/library/std/src/io/util.rs
+++ b/library/std/src/io/util.rs
@@ -1,5 +1,8 @@
#![allow(missing_copy_implementations)]
+#[cfg(test)]
+mod tests;
+
use crate::fmt;
use crate::io::{self, BufRead, ErrorKind, Initializer, IoSlice, IoSliceMut, Read, Write};
use crate::mem::MaybeUninit;
@@ -49,24 +52,27 @@
W: Write,
{
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.
+ // FIXME: #42788
+ //
+ // - This creates a (mut) reference to a slice of
+ // _uninitialized_ integers, which is **undefined behavior**
+ //
+ // - Only the standard library gets to soundly "ignore" this,
+ // based on its privileged knowledge of unstable rustc
+ // internals;
unsafe {
- reader.initializer().initialize(buf.get_mut());
+ reader.initializer().initialize(buf.assume_init_mut());
}
let mut written = 0;
loop {
- let len = match reader.read(unsafe { buf.get_mut() }) {
+ let len = match reader.read(unsafe { buf.assume_init_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(unsafe { &buf.get_ref()[..len] })?;
+ writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?;
written += len as u64;
}
}
@@ -248,58 +254,33 @@
}
}
+#[stable(feature = "write_mt", since = "1.48.0")]
+impl Write for &Sink {
+ #[inline]
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ Ok(buf.len())
+ }
+
+ #[inline]
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ let total_len = bufs.iter().map(|b| b.len()).sum();
+ Ok(total_len)
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ #[inline]
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for Sink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("Sink { .. }")
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::io::prelude::*;
- use crate::io::{copy, empty, repeat, sink};
-
- #[test]
- fn copy_copies() {
- let mut r = repeat(0).take(4);
- let mut w = sink();
- assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
-
- let mut r = repeat(0).take(1 << 17);
- assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
- }
-
- #[test]
- fn sink_sinks() {
- let mut s = sink();
- assert_eq!(s.write(&[]).unwrap(), 0);
- assert_eq!(s.write(&[0]).unwrap(), 1);
- assert_eq!(s.write(&[0; 1024]).unwrap(), 1024);
- assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024);
- }
-
- #[test]
- fn empty_reads() {
- let mut e = empty();
- assert_eq!(e.read(&mut []).unwrap(), 0);
- assert_eq!(e.read(&mut [0]).unwrap(), 0);
- assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
- assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0);
- }
-
- #[test]
- fn repeat_repeats() {
- let mut r = repeat(4);
- let mut b = [0; 1024];
- assert_eq!(r.read(&mut b).unwrap(), 1024);
- assert!(b.iter().all(|b| *b == 4));
- }
-
- #[test]
- fn take_some_bytes() {
- assert_eq!(repeat(4).take(100).bytes().count(), 100);
- assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4);
- assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
- }
-}
diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs
new file mode 100644
index 0000000..e5e32ec
--- /dev/null
+++ b/library/std/src/io/util/tests.rs
@@ -0,0 +1,45 @@
+use crate::io::prelude::*;
+use crate::io::{copy, empty, repeat, sink};
+
+#[test]
+fn copy_copies() {
+ let mut r = repeat(0).take(4);
+ let mut w = sink();
+ assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
+
+ let mut r = repeat(0).take(1 << 17);
+ assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
+}
+
+#[test]
+fn sink_sinks() {
+ let mut s = sink();
+ assert_eq!(s.write(&[]).unwrap(), 0);
+ assert_eq!(s.write(&[0]).unwrap(), 1);
+ assert_eq!(s.write(&[0; 1024]).unwrap(), 1024);
+ assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024);
+}
+
+#[test]
+fn empty_reads() {
+ let mut e = empty();
+ assert_eq!(e.read(&mut []).unwrap(), 0);
+ assert_eq!(e.read(&mut [0]).unwrap(), 0);
+ assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
+ assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0);
+}
+
+#[test]
+fn repeat_repeats() {
+ let mut r = repeat(4);
+ let mut b = [0; 1024];
+ assert_eq!(r.read(&mut b).unwrap(), 1024);
+ assert!(b.iter().all(|b| *b == 4));
+}
+
+#[test]
+fn take_some_bytes() {
+ assert_eq!(repeat(4).take(100).bytes().count(), 100);
+ assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4);
+ assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
+}
diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs
index af25c39..54ce0e7 100644
--- a/library/std/src/keyword_docs.rs
+++ b/library/std/src/keyword_docs.rs
@@ -107,7 +107,7 @@
/// 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
/// make it a variable that gets carried around to each function that needs it. In these cases, the
-/// `const` keyword provides a convenient alternative to code duplication.
+/// `const` keyword provides a convenient alternative to code duplication:
///
/// ```rust
/// const THING: u32 = 0xABAD1DEA;
@@ -115,10 +115,12 @@
/// let foo = 123 + THING;
/// ```
///
-/// Constants must be explicitly typed, unlike with `let` you can't ignore its type and let the
-/// compiler figure it out. Any constant value can be defined in a const, which in practice happens
-/// to be most things that would be reasonable to have a constant (barring `const fn`s). For
-/// example, you can't have a File as a `const`.
+/// Constants must be explicitly typed; unlike with `let`, you can't ignore their type and let the
+/// compiler figure it out. Any constant value can be defined in a `const`, which in practice happens
+/// to be most things that would be reasonable to have in a constant (barring `const fn`s). For
+/// example, you can't have a [`File`] as a `const`.
+///
+/// [`File`]: crate::fs::File
///
/// The only lifetime allowed in a constant is `'static`, which is the lifetime that encompasses
/// all others in a Rust program. For example, if you wanted to define a constant string, it would
@@ -128,7 +130,7 @@
/// const WORDS: &'static str = "hello rust!";
/// ```
///
-/// Thanks to static lifetime elision, you usually don't have to explicitly use 'static:
+/// Thanks to static lifetime elision, you usually don't have to explicitly use `'static`:
///
/// ```rust
/// const WORDS: &str = "hello convenience!";
@@ -136,19 +138,19 @@
///
/// `const` items looks remarkably similar to `static` items, which introduces some confusion as
/// to which one should be used at which times. To put it simply, constants are inlined wherever
-/// they're used, making using them identical to simply replacing the name of the const with its
-/// value. Static variables on the other hand point to a single location in memory, which all
+/// they're used, making using them identical to simply replacing the name of the `const` with its
+/// value. Static variables, on the other hand, point to a single location in memory, which all
/// accesses share. This means that, unlike with constants, they can't have destructors, and act as
/// a single value across the entire codebase.
///
-/// Constants, as with statics, should always be in SCREAMING_SNAKE_CASE.
+/// Constants, like statics, should always be in `SCREAMING_SNAKE_CASE`.
///
/// The `const` keyword is also used in raw pointers in combination with `mut`, as seen in `*const
-/// T` and `*mut T`. More about that can be read at the [pointer] primitive part of the Rust docs.
+/// T` and `*mut T`. More about `const` as used in raw pointers can be read at the Rust docs for the [pointer primitive].
///
-/// For more detail on `const`, see the [Rust Book] or the [Reference]
+/// For more detail on `const`, see the [Rust Book] or the [Reference].
///
-/// [pointer]: primitive.pointer.html
+/// [pointer primitive]: primitive.pointer.html
/// [Rust Book]:
/// ../book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants
/// [Reference]: ../reference/items/constant-items.html
diff --git a/library/std/src/lazy.rs b/library/std/src/lazy.rs
index f054858..68f5795 100644
--- a/library/std/src/lazy.rs
+++ b/library/std/src/lazy.rs
@@ -1,11 +1,16 @@
//! Lazy values and one-time initialization of static data.
+#[cfg(test)]
+mod tests;
+
use crate::{
cell::{Cell, UnsafeCell},
fmt,
- mem::{self, MaybeUninit},
+ marker::PhantomData,
+ mem::MaybeUninit,
ops::{Deref, Drop},
panic::{RefUnwindSafe, UnwindSafe},
+ pin::Pin,
sync::Once,
};
@@ -43,6 +48,26 @@
once: Once,
// Whether or not the value is initialized is tracked by `state_and_queue`.
value: UnsafeCell<MaybeUninit<T>>,
+ /// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl.
+ ///
+ /// ```compile_fail,E0597
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::SyncOnceCell;
+ ///
+ /// struct A<'a>(&'a str);
+ ///
+ /// impl<'a> Drop for A<'a> {
+ /// fn drop(&mut self) {}
+ /// }
+ ///
+ /// let cell = SyncOnceCell::new();
+ /// {
+ /// let s = String::new();
+ /// let _ = cell.set(A(&s));
+ /// }
+ /// ```
+ _marker: PhantomData<T>,
}
// Why do we need `T: Send`?
@@ -116,7 +141,11 @@
/// Creates a new empty cell.
#[unstable(feature = "once_cell", issue = "74465")]
pub const fn new() -> SyncOnceCell<T> {
- SyncOnceCell { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()) }
+ SyncOnceCell {
+ once: Once::new(),
+ value: UnsafeCell::new(MaybeUninit::uninit()),
+ _marker: PhantomData,
+ }
}
/// Gets the reference to the underlying value.
@@ -265,10 +294,64 @@
debug_assert!(self.is_initialized());
- // Safety: The inner value has been initialized
+ // SAFETY: The inner value has been initialized
Ok(unsafe { self.get_unchecked() })
}
+ /// Internal-only API that gets the contents of the cell, initializing it
+ /// in two steps with `f` and `g` if the cell was empty.
+ ///
+ /// `f` is called to construct the value, which is then moved into the cell
+ /// and given as a (pinned) mutable reference to `g` to finish
+ /// initialization.
+ ///
+ /// This allows `g` to inspect an manipulate the value after it has been
+ /// moved into its final place in the cell, but before the cell is
+ /// considered initialized.
+ ///
+ /// # Panics
+ ///
+ /// If `f` or `g` panics, the panic is propagated to the caller, and the
+ /// cell remains uninitialized.
+ ///
+ /// With the current implementation, if `g` panics, the value from `f` will
+ /// not be dropped. This should probably be fixed if this is ever used for
+ /// a type where this matters.
+ ///
+ /// It is an error to reentrantly initialize the cell from `f`. The exact
+ /// outcome is unspecified. Current implementation deadlocks, but this may
+ /// be changed to a panic in the future.
+ pub(crate) fn get_or_init_pin<F, G>(self: Pin<&Self>, f: F, g: G) -> Pin<&T>
+ where
+ F: FnOnce() -> T,
+ G: FnOnce(Pin<&mut T>),
+ {
+ if let Some(value) = self.get_ref().get() {
+ // SAFETY: The inner value was already initialized, and will not be
+ // moved anymore.
+ return unsafe { Pin::new_unchecked(value) };
+ }
+
+ let slot = &self.value;
+
+ // Ignore poisoning from other threads
+ // If another thread panics, then we'll be able to run our closure
+ self.once.call_once_force(|_| {
+ let value = f();
+ // SAFETY: We use the Once (self.once) to guarantee unique access
+ // to the UnsafeCell (slot).
+ let value: &mut T = unsafe { (&mut *slot.get()).write(value) };
+ // SAFETY: The value has been written to its final place in
+ // self.value. We do not to move it anymore, which we promise here
+ // with a Pin<&mut T>.
+ g(unsafe { Pin::new_unchecked(value) });
+ });
+
+ // SAFETY: The inner value has been initialized, and will not be moved
+ // anymore.
+ unsafe { Pin::new_unchecked(self.get_ref().get_unchecked()) }
+ }
+
/// Consumes the `SyncOnceCell`, returning the wrapped value. Returns
/// `None` if the cell was empty.
///
@@ -288,13 +371,7 @@
/// ```
#[unstable(feature = "once_cell", issue = "74465")]
pub fn into_inner(mut self) -> Option<T> {
- // Safety: Safe because we immediately free `self` without dropping
- let inner = unsafe { self.take_inner() };
-
- // Don't drop this `SyncOnceCell`. We just moved out one of the fields, but didn't set
- // the state to uninitialized.
- mem::ManuallyDrop::new(self);
- inner
+ self.take()
}
/// Takes the value out of this `SyncOnceCell`, moving it back to an uninitialized state.
@@ -320,22 +397,12 @@
/// ```
#[unstable(feature = "once_cell", issue = "74465")]
pub fn take(&mut self) -> Option<T> {
- mem::take(self).into_inner()
- }
-
- /// Takes the wrapped value out of a `SyncOnceCell`.
- /// Afterwards the cell is no longer initialized.
- ///
- /// Safety: The cell must now be free'd WITHOUT dropping. No other usages of the cell
- /// are valid. Only used by `into_inner` and `drop`.
- unsafe fn take_inner(&mut self) -> Option<T> {
- // The mutable reference guarantees there are no other threads that can observe us
- // taking out the wrapped value.
- // Right after this function `self` is supposed to be freed, so it makes little sense
- // to atomically set the state to uninitialized.
if self.is_initialized() {
- let value = mem::replace(&mut self.value, UnsafeCell::new(MaybeUninit::uninit()));
- Some(value.into_inner().assume_init())
+ self.once = Once::new();
+ // SAFETY: `self.value` is initialized and contains a valid `T`.
+ // `self.once` is reset, so `is_initialized()` will be false again
+ // which prevents the value from being read twice.
+ unsafe { Some((&mut *self.value.get()).assume_init_read()) }
} else {
None
}
@@ -376,21 +443,24 @@
/// Safety: The value must be initialized
unsafe fn get_unchecked(&self) -> &T {
debug_assert!(self.is_initialized());
- (&*self.value.get()).get_ref()
+ (&*self.value.get()).assume_init_ref()
}
/// Safety: The value must be initialized
unsafe fn get_unchecked_mut(&mut self) -> &mut T {
debug_assert!(self.is_initialized());
- (&mut *self.value.get()).get_mut()
+ (&mut *self.value.get()).assume_init_mut()
}
}
unsafe impl<#[may_dangle] T> Drop for SyncOnceCell<T> {
fn drop(&mut self) {
- // Safety: The cell is being dropped, so it can't be accessed again.
- // We also don't touch the `T`, which validates our usage of #[may_dangle].
- unsafe { self.take_inner() };
+ if self.is_initialized() {
+ // Safety: The cell is initialized and being dropped, so it can't
+ // be accessed again. We also don't touch the `T` other than
+ // dropping it, which validates our usage of #[may_dangle].
+ unsafe { (&mut *self.value.get()).assume_init_drop() };
+ }
}
}
@@ -506,333 +576,3 @@
SyncLazy::new(T::default)
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::{
- lazy::{Lazy, SyncLazy, SyncOnceCell},
- panic,
- sync::{
- atomic::{AtomicUsize, Ordering::SeqCst},
- mpsc::channel,
- Mutex,
- },
- thread,
- };
-
- #[test]
- fn lazy_default() {
- static CALLED: AtomicUsize = AtomicUsize::new(0);
-
- struct Foo(u8);
- impl Default for Foo {
- fn default() -> Self {
- CALLED.fetch_add(1, SeqCst);
- Foo(42)
- }
- }
-
- let lazy: Lazy<Mutex<Foo>> = <_>::default();
-
- assert_eq!(CALLED.load(SeqCst), 0);
-
- assert_eq!(lazy.lock().unwrap().0, 42);
- assert_eq!(CALLED.load(SeqCst), 1);
-
- lazy.lock().unwrap().0 = 21;
-
- assert_eq!(lazy.lock().unwrap().0, 21);
- assert_eq!(CALLED.load(SeqCst), 1);
- }
-
- #[test]
- fn lazy_poisoning() {
- let x: Lazy<String> = Lazy::new(|| panic!("kaboom"));
- for _ in 0..2 {
- let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
- assert!(res.is_err());
- }
- }
-
- fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R {
- thread::spawn(f).join().unwrap()
- }
-
- #[test]
- fn sync_once_cell() {
- static ONCE_CELL: SyncOnceCell<i32> = SyncOnceCell::new();
-
- assert!(ONCE_CELL.get().is_none());
-
- spawn_and_wait(|| {
- ONCE_CELL.get_or_init(|| 92);
- assert_eq!(ONCE_CELL.get(), Some(&92));
- });
-
- ONCE_CELL.get_or_init(|| panic!("Kabom!"));
- assert_eq!(ONCE_CELL.get(), Some(&92));
- }
-
- #[test]
- fn sync_once_cell_get_mut() {
- let mut c = SyncOnceCell::new();
- assert!(c.get_mut().is_none());
- c.set(90).unwrap();
- *c.get_mut().unwrap() += 2;
- assert_eq!(c.get_mut(), Some(&mut 92));
- }
-
- #[test]
- fn sync_once_cell_get_unchecked() {
- let c = SyncOnceCell::new();
- c.set(92).unwrap();
- unsafe {
- assert_eq!(c.get_unchecked(), &92);
- }
- }
-
- #[test]
- fn sync_once_cell_drop() {
- static DROP_CNT: AtomicUsize = AtomicUsize::new(0);
- struct Dropper;
- impl Drop for Dropper {
- fn drop(&mut self) {
- DROP_CNT.fetch_add(1, SeqCst);
- }
- }
-
- let x = SyncOnceCell::new();
- spawn_and_wait(move || {
- x.get_or_init(|| Dropper);
- assert_eq!(DROP_CNT.load(SeqCst), 0);
- drop(x);
- });
-
- assert_eq!(DROP_CNT.load(SeqCst), 1);
- }
-
- #[test]
- fn sync_once_cell_drop_empty() {
- let x = SyncOnceCell::<String>::new();
- drop(x);
- }
-
- #[test]
- fn clone() {
- let s = SyncOnceCell::new();
- let c = s.clone();
- assert!(c.get().is_none());
-
- s.set("hello".to_string()).unwrap();
- let c = s.clone();
- assert_eq!(c.get().map(String::as_str), Some("hello"));
- }
-
- #[test]
- fn get_or_try_init() {
- let cell: SyncOnceCell<String> = SyncOnceCell::new();
- assert!(cell.get().is_none());
-
- let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() }));
- assert!(res.is_err());
- assert!(!cell.is_initialized());
- assert!(cell.get().is_none());
-
- assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
-
- assert_eq!(
- cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())),
- Ok(&"hello".to_string())
- );
- assert_eq!(cell.get(), Some(&"hello".to_string()));
- }
-
- #[test]
- fn from_impl() {
- assert_eq!(SyncOnceCell::from("value").get(), Some(&"value"));
- assert_ne!(SyncOnceCell::from("foo").get(), Some(&"bar"));
- }
-
- #[test]
- fn partialeq_impl() {
- assert!(SyncOnceCell::from("value") == SyncOnceCell::from("value"));
- assert!(SyncOnceCell::from("foo") != SyncOnceCell::from("bar"));
-
- assert!(SyncOnceCell::<String>::new() == SyncOnceCell::new());
- assert!(SyncOnceCell::<String>::new() != SyncOnceCell::from("value".to_owned()));
- }
-
- #[test]
- fn into_inner() {
- let cell: SyncOnceCell<String> = SyncOnceCell::new();
- assert_eq!(cell.into_inner(), None);
- let cell = SyncOnceCell::new();
- cell.set("hello".to_string()).unwrap();
- assert_eq!(cell.into_inner(), Some("hello".to_string()));
- }
-
- #[test]
- fn sync_lazy_new() {
- static CALLED: AtomicUsize = AtomicUsize::new(0);
- static SYNC_LAZY: SyncLazy<i32> = SyncLazy::new(|| {
- CALLED.fetch_add(1, SeqCst);
- 92
- });
-
- assert_eq!(CALLED.load(SeqCst), 0);
-
- spawn_and_wait(|| {
- let y = *SYNC_LAZY - 30;
- assert_eq!(y, 62);
- assert_eq!(CALLED.load(SeqCst), 1);
- });
-
- let y = *SYNC_LAZY - 30;
- assert_eq!(y, 62);
- assert_eq!(CALLED.load(SeqCst), 1);
- }
-
- #[test]
- fn sync_lazy_default() {
- static CALLED: AtomicUsize = AtomicUsize::new(0);
-
- struct Foo(u8);
- impl Default for Foo {
- fn default() -> Self {
- CALLED.fetch_add(1, SeqCst);
- Foo(42)
- }
- }
-
- let lazy: SyncLazy<Mutex<Foo>> = <_>::default();
-
- assert_eq!(CALLED.load(SeqCst), 0);
-
- assert_eq!(lazy.lock().unwrap().0, 42);
- assert_eq!(CALLED.load(SeqCst), 1);
-
- lazy.lock().unwrap().0 = 21;
-
- assert_eq!(lazy.lock().unwrap().0, 21);
- assert_eq!(CALLED.load(SeqCst), 1);
- }
-
- #[test]
- fn static_sync_lazy() {
- static XS: SyncLazy<Vec<i32>> = SyncLazy::new(|| {
- let mut xs = Vec::new();
- xs.push(1);
- xs.push(2);
- xs.push(3);
- xs
- });
-
- spawn_and_wait(|| {
- assert_eq!(&*XS, &vec![1, 2, 3]);
- });
-
- assert_eq!(&*XS, &vec![1, 2, 3]);
- }
-
- #[test]
- fn static_sync_lazy_via_fn() {
- fn xs() -> &'static Vec<i32> {
- static XS: SyncOnceCell<Vec<i32>> = SyncOnceCell::new();
- XS.get_or_init(|| {
- let mut xs = Vec::new();
- xs.push(1);
- xs.push(2);
- xs.push(3);
- xs
- })
- }
- assert_eq!(xs(), &vec![1, 2, 3]);
- }
-
- #[test]
- fn sync_lazy_poisoning() {
- let x: SyncLazy<String> = SyncLazy::new(|| panic!("kaboom"));
- for _ in 0..2 {
- let res = panic::catch_unwind(|| x.len());
- assert!(res.is_err());
- }
- }
-
- #[test]
- fn is_sync_send() {
- fn assert_traits<T: Send + Sync>() {}
- assert_traits::<SyncOnceCell<String>>();
- assert_traits::<SyncLazy<String>>();
- }
-
- #[test]
- fn eval_once_macro() {
- macro_rules! eval_once {
- (|| -> $ty:ty {
- $($body:tt)*
- }) => {{
- static ONCE_CELL: SyncOnceCell<$ty> = SyncOnceCell::new();
- fn init() -> $ty {
- $($body)*
- }
- ONCE_CELL.get_or_init(init)
- }};
- }
-
- let fib: &'static Vec<i32> = eval_once! {
- || -> Vec<i32> {
- let mut res = vec![1, 1];
- for i in 0..10 {
- let next = res[i] + res[i + 1];
- res.push(next);
- }
- res
- }
- };
- assert_eq!(fib[5], 8)
- }
-
- #[test]
- fn sync_once_cell_does_not_leak_partially_constructed_boxes() {
- static ONCE_CELL: SyncOnceCell<String> = SyncOnceCell::new();
-
- let n_readers = 10;
- let n_writers = 3;
- const MSG: &str = "Hello, World";
-
- let (tx, rx) = channel();
-
- for _ in 0..n_readers {
- let tx = tx.clone();
- thread::spawn(move || {
- loop {
- if let Some(msg) = ONCE_CELL.get() {
- tx.send(msg).unwrap();
- break;
- }
- #[cfg(target_env = "sgx")]
- crate::thread::yield_now();
- }
- });
- }
- for _ in 0..n_writers {
- thread::spawn(move || {
- let _ = ONCE_CELL.set(MSG.to_owned());
- });
- }
-
- for _ in 0..n_readers {
- let msg = rx.recv().unwrap();
- assert_eq!(msg, MSG);
- }
- }
-
- #[test]
- fn dropck() {
- let cell = SyncOnceCell::new();
- {
- let s = String::new();
- cell.set(&s).unwrap();
- }
- }
-}
diff --git a/library/std/src/lazy/tests.rs b/library/std/src/lazy/tests.rs
new file mode 100644
index 0000000..a170edb
--- /dev/null
+++ b/library/std/src/lazy/tests.rs
@@ -0,0 +1,323 @@
+use crate::{
+ lazy::{Lazy, SyncLazy, SyncOnceCell},
+ panic,
+ sync::{
+ atomic::{AtomicUsize, Ordering::SeqCst},
+ mpsc::channel,
+ Mutex,
+ },
+ thread,
+};
+
+#[test]
+fn lazy_default() {
+ static CALLED: AtomicUsize = AtomicUsize::new(0);
+
+ struct Foo(u8);
+ impl Default for Foo {
+ fn default() -> Self {
+ CALLED.fetch_add(1, SeqCst);
+ Foo(42)
+ }
+ }
+
+ let lazy: Lazy<Mutex<Foo>> = <_>::default();
+
+ assert_eq!(CALLED.load(SeqCst), 0);
+
+ assert_eq!(lazy.lock().unwrap().0, 42);
+ assert_eq!(CALLED.load(SeqCst), 1);
+
+ lazy.lock().unwrap().0 = 21;
+
+ assert_eq!(lazy.lock().unwrap().0, 21);
+ assert_eq!(CALLED.load(SeqCst), 1);
+}
+
+#[test]
+fn lazy_poisoning() {
+ let x: Lazy<String> = Lazy::new(|| panic!("kaboom"));
+ for _ in 0..2 {
+ let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
+ assert!(res.is_err());
+ }
+}
+
+fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R {
+ thread::spawn(f).join().unwrap()
+}
+
+#[test]
+fn sync_once_cell() {
+ static ONCE_CELL: SyncOnceCell<i32> = SyncOnceCell::new();
+
+ assert!(ONCE_CELL.get().is_none());
+
+ spawn_and_wait(|| {
+ ONCE_CELL.get_or_init(|| 92);
+ assert_eq!(ONCE_CELL.get(), Some(&92));
+ });
+
+ ONCE_CELL.get_or_init(|| panic!("Kabom!"));
+ assert_eq!(ONCE_CELL.get(), Some(&92));
+}
+
+#[test]
+fn sync_once_cell_get_mut() {
+ let mut c = SyncOnceCell::new();
+ assert!(c.get_mut().is_none());
+ c.set(90).unwrap();
+ *c.get_mut().unwrap() += 2;
+ assert_eq!(c.get_mut(), Some(&mut 92));
+}
+
+#[test]
+fn sync_once_cell_get_unchecked() {
+ let c = SyncOnceCell::new();
+ c.set(92).unwrap();
+ unsafe {
+ assert_eq!(c.get_unchecked(), &92);
+ }
+}
+
+#[test]
+fn sync_once_cell_drop() {
+ static DROP_CNT: AtomicUsize = AtomicUsize::new(0);
+ struct Dropper;
+ impl Drop for Dropper {
+ fn drop(&mut self) {
+ DROP_CNT.fetch_add(1, SeqCst);
+ }
+ }
+
+ let x = SyncOnceCell::new();
+ spawn_and_wait(move || {
+ x.get_or_init(|| Dropper);
+ assert_eq!(DROP_CNT.load(SeqCst), 0);
+ drop(x);
+ });
+
+ assert_eq!(DROP_CNT.load(SeqCst), 1);
+}
+
+#[test]
+fn sync_once_cell_drop_empty() {
+ let x = SyncOnceCell::<String>::new();
+ drop(x);
+}
+
+#[test]
+fn clone() {
+ let s = SyncOnceCell::new();
+ let c = s.clone();
+ assert!(c.get().is_none());
+
+ s.set("hello".to_string()).unwrap();
+ let c = s.clone();
+ assert_eq!(c.get().map(String::as_str), Some("hello"));
+}
+
+#[test]
+fn get_or_try_init() {
+ let cell: SyncOnceCell<String> = SyncOnceCell::new();
+ assert!(cell.get().is_none());
+
+ let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() }));
+ assert!(res.is_err());
+ assert!(!cell.is_initialized());
+ assert!(cell.get().is_none());
+
+ assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
+
+ assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string()));
+ assert_eq!(cell.get(), Some(&"hello".to_string()));
+}
+
+#[test]
+fn from_impl() {
+ assert_eq!(SyncOnceCell::from("value").get(), Some(&"value"));
+ assert_ne!(SyncOnceCell::from("foo").get(), Some(&"bar"));
+}
+
+#[test]
+fn partialeq_impl() {
+ assert!(SyncOnceCell::from("value") == SyncOnceCell::from("value"));
+ assert!(SyncOnceCell::from("foo") != SyncOnceCell::from("bar"));
+
+ assert!(SyncOnceCell::<String>::new() == SyncOnceCell::new());
+ assert!(SyncOnceCell::<String>::new() != SyncOnceCell::from("value".to_owned()));
+}
+
+#[test]
+fn into_inner() {
+ let cell: SyncOnceCell<String> = SyncOnceCell::new();
+ assert_eq!(cell.into_inner(), None);
+ let cell = SyncOnceCell::new();
+ cell.set("hello".to_string()).unwrap();
+ assert_eq!(cell.into_inner(), Some("hello".to_string()));
+}
+
+#[test]
+fn sync_lazy_new() {
+ static CALLED: AtomicUsize = AtomicUsize::new(0);
+ static SYNC_LAZY: SyncLazy<i32> = SyncLazy::new(|| {
+ CALLED.fetch_add(1, SeqCst);
+ 92
+ });
+
+ assert_eq!(CALLED.load(SeqCst), 0);
+
+ spawn_and_wait(|| {
+ let y = *SYNC_LAZY - 30;
+ assert_eq!(y, 62);
+ assert_eq!(CALLED.load(SeqCst), 1);
+ });
+
+ let y = *SYNC_LAZY - 30;
+ assert_eq!(y, 62);
+ assert_eq!(CALLED.load(SeqCst), 1);
+}
+
+#[test]
+fn sync_lazy_default() {
+ static CALLED: AtomicUsize = AtomicUsize::new(0);
+
+ struct Foo(u8);
+ impl Default for Foo {
+ fn default() -> Self {
+ CALLED.fetch_add(1, SeqCst);
+ Foo(42)
+ }
+ }
+
+ let lazy: SyncLazy<Mutex<Foo>> = <_>::default();
+
+ assert_eq!(CALLED.load(SeqCst), 0);
+
+ assert_eq!(lazy.lock().unwrap().0, 42);
+ assert_eq!(CALLED.load(SeqCst), 1);
+
+ lazy.lock().unwrap().0 = 21;
+
+ assert_eq!(lazy.lock().unwrap().0, 21);
+ assert_eq!(CALLED.load(SeqCst), 1);
+}
+
+#[test]
+fn static_sync_lazy() {
+ static XS: SyncLazy<Vec<i32>> = SyncLazy::new(|| {
+ let mut xs = Vec::new();
+ xs.push(1);
+ xs.push(2);
+ xs.push(3);
+ xs
+ });
+
+ spawn_and_wait(|| {
+ assert_eq!(&*XS, &vec![1, 2, 3]);
+ });
+
+ assert_eq!(&*XS, &vec![1, 2, 3]);
+}
+
+#[test]
+fn static_sync_lazy_via_fn() {
+ fn xs() -> &'static Vec<i32> {
+ static XS: SyncOnceCell<Vec<i32>> = SyncOnceCell::new();
+ XS.get_or_init(|| {
+ let mut xs = Vec::new();
+ xs.push(1);
+ xs.push(2);
+ xs.push(3);
+ xs
+ })
+ }
+ assert_eq!(xs(), &vec![1, 2, 3]);
+}
+
+#[test]
+fn sync_lazy_poisoning() {
+ let x: SyncLazy<String> = SyncLazy::new(|| panic!("kaboom"));
+ for _ in 0..2 {
+ let res = panic::catch_unwind(|| x.len());
+ assert!(res.is_err());
+ }
+}
+
+#[test]
+fn is_sync_send() {
+ fn assert_traits<T: Send + Sync>() {}
+ assert_traits::<SyncOnceCell<String>>();
+ assert_traits::<SyncLazy<String>>();
+}
+
+#[test]
+fn eval_once_macro() {
+ macro_rules! eval_once {
+ (|| -> $ty:ty {
+ $($body:tt)*
+ }) => {{
+ static ONCE_CELL: SyncOnceCell<$ty> = SyncOnceCell::new();
+ fn init() -> $ty {
+ $($body)*
+ }
+ ONCE_CELL.get_or_init(init)
+ }};
+ }
+
+ let fib: &'static Vec<i32> = eval_once! {
+ || -> Vec<i32> {
+ let mut res = vec![1, 1];
+ for i in 0..10 {
+ let next = res[i] + res[i + 1];
+ res.push(next);
+ }
+ res
+ }
+ };
+ assert_eq!(fib[5], 8)
+}
+
+#[test]
+fn sync_once_cell_does_not_leak_partially_constructed_boxes() {
+ static ONCE_CELL: SyncOnceCell<String> = SyncOnceCell::new();
+
+ let n_readers = 10;
+ let n_writers = 3;
+ const MSG: &str = "Hello, World";
+
+ let (tx, rx) = channel();
+
+ for _ in 0..n_readers {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ loop {
+ if let Some(msg) = ONCE_CELL.get() {
+ tx.send(msg).unwrap();
+ break;
+ }
+ #[cfg(target_env = "sgx")]
+ crate::thread::yield_now();
+ }
+ });
+ }
+ for _ in 0..n_writers {
+ thread::spawn(move || {
+ let _ = ONCE_CELL.set(MSG.to_owned());
+ });
+ }
+
+ for _ in 0..n_readers {
+ let msg = rx.recv().unwrap();
+ assert_eq!(msg, MSG);
+ }
+}
+
+#[test]
+fn dropck() {
+ let cell = SyncOnceCell::new();
+ {
+ let s = String::new();
+ cell.set(&s).unwrap();
+ }
+}
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 1142b74..5aa9d6a 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -226,9 +226,9 @@
#![feature(asm)]
#![feature(associated_type_bounds)]
#![feature(atomic_mut_ptr)]
+#![feature(bool_to_option)]
#![feature(box_syntax)]
#![feature(c_variadic)]
-#![feature(can_vector)]
#![feature(cfg_accessible)]
#![feature(cfg_target_has_atomic)]
#![feature(cfg_target_thread_local)]
@@ -237,17 +237,23 @@
#![feature(clamp)]
#![feature(concat_idents)]
#![feature(const_cstr_unchecked)]
+#![cfg_attr(not(bootstrap), feature(const_fn_floating_point_arithmetic))]
#![feature(const_fn_transmute)]
+#![feature(const_fn)]
+#![cfg_attr(not(bootstrap), feature(const_fn_fn_ptr_basics))]
+#![feature(const_ip)]
+#![feature(const_ipv6)]
#![feature(const_raw_ptr_deref)]
+#![feature(const_ipv4)]
#![feature(container_error_extra)]
#![feature(core_intrinsics)]
#![feature(custom_test_frameworks)]
#![feature(decl_macro)]
-#![feature(doc_alias)]
+#![cfg_attr(bootstrap, feature(doc_alias))]
#![feature(doc_cfg)]
#![feature(doc_keyword)]
#![feature(doc_masked)]
-#![cfg_attr(not(bootstrap), feature(doc_spotlight))]
+#![feature(doc_spotlight)]
#![feature(dropck_eyepatch)]
#![feature(duration_constants)]
#![feature(exact_size_is_empty)]
@@ -256,18 +262,16 @@
#![feature(external_doc)]
#![feature(fn_traits)]
#![feature(format_args_nl)]
-#![feature(future_readiness_fns)]
#![feature(gen_future)]
#![feature(generator_trait)]
+#![feature(get_mut_unchecked)]
#![feature(global_asm)]
-#![feature(hash_raw_entry)]
#![feature(hashmap_internals)]
#![feature(int_error_internals)]
#![feature(int_error_matching)]
#![feature(integer_atomics)]
#![feature(into_future)]
#![feature(lang_items)]
-#![feature(libc)]
#![feature(link_args)]
#![feature(linkage)]
#![feature(llvm_asm)]
@@ -287,6 +291,7 @@
#![feature(panic_info_message)]
#![feature(panic_internals)]
#![feature(panic_unwind)]
+#![feature(pin_static_ref)]
#![feature(prelude_import)]
#![feature(ptr_internals)]
#![feature(raw)]
@@ -308,12 +313,15 @@
#![feature(str_internals)]
#![feature(test)]
#![feature(thread_local)]
+#![feature(thread_local_internals)]
#![feature(toowned_clone_into)]
#![feature(total_cmp)]
#![feature(trace_macros)]
#![feature(try_reserve)]
#![feature(unboxed_closures)]
#![feature(unsafe_block_in_unsafe_fn)]
+#![feature(unsafe_cell_get_mut)]
+#![feature(unsafe_cell_raw_get)]
#![feature(untagged_unions)]
#![feature(unwind_attributes)]
#![feature(vec_into_raw_parts)]
diff --git a/library/std/src/memchr.rs b/library/std/src/memchr.rs
index d69294b..86a08f7 100644
--- a/library/std/src/memchr.rs
+++ b/library/std/src/memchr.rs
@@ -1,6 +1,9 @@
// Original implementation taken from rust-memchr.
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
+#[cfg(test)]
+mod tests;
+
/// A safe interface to `memchr`.
///
/// Returns the index corresponding to the first occurrence of `needle` in
@@ -44,90 +47,3 @@
pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
crate::sys::memchr::memrchr(needle, haystack)
}
-
-#[cfg(test)]
-mod tests {
- // test the implementations for the current platform
- use super::{memchr, memrchr};
-
- #[test]
- fn matches_one() {
- assert_eq!(Some(0), memchr(b'a', b"a"));
- }
-
- #[test]
- fn matches_begin() {
- assert_eq!(Some(0), memchr(b'a', b"aaaa"));
- }
-
- #[test]
- fn matches_end() {
- assert_eq!(Some(4), memchr(b'z', b"aaaaz"));
- }
-
- #[test]
- fn matches_nul() {
- assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00"));
- }
-
- #[test]
- fn matches_past_nul() {
- assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z"));
- }
-
- #[test]
- fn no_match_empty() {
- assert_eq!(None, memchr(b'a', b""));
- }
-
- #[test]
- fn no_match() {
- assert_eq!(None, memchr(b'a', b"xyz"));
- }
-
- #[test]
- fn matches_one_reversed() {
- assert_eq!(Some(0), memrchr(b'a', b"a"));
- }
-
- #[test]
- fn matches_begin_reversed() {
- assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
- }
-
- #[test]
- fn matches_end_reversed() {
- assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
- }
-
- #[test]
- fn matches_nul_reversed() {
- assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
- }
-
- #[test]
- fn matches_past_nul_reversed() {
- assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
- }
-
- #[test]
- fn no_match_empty_reversed() {
- assert_eq!(None, memrchr(b'a', b""));
- }
-
- #[test]
- fn no_match_reversed() {
- assert_eq!(None, memrchr(b'a', b"xyz"));
- }
-
- #[test]
- fn each_alignment() {
- let mut data = [1u8; 64];
- let needle = 2;
- let pos = 40;
- data[pos] = needle;
- for start in 0..16 {
- assert_eq!(Some(pos - start), memchr(needle, &data[start..]));
- }
- }
-}
diff --git a/library/std/src/memchr/tests.rs b/library/std/src/memchr/tests.rs
new file mode 100644
index 0000000..557d749
--- /dev/null
+++ b/library/std/src/memchr/tests.rs
@@ -0,0 +1,86 @@
+// Original implementation taken from rust-memchr.
+// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
+
+// test the implementations for the current platform
+use super::{memchr, memrchr};
+
+#[test]
+fn matches_one() {
+ assert_eq!(Some(0), memchr(b'a', b"a"));
+}
+
+#[test]
+fn matches_begin() {
+ assert_eq!(Some(0), memchr(b'a', b"aaaa"));
+}
+
+#[test]
+fn matches_end() {
+ assert_eq!(Some(4), memchr(b'z', b"aaaaz"));
+}
+
+#[test]
+fn matches_nul() {
+ assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00"));
+}
+
+#[test]
+fn matches_past_nul() {
+ assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z"));
+}
+
+#[test]
+fn no_match_empty() {
+ assert_eq!(None, memchr(b'a', b""));
+}
+
+#[test]
+fn no_match() {
+ assert_eq!(None, memchr(b'a', b"xyz"));
+}
+
+#[test]
+fn matches_one_reversed() {
+ assert_eq!(Some(0), memrchr(b'a', b"a"));
+}
+
+#[test]
+fn matches_begin_reversed() {
+ assert_eq!(Some(3), memrchr(b'a', b"aaaa"));
+}
+
+#[test]
+fn matches_end_reversed() {
+ assert_eq!(Some(0), memrchr(b'z', b"zaaaa"));
+}
+
+#[test]
+fn matches_nul_reversed() {
+ assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00"));
+}
+
+#[test]
+fn matches_past_nul_reversed() {
+ assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa"));
+}
+
+#[test]
+fn no_match_empty_reversed() {
+ assert_eq!(None, memrchr(b'a', b""));
+}
+
+#[test]
+fn no_match_reversed() {
+ assert_eq!(None, memrchr(b'a', b"xyz"));
+}
+
+#[test]
+fn each_alignment() {
+ let mut data = [1u8; 64];
+ let needle = 2;
+ let pos = 40;
+ data[pos] = needle;
+ for start in 0..16 {
+ assert_eq!(Some(pos - start), memchr(needle, &data[start..]));
+ }
+}
diff --git a/library/std/src/net/addr.rs b/library/std/src/net/addr.rs
index d7d9686..e213963 100644
--- a/library/std/src/net/addr.rs
+++ b/library/std/src/net/addr.rs
@@ -1,3 +1,6 @@
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
use crate::cmp::Ordering;
use crate::convert::TryInto;
use crate::fmt;
@@ -210,8 +213,6 @@
///
/// [IP address]: IpAddr
/// [`IPv4` address]: IpAddr::V4
- /// [`false`]: ../../std/primitive.bool.html
- /// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
@@ -232,8 +233,6 @@
///
/// [IP address]: IpAddr
/// [`IPv6` address]: IpAddr::V6
- /// [`false`]: ../../std/primitive.bool.html
- /// [`true`]: ../../std/primitive.bool.html
///
/// # Examples
///
@@ -746,9 +745,9 @@
/// `(`[`Ipv4Addr`]`, `[`u16`]`)`, `(`[`Ipv6Addr`]`, `[`u16`]`)`:
/// [`to_socket_addrs`] constructs a [`SocketAddr`] trivially.
///
-/// * `(`[`&str`]`, `[`u16`]`)`: the string should be either a string representation
+/// * `(`[`&str`]`, `[`u16`]`)`: [`&str`] should be either a string representation
/// of an [`IpAddr`] address as expected by [`FromStr`] implementation or a host
-/// name.
+/// name. [`u16`] is the port number.
///
/// * [`&str`]: the string should be either a string representation of a
/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like
@@ -991,236 +990,3 @@
(&**self).to_socket_addrs()
}
}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use crate::net::test::{sa4, sa6, tsa};
- use crate::net::*;
-
- #[test]
- fn to_socket_addr_ipaddr_u16() {
- let a = Ipv4Addr::new(77, 88, 21, 11);
- let p = 12345;
- let e = SocketAddr::V4(SocketAddrV4::new(a, p));
- assert_eq!(Ok(vec![e]), tsa((a, p)));
- }
-
- #[test]
- fn to_socket_addr_str_u16() {
- let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
- assert_eq!(Ok(vec![a]), tsa(("77.88.21.11", 24352)));
-
- let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
- 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]
- fn to_socket_addr_str() {
- let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
- assert_eq!(Ok(vec![a]), tsa("77.88.21.11:24352"));
-
- let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
- 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]
- fn to_socket_addr_string() {
- let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
- assert_eq!(Ok(vec![a]), tsa(&*format!("{}:{}", "77.88.21.11", "24352")));
- assert_eq!(Ok(vec![a]), tsa(&format!("{}:{}", "77.88.21.11", "24352")));
- assert_eq!(Ok(vec![a]), tsa(format!("{}:{}", "77.88.21.11", "24352")));
-
- let s = format!("{}:{}", "77.88.21.11", "24352");
- assert_eq!(Ok(vec![a]), tsa(s));
- // s has been moved into the tsa call
- }
-
- #[test]
- fn bind_udp_socket_bad() {
- // rust-lang/rust#53957: This is a regression test for a parsing problem
- // discovered as part of issue rust-lang/rust#23076, where we were
- // incorrectly parsing invalid input and then that would result in a
- // successful `UdpSocket` binding when we would expect failure.
- //
- // At one time, this test was written as a call to `tsa` with
- // INPUT_23076. However, that structure yields an unreliable test,
- // because it ends up passing junk input to the DNS server, and some DNS
- // servers will respond with `Ok` to such input, with the ip address of
- // the DNS server itself.
- //
- // This form of the test is more robust: even when the DNS server
- // returns its own address, it is still an error to bind a UDP socket to
- // a non-local address, and so we still get an error here in that case.
-
- const INPUT_23076: &'static str = "1200::AB00:1234::2552:7777:1313:34300";
-
- assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err())
- }
-
- #[test]
- fn set_ip() {
- fn ip4(low: u8) -> Ipv4Addr {
- Ipv4Addr::new(77, 88, 21, low)
- }
- fn ip6(low: u16) -> Ipv6Addr {
- Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, low)
- }
-
- let mut v4 = SocketAddrV4::new(ip4(11), 80);
- assert_eq!(v4.ip(), &ip4(11));
- v4.set_ip(ip4(12));
- assert_eq!(v4.ip(), &ip4(12));
-
- let mut addr = SocketAddr::V4(v4);
- assert_eq!(addr.ip(), IpAddr::V4(ip4(12)));
- addr.set_ip(IpAddr::V4(ip4(13)));
- assert_eq!(addr.ip(), IpAddr::V4(ip4(13)));
- addr.set_ip(IpAddr::V6(ip6(14)));
- assert_eq!(addr.ip(), IpAddr::V6(ip6(14)));
-
- let mut v6 = SocketAddrV6::new(ip6(1), 80, 0, 0);
- assert_eq!(v6.ip(), &ip6(1));
- v6.set_ip(ip6(2));
- assert_eq!(v6.ip(), &ip6(2));
-
- let mut addr = SocketAddr::V6(v6);
- assert_eq!(addr.ip(), IpAddr::V6(ip6(2)));
- addr.set_ip(IpAddr::V6(ip6(3)));
- assert_eq!(addr.ip(), IpAddr::V6(ip6(3)));
- addr.set_ip(IpAddr::V4(ip4(4)));
- assert_eq!(addr.ip(), IpAddr::V4(ip4(4)));
- }
-
- #[test]
- fn set_port() {
- let mut v4 = SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80);
- assert_eq!(v4.port(), 80);
- v4.set_port(443);
- assert_eq!(v4.port(), 443);
-
- let mut addr = SocketAddr::V4(v4);
- assert_eq!(addr.port(), 443);
- addr.set_port(8080);
- assert_eq!(addr.port(), 8080);
-
- let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 0);
- assert_eq!(v6.port(), 80);
- v6.set_port(443);
- assert_eq!(v6.port(), 443);
-
- let mut addr = SocketAddr::V6(v6);
- assert_eq!(addr.port(), 443);
- addr.set_port(8080);
- assert_eq!(addr.port(), 8080);
- }
-
- #[test]
- fn set_flowinfo() {
- let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0);
- assert_eq!(v6.flowinfo(), 10);
- v6.set_flowinfo(20);
- assert_eq!(v6.flowinfo(), 20);
- }
-
- #[test]
- fn set_scope_id() {
- let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 10);
- assert_eq!(v6.scope_id(), 10);
- v6.set_scope_id(20);
- assert_eq!(v6.scope_id(), 20);
- }
-
- #[test]
- fn is_v4() {
- let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80));
- assert!(v4.is_ipv4());
- assert!(!v4.is_ipv6());
- }
-
- #[test]
- fn is_v6() {
- let v6 = SocketAddr::V6(SocketAddrV6::new(
- Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1),
- 80,
- 10,
- 0,
- ));
- assert!(!v6.is_ipv4());
- assert!(v6.is_ipv6());
- }
-
- #[test]
- fn socket_v4_to_str() {
- let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080);
-
- assert_eq!(format!("{}", socket), "192.168.0.1:8080");
- assert_eq!(format!("{:<20}", socket), "192.168.0.1:8080 ");
- assert_eq!(format!("{:>20}", socket), " 192.168.0.1:8080");
- assert_eq!(format!("{:^20}", socket), " 192.168.0.1:8080 ");
- assert_eq!(format!("{:.10}", socket), "192.168.0.");
- }
-
- #[test]
- fn socket_v6_to_str() {
- let socket: SocketAddrV6 = "[2a02:6b8:0:1::1]:53".parse().unwrap();
-
- assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1]:53");
- assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1]:53 ");
- assert_eq!(format!("{:>24}", socket), " [2a02:6b8:0:1::1]:53");
- assert_eq!(format!("{:^24}", socket), " [2a02:6b8:0:1::1]:53 ");
- assert_eq!(format!("{:.15}", socket), "[2a02:6b8:0:1::");
- }
-
- #[test]
- fn compare() {
- let v4_1 = "224.120.45.1:23456".parse::<SocketAddrV4>().unwrap();
- let v4_2 = "224.210.103.5:12345".parse::<SocketAddrV4>().unwrap();
- let v4_3 = "224.210.103.5:23456".parse::<SocketAddrV4>().unwrap();
- let v6_1 = "[2001:db8:f00::1002]:23456".parse::<SocketAddrV6>().unwrap();
- let v6_2 = "[2001:db8:f00::2001]:12345".parse::<SocketAddrV6>().unwrap();
- let v6_3 = "[2001:db8:f00::2001]:23456".parse::<SocketAddrV6>().unwrap();
-
- // equality
- assert_eq!(v4_1, v4_1);
- assert_eq!(v6_1, v6_1);
- assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1));
- assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1));
- assert!(v4_1 != v4_2);
- assert!(v6_1 != v6_2);
-
- // compare different addresses
- assert!(v4_1 < v4_2);
- assert!(v6_1 < v6_2);
- assert!(v4_2 > v4_1);
- assert!(v6_2 > v6_1);
-
- // compare the same address with different ports
- assert!(v4_2 < v4_3);
- assert!(v6_2 < v6_3);
- assert!(v4_3 > v4_2);
- assert!(v6_3 > v6_2);
-
- // compare different addresses with the same port
- assert!(v4_1 < v4_3);
- assert!(v6_1 < v6_3);
- assert!(v4_3 > v4_1);
- assert!(v6_3 > v6_1);
-
- // compare with an inferred right-hand side
- assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap());
- assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap());
- assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap());
- }
-}
diff --git a/library/std/src/net/addr/tests.rs b/library/std/src/net/addr/tests.rs
new file mode 100644
index 0000000..cee9087
--- /dev/null
+++ b/library/std/src/net/addr/tests.rs
@@ -0,0 +1,229 @@
+use crate::net::test::{sa4, sa6, tsa};
+use crate::net::*;
+
+#[test]
+fn to_socket_addr_ipaddr_u16() {
+ let a = Ipv4Addr::new(77, 88, 21, 11);
+ let p = 12345;
+ let e = SocketAddr::V4(SocketAddrV4::new(a, p));
+ assert_eq!(Ok(vec![e]), tsa((a, p)));
+}
+
+#[test]
+fn to_socket_addr_str_u16() {
+ let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
+ assert_eq!(Ok(vec![a]), tsa(("77.88.21.11", 24352)));
+
+ let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
+ 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]
+fn to_socket_addr_str() {
+ let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
+ assert_eq!(Ok(vec![a]), tsa("77.88.21.11:24352"));
+
+ let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
+ 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]
+fn to_socket_addr_string() {
+ let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
+ assert_eq!(Ok(vec![a]), tsa(&*format!("{}:{}", "77.88.21.11", "24352")));
+ assert_eq!(Ok(vec![a]), tsa(&format!("{}:{}", "77.88.21.11", "24352")));
+ assert_eq!(Ok(vec![a]), tsa(format!("{}:{}", "77.88.21.11", "24352")));
+
+ let s = format!("{}:{}", "77.88.21.11", "24352");
+ assert_eq!(Ok(vec![a]), tsa(s));
+ // s has been moved into the tsa call
+}
+
+#[test]
+fn bind_udp_socket_bad() {
+ // rust-lang/rust#53957: This is a regression test for a parsing problem
+ // discovered as part of issue rust-lang/rust#23076, where we were
+ // incorrectly parsing invalid input and then that would result in a
+ // successful `UdpSocket` binding when we would expect failure.
+ //
+ // At one time, this test was written as a call to `tsa` with
+ // INPUT_23076. However, that structure yields an unreliable test,
+ // because it ends up passing junk input to the DNS server, and some DNS
+ // servers will respond with `Ok` to such input, with the ip address of
+ // the DNS server itself.
+ //
+ // This form of the test is more robust: even when the DNS server
+ // returns its own address, it is still an error to bind a UDP socket to
+ // a non-local address, and so we still get an error here in that case.
+
+ const INPUT_23076: &'static str = "1200::AB00:1234::2552:7777:1313:34300";
+
+ assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err())
+}
+
+#[test]
+fn set_ip() {
+ fn ip4(low: u8) -> Ipv4Addr {
+ Ipv4Addr::new(77, 88, 21, low)
+ }
+ fn ip6(low: u16) -> Ipv6Addr {
+ Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, low)
+ }
+
+ let mut v4 = SocketAddrV4::new(ip4(11), 80);
+ assert_eq!(v4.ip(), &ip4(11));
+ v4.set_ip(ip4(12));
+ assert_eq!(v4.ip(), &ip4(12));
+
+ let mut addr = SocketAddr::V4(v4);
+ assert_eq!(addr.ip(), IpAddr::V4(ip4(12)));
+ addr.set_ip(IpAddr::V4(ip4(13)));
+ assert_eq!(addr.ip(), IpAddr::V4(ip4(13)));
+ addr.set_ip(IpAddr::V6(ip6(14)));
+ assert_eq!(addr.ip(), IpAddr::V6(ip6(14)));
+
+ let mut v6 = SocketAddrV6::new(ip6(1), 80, 0, 0);
+ assert_eq!(v6.ip(), &ip6(1));
+ v6.set_ip(ip6(2));
+ assert_eq!(v6.ip(), &ip6(2));
+
+ let mut addr = SocketAddr::V6(v6);
+ assert_eq!(addr.ip(), IpAddr::V6(ip6(2)));
+ addr.set_ip(IpAddr::V6(ip6(3)));
+ assert_eq!(addr.ip(), IpAddr::V6(ip6(3)));
+ addr.set_ip(IpAddr::V4(ip4(4)));
+ assert_eq!(addr.ip(), IpAddr::V4(ip4(4)));
+}
+
+#[test]
+fn set_port() {
+ let mut v4 = SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80);
+ assert_eq!(v4.port(), 80);
+ v4.set_port(443);
+ assert_eq!(v4.port(), 443);
+
+ let mut addr = SocketAddr::V4(v4);
+ assert_eq!(addr.port(), 443);
+ addr.set_port(8080);
+ assert_eq!(addr.port(), 8080);
+
+ let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 0);
+ assert_eq!(v6.port(), 80);
+ v6.set_port(443);
+ assert_eq!(v6.port(), 443);
+
+ let mut addr = SocketAddr::V6(v6);
+ assert_eq!(addr.port(), 443);
+ addr.set_port(8080);
+ assert_eq!(addr.port(), 8080);
+}
+
+#[test]
+fn set_flowinfo() {
+ let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0);
+ assert_eq!(v6.flowinfo(), 10);
+ v6.set_flowinfo(20);
+ assert_eq!(v6.flowinfo(), 20);
+}
+
+#[test]
+fn set_scope_id() {
+ let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 10);
+ assert_eq!(v6.scope_id(), 10);
+ v6.set_scope_id(20);
+ assert_eq!(v6.scope_id(), 20);
+}
+
+#[test]
+fn is_v4() {
+ let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80));
+ assert!(v4.is_ipv4());
+ assert!(!v4.is_ipv6());
+}
+
+#[test]
+fn is_v6() {
+ let v6 = SocketAddr::V6(SocketAddrV6::new(
+ Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1),
+ 80,
+ 10,
+ 0,
+ ));
+ assert!(!v6.is_ipv4());
+ assert!(v6.is_ipv6());
+}
+
+#[test]
+fn socket_v4_to_str() {
+ let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080);
+
+ assert_eq!(format!("{}", socket), "192.168.0.1:8080");
+ assert_eq!(format!("{:<20}", socket), "192.168.0.1:8080 ");
+ assert_eq!(format!("{:>20}", socket), " 192.168.0.1:8080");
+ assert_eq!(format!("{:^20}", socket), " 192.168.0.1:8080 ");
+ assert_eq!(format!("{:.10}", socket), "192.168.0.");
+}
+
+#[test]
+fn socket_v6_to_str() {
+ let socket: SocketAddrV6 = "[2a02:6b8:0:1::1]:53".parse().unwrap();
+
+ assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1]:53");
+ assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1]:53 ");
+ assert_eq!(format!("{:>24}", socket), " [2a02:6b8:0:1::1]:53");
+ assert_eq!(format!("{:^24}", socket), " [2a02:6b8:0:1::1]:53 ");
+ assert_eq!(format!("{:.15}", socket), "[2a02:6b8:0:1::");
+}
+
+#[test]
+fn compare() {
+ let v4_1 = "224.120.45.1:23456".parse::<SocketAddrV4>().unwrap();
+ let v4_2 = "224.210.103.5:12345".parse::<SocketAddrV4>().unwrap();
+ let v4_3 = "224.210.103.5:23456".parse::<SocketAddrV4>().unwrap();
+ let v6_1 = "[2001:db8:f00::1002]:23456".parse::<SocketAddrV6>().unwrap();
+ let v6_2 = "[2001:db8:f00::2001]:12345".parse::<SocketAddrV6>().unwrap();
+ let v6_3 = "[2001:db8:f00::2001]:23456".parse::<SocketAddrV6>().unwrap();
+
+ // equality
+ assert_eq!(v4_1, v4_1);
+ assert_eq!(v6_1, v6_1);
+ assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1));
+ assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1));
+ assert!(v4_1 != v4_2);
+ assert!(v6_1 != v6_2);
+
+ // compare different addresses
+ assert!(v4_1 < v4_2);
+ assert!(v6_1 < v6_2);
+ assert!(v4_2 > v4_1);
+ assert!(v6_2 > v6_1);
+
+ // compare the same address with different ports
+ assert!(v4_2 < v4_3);
+ assert!(v6_2 < v6_3);
+ assert!(v4_3 > v4_2);
+ assert!(v6_3 > v6_2);
+
+ // compare different addresses with the same port
+ assert!(v4_1 < v4_3);
+ assert!(v6_1 < v6_3);
+ assert!(v4_3 > v4_1);
+ assert!(v6_3 > v6_1);
+
+ // compare with an inferred right-hand side
+ assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap());
+ assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap());
+ assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap());
+}
diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs
index 85bb6b6..f01a7b7 100644
--- a/library/std/src/net/ip.rs
+++ b/library/std/src/net/ip.rs
@@ -6,6 +6,10 @@
issue = "27709"
)]
+// Tests for this module
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
use crate::cmp::Ordering;
use crate::fmt::{self, Write as FmtWrite};
use crate::hash;
@@ -136,8 +140,6 @@
/// See the documentation for [`Ipv4Addr::is_unspecified()`] and
/// [`Ipv6Addr::is_unspecified()`] for more details.
///
- /// [`true`]: ../../std/primitive.bool.html
- ///
/// # Examples
///
/// ```
@@ -146,8 +148,9 @@
/// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true);
/// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true);
/// ```
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
#[stable(feature = "ip_shared", since = "1.12.0")]
- pub fn is_unspecified(&self) -> bool {
+ pub const fn is_unspecified(&self) -> bool {
match self {
IpAddr::V4(ip) => ip.is_unspecified(),
IpAddr::V6(ip) => ip.is_unspecified(),
@@ -159,8 +162,6 @@
/// See the documentation for [`Ipv4Addr::is_loopback()`] and
/// [`Ipv6Addr::is_loopback()`] for more details.
///
- /// [`true`]: ../../std/primitive.bool.html
- ///
/// # Examples
///
/// ```
@@ -169,8 +170,9 @@
/// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true);
/// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true);
/// ```
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
#[stable(feature = "ip_shared", since = "1.12.0")]
- pub fn is_loopback(&self) -> bool {
+ pub const fn is_loopback(&self) -> bool {
match self {
IpAddr::V4(ip) => ip.is_loopback(),
IpAddr::V6(ip) => ip.is_loopback(),
@@ -182,8 +184,6 @@
/// See the documentation for [`Ipv4Addr::is_global()`] and
/// [`Ipv6Addr::is_global()`] for more details.
///
- /// [`true`]: ../../std/primitive.bool.html
- ///
/// # Examples
///
/// ```
@@ -194,7 +194,8 @@
/// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true);
/// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true);
/// ```
- pub fn is_global(&self) -> bool {
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
+ pub const fn is_global(&self) -> bool {
match self {
IpAddr::V4(ip) => ip.is_global(),
IpAddr::V6(ip) => ip.is_global(),
@@ -206,8 +207,6 @@
/// See the documentation for [`Ipv4Addr::is_multicast()`] and
/// [`Ipv6Addr::is_multicast()`] for more details.
///
- /// [`true`]: ../../std/primitive.bool.html
- ///
/// # Examples
///
/// ```
@@ -216,8 +215,9 @@
/// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true);
/// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true);
/// ```
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
#[stable(feature = "ip_shared", since = "1.12.0")]
- pub fn is_multicast(&self) -> bool {
+ pub const fn is_multicast(&self) -> bool {
match self {
IpAddr::V4(ip) => ip.is_multicast(),
IpAddr::V6(ip) => ip.is_multicast(),
@@ -229,8 +229,6 @@
/// See the documentation for [`Ipv4Addr::is_documentation()`] and
/// [`Ipv6Addr::is_documentation()`] for more details.
///
- /// [`true`]: ../../std/primitive.bool.html
- ///
/// # Examples
///
/// ```
@@ -244,7 +242,8 @@
/// true
/// );
/// ```
- pub fn is_documentation(&self) -> bool {
+ #[rustc_const_unstable(feature = "const_ip", issue = "76205")]
+ pub const fn is_documentation(&self) -> bool {
match self {
IpAddr::V4(ip) => ip.is_documentation(),
IpAddr::V6(ip) => ip.is_documentation(),
@@ -254,8 +253,6 @@
/// Returns [`true`] if this address is an [`IPv4` address], and [`false`]
/// otherwise.
///
- /// [`true`]: ../../std/primitive.bool.html
- /// [`false`]: ../../std/primitive.bool.html
/// [`IPv4` address]: IpAddr::V4
///
/// # Examples
@@ -274,8 +271,6 @@
/// Returns [`true`] if this address is an [`IPv6` address], and [`false`]
/// otherwise.
///
- /// [`true`]: ../../std/primitive.bool.html
- /// [`false`]: ../../std/primitive.bool.html
/// [`IPv6` address]: IpAddr::V6
///
/// # Examples
@@ -361,8 +356,9 @@
/// let addr = Ipv4Addr::new(127, 0, 0, 1);
/// assert_eq!(addr.octets(), [127, 0, 0, 1]);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn octets(&self) -> [u8; 4] {
+ pub const fn octets(&self) -> [u8; 4] {
// This returns the order we want because s_addr is stored in big-endian.
self.inner.s_addr.to_ne_bytes()
}
@@ -372,7 +368,6 @@
/// This property is defined in _UNIX Network Programming, Second Edition_,
/// W. Richard Stevens, p. 891; see also [ip7].
///
- /// [`true`]: ../../std/primitive.bool.html
/// [ip7]: http://man7.org/linux/man-pages/man7/ip.7.html
///
/// # Examples
@@ -393,7 +388,6 @@
///
/// This property is defined by [IETF RFC 1122].
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122
///
/// # Examples
@@ -404,8 +398,9 @@
/// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true);
/// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[stable(since = "1.7.0", feature = "ip_17")]
- pub fn is_loopback(&self) -> bool {
+ pub const fn is_loopback(&self) -> bool {
self.octets()[0] == 127
}
@@ -417,7 +412,6 @@
/// - 172.16.0.0/12
/// - 192.168.0.0/16
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918
///
/// # Examples
@@ -433,8 +427,9 @@
/// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true);
/// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[stable(since = "1.7.0", feature = "ip_17")]
- pub fn is_private(&self) -> bool {
+ pub const fn is_private(&self) -> bool {
match self.octets() {
[10, ..] => true,
[172, b, ..] if b >= 16 && b <= 31 => true,
@@ -447,7 +442,6 @@
///
/// This property is defined by [IETF RFC 3927].
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927
///
/// # Examples
@@ -459,8 +453,9 @@
/// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true);
/// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[stable(since = "1.7.0", feature = "ip_17")]
- pub fn is_link_local(&self) -> bool {
+ pub const fn is_link_local(&self) -> bool {
match self.octets() {
[169, 254, ..] => true,
_ => false,
@@ -486,8 +481,6 @@
/// - addresses reserved for networking devices benchmarking (see
/// [`Ipv4Addr::is_benchmarking()`])
///
- /// [`true`]: ../../std/primitive.bool.html
- /// [`false`]: ../../std/primitive.bool.html
/// [ipv4-sr]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
///
/// # Examples
@@ -538,10 +531,13 @@
/// 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 {
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
+ pub const fn is_global(&self) -> bool {
// 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 {
+ if u32::from_be_bytes(self.octets()) == 0xc0000009
+ || u32::from_be_bytes(self.octets()) == 0xc000000a
+ {
return true;
}
!self.is_private()
@@ -560,7 +556,6 @@
/// Returns [`true`] if this address is part of the Shared Address Space defined in
/// [IETF RFC 6598] (`100.64.0.0/10`).
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598
///
/// # Examples
@@ -573,7 +568,8 @@
/// 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 {
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
+ pub const fn is_shared(&self) -> bool {
self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000)
}
@@ -586,7 +582,6 @@
/// - `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])
///
- /// [`true`]: ../../std/primitive.bool.html
/// [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
@@ -605,7 +600,8 @@
/// 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 {
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
+ pub const fn is_ietf_protocol_assignment(&self) -> bool {
self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0
}
@@ -613,7 +609,6 @@
/// 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`.
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544
/// [errata 423]: https://www.rfc-editor.org/errata/eid423
///
@@ -628,7 +623,8 @@
/// 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 {
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
+ pub const fn is_benchmarking(&self) -> bool {
self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18
}
@@ -637,7 +633,6 @@
/// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since
/// it is obviously not reserved for future use.
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112
///
/// # Warning
@@ -660,7 +655,8 @@
/// // 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 {
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
+ pub const fn is_reserved(&self) -> bool {
self.octets()[0] & 240 == 240 && !self.is_broadcast()
}
@@ -669,7 +665,6 @@
/// Multicast addresses have a most significant octet between 224 and 239,
/// and is defined by [IETF RFC 5771].
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771
///
/// # Examples
@@ -681,8 +676,9 @@
/// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true);
/// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[stable(since = "1.7.0", feature = "ip_17")]
- pub fn is_multicast(&self) -> bool {
+ pub const fn is_multicast(&self) -> bool {
self.octets()[0] >= 224 && self.octets()[0] <= 239
}
@@ -690,7 +686,6 @@
///
/// A broadcast address has all octets set to 255 as defined in [IETF RFC 919].
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 919]: https://tools.ietf.org/html/rfc919
///
/// # Examples
@@ -701,9 +696,10 @@
/// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true);
/// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[stable(since = "1.7.0", feature = "ip_17")]
- pub fn is_broadcast(&self) -> bool {
- self == &Self::BROADCAST
+ pub const fn is_broadcast(&self) -> bool {
+ u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets())
}
/// Returns [`true`] if this address is in a range designated for documentation.
@@ -714,7 +710,6 @@
/// - 198.51.100.0/24 (TEST-NET-2)
/// - 203.0.113.0/24 (TEST-NET-3)
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737
///
/// # Examples
@@ -727,8 +722,9 @@
/// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true);
/// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[stable(since = "1.7.0", feature = "ip_17")]
- pub fn is_documentation(&self) -> bool {
+ pub const fn is_documentation(&self) -> bool {
match self.octets() {
[192, 0, 2, _] => true,
[198, 51, 100, _] => true,
@@ -741,6 +737,9 @@
///
/// a.b.c.d becomes ::a.b.c.d
///
+ /// This isn't typically the method you want; these addresses don't typically
+ /// function on modern systems. Use `to_ipv6_mapped` instead.
+ ///
/// [`IPv6` address]: Ipv6Addr
///
/// # Examples
@@ -753,10 +752,13 @@
/// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 767)
/// );
/// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn to_ipv6_compatible(&self) -> Ipv6Addr {
+ pub const fn to_ipv6_compatible(&self) -> Ipv6Addr {
let [a, b, c, d] = self.octets();
- Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d])
+ Ipv6Addr {
+ inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d] },
+ }
}
/// Converts this address to an IPv4-mapped [`IPv6` address].
@@ -773,10 +775,13 @@
/// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(),
/// Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 49152, 767));
/// ```
+ #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn to_ipv6_mapped(&self) -> Ipv6Addr {
+ pub const fn to_ipv6_mapped(&self) -> Ipv6Addr {
let [a, b, c, d] = self.octets();
- Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d])
+ Ipv6Addr {
+ inner: c::in6_addr { s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] },
+ }
}
}
@@ -1098,8 +1103,9 @@
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(),
/// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn segments(&self) -> [u16; 8] {
+ pub const fn segments(&self) -> [u16; 8] {
// All elements in `s6_addr` must be big endian.
// SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`.
let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.inner.s6_addr) };
@@ -1120,7 +1126,6 @@
///
/// This property is defined in [IETF RFC 4291].
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
///
/// # Examples
@@ -1131,16 +1136,16 @@
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false);
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[stable(since = "1.7.0", feature = "ip_17")]
- pub fn is_unspecified(&self) -> bool {
- self.segments() == [0, 0, 0, 0, 0, 0, 0, 0]
+ pub const fn is_unspecified(&self) -> bool {
+ u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets())
}
/// Returns [`true`] if this is a loopback address (::1).
///
/// This property is defined in [IETF RFC 4291].
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
///
/// # Examples
@@ -1151,9 +1156,10 @@
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false);
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[stable(since = "1.7.0", feature = "ip_17")]
- pub fn is_loopback(&self) -> bool {
- self.segments() == [0, 0, 0, 0, 0, 0, 0, 1]
+ pub const fn is_loopback(&self) -> bool {
+ u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets())
}
/// Returns [`true`] if the address appears to be globally routable.
@@ -1164,9 +1170,6 @@
/// - link-local and unique local unicast addresses
/// - interface-, link-, realm-, admin- and site-local multicast addresses
///
- /// [`true`]: ../../std/primitive.bool.html
- /// [`false`]: ../../std/primitive.bool.html
- ///
/// # Examples
///
/// ```
@@ -1178,7 +1181,8 @@
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_global(), false);
/// assert_eq!(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1).is_global(), true);
/// ```
- pub fn is_global(&self) -> bool {
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ pub const fn is_global(&self) -> bool {
match self.multicast_scope() {
Some(Ipv6MulticastScope::Global) => true,
None => self.is_unicast_global(),
@@ -1192,8 +1196,6 @@
///
/// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193
///
- /// [`true`]: ../../std/primitive.bool.html
- ///
/// # Examples
///
/// ```
@@ -1204,7 +1206,8 @@
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false);
/// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true);
/// ```
- pub fn is_unique_local(&self) -> bool {
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ pub const fn is_unique_local(&self) -> bool {
(self.segments()[0] & 0xfe00) == 0xfc00
}
@@ -1225,8 +1228,6 @@
/// addresses such as `fe80:0:0:1::` or `fe81::` as unicast link-local addresses for example.
/// If you need a less strict validation use [`Ipv6Addr::is_unicast_link_local()`] instead.
///
- /// [`true`]: ../../std/primitive.bool.html
- ///
/// # Examples
///
/// ```
@@ -1259,7 +1260,8 @@
/// [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
/// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406
- pub fn is_unicast_link_local_strict(&self) -> bool {
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ pub const fn is_unicast_link_local_strict(&self) -> bool {
(self.segments()[0] & 0xffff) == 0xfe80
&& (self.segments()[1] & 0xffff) == 0
&& (self.segments()[2] & 0xffff) == 0
@@ -1284,8 +1286,6 @@
/// If you need a strict validation fully compliant with the RFC, use
/// [`Ipv6Addr::is_unicast_link_local_strict()`] instead.
///
- /// [`true`]: ../../std/primitive.bool.html
- ///
/// # Examples
///
/// ```
@@ -1316,7 +1316,8 @@
///
/// [IETF RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4
/// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406
- pub fn is_unicast_link_local(&self) -> bool {
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ pub const fn is_unicast_link_local(&self) -> bool {
(self.segments()[0] & 0xffc0) == 0xfe80
}
@@ -1331,7 +1332,6 @@
/// +----------+-------------------------+----------------------------+
/// ```
///
- /// [`true`]: ../../std/primitive.bool.html
/// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7
///
/// # Examples
@@ -1355,7 +1355,8 @@
/// addresses.
///
/// [RFC 3879]: https://tools.ietf.org/html/rfc3879
- pub fn is_unicast_site_local(&self) -> bool {
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ pub const fn is_unicast_site_local(&self) -> bool {
(self.segments()[0] & 0xffc0) == 0xfec0
}
@@ -1364,7 +1365,6 @@
///
/// This property is defined in [IETF RFC 3849].
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849
///
/// # Examples
@@ -1377,7 +1377,8 @@
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false);
/// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true);
/// ```
- pub fn is_documentation(&self) -> bool {
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ pub const fn is_documentation(&self) -> bool {
(self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8)
}
@@ -1399,7 +1400,6 @@
/// Global Unicast).
/// ```
///
- /// [`true`]: ../../std/primitive.bool.html
/// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7
///
/// # Examples
@@ -1412,7 +1412,8 @@
/// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false);
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true);
/// ```
- pub fn is_unicast_global(&self) -> bool {
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ pub const fn is_unicast_global(&self) -> bool {
!self.is_multicast()
&& !self.is_loopback()
&& !self.is_unicast_link_local()
@@ -1436,7 +1437,8 @@
/// );
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None);
/// ```
- pub fn multicast_scope(&self) -> Option<Ipv6MulticastScope> {
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ pub const fn multicast_scope(&self) -> Option<Ipv6MulticastScope> {
if self.is_multicast() {
match self.segments()[0] & 0x000f {
1 => Some(Ipv6MulticastScope::InterfaceLocal),
@@ -1457,7 +1459,6 @@
///
/// This property is defined by [IETF RFC 4291].
///
- /// [`true`]: ../../std/primitive.bool.html
/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
///
/// # Examples
@@ -1468,8 +1469,9 @@
/// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true);
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false);
/// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[stable(since = "1.7.0", feature = "ip_17")]
- pub fn is_multicast(&self) -> bool {
+ pub const fn is_multicast(&self) -> bool {
(self.segments()[0] & 0xff00) == 0xff00
}
@@ -1494,7 +1496,8 @@
/// Some(Ipv4Addr::new(192, 10, 2, 255)));
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None);
/// ```
- pub fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> {
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
+ pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> {
match self.octets() {
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => {
Some(Ipv4Addr::new(a, b, c, d))
@@ -1521,8 +1524,9 @@
/// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(),
/// Some(Ipv4Addr::new(0, 0, 0, 1)));
/// ```
+ #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn to_ipv4(&self) -> Option<Ipv4Addr> {
+ pub const fn to_ipv4(&self) -> Option<Ipv4Addr> {
if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() {
let [a, b] = ab.to_be_bytes();
let [c, d] = cd.to_be_bytes();
@@ -1895,862 +1899,3 @@
IpAddr::V6(Ipv6Addr::from(segments))
}
}
-
-// Tests for this module
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use crate::net::test::{sa4, sa6, tsa};
- use crate::net::*;
- use crate::str::FromStr;
-
- #[test]
- fn test_from_str_ipv4() {
- assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse());
- assert_eq!(Ok(Ipv4Addr::new(255, 255, 255, 255)), "255.255.255.255".parse());
- assert_eq!(Ok(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0".parse());
-
- // out of range
- let none: Option<Ipv4Addr> = "256.0.0.1".parse().ok();
- assert_eq!(None, none);
- // too short
- let none: Option<Ipv4Addr> = "255.0.0".parse().ok();
- assert_eq!(None, none);
- // too long
- let none: Option<Ipv4Addr> = "255.0.0.1.2".parse().ok();
- assert_eq!(None, none);
- // no number between dots
- let none: Option<Ipv4Addr> = "255.0..1".parse().ok();
- assert_eq!(None, none);
- }
-
- #[test]
- fn test_from_str_ipv6() {
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse());
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse());
-
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse());
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse());
-
- assert_eq!(
- Ok(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)),
- "2a02:6b8::11:11".parse()
- );
-
- // too long group
- let none: Option<Ipv6Addr> = "::00000".parse().ok();
- assert_eq!(None, none);
- // too short
- let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7".parse().ok();
- assert_eq!(None, none);
- // too long
- let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:8:9".parse().ok();
- assert_eq!(None, none);
- // triple colon
- let none: Option<Ipv6Addr> = "1:2:::6:7:8".parse().ok();
- assert_eq!(None, none);
- // two double colons
- let none: Option<Ipv6Addr> = "1:2::6::8".parse().ok();
- assert_eq!(None, none);
- // `::` indicating zero groups of zeros
- let none: Option<Ipv6Addr> = "1:2:3:4::5:6:7:8".parse().ok();
- assert_eq!(None, none);
- }
-
- #[test]
- fn test_from_str_ipv4_in_ipv6() {
- assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), "::192.0.2.33".parse());
- assert_eq!(
- Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)),
- "::FFFF:192.0.2.33".parse()
- );
- assert_eq!(
- Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)),
- "64:ff9b::192.0.2.33".parse()
- );
- assert_eq!(
- Ok(Ipv6Addr::new(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)),
- "2001:db8:122:c000:2:2100:192.0.2.33".parse()
- );
-
- // colon after v4
- let none: Option<Ipv4Addr> = "::127.0.0.1:".parse().ok();
- assert_eq!(None, none);
- // not enough groups
- let none: Option<Ipv6Addr> = "1.2.3.4.5:127.0.0.1".parse().ok();
- assert_eq!(None, none);
- // too many groups
- let none: Option<Ipv6Addr> = "1.2.3.4.5:6:7:127.0.0.1".parse().ok();
- assert_eq!(None, none);
- }
-
- #[test]
- fn test_from_str_socket_addr() {
- assert_eq!(Ok(sa4(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse());
- assert_eq!(
- Ok(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)),
- "77.88.21.11:80".parse()
- );
- assert_eq!(
- Ok(sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53)),
- "[2a02:6b8:0:1::1]:53".parse()
- );
- assert_eq!(
- Ok(SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0)),
- "[2a02:6b8:0:1::1]:53".parse()
- );
- assert_eq!(
- Ok(sa6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22)),
- "[::127.0.0.1]:22".parse()
- );
- assert_eq!(
- Ok(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22, 0, 0)),
- "[::127.0.0.1]:22".parse()
- );
-
- // without port
- let none: Option<SocketAddr> = "127.0.0.1".parse().ok();
- assert_eq!(None, none);
- // without port
- let none: Option<SocketAddr> = "127.0.0.1:".parse().ok();
- assert_eq!(None, none);
- // wrong brackets around v4
- let none: Option<SocketAddr> = "[127.0.0.1]:22".parse().ok();
- assert_eq!(None, none);
- // port out of range
- let none: Option<SocketAddr> = "127.0.0.1:123456".parse().ok();
- assert_eq!(None, none);
- }
-
- #[test]
- fn ipv4_addr_to_string() {
- assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_string(), "127.0.0.1");
- // Short address
- assert_eq!(Ipv4Addr::new(1, 1, 1, 1).to_string(), "1.1.1.1");
- // Long address
- assert_eq!(Ipv4Addr::new(127, 127, 127, 127).to_string(), "127.127.127.127");
-
- // Test padding
- assert_eq!(&format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 ");
- assert_eq!(&format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1");
- }
-
- #[test]
- fn ipv6_addr_to_string() {
- // ipv4-mapped address
- let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280);
- assert_eq!(a1.to_string(), "::ffff:192.0.2.128");
-
- // ipv4-compatible address
- let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280);
- assert_eq!(a1.to_string(), "::192.0.2.128");
-
- // v6 address with no zero segments
- assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f");
-
- // longest possible IPv6 length
- assert_eq!(
- Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888)
- .to_string(),
- "1111:2222:3333:4444:5555:6666:7777:8888"
- );
- // padding
- assert_eq!(
- &format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)),
- "1:2:3:4:5:6:7:8 "
- );
- assert_eq!(
- &format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)),
- " 1:2:3:4:5:6:7:8"
- );
-
- // reduce a single run of zeros
- assert_eq!(
- "ae::ffff:102:304",
- Ipv6Addr::new(0xae, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304).to_string()
- );
-
- // don't reduce just a single zero segment
- assert_eq!("1:2:3:4:5:6:0:8", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 8).to_string());
-
- // 'any' address
- assert_eq!("::", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).to_string());
-
- // loopback address
- assert_eq!("::1", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_string());
-
- // ends in zeros
- assert_eq!("1::", Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0).to_string());
-
- // two runs of zeros, second one is longer
- assert_eq!("1:0:0:4::8", Ipv6Addr::new(1, 0, 0, 4, 0, 0, 0, 8).to_string());
-
- // two runs of zeros, equal length
- assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string());
- }
-
- #[test]
- fn ipv4_to_ipv6() {
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678),
- Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_mapped()
- );
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678),
- Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_compatible()
- );
- }
-
- #[test]
- fn ipv6_to_ipv4_mapped() {
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4_mapped(),
- Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
- );
- assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4_mapped(), None);
- }
-
- #[test]
- fn ipv6_to_ipv4() {
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4(),
- Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
- );
- assert_eq!(
- Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4(),
- Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
- );
- assert_eq!(Ipv6Addr::new(0, 0, 1, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), None);
- }
-
- #[test]
- fn ip_properties() {
- macro_rules! ip {
- ($s:expr) => {
- IpAddr::from_str($s).unwrap()
- };
- }
-
- 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());
- }
- }};
- }
-
- 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() {
- macro_rules! ip {
- ($s:expr) => {
- Ipv4Addr::from_str($s).unwrap()
- };
- }
-
- 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() {
- 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);
- }
- }
- }
-
- 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]
- fn to_socket_addr_socketaddr() {
- let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 12345);
- assert_eq!(Ok(vec![a]), tsa(a));
- }
-
- #[test]
- fn test_ipv4_to_int() {
- let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44);
- assert_eq!(u32::from(a), 0x11223344);
- }
-
- #[test]
- fn test_int_to_ipv4() {
- let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44);
- assert_eq!(Ipv4Addr::from(0x11223344), a);
- }
-
- #[test]
- fn test_ipv6_to_int() {
- let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11);
- assert_eq!(u128::from(a), 0x112233445566778899aabbccddeeff11u128);
- }
-
- #[test]
- fn test_int_to_ipv6() {
- let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11);
- assert_eq!(Ipv6Addr::from(0x112233445566778899aabbccddeeff11u128), a);
- }
-
- #[test]
- fn ipv4_from_constructors() {
- assert_eq!(Ipv4Addr::LOCALHOST, Ipv4Addr::new(127, 0, 0, 1));
- assert!(Ipv4Addr::LOCALHOST.is_loopback());
- assert_eq!(Ipv4Addr::UNSPECIFIED, Ipv4Addr::new(0, 0, 0, 0));
- assert!(Ipv4Addr::UNSPECIFIED.is_unspecified());
- assert_eq!(Ipv4Addr::BROADCAST, Ipv4Addr::new(255, 255, 255, 255));
- assert!(Ipv4Addr::BROADCAST.is_broadcast());
- }
-
- #[test]
- fn ipv6_from_contructors() {
- assert_eq!(Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
- assert!(Ipv6Addr::LOCALHOST.is_loopback());
- assert_eq!(Ipv6Addr::UNSPECIFIED, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
- assert!(Ipv6Addr::UNSPECIFIED.is_unspecified());
- }
-
- #[test]
- fn ipv4_from_octets() {
- assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1))
- }
-
- #[test]
- fn ipv6_from_segments() {
- let from_u16s =
- Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]);
- let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff);
- assert_eq!(new, from_u16s);
- }
-
- #[test]
- fn ipv6_from_octets() {
- let from_u16s =
- Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]);
- let from_u8s = Ipv6Addr::from([
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd,
- 0xee, 0xff,
- ]);
- assert_eq!(from_u16s, from_u8s);
- }
-
- #[test]
- fn cmp() {
- let v41 = Ipv4Addr::new(100, 64, 3, 3);
- let v42 = Ipv4Addr::new(192, 0, 2, 2);
- let v61 = "2001:db8:f00::1002".parse::<Ipv6Addr>().unwrap();
- let v62 = "2001:db8:f00::2001".parse::<Ipv6Addr>().unwrap();
- assert!(v41 < v42);
- assert!(v61 < v62);
-
- assert_eq!(v41, IpAddr::V4(v41));
- assert_eq!(v61, IpAddr::V6(v61));
- assert!(v41 != IpAddr::V4(v42));
- assert!(v61 != IpAddr::V6(v62));
-
- assert!(v41 < IpAddr::V4(v42));
- assert!(v61 < IpAddr::V6(v62));
- assert!(IpAddr::V4(v41) < v42);
- assert!(IpAddr::V6(v61) < v62);
-
- assert!(v41 < IpAddr::V6(v61));
- assert!(IpAddr::V4(v41) < v61);
- }
-
- #[test]
- fn is_v4() {
- let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3));
- assert!(ip.is_ipv4());
- assert!(!ip.is_ipv6());
- }
-
- #[test]
- fn is_v6() {
- let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678));
- assert!(!ip.is_ipv4());
- assert!(ip.is_ipv6());
- }
-}
diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs
new file mode 100644
index 0000000..d9fbdd1
--- /dev/null
+++ b/library/std/src/net/ip/tests.rs
@@ -0,0 +1,939 @@
+use crate::net::test::{sa4, sa6, tsa};
+use crate::net::*;
+use crate::str::FromStr;
+
+#[test]
+fn test_from_str_ipv4() {
+ assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse());
+ assert_eq!(Ok(Ipv4Addr::new(255, 255, 255, 255)), "255.255.255.255".parse());
+ assert_eq!(Ok(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0".parse());
+
+ // out of range
+ let none: Option<Ipv4Addr> = "256.0.0.1".parse().ok();
+ assert_eq!(None, none);
+ // too short
+ let none: Option<Ipv4Addr> = "255.0.0".parse().ok();
+ assert_eq!(None, none);
+ // too long
+ let none: Option<Ipv4Addr> = "255.0.0.1.2".parse().ok();
+ assert_eq!(None, none);
+ // no number between dots
+ let none: Option<Ipv4Addr> = "255.0..1".parse().ok();
+ assert_eq!(None, none);
+}
+
+#[test]
+fn test_from_str_ipv6() {
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse());
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse());
+
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse());
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse());
+
+ assert_eq!(Ok(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), "2a02:6b8::11:11".parse());
+
+ // too long group
+ let none: Option<Ipv6Addr> = "::00000".parse().ok();
+ assert_eq!(None, none);
+ // too short
+ let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7".parse().ok();
+ assert_eq!(None, none);
+ // too long
+ let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:8:9".parse().ok();
+ assert_eq!(None, none);
+ // triple colon
+ let none: Option<Ipv6Addr> = "1:2:::6:7:8".parse().ok();
+ assert_eq!(None, none);
+ // two double colons
+ let none: Option<Ipv6Addr> = "1:2::6::8".parse().ok();
+ assert_eq!(None, none);
+ // `::` indicating zero groups of zeros
+ let none: Option<Ipv6Addr> = "1:2:3:4::5:6:7:8".parse().ok();
+ assert_eq!(None, none);
+}
+
+#[test]
+fn test_from_str_ipv4_in_ipv6() {
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), "::192.0.2.33".parse());
+ assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), "::FFFF:192.0.2.33".parse());
+ assert_eq!(
+ Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)),
+ "64:ff9b::192.0.2.33".parse()
+ );
+ assert_eq!(
+ Ok(Ipv6Addr::new(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)),
+ "2001:db8:122:c000:2:2100:192.0.2.33".parse()
+ );
+
+ // colon after v4
+ let none: Option<Ipv4Addr> = "::127.0.0.1:".parse().ok();
+ assert_eq!(None, none);
+ // not enough groups
+ let none: Option<Ipv6Addr> = "1.2.3.4.5:127.0.0.1".parse().ok();
+ assert_eq!(None, none);
+ // too many groups
+ let none: Option<Ipv6Addr> = "1.2.3.4.5:6:7:127.0.0.1".parse().ok();
+ assert_eq!(None, none);
+}
+
+#[test]
+fn test_from_str_socket_addr() {
+ assert_eq!(Ok(sa4(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse());
+ assert_eq!(Ok(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse());
+ assert_eq!(
+ Ok(sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53)),
+ "[2a02:6b8:0:1::1]:53".parse()
+ );
+ assert_eq!(
+ Ok(SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0)),
+ "[2a02:6b8:0:1::1]:53".parse()
+ );
+ assert_eq!(Ok(sa6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22)), "[::127.0.0.1]:22".parse());
+ assert_eq!(
+ Ok(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22, 0, 0)),
+ "[::127.0.0.1]:22".parse()
+ );
+
+ // without port
+ let none: Option<SocketAddr> = "127.0.0.1".parse().ok();
+ assert_eq!(None, none);
+ // without port
+ let none: Option<SocketAddr> = "127.0.0.1:".parse().ok();
+ assert_eq!(None, none);
+ // wrong brackets around v4
+ let none: Option<SocketAddr> = "[127.0.0.1]:22".parse().ok();
+ assert_eq!(None, none);
+ // port out of range
+ let none: Option<SocketAddr> = "127.0.0.1:123456".parse().ok();
+ assert_eq!(None, none);
+}
+
+#[test]
+fn ipv4_addr_to_string() {
+ assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_string(), "127.0.0.1");
+ // Short address
+ assert_eq!(Ipv4Addr::new(1, 1, 1, 1).to_string(), "1.1.1.1");
+ // Long address
+ assert_eq!(Ipv4Addr::new(127, 127, 127, 127).to_string(), "127.127.127.127");
+
+ // Test padding
+ assert_eq!(&format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 ");
+ assert_eq!(&format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1");
+}
+
+#[test]
+fn ipv6_addr_to_string() {
+ // ipv4-mapped address
+ let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280);
+ assert_eq!(a1.to_string(), "::ffff:192.0.2.128");
+
+ // ipv4-compatible address
+ let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280);
+ assert_eq!(a1.to_string(), "::192.0.2.128");
+
+ // v6 address with no zero segments
+ assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f");
+
+ // longest possible IPv6 length
+ assert_eq!(
+ Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888).to_string(),
+ "1111:2222:3333:4444:5555:6666:7777:8888"
+ );
+ // padding
+ assert_eq!(&format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), "1:2:3:4:5:6:7:8 ");
+ assert_eq!(&format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), " 1:2:3:4:5:6:7:8");
+
+ // reduce a single run of zeros
+ assert_eq!(
+ "ae::ffff:102:304",
+ Ipv6Addr::new(0xae, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304).to_string()
+ );
+
+ // don't reduce just a single zero segment
+ assert_eq!("1:2:3:4:5:6:0:8", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 8).to_string());
+
+ // 'any' address
+ assert_eq!("::", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).to_string());
+
+ // loopback address
+ assert_eq!("::1", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_string());
+
+ // ends in zeros
+ assert_eq!("1::", Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0).to_string());
+
+ // two runs of zeros, second one is longer
+ assert_eq!("1:0:0:4::8", Ipv6Addr::new(1, 0, 0, 4, 0, 0, 0, 8).to_string());
+
+ // two runs of zeros, equal length
+ assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string());
+}
+
+#[test]
+fn ipv4_to_ipv6() {
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678),
+ Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_mapped()
+ );
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678),
+ Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_compatible()
+ );
+}
+
+#[test]
+fn ipv6_to_ipv4_mapped() {
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4_mapped(),
+ Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
+ );
+ assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4_mapped(), None);
+}
+
+#[test]
+fn ipv6_to_ipv4() {
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4(),
+ Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
+ );
+ assert_eq!(
+ Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4(),
+ Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78))
+ );
+ assert_eq!(Ipv6Addr::new(0, 0, 1, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), None);
+}
+
+#[test]
+fn ip_properties() {
+ macro_rules! ip {
+ ($s:expr) => {
+ IpAddr::from_str($s).unwrap()
+ };
+ }
+
+ 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());
+ }
+ }};
+ }
+
+ 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() {
+ macro_rules! ip {
+ ($s:expr) => {
+ Ipv4Addr::from_str($s).unwrap()
+ };
+ }
+
+ 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() {
+ 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);
+ }
+ }
+ }
+
+ 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]
+fn to_socket_addr_socketaddr() {
+ let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 12345);
+ assert_eq!(Ok(vec![a]), tsa(a));
+}
+
+#[test]
+fn test_ipv4_to_int() {
+ let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44);
+ assert_eq!(u32::from(a), 0x11223344);
+}
+
+#[test]
+fn test_int_to_ipv4() {
+ let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44);
+ assert_eq!(Ipv4Addr::from(0x11223344), a);
+}
+
+#[test]
+fn test_ipv6_to_int() {
+ let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11);
+ assert_eq!(u128::from(a), 0x112233445566778899aabbccddeeff11u128);
+}
+
+#[test]
+fn test_int_to_ipv6() {
+ let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11);
+ assert_eq!(Ipv6Addr::from(0x112233445566778899aabbccddeeff11u128), a);
+}
+
+#[test]
+fn ipv4_from_constructors() {
+ assert_eq!(Ipv4Addr::LOCALHOST, Ipv4Addr::new(127, 0, 0, 1));
+ assert!(Ipv4Addr::LOCALHOST.is_loopback());
+ assert_eq!(Ipv4Addr::UNSPECIFIED, Ipv4Addr::new(0, 0, 0, 0));
+ assert!(Ipv4Addr::UNSPECIFIED.is_unspecified());
+ assert_eq!(Ipv4Addr::BROADCAST, Ipv4Addr::new(255, 255, 255, 255));
+ assert!(Ipv4Addr::BROADCAST.is_broadcast());
+}
+
+#[test]
+fn ipv6_from_contructors() {
+ assert_eq!(Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
+ assert!(Ipv6Addr::LOCALHOST.is_loopback());
+ assert_eq!(Ipv6Addr::UNSPECIFIED, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
+ assert!(Ipv6Addr::UNSPECIFIED.is_unspecified());
+}
+
+#[test]
+fn ipv4_from_octets() {
+ assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1))
+}
+
+#[test]
+fn ipv6_from_segments() {
+ let from_u16s =
+ Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]);
+ let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff);
+ assert_eq!(new, from_u16s);
+}
+
+#[test]
+fn ipv6_from_octets() {
+ let from_u16s =
+ Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]);
+ let from_u8s = Ipv6Addr::from([
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
+ 0xff,
+ ]);
+ assert_eq!(from_u16s, from_u8s);
+}
+
+#[test]
+fn cmp() {
+ let v41 = Ipv4Addr::new(100, 64, 3, 3);
+ let v42 = Ipv4Addr::new(192, 0, 2, 2);
+ let v61 = "2001:db8:f00::1002".parse::<Ipv6Addr>().unwrap();
+ let v62 = "2001:db8:f00::2001".parse::<Ipv6Addr>().unwrap();
+ assert!(v41 < v42);
+ assert!(v61 < v62);
+
+ assert_eq!(v41, IpAddr::V4(v41));
+ assert_eq!(v61, IpAddr::V6(v61));
+ assert!(v41 != IpAddr::V4(v42));
+ assert!(v61 != IpAddr::V6(v62));
+
+ assert!(v41 < IpAddr::V4(v42));
+ assert!(v61 < IpAddr::V6(v62));
+ assert!(IpAddr::V4(v41) < v42);
+ assert!(IpAddr::V6(v61) < v62);
+
+ assert!(v41 < IpAddr::V6(v61));
+ assert!(IpAddr::V4(v41) < v61);
+}
+
+#[test]
+fn is_v4() {
+ let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3));
+ assert!(ip.is_ipv4());
+ assert!(!ip.is_ipv6());
+}
+
+#[test]
+fn is_v6() {
+ let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678));
+ assert!(!ip.is_ipv4());
+ assert!(ip.is_ipv6());
+}
+
+#[test]
+fn ipv4_const() {
+ // test that the methods of `Ipv4Addr` are usable in a const context
+
+ const IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
+ assert_eq!(IP_ADDRESS, Ipv4Addr::LOCALHOST);
+
+ const OCTETS: [u8; 4] = IP_ADDRESS.octets();
+ assert_eq!(OCTETS, [127, 0, 0, 1]);
+
+ const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified();
+ assert!(!IS_UNSPECIFIED);
+
+ const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback();
+ assert!(IS_LOOPBACK);
+
+ const IS_PRIVATE: bool = IP_ADDRESS.is_private();
+ assert!(!IS_PRIVATE);
+
+ const IS_LINK_LOCAL: bool = IP_ADDRESS.is_link_local();
+ assert!(!IS_LINK_LOCAL);
+
+ const IS_GLOBAL: bool = IP_ADDRESS.is_global();
+ assert!(!IS_GLOBAL);
+
+ const IS_SHARED: bool = IP_ADDRESS.is_shared();
+ assert!(!IS_SHARED);
+
+ const IS_IETF_PROTOCOL_ASSIGNMENT: bool = IP_ADDRESS.is_ietf_protocol_assignment();
+ assert!(!IS_IETF_PROTOCOL_ASSIGNMENT);
+
+ const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking();
+ assert!(!IS_BENCHMARKING);
+
+ const IS_RESERVED: bool = IP_ADDRESS.is_reserved();
+ assert!(!IS_RESERVED);
+
+ const IS_MULTICAST: bool = IP_ADDRESS.is_multicast();
+ assert!(!IS_MULTICAST);
+
+ const IS_BROADCAST: bool = IP_ADDRESS.is_broadcast();
+ assert!(!IS_BROADCAST);
+
+ const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation();
+ assert!(!IS_DOCUMENTATION);
+
+ const IP_V6_COMPATIBLE: Ipv6Addr = IP_ADDRESS.to_ipv6_compatible();
+ assert_eq!(
+ IP_V6_COMPATIBLE,
+ Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1])
+ );
+
+ const IP_V6_MAPPED: Ipv6Addr = IP_ADDRESS.to_ipv6_mapped();
+ assert_eq!(
+ IP_V6_MAPPED,
+ Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1])
+ );
+}
+
+#[test]
+fn ipv6_const() {
+ // test that the methods of `Ipv6Addr` are usable in a const context
+
+ const IP_ADDRESS: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
+ assert_eq!(IP_ADDRESS, Ipv6Addr::LOCALHOST);
+
+ const SEGMENTS: [u16; 8] = IP_ADDRESS.segments();
+ assert_eq!(SEGMENTS, [0, 0, 0, 0, 0, 0, 0, 1]);
+
+ const OCTETS: [u8; 16] = IP_ADDRESS.octets();
+ assert_eq!(OCTETS, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
+
+ const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified();
+ assert!(!IS_UNSPECIFIED);
+
+ const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback();
+ assert!(IS_LOOPBACK);
+
+ const IS_GLOBAL: bool = IP_ADDRESS.is_global();
+ assert!(!IS_GLOBAL);
+
+ const IS_UNIQUE_LOCAL: bool = IP_ADDRESS.is_unique_local();
+ assert!(!IS_UNIQUE_LOCAL);
+
+ const IS_UNICAST_LINK_LOCAL_STRICT: bool = IP_ADDRESS.is_unicast_link_local_strict();
+ assert!(!IS_UNICAST_LINK_LOCAL_STRICT);
+
+ const IS_UNICAST_LINK_LOCAL: bool = IP_ADDRESS.is_unicast_link_local();
+ assert!(!IS_UNICAST_LINK_LOCAL);
+
+ const IS_UNICAST_SITE_LOCAL: bool = IP_ADDRESS.is_unicast_site_local();
+ assert!(!IS_UNICAST_SITE_LOCAL);
+
+ const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation();
+ assert!(!IS_DOCUMENTATION);
+
+ const IS_UNICAST_GLOBAL: bool = IP_ADDRESS.is_unicast_global();
+ assert!(!IS_UNICAST_GLOBAL);
+
+ const MULTICAST_SCOPE: Option<Ipv6MulticastScope> = IP_ADDRESS.multicast_scope();
+ assert_eq!(MULTICAST_SCOPE, None);
+
+ const IS_MULTICAST: bool = IP_ADDRESS.is_multicast();
+ assert!(!IS_MULTICAST);
+
+ const IP_V4: Option<Ipv4Addr> = IP_ADDRESS.to_ipv4();
+ assert_eq!(IP_V4.unwrap(), Ipv4Addr::new(0, 0, 0, 1));
+}
+
+#[test]
+fn ip_const() {
+ // test that the methods of `IpAddr` are usable in a const context
+
+ const IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
+
+ const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified();
+ assert!(!IS_UNSPECIFIED);
+
+ const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback();
+ assert!(IS_LOOPBACK);
+
+ const IS_GLOBAL: bool = IP_ADDRESS.is_global();
+ assert!(!IS_GLOBAL);
+
+ const IS_MULTICAST: bool = IP_ADDRESS.is_multicast();
+ assert!(!IS_MULTICAST);
+}
diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs
index a425aca..0570a7c 100644
--- a/library/std/src/net/parser.rs
+++ b/library/std/src/net/parser.rs
@@ -3,6 +3,9 @@
//! This module is "publicly exported" through the `FromStr` implementations
//! below.
+#[cfg(test)]
+mod tests;
+
use crate::error::Error;
use crate::fmt;
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
@@ -321,146 +324,3 @@
"invalid IP address syntax"
}
}
-
-#[cfg(test)]
-mod tests {
- // FIXME: These tests are all excellent candidates for AFL fuzz testing
- use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
- use crate::str::FromStr;
-
- const PORT: u16 = 8080;
-
- const IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 0, 1);
- const IPV4_STR: &str = "192.168.0.1";
- const IPV4_STR_PORT: &str = "192.168.0.1:8080";
-
- const IPV6: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc0a8, 0x1);
- const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1";
- const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1";
- const IPV6_STR_V4: &str = "2001:db8::192.168.0.1";
- const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080";
-
- #[test]
- fn parse_ipv4() {
- let result: Ipv4Addr = IPV4_STR.parse().unwrap();
- assert_eq!(result, IPV4);
-
- assert!(Ipv4Addr::from_str(IPV4_STR_PORT).is_err());
- assert!(Ipv4Addr::from_str(IPV6_STR_FULL).is_err());
- assert!(Ipv4Addr::from_str(IPV6_STR_COMPRESS).is_err());
- assert!(Ipv4Addr::from_str(IPV6_STR_V4).is_err());
- assert!(Ipv4Addr::from_str(IPV6_STR_PORT).is_err());
- }
-
- #[test]
- fn parse_ipv6() {
- let result: Ipv6Addr = IPV6_STR_FULL.parse().unwrap();
- assert_eq!(result, IPV6);
-
- let result: Ipv6Addr = IPV6_STR_COMPRESS.parse().unwrap();
- assert_eq!(result, IPV6);
-
- let result: Ipv6Addr = IPV6_STR_V4.parse().unwrap();
- assert_eq!(result, IPV6);
-
- assert!(Ipv6Addr::from_str(IPV4_STR).is_err());
- assert!(Ipv6Addr::from_str(IPV4_STR_PORT).is_err());
- assert!(Ipv6Addr::from_str(IPV6_STR_PORT).is_err());
- }
-
- #[test]
- fn parse_ip() {
- let result: IpAddr = IPV4_STR.parse().unwrap();
- assert_eq!(result, IpAddr::from(IPV4));
-
- let result: IpAddr = IPV6_STR_FULL.parse().unwrap();
- assert_eq!(result, IpAddr::from(IPV6));
-
- let result: IpAddr = IPV6_STR_COMPRESS.parse().unwrap();
- assert_eq!(result, IpAddr::from(IPV6));
-
- let result: IpAddr = IPV6_STR_V4.parse().unwrap();
- assert_eq!(result, IpAddr::from(IPV6));
-
- assert!(IpAddr::from_str(IPV4_STR_PORT).is_err());
- assert!(IpAddr::from_str(IPV6_STR_PORT).is_err());
- }
-
- #[test]
- fn parse_socket_v4() {
- let result: SocketAddrV4 = IPV4_STR_PORT.parse().unwrap();
- assert_eq!(result, SocketAddrV4::new(IPV4, PORT));
-
- assert!(SocketAddrV4::from_str(IPV4_STR).is_err());
- assert!(SocketAddrV4::from_str(IPV6_STR_FULL).is_err());
- assert!(SocketAddrV4::from_str(IPV6_STR_COMPRESS).is_err());
- assert!(SocketAddrV4::from_str(IPV6_STR_V4).is_err());
- assert!(SocketAddrV4::from_str(IPV6_STR_PORT).is_err());
- }
-
- #[test]
- fn parse_socket_v6() {
- let result: SocketAddrV6 = IPV6_STR_PORT.parse().unwrap();
- assert_eq!(result, SocketAddrV6::new(IPV6, PORT, 0, 0));
-
- assert!(SocketAddrV6::from_str(IPV4_STR).is_err());
- assert!(SocketAddrV6::from_str(IPV4_STR_PORT).is_err());
- assert!(SocketAddrV6::from_str(IPV6_STR_FULL).is_err());
- assert!(SocketAddrV6::from_str(IPV6_STR_COMPRESS).is_err());
- assert!(SocketAddrV6::from_str(IPV6_STR_V4).is_err());
- }
-
- #[test]
- fn parse_socket() {
- let result: SocketAddr = IPV4_STR_PORT.parse().unwrap();
- assert_eq!(result, SocketAddr::from((IPV4, PORT)));
-
- let result: SocketAddr = IPV6_STR_PORT.parse().unwrap();
- assert_eq!(result, SocketAddr::from((IPV6, PORT)));
-
- assert!(SocketAddr::from_str(IPV4_STR).is_err());
- assert!(SocketAddr::from_str(IPV6_STR_FULL).is_err());
- assert!(SocketAddr::from_str(IPV6_STR_COMPRESS).is_err());
- assert!(SocketAddr::from_str(IPV6_STR_V4).is_err());
- }
-
- #[test]
- fn ipv6_corner_cases() {
- let result: Ipv6Addr = "1::".parse().unwrap();
- assert_eq!(result, Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0));
-
- let result: Ipv6Addr = "1:1::".parse().unwrap();
- assert_eq!(result, Ipv6Addr::new(1, 1, 0, 0, 0, 0, 0, 0));
-
- let result: Ipv6Addr = "::1".parse().unwrap();
- assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
-
- let result: Ipv6Addr = "::1:1".parse().unwrap();
- assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 1, 1));
-
- let result: Ipv6Addr = "::".parse().unwrap();
- assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
-
- let result: Ipv6Addr = "::192.168.0.1".parse().unwrap();
- assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc0a8, 0x1));
-
- let result: Ipv6Addr = "::1:192.168.0.1".parse().unwrap();
- assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 1, 0xc0a8, 0x1));
-
- let result: Ipv6Addr = "1:1:1:1:1:1:192.168.0.1".parse().unwrap();
- assert_eq!(result, Ipv6Addr::new(1, 1, 1, 1, 1, 1, 0xc0a8, 0x1));
- }
-
- // Things that might not seem like failures but are
- #[test]
- fn ipv6_corner_failures() {
- // No IP address before the ::
- assert!(Ipv6Addr::from_str("1:192.168.0.1::").is_err());
-
- // :: must have at least 1 set of zeroes
- assert!(Ipv6Addr::from_str("1:1:1:1::1:1:1:1").is_err());
-
- // Need brackets for a port
- assert!(SocketAddrV6::from_str("1:1:1:1:1:1:1:1:8080").is_err());
- }
-}
diff --git a/library/std/src/net/parser/tests.rs b/library/std/src/net/parser/tests.rs
new file mode 100644
index 0000000..ecf5a78
--- /dev/null
+++ b/library/std/src/net/parser/tests.rs
@@ -0,0 +1,139 @@
+// FIXME: These tests are all excellent candidates for AFL fuzz testing
+use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
+use crate::str::FromStr;
+
+const PORT: u16 = 8080;
+
+const IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 0, 1);
+const IPV4_STR: &str = "192.168.0.1";
+const IPV4_STR_PORT: &str = "192.168.0.1:8080";
+
+const IPV6: Ipv6Addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc0a8, 0x1);
+const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1";
+const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1";
+const IPV6_STR_V4: &str = "2001:db8::192.168.0.1";
+const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080";
+
+#[test]
+fn parse_ipv4() {
+ let result: Ipv4Addr = IPV4_STR.parse().unwrap();
+ assert_eq!(result, IPV4);
+
+ assert!(Ipv4Addr::from_str(IPV4_STR_PORT).is_err());
+ assert!(Ipv4Addr::from_str(IPV6_STR_FULL).is_err());
+ assert!(Ipv4Addr::from_str(IPV6_STR_COMPRESS).is_err());
+ assert!(Ipv4Addr::from_str(IPV6_STR_V4).is_err());
+ assert!(Ipv4Addr::from_str(IPV6_STR_PORT).is_err());
+}
+
+#[test]
+fn parse_ipv6() {
+ let result: Ipv6Addr = IPV6_STR_FULL.parse().unwrap();
+ assert_eq!(result, IPV6);
+
+ let result: Ipv6Addr = IPV6_STR_COMPRESS.parse().unwrap();
+ assert_eq!(result, IPV6);
+
+ let result: Ipv6Addr = IPV6_STR_V4.parse().unwrap();
+ assert_eq!(result, IPV6);
+
+ assert!(Ipv6Addr::from_str(IPV4_STR).is_err());
+ assert!(Ipv6Addr::from_str(IPV4_STR_PORT).is_err());
+ assert!(Ipv6Addr::from_str(IPV6_STR_PORT).is_err());
+}
+
+#[test]
+fn parse_ip() {
+ let result: IpAddr = IPV4_STR.parse().unwrap();
+ assert_eq!(result, IpAddr::from(IPV4));
+
+ let result: IpAddr = IPV6_STR_FULL.parse().unwrap();
+ assert_eq!(result, IpAddr::from(IPV6));
+
+ let result: IpAddr = IPV6_STR_COMPRESS.parse().unwrap();
+ assert_eq!(result, IpAddr::from(IPV6));
+
+ let result: IpAddr = IPV6_STR_V4.parse().unwrap();
+ assert_eq!(result, IpAddr::from(IPV6));
+
+ assert!(IpAddr::from_str(IPV4_STR_PORT).is_err());
+ assert!(IpAddr::from_str(IPV6_STR_PORT).is_err());
+}
+
+#[test]
+fn parse_socket_v4() {
+ let result: SocketAddrV4 = IPV4_STR_PORT.parse().unwrap();
+ assert_eq!(result, SocketAddrV4::new(IPV4, PORT));
+
+ assert!(SocketAddrV4::from_str(IPV4_STR).is_err());
+ assert!(SocketAddrV4::from_str(IPV6_STR_FULL).is_err());
+ assert!(SocketAddrV4::from_str(IPV6_STR_COMPRESS).is_err());
+ assert!(SocketAddrV4::from_str(IPV6_STR_V4).is_err());
+ assert!(SocketAddrV4::from_str(IPV6_STR_PORT).is_err());
+}
+
+#[test]
+fn parse_socket_v6() {
+ let result: SocketAddrV6 = IPV6_STR_PORT.parse().unwrap();
+ assert_eq!(result, SocketAddrV6::new(IPV6, PORT, 0, 0));
+
+ assert!(SocketAddrV6::from_str(IPV4_STR).is_err());
+ assert!(SocketAddrV6::from_str(IPV4_STR_PORT).is_err());
+ assert!(SocketAddrV6::from_str(IPV6_STR_FULL).is_err());
+ assert!(SocketAddrV6::from_str(IPV6_STR_COMPRESS).is_err());
+ assert!(SocketAddrV6::from_str(IPV6_STR_V4).is_err());
+}
+
+#[test]
+fn parse_socket() {
+ let result: SocketAddr = IPV4_STR_PORT.parse().unwrap();
+ assert_eq!(result, SocketAddr::from((IPV4, PORT)));
+
+ let result: SocketAddr = IPV6_STR_PORT.parse().unwrap();
+ assert_eq!(result, SocketAddr::from((IPV6, PORT)));
+
+ assert!(SocketAddr::from_str(IPV4_STR).is_err());
+ assert!(SocketAddr::from_str(IPV6_STR_FULL).is_err());
+ assert!(SocketAddr::from_str(IPV6_STR_COMPRESS).is_err());
+ assert!(SocketAddr::from_str(IPV6_STR_V4).is_err());
+}
+
+#[test]
+fn ipv6_corner_cases() {
+ let result: Ipv6Addr = "1::".parse().unwrap();
+ assert_eq!(result, Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0));
+
+ let result: Ipv6Addr = "1:1::".parse().unwrap();
+ assert_eq!(result, Ipv6Addr::new(1, 1, 0, 0, 0, 0, 0, 0));
+
+ let result: Ipv6Addr = "::1".parse().unwrap();
+ assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
+
+ let result: Ipv6Addr = "::1:1".parse().unwrap();
+ assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 1, 1));
+
+ let result: Ipv6Addr = "::".parse().unwrap();
+ assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0));
+
+ let result: Ipv6Addr = "::192.168.0.1".parse().unwrap();
+ assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc0a8, 0x1));
+
+ let result: Ipv6Addr = "::1:192.168.0.1".parse().unwrap();
+ assert_eq!(result, Ipv6Addr::new(0, 0, 0, 0, 0, 1, 0xc0a8, 0x1));
+
+ let result: Ipv6Addr = "1:1:1:1:1:1:192.168.0.1".parse().unwrap();
+ assert_eq!(result, Ipv6Addr::new(1, 1, 1, 1, 1, 1, 0xc0a8, 0x1));
+}
+
+// Things that might not seem like failures but are
+#[test]
+fn ipv6_corner_failures() {
+ // No IP address before the ::
+ assert!(Ipv6Addr::from_str("1:192.168.0.1::").is_err());
+
+ // :: must have at least 1 set of zeroes
+ assert!(Ipv6Addr::from_str("1:1:1:1::1:1:1:1").is_err());
+
+ // Need brackets for a port
+ assert!(SocketAddrV6::from_str("1:1:1:1:1:1:1:1:8080").is_err());
+}
diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs
index a76c9c4..58c6343 100644
--- a/library/std/src/net/tcp.rs
+++ b/library/std/src/net/tcp.rs
@@ -1,4 +1,8 @@
#![deny(unsafe_op_in_unsafe_fn)]
+
+#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))]
+mod tests;
+
use crate::io::prelude::*;
use crate::fmt;
@@ -936,869 +940,3 @@
self.0.fmt(f)
}
}
-
-#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))]
-mod tests {
- use crate::fmt;
- use crate::io::prelude::*;
- use crate::io::{ErrorKind, IoSlice, IoSliceMut};
- use crate::net::test::{next_test_ip4, next_test_ip6};
- use crate::net::*;
- use crate::sync::mpsc::channel;
- use crate::thread;
- use crate::time::{Duration, Instant};
-
- fn each_ip(f: &mut dyn FnMut(SocketAddr)) {
- f(next_test_ip4());
- f(next_test_ip6());
- }
-
- macro_rules! t {
- ($e:expr) => {
- match $e {
- Ok(t) => t,
- Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
- }
- };
- }
-
- #[test]
- fn bind_error() {
- match TcpListener::bind("1.1.1.1:9999") {
- Ok(..) => panic!(),
- Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable),
- }
- }
-
- #[test]
- fn connect_error() {
- match TcpStream::connect("0.0.0.0:1") {
- Ok(..) => panic!(),
- Err(e) => assert!(
- e.kind() == ErrorKind::ConnectionRefused
- || e.kind() == ErrorKind::InvalidInput
- || e.kind() == ErrorKind::AddrInUse
- || e.kind() == ErrorKind::AddrNotAvailable,
- "bad error: {} {:?}",
- e,
- e.kind()
- ),
- }
- }
-
- #[test]
- fn listen_localhost() {
- let socket_addr = next_test_ip4();
- let listener = t!(TcpListener::bind(&socket_addr));
-
- let _t = thread::spawn(move || {
- let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port())));
- t!(stream.write(&[144]));
- });
-
- let mut stream = t!(listener.accept()).0;
- let mut buf = [0];
- t!(stream.read(&mut buf));
- assert!(buf[0] == 144);
- }
-
- #[test]
- fn connect_loopback() {
- each_ip(&mut |addr| {
- let acceptor = t!(TcpListener::bind(&addr));
-
- let _t = thread::spawn(move || {
- let host = match addr {
- SocketAddr::V4(..) => "127.0.0.1",
- SocketAddr::V6(..) => "::1",
- };
- let mut stream = t!(TcpStream::connect(&(host, addr.port())));
- t!(stream.write(&[66]));
- });
-
- let mut stream = t!(acceptor.accept()).0;
- let mut buf = [0];
- t!(stream.read(&mut buf));
- assert!(buf[0] == 66);
- })
- }
-
- #[test]
- fn smoke_test() {
- each_ip(&mut |addr| {
- let acceptor = t!(TcpListener::bind(&addr));
-
- let (tx, rx) = channel();
- let _t = thread::spawn(move || {
- let mut stream = t!(TcpStream::connect(&addr));
- t!(stream.write(&[99]));
- tx.send(t!(stream.local_addr())).unwrap();
- });
-
- let (mut stream, addr) = t!(acceptor.accept());
- let mut buf = [0];
- t!(stream.read(&mut buf));
- assert!(buf[0] == 99);
- assert_eq!(addr, t!(rx.recv()));
- })
- }
-
- #[test]
- fn read_eof() {
- each_ip(&mut |addr| {
- let acceptor = t!(TcpListener::bind(&addr));
-
- let _t = thread::spawn(move || {
- let _stream = t!(TcpStream::connect(&addr));
- // Close
- });
-
- let mut stream = t!(acceptor.accept()).0;
- let mut buf = [0];
- let nread = t!(stream.read(&mut buf));
- assert_eq!(nread, 0);
- let nread = t!(stream.read(&mut buf));
- assert_eq!(nread, 0);
- })
- }
-
- #[test]
- fn write_close() {
- each_ip(&mut |addr| {
- let acceptor = t!(TcpListener::bind(&addr));
-
- let (tx, rx) = channel();
- let _t = thread::spawn(move || {
- drop(t!(TcpStream::connect(&addr)));
- tx.send(()).unwrap();
- });
-
- let mut stream = t!(acceptor.accept()).0;
- rx.recv().unwrap();
- let buf = [0];
- match stream.write(&buf) {
- Ok(..) => {}
- Err(e) => {
- assert!(
- e.kind() == ErrorKind::ConnectionReset
- || e.kind() == ErrorKind::BrokenPipe
- || e.kind() == ErrorKind::ConnectionAborted,
- "unknown error: {}",
- e
- );
- }
- }
- })
- }
-
- #[test]
- fn multiple_connect_serial() {
- each_ip(&mut |addr| {
- let max = 10;
- let acceptor = t!(TcpListener::bind(&addr));
-
- let _t = thread::spawn(move || {
- for _ in 0..max {
- let mut stream = t!(TcpStream::connect(&addr));
- t!(stream.write(&[99]));
- }
- });
-
- for stream in acceptor.incoming().take(max) {
- let mut stream = t!(stream);
- let mut buf = [0];
- t!(stream.read(&mut buf));
- assert_eq!(buf[0], 99);
- }
- })
- }
-
- #[test]
- fn multiple_connect_interleaved_greedy_schedule() {
- const MAX: usize = 10;
- each_ip(&mut |addr| {
- let acceptor = t!(TcpListener::bind(&addr));
-
- let _t = thread::spawn(move || {
- let acceptor = acceptor;
- for (i, stream) in acceptor.incoming().enumerate().take(MAX) {
- // Start another thread to handle the connection
- let _t = thread::spawn(move || {
- let mut stream = t!(stream);
- let mut buf = [0];
- t!(stream.read(&mut buf));
- assert!(buf[0] == i as u8);
- });
- }
- });
-
- connect(0, addr);
- });
-
- fn connect(i: usize, addr: SocketAddr) {
- if i == MAX {
- return;
- }
-
- let t = thread::spawn(move || {
- let mut stream = t!(TcpStream::connect(&addr));
- // Connect again before writing
- connect(i + 1, addr);
- t!(stream.write(&[i as u8]));
- });
- t.join().ok().expect("thread panicked");
- }
- }
-
- #[test]
- fn multiple_connect_interleaved_lazy_schedule() {
- const MAX: usize = 10;
- each_ip(&mut |addr| {
- let acceptor = t!(TcpListener::bind(&addr));
-
- let _t = thread::spawn(move || {
- for stream in acceptor.incoming().take(MAX) {
- // Start another thread to handle the connection
- let _t = thread::spawn(move || {
- let mut stream = t!(stream);
- let mut buf = [0];
- t!(stream.read(&mut buf));
- assert!(buf[0] == 99);
- });
- }
- });
-
- connect(0, addr);
- });
-
- fn connect(i: usize, addr: SocketAddr) {
- if i == MAX {
- return;
- }
-
- let t = thread::spawn(move || {
- let mut stream = t!(TcpStream::connect(&addr));
- connect(i + 1, addr);
- t!(stream.write(&[99]));
- });
- t.join().ok().expect("thread panicked");
- }
- }
-
- #[test]
- fn socket_and_peer_name() {
- each_ip(&mut |addr| {
- let listener = t!(TcpListener::bind(&addr));
- let so_name = t!(listener.local_addr());
- assert_eq!(addr, so_name);
- let _t = thread::spawn(move || {
- t!(listener.accept());
- });
-
- let stream = t!(TcpStream::connect(&addr));
- assert_eq!(addr, t!(stream.peer_addr()));
- })
- }
-
- #[test]
- fn partial_read() {
- each_ip(&mut |addr| {
- let (tx, rx) = channel();
- let srv = t!(TcpListener::bind(&addr));
- let _t = thread::spawn(move || {
- let mut cl = t!(srv.accept()).0;
- cl.write(&[10]).unwrap();
- let mut b = [0];
- t!(cl.read(&mut b));
- tx.send(()).unwrap();
- });
-
- let mut c = t!(TcpStream::connect(&addr));
- let mut b = [0; 10];
- assert_eq!(c.read(&mut b).unwrap(), 1);
- t!(c.write(&[1]));
- rx.recv().unwrap();
- })
- }
-
- #[test]
- fn read_vectored() {
- each_ip(&mut |addr| {
- let srv = t!(TcpListener::bind(&addr));
- let mut s1 = t!(TcpStream::connect(&addr));
- let mut s2 = t!(srv.accept()).0;
-
- let len = s1.write(&[10, 11, 12]).unwrap();
- assert_eq!(len, 3);
-
- let mut a = [];
- let mut b = [0];
- let mut c = [0; 3];
- let len = t!(s2.read_vectored(&mut [
- IoSliceMut::new(&mut a),
- IoSliceMut::new(&mut b),
- IoSliceMut::new(&mut c)
- ],));
- assert!(len > 0);
- assert_eq!(b, [10]);
- // some implementations don't support readv, so we may only fill the first buffer
- assert!(len == 1 || c == [11, 12, 0]);
- })
- }
-
- #[test]
- fn write_vectored() {
- each_ip(&mut |addr| {
- let srv = t!(TcpListener::bind(&addr));
- let mut s1 = t!(TcpStream::connect(&addr));
- let mut s2 = t!(srv.accept()).0;
-
- let a = [];
- let b = [10];
- let c = [11, 12];
- 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));
- // some implementations don't support writev, so we may only write the first buffer
- if len == 1 {
- assert_eq!(buf, [10, 0, 0, 0]);
- } else {
- assert_eq!(len, 3);
- assert_eq!(buf, [10, 11, 12, 0]);
- }
- })
- }
-
- #[test]
- fn double_bind() {
- each_ip(&mut |addr| {
- let listener1 = t!(TcpListener::bind(&addr));
- match TcpListener::bind(&addr) {
- Ok(listener2) => panic!(
- "This system (perhaps due to options set by TcpListener::bind) \
- permits double binding: {:?} and {:?}",
- listener1, listener2
- ),
- Err(e) => {
- assert!(
- e.kind() == ErrorKind::ConnectionRefused
- || e.kind() == ErrorKind::Other
- || e.kind() == ErrorKind::AddrInUse,
- "unknown error: {} {:?}",
- e,
- e.kind()
- );
- }
- }
- })
- }
-
- #[test]
- fn tcp_clone_smoke() {
- each_ip(&mut |addr| {
- let acceptor = t!(TcpListener::bind(&addr));
-
- let _t = thread::spawn(move || {
- let mut s = t!(TcpStream::connect(&addr));
- let mut buf = [0, 0];
- assert_eq!(s.read(&mut buf).unwrap(), 1);
- assert_eq!(buf[0], 1);
- t!(s.write(&[2]));
- });
-
- let mut s1 = t!(acceptor.accept()).0;
- let s2 = t!(s1.try_clone());
-
- let (tx1, rx1) = channel();
- let (tx2, rx2) = channel();
- let _t = thread::spawn(move || {
- let mut s2 = s2;
- rx1.recv().unwrap();
- t!(s2.write(&[1]));
- tx2.send(()).unwrap();
- });
- tx1.send(()).unwrap();
- let mut buf = [0, 0];
- assert_eq!(s1.read(&mut buf).unwrap(), 1);
- rx2.recv().unwrap();
- })
- }
-
- #[test]
- fn tcp_clone_two_read() {
- each_ip(&mut |addr| {
- let acceptor = t!(TcpListener::bind(&addr));
- let (tx1, rx) = channel();
- let tx2 = tx1.clone();
-
- let _t = thread::spawn(move || {
- let mut s = t!(TcpStream::connect(&addr));
- t!(s.write(&[1]));
- rx.recv().unwrap();
- t!(s.write(&[2]));
- rx.recv().unwrap();
- });
-
- let mut s1 = t!(acceptor.accept()).0;
- let s2 = t!(s1.try_clone());
-
- let (done, rx) = channel();
- let _t = thread::spawn(move || {
- let mut s2 = s2;
- let mut buf = [0, 0];
- t!(s2.read(&mut buf));
- tx2.send(()).unwrap();
- done.send(()).unwrap();
- });
- let mut buf = [0, 0];
- t!(s1.read(&mut buf));
- tx1.send(()).unwrap();
-
- rx.recv().unwrap();
- })
- }
-
- #[test]
- fn tcp_clone_two_write() {
- each_ip(&mut |addr| {
- let acceptor = t!(TcpListener::bind(&addr));
-
- let _t = thread::spawn(move || {
- let mut s = t!(TcpStream::connect(&addr));
- let mut buf = [0, 1];
- t!(s.read(&mut buf));
- t!(s.read(&mut buf));
- });
-
- let mut s1 = t!(acceptor.accept()).0;
- let s2 = t!(s1.try_clone());
-
- let (done, rx) = channel();
- let _t = thread::spawn(move || {
- let mut s2 = s2;
- t!(s2.write(&[1]));
- done.send(()).unwrap();
- });
- t!(s1.write(&[2]));
-
- rx.recv().unwrap();
- })
- }
-
- #[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));
- let _t = thread::spawn(move || {
- let mut c = t!(a.accept()).0;
- let mut b = [0];
- assert_eq!(c.read(&mut b).unwrap(), 0);
- t!(c.write(&[1]));
- });
-
- let mut s = t!(TcpStream::connect(&addr));
- t!(s.shutdown(Shutdown::Write));
- assert!(s.write(&[1]).is_err());
- let mut b = [0, 0];
- assert_eq!(t!(s.read(&mut b)), 1);
- assert_eq!(b[0], 1);
- })
- }
-
- #[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));
- let (tx, rx) = channel::<()>();
- let _t = thread::spawn(move || {
- let _s = t!(a.accept());
- let _ = rx.recv();
- });
-
- let mut b = [0];
- let mut s = t!(TcpStream::connect(&addr));
- let mut s2 = t!(s.try_clone());
-
- // closing should prevent reads/writes
- t!(s.shutdown(Shutdown::Write));
- assert!(s.write(&[0]).is_err());
- t!(s.shutdown(Shutdown::Read));
- assert_eq!(s.read(&mut b).unwrap(), 0);
-
- // closing should affect previous handles
- assert!(s2.write(&[0]).is_err());
- assert_eq!(s2.read(&mut b).unwrap(), 0);
-
- // closing should affect new handles
- let mut s3 = t!(s.try_clone());
- assert!(s3.write(&[0]).is_err());
- assert_eq!(s3.read(&mut b).unwrap(), 0);
-
- // make sure these don't die
- let _ = s2.shutdown(Shutdown::Read);
- let _ = s2.shutdown(Shutdown::Write);
- let _ = s3.shutdown(Shutdown::Read);
- let _ = s3.shutdown(Shutdown::Write);
- drop(tx);
- })
- }
-
- #[test]
- #[cfg(unix)] // test doesn't work on Windows, see #31657
- fn close_read_wakes_up() {
- each_ip(&mut |addr| {
- let a = t!(TcpListener::bind(&addr));
- let (tx1, rx) = channel::<()>();
- let _t = thread::spawn(move || {
- let _s = t!(a.accept());
- let _ = rx.recv();
- });
-
- let s = t!(TcpStream::connect(&addr));
- let s2 = t!(s.try_clone());
- let (tx, rx) = channel();
- let _t = thread::spawn(move || {
- let mut s2 = s2;
- assert_eq!(t!(s2.read(&mut [0])), 0);
- tx.send(()).unwrap();
- });
- // this should wake up the child thread
- t!(s.shutdown(Shutdown::Read));
-
- // this test will never finish if the child doesn't wake up
- rx.recv().unwrap();
- drop(tx1);
- })
- }
-
- #[test]
- fn clone_while_reading() {
- each_ip(&mut |addr| {
- let accept = t!(TcpListener::bind(&addr));
-
- // Enqueue a thread to write to a socket
- let (tx, rx) = channel();
- let (txdone, rxdone) = channel();
- let txdone2 = txdone.clone();
- let _t = thread::spawn(move || {
- let mut tcp = t!(TcpStream::connect(&addr));
- rx.recv().unwrap();
- t!(tcp.write(&[0]));
- txdone2.send(()).unwrap();
- });
-
- // Spawn off a reading clone
- let tcp = t!(accept.accept()).0;
- let tcp2 = t!(tcp.try_clone());
- let txdone3 = txdone.clone();
- let _t = thread::spawn(move || {
- let mut tcp2 = tcp2;
- t!(tcp2.read(&mut [0]));
- txdone3.send(()).unwrap();
- });
-
- // Try to ensure that the reading clone is indeed reading
- for _ in 0..50 {
- thread::yield_now();
- }
-
- // clone the handle again while it's reading, then let it finish the
- // read.
- let _ = t!(tcp.try_clone());
- tx.send(()).unwrap();
- rxdone.recv().unwrap();
- rxdone.recv().unwrap();
- })
- }
-
- #[test]
- fn clone_accept_smoke() {
- each_ip(&mut |addr| {
- let a = t!(TcpListener::bind(&addr));
- let a2 = t!(a.try_clone());
-
- let _t = thread::spawn(move || {
- let _ = TcpStream::connect(&addr);
- });
- let _t = thread::spawn(move || {
- let _ = TcpStream::connect(&addr);
- });
-
- t!(a.accept());
- t!(a2.accept());
- })
- }
-
- #[test]
- fn clone_accept_concurrent() {
- each_ip(&mut |addr| {
- let a = t!(TcpListener::bind(&addr));
- let a2 = t!(a.try_clone());
-
- let (tx, rx) = channel();
- let tx2 = tx.clone();
-
- let _t = thread::spawn(move || {
- tx.send(t!(a.accept())).unwrap();
- });
- let _t = thread::spawn(move || {
- tx2.send(t!(a2.accept())).unwrap();
- });
-
- let _t = thread::spawn(move || {
- let _ = TcpStream::connect(&addr);
- });
- let _t = thread::spawn(move || {
- let _ = TcpStream::connect(&addr);
- });
-
- rx.recv().unwrap();
- rx.recv().unwrap();
- })
- }
-
- #[test]
- fn debug() {
- #[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(target_env = "sgx")]
- use crate::os::fortanix_sgx::io::AsRawFd;
- #[cfg(unix)]
- use crate::os::unix::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 compare = format!(
- "TcpListener {{ addr: {:?}, {}: {:?} }}",
- 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 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 openbsd tests once their socket timeout code
- // no longer has rounding errors.
- // VxWorks ignores SO_SNDTIMEO.
- #[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)]
- #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
- #[test]
- fn timeouts() {
- let addr = next_test_ip4();
- let listener = t!(TcpListener::bind(&addr));
-
- let stream = t!(TcpStream::connect(&("localhost", addr.port())));
- let dur = Duration::new(15410, 0);
-
- assert_eq!(None, t!(stream.read_timeout()));
-
- t!(stream.set_read_timeout(Some(dur)));
- assert_eq!(Some(dur), t!(stream.read_timeout()));
-
- assert_eq!(None, t!(stream.write_timeout()));
-
- t!(stream.set_write_timeout(Some(dur)));
- assert_eq!(Some(dur), t!(stream.write_timeout()));
-
- t!(stream.set_read_timeout(None));
- assert_eq!(None, t!(stream.read_timeout()));
-
- t!(stream.set_write_timeout(None));
- assert_eq!(None, t!(stream.write_timeout()));
- drop(listener);
- }
-
- #[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));
-
- let mut stream = t!(TcpStream::connect(&("localhost", addr.port())));
- t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
-
- let mut buf = [0; 10];
- let start = Instant::now();
- let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
- assert!(
- kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
- "unexpected_error: {:?}",
- kind
- );
- assert!(start.elapsed() > Duration::from_millis(400));
- drop(listener);
- }
-
- #[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));
-
- let mut stream = t!(TcpStream::connect(&("localhost", addr.port())));
- t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
-
- let mut other_end = t!(listener.accept()).0;
- t!(other_end.write_all(b"hello world"));
-
- let mut buf = [0; 11];
- t!(stream.read(&mut buf));
- assert_eq!(b"hello world", &buf[..]);
-
- let start = Instant::now();
- let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
- assert!(
- kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
- "unexpected_error: {:?}",
- kind
- );
- assert!(start.elapsed() > Duration::from_millis(400));
- drop(listener);
- }
-
- // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
- // when passed zero Durations
- #[test]
- fn test_timeout_zero_duration() {
- let addr = next_test_ip4();
-
- let listener = t!(TcpListener::bind(&addr));
- let stream = t!(TcpStream::connect(&addr));
-
- 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]
- #[cfg_attr(target_env = "sgx", ignore)]
- fn nodelay() {
- let addr = next_test_ip4();
- let _listener = t!(TcpListener::bind(&addr));
-
- let stream = t!(TcpStream::connect(&("localhost", addr.port())));
-
- assert_eq!(false, t!(stream.nodelay()));
- t!(stream.set_nodelay(true));
- assert_eq!(true, t!(stream.nodelay()));
- t!(stream.set_nodelay(false));
- assert_eq!(false, t!(stream.nodelay()));
- }
-
- #[test]
- #[cfg_attr(target_env = "sgx", ignore)]
- fn ttl() {
- let ttl = 100;
-
- let addr = next_test_ip4();
- let listener = t!(TcpListener::bind(&addr));
-
- t!(listener.set_ttl(ttl));
- assert_eq!(ttl, t!(listener.ttl()));
-
- let stream = t!(TcpStream::connect(&("localhost", addr.port())));
-
- t!(stream.set_ttl(ttl));
- assert_eq!(ttl, t!(stream.ttl()));
- }
-
- #[test]
- #[cfg_attr(target_env = "sgx", ignore)]
- fn set_nonblocking() {
- let addr = next_test_ip4();
- let listener = t!(TcpListener::bind(&addr));
-
- t!(listener.set_nonblocking(true));
- t!(listener.set_nonblocking(false));
-
- let mut stream = t!(TcpStream::connect(&("localhost", addr.port())));
-
- t!(stream.set_nonblocking(false));
- t!(stream.set_nonblocking(true));
-
- let mut buf = [0];
- match stream.read(&mut buf) {
- Ok(_) => panic!("expected error"),
- Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
- Err(e) => panic!("unexpected error {}", e),
- }
- }
-
- #[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();
-
- let srv = t!(TcpListener::bind(&addr));
- let _t = thread::spawn(move || {
- let mut cl = t!(srv.accept()).0;
- cl.write(&[1, 3, 3, 7]).unwrap();
- t!(rxdone.recv());
- });
-
- let mut c = t!(TcpStream::connect(&addr));
- let mut b = [0; 10];
- for _ in 1..3 {
- let len = c.peek(&mut b).unwrap();
- assert_eq!(len, 4);
- }
- let len = c.read(&mut b).unwrap();
- assert_eq!(len, 4);
-
- t!(c.set_nonblocking(true));
- match c.peek(&mut b) {
- Ok(_) => panic!("expected error"),
- Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
- Err(e) => panic!("unexpected error {}", e),
- }
- t!(txdone.send(()));
- })
- }
-
- #[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();
- TcpStream::connect_timeout(&addr, Duration::from_secs(2)).unwrap();
- }
-}
diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs
new file mode 100644
index 0000000..abe9bc2
--- /dev/null
+++ b/library/std/src/net/tcp/tests.rs
@@ -0,0 +1,862 @@
+use crate::fmt;
+use crate::io::prelude::*;
+use crate::io::{ErrorKind, IoSlice, IoSliceMut};
+use crate::net::test::{next_test_ip4, next_test_ip6};
+use crate::net::*;
+use crate::sync::mpsc::channel;
+use crate::thread;
+use crate::time::{Duration, Instant};
+
+fn each_ip(f: &mut dyn FnMut(SocketAddr)) {
+ f(next_test_ip4());
+ f(next_test_ip6());
+}
+
+macro_rules! t {
+ ($e:expr) => {
+ match $e {
+ Ok(t) => t,
+ Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
+ }
+ };
+}
+
+#[test]
+fn bind_error() {
+ match TcpListener::bind("1.1.1.1:9999") {
+ Ok(..) => panic!(),
+ Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable),
+ }
+}
+
+#[test]
+fn connect_error() {
+ match TcpStream::connect("0.0.0.0:1") {
+ Ok(..) => panic!(),
+ Err(e) => assert!(
+ e.kind() == ErrorKind::ConnectionRefused
+ || e.kind() == ErrorKind::InvalidInput
+ || e.kind() == ErrorKind::AddrInUse
+ || e.kind() == ErrorKind::AddrNotAvailable,
+ "bad error: {} {:?}",
+ e,
+ e.kind()
+ ),
+ }
+}
+
+#[test]
+fn listen_localhost() {
+ let socket_addr = next_test_ip4();
+ let listener = t!(TcpListener::bind(&socket_addr));
+
+ let _t = thread::spawn(move || {
+ let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port())));
+ t!(stream.write(&[144]));
+ });
+
+ let mut stream = t!(listener.accept()).0;
+ let mut buf = [0];
+ t!(stream.read(&mut buf));
+ assert!(buf[0] == 144);
+}
+
+#[test]
+fn connect_loopback() {
+ each_ip(&mut |addr| {
+ let acceptor = t!(TcpListener::bind(&addr));
+
+ let _t = thread::spawn(move || {
+ let host = match addr {
+ SocketAddr::V4(..) => "127.0.0.1",
+ SocketAddr::V6(..) => "::1",
+ };
+ let mut stream = t!(TcpStream::connect(&(host, addr.port())));
+ t!(stream.write(&[66]));
+ });
+
+ let mut stream = t!(acceptor.accept()).0;
+ let mut buf = [0];
+ t!(stream.read(&mut buf));
+ assert!(buf[0] == 66);
+ })
+}
+
+#[test]
+fn smoke_test() {
+ each_ip(&mut |addr| {
+ let acceptor = t!(TcpListener::bind(&addr));
+
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move || {
+ let mut stream = t!(TcpStream::connect(&addr));
+ t!(stream.write(&[99]));
+ tx.send(t!(stream.local_addr())).unwrap();
+ });
+
+ let (mut stream, addr) = t!(acceptor.accept());
+ let mut buf = [0];
+ t!(stream.read(&mut buf));
+ assert!(buf[0] == 99);
+ assert_eq!(addr, t!(rx.recv()));
+ })
+}
+
+#[test]
+fn read_eof() {
+ each_ip(&mut |addr| {
+ let acceptor = t!(TcpListener::bind(&addr));
+
+ let _t = thread::spawn(move || {
+ let _stream = t!(TcpStream::connect(&addr));
+ // Close
+ });
+
+ let mut stream = t!(acceptor.accept()).0;
+ let mut buf = [0];
+ let nread = t!(stream.read(&mut buf));
+ assert_eq!(nread, 0);
+ let nread = t!(stream.read(&mut buf));
+ assert_eq!(nread, 0);
+ })
+}
+
+#[test]
+fn write_close() {
+ each_ip(&mut |addr| {
+ let acceptor = t!(TcpListener::bind(&addr));
+
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move || {
+ drop(t!(TcpStream::connect(&addr)));
+ tx.send(()).unwrap();
+ });
+
+ let mut stream = t!(acceptor.accept()).0;
+ rx.recv().unwrap();
+ let buf = [0];
+ match stream.write(&buf) {
+ Ok(..) => {}
+ Err(e) => {
+ assert!(
+ e.kind() == ErrorKind::ConnectionReset
+ || e.kind() == ErrorKind::BrokenPipe
+ || e.kind() == ErrorKind::ConnectionAborted,
+ "unknown error: {}",
+ e
+ );
+ }
+ }
+ })
+}
+
+#[test]
+fn multiple_connect_serial() {
+ each_ip(&mut |addr| {
+ let max = 10;
+ let acceptor = t!(TcpListener::bind(&addr));
+
+ let _t = thread::spawn(move || {
+ for _ in 0..max {
+ let mut stream = t!(TcpStream::connect(&addr));
+ t!(stream.write(&[99]));
+ }
+ });
+
+ for stream in acceptor.incoming().take(max) {
+ let mut stream = t!(stream);
+ let mut buf = [0];
+ t!(stream.read(&mut buf));
+ assert_eq!(buf[0], 99);
+ }
+ })
+}
+
+#[test]
+fn multiple_connect_interleaved_greedy_schedule() {
+ const MAX: usize = 10;
+ each_ip(&mut |addr| {
+ let acceptor = t!(TcpListener::bind(&addr));
+
+ let _t = thread::spawn(move || {
+ let acceptor = acceptor;
+ for (i, stream) in acceptor.incoming().enumerate().take(MAX) {
+ // Start another thread to handle the connection
+ let _t = thread::spawn(move || {
+ let mut stream = t!(stream);
+ let mut buf = [0];
+ t!(stream.read(&mut buf));
+ assert!(buf[0] == i as u8);
+ });
+ }
+ });
+
+ connect(0, addr);
+ });
+
+ fn connect(i: usize, addr: SocketAddr) {
+ if i == MAX {
+ return;
+ }
+
+ let t = thread::spawn(move || {
+ let mut stream = t!(TcpStream::connect(&addr));
+ // Connect again before writing
+ connect(i + 1, addr);
+ t!(stream.write(&[i as u8]));
+ });
+ t.join().ok().expect("thread panicked");
+ }
+}
+
+#[test]
+fn multiple_connect_interleaved_lazy_schedule() {
+ const MAX: usize = 10;
+ each_ip(&mut |addr| {
+ let acceptor = t!(TcpListener::bind(&addr));
+
+ let _t = thread::spawn(move || {
+ for stream in acceptor.incoming().take(MAX) {
+ // Start another thread to handle the connection
+ let _t = thread::spawn(move || {
+ let mut stream = t!(stream);
+ let mut buf = [0];
+ t!(stream.read(&mut buf));
+ assert!(buf[0] == 99);
+ });
+ }
+ });
+
+ connect(0, addr);
+ });
+
+ fn connect(i: usize, addr: SocketAddr) {
+ if i == MAX {
+ return;
+ }
+
+ let t = thread::spawn(move || {
+ let mut stream = t!(TcpStream::connect(&addr));
+ connect(i + 1, addr);
+ t!(stream.write(&[99]));
+ });
+ t.join().ok().expect("thread panicked");
+ }
+}
+
+#[test]
+fn socket_and_peer_name() {
+ each_ip(&mut |addr| {
+ let listener = t!(TcpListener::bind(&addr));
+ let so_name = t!(listener.local_addr());
+ assert_eq!(addr, so_name);
+ let _t = thread::spawn(move || {
+ t!(listener.accept());
+ });
+
+ let stream = t!(TcpStream::connect(&addr));
+ assert_eq!(addr, t!(stream.peer_addr()));
+ })
+}
+
+#[test]
+fn partial_read() {
+ each_ip(&mut |addr| {
+ let (tx, rx) = channel();
+ let srv = t!(TcpListener::bind(&addr));
+ let _t = thread::spawn(move || {
+ let mut cl = t!(srv.accept()).0;
+ cl.write(&[10]).unwrap();
+ let mut b = [0];
+ t!(cl.read(&mut b));
+ tx.send(()).unwrap();
+ });
+
+ let mut c = t!(TcpStream::connect(&addr));
+ let mut b = [0; 10];
+ assert_eq!(c.read(&mut b).unwrap(), 1);
+ t!(c.write(&[1]));
+ rx.recv().unwrap();
+ })
+}
+
+#[test]
+fn read_vectored() {
+ each_ip(&mut |addr| {
+ let srv = t!(TcpListener::bind(&addr));
+ let mut s1 = t!(TcpStream::connect(&addr));
+ let mut s2 = t!(srv.accept()).0;
+
+ let len = s1.write(&[10, 11, 12]).unwrap();
+ assert_eq!(len, 3);
+
+ let mut a = [];
+ let mut b = [0];
+ let mut c = [0; 3];
+ let len = t!(s2.read_vectored(&mut [
+ IoSliceMut::new(&mut a),
+ IoSliceMut::new(&mut b),
+ IoSliceMut::new(&mut c)
+ ],));
+ assert!(len > 0);
+ assert_eq!(b, [10]);
+ // some implementations don't support readv, so we may only fill the first buffer
+ assert!(len == 1 || c == [11, 12, 0]);
+ })
+}
+
+#[test]
+fn write_vectored() {
+ each_ip(&mut |addr| {
+ let srv = t!(TcpListener::bind(&addr));
+ let mut s1 = t!(TcpStream::connect(&addr));
+ let mut s2 = t!(srv.accept()).0;
+
+ let a = [];
+ let b = [10];
+ let c = [11, 12];
+ 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));
+ // some implementations don't support writev, so we may only write the first buffer
+ if len == 1 {
+ assert_eq!(buf, [10, 0, 0, 0]);
+ } else {
+ assert_eq!(len, 3);
+ assert_eq!(buf, [10, 11, 12, 0]);
+ }
+ })
+}
+
+#[test]
+fn double_bind() {
+ each_ip(&mut |addr| {
+ let listener1 = t!(TcpListener::bind(&addr));
+ match TcpListener::bind(&addr) {
+ Ok(listener2) => panic!(
+ "This system (perhaps due to options set by TcpListener::bind) \
+ permits double binding: {:?} and {:?}",
+ listener1, listener2
+ ),
+ Err(e) => {
+ assert!(
+ e.kind() == ErrorKind::ConnectionRefused
+ || e.kind() == ErrorKind::Other
+ || e.kind() == ErrorKind::AddrInUse,
+ "unknown error: {} {:?}",
+ e,
+ e.kind()
+ );
+ }
+ }
+ })
+}
+
+#[test]
+fn tcp_clone_smoke() {
+ each_ip(&mut |addr| {
+ let acceptor = t!(TcpListener::bind(&addr));
+
+ let _t = thread::spawn(move || {
+ let mut s = t!(TcpStream::connect(&addr));
+ let mut buf = [0, 0];
+ assert_eq!(s.read(&mut buf).unwrap(), 1);
+ assert_eq!(buf[0], 1);
+ t!(s.write(&[2]));
+ });
+
+ let mut s1 = t!(acceptor.accept()).0;
+ let s2 = t!(s1.try_clone());
+
+ let (tx1, rx1) = channel();
+ let (tx2, rx2) = channel();
+ let _t = thread::spawn(move || {
+ let mut s2 = s2;
+ rx1.recv().unwrap();
+ t!(s2.write(&[1]));
+ tx2.send(()).unwrap();
+ });
+ tx1.send(()).unwrap();
+ let mut buf = [0, 0];
+ assert_eq!(s1.read(&mut buf).unwrap(), 1);
+ rx2.recv().unwrap();
+ })
+}
+
+#[test]
+fn tcp_clone_two_read() {
+ each_ip(&mut |addr| {
+ let acceptor = t!(TcpListener::bind(&addr));
+ let (tx1, rx) = channel();
+ let tx2 = tx1.clone();
+
+ let _t = thread::spawn(move || {
+ let mut s = t!(TcpStream::connect(&addr));
+ t!(s.write(&[1]));
+ rx.recv().unwrap();
+ t!(s.write(&[2]));
+ rx.recv().unwrap();
+ });
+
+ let mut s1 = t!(acceptor.accept()).0;
+ let s2 = t!(s1.try_clone());
+
+ let (done, rx) = channel();
+ let _t = thread::spawn(move || {
+ let mut s2 = s2;
+ let mut buf = [0, 0];
+ t!(s2.read(&mut buf));
+ tx2.send(()).unwrap();
+ done.send(()).unwrap();
+ });
+ let mut buf = [0, 0];
+ t!(s1.read(&mut buf));
+ tx1.send(()).unwrap();
+
+ rx.recv().unwrap();
+ })
+}
+
+#[test]
+fn tcp_clone_two_write() {
+ each_ip(&mut |addr| {
+ let acceptor = t!(TcpListener::bind(&addr));
+
+ let _t = thread::spawn(move || {
+ let mut s = t!(TcpStream::connect(&addr));
+ let mut buf = [0, 1];
+ t!(s.read(&mut buf));
+ t!(s.read(&mut buf));
+ });
+
+ let mut s1 = t!(acceptor.accept()).0;
+ let s2 = t!(s1.try_clone());
+
+ let (done, rx) = channel();
+ let _t = thread::spawn(move || {
+ let mut s2 = s2;
+ t!(s2.write(&[1]));
+ done.send(()).unwrap();
+ });
+ t!(s1.write(&[2]));
+
+ rx.recv().unwrap();
+ })
+}
+
+#[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));
+ let _t = thread::spawn(move || {
+ let mut c = t!(a.accept()).0;
+ let mut b = [0];
+ assert_eq!(c.read(&mut b).unwrap(), 0);
+ t!(c.write(&[1]));
+ });
+
+ let mut s = t!(TcpStream::connect(&addr));
+ t!(s.shutdown(Shutdown::Write));
+ assert!(s.write(&[1]).is_err());
+ let mut b = [0, 0];
+ assert_eq!(t!(s.read(&mut b)), 1);
+ assert_eq!(b[0], 1);
+ })
+}
+
+#[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));
+ let (tx, rx) = channel::<()>();
+ let _t = thread::spawn(move || {
+ let _s = t!(a.accept());
+ let _ = rx.recv();
+ });
+
+ let mut b = [0];
+ let mut s = t!(TcpStream::connect(&addr));
+ let mut s2 = t!(s.try_clone());
+
+ // closing should prevent reads/writes
+ t!(s.shutdown(Shutdown::Write));
+ assert!(s.write(&[0]).is_err());
+ t!(s.shutdown(Shutdown::Read));
+ assert_eq!(s.read(&mut b).unwrap(), 0);
+
+ // closing should affect previous handles
+ assert!(s2.write(&[0]).is_err());
+ assert_eq!(s2.read(&mut b).unwrap(), 0);
+
+ // closing should affect new handles
+ let mut s3 = t!(s.try_clone());
+ assert!(s3.write(&[0]).is_err());
+ assert_eq!(s3.read(&mut b).unwrap(), 0);
+
+ // make sure these don't die
+ let _ = s2.shutdown(Shutdown::Read);
+ let _ = s2.shutdown(Shutdown::Write);
+ let _ = s3.shutdown(Shutdown::Read);
+ let _ = s3.shutdown(Shutdown::Write);
+ drop(tx);
+ })
+}
+
+#[test]
+#[cfg(unix)] // test doesn't work on Windows, see #31657
+fn close_read_wakes_up() {
+ each_ip(&mut |addr| {
+ let a = t!(TcpListener::bind(&addr));
+ let (tx1, rx) = channel::<()>();
+ let _t = thread::spawn(move || {
+ let _s = t!(a.accept());
+ let _ = rx.recv();
+ });
+
+ let s = t!(TcpStream::connect(&addr));
+ let s2 = t!(s.try_clone());
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move || {
+ let mut s2 = s2;
+ assert_eq!(t!(s2.read(&mut [0])), 0);
+ tx.send(()).unwrap();
+ });
+ // this should wake up the child thread
+ t!(s.shutdown(Shutdown::Read));
+
+ // this test will never finish if the child doesn't wake up
+ rx.recv().unwrap();
+ drop(tx1);
+ })
+}
+
+#[test]
+fn clone_while_reading() {
+ each_ip(&mut |addr| {
+ let accept = t!(TcpListener::bind(&addr));
+
+ // Enqueue a thread to write to a socket
+ let (tx, rx) = channel();
+ let (txdone, rxdone) = channel();
+ let txdone2 = txdone.clone();
+ let _t = thread::spawn(move || {
+ let mut tcp = t!(TcpStream::connect(&addr));
+ rx.recv().unwrap();
+ t!(tcp.write(&[0]));
+ txdone2.send(()).unwrap();
+ });
+
+ // Spawn off a reading clone
+ let tcp = t!(accept.accept()).0;
+ let tcp2 = t!(tcp.try_clone());
+ let txdone3 = txdone.clone();
+ let _t = thread::spawn(move || {
+ let mut tcp2 = tcp2;
+ t!(tcp2.read(&mut [0]));
+ txdone3.send(()).unwrap();
+ });
+
+ // Try to ensure that the reading clone is indeed reading
+ for _ in 0..50 {
+ thread::yield_now();
+ }
+
+ // clone the handle again while it's reading, then let it finish the
+ // read.
+ let _ = t!(tcp.try_clone());
+ tx.send(()).unwrap();
+ rxdone.recv().unwrap();
+ rxdone.recv().unwrap();
+ })
+}
+
+#[test]
+fn clone_accept_smoke() {
+ each_ip(&mut |addr| {
+ let a = t!(TcpListener::bind(&addr));
+ let a2 = t!(a.try_clone());
+
+ let _t = thread::spawn(move || {
+ let _ = TcpStream::connect(&addr);
+ });
+ let _t = thread::spawn(move || {
+ let _ = TcpStream::connect(&addr);
+ });
+
+ t!(a.accept());
+ t!(a2.accept());
+ })
+}
+
+#[test]
+fn clone_accept_concurrent() {
+ each_ip(&mut |addr| {
+ let a = t!(TcpListener::bind(&addr));
+ let a2 = t!(a.try_clone());
+
+ let (tx, rx) = channel();
+ let tx2 = tx.clone();
+
+ let _t = thread::spawn(move || {
+ tx.send(t!(a.accept())).unwrap();
+ });
+ let _t = thread::spawn(move || {
+ tx2.send(t!(a2.accept())).unwrap();
+ });
+
+ let _t = thread::spawn(move || {
+ let _ = TcpStream::connect(&addr);
+ });
+ let _t = thread::spawn(move || {
+ let _ = TcpStream::connect(&addr);
+ });
+
+ rx.recv().unwrap();
+ rx.recv().unwrap();
+ })
+}
+
+#[test]
+fn debug() {
+ #[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(target_env = "sgx")]
+ use crate::os::fortanix_sgx::io::AsRawFd;
+ #[cfg(unix)]
+ use crate::os::unix::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 compare = format!(
+ "TcpListener {{ addr: {:?}, {}: {:?} }}",
+ 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 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 openbsd tests once their socket timeout code
+// no longer has rounding errors.
+// VxWorks ignores SO_SNDTIMEO.
+#[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)]
+#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
+#[test]
+fn timeouts() {
+ let addr = next_test_ip4();
+ let listener = t!(TcpListener::bind(&addr));
+
+ let stream = t!(TcpStream::connect(&("localhost", addr.port())));
+ let dur = Duration::new(15410, 0);
+
+ assert_eq!(None, t!(stream.read_timeout()));
+
+ t!(stream.set_read_timeout(Some(dur)));
+ assert_eq!(Some(dur), t!(stream.read_timeout()));
+
+ assert_eq!(None, t!(stream.write_timeout()));
+
+ t!(stream.set_write_timeout(Some(dur)));
+ assert_eq!(Some(dur), t!(stream.write_timeout()));
+
+ t!(stream.set_read_timeout(None));
+ assert_eq!(None, t!(stream.read_timeout()));
+
+ t!(stream.set_write_timeout(None));
+ assert_eq!(None, t!(stream.write_timeout()));
+ drop(listener);
+}
+
+#[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));
+
+ let mut stream = t!(TcpStream::connect(&("localhost", addr.port())));
+ t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+ let mut buf = [0; 10];
+ let start = Instant::now();
+ let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
+ assert!(
+ kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
+ "unexpected_error: {:?}",
+ kind
+ );
+ assert!(start.elapsed() > Duration::from_millis(400));
+ drop(listener);
+}
+
+#[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));
+
+ let mut stream = t!(TcpStream::connect(&("localhost", addr.port())));
+ t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+ let mut other_end = t!(listener.accept()).0;
+ t!(other_end.write_all(b"hello world"));
+
+ let mut buf = [0; 11];
+ t!(stream.read(&mut buf));
+ assert_eq!(b"hello world", &buf[..]);
+
+ let start = Instant::now();
+ let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
+ assert!(
+ kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
+ "unexpected_error: {:?}",
+ kind
+ );
+ assert!(start.elapsed() > Duration::from_millis(400));
+ drop(listener);
+}
+
+// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+// when passed zero Durations
+#[test]
+fn test_timeout_zero_duration() {
+ let addr = next_test_ip4();
+
+ let listener = t!(TcpListener::bind(&addr));
+ let stream = t!(TcpStream::connect(&addr));
+
+ 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]
+#[cfg_attr(target_env = "sgx", ignore)]
+fn nodelay() {
+ let addr = next_test_ip4();
+ let _listener = t!(TcpListener::bind(&addr));
+
+ let stream = t!(TcpStream::connect(&("localhost", addr.port())));
+
+ assert_eq!(false, t!(stream.nodelay()));
+ t!(stream.set_nodelay(true));
+ assert_eq!(true, t!(stream.nodelay()));
+ t!(stream.set_nodelay(false));
+ assert_eq!(false, t!(stream.nodelay()));
+}
+
+#[test]
+#[cfg_attr(target_env = "sgx", ignore)]
+fn ttl() {
+ let ttl = 100;
+
+ let addr = next_test_ip4();
+ let listener = t!(TcpListener::bind(&addr));
+
+ t!(listener.set_ttl(ttl));
+ assert_eq!(ttl, t!(listener.ttl()));
+
+ let stream = t!(TcpStream::connect(&("localhost", addr.port())));
+
+ t!(stream.set_ttl(ttl));
+ assert_eq!(ttl, t!(stream.ttl()));
+}
+
+#[test]
+#[cfg_attr(target_env = "sgx", ignore)]
+fn set_nonblocking() {
+ let addr = next_test_ip4();
+ let listener = t!(TcpListener::bind(&addr));
+
+ t!(listener.set_nonblocking(true));
+ t!(listener.set_nonblocking(false));
+
+ let mut stream = t!(TcpStream::connect(&("localhost", addr.port())));
+
+ t!(stream.set_nonblocking(false));
+ t!(stream.set_nonblocking(true));
+
+ let mut buf = [0];
+ match stream.read(&mut buf) {
+ Ok(_) => panic!("expected error"),
+ Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
+ Err(e) => panic!("unexpected error {}", e),
+ }
+}
+
+#[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();
+
+ let srv = t!(TcpListener::bind(&addr));
+ let _t = thread::spawn(move || {
+ let mut cl = t!(srv.accept()).0;
+ cl.write(&[1, 3, 3, 7]).unwrap();
+ t!(rxdone.recv());
+ });
+
+ let mut c = t!(TcpStream::connect(&addr));
+ let mut b = [0; 10];
+ for _ in 1..3 {
+ let len = c.peek(&mut b).unwrap();
+ assert_eq!(len, 4);
+ }
+ let len = c.read(&mut b).unwrap();
+ assert_eq!(len, 4);
+
+ t!(c.set_nonblocking(true));
+ match c.peek(&mut b) {
+ Ok(_) => panic!("expected error"),
+ Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
+ Err(e) => panic!("unexpected error {}", e),
+ }
+ t!(txdone.send(()));
+ })
+}
+
+#[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();
+ TcpStream::connect_timeout(&addr, Duration::from_secs(2)).unwrap();
+}
diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs
index d730b2b..17e3e44 100644
--- a/library/std/src/net/udp.rs
+++ b/library/std/src/net/udp.rs
@@ -1,3 +1,6 @@
+#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))]
+mod tests;
+
use crate::fmt;
use crate::io::{self, Error, ErrorKind};
use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs};
@@ -798,380 +801,3 @@
self.0.fmt(f)
}
}
-
-#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))]
-mod tests {
- use crate::io::ErrorKind;
- use crate::net::test::{next_test_ip4, next_test_ip6};
- use crate::net::*;
- use crate::sync::mpsc::channel;
- use crate::sys_common::AsInner;
- use crate::thread;
- use crate::time::{Duration, Instant};
-
- fn each_ip(f: &mut dyn FnMut(SocketAddr, SocketAddr)) {
- f(next_test_ip4(), next_test_ip4());
- f(next_test_ip6(), next_test_ip6());
- }
-
- macro_rules! t {
- ($e:expr) => {
- match $e {
- Ok(t) => t,
- Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
- }
- };
- }
-
- #[test]
- fn bind_error() {
- match UdpSocket::bind("1.1.1.1:9999") {
- Ok(..) => panic!(),
- Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable),
- }
- }
-
- #[test]
- fn socket_smoke_test_ip4() {
- each_ip(&mut |server_ip, client_ip| {
- let (tx1, rx1) = channel();
- let (tx2, rx2) = channel();
-
- let _t = thread::spawn(move || {
- let client = t!(UdpSocket::bind(&client_ip));
- rx1.recv().unwrap();
- t!(client.send_to(&[99], &server_ip));
- tx2.send(()).unwrap();
- });
-
- let server = t!(UdpSocket::bind(&server_ip));
- tx1.send(()).unwrap();
- let mut buf = [0];
- let (nread, src) = t!(server.recv_from(&mut buf));
- assert_eq!(nread, 1);
- assert_eq!(buf[0], 99);
- assert_eq!(src, client_ip);
- rx2.recv().unwrap();
- })
- }
-
- #[test]
- fn socket_name() {
- each_ip(&mut |addr, _| {
- let server = t!(UdpSocket::bind(&addr));
- assert_eq!(addr, t!(server.local_addr()));
- })
- }
-
- #[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));
- let sock2 = t!(UdpSocket::bind(&addr2));
-
- let _t = thread::spawn(move || {
- let mut buf = [0, 0];
- assert_eq!(sock2.recv_from(&mut buf).unwrap(), (1, addr1));
- assert_eq!(buf[0], 1);
- t!(sock2.send_to(&[2], &addr1));
- });
-
- let sock3 = t!(sock1.try_clone());
-
- let (tx1, rx1) = channel();
- let (tx2, rx2) = channel();
- let _t = thread::spawn(move || {
- rx1.recv().unwrap();
- t!(sock3.send_to(&[1], &addr2));
- tx2.send(()).unwrap();
- });
- tx1.send(()).unwrap();
- let mut buf = [0, 0];
- assert_eq!(sock1.recv_from(&mut buf).unwrap(), (1, addr2));
- rx2.recv().unwrap();
- })
- }
-
- #[test]
- fn udp_clone_two_read() {
- each_ip(&mut |addr1, addr2| {
- let sock1 = t!(UdpSocket::bind(&addr1));
- let sock2 = t!(UdpSocket::bind(&addr2));
- let (tx1, rx) = channel();
- let tx2 = tx1.clone();
-
- let _t = thread::spawn(move || {
- t!(sock2.send_to(&[1], &addr1));
- rx.recv().unwrap();
- t!(sock2.send_to(&[2], &addr1));
- rx.recv().unwrap();
- });
-
- let sock3 = t!(sock1.try_clone());
-
- let (done, rx) = channel();
- let _t = thread::spawn(move || {
- let mut buf = [0, 0];
- t!(sock3.recv_from(&mut buf));
- tx2.send(()).unwrap();
- done.send(()).unwrap();
- });
- let mut buf = [0, 0];
- t!(sock1.recv_from(&mut buf));
- tx1.send(()).unwrap();
-
- rx.recv().unwrap();
- })
- }
-
- #[test]
- fn udp_clone_two_write() {
- each_ip(&mut |addr1, addr2| {
- let sock1 = t!(UdpSocket::bind(&addr1));
- let sock2 = t!(UdpSocket::bind(&addr2));
-
- let (tx, rx) = channel();
- let (serv_tx, serv_rx) = channel();
-
- let _t = thread::spawn(move || {
- let mut buf = [0, 1];
- rx.recv().unwrap();
- t!(sock2.recv_from(&mut buf));
- serv_tx.send(()).unwrap();
- });
-
- let sock3 = t!(sock1.try_clone());
-
- let (done, rx) = channel();
- let tx2 = tx.clone();
- let _t = thread::spawn(move || {
- match sock3.send_to(&[1], &addr2) {
- Ok(..) => {
- let _ = tx2.send(());
- }
- Err(..) => {}
- }
- done.send(()).unwrap();
- });
- match sock1.send_to(&[2], &addr2) {
- Ok(..) => {
- let _ = tx.send(());
- }
- Err(..) => {}
- }
- drop(tx);
-
- rx.recv().unwrap();
- serv_rx.recv().unwrap();
- })
- }
-
- #[test]
- fn debug() {
- let name = if cfg!(windows) { "socket" } else { "fd" };
- let socket_addr = next_test_ip4();
-
- let udpsock = t!(UdpSocket::bind(&socket_addr));
- let udpsock_inner = udpsock.0.socket().as_inner();
- let compare =
- format!("UdpSocket {{ addr: {:?}, {}: {:?} }}", socket_addr, name, udpsock_inner);
- assert_eq!(format!("{:?}", udpsock), compare);
- }
-
- // FIXME: re-enabled openbsd/netbsd tests once their socket timeout code
- // no longer has rounding errors.
- // VxWorks ignores SO_SNDTIMEO.
- #[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)]
- #[test]
- fn timeouts() {
- let addr = next_test_ip4();
-
- let stream = t!(UdpSocket::bind(&addr));
- let dur = Duration::new(15410, 0);
-
- assert_eq!(None, t!(stream.read_timeout()));
-
- t!(stream.set_read_timeout(Some(dur)));
- assert_eq!(Some(dur), t!(stream.read_timeout()));
-
- assert_eq!(None, t!(stream.write_timeout()));
-
- t!(stream.set_write_timeout(Some(dur)));
- assert_eq!(Some(dur), t!(stream.write_timeout()));
-
- t!(stream.set_read_timeout(None));
- assert_eq!(None, t!(stream.read_timeout()));
-
- t!(stream.set_write_timeout(None));
- assert_eq!(None, t!(stream.write_timeout()));
- }
-
- #[test]
- fn test_read_timeout() {
- let addr = next_test_ip4();
-
- let stream = t!(UdpSocket::bind(&addr));
- t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
-
- let mut buf = [0; 10];
-
- let start = Instant::now();
- loop {
- let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
- if kind != ErrorKind::Interrupted {
- assert!(
- kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
- "unexpected_error: {:?}",
- kind
- );
- break;
- }
- }
- assert!(start.elapsed() > Duration::from_millis(400));
- }
-
- #[test]
- fn test_read_with_timeout() {
- let addr = next_test_ip4();
-
- let stream = t!(UdpSocket::bind(&addr));
- t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
-
- t!(stream.send_to(b"hello world", &addr));
-
- let mut buf = [0; 11];
- t!(stream.recv_from(&mut buf));
- assert_eq!(b"hello world", &buf[..]);
-
- let start = Instant::now();
- loop {
- let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
- if kind != ErrorKind::Interrupted {
- assert!(
- kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
- "unexpected_error: {:?}",
- kind
- );
- break;
- }
- }
- assert!(start.elapsed() > Duration::from_millis(400));
- }
-
- // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
- // when passed zero Durations
- #[test]
- fn test_timeout_zero_duration() {
- let addr = next_test_ip4();
-
- let socket = t!(UdpSocket::bind(&addr));
-
- let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
- let err = result.unwrap_err();
- assert_eq!(err.kind(), ErrorKind::InvalidInput);
-
- let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
- let err = result.unwrap_err();
- assert_eq!(err.kind(), ErrorKind::InvalidInput);
- }
-
- #[test]
- fn connect_send_recv() {
- let addr = next_test_ip4();
-
- let socket = t!(UdpSocket::bind(&addr));
- t!(socket.connect(addr));
-
- t!(socket.send(b"hello world"));
-
- let mut buf = [0; 11];
- t!(socket.recv(&mut buf));
- assert_eq!(b"hello world", &buf[..]);
- }
-
- #[test]
- fn connect_send_peek_recv() {
- each_ip(&mut |addr, _| {
- let socket = t!(UdpSocket::bind(&addr));
- t!(socket.connect(addr));
-
- t!(socket.send(b"hello world"));
-
- for _ in 1..3 {
- let mut buf = [0; 11];
- let size = t!(socket.peek(&mut buf));
- assert_eq!(b"hello world", &buf[..]);
- assert_eq!(size, 11);
- }
-
- let mut buf = [0; 11];
- let size = t!(socket.recv(&mut buf));
- assert_eq!(b"hello world", &buf[..]);
- assert_eq!(size, 11);
- })
- }
-
- #[test]
- fn peek_from() {
- each_ip(&mut |addr, _| {
- let socket = t!(UdpSocket::bind(&addr));
- t!(socket.send_to(b"hello world", &addr));
-
- for _ in 1..3 {
- let mut buf = [0; 11];
- let (size, _) = t!(socket.peek_from(&mut buf));
- assert_eq!(b"hello world", &buf[..]);
- assert_eq!(size, 11);
- }
-
- let mut buf = [0; 11];
- let (size, _) = t!(socket.recv_from(&mut buf));
- assert_eq!(b"hello world", &buf[..]);
- assert_eq!(size, 11);
- })
- }
-
- #[test]
- fn ttl() {
- let ttl = 100;
-
- let addr = next_test_ip4();
-
- let stream = t!(UdpSocket::bind(&addr));
-
- t!(stream.set_ttl(ttl));
- assert_eq!(ttl, t!(stream.ttl()));
- }
-
- #[test]
- fn set_nonblocking() {
- each_ip(&mut |addr, _| {
- let socket = t!(UdpSocket::bind(&addr));
-
- t!(socket.set_nonblocking(true));
- t!(socket.set_nonblocking(false));
-
- t!(socket.connect(addr));
-
- t!(socket.set_nonblocking(false));
- t!(socket.set_nonblocking(true));
-
- let mut buf = [0];
- match socket.recv(&mut buf) {
- Ok(_) => panic!("expected error"),
- Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
- Err(e) => panic!("unexpected error {}", e),
- }
- })
- }
-}
diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs
new file mode 100644
index 0000000..658369f
--- /dev/null
+++ b/library/std/src/net/udp/tests.rs
@@ -0,0 +1,372 @@
+use crate::io::ErrorKind;
+use crate::net::test::{next_test_ip4, next_test_ip6};
+use crate::net::*;
+use crate::sync::mpsc::channel;
+use crate::sys_common::AsInner;
+use crate::thread;
+use crate::time::{Duration, Instant};
+
+fn each_ip(f: &mut dyn FnMut(SocketAddr, SocketAddr)) {
+ f(next_test_ip4(), next_test_ip4());
+ f(next_test_ip6(), next_test_ip6());
+}
+
+macro_rules! t {
+ ($e:expr) => {
+ match $e {
+ Ok(t) => t,
+ Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
+ }
+ };
+}
+
+#[test]
+fn bind_error() {
+ match UdpSocket::bind("1.1.1.1:9999") {
+ Ok(..) => panic!(),
+ Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable),
+ }
+}
+
+#[test]
+fn socket_smoke_test_ip4() {
+ each_ip(&mut |server_ip, client_ip| {
+ let (tx1, rx1) = channel();
+ let (tx2, rx2) = channel();
+
+ let _t = thread::spawn(move || {
+ let client = t!(UdpSocket::bind(&client_ip));
+ rx1.recv().unwrap();
+ t!(client.send_to(&[99], &server_ip));
+ tx2.send(()).unwrap();
+ });
+
+ let server = t!(UdpSocket::bind(&server_ip));
+ tx1.send(()).unwrap();
+ let mut buf = [0];
+ let (nread, src) = t!(server.recv_from(&mut buf));
+ assert_eq!(nread, 1);
+ assert_eq!(buf[0], 99);
+ assert_eq!(src, client_ip);
+ rx2.recv().unwrap();
+ })
+}
+
+#[test]
+fn socket_name() {
+ each_ip(&mut |addr, _| {
+ let server = t!(UdpSocket::bind(&addr));
+ assert_eq!(addr, t!(server.local_addr()));
+ })
+}
+
+#[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));
+ let sock2 = t!(UdpSocket::bind(&addr2));
+
+ let _t = thread::spawn(move || {
+ let mut buf = [0, 0];
+ assert_eq!(sock2.recv_from(&mut buf).unwrap(), (1, addr1));
+ assert_eq!(buf[0], 1);
+ t!(sock2.send_to(&[2], &addr1));
+ });
+
+ let sock3 = t!(sock1.try_clone());
+
+ let (tx1, rx1) = channel();
+ let (tx2, rx2) = channel();
+ let _t = thread::spawn(move || {
+ rx1.recv().unwrap();
+ t!(sock3.send_to(&[1], &addr2));
+ tx2.send(()).unwrap();
+ });
+ tx1.send(()).unwrap();
+ let mut buf = [0, 0];
+ assert_eq!(sock1.recv_from(&mut buf).unwrap(), (1, addr2));
+ rx2.recv().unwrap();
+ })
+}
+
+#[test]
+fn udp_clone_two_read() {
+ each_ip(&mut |addr1, addr2| {
+ let sock1 = t!(UdpSocket::bind(&addr1));
+ let sock2 = t!(UdpSocket::bind(&addr2));
+ let (tx1, rx) = channel();
+ let tx2 = tx1.clone();
+
+ let _t = thread::spawn(move || {
+ t!(sock2.send_to(&[1], &addr1));
+ rx.recv().unwrap();
+ t!(sock2.send_to(&[2], &addr1));
+ rx.recv().unwrap();
+ });
+
+ let sock3 = t!(sock1.try_clone());
+
+ let (done, rx) = channel();
+ let _t = thread::spawn(move || {
+ let mut buf = [0, 0];
+ t!(sock3.recv_from(&mut buf));
+ tx2.send(()).unwrap();
+ done.send(()).unwrap();
+ });
+ let mut buf = [0, 0];
+ t!(sock1.recv_from(&mut buf));
+ tx1.send(()).unwrap();
+
+ rx.recv().unwrap();
+ })
+}
+
+#[test]
+fn udp_clone_two_write() {
+ each_ip(&mut |addr1, addr2| {
+ let sock1 = t!(UdpSocket::bind(&addr1));
+ let sock2 = t!(UdpSocket::bind(&addr2));
+
+ let (tx, rx) = channel();
+ let (serv_tx, serv_rx) = channel();
+
+ let _t = thread::spawn(move || {
+ let mut buf = [0, 1];
+ rx.recv().unwrap();
+ t!(sock2.recv_from(&mut buf));
+ serv_tx.send(()).unwrap();
+ });
+
+ let sock3 = t!(sock1.try_clone());
+
+ let (done, rx) = channel();
+ let tx2 = tx.clone();
+ let _t = thread::spawn(move || {
+ match sock3.send_to(&[1], &addr2) {
+ Ok(..) => {
+ let _ = tx2.send(());
+ }
+ Err(..) => {}
+ }
+ done.send(()).unwrap();
+ });
+ match sock1.send_to(&[2], &addr2) {
+ Ok(..) => {
+ let _ = tx.send(());
+ }
+ Err(..) => {}
+ }
+ drop(tx);
+
+ rx.recv().unwrap();
+ serv_rx.recv().unwrap();
+ })
+}
+
+#[test]
+fn debug() {
+ let name = if cfg!(windows) { "socket" } else { "fd" };
+ let socket_addr = next_test_ip4();
+
+ let udpsock = t!(UdpSocket::bind(&socket_addr));
+ let udpsock_inner = udpsock.0.socket().as_inner();
+ let compare = format!("UdpSocket {{ addr: {:?}, {}: {:?} }}", socket_addr, name, udpsock_inner);
+ assert_eq!(format!("{:?}", udpsock), compare);
+}
+
+// FIXME: re-enabled openbsd/netbsd tests once their socket timeout code
+// no longer has rounding errors.
+// VxWorks ignores SO_SNDTIMEO.
+#[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)]
+#[test]
+fn timeouts() {
+ let addr = next_test_ip4();
+
+ let stream = t!(UdpSocket::bind(&addr));
+ let dur = Duration::new(15410, 0);
+
+ assert_eq!(None, t!(stream.read_timeout()));
+
+ t!(stream.set_read_timeout(Some(dur)));
+ assert_eq!(Some(dur), t!(stream.read_timeout()));
+
+ assert_eq!(None, t!(stream.write_timeout()));
+
+ t!(stream.set_write_timeout(Some(dur)));
+ assert_eq!(Some(dur), t!(stream.write_timeout()));
+
+ t!(stream.set_read_timeout(None));
+ assert_eq!(None, t!(stream.read_timeout()));
+
+ t!(stream.set_write_timeout(None));
+ assert_eq!(None, t!(stream.write_timeout()));
+}
+
+#[test]
+fn test_read_timeout() {
+ let addr = next_test_ip4();
+
+ let stream = t!(UdpSocket::bind(&addr));
+ t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+ let mut buf = [0; 10];
+
+ let start = Instant::now();
+ loop {
+ let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
+ if kind != ErrorKind::Interrupted {
+ assert!(
+ kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
+ "unexpected_error: {:?}",
+ kind
+ );
+ break;
+ }
+ }
+ assert!(start.elapsed() > Duration::from_millis(400));
+}
+
+#[test]
+fn test_read_with_timeout() {
+ let addr = next_test_ip4();
+
+ let stream = t!(UdpSocket::bind(&addr));
+ t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+ t!(stream.send_to(b"hello world", &addr));
+
+ let mut buf = [0; 11];
+ t!(stream.recv_from(&mut buf));
+ assert_eq!(b"hello world", &buf[..]);
+
+ let start = Instant::now();
+ loop {
+ let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
+ if kind != ErrorKind::Interrupted {
+ assert!(
+ kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
+ "unexpected_error: {:?}",
+ kind
+ );
+ break;
+ }
+ }
+ assert!(start.elapsed() > Duration::from_millis(400));
+}
+
+// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+// when passed zero Durations
+#[test]
+fn test_timeout_zero_duration() {
+ let addr = next_test_ip4();
+
+ let socket = t!(UdpSocket::bind(&addr));
+
+ let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+ let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+}
+
+#[test]
+fn connect_send_recv() {
+ let addr = next_test_ip4();
+
+ let socket = t!(UdpSocket::bind(&addr));
+ t!(socket.connect(addr));
+
+ t!(socket.send(b"hello world"));
+
+ let mut buf = [0; 11];
+ t!(socket.recv(&mut buf));
+ assert_eq!(b"hello world", &buf[..]);
+}
+
+#[test]
+fn connect_send_peek_recv() {
+ each_ip(&mut |addr, _| {
+ let socket = t!(UdpSocket::bind(&addr));
+ t!(socket.connect(addr));
+
+ t!(socket.send(b"hello world"));
+
+ for _ in 1..3 {
+ let mut buf = [0; 11];
+ let size = t!(socket.peek(&mut buf));
+ assert_eq!(b"hello world", &buf[..]);
+ assert_eq!(size, 11);
+ }
+
+ let mut buf = [0; 11];
+ let size = t!(socket.recv(&mut buf));
+ assert_eq!(b"hello world", &buf[..]);
+ assert_eq!(size, 11);
+ })
+}
+
+#[test]
+fn peek_from() {
+ each_ip(&mut |addr, _| {
+ let socket = t!(UdpSocket::bind(&addr));
+ t!(socket.send_to(b"hello world", &addr));
+
+ for _ in 1..3 {
+ let mut buf = [0; 11];
+ let (size, _) = t!(socket.peek_from(&mut buf));
+ assert_eq!(b"hello world", &buf[..]);
+ assert_eq!(size, 11);
+ }
+
+ let mut buf = [0; 11];
+ let (size, _) = t!(socket.recv_from(&mut buf));
+ assert_eq!(b"hello world", &buf[..]);
+ assert_eq!(size, 11);
+ })
+}
+
+#[test]
+fn ttl() {
+ let ttl = 100;
+
+ let addr = next_test_ip4();
+
+ let stream = t!(UdpSocket::bind(&addr));
+
+ t!(stream.set_ttl(ttl));
+ assert_eq!(ttl, t!(stream.ttl()));
+}
+
+#[test]
+fn set_nonblocking() {
+ each_ip(&mut |addr, _| {
+ let socket = t!(UdpSocket::bind(&addr));
+
+ t!(socket.set_nonblocking(true));
+ t!(socket.set_nonblocking(false));
+
+ t!(socket.connect(addr));
+
+ t!(socket.set_nonblocking(false));
+ t!(socket.set_nonblocking(true));
+
+ let mut buf = [0];
+ match socket.recv(&mut buf) {
+ Ok(_) => panic!("expected error"),
+ Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
+ Err(e) => panic!("unexpected error {}", e),
+ }
+ })
+}
diff --git a/library/std/src/num.rs b/library/std/src/num.rs
index b496c16..0f1c596 100644
--- a/library/std/src/num.rs
+++ b/library/std/src/num.rs
@@ -6,6 +6,12 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
+#[cfg(test)]
+mod tests;
+
+#[cfg(test)]
+mod benches;
+
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::num::Wrapping;
#[stable(feature = "rust1", since = "1.0.0")]
@@ -48,250 +54,3 @@
assert_eq!(ten.div(two), ten / two);
assert_eq!(ten.rem(two), ten % two);
}
-
-#[cfg(test)]
-mod tests {
- use crate::ops::Mul;
-
- #[test]
- fn test_saturating_add_uint() {
- assert_eq!(3_usize.saturating_add(5_usize), 8_usize);
- assert_eq!(3_usize.saturating_add(usize::MAX - 1), usize::MAX);
- assert_eq!(usize::MAX.saturating_add(usize::MAX), usize::MAX);
- assert_eq!((usize::MAX - 2).saturating_add(1), usize::MAX - 1);
- }
-
- #[test]
- fn test_saturating_sub_uint() {
- assert_eq!(5_usize.saturating_sub(3_usize), 2_usize);
- assert_eq!(3_usize.saturating_sub(5_usize), 0_usize);
- assert_eq!(0_usize.saturating_sub(1_usize), 0_usize);
- assert_eq!((usize::MAX - 1).saturating_sub(usize::MAX), 0);
- }
-
- #[test]
- fn test_saturating_add_int() {
- assert_eq!(3i32.saturating_add(5), 8);
- assert_eq!(3isize.saturating_add(isize::MAX - 1), isize::MAX);
- assert_eq!(isize::MAX.saturating_add(isize::MAX), isize::MAX);
- assert_eq!((isize::MAX - 2).saturating_add(1), isize::MAX - 1);
- assert_eq!(3i32.saturating_add(-5), -2);
- assert_eq!(isize::MIN.saturating_add(-1), isize::MIN);
- assert_eq!((-2isize).saturating_add(-isize::MAX), isize::MIN);
- }
-
- #[test]
- fn test_saturating_sub_int() {
- assert_eq!(3i32.saturating_sub(5), -2);
- assert_eq!(isize::MIN.saturating_sub(1), isize::MIN);
- assert_eq!((-2isize).saturating_sub(isize::MAX), isize::MIN);
- assert_eq!(3i32.saturating_sub(-5), 8);
- assert_eq!(3isize.saturating_sub(-(isize::MAX - 1)), isize::MAX);
- assert_eq!(isize::MAX.saturating_sub(-isize::MAX), isize::MAX);
- assert_eq!((isize::MAX - 2).saturating_sub(-1), isize::MAX - 1);
- }
-
- #[test]
- fn test_checked_add() {
- let five_less = usize::MAX - 5;
- assert_eq!(five_less.checked_add(0), Some(usize::MAX - 5));
- assert_eq!(five_less.checked_add(1), Some(usize::MAX - 4));
- assert_eq!(five_less.checked_add(2), Some(usize::MAX - 3));
- assert_eq!(five_less.checked_add(3), Some(usize::MAX - 2));
- assert_eq!(five_less.checked_add(4), Some(usize::MAX - 1));
- assert_eq!(five_less.checked_add(5), Some(usize::MAX));
- assert_eq!(five_less.checked_add(6), None);
- assert_eq!(five_less.checked_add(7), None);
- }
-
- #[test]
- fn test_checked_sub() {
- assert_eq!(5_usize.checked_sub(0), Some(5));
- assert_eq!(5_usize.checked_sub(1), Some(4));
- assert_eq!(5_usize.checked_sub(2), Some(3));
- assert_eq!(5_usize.checked_sub(3), Some(2));
- assert_eq!(5_usize.checked_sub(4), Some(1));
- assert_eq!(5_usize.checked_sub(5), Some(0));
- assert_eq!(5_usize.checked_sub(6), None);
- assert_eq!(5_usize.checked_sub(7), None);
- }
-
- #[test]
- fn test_checked_mul() {
- let third = usize::MAX / 3;
- assert_eq!(third.checked_mul(0), Some(0));
- assert_eq!(third.checked_mul(1), Some(third));
- assert_eq!(third.checked_mul(2), Some(third * 2));
- assert_eq!(third.checked_mul(3), Some(third * 3));
- assert_eq!(third.checked_mul(4), None);
- }
-
- macro_rules! test_is_power_of_two {
- ($test_name:ident, $T:ident) => {
- fn $test_name() {
- #![test]
- assert_eq!((0 as $T).is_power_of_two(), false);
- assert_eq!((1 as $T).is_power_of_two(), true);
- assert_eq!((2 as $T).is_power_of_two(), true);
- assert_eq!((3 as $T).is_power_of_two(), false);
- assert_eq!((4 as $T).is_power_of_two(), true);
- assert_eq!((5 as $T).is_power_of_two(), false);
- assert_eq!(($T::MAX / 2 + 1).is_power_of_two(), true);
- }
- };
- }
-
- test_is_power_of_two! { test_is_power_of_two_u8, u8 }
- test_is_power_of_two! { test_is_power_of_two_u16, u16 }
- test_is_power_of_two! { test_is_power_of_two_u32, u32 }
- test_is_power_of_two! { test_is_power_of_two_u64, u64 }
- test_is_power_of_two! { test_is_power_of_two_uint, usize }
-
- macro_rules! test_next_power_of_two {
- ($test_name:ident, $T:ident) => {
- fn $test_name() {
- #![test]
- assert_eq!((0 as $T).next_power_of_two(), 1);
- let mut next_power = 1;
- for i in 1 as $T..40 {
- assert_eq!(i.next_power_of_two(), next_power);
- if i == next_power {
- next_power *= 2
- }
- }
- }
- };
- }
-
- test_next_power_of_two! { test_next_power_of_two_u8, u8 }
- test_next_power_of_two! { test_next_power_of_two_u16, u16 }
- test_next_power_of_two! { test_next_power_of_two_u32, u32 }
- test_next_power_of_two! { test_next_power_of_two_u64, u64 }
- test_next_power_of_two! { test_next_power_of_two_uint, usize }
-
- macro_rules! test_checked_next_power_of_two {
- ($test_name:ident, $T:ident) => {
- fn $test_name() {
- #![test]
- assert_eq!((0 as $T).checked_next_power_of_two(), Some(1));
- let smax = $T::MAX >> 1;
- assert_eq!(smax.checked_next_power_of_two(), Some(smax + 1));
- assert_eq!((smax + 1).checked_next_power_of_two(), Some(smax + 1));
- assert_eq!((smax + 2).checked_next_power_of_two(), None);
- assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None);
- assert_eq!($T::MAX.checked_next_power_of_two(), None);
- let mut next_power = 1;
- for i in 1 as $T..40 {
- assert_eq!(i.checked_next_power_of_two(), Some(next_power));
- if i == next_power {
- next_power *= 2
- }
- }
- }
- };
- }
-
- test_checked_next_power_of_two! { test_checked_next_power_of_two_u8, u8 }
- test_checked_next_power_of_two! { test_checked_next_power_of_two_u16, u16 }
- test_checked_next_power_of_two! { test_checked_next_power_of_two_u32, u32 }
- test_checked_next_power_of_two! { test_checked_next_power_of_two_u64, u64 }
- test_checked_next_power_of_two! { test_checked_next_power_of_two_uint, usize }
-
- #[test]
- fn test_pow() {
- fn naive_pow<T: Mul<Output = T> + Copy>(one: T, base: T, exp: usize) -> T {
- (0..exp).fold(one, |acc, _| acc * base)
- }
- macro_rules! assert_pow {
- (($num:expr, $exp:expr) => $expected:expr) => {{
- let result = $num.pow($exp);
- assert_eq!(result, $expected);
- assert_eq!(result, naive_pow(1, $num, $exp));
- }};
- }
- assert_pow!((3u32, 0 ) => 1);
- assert_pow!((5u32, 1 ) => 5);
- assert_pow!((-4i32, 2 ) => 16);
- assert_pow!((8u32, 3 ) => 512);
- assert_pow!((2u64, 50) => 1125899906842624);
- }
-
- #[test]
- fn test_uint_to_str_overflow() {
- let mut u8_val: u8 = 255;
- assert_eq!(u8_val.to_string(), "255");
-
- u8_val = u8_val.wrapping_add(1);
- assert_eq!(u8_val.to_string(), "0");
-
- let mut u16_val: u16 = 65_535;
- assert_eq!(u16_val.to_string(), "65535");
-
- u16_val = u16_val.wrapping_add(1);
- assert_eq!(u16_val.to_string(), "0");
-
- let mut u32_val: u32 = 4_294_967_295;
- assert_eq!(u32_val.to_string(), "4294967295");
-
- u32_val = u32_val.wrapping_add(1);
- assert_eq!(u32_val.to_string(), "0");
-
- let mut u64_val: u64 = 18_446_744_073_709_551_615;
- assert_eq!(u64_val.to_string(), "18446744073709551615");
-
- u64_val = u64_val.wrapping_add(1);
- assert_eq!(u64_val.to_string(), "0");
- }
-
- fn from_str<T: crate::str::FromStr>(t: &str) -> Option<T> {
- crate::str::FromStr::from_str(t).ok()
- }
-
- #[test]
- fn test_uint_from_str_overflow() {
- let mut u8_val: u8 = 255;
- assert_eq!(from_str::<u8>("255"), Some(u8_val));
- assert_eq!(from_str::<u8>("256"), None);
-
- u8_val = u8_val.wrapping_add(1);
- assert_eq!(from_str::<u8>("0"), Some(u8_val));
- assert_eq!(from_str::<u8>("-1"), None);
-
- let mut u16_val: u16 = 65_535;
- assert_eq!(from_str::<u16>("65535"), Some(u16_val));
- assert_eq!(from_str::<u16>("65536"), None);
-
- u16_val = u16_val.wrapping_add(1);
- assert_eq!(from_str::<u16>("0"), Some(u16_val));
- assert_eq!(from_str::<u16>("-1"), None);
-
- let mut u32_val: u32 = 4_294_967_295;
- assert_eq!(from_str::<u32>("4294967295"), Some(u32_val));
- assert_eq!(from_str::<u32>("4294967296"), None);
-
- u32_val = u32_val.wrapping_add(1);
- assert_eq!(from_str::<u32>("0"), Some(u32_val));
- assert_eq!(from_str::<u32>("-1"), None);
-
- let mut u64_val: u64 = 18_446_744_073_709_551_615;
- assert_eq!(from_str::<u64>("18446744073709551615"), Some(u64_val));
- assert_eq!(from_str::<u64>("18446744073709551616"), None);
-
- u64_val = u64_val.wrapping_add(1);
- assert_eq!(from_str::<u64>("0"), Some(u64_val));
- assert_eq!(from_str::<u64>("-1"), None);
- }
-}
-
-#[cfg(test)]
-mod bench {
- use test::Bencher;
-
- #[bench]
- fn bench_pow_function(b: &mut Bencher) {
- let v = (0..1024).collect::<Vec<u32>>();
- b.iter(|| {
- v.iter().fold(0u32, |old, new| old.pow(*new as u32));
- });
- }
-}
diff --git a/library/std/src/num/benches.rs b/library/std/src/num/benches.rs
new file mode 100644
index 0000000..233ea05
--- /dev/null
+++ b/library/std/src/num/benches.rs
@@ -0,0 +1,9 @@
+use test::Bencher;
+
+#[bench]
+fn bench_pow_function(b: &mut Bencher) {
+ let v = (0..1024).collect::<Vec<u32>>();
+ b.iter(|| {
+ v.iter().fold(0u32, |old, new| old.pow(*new as u32));
+ });
+}
diff --git a/library/std/src/num/tests.rs b/library/std/src/num/tests.rs
new file mode 100644
index 0000000..2f50b73
--- /dev/null
+++ b/library/std/src/num/tests.rs
@@ -0,0 +1,230 @@
+use crate::ops::Mul;
+
+#[test]
+fn test_saturating_add_uint() {
+ assert_eq!(3_usize.saturating_add(5_usize), 8_usize);
+ assert_eq!(3_usize.saturating_add(usize::MAX - 1), usize::MAX);
+ assert_eq!(usize::MAX.saturating_add(usize::MAX), usize::MAX);
+ assert_eq!((usize::MAX - 2).saturating_add(1), usize::MAX - 1);
+}
+
+#[test]
+fn test_saturating_sub_uint() {
+ assert_eq!(5_usize.saturating_sub(3_usize), 2_usize);
+ assert_eq!(3_usize.saturating_sub(5_usize), 0_usize);
+ assert_eq!(0_usize.saturating_sub(1_usize), 0_usize);
+ assert_eq!((usize::MAX - 1).saturating_sub(usize::MAX), 0);
+}
+
+#[test]
+fn test_saturating_add_int() {
+ assert_eq!(3i32.saturating_add(5), 8);
+ assert_eq!(3isize.saturating_add(isize::MAX - 1), isize::MAX);
+ assert_eq!(isize::MAX.saturating_add(isize::MAX), isize::MAX);
+ assert_eq!((isize::MAX - 2).saturating_add(1), isize::MAX - 1);
+ assert_eq!(3i32.saturating_add(-5), -2);
+ assert_eq!(isize::MIN.saturating_add(-1), isize::MIN);
+ assert_eq!((-2isize).saturating_add(-isize::MAX), isize::MIN);
+}
+
+#[test]
+fn test_saturating_sub_int() {
+ assert_eq!(3i32.saturating_sub(5), -2);
+ assert_eq!(isize::MIN.saturating_sub(1), isize::MIN);
+ assert_eq!((-2isize).saturating_sub(isize::MAX), isize::MIN);
+ assert_eq!(3i32.saturating_sub(-5), 8);
+ assert_eq!(3isize.saturating_sub(-(isize::MAX - 1)), isize::MAX);
+ assert_eq!(isize::MAX.saturating_sub(-isize::MAX), isize::MAX);
+ assert_eq!((isize::MAX - 2).saturating_sub(-1), isize::MAX - 1);
+}
+
+#[test]
+fn test_checked_add() {
+ let five_less = usize::MAX - 5;
+ assert_eq!(five_less.checked_add(0), Some(usize::MAX - 5));
+ assert_eq!(five_less.checked_add(1), Some(usize::MAX - 4));
+ assert_eq!(five_less.checked_add(2), Some(usize::MAX - 3));
+ assert_eq!(five_less.checked_add(3), Some(usize::MAX - 2));
+ assert_eq!(five_less.checked_add(4), Some(usize::MAX - 1));
+ assert_eq!(five_less.checked_add(5), Some(usize::MAX));
+ assert_eq!(five_less.checked_add(6), None);
+ assert_eq!(five_less.checked_add(7), None);
+}
+
+#[test]
+fn test_checked_sub() {
+ assert_eq!(5_usize.checked_sub(0), Some(5));
+ assert_eq!(5_usize.checked_sub(1), Some(4));
+ assert_eq!(5_usize.checked_sub(2), Some(3));
+ assert_eq!(5_usize.checked_sub(3), Some(2));
+ assert_eq!(5_usize.checked_sub(4), Some(1));
+ assert_eq!(5_usize.checked_sub(5), Some(0));
+ assert_eq!(5_usize.checked_sub(6), None);
+ assert_eq!(5_usize.checked_sub(7), None);
+}
+
+#[test]
+fn test_checked_mul() {
+ let third = usize::MAX / 3;
+ assert_eq!(third.checked_mul(0), Some(0));
+ assert_eq!(third.checked_mul(1), Some(third));
+ assert_eq!(third.checked_mul(2), Some(third * 2));
+ assert_eq!(third.checked_mul(3), Some(third * 3));
+ assert_eq!(third.checked_mul(4), None);
+}
+
+macro_rules! test_is_power_of_two {
+ ($test_name:ident, $T:ident) => {
+ fn $test_name() {
+ #![test]
+ assert_eq!((0 as $T).is_power_of_two(), false);
+ assert_eq!((1 as $T).is_power_of_two(), true);
+ assert_eq!((2 as $T).is_power_of_two(), true);
+ assert_eq!((3 as $T).is_power_of_two(), false);
+ assert_eq!((4 as $T).is_power_of_two(), true);
+ assert_eq!((5 as $T).is_power_of_two(), false);
+ assert_eq!(($T::MAX / 2 + 1).is_power_of_two(), true);
+ }
+ };
+}
+
+test_is_power_of_two! { test_is_power_of_two_u8, u8 }
+test_is_power_of_two! { test_is_power_of_two_u16, u16 }
+test_is_power_of_two! { test_is_power_of_two_u32, u32 }
+test_is_power_of_two! { test_is_power_of_two_u64, u64 }
+test_is_power_of_two! { test_is_power_of_two_uint, usize }
+
+macro_rules! test_next_power_of_two {
+ ($test_name:ident, $T:ident) => {
+ fn $test_name() {
+ #![test]
+ assert_eq!((0 as $T).next_power_of_two(), 1);
+ let mut next_power = 1;
+ for i in 1 as $T..40 {
+ assert_eq!(i.next_power_of_two(), next_power);
+ if i == next_power {
+ next_power *= 2
+ }
+ }
+ }
+ };
+}
+
+test_next_power_of_two! { test_next_power_of_two_u8, u8 }
+test_next_power_of_two! { test_next_power_of_two_u16, u16 }
+test_next_power_of_two! { test_next_power_of_two_u32, u32 }
+test_next_power_of_two! { test_next_power_of_two_u64, u64 }
+test_next_power_of_two! { test_next_power_of_two_uint, usize }
+
+macro_rules! test_checked_next_power_of_two {
+ ($test_name:ident, $T:ident) => {
+ fn $test_name() {
+ #![test]
+ assert_eq!((0 as $T).checked_next_power_of_two(), Some(1));
+ let smax = $T::MAX >> 1;
+ assert_eq!(smax.checked_next_power_of_two(), Some(smax + 1));
+ assert_eq!((smax + 1).checked_next_power_of_two(), Some(smax + 1));
+ assert_eq!((smax + 2).checked_next_power_of_two(), None);
+ assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None);
+ assert_eq!($T::MAX.checked_next_power_of_two(), None);
+ let mut next_power = 1;
+ for i in 1 as $T..40 {
+ assert_eq!(i.checked_next_power_of_two(), Some(next_power));
+ if i == next_power {
+ next_power *= 2
+ }
+ }
+ }
+ };
+}
+
+test_checked_next_power_of_two! { test_checked_next_power_of_two_u8, u8 }
+test_checked_next_power_of_two! { test_checked_next_power_of_two_u16, u16 }
+test_checked_next_power_of_two! { test_checked_next_power_of_two_u32, u32 }
+test_checked_next_power_of_two! { test_checked_next_power_of_two_u64, u64 }
+test_checked_next_power_of_two! { test_checked_next_power_of_two_uint, usize }
+
+#[test]
+fn test_pow() {
+ fn naive_pow<T: Mul<Output = T> + Copy>(one: T, base: T, exp: usize) -> T {
+ (0..exp).fold(one, |acc, _| acc * base)
+ }
+ macro_rules! assert_pow {
+ (($num:expr, $exp:expr) => $expected:expr) => {{
+ let result = $num.pow($exp);
+ assert_eq!(result, $expected);
+ assert_eq!(result, naive_pow(1, $num, $exp));
+ }};
+ }
+ assert_pow!((3u32, 0 ) => 1);
+ assert_pow!((5u32, 1 ) => 5);
+ assert_pow!((-4i32, 2 ) => 16);
+ assert_pow!((8u32, 3 ) => 512);
+ assert_pow!((2u64, 50) => 1125899906842624);
+}
+
+#[test]
+fn test_uint_to_str_overflow() {
+ let mut u8_val: u8 = 255;
+ assert_eq!(u8_val.to_string(), "255");
+
+ u8_val = u8_val.wrapping_add(1);
+ assert_eq!(u8_val.to_string(), "0");
+
+ let mut u16_val: u16 = 65_535;
+ assert_eq!(u16_val.to_string(), "65535");
+
+ u16_val = u16_val.wrapping_add(1);
+ assert_eq!(u16_val.to_string(), "0");
+
+ let mut u32_val: u32 = 4_294_967_295;
+ assert_eq!(u32_val.to_string(), "4294967295");
+
+ u32_val = u32_val.wrapping_add(1);
+ assert_eq!(u32_val.to_string(), "0");
+
+ let mut u64_val: u64 = 18_446_744_073_709_551_615;
+ assert_eq!(u64_val.to_string(), "18446744073709551615");
+
+ u64_val = u64_val.wrapping_add(1);
+ assert_eq!(u64_val.to_string(), "0");
+}
+
+fn from_str<T: crate::str::FromStr>(t: &str) -> Option<T> {
+ crate::str::FromStr::from_str(t).ok()
+}
+
+#[test]
+fn test_uint_from_str_overflow() {
+ let mut u8_val: u8 = 255;
+ assert_eq!(from_str::<u8>("255"), Some(u8_val));
+ assert_eq!(from_str::<u8>("256"), None);
+
+ u8_val = u8_val.wrapping_add(1);
+ assert_eq!(from_str::<u8>("0"), Some(u8_val));
+ assert_eq!(from_str::<u8>("-1"), None);
+
+ let mut u16_val: u16 = 65_535;
+ assert_eq!(from_str::<u16>("65535"), Some(u16_val));
+ assert_eq!(from_str::<u16>("65536"), None);
+
+ u16_val = u16_val.wrapping_add(1);
+ assert_eq!(from_str::<u16>("0"), Some(u16_val));
+ assert_eq!(from_str::<u16>("-1"), None);
+
+ let mut u32_val: u32 = 4_294_967_295;
+ assert_eq!(from_str::<u32>("4294967295"), Some(u32_val));
+ assert_eq!(from_str::<u32>("4294967296"), None);
+
+ u32_val = u32_val.wrapping_add(1);
+ assert_eq!(from_str::<u32>("0"), Some(u32_val));
+ assert_eq!(from_str::<u32>("-1"), None);
+
+ let mut u64_val: u64 = 18_446_744_073_709_551_615;
+ assert_eq!(from_str::<u64>("18446744073709551615"), Some(u64_val));
+ assert_eq!(from_str::<u64>("18446744073709551616"), None);
+
+ u64_val = u64_val.wrapping_add(1);
+ assert_eq!(from_str::<u64>("0"), Some(u64_val));
+ assert_eq!(from_str::<u64>("-1"), None);
+}
diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs
index cae65f1..ff23c3d 100644
--- a/library/std/src/os/linux/fs.rs
+++ b/library/std/src/os/linux/fs.rs
@@ -1,3 +1,5 @@
+//! Linux-specific extensions to primitives in the `std::fs` module.
+
#![stable(feature = "metadata_ext", since = "1.1.0")]
use crate::fs::Metadata;
@@ -196,7 +198,7 @@
fn st_atime(&self) -> i64;
/// Returns the last access time of the file, in nanoseconds since [`st_atime`].
///
- /// [`st_atime`]: #tymethod.st_atime
+ /// [`st_atime`]: Self::st_atime
///
/// # Examples
///
@@ -232,7 +234,7 @@
fn st_mtime(&self) -> i64;
/// Returns the last modification time of the file, in nanoseconds since [`st_mtime`].
///
- /// [`st_mtime`]: #tymethod.st_mtime
+ /// [`st_mtime`]: Self::st_mtime
///
/// # Examples
///
@@ -268,7 +270,7 @@
fn st_ctime(&self) -> i64;
/// Returns the last status change time of the file, in nanoseconds since [`st_ctime`].
///
- /// [`st_ctime`]: #tymethod.st_ctime
+ /// [`st_ctime`]: Self::st_ctime
///
/// # Examples
///
diff --git a/library/std/src/os/linux/mod.rs b/library/std/src/os/linux/mod.rs
index d353071..f179a52 100644
--- a/library/std/src/os/linux/mod.rs
+++ b/library/std/src/os/linux/mod.rs
@@ -1,4 +1,4 @@
-//! Linux-specific definitions
+//! Linux-specific definitions.
#![stable(feature = "raw_ext", since = "1.1.0")]
diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs
index eb8589e..617c409 100644
--- a/library/std/src/os/linux/raw.rs
+++ b/library/std/src/os/linux/raw.rs
@@ -1,4 +1,4 @@
-//! Linux-specific raw type definitions
+//! Linux-specific raw type definitions.
#![stable(feature = "raw_ext", since = "1.1.0")]
#![rustc_deprecated(
@@ -29,6 +29,7 @@
target_arch = "x86",
target_arch = "le32",
target_arch = "powerpc",
+ target_arch = "sparc",
target_arch = "arm",
target_arch = "asmjs",
target_arch = "wasm32"
@@ -170,63 +171,63 @@
#[cfg(target_arch = "hexagon")]
mod arch {
- use crate::os::raw::{c_int, c_long, c_longlong, c_ulonglong};
+ use crate::os::raw::{c_int, c_long, c_uint};
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub type blkcnt_t = c_longlong;
+ pub type blkcnt_t = i64;
#[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;
+ pub type ino_t = u64;
#[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;
+ pub type off_t = i64;
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub type time_t = c_long;
+ pub type time_t = i64;
#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")]
pub struct stat {
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_dev: ::dev_t,
+ pub st_dev: u64,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_ino: ::c_ulonglong,
+ pub st_ino: u64,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_mode: ::c_uint,
+ pub st_mode: u32,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_nlink: ::c_uint,
+ pub st_nlink: u32,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_uid: ::c_uint,
+ pub st_uid: u32,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_gid: ::c_uint,
+ pub st_gid: u32,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_rdev: ::c_ulonglong,
+ pub st_rdev: u64,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub __pad1: ::c_ulong,
+ pub __pad1: u32,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_size: ::c_longlong,
+ pub st_size: i64,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_blksize: ::blksize_t,
+ pub st_blksize: i32,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub __pad2: ::c_int,
+ pub __pad2: i32,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_blocks: ::blkcnt_t,
+ pub st_blocks: i64,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_atime: ::time_t,
+ pub st_atime: i64,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_atime_nsec: ::c_long,
+ pub st_atime_nsec: c_long,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_mtime: ::time_t,
+ pub st_mtime: i64,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_mtime_nsec: ::c_long,
+ pub st_mtime_nsec: c_long,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_ctime: ::time_t,
+ pub st_ctime: i64,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub st_ctime_nsec: ::c_long,
+ pub st_ctime_nsec: c_long,
#[stable(feature = "raw_ext", since = "1.1.0")]
- pub __pad3: [::c_int; 2],
+ pub __pad3: [c_int; 2],
}
}
@@ -234,7 +235,8 @@
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64",
- target_arch = "riscv64"
+ target_arch = "riscv64",
+ target_arch = "riscv32"
))]
mod arch {
pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t};
diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs
index 47daf0c..16272aa 100644
--- a/library/std/src/os/raw/mod.rs
+++ b/library/std/src/os/raw/mod.rs
@@ -8,6 +8,9 @@
#![stable(feature = "raw_os", since = "1.1.0")]
+#[cfg(test)]
+mod tests;
+
#[doc(include = "char.md")]
#[cfg(any(
all(
@@ -19,7 +22,8 @@
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
- target_arch = "riscv64"
+ target_arch = "riscv64",
+ target_arch = "riscv32"
)
),
all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")),
@@ -62,7 +66,8 @@
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
- target_arch = "riscv64"
+ target_arch = "riscv64",
+ target_arch = "riscv32"
)
),
all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")),
@@ -144,24 +149,3 @@
#[stable(feature = "raw_os", since = "1.1.0")]
#[doc(no_inline)]
pub use core::ffi::c_void;
-
-#[cfg(test)]
-#[allow(unused_imports)]
-mod tests {
- use crate::any::TypeId;
- use crate::mem;
-
- macro_rules! ok {
- ($($t:ident)*) => {$(
- assert!(TypeId::of::<libc::$t>() == TypeId::of::<raw::$t>(),
- "{} is wrong", stringify!($t));
- )*}
- }
-
- #[test]
- fn same() {
- use crate::os::raw;
- ok!(c_char c_schar c_uchar c_short c_ushort c_int c_uint c_long c_ulong
- c_longlong c_ulonglong c_float c_double);
- }
-}
diff --git a/library/std/src/os/raw/tests.rs b/library/std/src/os/raw/tests.rs
new file mode 100644
index 0000000..e7bb7d7
--- /dev/null
+++ b/library/std/src/os/raw/tests.rs
@@ -0,0 +1,15 @@
+use crate::any::TypeId;
+
+macro_rules! ok {
+ ($($t:ident)*) => {$(
+ assert!(TypeId::of::<libc::$t>() == TypeId::of::<raw::$t>(),
+ "{} is wrong", stringify!($t));
+ )*}
+}
+
+#[test]
+fn same() {
+ use crate::os::raw;
+ ok!(c_char c_schar c_uchar c_short c_ushort c_int c_uint c_long c_ulong
+ c_longlong c_ulonglong c_float c_double);
+}
diff --git a/library/std/src/os/redox/fs.rs b/library/std/src/os/redox/fs.rs
index 94d6565..0f179c8 100644
--- a/library/std/src/os/redox/fs.rs
+++ b/library/std/src/os/redox/fs.rs
@@ -200,7 +200,7 @@
fn st_atime(&self) -> i64;
/// Returns the last access time of the file, in nanoseconds since [`st_atime`].
///
- /// [`st_atime`]: #tymethod.st_atime
+ /// [`st_atime`]: Self::st_atime
///
/// # Examples
///
@@ -236,7 +236,7 @@
fn st_mtime(&self) -> i64;
/// Returns the last modification time of the file, in nanoseconds since [`st_mtime`].
///
- /// [`st_mtime`]: #tymethod.st_mtime
+ /// [`st_mtime`]: Self::st_mtime
///
/// # Examples
///
@@ -272,7 +272,7 @@
fn st_ctime(&self) -> i64;
/// Returns the last status change time of the file, in nanoseconds since [`st_ctime`].
///
- /// [`st_ctime`]: #tymethod.st_ctime
+ /// [`st_ctime`]: Self::st_ctime
///
/// # Examples
///
diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs
index 8fcb240..42818673 100644
--- a/library/std/src/panic.rs
+++ b/library/std/src/panic.rs
@@ -232,16 +232,16 @@
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
impl RefUnwindSafe for atomic::AtomicIsize {}
#[cfg(target_has_atomic_load_store = "8")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
+#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
impl RefUnwindSafe for atomic::AtomicI8 {}
#[cfg(target_has_atomic_load_store = "16")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
+#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
impl RefUnwindSafe for atomic::AtomicI16 {}
#[cfg(target_has_atomic_load_store = "32")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
+#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
impl RefUnwindSafe for atomic::AtomicI32 {}
#[cfg(target_has_atomic_load_store = "64")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
+#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
impl RefUnwindSafe for atomic::AtomicI64 {}
#[cfg(target_has_atomic_load_store = "128")]
#[unstable(feature = "integer_atomics", issue = "32976")]
@@ -251,16 +251,16 @@
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
impl RefUnwindSafe for atomic::AtomicUsize {}
#[cfg(target_has_atomic_load_store = "8")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
+#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
impl RefUnwindSafe for atomic::AtomicU8 {}
#[cfg(target_has_atomic_load_store = "16")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
+#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
impl RefUnwindSafe for atomic::AtomicU16 {}
#[cfg(target_has_atomic_load_store = "32")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
+#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
impl RefUnwindSafe for atomic::AtomicU32 {}
#[cfg(target_has_atomic_load_store = "64")]
-#[unstable(feature = "integer_atomics", issue = "32976")]
+#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
impl RefUnwindSafe for atomic::AtomicU64 {}
#[cfg(target_has_atomic_load_store = "128")]
#[unstable(feature = "integer_atomics", issue = "32976")]
@@ -359,6 +359,9 @@
/// aborting the process as well. This function *only* catches unwinding panics,
/// not those that abort the process.
///
+/// Also note that unwinding into Rust code with a foreign exception (e.g. a
+/// an exception thrown from C++ code) is undefined behavior.
+///
/// # Examples
///
/// ```
@@ -408,3 +411,6 @@
pub fn resume_unwind(payload: Box<dyn Any + Send>) -> ! {
panicking::rust_panic_without_hook(payload)
}
+
+#[cfg(test)]
+mod tests;
diff --git a/library/std/src/panic/tests.rs b/library/std/src/panic/tests.rs
new file mode 100644
index 0000000..b37d740
--- /dev/null
+++ b/library/std/src/panic/tests.rs
@@ -0,0 +1,56 @@
+#![allow(dead_code)]
+
+use crate::cell::RefCell;
+use crate::panic::{AssertUnwindSafe, UnwindSafe};
+use crate::rc::Rc;
+use crate::sync::{Arc, Mutex, RwLock};
+
+struct Foo {
+ a: i32,
+}
+
+fn assert<T: UnwindSafe + ?Sized>() {}
+
+#[test]
+fn panic_safety_traits() {
+ assert::<i32>();
+ assert::<&i32>();
+ assert::<*mut i32>();
+ assert::<*const i32>();
+ assert::<usize>();
+ assert::<str>();
+ assert::<&str>();
+ assert::<Foo>();
+ assert::<&Foo>();
+ assert::<Vec<i32>>();
+ assert::<String>();
+ assert::<RefCell<i32>>();
+ assert::<Box<i32>>();
+ assert::<Mutex<i32>>();
+ assert::<RwLock<i32>>();
+ assert::<&Mutex<i32>>();
+ assert::<&RwLock<i32>>();
+ assert::<Rc<i32>>();
+ assert::<Arc<i32>>();
+ assert::<Box<[u8]>>();
+
+ {
+ trait Trait: UnwindSafe {}
+ assert::<Box<dyn Trait>>();
+ }
+
+ fn bar<T>() {
+ assert::<Mutex<T>>();
+ assert::<RwLock<T>>();
+ }
+
+ fn baz<T: UnwindSafe>() {
+ assert::<Box<T>>();
+ assert::<Vec<T>>();
+ assert::<RefCell<T>>();
+ assert::<AssertUnwindSafe<T>>();
+ assert::<&AssertUnwindSafe<T>>();
+ assert::<Rc<AssertUnwindSafe<T>>>();
+ assert::<Arc<AssertUnwindSafe<T>>>();
+ }
+}
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 08d363a..8dceb12d 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -60,6 +60,14 @@
rtabort!("Rust panics must be rethrown");
}
+/// This function is called by the panic runtime if it catches an exception
+/// object which does not correspond to a Rust panic.
+#[cfg(not(test))]
+#[rustc_std_internal_symbol]
+extern "C" fn __rust_foreign_exception() -> ! {
+ rtabort!("Rust cannot catch foreign exceptions");
+}
+
#[derive(Copy, Clone)]
enum Hook {
Default,
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index afdbdbe..6fa7304 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -1,5 +1,3 @@
-// ignore-tidy-filelength
-
//! Cross-platform path manipulation.
//!
//! This module provides two types, [`PathBuf`] and [`Path`] (akin to [`String`]
@@ -60,6 +58,10 @@
//! [`push`]: PathBuf::push
#![stable(feature = "rust1", since = "1.0.0")]
+#![deny(unsafe_op_in_unsafe_fn)]
+
+#[cfg(test)]
+mod tests;
use crate::borrow::{Borrow, Cow};
use crate::cmp;
@@ -293,7 +295,8 @@
unsafe { &*(s as *const OsStr as *const [u8]) }
}
unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {
- &*(s as *const [u8] as *const OsStr)
+ // SAFETY: see the comment of `os_str_as_u8_slice`
+ unsafe { &*(s as *const [u8] as *const OsStr) }
}
// Detect scheme on Redox
@@ -313,24 +316,21 @@
// basic workhorse for splitting stem and extension
fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {
- unsafe {
- if os_str_as_u8_slice(file) == b".." {
- return (Some(file), None);
- }
+ if os_str_as_u8_slice(file) == b".." {
+ return (Some(file), None);
+ }
- // The unsafety here stems from converting between &OsStr and &[u8]
- // and back. This is safe to do because (1) we only look at ASCII
- // contents of the encoding and (2) new &OsStr values are produced
- // only from ASCII-bounded slices of existing &OsStr values.
-
- let mut iter = os_str_as_u8_slice(file).rsplitn(2, |b| *b == b'.');
- let after = iter.next();
- let before = iter.next();
- if before == Some(b"") {
- (Some(file), None)
- } else {
- (before.map(|s| u8_slice_as_os_str(s)), after.map(|s| u8_slice_as_os_str(s)))
- }
+ // The unsafety here stems from converting between &OsStr and &[u8]
+ // and back. This is safe to do because (1) we only look at ASCII
+ // contents of the encoding and (2) new &OsStr values are produced
+ // only from ASCII-bounded slices of existing &OsStr values.
+ let mut iter = os_str_as_u8_slice(file).rsplitn(2, |b| *b == b'.');
+ let after = iter.next();
+ let before = iter.next();
+ if before == Some(b"") {
+ (Some(file), None)
+ } else {
+ unsafe { (before.map(|s| u8_slice_as_os_str(s)), after.map(|s| u8_slice_as_os_str(s))) }
}
}
@@ -1701,7 +1701,7 @@
// The following (private!) function allows construction of a path from a u8
// slice, which is only safe when it is known to follow the OsStr encoding.
unsafe fn from_u8_slice(s: &[u8]) -> &Path {
- Path::new(u8_slice_as_os_str(s))
+ unsafe { Path::new(u8_slice_as_os_str(s)) }
}
// The following (private!) function reveals the byte encoding used for OsStr.
fn as_u8_slice(&self) -> &[u8] {
@@ -1838,7 +1838,7 @@
// FIXME: Allow Redox prefixes
self.has_root() || has_redox_scheme(self.as_u8_slice())
} else {
- self.has_root() && (cfg!(unix) || self.prefix().is_some())
+ self.has_root() && (cfg!(any(unix, target_os = "wasi")) || self.prefix().is_some())
}
}
@@ -2741,1401 +2741,3 @@
"prefix not found"
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- use crate::rc::Rc;
- use crate::sync::Arc;
-
- macro_rules! t(
- ($path:expr, iter: $iter:expr) => (
- {
- let path = Path::new($path);
-
- // Forward iteration
- let comps = path.iter()
- .map(|p| p.to_string_lossy().into_owned())
- .collect::<Vec<String>>();
- let exp: &[&str] = &$iter;
- let exps = exp.iter().map(|s| s.to_string()).collect::<Vec<String>>();
- assert!(comps == exps, "iter: Expected {:?}, found {:?}",
- exps, comps);
-
- // Reverse iteration
- let comps = Path::new($path).iter().rev()
- .map(|p| p.to_string_lossy().into_owned())
- .collect::<Vec<String>>();
- let exps = exps.into_iter().rev().collect::<Vec<String>>();
- assert!(comps == exps, "iter().rev(): Expected {:?}, found {:?}",
- exps, comps);
- }
- );
-
- ($path:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr) => (
- {
- let path = Path::new($path);
-
- let act_root = path.has_root();
- assert!(act_root == $has_root, "has_root: Expected {:?}, found {:?}",
- $has_root, act_root);
-
- let act_abs = path.is_absolute();
- assert!(act_abs == $is_absolute, "is_absolute: Expected {:?}, found {:?}",
- $is_absolute, act_abs);
- }
- );
-
- ($path:expr, parent: $parent:expr, file_name: $file:expr) => (
- {
- let path = Path::new($path);
-
- let parent = path.parent().map(|p| p.to_str().unwrap());
- let exp_parent: Option<&str> = $parent;
- assert!(parent == exp_parent, "parent: Expected {:?}, found {:?}",
- exp_parent, parent);
-
- let file = path.file_name().map(|p| p.to_str().unwrap());
- let exp_file: Option<&str> = $file;
- assert!(file == exp_file, "file_name: Expected {:?}, found {:?}",
- exp_file, file);
- }
- );
-
- ($path:expr, file_stem: $file_stem:expr, extension: $extension:expr) => (
- {
- let path = Path::new($path);
-
- let stem = path.file_stem().map(|p| p.to_str().unwrap());
- let exp_stem: Option<&str> = $file_stem;
- assert!(stem == exp_stem, "file_stem: Expected {:?}, found {:?}",
- exp_stem, stem);
-
- let ext = path.extension().map(|p| p.to_str().unwrap());
- let exp_ext: Option<&str> = $extension;
- assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}",
- exp_ext, ext);
- }
- );
-
- ($path:expr, iter: $iter:expr,
- has_root: $has_root:expr, is_absolute: $is_absolute:expr,
- parent: $parent:expr, file_name: $file:expr,
- file_stem: $file_stem:expr, extension: $extension:expr) => (
- {
- t!($path, iter: $iter);
- t!($path, has_root: $has_root, is_absolute: $is_absolute);
- t!($path, parent: $parent, file_name: $file);
- t!($path, file_stem: $file_stem, extension: $extension);
- }
- );
- );
-
- #[test]
- fn into() {
- use crate::borrow::Cow;
-
- let static_path = Path::new("/home/foo");
- let static_cow_path: Cow<'static, Path> = static_path.into();
- let pathbuf = PathBuf::from("/home/foo");
-
- {
- let path: &Path = &pathbuf;
- let borrowed_cow_path: Cow<'_, Path> = path.into();
-
- assert_eq!(static_cow_path, borrowed_cow_path);
- }
-
- let owned_cow_path: Cow<'static, Path> = pathbuf.into();
-
- assert_eq!(static_cow_path, owned_cow_path);
- }
-
- #[test]
- #[cfg(unix)]
- pub fn test_decompositions_unix() {
- t!("",
- iter: [],
- has_root: false,
- is_absolute: false,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("foo",
- iter: ["foo"],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("/",
- iter: ["/"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("/foo",
- iter: ["/", "foo"],
- has_root: true,
- is_absolute: true,
- parent: Some("/"),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("foo/",
- iter: ["foo"],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("/foo/",
- iter: ["/", "foo"],
- has_root: true,
- is_absolute: true,
- parent: Some("/"),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("foo/bar",
- iter: ["foo", "bar"],
- has_root: false,
- is_absolute: false,
- parent: Some("foo"),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("/foo/bar",
- iter: ["/", "foo", "bar"],
- has_root: true,
- is_absolute: true,
- parent: Some("/foo"),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("///foo///",
- iter: ["/", "foo"],
- has_root: true,
- is_absolute: true,
- parent: Some("/"),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("///foo///bar",
- iter: ["/", "foo", "bar"],
- has_root: true,
- is_absolute: true,
- parent: Some("///foo"),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("./.",
- iter: ["."],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("/..",
- iter: ["/", ".."],
- has_root: true,
- is_absolute: true,
- parent: Some("/"),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("../",
- iter: [".."],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("foo/.",
- iter: ["foo"],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("foo/..",
- iter: ["foo", ".."],
- has_root: false,
- is_absolute: false,
- parent: Some("foo"),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("foo/./",
- iter: ["foo"],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("foo/./bar",
- iter: ["foo", "bar"],
- has_root: false,
- is_absolute: false,
- parent: Some("foo"),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("foo/../",
- iter: ["foo", ".."],
- has_root: false,
- is_absolute: false,
- parent: Some("foo"),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("foo/../bar",
- iter: ["foo", "..", "bar"],
- has_root: false,
- is_absolute: false,
- parent: Some("foo/.."),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("./a",
- iter: [".", "a"],
- has_root: false,
- is_absolute: false,
- parent: Some("."),
- file_name: Some("a"),
- file_stem: Some("a"),
- extension: None
- );
-
- t!(".",
- iter: ["."],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("./",
- iter: ["."],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("a/b",
- iter: ["a", "b"],
- has_root: false,
- is_absolute: false,
- parent: Some("a"),
- file_name: Some("b"),
- file_stem: Some("b"),
- extension: None
- );
-
- t!("a//b",
- iter: ["a", "b"],
- has_root: false,
- is_absolute: false,
- parent: Some("a"),
- file_name: Some("b"),
- file_stem: Some("b"),
- extension: None
- );
-
- t!("a/./b",
- iter: ["a", "b"],
- has_root: false,
- is_absolute: false,
- parent: Some("a"),
- file_name: Some("b"),
- file_stem: Some("b"),
- extension: None
- );
-
- t!("a/b/c",
- iter: ["a", "b", "c"],
- has_root: false,
- is_absolute: false,
- parent: Some("a/b"),
- file_name: Some("c"),
- file_stem: Some("c"),
- extension: None
- );
-
- t!(".foo",
- iter: [".foo"],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: Some(".foo"),
- file_stem: Some(".foo"),
- extension: None
- );
- }
-
- #[test]
- #[cfg(windows)]
- pub fn test_decompositions_windows() {
- t!("",
- iter: [],
- has_root: false,
- is_absolute: false,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("foo",
- iter: ["foo"],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("/",
- iter: ["\\"],
- has_root: true,
- is_absolute: false,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\",
- iter: ["\\"],
- has_root: true,
- is_absolute: false,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("c:",
- iter: ["c:"],
- has_root: false,
- is_absolute: false,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("c:\\",
- iter: ["c:", "\\"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("c:/",
- iter: ["c:", "\\"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("/foo",
- iter: ["\\", "foo"],
- has_root: true,
- is_absolute: false,
- parent: Some("/"),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("foo/",
- iter: ["foo"],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("/foo/",
- iter: ["\\", "foo"],
- has_root: true,
- is_absolute: false,
- parent: Some("/"),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("foo/bar",
- iter: ["foo", "bar"],
- has_root: false,
- is_absolute: false,
- parent: Some("foo"),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("/foo/bar",
- iter: ["\\", "foo", "bar"],
- has_root: true,
- is_absolute: false,
- parent: Some("/foo"),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("///foo///",
- iter: ["\\", "foo"],
- has_root: true,
- is_absolute: false,
- parent: Some("/"),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("///foo///bar",
- iter: ["\\", "foo", "bar"],
- has_root: true,
- is_absolute: false,
- parent: Some("///foo"),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("./.",
- iter: ["."],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("/..",
- iter: ["\\", ".."],
- has_root: true,
- is_absolute: false,
- parent: Some("/"),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("../",
- iter: [".."],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("foo/.",
- iter: ["foo"],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("foo/..",
- iter: ["foo", ".."],
- has_root: false,
- is_absolute: false,
- parent: Some("foo"),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("foo/./",
- iter: ["foo"],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: Some("foo"),
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("foo/./bar",
- iter: ["foo", "bar"],
- has_root: false,
- is_absolute: false,
- parent: Some("foo"),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("foo/../",
- iter: ["foo", ".."],
- has_root: false,
- is_absolute: false,
- parent: Some("foo"),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("foo/../bar",
- iter: ["foo", "..", "bar"],
- has_root: false,
- is_absolute: false,
- parent: Some("foo/.."),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("./a",
- iter: [".", "a"],
- has_root: false,
- is_absolute: false,
- parent: Some("."),
- file_name: Some("a"),
- file_stem: Some("a"),
- extension: None
- );
-
- t!(".",
- iter: ["."],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("./",
- iter: ["."],
- has_root: false,
- is_absolute: false,
- parent: Some(""),
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("a/b",
- iter: ["a", "b"],
- has_root: false,
- is_absolute: false,
- parent: Some("a"),
- file_name: Some("b"),
- file_stem: Some("b"),
- extension: None
- );
-
- t!("a//b",
- iter: ["a", "b"],
- has_root: false,
- is_absolute: false,
- parent: Some("a"),
- file_name: Some("b"),
- file_stem: Some("b"),
- extension: None
- );
-
- t!("a/./b",
- iter: ["a", "b"],
- has_root: false,
- is_absolute: false,
- parent: Some("a"),
- file_name: Some("b"),
- file_stem: Some("b"),
- extension: None
- );
-
- t!("a/b/c",
- iter: ["a", "b", "c"],
- has_root: false,
- is_absolute: false,
- parent: Some("a/b"),
- file_name: Some("c"),
- file_stem: Some("c"),
- extension: None);
-
- t!("a\\b\\c",
- iter: ["a", "b", "c"],
- has_root: false,
- is_absolute: false,
- parent: Some("a\\b"),
- file_name: Some("c"),
- file_stem: Some("c"),
- extension: None
- );
-
- t!("\\a",
- iter: ["\\", "a"],
- has_root: true,
- is_absolute: false,
- parent: Some("\\"),
- file_name: Some("a"),
- file_stem: Some("a"),
- extension: None
- );
-
- t!("c:\\foo.txt",
- iter: ["c:", "\\", "foo.txt"],
- has_root: true,
- is_absolute: true,
- parent: Some("c:\\"),
- file_name: Some("foo.txt"),
- file_stem: Some("foo"),
- extension: Some("txt")
- );
-
- t!("\\\\server\\share\\foo.txt",
- iter: ["\\\\server\\share", "\\", "foo.txt"],
- has_root: true,
- is_absolute: true,
- parent: Some("\\\\server\\share\\"),
- file_name: Some("foo.txt"),
- file_stem: Some("foo"),
- extension: Some("txt")
- );
-
- t!("\\\\server\\share",
- iter: ["\\\\server\\share", "\\"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\server",
- iter: ["\\", "server"],
- has_root: true,
- is_absolute: false,
- parent: Some("\\"),
- file_name: Some("server"),
- file_stem: Some("server"),
- extension: None
- );
-
- t!("\\\\?\\bar\\foo.txt",
- iter: ["\\\\?\\bar", "\\", "foo.txt"],
- has_root: true,
- is_absolute: true,
- parent: Some("\\\\?\\bar\\"),
- file_name: Some("foo.txt"),
- file_stem: Some("foo"),
- extension: Some("txt")
- );
-
- t!("\\\\?\\bar",
- iter: ["\\\\?\\bar"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\?\\",
- iter: ["\\\\?\\"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\?\\UNC\\server\\share\\foo.txt",
- iter: ["\\\\?\\UNC\\server\\share", "\\", "foo.txt"],
- has_root: true,
- is_absolute: true,
- parent: Some("\\\\?\\UNC\\server\\share\\"),
- file_name: Some("foo.txt"),
- file_stem: Some("foo"),
- extension: Some("txt")
- );
-
- t!("\\\\?\\UNC\\server",
- iter: ["\\\\?\\UNC\\server"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\?\\UNC\\",
- iter: ["\\\\?\\UNC\\"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\?\\C:\\foo.txt",
- iter: ["\\\\?\\C:", "\\", "foo.txt"],
- has_root: true,
- is_absolute: true,
- parent: Some("\\\\?\\C:\\"),
- file_name: Some("foo.txt"),
- file_stem: Some("foo"),
- extension: Some("txt")
- );
-
- t!("\\\\?\\C:\\",
- iter: ["\\\\?\\C:", "\\"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\?\\C:",
- iter: ["\\\\?\\C:"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\?\\foo/bar",
- iter: ["\\\\?\\foo/bar"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\?\\C:/foo",
- iter: ["\\\\?\\C:/foo"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\.\\foo\\bar",
- iter: ["\\\\.\\foo", "\\", "bar"],
- has_root: true,
- is_absolute: true,
- parent: Some("\\\\.\\foo\\"),
- file_name: Some("bar"),
- file_stem: Some("bar"),
- extension: None
- );
-
- t!("\\\\.\\foo",
- iter: ["\\\\.\\foo", "\\"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\.\\foo/bar",
- iter: ["\\\\.\\foo/bar", "\\"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\.\\foo\\bar/baz",
- iter: ["\\\\.\\foo", "\\", "bar", "baz"],
- has_root: true,
- is_absolute: true,
- parent: Some("\\\\.\\foo\\bar"),
- file_name: Some("baz"),
- file_stem: Some("baz"),
- extension: None
- );
-
- t!("\\\\.\\",
- iter: ["\\\\.\\", "\\"],
- has_root: true,
- is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
- extension: None
- );
-
- t!("\\\\?\\a\\b\\",
- iter: ["\\\\?\\a", "\\", "b"],
- has_root: true,
- is_absolute: true,
- parent: Some("\\\\?\\a\\"),
- file_name: Some("b"),
- file_stem: Some("b"),
- extension: None
- );
- }
-
- #[test]
- pub fn test_stem_ext() {
- t!("foo",
- file_stem: Some("foo"),
- extension: None
- );
-
- t!("foo.",
- file_stem: Some("foo"),
- extension: Some("")
- );
-
- t!(".foo",
- file_stem: Some(".foo"),
- extension: None
- );
-
- t!("foo.txt",
- file_stem: Some("foo"),
- extension: Some("txt")
- );
-
- t!("foo.bar.txt",
- file_stem: Some("foo.bar"),
- extension: Some("txt")
- );
-
- t!("foo.bar.",
- file_stem: Some("foo.bar"),
- extension: Some("")
- );
-
- t!(".", file_stem: None, extension: None);
-
- t!("..", file_stem: None, extension: None);
-
- t!("", file_stem: None, extension: None);
- }
-
- #[test]
- pub fn test_push() {
- macro_rules! tp(
- ($path:expr, $push:expr, $expected:expr) => ( {
- let mut actual = PathBuf::from($path);
- actual.push($push);
- assert!(actual.to_str() == Some($expected),
- "pushing {:?} onto {:?}: Expected {:?}, got {:?}",
- $push, $path, $expected, actual.to_str().unwrap());
- });
- );
-
- if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) {
- tp!("", "foo", "foo");
- tp!("foo", "bar", "foo/bar");
- tp!("foo/", "bar", "foo/bar");
- tp!("foo//", "bar", "foo//bar");
- tp!("foo/.", "bar", "foo/./bar");
- tp!("foo./.", "bar", "foo././bar");
- tp!("foo", "", "foo/");
- tp!("foo", ".", "foo/.");
- tp!("foo", "..", "foo/..");
- tp!("foo", "/", "/");
- tp!("/foo/bar", "/", "/");
- tp!("/foo/bar", "/baz", "/baz");
- tp!("/foo/bar", "./baz", "/foo/bar/./baz");
- } else {
- tp!("", "foo", "foo");
- tp!("foo", "bar", r"foo\bar");
- tp!("foo/", "bar", r"foo/bar");
- tp!(r"foo\", "bar", r"foo\bar");
- tp!("foo//", "bar", r"foo//bar");
- tp!(r"foo\\", "bar", r"foo\\bar");
- tp!("foo/.", "bar", r"foo/.\bar");
- tp!("foo./.", "bar", r"foo./.\bar");
- tp!(r"foo\.", "bar", r"foo\.\bar");
- tp!(r"foo.\.", "bar", r"foo.\.\bar");
- tp!("foo", "", "foo\\");
- tp!("foo", ".", r"foo\.");
- tp!("foo", "..", r"foo\..");
- tp!("foo", "/", "/");
- tp!("foo", r"\", r"\");
- tp!("/foo/bar", "/", "/");
- tp!(r"\foo\bar", r"\", r"\");
- tp!("/foo/bar", "/baz", "/baz");
- tp!("/foo/bar", r"\baz", r"\baz");
- tp!("/foo/bar", "./baz", r"/foo/bar\./baz");
- tp!("/foo/bar", r".\baz", r"/foo/bar\.\baz");
-
- tp!("c:\\", "windows", "c:\\windows");
- tp!("c:", "windows", "c:windows");
-
- tp!("a\\b\\c", "d", "a\\b\\c\\d");
- tp!("\\a\\b\\c", "d", "\\a\\b\\c\\d");
- tp!("a\\b", "c\\d", "a\\b\\c\\d");
- tp!("a\\b", "\\c\\d", "\\c\\d");
- tp!("a\\b", ".", "a\\b\\.");
- tp!("a\\b", "..\\c", "a\\b\\..\\c");
- tp!("a\\b", "C:a.txt", "C:a.txt");
- tp!("a\\b", "C:\\a.txt", "C:\\a.txt");
- tp!("C:\\a", "C:\\b.txt", "C:\\b.txt");
- tp!("C:\\a\\b\\c", "C:d", "C:d");
- tp!("C:a\\b\\c", "C:d", "C:d");
- tp!("C:", r"a\b\c", r"C:a\b\c");
- tp!("C:", r"..\a", r"C:..\a");
- tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar");
- tp!("\\\\server\\share\\foo", "C:baz", "C:baz");
- tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d");
- tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d");
- tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d");
- tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz");
- tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar");
- tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a");
- tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a");
-
- // Note: modified from old path API
- tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo");
-
- tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share");
- tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz");
- tp!("\\\\.\\foo\\bar", "C:a", "C:a");
- // again, not sure about the following, but I'm assuming \\.\ should be verbatim
- tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar");
-
- tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one
- }
- }
-
- #[test]
- pub fn test_pop() {
- macro_rules! tp(
- ($path:expr, $expected:expr, $output:expr) => ( {
- let mut actual = PathBuf::from($path);
- let output = actual.pop();
- assert!(actual.to_str() == Some($expected) && output == $output,
- "popping from {:?}: Expected {:?}/{:?}, got {:?}/{:?}",
- $path, $expected, $output,
- actual.to_str().unwrap(), output);
- });
- );
-
- tp!("", "", false);
- tp!("/", "/", false);
- tp!("foo", "", true);
- tp!(".", "", true);
- tp!("/foo", "/", true);
- tp!("/foo/bar", "/foo", true);
- tp!("foo/bar", "foo", true);
- tp!("foo/.", "", true);
- tp!("foo//bar", "foo", true);
-
- if cfg!(windows) {
- tp!("a\\b\\c", "a\\b", true);
- tp!("\\a", "\\", true);
- tp!("\\", "\\", false);
-
- tp!("C:\\a\\b", "C:\\a", true);
- tp!("C:\\a", "C:\\", true);
- tp!("C:\\", "C:\\", false);
- tp!("C:a\\b", "C:a", true);
- tp!("C:a", "C:", true);
- tp!("C:", "C:", false);
- tp!("\\\\server\\share\\a\\b", "\\\\server\\share\\a", true);
- tp!("\\\\server\\share\\a", "\\\\server\\share\\", true);
- tp!("\\\\server\\share", "\\\\server\\share", false);
- tp!("\\\\?\\a\\b\\c", "\\\\?\\a\\b", true);
- tp!("\\\\?\\a\\b", "\\\\?\\a\\", true);
- tp!("\\\\?\\a", "\\\\?\\a", false);
- tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true);
- tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true);
- tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false);
- tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true);
- tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true);
- tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false);
- tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true);
- tp!("\\\\.\\a\\b", "\\\\.\\a\\", true);
- tp!("\\\\.\\a", "\\\\.\\a", false);
-
- tp!("\\\\?\\a\\b\\", "\\\\?\\a\\", true);
- }
- }
-
- #[test]
- pub fn test_set_file_name() {
- macro_rules! tfn(
- ($path:expr, $file:expr, $expected:expr) => ( {
- let mut p = PathBuf::from($path);
- p.set_file_name($file);
- assert!(p.to_str() == Some($expected),
- "setting file name of {:?} to {:?}: Expected {:?}, got {:?}",
- $path, $file, $expected,
- p.to_str().unwrap());
- });
- );
-
- tfn!("foo", "foo", "foo");
- tfn!("foo", "bar", "bar");
- tfn!("foo", "", "");
- tfn!("", "foo", "foo");
- if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) {
- tfn!(".", "foo", "./foo");
- tfn!("foo/", "bar", "bar");
- tfn!("foo/.", "bar", "bar");
- tfn!("..", "foo", "../foo");
- tfn!("foo/..", "bar", "foo/../bar");
- tfn!("/", "foo", "/foo");
- } else {
- tfn!(".", "foo", r".\foo");
- tfn!(r"foo\", "bar", r"bar");
- tfn!(r"foo\.", "bar", r"bar");
- tfn!("..", "foo", r"..\foo");
- tfn!(r"foo\..", "bar", r"foo\..\bar");
- tfn!(r"\", "foo", r"\foo");
- }
- }
-
- #[test]
- pub fn test_set_extension() {
- macro_rules! tfe(
- ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( {
- let mut p = PathBuf::from($path);
- let output = p.set_extension($ext);
- assert!(p.to_str() == Some($expected) && output == $output,
- "setting extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}",
- $path, $ext, $expected, $output,
- p.to_str().unwrap(), output);
- });
- );
-
- tfe!("foo", "txt", "foo.txt", true);
- tfe!("foo.bar", "txt", "foo.txt", true);
- tfe!("foo.bar.baz", "txt", "foo.bar.txt", true);
- tfe!(".test", "txt", ".test.txt", true);
- tfe!("foo.txt", "", "foo", true);
- tfe!("foo", "", "foo", true);
- tfe!("", "foo", "", false);
- tfe!(".", "foo", ".", false);
- tfe!("foo/", "bar", "foo.bar", true);
- tfe!("foo/.", "bar", "foo.bar", true);
- tfe!("..", "foo", "..", false);
- tfe!("foo/..", "bar", "foo/..", false);
- tfe!("/", "foo", "/", false);
- }
-
- #[test]
- fn test_eq_receivers() {
- use crate::borrow::Cow;
-
- let borrowed: &Path = Path::new("foo/bar");
- 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();
-
- macro_rules! t {
- ($($current:expr),+) => {
- $(
- assert_eq!($current, borrowed);
- assert_eq!($current, owned);
- assert_eq!($current, borrowed_cow);
- assert_eq!($current, owned_cow);
- )+
- }
- }
-
- t!(borrowed, owned, borrowed_cow, owned_cow);
- }
-
- #[test]
- pub fn test_compare() {
- use crate::collections::hash_map::DefaultHasher;
- use crate::hash::{Hash, Hasher};
-
- fn hash<T: Hash>(t: T) -> u64 {
- let mut s = DefaultHasher::new();
- t.hash(&mut s);
- s.finish()
- }
-
- macro_rules! tc(
- ($path1:expr, $path2:expr, eq: $eq:expr,
- starts_with: $starts_with:expr, ends_with: $ends_with:expr,
- relative_from: $relative_from:expr) => ({
- let path1 = Path::new($path1);
- let path2 = Path::new($path2);
-
- let eq = path1 == path2;
- assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}",
- $path1, $path2, $eq, eq);
- assert!($eq == (hash(path1) == hash(path2)),
- "{:?} == {:?}, expected {:?}, got {} and {}",
- $path1, $path2, $eq, hash(path1), hash(path2));
-
- let starts_with = path1.starts_with(path2);
- assert!(starts_with == $starts_with,
- "{:?}.starts_with({:?}), expected {:?}, got {:?}", $path1, $path2,
- $starts_with, starts_with);
-
- let ends_with = path1.ends_with(path2);
- assert!(ends_with == $ends_with,
- "{:?}.ends_with({:?}), expected {:?}, got {:?}", $path1, $path2,
- $ends_with, ends_with);
-
- let relative_from = path1.strip_prefix(path2)
- .map(|p| p.to_str().unwrap())
- .ok();
- let exp: Option<&str> = $relative_from;
- assert!(relative_from == exp,
- "{:?}.strip_prefix({:?}), expected {:?}, got {:?}",
- $path1, $path2, exp, relative_from);
- });
- );
-
- tc!("", "",
- eq: true,
- starts_with: true,
- ends_with: true,
- relative_from: Some("")
- );
-
- tc!("foo", "",
- eq: false,
- starts_with: true,
- ends_with: true,
- relative_from: Some("foo")
- );
-
- tc!("", "foo",
- eq: false,
- starts_with: false,
- ends_with: false,
- relative_from: None
- );
-
- tc!("foo", "foo",
- eq: true,
- starts_with: true,
- ends_with: true,
- relative_from: Some("")
- );
-
- tc!("foo/", "foo",
- eq: true,
- starts_with: true,
- ends_with: true,
- relative_from: Some("")
- );
-
- tc!("foo/bar", "foo",
- eq: false,
- starts_with: true,
- ends_with: false,
- relative_from: Some("bar")
- );
-
- tc!("foo/bar/baz", "foo/bar",
- eq: false,
- starts_with: true,
- ends_with: false,
- relative_from: Some("baz")
- );
-
- tc!("foo/bar", "foo/bar/baz",
- eq: false,
- starts_with: false,
- ends_with: false,
- relative_from: None
- );
-
- tc!("./foo/bar/", ".",
- eq: false,
- starts_with: true,
- ends_with: false,
- relative_from: Some("foo/bar")
- );
-
- if cfg!(windows) {
- tc!(r"C:\src\rust\cargo-test\test\Cargo.toml",
- r"c:\src\rust\cargo-test\test",
- eq: false,
- starts_with: true,
- ends_with: false,
- relative_from: Some("Cargo.toml")
- );
-
- tc!(r"c:\foo", r"C:\foo",
- eq: true,
- starts_with: true,
- ends_with: true,
- relative_from: Some("")
- );
- }
- }
-
- #[test]
- fn test_components_debug() {
- let path = Path::new("/tmp");
-
- let mut components = path.components();
-
- let expected = "Components([RootDir, Normal(\"tmp\")])";
- let actual = format!("{:?}", components);
- assert_eq!(expected, actual);
-
- let _ = components.next().unwrap();
- let expected = "Components([Normal(\"tmp\")])";
- let actual = format!("{:?}", components);
- assert_eq!(expected, actual);
-
- let _ = components.next().unwrap();
- let expected = "Components([])";
- let actual = format!("{:?}", components);
- assert_eq!(expected, actual);
- }
-
- #[cfg(unix)]
- #[test]
- fn test_iter_debug() {
- let path = Path::new("/tmp");
-
- let mut iter = path.iter();
-
- let expected = "Iter([\"/\", \"tmp\"])";
- let actual = format!("{:?}", iter);
- assert_eq!(expected, actual);
-
- let _ = iter.next().unwrap();
- let expected = "Iter([\"tmp\"])";
- let actual = format!("{:?}", iter);
- assert_eq!(expected, actual);
-
- let _ = iter.next().unwrap();
- let expected = "Iter([])";
- let actual = format!("{:?}", iter);
- assert_eq!(expected, actual);
- }
-
- #[test]
- fn into_boxed() {
- let orig: &str = "some/sort/of/path";
- let path = Path::new(orig);
- let boxed: Box<Path> = Box::from(path);
- let path_buf = path.to_owned().into_boxed_path().into_path_buf();
- assert_eq!(path, &*boxed);
- assert_eq!(&*boxed, &*path_buf);
- assert_eq!(&*path_buf, path);
- }
-
- #[test]
- fn test_clone_into() {
- let mut path_buf = PathBuf::from("supercalifragilisticexpialidocious");
- let path = Path::new("short");
- path.clone_into(&mut path_buf);
- assert_eq!(path, path_buf);
- assert!(path_buf.into_os_string().capacity() >= 15);
- }
-
- #[test]
- fn display_format_flags() {
- assert_eq!(format!("a{:#<5}b", Path::new("").display()), "a#####b");
- assert_eq!(format!("a{:#<5}b", Path::new("a").display()), "aa####b");
- }
-
- #[test]
- fn into_rc() {
- let orig = "hello/world";
- let path = Path::new(orig);
- let rc: Rc<Path> = Rc::from(path);
- let arc: Arc<Path> = Arc::from(path);
-
- assert_eq!(&*rc, path);
- assert_eq!(&*arc, path);
-
- let rc2: Rc<Path> = Rc::from(path.to_owned());
- let arc2: Arc<Path> = Arc::from(path.to_owned());
-
- assert_eq!(&*rc2, path);
- assert_eq!(&*arc2, path);
- }
-}
diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
new file mode 100644
index 0000000..ff94fda
--- /dev/null
+++ b/library/std/src/path/tests.rs
@@ -0,0 +1,1394 @@
+use super::*;
+
+use crate::rc::Rc;
+use crate::sync::Arc;
+
+macro_rules! t(
+ ($path:expr, iter: $iter:expr) => (
+ {
+ let path = Path::new($path);
+
+ // Forward iteration
+ let comps = path.iter()
+ .map(|p| p.to_string_lossy().into_owned())
+ .collect::<Vec<String>>();
+ let exp: &[&str] = &$iter;
+ let exps = exp.iter().map(|s| s.to_string()).collect::<Vec<String>>();
+ assert!(comps == exps, "iter: Expected {:?}, found {:?}",
+ exps, comps);
+
+ // Reverse iteration
+ let comps = Path::new($path).iter().rev()
+ .map(|p| p.to_string_lossy().into_owned())
+ .collect::<Vec<String>>();
+ let exps = exps.into_iter().rev().collect::<Vec<String>>();
+ assert!(comps == exps, "iter().rev(): Expected {:?}, found {:?}",
+ exps, comps);
+ }
+ );
+
+ ($path:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr) => (
+ {
+ let path = Path::new($path);
+
+ let act_root = path.has_root();
+ assert!(act_root == $has_root, "has_root: Expected {:?}, found {:?}",
+ $has_root, act_root);
+
+ let act_abs = path.is_absolute();
+ assert!(act_abs == $is_absolute, "is_absolute: Expected {:?}, found {:?}",
+ $is_absolute, act_abs);
+ }
+ );
+
+ ($path:expr, parent: $parent:expr, file_name: $file:expr) => (
+ {
+ let path = Path::new($path);
+
+ let parent = path.parent().map(|p| p.to_str().unwrap());
+ let exp_parent: Option<&str> = $parent;
+ assert!(parent == exp_parent, "parent: Expected {:?}, found {:?}",
+ exp_parent, parent);
+
+ let file = path.file_name().map(|p| p.to_str().unwrap());
+ let exp_file: Option<&str> = $file;
+ assert!(file == exp_file, "file_name: Expected {:?}, found {:?}",
+ exp_file, file);
+ }
+ );
+
+ ($path:expr, file_stem: $file_stem:expr, extension: $extension:expr) => (
+ {
+ let path = Path::new($path);
+
+ let stem = path.file_stem().map(|p| p.to_str().unwrap());
+ let exp_stem: Option<&str> = $file_stem;
+ assert!(stem == exp_stem, "file_stem: Expected {:?}, found {:?}",
+ exp_stem, stem);
+
+ let ext = path.extension().map(|p| p.to_str().unwrap());
+ let exp_ext: Option<&str> = $extension;
+ assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}",
+ exp_ext, ext);
+ }
+ );
+
+ ($path:expr, iter: $iter:expr,
+ has_root: $has_root:expr, is_absolute: $is_absolute:expr,
+ parent: $parent:expr, file_name: $file:expr,
+ file_stem: $file_stem:expr, extension: $extension:expr) => (
+ {
+ t!($path, iter: $iter);
+ t!($path, has_root: $has_root, is_absolute: $is_absolute);
+ t!($path, parent: $parent, file_name: $file);
+ t!($path, file_stem: $file_stem, extension: $extension);
+ }
+ );
+);
+
+#[test]
+fn into() {
+ use crate::borrow::Cow;
+
+ let static_path = Path::new("/home/foo");
+ let static_cow_path: Cow<'static, Path> = static_path.into();
+ let pathbuf = PathBuf::from("/home/foo");
+
+ {
+ let path: &Path = &pathbuf;
+ let borrowed_cow_path: Cow<'_, Path> = path.into();
+
+ assert_eq!(static_cow_path, borrowed_cow_path);
+ }
+
+ let owned_cow_path: Cow<'static, Path> = pathbuf.into();
+
+ assert_eq!(static_cow_path, owned_cow_path);
+}
+
+#[test]
+#[cfg(unix)]
+pub fn test_decompositions_unix() {
+ t!("",
+ iter: [],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("/",
+ iter: ["/"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/foo",
+ iter: ["/", "foo"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("/"),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("/foo/",
+ iter: ["/", "foo"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("/"),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/bar",
+ iter: ["foo", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("/foo/bar",
+ iter: ["/", "foo", "bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("/foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("///foo///",
+ iter: ["/", "foo"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("/"),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("///foo///bar",
+ iter: ["/", "foo", "bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("///foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("./.",
+ iter: ["."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/..",
+ iter: ["/", ".."],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("/"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("../",
+ iter: [".."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/.",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/..",
+ iter: ["foo", ".."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/./",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/./bar",
+ iter: ["foo", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("foo/../",
+ iter: ["foo", ".."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/../bar",
+ iter: ["foo", "..", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/.."),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("./a",
+ iter: [".", "a"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: Some("a"),
+ file_stem: Some("a"),
+ extension: None
+ );
+
+ t!(".",
+ iter: ["."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("./",
+ iter: ["."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("a/b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a//b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a/./b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a/b/c",
+ iter: ["a", "b", "c"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a/b"),
+ file_name: Some("c"),
+ file_stem: Some("c"),
+ extension: None
+ );
+
+ t!(".foo",
+ iter: [".foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: Some(".foo"),
+ file_stem: Some(".foo"),
+ extension: None
+ );
+}
+
+#[test]
+#[cfg(windows)]
+pub fn test_decompositions_windows() {
+ t!("",
+ iter: [],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("/",
+ iter: ["\\"],
+ has_root: true,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\",
+ iter: ["\\"],
+ has_root: true,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("c:",
+ iter: ["c:"],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("c:\\",
+ iter: ["c:", "\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("c:/",
+ iter: ["c:", "\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/foo",
+ iter: ["\\", "foo"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("/"),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("/foo/",
+ iter: ["\\", "foo"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("/"),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/bar",
+ iter: ["foo", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("/foo/bar",
+ iter: ["\\", "foo", "bar"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("/foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("///foo///",
+ iter: ["\\", "foo"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("/"),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("///foo///bar",
+ iter: ["\\", "foo", "bar"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("///foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("./.",
+ iter: ["."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/..",
+ iter: ["\\", ".."],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("/"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("../",
+ iter: [".."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/.",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/..",
+ iter: ["foo", ".."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/./",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/./bar",
+ iter: ["foo", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("foo/../",
+ iter: ["foo", ".."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/../bar",
+ iter: ["foo", "..", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/.."),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("./a",
+ iter: [".", "a"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: Some("a"),
+ file_stem: Some("a"),
+ extension: None
+ );
+
+ t!(".",
+ iter: ["."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("./",
+ iter: ["."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(""),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("a/b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a//b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a/./b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a/b/c",
+ iter: ["a", "b", "c"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a/b"),
+ file_name: Some("c"),
+ file_stem: Some("c"),
+ extension: None);
+
+ t!("a\\b\\c",
+ iter: ["a", "b", "c"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a\\b"),
+ file_name: Some("c"),
+ file_stem: Some("c"),
+ extension: None
+ );
+
+ t!("\\a",
+ iter: ["\\", "a"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("\\"),
+ file_name: Some("a"),
+ file_stem: Some("a"),
+ extension: None
+ );
+
+ t!("c:\\foo.txt",
+ iter: ["c:", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("c:\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("\\\\server\\share\\foo.txt",
+ iter: ["\\\\server\\share", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\server\\share\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("\\\\server\\share",
+ iter: ["\\\\server\\share", "\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\server",
+ iter: ["\\", "server"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("\\"),
+ file_name: Some("server"),
+ file_stem: Some("server"),
+ extension: None
+ );
+
+ t!("\\\\?\\bar\\foo.txt",
+ iter: ["\\\\?\\bar", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\bar\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("\\\\?\\bar",
+ iter: ["\\\\?\\bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\",
+ iter: ["\\\\?\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\UNC\\server\\share\\foo.txt",
+ iter: ["\\\\?\\UNC\\server\\share", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\UNC\\server\\share\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("\\\\?\\UNC\\server",
+ iter: ["\\\\?\\UNC\\server"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\UNC\\",
+ iter: ["\\\\?\\UNC\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\C:\\foo.txt",
+ iter: ["\\\\?\\C:", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\C:\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("\\\\?\\C:\\",
+ iter: ["\\\\?\\C:", "\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\C:",
+ iter: ["\\\\?\\C:"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\foo/bar",
+ iter: ["\\\\?\\foo/bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\C:/foo",
+ iter: ["\\\\?\\C:/foo"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\.\\foo\\bar",
+ iter: ["\\\\.\\foo", "\\", "bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\.\\foo\\"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("\\\\.\\foo",
+ iter: ["\\\\.\\foo", "\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\.\\foo/bar",
+ iter: ["\\\\.\\foo/bar", "\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\.\\foo\\bar/baz",
+ iter: ["\\\\.\\foo", "\\", "bar", "baz"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\.\\foo\\bar"),
+ file_name: Some("baz"),
+ file_stem: Some("baz"),
+ extension: None
+ );
+
+ t!("\\\\.\\",
+ iter: ["\\\\.\\", "\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\a\\b\\",
+ iter: ["\\\\?\\a", "\\", "b"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\a\\"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+}
+
+#[test]
+pub fn test_stem_ext() {
+ t!("foo",
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo.",
+ file_stem: Some("foo"),
+ extension: Some("")
+ );
+
+ t!(".foo",
+ file_stem: Some(".foo"),
+ extension: None
+ );
+
+ t!("foo.txt",
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("foo.bar.txt",
+ file_stem: Some("foo.bar"),
+ extension: Some("txt")
+ );
+
+ t!("foo.bar.",
+ file_stem: Some("foo.bar"),
+ extension: Some("")
+ );
+
+ t!(".", file_stem: None, extension: None);
+
+ t!("..", file_stem: None, extension: None);
+
+ t!("", file_stem: None, extension: None);
+}
+
+#[test]
+pub fn test_push() {
+ macro_rules! tp(
+ ($path:expr, $push:expr, $expected:expr) => ( {
+ let mut actual = PathBuf::from($path);
+ actual.push($push);
+ assert!(actual.to_str() == Some($expected),
+ "pushing {:?} onto {:?}: Expected {:?}, got {:?}",
+ $push, $path, $expected, actual.to_str().unwrap());
+ });
+ );
+
+ if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) {
+ tp!("", "foo", "foo");
+ tp!("foo", "bar", "foo/bar");
+ tp!("foo/", "bar", "foo/bar");
+ tp!("foo//", "bar", "foo//bar");
+ tp!("foo/.", "bar", "foo/./bar");
+ tp!("foo./.", "bar", "foo././bar");
+ tp!("foo", "", "foo/");
+ tp!("foo", ".", "foo/.");
+ tp!("foo", "..", "foo/..");
+ tp!("foo", "/", "/");
+ tp!("/foo/bar", "/", "/");
+ tp!("/foo/bar", "/baz", "/baz");
+ tp!("/foo/bar", "./baz", "/foo/bar/./baz");
+ } else {
+ tp!("", "foo", "foo");
+ tp!("foo", "bar", r"foo\bar");
+ tp!("foo/", "bar", r"foo/bar");
+ tp!(r"foo\", "bar", r"foo\bar");
+ tp!("foo//", "bar", r"foo//bar");
+ tp!(r"foo\\", "bar", r"foo\\bar");
+ tp!("foo/.", "bar", r"foo/.\bar");
+ tp!("foo./.", "bar", r"foo./.\bar");
+ tp!(r"foo\.", "bar", r"foo\.\bar");
+ tp!(r"foo.\.", "bar", r"foo.\.\bar");
+ tp!("foo", "", "foo\\");
+ tp!("foo", ".", r"foo\.");
+ tp!("foo", "..", r"foo\..");
+ tp!("foo", "/", "/");
+ tp!("foo", r"\", r"\");
+ tp!("/foo/bar", "/", "/");
+ tp!(r"\foo\bar", r"\", r"\");
+ tp!("/foo/bar", "/baz", "/baz");
+ tp!("/foo/bar", r"\baz", r"\baz");
+ tp!("/foo/bar", "./baz", r"/foo/bar\./baz");
+ tp!("/foo/bar", r".\baz", r"/foo/bar\.\baz");
+
+ tp!("c:\\", "windows", "c:\\windows");
+ tp!("c:", "windows", "c:windows");
+
+ tp!("a\\b\\c", "d", "a\\b\\c\\d");
+ tp!("\\a\\b\\c", "d", "\\a\\b\\c\\d");
+ tp!("a\\b", "c\\d", "a\\b\\c\\d");
+ tp!("a\\b", "\\c\\d", "\\c\\d");
+ tp!("a\\b", ".", "a\\b\\.");
+ tp!("a\\b", "..\\c", "a\\b\\..\\c");
+ tp!("a\\b", "C:a.txt", "C:a.txt");
+ tp!("a\\b", "C:\\a.txt", "C:\\a.txt");
+ tp!("C:\\a", "C:\\b.txt", "C:\\b.txt");
+ tp!("C:\\a\\b\\c", "C:d", "C:d");
+ tp!("C:a\\b\\c", "C:d", "C:d");
+ tp!("C:", r"a\b\c", r"C:a\b\c");
+ tp!("C:", r"..\a", r"C:..\a");
+ tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar");
+ tp!("\\\\server\\share\\foo", "C:baz", "C:baz");
+ tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d");
+ tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d");
+ tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d");
+ tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz");
+ tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar");
+ tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a");
+ tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a");
+
+ // Note: modified from old path API
+ tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo");
+
+ tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share");
+ tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz");
+ tp!("\\\\.\\foo\\bar", "C:a", "C:a");
+ // again, not sure about the following, but I'm assuming \\.\ should be verbatim
+ tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar");
+
+ tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one
+ }
+}
+
+#[test]
+pub fn test_pop() {
+ macro_rules! tp(
+ ($path:expr, $expected:expr, $output:expr) => ( {
+ let mut actual = PathBuf::from($path);
+ let output = actual.pop();
+ assert!(actual.to_str() == Some($expected) && output == $output,
+ "popping from {:?}: Expected {:?}/{:?}, got {:?}/{:?}",
+ $path, $expected, $output,
+ actual.to_str().unwrap(), output);
+ });
+ );
+
+ tp!("", "", false);
+ tp!("/", "/", false);
+ tp!("foo", "", true);
+ tp!(".", "", true);
+ tp!("/foo", "/", true);
+ tp!("/foo/bar", "/foo", true);
+ tp!("foo/bar", "foo", true);
+ tp!("foo/.", "", true);
+ tp!("foo//bar", "foo", true);
+
+ if cfg!(windows) {
+ tp!("a\\b\\c", "a\\b", true);
+ tp!("\\a", "\\", true);
+ tp!("\\", "\\", false);
+
+ tp!("C:\\a\\b", "C:\\a", true);
+ tp!("C:\\a", "C:\\", true);
+ tp!("C:\\", "C:\\", false);
+ tp!("C:a\\b", "C:a", true);
+ tp!("C:a", "C:", true);
+ tp!("C:", "C:", false);
+ tp!("\\\\server\\share\\a\\b", "\\\\server\\share\\a", true);
+ tp!("\\\\server\\share\\a", "\\\\server\\share\\", true);
+ tp!("\\\\server\\share", "\\\\server\\share", false);
+ tp!("\\\\?\\a\\b\\c", "\\\\?\\a\\b", true);
+ tp!("\\\\?\\a\\b", "\\\\?\\a\\", true);
+ tp!("\\\\?\\a", "\\\\?\\a", false);
+ tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true);
+ tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true);
+ tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false);
+ tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true);
+ tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true);
+ tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false);
+ tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true);
+ tp!("\\\\.\\a\\b", "\\\\.\\a\\", true);
+ tp!("\\\\.\\a", "\\\\.\\a", false);
+
+ tp!("\\\\?\\a\\b\\", "\\\\?\\a\\", true);
+ }
+}
+
+#[test]
+pub fn test_set_file_name() {
+ macro_rules! tfn(
+ ($path:expr, $file:expr, $expected:expr) => ( {
+ let mut p = PathBuf::from($path);
+ p.set_file_name($file);
+ assert!(p.to_str() == Some($expected),
+ "setting file name of {:?} to {:?}: Expected {:?}, got {:?}",
+ $path, $file, $expected,
+ p.to_str().unwrap());
+ });
+ );
+
+ tfn!("foo", "foo", "foo");
+ tfn!("foo", "bar", "bar");
+ tfn!("foo", "", "");
+ tfn!("", "foo", "foo");
+ if cfg!(unix) || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) {
+ tfn!(".", "foo", "./foo");
+ tfn!("foo/", "bar", "bar");
+ tfn!("foo/.", "bar", "bar");
+ tfn!("..", "foo", "../foo");
+ tfn!("foo/..", "bar", "foo/../bar");
+ tfn!("/", "foo", "/foo");
+ } else {
+ tfn!(".", "foo", r".\foo");
+ tfn!(r"foo\", "bar", r"bar");
+ tfn!(r"foo\.", "bar", r"bar");
+ tfn!("..", "foo", r"..\foo");
+ tfn!(r"foo\..", "bar", r"foo\..\bar");
+ tfn!(r"\", "foo", r"\foo");
+ }
+}
+
+#[test]
+pub fn test_set_extension() {
+ macro_rules! tfe(
+ ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( {
+ let mut p = PathBuf::from($path);
+ let output = p.set_extension($ext);
+ assert!(p.to_str() == Some($expected) && output == $output,
+ "setting extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}",
+ $path, $ext, $expected, $output,
+ p.to_str().unwrap(), output);
+ });
+ );
+
+ tfe!("foo", "txt", "foo.txt", true);
+ tfe!("foo.bar", "txt", "foo.txt", true);
+ tfe!("foo.bar.baz", "txt", "foo.bar.txt", true);
+ tfe!(".test", "txt", ".test.txt", true);
+ tfe!("foo.txt", "", "foo", true);
+ tfe!("foo", "", "foo", true);
+ tfe!("", "foo", "", false);
+ tfe!(".", "foo", ".", false);
+ tfe!("foo/", "bar", "foo.bar", true);
+ tfe!("foo/.", "bar", "foo.bar", true);
+ tfe!("..", "foo", "..", false);
+ tfe!("foo/..", "bar", "foo/..", false);
+ tfe!("/", "foo", "/", false);
+}
+
+#[test]
+fn test_eq_receivers() {
+ use crate::borrow::Cow;
+
+ let borrowed: &Path = Path::new("foo/bar");
+ 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();
+
+ macro_rules! t {
+ ($($current:expr),+) => {
+ $(
+ assert_eq!($current, borrowed);
+ assert_eq!($current, owned);
+ assert_eq!($current, borrowed_cow);
+ assert_eq!($current, owned_cow);
+ )+
+ }
+ }
+
+ t!(borrowed, owned, borrowed_cow, owned_cow);
+}
+
+#[test]
+pub fn test_compare() {
+ use crate::collections::hash_map::DefaultHasher;
+ use crate::hash::{Hash, Hasher};
+
+ fn hash<T: Hash>(t: T) -> u64 {
+ let mut s = DefaultHasher::new();
+ t.hash(&mut s);
+ s.finish()
+ }
+
+ macro_rules! tc(
+ ($path1:expr, $path2:expr, eq: $eq:expr,
+ starts_with: $starts_with:expr, ends_with: $ends_with:expr,
+ relative_from: $relative_from:expr) => ({
+ let path1 = Path::new($path1);
+ let path2 = Path::new($path2);
+
+ let eq = path1 == path2;
+ assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}",
+ $path1, $path2, $eq, eq);
+ assert!($eq == (hash(path1) == hash(path2)),
+ "{:?} == {:?}, expected {:?}, got {} and {}",
+ $path1, $path2, $eq, hash(path1), hash(path2));
+
+ let starts_with = path1.starts_with(path2);
+ assert!(starts_with == $starts_with,
+ "{:?}.starts_with({:?}), expected {:?}, got {:?}", $path1, $path2,
+ $starts_with, starts_with);
+
+ let ends_with = path1.ends_with(path2);
+ assert!(ends_with == $ends_with,
+ "{:?}.ends_with({:?}), expected {:?}, got {:?}", $path1, $path2,
+ $ends_with, ends_with);
+
+ let relative_from = path1.strip_prefix(path2)
+ .map(|p| p.to_str().unwrap())
+ .ok();
+ let exp: Option<&str> = $relative_from;
+ assert!(relative_from == exp,
+ "{:?}.strip_prefix({:?}), expected {:?}, got {:?}",
+ $path1, $path2, exp, relative_from);
+ });
+ );
+
+ tc!("", "",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+
+ tc!("foo", "",
+ eq: false,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("foo")
+ );
+
+ tc!("", "foo",
+ eq: false,
+ starts_with: false,
+ ends_with: false,
+ relative_from: None
+ );
+
+ tc!("foo", "foo",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+
+ tc!("foo/", "foo",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+
+ tc!("foo/bar", "foo",
+ eq: false,
+ starts_with: true,
+ ends_with: false,
+ relative_from: Some("bar")
+ );
+
+ tc!("foo/bar/baz", "foo/bar",
+ eq: false,
+ starts_with: true,
+ ends_with: false,
+ relative_from: Some("baz")
+ );
+
+ tc!("foo/bar", "foo/bar/baz",
+ eq: false,
+ starts_with: false,
+ ends_with: false,
+ relative_from: None
+ );
+
+ tc!("./foo/bar/", ".",
+ eq: false,
+ starts_with: true,
+ ends_with: false,
+ relative_from: Some("foo/bar")
+ );
+
+ if cfg!(windows) {
+ tc!(r"C:\src\rust\cargo-test\test\Cargo.toml",
+ r"c:\src\rust\cargo-test\test",
+ eq: false,
+ starts_with: true,
+ ends_with: false,
+ relative_from: Some("Cargo.toml")
+ );
+
+ tc!(r"c:\foo", r"C:\foo",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+ }
+}
+
+#[test]
+fn test_components_debug() {
+ let path = Path::new("/tmp");
+
+ let mut components = path.components();
+
+ let expected = "Components([RootDir, Normal(\"tmp\")])";
+ let actual = format!("{:?}", components);
+ assert_eq!(expected, actual);
+
+ let _ = components.next().unwrap();
+ let expected = "Components([Normal(\"tmp\")])";
+ let actual = format!("{:?}", components);
+ assert_eq!(expected, actual);
+
+ let _ = components.next().unwrap();
+ let expected = "Components([])";
+ let actual = format!("{:?}", components);
+ assert_eq!(expected, actual);
+}
+
+#[cfg(unix)]
+#[test]
+fn test_iter_debug() {
+ let path = Path::new("/tmp");
+
+ let mut iter = path.iter();
+
+ let expected = "Iter([\"/\", \"tmp\"])";
+ let actual = format!("{:?}", iter);
+ assert_eq!(expected, actual);
+
+ let _ = iter.next().unwrap();
+ let expected = "Iter([\"tmp\"])";
+ let actual = format!("{:?}", iter);
+ assert_eq!(expected, actual);
+
+ let _ = iter.next().unwrap();
+ let expected = "Iter([])";
+ let actual = format!("{:?}", iter);
+ assert_eq!(expected, actual);
+}
+
+#[test]
+fn into_boxed() {
+ let orig: &str = "some/sort/of/path";
+ let path = Path::new(orig);
+ let boxed: Box<Path> = Box::from(path);
+ let path_buf = path.to_owned().into_boxed_path().into_path_buf();
+ assert_eq!(path, &*boxed);
+ assert_eq!(&*boxed, &*path_buf);
+ assert_eq!(&*path_buf, path);
+}
+
+#[test]
+fn test_clone_into() {
+ let mut path_buf = PathBuf::from("supercalifragilisticexpialidocious");
+ let path = Path::new("short");
+ path.clone_into(&mut path_buf);
+ assert_eq!(path, path_buf);
+ assert!(path_buf.into_os_string().capacity() >= 15);
+}
+
+#[test]
+fn display_format_flags() {
+ assert_eq!(format!("a{:#<5}b", Path::new("").display()), "a#####b");
+ assert_eq!(format!("a{:#<5}b", Path::new("a").display()), "aa####b");
+}
+
+#[test]
+fn into_rc() {
+ let orig = "hello/world";
+ let path = Path::new(orig);
+ let rc: Rc<Path> = Rc::from(path);
+ let arc: Arc<Path> = Arc::from(path);
+
+ assert_eq!(&*rc, path);
+ assert_eq!(&*arc, path);
+
+ let rc2: Rc<Path> = Rc::from(path.to_owned());
+ let arc2: Arc<Path> = Arc::from(path.to_owned());
+
+ assert_eq!(&*rc2, path);
+ assert_eq!(&*arc2, path);
+}
diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs
index 710c616..c7a7c77 100644
--- a/library/std/src/prelude/mod.rs
+++ b/library/std/src/prelude/mod.rs
@@ -75,7 +75,7 @@
//! [`std::result`]: crate::result
//! [`std::slice`]: crate::slice
//! [`std::string`]: crate::string
-//! [`std::vec`]: ../vec/index.html
+//! [`std::vec`]: mod@crate::vec
//! [`to_owned`]: crate::borrow::ToOwned::to_owned
//! [book-closures]: ../../book/ch13-01-closures.html
//! [book-dtor]: ../../book/ch15-03-drop.html
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index be7fd0d..81bbf37 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -1,7 +1,6 @@
#[doc(primitive = "bool")]
#[doc(alias = "true")]
#[doc(alias = "false")]
-//
/// The boolean type.
///
/// The `bool` represents a value, which could only be either `true` or `false`. If you cast
@@ -12,18 +11,17 @@
/// `bool` implements various traits, such as [`BitAnd`], [`BitOr`], [`Not`], etc.,
/// which allow us to perform boolean operations using `&`, `|` and `!`.
///
-/// `if` always demands a `bool` value. [`assert!`], being an important macro in testing,
-/// checks whether an expression returns `true`.
+/// `if` always demands a `bool` value. [`assert!`], which is an important macro in testing,
+/// checks whether an expression returns `true` and panics if it isn't.
///
/// ```
/// let bool_val = true & false | false;
/// assert!(!bool_val);
/// ```
///
-/// [`assert!`]: macro.assert.html
-/// [`BitAnd`]: ops/trait.BitAnd.html
-/// [`BitOr`]: ops/trait.BitOr.html
-/// [`Not`]: ops/trait.Not.html
+/// [`BitAnd`]: ops::BitAnd
+/// [`BitOr`]: ops::BitOr
+/// [`Not`]: ops::Not
///
/// # Examples
///
@@ -46,7 +44,7 @@
/// }
/// ```
///
-/// Also, since `bool` implements the [`Copy`](marker/trait.Copy.html) trait, we don't
+/// Also, since `bool` implements the [`Copy`] trait, we don't
/// have to worry about the move semantics (just like the integer and float primitives).
///
/// Now an example of `bool` cast to integer type:
@@ -100,8 +98,8 @@
/// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another
/// behaviour of the `!` type - expressions with type `!` will coerce into any other type.
///
-/// [`u32`]: primitive.str.html
-/// [`exit`]: process/fn.exit.html
+/// [`u32`]: prim@u32
+/// [`exit`]: process::exit
///
/// # `!` and generics
///
@@ -185,26 +183,58 @@
/// ever stops, it means that an error occurred. We don't even have to wrap the loop in an `Ok`
/// because `!` coerces to `Result<!, ConnectionError>` automatically.
///
-/// [`String::from_str`]: str/trait.FromStr.html#tymethod.from_str
-/// [`Result<String, !>`]: result/enum.Result.html
-/// [`Result<T, !>`]: result/enum.Result.html
-/// [`Result<!, E>`]: result/enum.Result.html
-/// [`Ok`]: result/enum.Result.html#variant.Ok
-/// [`String`]: string/struct.String.html
-/// [`Err`]: result/enum.Result.html#variant.Err
-/// [`FromStr`]: str/trait.FromStr.html
+/// [`String::from_str`]: str::FromStr::from_str
+/// [`Result<String, !>`]: Result
+/// [`Result<T, !>`]: Result
+/// [`Result<!, E>`]: Result
+/// [`String`]: string::String
+/// [`FromStr`]: str::FromStr
///
/// # `!` and traits
///
/// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl`
-/// which doesn't `panic!`. As it turns out, most traits can have an `impl` for `!`. Take [`Debug`]
+/// which doesn't `panic!`. The reason is that functions returning an `impl Trait` where `!`
+/// does not have an `impl` of `Trait` cannot diverge as their only possible code path. In other
+/// words, they can't return `!` from every code path. As an example, this code doesn't compile:
+///
+/// ```compile_fail
+/// use core::ops::Add;
+///
+/// fn foo() -> impl Add<u32> {
+/// unimplemented!()
+/// }
+/// ```
+///
+/// But this code does:
+///
+/// ```
+/// use core::ops::Add;
+///
+/// fn foo() -> impl Add<u32> {
+/// if true {
+/// unimplemented!()
+/// } else {
+/// 0
+/// }
+/// }
+/// ```
+///
+/// The reason is that, in the first example, there are many possible types that `!` could coerce
+/// to, because many types implement `Add<u32>`. However, in the second example,
+/// the `else` branch returns a `0`, which the compiler infers from the return type to be of type
+/// `u32`. Since `u32` is a concrete type, `!` can and will be coerced to it. See issue [#36375]
+/// for more information on this quirk of `!`.
+///
+/// [#36375]: https://github.com/rust-lang/rust/issues/36375
+///
+/// As it turns out, though, most traits can have an `impl` for `!`. Take [`Debug`]
/// for example:
///
/// ```
/// #![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 {
@@ -233,11 +263,9 @@
/// `impl` for this which simply panics, but the same is true for any type (we could `impl
/// Default` for (eg.) [`File`] by just making [`default()`] panic.)
///
-/// [`fmt::Result`]: fmt/type.Result.html
-/// [`File`]: fs/struct.File.html
-/// [`Debug`]: fmt/trait.Debug.html
-/// [`Default`]: default/trait.Default.html
-/// [`default()`]: default/trait.Default.html#tymethod.default
+/// [`File`]: fs::File
+/// [`Debug`]: fmt::Debug
+/// [`default()`]: Default::default
///
#[unstable(feature = "never_type", issue = "35121")]
mod prim_never {}
@@ -356,11 +384,12 @@
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_unit {}
+#[doc(alias = "ptr")]
#[doc(primitive = "pointer")]
//
/// Raw, unsafe pointers, `*const T`, and `*mut T`.
///
-/// *[See also the `std::ptr` module](ptr/index.html).*
+/// *[See also the `std::ptr` module][`ptr`].*
///
/// 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
@@ -439,13 +468,13 @@
/// but C APIs hand out a lot of pointers generally, so are a common source
/// of raw pointers in Rust.
///
-/// [`null`]: ../std/ptr/fn.null.html
-/// [`null_mut`]: ../std/ptr/fn.null_mut.html
+/// [`null`]: ptr::null
+/// [`null_mut`]: ptr::null_mut
/// [`is_null`]: ../std/primitive.pointer.html#method.is_null
/// [`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
+/// [`into_raw`]: Box::into_raw
+/// [`drop`]: mem::drop
+/// [`write`]: ptr::write
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_pointer {}
@@ -458,32 +487,32 @@
///
/// * A list with each element, i.e., `[x, y, z]`.
/// * A repeat expression `[x; N]`, which produces an array with `N` copies of `x`.
-/// The type of `x` must be [`Copy`][copy].
+/// The type of `x` must be [`Copy`].
///
/// Arrays of *any* size implement the following traits if the element type allows it:
///
-/// - [`Debug`][debug]
-/// - [`IntoIterator`][intoiterator] (implemented for `&[T; N]` and `&mut [T; N]`)
-/// - [`PartialEq`][partialeq], [`PartialOrd`][partialord], [`Eq`][eq], [`Ord`][ord]
-/// - [`Hash`][hash]
-/// - [`AsRef`][asref], [`AsMut`][asmut]
-/// - [`Borrow`][borrow], [`BorrowMut`][borrowmut]
+/// - [`Debug`]
+/// - [`IntoIterator`] (implemented for `&[T; N]` and `&mut [T; N]`)
+/// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]
+/// - [`Hash`]
+/// - [`AsRef`], [`AsMut`]
+/// - [`Borrow`], [`BorrowMut`]
///
-/// Arrays of sizes from 0 to 32 (inclusive) implement [`Default`][default] trait
+/// Arrays of sizes from 0 to 32 (inclusive) implement [`Default`] trait
/// if the element type allows it. As a stopgap, trait implementations are
/// statically generated up to size 32.
///
-/// Arrays of *any* size are [`Copy`][copy] if the element type is [`Copy`][copy]
-/// and [`Clone`][clone] if the element type is [`Clone`][clone]. This works
-/// because [`Copy`][copy] and [`Clone`][clone] traits are specially known
+/// Arrays of *any* size are [`Copy`] if the element type is [`Copy`]
+/// and [`Clone`] if the element type is [`Clone`]. This works
+/// because [`Copy`] and [`Clone`] traits are specially known
/// to the compiler.
///
/// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
/// an array. Indeed, this provides most of the API for working with arrays.
/// Slices have a dynamic size and do not coerce to arrays.
///
-/// You can move elements out of an array with a slice pattern. If you want
-/// one element, see [`mem::replace`][replace].
+/// You can move elements out of an array with a [slice pattern]. If you want
+/// one element, see [`mem::replace`].
///
/// # Examples
///
@@ -524,7 +553,7 @@
/// for x in &array { }
/// ```
///
-/// You can use a slice pattern to move elements out of an array:
+/// You can use a [slice pattern] to move elements out of an array:
///
/// ```
/// fn move_away(_: String) { /* Do interesting things. */ }
@@ -535,23 +564,11 @@
/// ```
///
/// [slice]: primitive.slice.html
-/// [copy]: marker/trait.Copy.html
-/// [clone]: clone/trait.Clone.html
-/// [debug]: fmt/trait.Debug.html
-/// [intoiterator]: iter/trait.IntoIterator.html
-/// [partialeq]: cmp/trait.PartialEq.html
-/// [partialord]: cmp/trait.PartialOrd.html
-/// [eq]: cmp/trait.Eq.html
-/// [ord]: cmp/trait.Ord.html
-/// [hash]: hash/trait.Hash.html
-/// [asref]: convert/trait.AsRef.html
-/// [asmut]: convert/trait.AsMut.html
-/// [borrow]: borrow/trait.Borrow.html
-/// [borrowmut]: borrow/trait.BorrowMut.html
-/// [default]: default/trait.Default.html
-/// [replace]: mem/fn.replace.html
-/// [`IntoIterator`]: iter/trait.IntoIterator.html
-///
+/// [`Debug`]: fmt::Debug
+/// [`Hash`]: hash::Hash
+/// [`Borrow`]: borrow::Borrow
+/// [`BorrowMut`]: borrow::BorrowMut
+/// [slice pattern]: ../reference/patterns.html#slice-patterns
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_array {}
@@ -563,7 +580,7 @@
/// means that elements are laid out so that every element is the same
/// distance from its neighbors.
///
-/// *[See also the `std::slice` module](slice/index.html).*
+/// *[See also the `std::slice` module][`crate::slice`].*
///
/// Slices are a view into a block of memory represented as a pointer and a
/// length.
@@ -608,7 +625,7 @@
//
/// String slices.
///
-/// *[See also the `std::str` module](str/index.html).*
+/// *[See also the `std::str` module][`crate::str`].*
///
/// The `str` type, also called a 'string slice', is the most primitive string
/// type. It is usually seen in its borrowed form, `&str`. It is also the type
@@ -660,8 +677,8 @@
/// assert_eq!(s, Ok(story));
/// ```
///
-/// [`as_ptr`]: #method.as_ptr
-/// [`len`]: #method.len
+/// [`as_ptr`]: str::as_ptr
+/// [`len`]: str::len
///
/// Note: This example shows the internals of `&str`. `unsafe` should not be
/// used to get a string slice under normal circumstances. Use `as_str`
@@ -729,15 +746,8 @@
/// * [`Default`]
/// * [`Hash`]
///
-/// [`Clone`]: clone/trait.Clone.html
-/// [`Copy`]: marker/trait.Copy.html
-/// [`PartialEq`]: cmp/trait.PartialEq.html
-/// [`Eq`]: cmp/trait.Eq.html
-/// [`PartialOrd`]: cmp/trait.PartialOrd.html
-/// [`Ord`]: cmp/trait.Ord.html
-/// [`Debug`]: fmt/trait.Debug.html
-/// [`Default`]: default/trait.Default.html
-/// [`Hash`]: hash/trait.Hash.html
+/// [`Debug`]: fmt::Debug
+/// [`Hash`]: hash::Hash
///
/// Due to a temporary restriction in Rust's type system, these traits are only
/// implemented on tuples of arity 12 or less. In the future, this may change.
@@ -810,7 +820,7 @@
///
/// For more information on floating point numbers, see [Wikipedia][wikipedia].
///
-/// *[See also the `std::f32::consts` module](f32/consts/index.html).*
+/// *[See also the `std::f32::consts` module][`crate::f32::consts`].*
///
/// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format
#[stable(feature = "rust1", since = "1.0.0")]
@@ -819,13 +829,14 @@
#[doc(primitive = "f64")]
/// A 64-bit floating point type (specifically, the "binary64" type defined in IEEE 754-2008).
///
-/// This type is very similar to [`f32`](primitive.f32.html), but has increased
+/// This type is very similar to [`f32`], but has increased
/// precision by using twice as many bits. Please see [the documentation for
-/// `f32`](primitive.f32.html) or [Wikipedia on double precision
+/// `f32`][`f32`] or [Wikipedia on double precision
/// values][wikipedia] for more information.
///
-/// *[See also the `std::f64::consts` module](f64/consts/index.html).*
+/// *[See also the `std::f64::consts` module][`crate::f64::consts`].*
///
+/// [`f32`]: prim@f32
/// [wikipedia]: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_f64 {}
@@ -945,9 +956,6 @@
/// implicit reference-pointer coercion and raw pointer equality via [`ptr::eq`], while
/// [`PartialEq`] compares values.
///
-/// [`ptr::eq`]: ptr/fn.eq.html
-/// [`PartialEq`]: cmp/trait.PartialEq.html
-///
/// ```
/// use std::ptr;
///
@@ -979,11 +987,9 @@
/// * [`Borrow`]
/// * [`Pointer`]
///
-/// [`Copy`]: marker/trait.Copy.html
-/// [`Clone`]: clone/trait.Clone.html
-/// [`Deref`]: ops/trait.Deref.html
-/// [`Borrow`]: borrow/trait.Borrow.html
-/// [`Pointer`]: fmt/trait.Pointer.html
+/// [`Deref`]: ops::Deref
+/// [`Borrow`]: borrow::Borrow
+/// [`Pointer`]: fmt::Pointer
///
/// `&mut T` references get all of the above except `Copy` and `Clone` (to prevent creating
/// multiple simultaneous mutable borrows), plus the following, regardless of the type of its
@@ -992,8 +998,8 @@
/// * [`DerefMut`]
/// * [`BorrowMut`]
///
-/// [`DerefMut`]: ops/trait.DerefMut.html
-/// [`BorrowMut`]: borrow/trait.BorrowMut.html
+/// [`DerefMut`]: ops::DerefMut
+/// [`BorrowMut`]: borrow::BorrowMut
///
/// The following traits are implemented on `&T` references if the underlying `T` also implements
/// that trait:
@@ -1008,18 +1014,10 @@
/// * [`Hash`]
/// * [`ToSocketAddrs`]
///
-/// [`std::fmt`]: fmt/index.html
-/// [`fmt::Write`]: fmt/trait.Write.html
-/// [`PartialOrd`]: cmp/trait.PartialOrd.html
-/// [`Ord`]: cmp/trait.Ord.html
-/// [`PartialEq`]: cmp/trait.PartialEq.html
-/// [`Eq`]: cmp/trait.Eq.html
-/// [`AsRef`]: convert/trait.AsRef.html
-/// [`Fn`]: ops/trait.Fn.html
-/// [`FnMut`]: ops/trait.FnMut.html
-/// [`FnOnce`]: ops/trait.FnOnce.html
-/// [`Hash`]: hash/trait.Hash.html
-/// [`ToSocketAddrs`]: net/trait.ToSocketAddrs.html
+/// [`std::fmt`]: fmt
+/// ['Pointer`]: fmt::Pointer
+/// [`Hash`]: hash::Hash
+/// [`ToSocketAddrs`]: net::ToSocketAddrs
///
/// `&mut T` references get all of the above except `ToSocketAddrs`, plus the following, if `T`
/// implements that trait:
@@ -1038,17 +1036,11 @@
/// * [`Seek`]
/// * [`BufRead`]
///
-/// [`AsMut`]: convert/trait.AsMut.html
-/// [`Iterator`]: iter/trait.Iterator.html
-/// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html
-/// [`ExactSizeIterator`]: iter/trait.ExactSizeIterator.html
-/// [`FusedIterator`]: iter/trait.FusedIterator.html
-/// [`TrustedLen`]: iter/trait.TrustedLen.html
-/// [`Send`]: marker/trait.Send.html
-/// [`io::Write`]: io/trait.Write.html
-/// [`Read`]: io/trait.Read.html
-/// [`Seek`]: io/trait.Seek.html
-/// [`BufRead`]: io/trait.BufRead.html
+/// [`FusedIterator`]: iter::FusedIterator
+/// [`TrustedLen`]: iter::TrustedLen
+/// [`Seek`]: io::Seek
+/// [`BufRead`]: io::BufRead
+/// [`Read`]: io::Read
///
/// Note that due to method call deref coercion, simply calling a trait method will act like they
/// work on references as well as they do on owned values! The implementations described here are
@@ -1063,9 +1055,9 @@
///
/// *See also the traits [`Fn`], [`FnMut`], and [`FnOnce`].*
///
-/// [`Fn`]: ops/trait.Fn.html
-/// [`FnMut`]: ops/trait.FnMut.html
-/// [`FnOnce`]: ops/trait.FnOnce.html
+/// [`Fn`]: ops::Fn
+/// [`FnMut`]: ops::FnMut
+/// [`FnOnce`]: ops::FnOnce
///
/// 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
@@ -1177,14 +1169,8 @@
/// * [`Pointer`]
/// * [`Debug`]
///
-/// [`Clone`]: clone/trait.Clone.html
-/// [`PartialEq`]: cmp/trait.PartialEq.html
-/// [`Eq`]: cmp/trait.Eq.html
-/// [`PartialOrd`]: cmp/trait.PartialOrd.html
-/// [`Ord`]: cmp/trait.Ord.html
-/// [`Hash`]: hash/trait.Hash.html
-/// [`Pointer`]: fmt/trait.Pointer.html
-/// [`Debug`]: fmt/trait.Debug.html
+/// [`Hash`]: hash::Hash
+/// [`Pointer`]: fmt::Pointer
///
/// Due to a temporary restriction in Rust's type system, these traits are only implemented on
/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this
@@ -1193,7 +1179,5 @@
/// In addition, function pointers of *any* signature, ABI, or safety are [`Copy`], and all *safe*
/// function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`]. This works because these traits
/// are specially known to the compiler.
-///
-/// [`Copy`]: marker/trait.Copy.html
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_fn {}
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index c42bc10..3d238b7 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -95,6 +95,10 @@
//! [`Read`]: io::Read
#![stable(feature = "process", since = "1.0.0")]
+#![deny(unsafe_op_in_unsafe_fn)]
+
+#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))]
+mod tests;
use crate::io::prelude::*;
@@ -106,6 +110,8 @@
use crate::str;
use crate::sys::pipe::{read2, AnonPipe};
use crate::sys::process as imp;
+#[unstable(feature = "command_access", issue = "44434")]
+pub use crate::sys_common::process::CommandEnvs;
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
/// Representation of a running or exited child process.
@@ -246,6 +252,25 @@
#[stable(feature = "process", since = "1.0.0")]
impl Write for ChildStdin {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ (&*self).write(buf)
+ }
+
+ fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ (&*self).write_vectored(bufs)
+ }
+
+ fn is_write_vectored(&self) -> bool {
+ io::Write::is_write_vectored(&&*self)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ (&*self).flush()
+ }
+}
+
+#[stable(feature = "write_mt", since = "1.48.0")]
+impl Write for &ChildStdin {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
@@ -318,7 +343,8 @@
#[inline]
unsafe fn initializer(&self) -> Initializer {
- Initializer::nop()
+ // SAFETY: Read is guaranteed to work on uninitialized memory
+ unsafe { Initializer::nop() }
}
}
@@ -378,7 +404,8 @@
#[inline]
unsafe fn initializer(&self) -> Initializer {
- Initializer::nop()
+ // SAFETY: Read is guaranteed to work on uninitialized memory
+ unsafe { Initializer::nop() }
}
}
@@ -869,6 +896,98 @@
.map(Child::from_inner)
.and_then(|mut p| p.wait())
}
+
+ /// Returns the path to the program that was given to [`Command::new`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![feature(command_access)]
+ /// use std::process::Command;
+ ///
+ /// let cmd = Command::new("echo");
+ /// assert_eq!(cmd.get_program(), "echo");
+ /// ```
+ #[unstable(feature = "command_access", issue = "44434")]
+ pub fn get_program(&self) -> &OsStr {
+ self.inner.get_program()
+ }
+
+ /// Returns an iterator of the arguments that will be passed to the program.
+ ///
+ /// This does not include the path to the program as the first argument;
+ /// it only includes the arguments specified with [`Command::arg`] and
+ /// [`Command::args`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![feature(command_access)]
+ /// use std::ffi::OsStr;
+ /// use std::process::Command;
+ ///
+ /// let mut cmd = Command::new("echo");
+ /// cmd.arg("first").arg("second");
+ /// let args: Vec<&OsStr> = cmd.get_args().collect();
+ /// assert_eq!(args, &["first", "second"]);
+ /// ```
+ #[unstable(feature = "command_access", issue = "44434")]
+ pub fn get_args(&self) -> CommandArgs<'_> {
+ CommandArgs { inner: self.inner.get_args() }
+ }
+
+ /// Returns an iterator of the environment variables that will be set when
+ /// the process is spawned.
+ ///
+ /// Each element is a tuple `(&OsStr, Option<&OsStr>)`, where the first
+ /// value is the key, and the second is the value, which is [`None`] if
+ /// the environment variable is to be explicitly removed.
+ ///
+ /// This only includes environment variables explicitly set with
+ /// [`Command::env`], [`Command::envs`], and [`Command::env_remove`]. It
+ /// does not include environment variables that will be inherited by the
+ /// child process.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![feature(command_access)]
+ /// use std::ffi::OsStr;
+ /// use std::process::Command;
+ ///
+ /// let mut cmd = Command::new("ls");
+ /// cmd.env("TERM", "dumb").env_remove("TZ");
+ /// let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect();
+ /// assert_eq!(envs, &[
+ /// (OsStr::new("TERM"), Some(OsStr::new("dumb"))),
+ /// (OsStr::new("TZ"), None)
+ /// ]);
+ /// ```
+ #[unstable(feature = "command_access", issue = "44434")]
+ pub fn get_envs(&self) -> CommandEnvs<'_> {
+ self.inner.get_envs()
+ }
+
+ /// Returns the working directory for the child process.
+ ///
+ /// This returns [`None`] if the working directory will not be changed.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #![feature(command_access)]
+ /// use std::path::Path;
+ /// use std::process::Command;
+ ///
+ /// let mut cmd = Command::new("ls");
+ /// assert_eq!(cmd.get_current_dir(), None);
+ /// cmd.current_dir("/bin");
+ /// assert_eq!(cmd.get_current_dir(), Some(Path::new("/bin")));
+ /// ```
+ #[unstable(feature = "command_access", issue = "44434")]
+ pub fn get_current_dir(&self) -> Option<&Path> {
+ self.inner.get_current_dir()
+ }
}
#[stable(feature = "rust1", since = "1.0.0")]
@@ -893,6 +1012,37 @@
}
}
+/// An iterator over the command arguments.
+///
+/// This struct is created by [`Command::get_args`]. See its documentation for
+/// more.
+#[unstable(feature = "command_access", issue = "44434")]
+#[derive(Debug)]
+pub struct CommandArgs<'a> {
+ inner: imp::CommandArgs<'a>,
+}
+
+#[unstable(feature = "command_access", issue = "44434")]
+impl<'a> Iterator for CommandArgs<'a> {
+ type Item = &'a OsStr;
+ fn next(&mut self) -> Option<&'a OsStr> {
+ self.inner.next()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.inner.size_hint()
+ }
+}
+
+#[unstable(feature = "command_access", issue = "44434")]
+impl<'a> ExactSizeIterator for CommandArgs<'a> {
+ fn len(&self) -> usize {
+ self.inner.len()
+ }
+ fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+}
+
/// The output of a finished process.
///
/// This is returned in a Result by either the [`output`] method of a
@@ -1702,411 +1852,3 @@
self.0.as_i32()
}
}
-
-#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten", target_env = "sgx"))))]
-mod tests {
- use crate::io::prelude::*;
-
- use super::{Command, Output, Stdio};
- use crate::io::ErrorKind;
- use crate::str;
-
- // FIXME(#10380) these tests should not all be ignored on android.
-
- #[test]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn smoke() {
- let p = if cfg!(target_os = "windows") {
- Command::new("cmd").args(&["/C", "exit 0"]).spawn()
- } else {
- Command::new("true").spawn()
- };
- assert!(p.is_ok());
- let mut p = p.unwrap();
- assert!(p.wait().unwrap().success());
- }
-
- #[test]
- #[cfg_attr(target_os = "android", ignore)]
- fn smoke_failure() {
- match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
- Ok(..) => panic!(),
- Err(..) => {}
- }
- }
-
- #[test]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn exit_reported_right() {
- let p = if cfg!(target_os = "windows") {
- Command::new("cmd").args(&["/C", "exit 1"]).spawn()
- } else {
- Command::new("false").spawn()
- };
- assert!(p.is_ok());
- let mut p = p.unwrap();
- assert!(p.wait().unwrap().code() == Some(1));
- drop(p.wait());
- }
-
- #[test]
- #[cfg(unix)]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn signal_reported_right() {
- use crate::os::unix::process::ExitStatusExt;
-
- let mut p =
- Command::new("/bin/sh").arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap();
- p.kill().unwrap();
- match p.wait().unwrap().signal() {
- Some(9) => {}
- result => panic!("not terminated by signal 9 (instead, {:?})", result),
- }
- }
-
- pub fn run_output(mut cmd: Command) -> String {
- let p = cmd.spawn();
- assert!(p.is_ok());
- let mut p = p.unwrap();
- assert!(p.stdout.is_some());
- let mut ret = String::new();
- p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap();
- assert!(p.wait().unwrap().success());
- return ret;
- }
-
- #[test]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn stdout_works() {
- if cfg!(target_os = "windows") {
- let mut cmd = Command::new("cmd");
- cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped());
- assert_eq!(run_output(cmd), "foobar\r\n");
- } else {
- let mut cmd = Command::new("echo");
- cmd.arg("foobar").stdout(Stdio::piped());
- assert_eq!(run_output(cmd), "foobar\n");
- }
- }
-
- #[test]
- #[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)]
- fn set_current_dir_works() {
- let mut cmd = Command::new("/bin/sh");
- cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped());
- assert_eq!(run_output(cmd), "/\n");
- }
-
- #[test]
- #[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)]
- fn stdin_works() {
- let mut p = Command::new("/bin/sh")
- .arg("-c")
- .arg("read line; echo $line")
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn()
- .unwrap();
- p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap();
- drop(p.stdin.take());
- let mut out = String::new();
- p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap();
- assert!(p.wait().unwrap().success());
- assert_eq!(out, "foobar\n");
- }
-
- #[test]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn test_process_status() {
- let mut status = if cfg!(target_os = "windows") {
- Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap()
- } else {
- Command::new("false").status().unwrap()
- };
- assert!(status.code() == Some(1));
-
- status = if cfg!(target_os = "windows") {
- Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap()
- } else {
- Command::new("true").status().unwrap()
- };
- assert!(status.success());
- }
-
- #[test]
- fn test_process_output_fail_to_start() {
- match Command::new("/no-binary-by-this-name-should-exist").output() {
- Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound),
- Ok(..) => panic!(),
- }
- }
-
- #[test]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn test_process_output_output() {
- let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
- Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap()
- } else {
- Command::new("echo").arg("hello").output().unwrap()
- };
- let output_str = str::from_utf8(&stdout).unwrap();
-
- assert!(status.success());
- assert_eq!(output_str.trim().to_string(), "hello");
- assert_eq!(stderr, Vec::new());
- }
-
- #[test]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn test_process_output_error() {
- let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
- Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap()
- } else {
- Command::new("mkdir").arg("./").output().unwrap()
- };
-
- assert!(status.code() == Some(1));
- assert_eq!(stdout, Vec::new());
- assert!(!stderr.is_empty());
- }
-
- #[test]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn test_finish_once() {
- let mut prog = if cfg!(target_os = "windows") {
- Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
- } else {
- Command::new("false").spawn().unwrap()
- };
- assert!(prog.wait().unwrap().code() == Some(1));
- }
-
- #[test]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn test_finish_twice() {
- let mut prog = if cfg!(target_os = "windows") {
- Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
- } else {
- Command::new("false").spawn().unwrap()
- };
- assert!(prog.wait().unwrap().code() == Some(1));
- assert!(prog.wait().unwrap().code() == Some(1));
- }
-
- #[test]
- #[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
- fn test_wait_with_output_once() {
- let prog = if cfg!(target_os = "windows") {
- Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap()
- } else {
- Command::new("echo").arg("hello").stdout(Stdio::piped()).spawn().unwrap()
- };
-
- let Output { status, stdout, stderr } = prog.wait_with_output().unwrap();
- let output_str = str::from_utf8(&stdout).unwrap();
-
- assert!(status.success());
- assert_eq!(output_str.trim().to_string(), "hello");
- assert_eq!(stderr, Vec::new());
- }
-
- #[cfg(all(unix, not(target_os = "android")))]
- pub fn env_cmd() -> Command {
- Command::new("env")
- }
- #[cfg(target_os = "android")]
- pub fn env_cmd() -> Command {
- let mut cmd = Command::new("/system/bin/sh");
- cmd.arg("-c").arg("set");
- cmd
- }
-
- #[cfg(windows)]
- pub fn env_cmd() -> Command {
- let mut cmd = Command::new("cmd");
- cmd.arg("/c").arg("set");
- cmd
- }
-
- #[test]
- #[cfg_attr(target_os = "vxworks", ignore)]
- fn test_override_env() {
- use crate::env;
-
- // In some build environments (such as chrooted Nix builds), `env` can
- // only be found in the explicitly-provided PATH env variable, not in
- // default places such as /bin or /usr/bin. So we need to pass through
- // PATH to our sub-process.
- let mut cmd = env_cmd();
- cmd.env_clear().env("RUN_TEST_NEW_ENV", "123");
- if let Some(p) = env::var_os("PATH") {
- cmd.env("PATH", &p);
- }
- let result = cmd.output().unwrap();
- let output = String::from_utf8_lossy(&result.stdout).to_string();
-
- assert!(
- output.contains("RUN_TEST_NEW_ENV=123"),
- "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}",
- output
- );
- }
-
- #[test]
- #[cfg_attr(target_os = "vxworks", ignore)]
- fn test_add_to_env() {
- let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap();
- let output = String::from_utf8_lossy(&result.stdout).to_string();
-
- assert!(
- output.contains("RUN_TEST_NEW_ENV=123"),
- "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}",
- output
- );
- }
-
- #[test]
- #[cfg_attr(target_os = "vxworks", ignore)]
- fn test_capture_env_at_spawn() {
- use crate::env;
-
- let mut cmd = env_cmd();
- cmd.env("RUN_TEST_NEW_ENV1", "123");
-
- // This variable will not be present if the environment has already
- // been captured above.
- env::set_var("RUN_TEST_NEW_ENV2", "456");
- let result = cmd.output().unwrap();
- env::remove_var("RUN_TEST_NEW_ENV2");
-
- let output = String::from_utf8_lossy(&result.stdout).to_string();
-
- assert!(
- output.contains("RUN_TEST_NEW_ENV1=123"),
- "didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{}",
- output
- );
- assert!(
- output.contains("RUN_TEST_NEW_ENV2=456"),
- "didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{}",
- output
- );
- }
-
- // Regression tests for #30858.
- #[test]
- fn test_interior_nul_in_progname_is_error() {
- match Command::new("has-some-\0\0s-inside").spawn() {
- Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
- Ok(_) => panic!(),
- }
- }
-
- #[test]
- fn test_interior_nul_in_arg_is_error() {
- match Command::new("echo").arg("has-some-\0\0s-inside").spawn() {
- Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
- Ok(_) => panic!(),
- }
- }
-
- #[test]
- fn test_interior_nul_in_args_is_error() {
- match Command::new("echo").args(&["has-some-\0\0s-inside"]).spawn() {
- Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
- Ok(_) => panic!(),
- }
- }
-
- #[test]
- fn test_interior_nul_in_current_dir_is_error() {
- match Command::new("echo").current_dir("has-some-\0\0s-inside").spawn() {
- Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
- Ok(_) => panic!(),
- }
- }
-
- // Regression tests for #30862.
- #[test]
- #[cfg_attr(target_os = "vxworks", ignore)]
- fn test_interior_nul_in_env_key_is_error() {
- match env_cmd().env("has-some-\0\0s-inside", "value").spawn() {
- Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
- Ok(_) => panic!(),
- }
- }
-
- #[test]
- #[cfg_attr(target_os = "vxworks", ignore)]
- fn test_interior_nul_in_env_value_is_error() {
- match env_cmd().env("key", "has-some-\0\0s-inside").spawn() {
- Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
- Ok(_) => panic!(),
- }
- }
-
- /// Tests that process creation flags work by debugging a process.
- /// Other creation flags make it hard or impossible to detect
- /// behavioral changes in the process.
- #[test]
- #[cfg(windows)]
- fn test_creation_flags() {
- use crate::os::windows::process::CommandExt;
- use crate::sys::c::{BOOL, DWORD, INFINITE};
- #[repr(C, packed)]
- struct DEBUG_EVENT {
- pub event_code: DWORD,
- pub process_id: DWORD,
- pub thread_id: DWORD,
- // This is a union in the real struct, but we don't
- // need this data for the purposes of this test.
- pub _junk: [u8; 164],
- }
-
- extern "system" {
- fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL;
- fn ContinueDebugEvent(
- dwProcessId: DWORD,
- dwThreadId: DWORD,
- dwContinueStatus: DWORD,
- ) -> BOOL;
- }
-
- const DEBUG_PROCESS: DWORD = 1;
- const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5;
- const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001;
-
- let mut child = Command::new("cmd")
- .creation_flags(DEBUG_PROCESS)
- .stdin(Stdio::piped())
- .spawn()
- .unwrap();
- child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap();
- let mut events = 0;
- let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] };
- loop {
- if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 {
- panic!("WaitForDebugEvent failed!");
- }
- events += 1;
-
- if event.event_code == EXIT_PROCESS_DEBUG_EVENT {
- break;
- }
-
- if unsafe {
- ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED)
- } == 0
- {
- panic!("ContinueDebugEvent failed!");
- }
- }
- assert!(events > 0);
- }
-
- #[test]
- fn test_command_implements_send_sync() {
- fn take_send_sync_type<T: Send + Sync>(_: T) {}
- take_send_sync_type(Command::new(""))
- }
-}
diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs
new file mode 100644
index 0000000..05e0934
--- /dev/null
+++ b/library/std/src/process/tests.rs
@@ -0,0 +1,401 @@
+use crate::io::prelude::*;
+
+use super::{Command, Output, Stdio};
+use crate::io::ErrorKind;
+use crate::str;
+
+// FIXME(#10380) these tests should not all be ignored on android.
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn smoke() {
+ let p = if cfg!(target_os = "windows") {
+ Command::new("cmd").args(&["/C", "exit 0"]).spawn()
+ } else {
+ Command::new("true").spawn()
+ };
+ assert!(p.is_ok());
+ let mut p = p.unwrap();
+ assert!(p.wait().unwrap().success());
+}
+
+#[test]
+#[cfg_attr(target_os = "android", ignore)]
+fn smoke_failure() {
+ match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
+ Ok(..) => panic!(),
+ Err(..) => {}
+ }
+}
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn exit_reported_right() {
+ let p = if cfg!(target_os = "windows") {
+ Command::new("cmd").args(&["/C", "exit 1"]).spawn()
+ } else {
+ Command::new("false").spawn()
+ };
+ assert!(p.is_ok());
+ let mut p = p.unwrap();
+ assert!(p.wait().unwrap().code() == Some(1));
+ drop(p.wait());
+}
+
+#[test]
+#[cfg(unix)]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn signal_reported_right() {
+ use crate::os::unix::process::ExitStatusExt;
+
+ let mut p =
+ Command::new("/bin/sh").arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap();
+ p.kill().unwrap();
+ match p.wait().unwrap().signal() {
+ Some(9) => {}
+ result => panic!("not terminated by signal 9 (instead, {:?})", result),
+ }
+}
+
+pub fn run_output(mut cmd: Command) -> String {
+ let p = cmd.spawn();
+ assert!(p.is_ok());
+ let mut p = p.unwrap();
+ assert!(p.stdout.is_some());
+ let mut ret = String::new();
+ p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap();
+ assert!(p.wait().unwrap().success());
+ return ret;
+}
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn stdout_works() {
+ if cfg!(target_os = "windows") {
+ let mut cmd = Command::new("cmd");
+ cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped());
+ assert_eq!(run_output(cmd), "foobar\r\n");
+ } else {
+ let mut cmd = Command::new("echo");
+ cmd.arg("foobar").stdout(Stdio::piped());
+ assert_eq!(run_output(cmd), "foobar\n");
+ }
+}
+
+#[test]
+#[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)]
+fn set_current_dir_works() {
+ let mut cmd = Command::new("/bin/sh");
+ cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped());
+ assert_eq!(run_output(cmd), "/\n");
+}
+
+#[test]
+#[cfg_attr(any(windows, target_os = "android", target_os = "vxworks"), ignore)]
+fn stdin_works() {
+ let mut p = Command::new("/bin/sh")
+ .arg("-c")
+ .arg("read line; echo $line")
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .spawn()
+ .unwrap();
+ p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap();
+ drop(p.stdin.take());
+ let mut out = String::new();
+ p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap();
+ assert!(p.wait().unwrap().success());
+ assert_eq!(out, "foobar\n");
+}
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn test_process_status() {
+ let mut status = if cfg!(target_os = "windows") {
+ Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap()
+ } else {
+ Command::new("false").status().unwrap()
+ };
+ assert!(status.code() == Some(1));
+
+ status = if cfg!(target_os = "windows") {
+ Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap()
+ } else {
+ Command::new("true").status().unwrap()
+ };
+ assert!(status.success());
+}
+
+#[test]
+fn test_process_output_fail_to_start() {
+ match Command::new("/no-binary-by-this-name-should-exist").output() {
+ Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound),
+ Ok(..) => panic!(),
+ }
+}
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn test_process_output_output() {
+ let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
+ Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap()
+ } else {
+ Command::new("echo").arg("hello").output().unwrap()
+ };
+ let output_str = str::from_utf8(&stdout).unwrap();
+
+ assert!(status.success());
+ assert_eq!(output_str.trim().to_string(), "hello");
+ assert_eq!(stderr, Vec::new());
+}
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn test_process_output_error() {
+ let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
+ Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap()
+ } else {
+ Command::new("mkdir").arg("./").output().unwrap()
+ };
+
+ assert!(status.code() == Some(1));
+ assert_eq!(stdout, Vec::new());
+ assert!(!stderr.is_empty());
+}
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn test_finish_once() {
+ let mut prog = if cfg!(target_os = "windows") {
+ Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
+ } else {
+ Command::new("false").spawn().unwrap()
+ };
+ assert!(prog.wait().unwrap().code() == Some(1));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn test_finish_twice() {
+ let mut prog = if cfg!(target_os = "windows") {
+ Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
+ } else {
+ Command::new("false").spawn().unwrap()
+ };
+ assert!(prog.wait().unwrap().code() == Some(1));
+ assert!(prog.wait().unwrap().code() == Some(1));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks", target_os = "android"), ignore)]
+fn test_wait_with_output_once() {
+ let prog = if cfg!(target_os = "windows") {
+ Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap()
+ } else {
+ Command::new("echo").arg("hello").stdout(Stdio::piped()).spawn().unwrap()
+ };
+
+ let Output { status, stdout, stderr } = prog.wait_with_output().unwrap();
+ let output_str = str::from_utf8(&stdout).unwrap();
+
+ assert!(status.success());
+ assert_eq!(output_str.trim().to_string(), "hello");
+ assert_eq!(stderr, Vec::new());
+}
+
+#[cfg(all(unix, not(target_os = "android")))]
+pub fn env_cmd() -> Command {
+ Command::new("env")
+}
+#[cfg(target_os = "android")]
+pub fn env_cmd() -> Command {
+ let mut cmd = Command::new("/system/bin/sh");
+ cmd.arg("-c").arg("set");
+ cmd
+}
+
+#[cfg(windows)]
+pub fn env_cmd() -> Command {
+ let mut cmd = Command::new("cmd");
+ cmd.arg("/c").arg("set");
+ cmd
+}
+
+#[test]
+#[cfg_attr(target_os = "vxworks", ignore)]
+fn test_override_env() {
+ use crate::env;
+
+ // In some build environments (such as chrooted Nix builds), `env` can
+ // only be found in the explicitly-provided PATH env variable, not in
+ // default places such as /bin or /usr/bin. So we need to pass through
+ // PATH to our sub-process.
+ let mut cmd = env_cmd();
+ cmd.env_clear().env("RUN_TEST_NEW_ENV", "123");
+ if let Some(p) = env::var_os("PATH") {
+ cmd.env("PATH", &p);
+ }
+ let result = cmd.output().unwrap();
+ let output = String::from_utf8_lossy(&result.stdout).to_string();
+
+ assert!(
+ output.contains("RUN_TEST_NEW_ENV=123"),
+ "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}",
+ output
+ );
+}
+
+#[test]
+#[cfg_attr(target_os = "vxworks", ignore)]
+fn test_add_to_env() {
+ let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap();
+ let output = String::from_utf8_lossy(&result.stdout).to_string();
+
+ assert!(
+ output.contains("RUN_TEST_NEW_ENV=123"),
+ "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}",
+ output
+ );
+}
+
+#[test]
+#[cfg_attr(target_os = "vxworks", ignore)]
+fn test_capture_env_at_spawn() {
+ use crate::env;
+
+ let mut cmd = env_cmd();
+ cmd.env("RUN_TEST_NEW_ENV1", "123");
+
+ // This variable will not be present if the environment has already
+ // been captured above.
+ env::set_var("RUN_TEST_NEW_ENV2", "456");
+ let result = cmd.output().unwrap();
+ env::remove_var("RUN_TEST_NEW_ENV2");
+
+ let output = String::from_utf8_lossy(&result.stdout).to_string();
+
+ assert!(
+ output.contains("RUN_TEST_NEW_ENV1=123"),
+ "didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{}",
+ output
+ );
+ assert!(
+ output.contains("RUN_TEST_NEW_ENV2=456"),
+ "didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{}",
+ output
+ );
+}
+
+// Regression tests for #30858.
+#[test]
+fn test_interior_nul_in_progname_is_error() {
+ match Command::new("has-some-\0\0s-inside").spawn() {
+ Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
+ Ok(_) => panic!(),
+ }
+}
+
+#[test]
+fn test_interior_nul_in_arg_is_error() {
+ match Command::new("echo").arg("has-some-\0\0s-inside").spawn() {
+ Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
+ Ok(_) => panic!(),
+ }
+}
+
+#[test]
+fn test_interior_nul_in_args_is_error() {
+ match Command::new("echo").args(&["has-some-\0\0s-inside"]).spawn() {
+ Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
+ Ok(_) => panic!(),
+ }
+}
+
+#[test]
+fn test_interior_nul_in_current_dir_is_error() {
+ match Command::new("echo").current_dir("has-some-\0\0s-inside").spawn() {
+ Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
+ Ok(_) => panic!(),
+ }
+}
+
+// Regression tests for #30862.
+#[test]
+#[cfg_attr(target_os = "vxworks", ignore)]
+fn test_interior_nul_in_env_key_is_error() {
+ match env_cmd().env("has-some-\0\0s-inside", "value").spawn() {
+ Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
+ Ok(_) => panic!(),
+ }
+}
+
+#[test]
+#[cfg_attr(target_os = "vxworks", ignore)]
+fn test_interior_nul_in_env_value_is_error() {
+ match env_cmd().env("key", "has-some-\0\0s-inside").spawn() {
+ Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
+ Ok(_) => panic!(),
+ }
+}
+
+/// Tests that process creation flags work by debugging a process.
+/// Other creation flags make it hard or impossible to detect
+/// behavioral changes in the process.
+#[test]
+#[cfg(windows)]
+fn test_creation_flags() {
+ use crate::os::windows::process::CommandExt;
+ use crate::sys::c::{BOOL, DWORD, INFINITE};
+ #[repr(C, packed)]
+ struct DEBUG_EVENT {
+ pub event_code: DWORD,
+ pub process_id: DWORD,
+ pub thread_id: DWORD,
+ // This is a union in the real struct, but we don't
+ // need this data for the purposes of this test.
+ pub _junk: [u8; 164],
+ }
+
+ extern "system" {
+ fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL;
+ fn ContinueDebugEvent(
+ dwProcessId: DWORD,
+ dwThreadId: DWORD,
+ dwContinueStatus: DWORD,
+ ) -> BOOL;
+ }
+
+ const DEBUG_PROCESS: DWORD = 1;
+ const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5;
+ const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001;
+
+ let mut child =
+ Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap();
+ child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap();
+ let mut events = 0;
+ let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] };
+ loop {
+ if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 {
+ panic!("WaitForDebugEvent failed!");
+ }
+ events += 1;
+
+ if event.event_code == EXIT_PROCESS_DEBUG_EVENT {
+ break;
+ }
+
+ if unsafe {
+ ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED)
+ } == 0
+ {
+ panic!("ContinueDebugEvent failed!");
+ }
+ }
+ assert!(events > 0);
+}
+
+#[test]
+fn test_command_implements_send_sync() {
+ fn take_send_sync_type<T: Send + Sync>(_: T) {}
+ take_send_sync_type(Command::new(""))
+}
diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs
index 23c989f..eab26b6 100644
--- a/library/std/src/sync/barrier.rs
+++ b/library/std/src/sync/barrier.rs
@@ -1,3 +1,6 @@
+#[cfg(test)]
+mod tests;
+
use crate::fmt;
use crate::sync::{Condvar, Mutex};
@@ -13,7 +16,7 @@
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(Barrier::new(10));
/// for _ in 0..10 {
-/// let c = barrier.clone();
+/// let c = Arc::clone(&barrier);
/// // The same messages will be printed together.
/// // You will NOT see any interleaving.
/// handles.push(thread::spawn(move|| {
@@ -40,11 +43,8 @@
generation_id: usize,
}
-/// A `BarrierWaitResult` is returned by [`wait`] when all threads in the [`Barrier`]
-/// have rendezvoused.
-///
-/// [`wait`]: struct.Barrier.html#method.wait
-/// [`Barrier`]: struct.Barrier.html
+/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
+/// in the [`Barrier`] have rendezvoused.
///
/// # Examples
///
@@ -67,10 +67,10 @@
impl Barrier {
/// Creates a new barrier that can block a given number of threads.
///
- /// A barrier will block `n`-1 threads which call [`wait`] and then wake up
- /// all threads at once when the `n`th thread calls [`wait`].
+ /// A barrier will block `n`-1 threads which call [`wait()`] and then wake
+ /// up all threads at once when the `n`th thread calls [`wait()`].
///
- /// [`wait`]: #method.wait
+ /// [`wait()`]: Barrier::wait
///
/// # Examples
///
@@ -94,12 +94,9 @@
/// be used continuously.
///
/// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
- /// returns `true` from [`is_leader`] when returning from this function, and
- /// all other threads will receive a result that will return `false` from
- /// [`is_leader`].
- ///
- /// [`BarrierWaitResult`]: struct.BarrierWaitResult.html
- /// [`is_leader`]: struct.BarrierWaitResult.html#method.is_leader
+ /// returns `true` from [`BarrierWaitResult::is_leader()`] when returning
+ /// from this function, and all other threads will receive a result that
+ /// will return `false` from [`BarrierWaitResult::is_leader()`].
///
/// # Examples
///
@@ -110,7 +107,7 @@
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(Barrier::new(10));
/// for _ in 0..10 {
- /// let c = barrier.clone();
+ /// let c = Arc::clone(&barrier);
/// // The same messages will be printed together.
/// // You will NOT see any interleaving.
/// handles.push(thread::spawn(move|| {
@@ -153,13 +150,12 @@
}
impl BarrierWaitResult {
- /// Returns `true` if this thread from [`wait`] is the "leader thread".
+ /// Returns `true` if this thread is the "leader thread" for the call to
+ /// [`Barrier::wait()`].
///
/// Only one thread will have `true` returned from their result, all other
/// threads will have `false` returned.
///
- /// [`wait`]: struct.Barrier.html#method.wait
- ///
/// # Examples
///
/// ```
@@ -174,42 +170,3 @@
self.0
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::sync::mpsc::{channel, TryRecvError};
- use crate::sync::{Arc, Barrier};
- use crate::thread;
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn test_barrier() {
- const N: usize = 10;
-
- let barrier = Arc::new(Barrier::new(N));
- let (tx, rx) = channel();
-
- for _ in 0..N - 1 {
- let c = barrier.clone();
- let tx = tx.clone();
- thread::spawn(move || {
- tx.send(c.wait().is_leader()).unwrap();
- });
- }
-
- // At this point, all spawned threads should be blocked,
- // so we shouldn't get anything from the port
- assert!(matches!(rx.try_recv(), Err(TryRecvError::Empty)));
-
- let mut leader_found = barrier.wait().is_leader();
-
- // Now, the barrier is cleared and we should get data.
- for _ in 0..N - 1 {
- if rx.recv().unwrap() {
- assert!(!leader_found);
- leader_found = true;
- }
- }
- assert!(leader_found);
- }
-}
diff --git a/library/std/src/sync/barrier/tests.rs b/library/std/src/sync/barrier/tests.rs
new file mode 100644
index 0000000..834a3e7
--- /dev/null
+++ b/library/std/src/sync/barrier/tests.rs
@@ -0,0 +1,35 @@
+use crate::sync::mpsc::{channel, TryRecvError};
+use crate::sync::{Arc, Barrier};
+use crate::thread;
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn test_barrier() {
+ const N: usize = 10;
+
+ let barrier = Arc::new(Barrier::new(N));
+ let (tx, rx) = channel();
+
+ for _ in 0..N - 1 {
+ let c = barrier.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ tx.send(c.wait().is_leader()).unwrap();
+ });
+ }
+
+ // At this point, all spawned threads should be blocked,
+ // so we shouldn't get anything from the port
+ assert!(matches!(rx.try_recv(), Err(TryRecvError::Empty)));
+
+ let mut leader_found = barrier.wait().is_leader();
+
+ // Now, the barrier is cleared and we should get data.
+ for _ in 0..N - 1 {
+ if rx.recv().unwrap() {
+ assert!(!leader_found);
+ leader_found = true;
+ }
+ }
+ assert!(leader_found);
+}
diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs
index 4efd86a..1376d8e 100644
--- a/library/std/src/sync/condvar.rs
+++ b/library/std/src/sync/condvar.rs
@@ -1,3 +1,6 @@
+#[cfg(test)]
+mod tests;
+
use crate::fmt;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sync::{mutex, MutexGuard, PoisonError};
@@ -33,7 +36,7 @@
/// use std::time::Duration;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
- /// let pair2 = pair.clone();
+ /// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move || {
/// let (lock, cvar) = &*pair2;
@@ -75,13 +78,9 @@
/// and a mutex. The predicate is always verified inside of the mutex before
/// determining that a thread must block.
///
-/// Functions in this module will block the current **thread** of execution and
-/// are bindings to system-provided condition variables where possible. Note
-/// that this module places one additional restriction over the system condition
-/// variables: each condvar can be used with precisely one mutex at runtime. Any
-/// attempt to use multiple mutexes on the same condition variable will result
-/// in a runtime panic. If this is not desired, then the unsafe primitives in
-/// `sys` do not have this restriction but may result in undefined behavior.
+/// Functions in this module will block the current **thread** of execution.
+/// Note that any attempt to use multiple mutexes on the same condition
+/// variable may result in a runtime panic.
///
/// # Examples
///
@@ -90,7 +89,7 @@
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
-/// let pair2 = pair.clone();
+/// let pair2 = Arc::clone(&pair);
///
/// // Inside of our lock, spawn a new thread, and then wait for it to start.
/// thread::spawn(move|| {
@@ -156,10 +155,8 @@
///
/// # Panics
///
- /// This function will [`panic!`] if it is used with more than one mutex
- /// over time. Each condition variable is dynamically bound to exactly one
- /// mutex to ensure defined behavior across platforms. If this functionality
- /// is not desired, then unsafe primitives in `sys` are provided.
+ /// This function may [`panic!`] if it is used with more than one mutex
+ /// over time.
///
/// [`notify_one`]: Self::notify_one
/// [`notify_all`]: Self::notify_all
@@ -173,7 +170,7 @@
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
- /// let pair2 = pair.clone();
+ /// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move|| {
/// let (lock, cvar) = &*pair2;
@@ -229,7 +226,7 @@
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
- /// let pair2 = pair.clone();
+ /// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move|| {
/// let (lock, cvar) = &*pair2;
@@ -288,7 +285,7 @@
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
- /// let pair2 = pair.clone();
+ /// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move|| {
/// let (lock, cvar) = &*pair2;
@@ -360,7 +357,7 @@
/// use std::time::Duration;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
- /// let pair2 = pair.clone();
+ /// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move|| {
/// let (lock, cvar) = &*pair2;
@@ -429,7 +426,7 @@
/// use std::time::Duration;
///
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
- /// let pair2 = pair.clone();
+ /// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move|| {
/// let (lock, cvar) = &*pair2;
@@ -493,7 +490,7 @@
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
- /// let pair2 = pair.clone();
+ /// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move|| {
/// let (lock, cvar) = &*pair2;
@@ -533,7 +530,7 @@
/// use std::thread;
///
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
- /// let pair2 = pair.clone();
+ /// let pair2 = Arc::clone(&pair);
///
/// thread::spawn(move|| {
/// let (lock, cvar) = &*pair2;
@@ -556,8 +553,8 @@
unsafe { self.inner.notify_all() }
}
- fn verify(&self, mutex: &sys_mutex::Mutex) {
- let addr = mutex as *const _ as usize;
+ fn verify(&self, mutex: &sys_mutex::MovableMutex) {
+ let addr = mutex.raw() as *const _ as usize;
match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) {
// If we got out 0, then we have successfully bound the mutex to
// this cvar.
@@ -598,218 +595,3 @@
unsafe { self.inner.destroy() }
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::sync::atomic::{AtomicBool, Ordering};
- use crate::sync::mpsc::channel;
- use crate::sync::{Arc, Condvar, Mutex};
- use crate::thread;
- use crate::time::Duration;
-
- #[test]
- fn smoke() {
- let c = Condvar::new();
- c.notify_one();
- c.notify_all();
- }
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn notify_one() {
- let m = Arc::new(Mutex::new(()));
- let m2 = m.clone();
- let c = Arc::new(Condvar::new());
- let c2 = c.clone();
-
- let g = m.lock().unwrap();
- let _t = thread::spawn(move || {
- let _g = m2.lock().unwrap();
- c2.notify_one();
- });
- let g = c.wait(g).unwrap();
- drop(g);
- }
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn notify_all() {
- const N: usize = 10;
-
- let data = Arc::new((Mutex::new(0), Condvar::new()));
- let (tx, rx) = channel();
- for _ in 0..N {
- let data = data.clone();
- let tx = tx.clone();
- thread::spawn(move || {
- let &(ref lock, ref cond) = &*data;
- let mut cnt = lock.lock().unwrap();
- *cnt += 1;
- if *cnt == N {
- tx.send(()).unwrap();
- }
- while *cnt != 0 {
- cnt = cond.wait(cnt).unwrap();
- }
- tx.send(()).unwrap();
- });
- }
- drop(tx);
-
- let &(ref lock, ref cond) = &*data;
- rx.recv().unwrap();
- let mut cnt = lock.lock().unwrap();
- *cnt = 0;
- cond.notify_all();
- drop(cnt);
-
- for _ in 0..N {
- rx.recv().unwrap();
- }
- }
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn wait_while() {
- let pair = Arc::new((Mutex::new(false), Condvar::new()));
- let pair2 = pair.clone();
-
- // 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 mut started = lock.lock().unwrap();
- *started = true;
- // We notify the condvar that the value has changed.
- cvar.notify_one();
- });
-
- // Wait for the thread to start up.
- let &(ref lock, ref cvar) = &*pair;
- let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started);
- assert!(*guard.unwrap());
- }
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn wait_timeout_wait() {
- let m = Arc::new(Mutex::new(()));
- let c = Arc::new(Condvar::new());
-
- loop {
- let g = m.lock().unwrap();
- let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap();
- // spurious wakeups mean this isn't necessarily true
- // so execute test again, if not timeout
- if !no_timeout.timed_out() {
- continue;
- }
-
- break;
- }
- }
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn wait_timeout_while_wait() {
- let m = Arc::new(Mutex::new(()));
- let c = Arc::new(Condvar::new());
-
- let g = m.lock().unwrap();
- let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap();
- // no spurious wakeups. ensure it timed-out
- assert!(wait.timed_out());
- }
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn wait_timeout_while_instant_satisfy() {
- let m = Arc::new(Mutex::new(()));
- let c = Arc::new(Condvar::new());
-
- let g = m.lock().unwrap();
- let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap();
- // ensure it didn't time-out even if we were not given any time.
- assert!(!wait.timed_out());
- }
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn wait_timeout_while_wake() {
- let pair = Arc::new((Mutex::new(false), Condvar::new()));
- let pair_copy = pair.clone();
-
- let &(ref m, ref c) = &*pair;
- let g = m.lock().unwrap();
- let _t = thread::spawn(move || {
- let &(ref lock, ref cvar) = &*pair_copy;
- let mut started = lock.lock().unwrap();
- thread::sleep(Duration::from_millis(1));
- *started = true;
- cvar.notify_one();
- });
- let (g2, wait) = c
- .wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified)
- .unwrap();
- // ensure it didn't time-out even if we were not given any time.
- assert!(!wait.timed_out());
- assert!(*g2);
- }
-
- #[test]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn wait_timeout_wake() {
- let m = Arc::new(Mutex::new(()));
- let c = Arc::new(Condvar::new());
-
- loop {
- let g = m.lock().unwrap();
-
- let c2 = c.clone();
- let m2 = m.clone();
-
- let notified = Arc::new(AtomicBool::new(false));
- let notified_copy = notified.clone();
-
- let t = thread::spawn(move || {
- let _g = m2.lock().unwrap();
- thread::sleep(Duration::from_millis(1));
- notified_copy.store(true, Ordering::SeqCst);
- c2.notify_one();
- });
- let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap();
- assert!(!timeout_res.timed_out());
- // spurious wakeups mean this isn't necessarily true
- // so execute test again, if not notified
- if !notified.load(Ordering::SeqCst) {
- t.join().unwrap();
- continue;
- }
- drop(g);
-
- t.join().unwrap();
-
- break;
- }
- }
-
- #[test]
- #[should_panic]
- #[cfg_attr(target_os = "emscripten", ignore)]
- fn two_mutexes() {
- let m = Arc::new(Mutex::new(()));
- let m2 = m.clone();
- let c = Arc::new(Condvar::new());
- let c2 = c.clone();
-
- let mut g = m.lock().unwrap();
- let _t = thread::spawn(move || {
- let _g = m2.lock().unwrap();
- c2.notify_one();
- });
- g = c.wait(g).unwrap();
- drop(g);
-
- let m = Mutex::new(());
- let _ = c.wait(m.lock().unwrap()).unwrap();
- }
-}
diff --git a/library/std/src/sync/condvar/tests.rs b/library/std/src/sync/condvar/tests.rs
new file mode 100644
index 0000000..86d099e
--- /dev/null
+++ b/library/std/src/sync/condvar/tests.rs
@@ -0,0 +1,211 @@
+use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sync::mpsc::channel;
+use crate::sync::{Arc, Condvar, Mutex};
+use crate::thread;
+use crate::time::Duration;
+
+#[test]
+fn smoke() {
+ let c = Condvar::new();
+ c.notify_one();
+ c.notify_all();
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn notify_one() {
+ let m = Arc::new(Mutex::new(()));
+ let m2 = m.clone();
+ let c = Arc::new(Condvar::new());
+ let c2 = c.clone();
+
+ let g = m.lock().unwrap();
+ let _t = thread::spawn(move || {
+ let _g = m2.lock().unwrap();
+ c2.notify_one();
+ });
+ let g = c.wait(g).unwrap();
+ drop(g);
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn notify_all() {
+ const N: usize = 10;
+
+ let data = Arc::new((Mutex::new(0), Condvar::new()));
+ let (tx, rx) = channel();
+ for _ in 0..N {
+ let data = data.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let &(ref lock, ref cond) = &*data;
+ let mut cnt = lock.lock().unwrap();
+ *cnt += 1;
+ if *cnt == N {
+ tx.send(()).unwrap();
+ }
+ while *cnt != 0 {
+ cnt = cond.wait(cnt).unwrap();
+ }
+ tx.send(()).unwrap();
+ });
+ }
+ drop(tx);
+
+ let &(ref lock, ref cond) = &*data;
+ rx.recv().unwrap();
+ let mut cnt = lock.lock().unwrap();
+ *cnt = 0;
+ cond.notify_all();
+ drop(cnt);
+
+ for _ in 0..N {
+ rx.recv().unwrap();
+ }
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn wait_while() {
+ let pair = Arc::new((Mutex::new(false), Condvar::new()));
+ let pair2 = pair.clone();
+
+ // 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 mut started = lock.lock().unwrap();
+ *started = true;
+ // We notify the condvar that the value has changed.
+ cvar.notify_one();
+ });
+
+ // Wait for the thread to start up.
+ let &(ref lock, ref cvar) = &*pair;
+ let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started);
+ assert!(*guard.unwrap());
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn wait_timeout_wait() {
+ let m = Arc::new(Mutex::new(()));
+ let c = Arc::new(Condvar::new());
+
+ loop {
+ let g = m.lock().unwrap();
+ let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap();
+ // spurious wakeups mean this isn't necessarily true
+ // so execute test again, if not timeout
+ if !no_timeout.timed_out() {
+ continue;
+ }
+
+ break;
+ }
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn wait_timeout_while_wait() {
+ let m = Arc::new(Mutex::new(()));
+ let c = Arc::new(Condvar::new());
+
+ let g = m.lock().unwrap();
+ let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap();
+ // no spurious wakeups. ensure it timed-out
+ assert!(wait.timed_out());
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn wait_timeout_while_instant_satisfy() {
+ let m = Arc::new(Mutex::new(()));
+ let c = Arc::new(Condvar::new());
+
+ let g = m.lock().unwrap();
+ let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap();
+ // ensure it didn't time-out even if we were not given any time.
+ assert!(!wait.timed_out());
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn wait_timeout_while_wake() {
+ let pair = Arc::new((Mutex::new(false), Condvar::new()));
+ let pair_copy = pair.clone();
+
+ let &(ref m, ref c) = &*pair;
+ let g = m.lock().unwrap();
+ let _t = thread::spawn(move || {
+ let &(ref lock, ref cvar) = &*pair_copy;
+ let mut started = lock.lock().unwrap();
+ thread::sleep(Duration::from_millis(1));
+ *started = true;
+ cvar.notify_one();
+ });
+ let (g2, wait) = c
+ .wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified)
+ .unwrap();
+ // ensure it didn't time-out even if we were not given any time.
+ assert!(!wait.timed_out());
+ assert!(*g2);
+}
+
+#[test]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn wait_timeout_wake() {
+ let m = Arc::new(Mutex::new(()));
+ let c = Arc::new(Condvar::new());
+
+ loop {
+ let g = m.lock().unwrap();
+
+ let c2 = c.clone();
+ let m2 = m.clone();
+
+ let notified = Arc::new(AtomicBool::new(false));
+ let notified_copy = notified.clone();
+
+ let t = thread::spawn(move || {
+ let _g = m2.lock().unwrap();
+ thread::sleep(Duration::from_millis(1));
+ notified_copy.store(true, Ordering::SeqCst);
+ c2.notify_one();
+ });
+ let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap();
+ assert!(!timeout_res.timed_out());
+ // spurious wakeups mean this isn't necessarily true
+ // so execute test again, if not notified
+ if !notified.load(Ordering::SeqCst) {
+ t.join().unwrap();
+ continue;
+ }
+ drop(g);
+
+ t.join().unwrap();
+
+ break;
+ }
+}
+
+#[test]
+#[should_panic]
+#[cfg_attr(target_os = "emscripten", ignore)]
+fn two_mutexes() {
+ let m = Arc::new(Mutex::new(()));
+ let m2 = m.clone();
+ let c = Arc::new(Condvar::new());
+ let c2 = c.clone();
+
+ let mut g = m.lock().unwrap();
+ let _t = thread::spawn(move || {
+ let _g = m2.lock().unwrap();
+ c2.notify_one();
+ });
+ g = c.wait(g).unwrap();
+ drop(g);
+
+ let m = Mutex::new(());
+ let _ = c.wait(m.lock().unwrap()).unwrap();
+}
diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs
index ac83017..dc13c94 100644
--- a/library/std/src/sync/mpsc/mod.rs
+++ b/library/std/src/sync/mpsc/mod.rs
@@ -108,6 +108,12 @@
#![stable(feature = "rust1", since = "1.0.0")]
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod sync_tests;
+
// A description of how Rust's channel implementation works
//
// Channels are supposed to be the basic building block for all other
@@ -1525,6 +1531,11 @@
#[stable(feature = "mpsc_error_conversions", since = "1.24.0")]
impl<T> From<SendError<T>> for TrySendError<T> {
+ /// Converts a `SendError<T>` into a `TrySendError<T>`.
+ ///
+ /// This conversion always returns a `TrySendError::Disconnected` containing the data in the `SendError<T>`.
+ ///
+ /// No data is allocated on the heap.
fn from(err: SendError<T>) -> TrySendError<T> {
match err {
SendError(t) => TrySendError::Disconnected(t),
@@ -1570,6 +1581,11 @@
#[stable(feature = "mpsc_error_conversions", since = "1.24.0")]
impl From<RecvError> for TryRecvError {
+ /// Converts a `RecvError` into a `TryRecvError`.
+ ///
+ /// This conversion always returns `TryRecvError::Disconnected`.
+ ///
+ /// No data is allocated on the heap.
fn from(err: RecvError) -> TryRecvError {
match err {
RecvError => TryRecvError::Disconnected,
@@ -1600,1370 +1616,14 @@
#[stable(feature = "mpsc_error_conversions", since = "1.24.0")]
impl From<RecvError> for RecvTimeoutError {
+ /// Converts a `RecvError` into a `RecvTimeoutError`.
+ ///
+ /// This conversion always returns `RecvTimeoutError::Disconnected`.
+ ///
+ /// No data is allocated on the heap.
fn from(err: RecvError) -> RecvTimeoutError {
match err {
RecvError => RecvTimeoutError::Disconnected,
}
}
}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use super::*;
- use crate::env;
- use crate::thread;
- use crate::time::{Duration, Instant};
-
- pub fn stress_factor() -> usize {
- match env::var("RUST_TEST_STRESS") {
- Ok(val) => val.parse().unwrap(),
- Err(..) => 1,
- }
- }
-
- #[test]
- fn smoke() {
- let (tx, rx) = channel::<i32>();
- tx.send(1).unwrap();
- assert_eq!(rx.recv().unwrap(), 1);
- }
-
- #[test]
- fn drop_full() {
- let (tx, _rx) = channel::<Box<isize>>();
- tx.send(box 1).unwrap();
- }
-
- #[test]
- fn drop_full_shared() {
- let (tx, _rx) = channel::<Box<isize>>();
- drop(tx.clone());
- drop(tx.clone());
- tx.send(box 1).unwrap();
- }
-
- #[test]
- fn smoke_shared() {
- let (tx, rx) = channel::<i32>();
- tx.send(1).unwrap();
- assert_eq!(rx.recv().unwrap(), 1);
- let tx = tx.clone();
- tx.send(1).unwrap();
- assert_eq!(rx.recv().unwrap(), 1);
- }
-
- #[test]
- fn smoke_threads() {
- let (tx, rx) = channel::<i32>();
- let _t = thread::spawn(move || {
- tx.send(1).unwrap();
- });
- assert_eq!(rx.recv().unwrap(), 1);
- }
-
- #[test]
- fn smoke_port_gone() {
- let (tx, rx) = channel::<i32>();
- drop(rx);
- assert!(tx.send(1).is_err());
- }
-
- #[test]
- fn smoke_shared_port_gone() {
- let (tx, rx) = channel::<i32>();
- drop(rx);
- assert!(tx.send(1).is_err())
- }
-
- #[test]
- fn smoke_shared_port_gone2() {
- let (tx, rx) = channel::<i32>();
- drop(rx);
- let tx2 = tx.clone();
- drop(tx);
- assert!(tx2.send(1).is_err());
- }
-
- #[test]
- fn port_gone_concurrent() {
- let (tx, rx) = channel::<i32>();
- let _t = thread::spawn(move || {
- rx.recv().unwrap();
- });
- while tx.send(1).is_ok() {}
- }
-
- #[test]
- fn port_gone_concurrent_shared() {
- let (tx, rx) = channel::<i32>();
- let tx2 = tx.clone();
- let _t = thread::spawn(move || {
- rx.recv().unwrap();
- });
- while tx.send(1).is_ok() && tx2.send(1).is_ok() {}
- }
-
- #[test]
- fn smoke_chan_gone() {
- let (tx, rx) = channel::<i32>();
- drop(tx);
- assert!(rx.recv().is_err());
- }
-
- #[test]
- fn smoke_chan_gone_shared() {
- let (tx, rx) = channel::<()>();
- let tx2 = tx.clone();
- drop(tx);
- drop(tx2);
- assert!(rx.recv().is_err());
- }
-
- #[test]
- fn chan_gone_concurrent() {
- let (tx, rx) = channel::<i32>();
- let _t = thread::spawn(move || {
- tx.send(1).unwrap();
- tx.send(1).unwrap();
- });
- while rx.recv().is_ok() {}
- }
-
- #[test]
- fn stress() {
- let (tx, rx) = channel::<i32>();
- let t = thread::spawn(move || {
- for _ in 0..10000 {
- tx.send(1).unwrap();
- }
- });
- for _ in 0..10000 {
- assert_eq!(rx.recv().unwrap(), 1);
- }
- t.join().ok().expect("thread panicked");
- }
-
- #[test]
- fn stress_shared() {
- const AMT: u32 = 10000;
- const NTHREADS: u32 = 8;
- let (tx, rx) = channel::<i32>();
-
- let t = thread::spawn(move || {
- for _ in 0..AMT * NTHREADS {
- assert_eq!(rx.recv().unwrap(), 1);
- }
- match rx.try_recv() {
- Ok(..) => panic!(),
- _ => {}
- }
- });
-
- for _ in 0..NTHREADS {
- let tx = tx.clone();
- thread::spawn(move || {
- for _ in 0..AMT {
- tx.send(1).unwrap();
- }
- });
- }
- drop(tx);
- t.join().ok().expect("thread panicked");
- }
-
- #[test]
- fn send_from_outside_runtime() {
- let (tx1, rx1) = channel::<()>();
- let (tx2, rx2) = channel::<i32>();
- let t1 = thread::spawn(move || {
- tx1.send(()).unwrap();
- for _ in 0..40 {
- assert_eq!(rx2.recv().unwrap(), 1);
- }
- });
- rx1.recv().unwrap();
- let t2 = thread::spawn(move || {
- for _ in 0..40 {
- tx2.send(1).unwrap();
- }
- });
- t1.join().ok().expect("thread panicked");
- t2.join().ok().expect("thread panicked");
- }
-
- #[test]
- fn recv_from_outside_runtime() {
- let (tx, rx) = channel::<i32>();
- let t = thread::spawn(move || {
- for _ in 0..40 {
- assert_eq!(rx.recv().unwrap(), 1);
- }
- });
- for _ in 0..40 {
- tx.send(1).unwrap();
- }
- t.join().ok().expect("thread panicked");
- }
-
- #[test]
- fn no_runtime() {
- let (tx1, rx1) = channel::<i32>();
- let (tx2, rx2) = channel::<i32>();
- let t1 = thread::spawn(move || {
- assert_eq!(rx1.recv().unwrap(), 1);
- tx2.send(2).unwrap();
- });
- let t2 = thread::spawn(move || {
- tx1.send(1).unwrap();
- assert_eq!(rx2.recv().unwrap(), 2);
- });
- t1.join().ok().expect("thread panicked");
- t2.join().ok().expect("thread panicked");
- }
-
- #[test]
- fn oneshot_single_thread_close_port_first() {
- // Simple test of closing without sending
- let (_tx, rx) = channel::<i32>();
- drop(rx);
- }
-
- #[test]
- fn oneshot_single_thread_close_chan_first() {
- // Simple test of closing without sending
- let (tx, _rx) = channel::<i32>();
- drop(tx);
- }
-
- #[test]
- fn oneshot_single_thread_send_port_close() {
- // Testing that the sender cleans up the payload if receiver is closed
- let (tx, rx) = channel::<Box<i32>>();
- drop(rx);
- assert!(tx.send(box 0).is_err());
- }
-
- #[test]
- fn oneshot_single_thread_recv_chan_close() {
- // Receiving on a closed chan will panic
- let res = thread::spawn(move || {
- let (tx, rx) = channel::<i32>();
- drop(tx);
- rx.recv().unwrap();
- })
- .join();
- // What is our res?
- assert!(res.is_err());
- }
-
- #[test]
- fn oneshot_single_thread_send_then_recv() {
- let (tx, rx) = channel::<Box<i32>>();
- tx.send(box 10).unwrap();
- assert!(*rx.recv().unwrap() == 10);
- }
-
- #[test]
- fn oneshot_single_thread_try_send_open() {
- let (tx, rx) = channel::<i32>();
- assert!(tx.send(10).is_ok());
- assert!(rx.recv().unwrap() == 10);
- }
-
- #[test]
- fn oneshot_single_thread_try_send_closed() {
- let (tx, rx) = channel::<i32>();
- drop(rx);
- assert!(tx.send(10).is_err());
- }
-
- #[test]
- fn oneshot_single_thread_try_recv_open() {
- let (tx, rx) = channel::<i32>();
- tx.send(10).unwrap();
- assert!(rx.recv() == Ok(10));
- }
-
- #[test]
- fn oneshot_single_thread_try_recv_closed() {
- let (tx, rx) = channel::<i32>();
- drop(tx);
- assert!(rx.recv().is_err());
- }
-
- #[test]
- fn oneshot_single_thread_peek_data() {
- let (tx, rx) = channel::<i32>();
- assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
- tx.send(10).unwrap();
- assert_eq!(rx.try_recv(), Ok(10));
- }
-
- #[test]
- fn oneshot_single_thread_peek_close() {
- let (tx, rx) = channel::<i32>();
- drop(tx);
- assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
- assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
- }
-
- #[test]
- fn oneshot_single_thread_peek_open() {
- let (_tx, rx) = channel::<i32>();
- assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
- }
-
- #[test]
- fn oneshot_multi_task_recv_then_send() {
- let (tx, rx) = channel::<Box<i32>>();
- let _t = thread::spawn(move || {
- assert!(*rx.recv().unwrap() == 10);
- });
-
- tx.send(box 10).unwrap();
- }
-
- #[test]
- fn oneshot_multi_task_recv_then_close() {
- let (tx, rx) = channel::<Box<i32>>();
- let _t = thread::spawn(move || {
- drop(tx);
- });
- let res = thread::spawn(move || {
- assert!(*rx.recv().unwrap() == 10);
- })
- .join();
- assert!(res.is_err());
- }
-
- #[test]
- fn oneshot_multi_thread_close_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = channel::<i32>();
- let _t = thread::spawn(move || {
- drop(rx);
- });
- drop(tx);
- }
- }
-
- #[test]
- fn oneshot_multi_thread_send_close_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = channel::<i32>();
- let _t = thread::spawn(move || {
- drop(rx);
- });
- let _ = thread::spawn(move || {
- tx.send(1).unwrap();
- })
- .join();
- }
- }
-
- #[test]
- fn oneshot_multi_thread_recv_close_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = channel::<i32>();
- thread::spawn(move || {
- let res = thread::spawn(move || {
- rx.recv().unwrap();
- })
- .join();
- assert!(res.is_err());
- });
- let _t = thread::spawn(move || {
- thread::spawn(move || {
- drop(tx);
- });
- });
- }
- }
-
- #[test]
- fn oneshot_multi_thread_send_recv_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = channel::<Box<isize>>();
- let _t = thread::spawn(move || {
- tx.send(box 10).unwrap();
- });
- assert!(*rx.recv().unwrap() == 10);
- }
- }
-
- #[test]
- fn stream_send_recv_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = channel();
-
- send(tx, 0);
- recv(rx, 0);
-
- fn send(tx: Sender<Box<i32>>, i: i32) {
- if i == 10 {
- return;
- }
-
- thread::spawn(move || {
- tx.send(box i).unwrap();
- send(tx, i + 1);
- });
- }
-
- fn recv(rx: Receiver<Box<i32>>, i: i32) {
- if i == 10 {
- return;
- }
-
- thread::spawn(move || {
- assert!(*rx.recv().unwrap() == i);
- recv(rx, i + 1);
- });
- }
- }
- }
-
- #[test]
- fn oneshot_single_thread_recv_timeout() {
- let (tx, rx) = channel();
- tx.send(()).unwrap();
- assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
- assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
- tx.send(()).unwrap();
- assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
- }
-
- #[test]
- fn stress_recv_timeout_two_threads() {
- let (tx, rx) = channel();
- let stress = stress_factor() + 100;
- let timeout = Duration::from_millis(100);
-
- thread::spawn(move || {
- for i in 0..stress {
- if i % 2 == 0 {
- thread::sleep(timeout * 2);
- }
- tx.send(1usize).unwrap();
- }
- });
-
- let mut recv_count = 0;
- loop {
- match rx.recv_timeout(timeout) {
- Ok(n) => {
- assert_eq!(n, 1usize);
- recv_count += 1;
- }
- Err(RecvTimeoutError::Timeout) => continue,
- Err(RecvTimeoutError::Disconnected) => break,
- }
- }
-
- assert_eq!(recv_count, stress);
- }
-
- #[test]
- fn recv_timeout_upgrade() {
- let (tx, rx) = channel::<()>();
- let timeout = Duration::from_millis(1);
- let _tx_clone = tx.clone();
-
- let start = Instant::now();
- assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout));
- assert!(Instant::now() >= start + timeout);
- }
-
- #[test]
- fn stress_recv_timeout_shared() {
- let (tx, rx) = channel();
- let stress = stress_factor() + 100;
-
- for i in 0..stress {
- let tx = tx.clone();
- thread::spawn(move || {
- thread::sleep(Duration::from_millis(i as u64 * 10));
- tx.send(1usize).unwrap();
- });
- }
-
- drop(tx);
-
- let mut recv_count = 0;
- loop {
- match rx.recv_timeout(Duration::from_millis(10)) {
- Ok(n) => {
- assert_eq!(n, 1usize);
- recv_count += 1;
- }
- Err(RecvTimeoutError::Timeout) => continue,
- Err(RecvTimeoutError::Disconnected) => break,
- }
- }
-
- assert_eq!(recv_count, stress);
- }
-
- #[test]
- fn very_long_recv_timeout_wont_panic() {
- let (tx, rx) = channel::<()>();
- let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX)));
- thread::sleep(Duration::from_secs(1));
- assert!(tx.send(()).is_ok());
- assert_eq!(join_handle.join().unwrap(), Ok(()));
- }
-
- #[test]
- fn recv_a_lot() {
- // Regression test that we don't run out of stack in scheduler context
- let (tx, rx) = channel();
- for _ in 0..10000 {
- tx.send(()).unwrap();
- }
- for _ in 0..10000 {
- rx.recv().unwrap();
- }
- }
-
- #[test]
- fn shared_recv_timeout() {
- let (tx, rx) = channel();
- let total = 5;
- for _ in 0..total {
- let tx = tx.clone();
- thread::spawn(move || {
- tx.send(()).unwrap();
- });
- }
-
- for _ in 0..total {
- rx.recv().unwrap();
- }
-
- assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
- tx.send(()).unwrap();
- assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
- }
-
- #[test]
- fn shared_chan_stress() {
- let (tx, rx) = channel();
- let total = stress_factor() + 100;
- for _ in 0..total {
- let tx = tx.clone();
- thread::spawn(move || {
- tx.send(()).unwrap();
- });
- }
-
- for _ in 0..total {
- rx.recv().unwrap();
- }
- }
-
- #[test]
- fn test_nested_recv_iter() {
- let (tx, rx) = channel::<i32>();
- let (total_tx, total_rx) = channel::<i32>();
-
- let _t = thread::spawn(move || {
- let mut acc = 0;
- for x in rx.iter() {
- acc += x;
- }
- total_tx.send(acc).unwrap();
- });
-
- tx.send(3).unwrap();
- tx.send(1).unwrap();
- tx.send(2).unwrap();
- drop(tx);
- assert_eq!(total_rx.recv().unwrap(), 6);
- }
-
- #[test]
- fn test_recv_iter_break() {
- let (tx, rx) = channel::<i32>();
- let (count_tx, count_rx) = channel();
-
- let _t = thread::spawn(move || {
- let mut count = 0;
- for x in rx.iter() {
- if count >= 3 {
- break;
- } else {
- count += x;
- }
- }
- count_tx.send(count).unwrap();
- });
-
- tx.send(2).unwrap();
- tx.send(2).unwrap();
- tx.send(2).unwrap();
- let _ = tx.send(2);
- drop(tx);
- assert_eq!(count_rx.recv().unwrap(), 4);
- }
-
- #[test]
- fn test_recv_try_iter() {
- let (request_tx, request_rx) = channel();
- let (response_tx, response_rx) = channel();
-
- // Request `x`s until we have `6`.
- let t = thread::spawn(move || {
- let mut count = 0;
- loop {
- for x in response_rx.try_iter() {
- count += x;
- if count == 6 {
- return count;
- }
- }
- request_tx.send(()).unwrap();
- }
- });
-
- for _ in request_rx.iter() {
- if response_tx.send(2).is_err() {
- break;
- }
- }
-
- assert_eq!(t.join().unwrap(), 6);
- }
-
- #[test]
- fn test_recv_into_iter_owned() {
- let mut iter = {
- let (tx, rx) = channel::<i32>();
- tx.send(1).unwrap();
- tx.send(2).unwrap();
-
- rx.into_iter()
- };
- assert_eq!(iter.next().unwrap(), 1);
- assert_eq!(iter.next().unwrap(), 2);
- assert_eq!(iter.next().is_none(), true);
- }
-
- #[test]
- fn test_recv_into_iter_borrowed() {
- let (tx, rx) = channel::<i32>();
- tx.send(1).unwrap();
- tx.send(2).unwrap();
- drop(tx);
- let mut iter = (&rx).into_iter();
- assert_eq!(iter.next().unwrap(), 1);
- assert_eq!(iter.next().unwrap(), 2);
- assert_eq!(iter.next().is_none(), true);
- }
-
- #[test]
- fn try_recv_states() {
- let (tx1, rx1) = channel::<i32>();
- let (tx2, rx2) = channel::<()>();
- let (tx3, rx3) = channel::<()>();
- let _t = thread::spawn(move || {
- rx2.recv().unwrap();
- tx1.send(1).unwrap();
- tx3.send(()).unwrap();
- rx2.recv().unwrap();
- drop(tx1);
- tx3.send(()).unwrap();
- });
-
- assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
- tx2.send(()).unwrap();
- rx3.recv().unwrap();
- assert_eq!(rx1.try_recv(), Ok(1));
- assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
- tx2.send(()).unwrap();
- rx3.recv().unwrap();
- assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected));
- }
-
- // This bug used to end up in a livelock inside of the Receiver destructor
- // because the internal state of the Shared packet was corrupted
- #[test]
- fn destroy_upgraded_shared_port_when_sender_still_active() {
- let (tx, rx) = channel();
- let (tx2, rx2) = channel();
- let _t = thread::spawn(move || {
- rx.recv().unwrap(); // wait on a oneshot
- drop(rx); // destroy a shared
- tx2.send(()).unwrap();
- });
- // make sure the other thread has gone to sleep
- for _ in 0..5000 {
- thread::yield_now();
- }
-
- // upgrade to a shared chan and send a message
- let t = tx.clone();
- drop(tx);
- t.send(()).unwrap();
-
- // wait for the child thread to exit before we exit
- rx2.recv().unwrap();
- }
-
- #[test]
- fn issue_32114() {
- let (tx, _) = channel();
- let _ = tx.send(123);
- assert_eq!(tx.send(123), Err(SendError(123)));
- }
-}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod sync_tests {
- use super::*;
- use crate::env;
- use crate::thread;
- use crate::time::Duration;
-
- pub fn stress_factor() -> usize {
- match env::var("RUST_TEST_STRESS") {
- Ok(val) => val.parse().unwrap(),
- Err(..) => 1,
- }
- }
-
- #[test]
- fn smoke() {
- let (tx, rx) = sync_channel::<i32>(1);
- tx.send(1).unwrap();
- assert_eq!(rx.recv().unwrap(), 1);
- }
-
- #[test]
- fn drop_full() {
- let (tx, _rx) = sync_channel::<Box<isize>>(1);
- tx.send(box 1).unwrap();
- }
-
- #[test]
- fn smoke_shared() {
- let (tx, rx) = sync_channel::<i32>(1);
- tx.send(1).unwrap();
- assert_eq!(rx.recv().unwrap(), 1);
- let tx = tx.clone();
- tx.send(1).unwrap();
- assert_eq!(rx.recv().unwrap(), 1);
- }
-
- #[test]
- fn recv_timeout() {
- let (tx, rx) = sync_channel::<i32>(1);
- assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
- tx.send(1).unwrap();
- assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1));
- }
-
- #[test]
- fn smoke_threads() {
- let (tx, rx) = sync_channel::<i32>(0);
- let _t = thread::spawn(move || {
- tx.send(1).unwrap();
- });
- assert_eq!(rx.recv().unwrap(), 1);
- }
-
- #[test]
- fn smoke_port_gone() {
- let (tx, rx) = sync_channel::<i32>(0);
- drop(rx);
- assert!(tx.send(1).is_err());
- }
-
- #[test]
- fn smoke_shared_port_gone2() {
- let (tx, rx) = sync_channel::<i32>(0);
- drop(rx);
- let tx2 = tx.clone();
- drop(tx);
- assert!(tx2.send(1).is_err());
- }
-
- #[test]
- fn port_gone_concurrent() {
- let (tx, rx) = sync_channel::<i32>(0);
- let _t = thread::spawn(move || {
- rx.recv().unwrap();
- });
- while tx.send(1).is_ok() {}
- }
-
- #[test]
- fn port_gone_concurrent_shared() {
- let (tx, rx) = sync_channel::<i32>(0);
- let tx2 = tx.clone();
- let _t = thread::spawn(move || {
- rx.recv().unwrap();
- });
- while tx.send(1).is_ok() && tx2.send(1).is_ok() {}
- }
-
- #[test]
- fn smoke_chan_gone() {
- let (tx, rx) = sync_channel::<i32>(0);
- drop(tx);
- assert!(rx.recv().is_err());
- }
-
- #[test]
- fn smoke_chan_gone_shared() {
- let (tx, rx) = sync_channel::<()>(0);
- let tx2 = tx.clone();
- drop(tx);
- drop(tx2);
- assert!(rx.recv().is_err());
- }
-
- #[test]
- fn chan_gone_concurrent() {
- let (tx, rx) = sync_channel::<i32>(0);
- thread::spawn(move || {
- tx.send(1).unwrap();
- tx.send(1).unwrap();
- });
- while rx.recv().is_ok() {}
- }
-
- #[test]
- fn stress() {
- let (tx, rx) = sync_channel::<i32>(0);
- thread::spawn(move || {
- for _ in 0..10000 {
- tx.send(1).unwrap();
- }
- });
- for _ in 0..10000 {
- assert_eq!(rx.recv().unwrap(), 1);
- }
- }
-
- #[test]
- fn stress_recv_timeout_two_threads() {
- let (tx, rx) = sync_channel::<i32>(0);
-
- thread::spawn(move || {
- for _ in 0..10000 {
- tx.send(1).unwrap();
- }
- });
-
- let mut recv_count = 0;
- loop {
- match rx.recv_timeout(Duration::from_millis(1)) {
- Ok(v) => {
- assert_eq!(v, 1);
- recv_count += 1;
- }
- Err(RecvTimeoutError::Timeout) => continue,
- Err(RecvTimeoutError::Disconnected) => break,
- }
- }
-
- assert_eq!(recv_count, 10000);
- }
-
- #[test]
- fn stress_recv_timeout_shared() {
- const AMT: u32 = 1000;
- const NTHREADS: u32 = 8;
- let (tx, rx) = sync_channel::<i32>(0);
- let (dtx, drx) = sync_channel::<()>(0);
-
- thread::spawn(move || {
- let mut recv_count = 0;
- loop {
- match rx.recv_timeout(Duration::from_millis(10)) {
- Ok(v) => {
- assert_eq!(v, 1);
- recv_count += 1;
- }
- Err(RecvTimeoutError::Timeout) => continue,
- Err(RecvTimeoutError::Disconnected) => break,
- }
- }
-
- assert_eq!(recv_count, AMT * NTHREADS);
- assert!(rx.try_recv().is_err());
-
- dtx.send(()).unwrap();
- });
-
- for _ in 0..NTHREADS {
- let tx = tx.clone();
- thread::spawn(move || {
- for _ in 0..AMT {
- tx.send(1).unwrap();
- }
- });
- }
-
- drop(tx);
-
- drx.recv().unwrap();
- }
-
- #[test]
- fn stress_shared() {
- const AMT: u32 = 1000;
- const NTHREADS: u32 = 8;
- let (tx, rx) = sync_channel::<i32>(0);
- let (dtx, drx) = sync_channel::<()>(0);
-
- thread::spawn(move || {
- for _ in 0..AMT * NTHREADS {
- assert_eq!(rx.recv().unwrap(), 1);
- }
- match rx.try_recv() {
- Ok(..) => panic!(),
- _ => {}
- }
- dtx.send(()).unwrap();
- });
-
- for _ in 0..NTHREADS {
- let tx = tx.clone();
- thread::spawn(move || {
- for _ in 0..AMT {
- tx.send(1).unwrap();
- }
- });
- }
- drop(tx);
- drx.recv().unwrap();
- }
-
- #[test]
- fn oneshot_single_thread_close_port_first() {
- // Simple test of closing without sending
- let (_tx, rx) = sync_channel::<i32>(0);
- drop(rx);
- }
-
- #[test]
- fn oneshot_single_thread_close_chan_first() {
- // Simple test of closing without sending
- let (tx, _rx) = sync_channel::<i32>(0);
- drop(tx);
- }
-
- #[test]
- fn oneshot_single_thread_send_port_close() {
- // Testing that the sender cleans up the payload if receiver is closed
- let (tx, rx) = sync_channel::<Box<i32>>(0);
- drop(rx);
- assert!(tx.send(box 0).is_err());
- }
-
- #[test]
- fn oneshot_single_thread_recv_chan_close() {
- // Receiving on a closed chan will panic
- let res = thread::spawn(move || {
- let (tx, rx) = sync_channel::<i32>(0);
- drop(tx);
- rx.recv().unwrap();
- })
- .join();
- // What is our res?
- assert!(res.is_err());
- }
-
- #[test]
- fn oneshot_single_thread_send_then_recv() {
- let (tx, rx) = sync_channel::<Box<i32>>(1);
- tx.send(box 10).unwrap();
- assert!(*rx.recv().unwrap() == 10);
- }
-
- #[test]
- fn oneshot_single_thread_try_send_open() {
- let (tx, rx) = sync_channel::<i32>(1);
- assert_eq!(tx.try_send(10), Ok(()));
- assert!(rx.recv().unwrap() == 10);
- }
-
- #[test]
- fn oneshot_single_thread_try_send_closed() {
- let (tx, rx) = sync_channel::<i32>(0);
- drop(rx);
- assert_eq!(tx.try_send(10), Err(TrySendError::Disconnected(10)));
- }
-
- #[test]
- fn oneshot_single_thread_try_send_closed2() {
- let (tx, _rx) = sync_channel::<i32>(0);
- assert_eq!(tx.try_send(10), Err(TrySendError::Full(10)));
- }
-
- #[test]
- fn oneshot_single_thread_try_recv_open() {
- let (tx, rx) = sync_channel::<i32>(1);
- tx.send(10).unwrap();
- assert!(rx.recv() == Ok(10));
- }
-
- #[test]
- fn oneshot_single_thread_try_recv_closed() {
- let (tx, rx) = sync_channel::<i32>(0);
- drop(tx);
- assert!(rx.recv().is_err());
- }
-
- #[test]
- fn oneshot_single_thread_try_recv_closed_with_data() {
- let (tx, rx) = sync_channel::<i32>(1);
- tx.send(10).unwrap();
- drop(tx);
- assert_eq!(rx.try_recv(), Ok(10));
- assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
- }
-
- #[test]
- fn oneshot_single_thread_peek_data() {
- let (tx, rx) = sync_channel::<i32>(1);
- assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
- tx.send(10).unwrap();
- assert_eq!(rx.try_recv(), Ok(10));
- }
-
- #[test]
- fn oneshot_single_thread_peek_close() {
- let (tx, rx) = sync_channel::<i32>(0);
- drop(tx);
- assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
- assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
- }
-
- #[test]
- fn oneshot_single_thread_peek_open() {
- let (_tx, rx) = sync_channel::<i32>(0);
- assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
- }
-
- #[test]
- fn oneshot_multi_task_recv_then_send() {
- let (tx, rx) = sync_channel::<Box<i32>>(0);
- let _t = thread::spawn(move || {
- assert!(*rx.recv().unwrap() == 10);
- });
-
- tx.send(box 10).unwrap();
- }
-
- #[test]
- fn oneshot_multi_task_recv_then_close() {
- let (tx, rx) = sync_channel::<Box<i32>>(0);
- let _t = thread::spawn(move || {
- drop(tx);
- });
- let res = thread::spawn(move || {
- assert!(*rx.recv().unwrap() == 10);
- })
- .join();
- assert!(res.is_err());
- }
-
- #[test]
- fn oneshot_multi_thread_close_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = sync_channel::<i32>(0);
- let _t = thread::spawn(move || {
- drop(rx);
- });
- drop(tx);
- }
- }
-
- #[test]
- fn oneshot_multi_thread_send_close_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = sync_channel::<i32>(0);
- let _t = thread::spawn(move || {
- drop(rx);
- });
- let _ = thread::spawn(move || {
- tx.send(1).unwrap();
- })
- .join();
- }
- }
-
- #[test]
- fn oneshot_multi_thread_recv_close_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = sync_channel::<i32>(0);
- let _t = thread::spawn(move || {
- let res = thread::spawn(move || {
- rx.recv().unwrap();
- })
- .join();
- assert!(res.is_err());
- });
- let _t = thread::spawn(move || {
- thread::spawn(move || {
- drop(tx);
- });
- });
- }
- }
-
- #[test]
- fn oneshot_multi_thread_send_recv_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = sync_channel::<Box<i32>>(0);
- let _t = thread::spawn(move || {
- tx.send(box 10).unwrap();
- });
- assert!(*rx.recv().unwrap() == 10);
- }
- }
-
- #[test]
- fn stream_send_recv_stress() {
- for _ in 0..stress_factor() {
- let (tx, rx) = sync_channel::<Box<i32>>(0);
-
- send(tx, 0);
- recv(rx, 0);
-
- fn send(tx: SyncSender<Box<i32>>, i: i32) {
- if i == 10 {
- return;
- }
-
- thread::spawn(move || {
- tx.send(box i).unwrap();
- send(tx, i + 1);
- });
- }
-
- fn recv(rx: Receiver<Box<i32>>, i: i32) {
- if i == 10 {
- return;
- }
-
- thread::spawn(move || {
- assert!(*rx.recv().unwrap() == i);
- recv(rx, i + 1);
- });
- }
- }
- }
-
- #[test]
- fn recv_a_lot() {
- // Regression test that we don't run out of stack in scheduler context
- let (tx, rx) = sync_channel(10000);
- for _ in 0..10000 {
- tx.send(()).unwrap();
- }
- for _ in 0..10000 {
- rx.recv().unwrap();
- }
- }
-
- #[test]
- fn shared_chan_stress() {
- let (tx, rx) = sync_channel(0);
- let total = stress_factor() + 100;
- for _ in 0..total {
- let tx = tx.clone();
- thread::spawn(move || {
- tx.send(()).unwrap();
- });
- }
-
- for _ in 0..total {
- rx.recv().unwrap();
- }
- }
-
- #[test]
- fn test_nested_recv_iter() {
- let (tx, rx) = sync_channel::<i32>(0);
- let (total_tx, total_rx) = sync_channel::<i32>(0);
-
- let _t = thread::spawn(move || {
- let mut acc = 0;
- for x in rx.iter() {
- acc += x;
- }
- total_tx.send(acc).unwrap();
- });
-
- tx.send(3).unwrap();
- tx.send(1).unwrap();
- tx.send(2).unwrap();
- drop(tx);
- assert_eq!(total_rx.recv().unwrap(), 6);
- }
-
- #[test]
- fn test_recv_iter_break() {
- let (tx, rx) = sync_channel::<i32>(0);
- let (count_tx, count_rx) = sync_channel(0);
-
- let _t = thread::spawn(move || {
- let mut count = 0;
- for x in rx.iter() {
- if count >= 3 {
- break;
- } else {
- count += x;
- }
- }
- count_tx.send(count).unwrap();
- });
-
- tx.send(2).unwrap();
- tx.send(2).unwrap();
- tx.send(2).unwrap();
- let _ = tx.try_send(2);
- drop(tx);
- assert_eq!(count_rx.recv().unwrap(), 4);
- }
-
- #[test]
- fn try_recv_states() {
- let (tx1, rx1) = sync_channel::<i32>(1);
- let (tx2, rx2) = sync_channel::<()>(1);
- let (tx3, rx3) = sync_channel::<()>(1);
- let _t = thread::spawn(move || {
- rx2.recv().unwrap();
- tx1.send(1).unwrap();
- tx3.send(()).unwrap();
- rx2.recv().unwrap();
- drop(tx1);
- tx3.send(()).unwrap();
- });
-
- assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
- tx2.send(()).unwrap();
- rx3.recv().unwrap();
- assert_eq!(rx1.try_recv(), Ok(1));
- assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
- tx2.send(()).unwrap();
- rx3.recv().unwrap();
- assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected));
- }
-
- // This bug used to end up in a livelock inside of the Receiver destructor
- // because the internal state of the Shared packet was corrupted
- #[test]
- fn destroy_upgraded_shared_port_when_sender_still_active() {
- let (tx, rx) = sync_channel::<()>(0);
- let (tx2, rx2) = sync_channel::<()>(0);
- let _t = thread::spawn(move || {
- rx.recv().unwrap(); // wait on a oneshot
- drop(rx); // destroy a shared
- tx2.send(()).unwrap();
- });
- // make sure the other thread has gone to sleep
- for _ in 0..5000 {
- thread::yield_now();
- }
-
- // upgrade to a shared chan and send a message
- let t = tx.clone();
- drop(tx);
- t.send(()).unwrap();
-
- // wait for the child thread to exit before we exit
- rx2.recv().unwrap();
- }
-
- #[test]
- fn send1() {
- let (tx, rx) = sync_channel::<i32>(0);
- let _t = thread::spawn(move || {
- rx.recv().unwrap();
- });
- assert_eq!(tx.send(1), Ok(()));
- }
-
- #[test]
- fn send2() {
- let (tx, rx) = sync_channel::<i32>(0);
- let _t = thread::spawn(move || {
- drop(rx);
- });
- assert!(tx.send(1).is_err());
- }
-
- #[test]
- fn send3() {
- let (tx, rx) = sync_channel::<i32>(1);
- assert_eq!(tx.send(1), Ok(()));
- let _t = thread::spawn(move || {
- drop(rx);
- });
- assert!(tx.send(1).is_err());
- }
-
- #[test]
- fn send4() {
- let (tx, rx) = sync_channel::<i32>(0);
- let tx2 = tx.clone();
- let (done, donerx) = channel();
- let done2 = done.clone();
- let _t = thread::spawn(move || {
- assert!(tx.send(1).is_err());
- done.send(()).unwrap();
- });
- let _t = thread::spawn(move || {
- assert!(tx2.send(2).is_err());
- done2.send(()).unwrap();
- });
- drop(rx);
- donerx.recv().unwrap();
- donerx.recv().unwrap();
- }
-
- #[test]
- fn try_send1() {
- let (tx, _rx) = sync_channel::<i32>(0);
- assert_eq!(tx.try_send(1), Err(TrySendError::Full(1)));
- }
-
- #[test]
- fn try_send2() {
- let (tx, _rx) = sync_channel::<i32>(1);
- assert_eq!(tx.try_send(1), Ok(()));
- assert_eq!(tx.try_send(1), Err(TrySendError::Full(1)));
- }
-
- #[test]
- fn try_send3() {
- let (tx, rx) = sync_channel::<i32>(1);
- assert_eq!(tx.try_send(1), Ok(()));
- drop(rx);
- assert_eq!(tx.try_send(1), Err(TrySendError::Disconnected(1)));
- }
-
- #[test]
- fn issue_15761() {
- fn repro() {
- let (tx1, rx1) = sync_channel::<()>(3);
- let (tx2, rx2) = sync_channel::<()>(3);
-
- let _t = thread::spawn(move || {
- rx1.recv().unwrap();
- tx2.try_send(()).unwrap();
- });
-
- tx1.try_send(()).unwrap();
- rx2.recv().unwrap();
- }
-
- for _ in 0..100 {
- repro()
- }
- }
-}
diff --git a/library/std/src/sync/mpsc/mpsc_queue.rs b/library/std/src/sync/mpsc/mpsc_queue.rs
index 6e7a7be..42bc639 100644
--- a/library/std/src/sync/mpsc/mpsc_queue.rs
+++ b/library/std/src/sync/mpsc/mpsc_queue.rs
@@ -11,6 +11,9 @@
// http://www.1024cores.net/home/lock-free-algorithms
// /queues/non-intrusive-mpsc-node-based-queue
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
pub use self::PopResult::*;
use core::cell::UnsafeCell;
@@ -112,54 +115,3 @@
}
}
}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use super::{Data, Empty, Inconsistent, Queue};
- use crate::sync::mpsc::channel;
- use crate::sync::Arc;
- use crate::thread;
-
- #[test]
- fn test_full() {
- let q: Queue<Box<_>> = Queue::new();
- q.push(box 1);
- q.push(box 2);
- }
-
- #[test]
- fn test() {
- let nthreads = 8;
- let nmsgs = 1000;
- let q = Queue::new();
- match q.pop() {
- Empty => {}
- Inconsistent | Data(..) => panic!(),
- }
- let (tx, rx) = channel();
- let q = Arc::new(q);
-
- for _ in 0..nthreads {
- let tx = tx.clone();
- let q = q.clone();
- thread::spawn(move || {
- for i in 0..nmsgs {
- q.push(i);
- }
- tx.send(()).unwrap();
- });
- }
-
- let mut i = 0;
- while i < nthreads * nmsgs {
- match q.pop() {
- Empty | Inconsistent => {}
- Data(_) => i += 1,
- }
- }
- drop(tx);
- for _ in 0..nthreads {
- rx.recv().unwrap();
- }
- }
-}
diff --git a/library/std/src/sync/mpsc/mpsc_queue/tests.rs b/library/std/src/sync/mpsc/mpsc_queue/tests.rs
new file mode 100644
index 0000000..348b834
--- /dev/null
+++ b/library/std/src/sync/mpsc/mpsc_queue/tests.rs
@@ -0,0 +1,47 @@
+use super::{Data, Empty, Inconsistent, Queue};
+use crate::sync::mpsc::channel;
+use crate::sync::Arc;
+use crate::thread;
+
+#[test]
+fn test_full() {
+ let q: Queue<Box<_>> = Queue::new();
+ q.push(box 1);
+ q.push(box 2);
+}
+
+#[test]
+fn test() {
+ let nthreads = 8;
+ let nmsgs = 1000;
+ let q = Queue::new();
+ match q.pop() {
+ Empty => {}
+ Inconsistent | Data(..) => panic!(),
+ }
+ let (tx, rx) = channel();
+ let q = Arc::new(q);
+
+ for _ in 0..nthreads {
+ let tx = tx.clone();
+ let q = q.clone();
+ thread::spawn(move || {
+ for i in 0..nmsgs {
+ q.push(i);
+ }
+ tx.send(()).unwrap();
+ });
+ }
+
+ let mut i = 0;
+ while i < nthreads * nmsgs {
+ match q.pop() {
+ Empty | Inconsistent => {}
+ Data(_) => i += 1,
+ }
+ }
+ drop(tx);
+ for _ in 0..nthreads {
+ rx.recv().unwrap();
+ }
+}
diff --git a/library/std/src/sync/mpsc/spsc_queue.rs b/library/std/src/sync/mpsc/spsc_queue.rs
index 0274268..9bf99f1 100644
--- a/library/std/src/sync/mpsc/spsc_queue.rs
+++ b/library/std/src/sync/mpsc/spsc_queue.rs
@@ -6,6 +6,9 @@
// http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
use core::cell::UnsafeCell;
use core::ptr;
@@ -231,108 +234,3 @@
}
}
}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use super::Queue;
- use crate::sync::mpsc::channel;
- use crate::sync::Arc;
- use crate::thread;
-
- #[test]
- fn smoke() {
- unsafe {
- let queue = Queue::with_additions(0, (), ());
- queue.push(1);
- queue.push(2);
- assert_eq!(queue.pop(), Some(1));
- assert_eq!(queue.pop(), Some(2));
- assert_eq!(queue.pop(), None);
- queue.push(3);
- queue.push(4);
- assert_eq!(queue.pop(), Some(3));
- assert_eq!(queue.pop(), Some(4));
- assert_eq!(queue.pop(), None);
- }
- }
-
- #[test]
- fn peek() {
- unsafe {
- let queue = Queue::with_additions(0, (), ());
- queue.push(vec![1]);
-
- // Ensure the borrowchecker works
- match queue.peek() {
- Some(vec) => {
- assert_eq!(&*vec, &[1]);
- }
- None => unreachable!(),
- }
-
- match queue.pop() {
- Some(vec) => {
- assert_eq!(&*vec, &[1]);
- }
- None => unreachable!(),
- }
- }
- }
-
- #[test]
- fn drop_full() {
- unsafe {
- let q: Queue<Box<_>> = Queue::with_additions(0, (), ());
- q.push(box 1);
- q.push(box 2);
- }
- }
-
- #[test]
- fn smoke_bound() {
- unsafe {
- let q = Queue::with_additions(0, (), ());
- q.push(1);
- q.push(2);
- assert_eq!(q.pop(), Some(1));
- assert_eq!(q.pop(), Some(2));
- assert_eq!(q.pop(), None);
- q.push(3);
- q.push(4);
- assert_eq!(q.pop(), Some(3));
- assert_eq!(q.pop(), Some(4));
- assert_eq!(q.pop(), None);
- }
- }
-
- #[test]
- fn stress() {
- unsafe {
- stress_bound(0);
- stress_bound(1);
- }
-
- unsafe fn stress_bound(bound: usize) {
- let q = Arc::new(Queue::with_additions(bound, (), ()));
-
- let (tx, rx) = channel();
- let q2 = q.clone();
- let _t = thread::spawn(move || {
- for _ in 0..100000 {
- loop {
- match q2.pop() {
- Some(1) => break,
- Some(_) => panic!(),
- None => {}
- }
- }
- }
- tx.send(()).unwrap();
- });
- for _ in 0..100000 {
- q.push(1);
- }
- rx.recv().unwrap();
- }
- }
-}
diff --git a/library/std/src/sync/mpsc/spsc_queue/tests.rs b/library/std/src/sync/mpsc/spsc_queue/tests.rs
new file mode 100644
index 0000000..e4fd15c
--- /dev/null
+++ b/library/std/src/sync/mpsc/spsc_queue/tests.rs
@@ -0,0 +1,101 @@
+use super::Queue;
+use crate::sync::mpsc::channel;
+use crate::sync::Arc;
+use crate::thread;
+
+#[test]
+fn smoke() {
+ unsafe {
+ let queue = Queue::with_additions(0, (), ());
+ queue.push(1);
+ queue.push(2);
+ assert_eq!(queue.pop(), Some(1));
+ assert_eq!(queue.pop(), Some(2));
+ assert_eq!(queue.pop(), None);
+ queue.push(3);
+ queue.push(4);
+ assert_eq!(queue.pop(), Some(3));
+ assert_eq!(queue.pop(), Some(4));
+ assert_eq!(queue.pop(), None);
+ }
+}
+
+#[test]
+fn peek() {
+ unsafe {
+ let queue = Queue::with_additions(0, (), ());
+ queue.push(vec![1]);
+
+ // Ensure the borrowchecker works
+ match queue.peek() {
+ Some(vec) => {
+ assert_eq!(&*vec, &[1]);
+ }
+ None => unreachable!(),
+ }
+
+ match queue.pop() {
+ Some(vec) => {
+ assert_eq!(&*vec, &[1]);
+ }
+ None => unreachable!(),
+ }
+ }
+}
+
+#[test]
+fn drop_full() {
+ unsafe {
+ let q: Queue<Box<_>> = Queue::with_additions(0, (), ());
+ q.push(box 1);
+ q.push(box 2);
+ }
+}
+
+#[test]
+fn smoke_bound() {
+ unsafe {
+ let q = Queue::with_additions(0, (), ());
+ q.push(1);
+ q.push(2);
+ assert_eq!(q.pop(), Some(1));
+ assert_eq!(q.pop(), Some(2));
+ assert_eq!(q.pop(), None);
+ q.push(3);
+ q.push(4);
+ assert_eq!(q.pop(), Some(3));
+ assert_eq!(q.pop(), Some(4));
+ assert_eq!(q.pop(), None);
+ }
+}
+
+#[test]
+fn stress() {
+ unsafe {
+ stress_bound(0);
+ stress_bound(1);
+ }
+
+ unsafe fn stress_bound(bound: usize) {
+ let q = Arc::new(Queue::with_additions(bound, (), ()));
+
+ let (tx, rx) = channel();
+ let q2 = q.clone();
+ let _t = thread::spawn(move || {
+ for _ in 0..100000 {
+ loop {
+ match q2.pop() {
+ Some(1) => break,
+ Some(_) => panic!(),
+ None => {}
+ }
+ }
+ }
+ tx.send(()).unwrap();
+ });
+ for _ in 0..100000 {
+ q.push(1);
+ }
+ rx.recv().unwrap();
+ }
+}
diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/src/sync/mpsc/sync_tests.rs
new file mode 100644
index 0000000..0052a38
--- /dev/null
+++ b/library/std/src/sync/mpsc/sync_tests.rs
@@ -0,0 +1,647 @@
+use super::*;
+use crate::env;
+use crate::thread;
+use crate::time::Duration;
+
+pub fn stress_factor() -> usize {
+ match env::var("RUST_TEST_STRESS") {
+ Ok(val) => val.parse().unwrap(),
+ Err(..) => 1,
+ }
+}
+
+#[test]
+fn smoke() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ tx.send(1).unwrap();
+ assert_eq!(rx.recv().unwrap(), 1);
+}
+
+#[test]
+fn drop_full() {
+ let (tx, _rx) = sync_channel::<Box<isize>>(1);
+ tx.send(box 1).unwrap();
+}
+
+#[test]
+fn smoke_shared() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ tx.send(1).unwrap();
+ assert_eq!(rx.recv().unwrap(), 1);
+ let tx = tx.clone();
+ tx.send(1).unwrap();
+ assert_eq!(rx.recv().unwrap(), 1);
+}
+
+#[test]
+fn recv_timeout() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
+ tx.send(1).unwrap();
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1));
+}
+
+#[test]
+fn smoke_threads() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let _t = thread::spawn(move || {
+ tx.send(1).unwrap();
+ });
+ assert_eq!(rx.recv().unwrap(), 1);
+}
+
+#[test]
+fn smoke_port_gone() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ drop(rx);
+ assert!(tx.send(1).is_err());
+}
+
+#[test]
+fn smoke_shared_port_gone2() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ drop(rx);
+ let tx2 = tx.clone();
+ drop(tx);
+ assert!(tx2.send(1).is_err());
+}
+
+#[test]
+fn port_gone_concurrent() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let _t = thread::spawn(move || {
+ rx.recv().unwrap();
+ });
+ while tx.send(1).is_ok() {}
+}
+
+#[test]
+fn port_gone_concurrent_shared() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let tx2 = tx.clone();
+ let _t = thread::spawn(move || {
+ rx.recv().unwrap();
+ });
+ while tx.send(1).is_ok() && tx2.send(1).is_ok() {}
+}
+
+#[test]
+fn smoke_chan_gone() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ drop(tx);
+ assert!(rx.recv().is_err());
+}
+
+#[test]
+fn smoke_chan_gone_shared() {
+ let (tx, rx) = sync_channel::<()>(0);
+ let tx2 = tx.clone();
+ drop(tx);
+ drop(tx2);
+ assert!(rx.recv().is_err());
+}
+
+#[test]
+fn chan_gone_concurrent() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ thread::spawn(move || {
+ tx.send(1).unwrap();
+ tx.send(1).unwrap();
+ });
+ while rx.recv().is_ok() {}
+}
+
+#[test]
+fn stress() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ thread::spawn(move || {
+ for _ in 0..10000 {
+ tx.send(1).unwrap();
+ }
+ });
+ for _ in 0..10000 {
+ assert_eq!(rx.recv().unwrap(), 1);
+ }
+}
+
+#[test]
+fn stress_recv_timeout_two_threads() {
+ let (tx, rx) = sync_channel::<i32>(0);
+
+ thread::spawn(move || {
+ for _ in 0..10000 {
+ tx.send(1).unwrap();
+ }
+ });
+
+ let mut recv_count = 0;
+ loop {
+ match rx.recv_timeout(Duration::from_millis(1)) {
+ Ok(v) => {
+ assert_eq!(v, 1);
+ recv_count += 1;
+ }
+ Err(RecvTimeoutError::Timeout) => continue,
+ Err(RecvTimeoutError::Disconnected) => break,
+ }
+ }
+
+ assert_eq!(recv_count, 10000);
+}
+
+#[test]
+fn stress_recv_timeout_shared() {
+ const AMT: u32 = 1000;
+ const NTHREADS: u32 = 8;
+ let (tx, rx) = sync_channel::<i32>(0);
+ let (dtx, drx) = sync_channel::<()>(0);
+
+ thread::spawn(move || {
+ let mut recv_count = 0;
+ loop {
+ match rx.recv_timeout(Duration::from_millis(10)) {
+ Ok(v) => {
+ assert_eq!(v, 1);
+ recv_count += 1;
+ }
+ Err(RecvTimeoutError::Timeout) => continue,
+ Err(RecvTimeoutError::Disconnected) => break,
+ }
+ }
+
+ assert_eq!(recv_count, AMT * NTHREADS);
+ assert!(rx.try_recv().is_err());
+
+ dtx.send(()).unwrap();
+ });
+
+ for _ in 0..NTHREADS {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ for _ in 0..AMT {
+ tx.send(1).unwrap();
+ }
+ });
+ }
+
+ drop(tx);
+
+ drx.recv().unwrap();
+}
+
+#[test]
+fn stress_shared() {
+ const AMT: u32 = 1000;
+ const NTHREADS: u32 = 8;
+ let (tx, rx) = sync_channel::<i32>(0);
+ let (dtx, drx) = sync_channel::<()>(0);
+
+ thread::spawn(move || {
+ for _ in 0..AMT * NTHREADS {
+ assert_eq!(rx.recv().unwrap(), 1);
+ }
+ match rx.try_recv() {
+ Ok(..) => panic!(),
+ _ => {}
+ }
+ dtx.send(()).unwrap();
+ });
+
+ for _ in 0..NTHREADS {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ for _ in 0..AMT {
+ tx.send(1).unwrap();
+ }
+ });
+ }
+ drop(tx);
+ drx.recv().unwrap();
+}
+
+#[test]
+fn oneshot_single_thread_close_port_first() {
+ // Simple test of closing without sending
+ let (_tx, rx) = sync_channel::<i32>(0);
+ drop(rx);
+}
+
+#[test]
+fn oneshot_single_thread_close_chan_first() {
+ // Simple test of closing without sending
+ let (tx, _rx) = sync_channel::<i32>(0);
+ drop(tx);
+}
+
+#[test]
+fn oneshot_single_thread_send_port_close() {
+ // Testing that the sender cleans up the payload if receiver is closed
+ let (tx, rx) = sync_channel::<Box<i32>>(0);
+ drop(rx);
+ assert!(tx.send(box 0).is_err());
+}
+
+#[test]
+fn oneshot_single_thread_recv_chan_close() {
+ // Receiving on a closed chan will panic
+ let res = thread::spawn(move || {
+ let (tx, rx) = sync_channel::<i32>(0);
+ drop(tx);
+ rx.recv().unwrap();
+ })
+ .join();
+ // What is our res?
+ assert!(res.is_err());
+}
+
+#[test]
+fn oneshot_single_thread_send_then_recv() {
+ let (tx, rx) = sync_channel::<Box<i32>>(1);
+ tx.send(box 10).unwrap();
+ assert!(*rx.recv().unwrap() == 10);
+}
+
+#[test]
+fn oneshot_single_thread_try_send_open() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ assert_eq!(tx.try_send(10), Ok(()));
+ assert!(rx.recv().unwrap() == 10);
+}
+
+#[test]
+fn oneshot_single_thread_try_send_closed() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ drop(rx);
+ assert_eq!(tx.try_send(10), Err(TrySendError::Disconnected(10)));
+}
+
+#[test]
+fn oneshot_single_thread_try_send_closed2() {
+ let (tx, _rx) = sync_channel::<i32>(0);
+ assert_eq!(tx.try_send(10), Err(TrySendError::Full(10)));
+}
+
+#[test]
+fn oneshot_single_thread_try_recv_open() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ tx.send(10).unwrap();
+ assert!(rx.recv() == Ok(10));
+}
+
+#[test]
+fn oneshot_single_thread_try_recv_closed() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ drop(tx);
+ assert!(rx.recv().is_err());
+}
+
+#[test]
+fn oneshot_single_thread_try_recv_closed_with_data() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ tx.send(10).unwrap();
+ drop(tx);
+ assert_eq!(rx.try_recv(), Ok(10));
+ assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
+}
+
+#[test]
+fn oneshot_single_thread_peek_data() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
+ tx.send(10).unwrap();
+ assert_eq!(rx.try_recv(), Ok(10));
+}
+
+#[test]
+fn oneshot_single_thread_peek_close() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ drop(tx);
+ assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
+ assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
+}
+
+#[test]
+fn oneshot_single_thread_peek_open() {
+ let (_tx, rx) = sync_channel::<i32>(0);
+ assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
+}
+
+#[test]
+fn oneshot_multi_task_recv_then_send() {
+ let (tx, rx) = sync_channel::<Box<i32>>(0);
+ let _t = thread::spawn(move || {
+ assert!(*rx.recv().unwrap() == 10);
+ });
+
+ tx.send(box 10).unwrap();
+}
+
+#[test]
+fn oneshot_multi_task_recv_then_close() {
+ let (tx, rx) = sync_channel::<Box<i32>>(0);
+ let _t = thread::spawn(move || {
+ drop(tx);
+ });
+ let res = thread::spawn(move || {
+ assert!(*rx.recv().unwrap() == 10);
+ })
+ .join();
+ assert!(res.is_err());
+}
+
+#[test]
+fn oneshot_multi_thread_close_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let _t = thread::spawn(move || {
+ drop(rx);
+ });
+ drop(tx);
+ }
+}
+
+#[test]
+fn oneshot_multi_thread_send_close_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let _t = thread::spawn(move || {
+ drop(rx);
+ });
+ let _ = thread::spawn(move || {
+ tx.send(1).unwrap();
+ })
+ .join();
+ }
+}
+
+#[test]
+fn oneshot_multi_thread_recv_close_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let _t = thread::spawn(move || {
+ let res = thread::spawn(move || {
+ rx.recv().unwrap();
+ })
+ .join();
+ assert!(res.is_err());
+ });
+ let _t = thread::spawn(move || {
+ thread::spawn(move || {
+ drop(tx);
+ });
+ });
+ }
+}
+
+#[test]
+fn oneshot_multi_thread_send_recv_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = sync_channel::<Box<i32>>(0);
+ let _t = thread::spawn(move || {
+ tx.send(box 10).unwrap();
+ });
+ assert!(*rx.recv().unwrap() == 10);
+ }
+}
+
+#[test]
+fn stream_send_recv_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = sync_channel::<Box<i32>>(0);
+
+ send(tx, 0);
+ recv(rx, 0);
+
+ fn send(tx: SyncSender<Box<i32>>, i: i32) {
+ if i == 10 {
+ return;
+ }
+
+ thread::spawn(move || {
+ tx.send(box i).unwrap();
+ send(tx, i + 1);
+ });
+ }
+
+ fn recv(rx: Receiver<Box<i32>>, i: i32) {
+ if i == 10 {
+ return;
+ }
+
+ thread::spawn(move || {
+ assert!(*rx.recv().unwrap() == i);
+ recv(rx, i + 1);
+ });
+ }
+ }
+}
+
+#[test]
+fn recv_a_lot() {
+ // Regression test that we don't run out of stack in scheduler context
+ let (tx, rx) = sync_channel(10000);
+ for _ in 0..10000 {
+ tx.send(()).unwrap();
+ }
+ for _ in 0..10000 {
+ rx.recv().unwrap();
+ }
+}
+
+#[test]
+fn shared_chan_stress() {
+ let (tx, rx) = sync_channel(0);
+ let total = stress_factor() + 100;
+ for _ in 0..total {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ tx.send(()).unwrap();
+ });
+ }
+
+ for _ in 0..total {
+ rx.recv().unwrap();
+ }
+}
+
+#[test]
+fn test_nested_recv_iter() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let (total_tx, total_rx) = sync_channel::<i32>(0);
+
+ let _t = thread::spawn(move || {
+ let mut acc = 0;
+ for x in rx.iter() {
+ acc += x;
+ }
+ total_tx.send(acc).unwrap();
+ });
+
+ tx.send(3).unwrap();
+ tx.send(1).unwrap();
+ tx.send(2).unwrap();
+ drop(tx);
+ assert_eq!(total_rx.recv().unwrap(), 6);
+}
+
+#[test]
+fn test_recv_iter_break() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let (count_tx, count_rx) = sync_channel(0);
+
+ let _t = thread::spawn(move || {
+ let mut count = 0;
+ for x in rx.iter() {
+ if count >= 3 {
+ break;
+ } else {
+ count += x;
+ }
+ }
+ count_tx.send(count).unwrap();
+ });
+
+ tx.send(2).unwrap();
+ tx.send(2).unwrap();
+ tx.send(2).unwrap();
+ let _ = tx.try_send(2);
+ drop(tx);
+ assert_eq!(count_rx.recv().unwrap(), 4);
+}
+
+#[test]
+fn try_recv_states() {
+ let (tx1, rx1) = sync_channel::<i32>(1);
+ let (tx2, rx2) = sync_channel::<()>(1);
+ let (tx3, rx3) = sync_channel::<()>(1);
+ let _t = thread::spawn(move || {
+ rx2.recv().unwrap();
+ tx1.send(1).unwrap();
+ tx3.send(()).unwrap();
+ rx2.recv().unwrap();
+ drop(tx1);
+ tx3.send(()).unwrap();
+ });
+
+ assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
+ tx2.send(()).unwrap();
+ rx3.recv().unwrap();
+ assert_eq!(rx1.try_recv(), Ok(1));
+ assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
+ tx2.send(()).unwrap();
+ rx3.recv().unwrap();
+ assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected));
+}
+
+// This bug used to end up in a livelock inside of the Receiver destructor
+// because the internal state of the Shared packet was corrupted
+#[test]
+fn destroy_upgraded_shared_port_when_sender_still_active() {
+ let (tx, rx) = sync_channel::<()>(0);
+ let (tx2, rx2) = sync_channel::<()>(0);
+ let _t = thread::spawn(move || {
+ rx.recv().unwrap(); // wait on a oneshot
+ drop(rx); // destroy a shared
+ tx2.send(()).unwrap();
+ });
+ // make sure the other thread has gone to sleep
+ for _ in 0..5000 {
+ thread::yield_now();
+ }
+
+ // upgrade to a shared chan and send a message
+ let t = tx.clone();
+ drop(tx);
+ t.send(()).unwrap();
+
+ // wait for the child thread to exit before we exit
+ rx2.recv().unwrap();
+}
+
+#[test]
+fn send1() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let _t = thread::spawn(move || {
+ rx.recv().unwrap();
+ });
+ assert_eq!(tx.send(1), Ok(()));
+}
+
+#[test]
+fn send2() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let _t = thread::spawn(move || {
+ drop(rx);
+ });
+ assert!(tx.send(1).is_err());
+}
+
+#[test]
+fn send3() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ assert_eq!(tx.send(1), Ok(()));
+ let _t = thread::spawn(move || {
+ drop(rx);
+ });
+ assert!(tx.send(1).is_err());
+}
+
+#[test]
+fn send4() {
+ let (tx, rx) = sync_channel::<i32>(0);
+ let tx2 = tx.clone();
+ let (done, donerx) = channel();
+ let done2 = done.clone();
+ let _t = thread::spawn(move || {
+ assert!(tx.send(1).is_err());
+ done.send(()).unwrap();
+ });
+ let _t = thread::spawn(move || {
+ assert!(tx2.send(2).is_err());
+ done2.send(()).unwrap();
+ });
+ drop(rx);
+ donerx.recv().unwrap();
+ donerx.recv().unwrap();
+}
+
+#[test]
+fn try_send1() {
+ let (tx, _rx) = sync_channel::<i32>(0);
+ assert_eq!(tx.try_send(1), Err(TrySendError::Full(1)));
+}
+
+#[test]
+fn try_send2() {
+ let (tx, _rx) = sync_channel::<i32>(1);
+ assert_eq!(tx.try_send(1), Ok(()));
+ assert_eq!(tx.try_send(1), Err(TrySendError::Full(1)));
+}
+
+#[test]
+fn try_send3() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ assert_eq!(tx.try_send(1), Ok(()));
+ drop(rx);
+ assert_eq!(tx.try_send(1), Err(TrySendError::Disconnected(1)));
+}
+
+#[test]
+fn issue_15761() {
+ fn repro() {
+ let (tx1, rx1) = sync_channel::<()>(3);
+ let (tx2, rx2) = sync_channel::<()>(3);
+
+ let _t = thread::spawn(move || {
+ rx1.recv().unwrap();
+ tx2.try_send(()).unwrap();
+ });
+
+ tx1.try_send(()).unwrap();
+ rx2.recv().unwrap();
+ }
+
+ for _ in 0..100 {
+ repro()
+ }
+}
diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/src/sync/mpsc/tests.rs
new file mode 100644
index 0000000..184ce19
--- /dev/null
+++ b/library/std/src/sync/mpsc/tests.rs
@@ -0,0 +1,706 @@
+use super::*;
+use crate::env;
+use crate::thread;
+use crate::time::{Duration, Instant};
+
+pub fn stress_factor() -> usize {
+ match env::var("RUST_TEST_STRESS") {
+ Ok(val) => val.parse().unwrap(),
+ Err(..) => 1,
+ }
+}
+
+#[test]
+fn smoke() {
+ let (tx, rx) = channel::<i32>();
+ tx.send(1).unwrap();
+ assert_eq!(rx.recv().unwrap(), 1);
+}
+
+#[test]
+fn drop_full() {
+ let (tx, _rx) = channel::<Box<isize>>();
+ tx.send(box 1).unwrap();
+}
+
+#[test]
+fn drop_full_shared() {
+ let (tx, _rx) = channel::<Box<isize>>();
+ drop(tx.clone());
+ drop(tx.clone());
+ tx.send(box 1).unwrap();
+}
+
+#[test]
+fn smoke_shared() {
+ let (tx, rx) = channel::<i32>();
+ tx.send(1).unwrap();
+ assert_eq!(rx.recv().unwrap(), 1);
+ let tx = tx.clone();
+ tx.send(1).unwrap();
+ assert_eq!(rx.recv().unwrap(), 1);
+}
+
+#[test]
+fn smoke_threads() {
+ let (tx, rx) = channel::<i32>();
+ let _t = thread::spawn(move || {
+ tx.send(1).unwrap();
+ });
+ assert_eq!(rx.recv().unwrap(), 1);
+}
+
+#[test]
+fn smoke_port_gone() {
+ let (tx, rx) = channel::<i32>();
+ drop(rx);
+ assert!(tx.send(1).is_err());
+}
+
+#[test]
+fn smoke_shared_port_gone() {
+ let (tx, rx) = channel::<i32>();
+ drop(rx);
+ assert!(tx.send(1).is_err())
+}
+
+#[test]
+fn smoke_shared_port_gone2() {
+ let (tx, rx) = channel::<i32>();
+ drop(rx);
+ let tx2 = tx.clone();
+ drop(tx);
+ assert!(tx2.send(1).is_err());
+}
+
+#[test]
+fn port_gone_concurrent() {
+ let (tx, rx) = channel::<i32>();
+ let _t = thread::spawn(move || {
+ rx.recv().unwrap();
+ });
+ while tx.send(1).is_ok() {}
+}
+
+#[test]
+fn port_gone_concurrent_shared() {
+ let (tx, rx) = channel::<i32>();
+ let tx2 = tx.clone();
+ let _t = thread::spawn(move || {
+ rx.recv().unwrap();
+ });
+ while tx.send(1).is_ok() && tx2.send(1).is_ok() {}
+}
+
+#[test]
+fn smoke_chan_gone() {
+ let (tx, rx) = channel::<i32>();
+ drop(tx);
+ assert!(rx.recv().is_err());
+}
+
+#[test]
+fn smoke_chan_gone_shared() {
+ let (tx, rx) = channel::<()>();
+ let tx2 = tx.clone();
+ drop(tx);
+ drop(tx2);
+ assert!(rx.recv().is_err());
+}
+
+#[test]
+fn chan_gone_concurrent() {
+ let (tx, rx) = channel::<i32>();
+ let _t = thread::spawn(move || {
+ tx.send(1).unwrap();
+ tx.send(1).unwrap();
+ });
+ while rx.recv().is_ok() {}
+}
+
+#[test]
+fn stress() {
+ let (tx, rx) = channel::<i32>();
+ let t = thread::spawn(move || {
+ for _ in 0..10000 {
+ tx.send(1).unwrap();
+ }
+ });
+ for _ in 0..10000 {
+ assert_eq!(rx.recv().unwrap(), 1);
+ }
+ t.join().ok().expect("thread panicked");
+}
+
+#[test]
+fn stress_shared() {
+ const AMT: u32 = 10000;
+ const NTHREADS: u32 = 8;
+ let (tx, rx) = channel::<i32>();
+
+ let t = thread::spawn(move || {
+ for _ in 0..AMT * NTHREADS {
+ assert_eq!(rx.recv().unwrap(), 1);
+ }
+ match rx.try_recv() {
+ Ok(..) => panic!(),
+ _ => {}
+ }
+ });
+
+ for _ in 0..NTHREADS {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ for _ in 0..AMT {
+ tx.send(1).unwrap();
+ }
+ });
+ }
+ drop(tx);
+ t.join().ok().expect("thread panicked");
+}
+
+#[test]
+fn send_from_outside_runtime() {
+ let (tx1, rx1) = channel::<()>();
+ let (tx2, rx2) = channel::<i32>();
+ let t1 = thread::spawn(move || {
+ tx1.send(()).unwrap();
+ for _ in 0..40 {
+ assert_eq!(rx2.recv().unwrap(), 1);
+ }
+ });
+ rx1.recv().unwrap();
+ let t2 = thread::spawn(move || {
+ for _ in 0..40 {
+ tx2.send(1).unwrap();
+ }
+ });
+ t1.join().ok().expect("thread panicked");
+ t2.join().ok().expect("thread panicked");
+}
+
+#[test]
+fn recv_from_outside_runtime() {
+ let (tx, rx) = channel::<i32>();
+ let t = thread::spawn(move || {
+ for _ in 0..40 {
+ assert_eq!(rx.recv().unwrap(), 1);
+ }
+ });
+ for _ in 0..40 {
+ tx.send(1).unwrap();
+ }
+ t.join().ok().expect("thread panicked");
+}
+
+#[test]
+fn no_runtime() {
+ let (tx1, rx1) = channel::<i32>();
+ let (tx2, rx2) = channel::<i32>();
+ let t1 = thread::spawn(move || {
+ assert_eq!(rx1.recv().unwrap(), 1);
+ tx2.send(2).unwrap();
+ });
+ let t2 = thread::spawn(move || {
+ tx1.send(1).unwrap();
+ assert_eq!(rx2.recv().unwrap(), 2);
+ });
+ t1.join().ok().expect("thread panicked");
+ t2.join().ok().expect("thread panicked");
+}
+
+#[test]
+fn oneshot_single_thread_close_port_first() {
+ // Simple test of closing without sending
+ let (_tx, rx) = channel::<i32>();
+ drop(rx);
+}
+
+#[test]
+fn oneshot_single_thread_close_chan_first() {
+ // Simple test of closing without sending
+ let (tx, _rx) = channel::<i32>();
+ drop(tx);
+}
+
+#[test]
+fn oneshot_single_thread_send_port_close() {
+ // Testing that the sender cleans up the payload if receiver is closed
+ let (tx, rx) = channel::<Box<i32>>();
+ drop(rx);
+ assert!(tx.send(box 0).is_err());
+}
+
+#[test]
+fn oneshot_single_thread_recv_chan_close() {
+ // Receiving on a closed chan will panic
+ let res = thread::spawn(move || {
+ let (tx, rx) = channel::<i32>();
+ drop(tx);
+ rx.recv().unwrap();
+ })
+ .join();
+ // What is our res?
+ assert!(res.is_err());
+}
+
+#[test]
+fn oneshot_single_thread_send_then_recv() {
+ let (tx, rx) = channel::<Box<i32>>();
+ tx.send(box 10).unwrap();
+ assert!(*rx.recv().unwrap() == 10);
+}
+
+#[test]
+fn oneshot_single_thread_try_send_open() {
+ let (tx, rx) = channel::<i32>();
+ assert!(tx.send(10).is_ok());
+ assert!(rx.recv().unwrap() == 10);
+}
+
+#[test]
+fn oneshot_single_thread_try_send_closed() {
+ let (tx, rx) = channel::<i32>();
+ drop(rx);
+ assert!(tx.send(10).is_err());
+}
+
+#[test]
+fn oneshot_single_thread_try_recv_open() {
+ let (tx, rx) = channel::<i32>();
+ tx.send(10).unwrap();
+ assert!(rx.recv() == Ok(10));
+}
+
+#[test]
+fn oneshot_single_thread_try_recv_closed() {
+ let (tx, rx) = channel::<i32>();
+ drop(tx);
+ assert!(rx.recv().is_err());
+}
+
+#[test]
+fn oneshot_single_thread_peek_data() {
+ let (tx, rx) = channel::<i32>();
+ assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
+ tx.send(10).unwrap();
+ assert_eq!(rx.try_recv(), Ok(10));
+}
+
+#[test]
+fn oneshot_single_thread_peek_close() {
+ let (tx, rx) = channel::<i32>();
+ drop(tx);
+ assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
+ assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));
+}
+
+#[test]
+fn oneshot_single_thread_peek_open() {
+ let (_tx, rx) = channel::<i32>();
+ assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
+}
+
+#[test]
+fn oneshot_multi_task_recv_then_send() {
+ let (tx, rx) = channel::<Box<i32>>();
+ let _t = thread::spawn(move || {
+ assert!(*rx.recv().unwrap() == 10);
+ });
+
+ tx.send(box 10).unwrap();
+}
+
+#[test]
+fn oneshot_multi_task_recv_then_close() {
+ let (tx, rx) = channel::<Box<i32>>();
+ let _t = thread::spawn(move || {
+ drop(tx);
+ });
+ let res = thread::spawn(move || {
+ assert!(*rx.recv().unwrap() == 10);
+ })
+ .join();
+ assert!(res.is_err());
+}
+
+#[test]
+fn oneshot_multi_thread_close_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = channel::<i32>();
+ let _t = thread::spawn(move || {
+ drop(rx);
+ });
+ drop(tx);
+ }
+}
+
+#[test]
+fn oneshot_multi_thread_send_close_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = channel::<i32>();
+ let _t = thread::spawn(move || {
+ drop(rx);
+ });
+ let _ = thread::spawn(move || {
+ tx.send(1).unwrap();
+ })
+ .join();
+ }
+}
+
+#[test]
+fn oneshot_multi_thread_recv_close_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = channel::<i32>();
+ thread::spawn(move || {
+ let res = thread::spawn(move || {
+ rx.recv().unwrap();
+ })
+ .join();
+ assert!(res.is_err());
+ });
+ let _t = thread::spawn(move || {
+ thread::spawn(move || {
+ drop(tx);
+ });
+ });
+ }
+}
+
+#[test]
+fn oneshot_multi_thread_send_recv_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = channel::<Box<isize>>();
+ let _t = thread::spawn(move || {
+ tx.send(box 10).unwrap();
+ });
+ assert!(*rx.recv().unwrap() == 10);
+ }
+}
+
+#[test]
+fn stream_send_recv_stress() {
+ for _ in 0..stress_factor() {
+ let (tx, rx) = channel();
+
+ send(tx, 0);
+ recv(rx, 0);
+
+ fn send(tx: Sender<Box<i32>>, i: i32) {
+ if i == 10 {
+ return;
+ }
+
+ thread::spawn(move || {
+ tx.send(box i).unwrap();
+ send(tx, i + 1);
+ });
+ }
+
+ fn recv(rx: Receiver<Box<i32>>, i: i32) {
+ if i == 10 {
+ return;
+ }
+
+ thread::spawn(move || {
+ assert!(*rx.recv().unwrap() == i);
+ recv(rx, i + 1);
+ });
+ }
+ }
+}
+
+#[test]
+fn oneshot_single_thread_recv_timeout() {
+ let (tx, rx) = channel();
+ tx.send(()).unwrap();
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
+ tx.send(()).unwrap();
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
+}
+
+#[test]
+fn stress_recv_timeout_two_threads() {
+ let (tx, rx) = channel();
+ let stress = stress_factor() + 100;
+ let timeout = Duration::from_millis(100);
+
+ thread::spawn(move || {
+ for i in 0..stress {
+ if i % 2 == 0 {
+ thread::sleep(timeout * 2);
+ }
+ tx.send(1usize).unwrap();
+ }
+ });
+
+ let mut recv_count = 0;
+ loop {
+ match rx.recv_timeout(timeout) {
+ Ok(n) => {
+ assert_eq!(n, 1usize);
+ recv_count += 1;
+ }
+ Err(RecvTimeoutError::Timeout) => continue,
+ Err(RecvTimeoutError::Disconnected) => break,
+ }
+ }
+
+ assert_eq!(recv_count, stress);
+}
+
+#[test]
+fn recv_timeout_upgrade() {
+ let (tx, rx) = channel::<()>();
+ let timeout = Duration::from_millis(1);
+ let _tx_clone = tx.clone();
+
+ let start = Instant::now();
+ assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout));
+ assert!(Instant::now() >= start + timeout);
+}
+
+#[test]
+fn stress_recv_timeout_shared() {
+ let (tx, rx) = channel();
+ let stress = stress_factor() + 100;
+
+ for i in 0..stress {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ thread::sleep(Duration::from_millis(i as u64 * 10));
+ tx.send(1usize).unwrap();
+ });
+ }
+
+ drop(tx);
+
+ let mut recv_count = 0;
+ loop {
+ match rx.recv_timeout(Duration::from_millis(10)) {
+ Ok(n) => {
+ assert_eq!(n, 1usize);
+ recv_count += 1;
+ }
+ Err(RecvTimeoutError::Timeout) => continue,
+ Err(RecvTimeoutError::Disconnected) => break,
+ }
+ }
+
+ assert_eq!(recv_count, stress);
+}
+
+#[test]
+fn very_long_recv_timeout_wont_panic() {
+ let (tx, rx) = channel::<()>();
+ let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX)));
+ thread::sleep(Duration::from_secs(1));
+ assert!(tx.send(()).is_ok());
+ assert_eq!(join_handle.join().unwrap(), Ok(()));
+}
+
+#[test]
+fn recv_a_lot() {
+ // Regression test that we don't run out of stack in scheduler context
+ let (tx, rx) = channel();
+ for _ in 0..10000 {
+ tx.send(()).unwrap();
+ }
+ for _ in 0..10000 {
+ rx.recv().unwrap();
+ }
+}
+
+#[test]
+fn shared_recv_timeout() {
+ let (tx, rx) = channel();
+ let total = 5;
+ for _ in 0..total {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ tx.send(()).unwrap();
+ });
+ }
+
+ for _ in 0..total {
+ rx.recv().unwrap();
+ }
+
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
+ tx.send(()).unwrap();
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
+}
+
+#[test]
+fn shared_chan_stress() {
+ let (tx, rx) = channel();
+ let total = stress_factor() + 100;
+ for _ in 0..total {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ tx.send(()).unwrap();
+ });
+ }
+
+ for _ in 0..total {
+ rx.recv().unwrap();
+ }
+}
+
+#[test]
+fn test_nested_recv_iter() {
+ let (tx, rx) = channel::<i32>();
+ let (total_tx, total_rx) = channel::<i32>();
+
+ let _t = thread::spawn(move || {
+ let mut acc = 0;
+ for x in rx.iter() {
+ acc += x;
+ }
+ total_tx.send(acc).unwrap();
+ });
+
+ tx.send(3).unwrap();
+ tx.send(1).unwrap();
+ tx.send(2).unwrap();
+ drop(tx);
+ assert_eq!(total_rx.recv().unwrap(), 6);
+}
+
+#[test]
+fn test_recv_iter_break() {
+ let (tx, rx) = channel::<i32>();
+ let (count_tx, count_rx) = channel();
+
+ let _t = thread::spawn(move || {
+ let mut count = 0;
+ for x in rx.iter() {
+ if count >= 3 {
+ break;
+ } else {
+ count += x;
+ }
+ }
+ count_tx.send(count).unwrap();
+ });
+
+ tx.send(2).unwrap();
+ tx.send(2).unwrap();
+ tx.send(2).unwrap();
+ let _ = tx.send(2);
+ drop(tx);
+ assert_eq!(count_rx.recv().unwrap(), 4);
+}
+
+#[test]
+fn test_recv_try_iter() {
+ let (request_tx, request_rx) = channel();
+ let (response_tx, response_rx) = channel();
+
+ // Request `x`s until we have `6`.
+ let t = thread::spawn(move || {
+ let mut count = 0;
+ loop {
+ for x in response_rx.try_iter() {
+ count += x;
+ if count == 6 {
+ return count;
+ }
+ }
+ request_tx.send(()).unwrap();
+ }
+ });
+
+ for _ in request_rx.iter() {
+ if response_tx.send(2).is_err() {
+ break;
+ }
+ }
+
+ assert_eq!(t.join().unwrap(), 6);
+}
+
+#[test]
+fn test_recv_into_iter_owned() {
+ let mut iter = {
+ let (tx, rx) = channel::<i32>();
+ tx.send(1).unwrap();
+ tx.send(2).unwrap();
+
+ rx.into_iter()
+ };
+ assert_eq!(iter.next().unwrap(), 1);
+ assert_eq!(iter.next().unwrap(), 2);
+ assert_eq!(iter.next().is_none(), true);
+}
+
+#[test]
+fn test_recv_into_iter_borrowed() {
+ let (tx, rx) = channel::<i32>();
+ tx.send(1).unwrap();
+ tx.send(2).unwrap();
+ drop(tx);
+ let mut iter = (&rx).into_iter();
+ assert_eq!(iter.next().unwrap(), 1);
+ assert_eq!(iter.next().unwrap(), 2);
+ assert_eq!(iter.next().is_none(), true);
+}
+
+#[test]
+fn try_recv_states() {
+ let (tx1, rx1) = channel::<i32>();
+ let (tx2, rx2) = channel::<()>();
+ let (tx3, rx3) = channel::<()>();
+ let _t = thread::spawn(move || {
+ rx2.recv().unwrap();
+ tx1.send(1).unwrap();
+ tx3.send(()).unwrap();
+ rx2.recv().unwrap();
+ drop(tx1);
+ tx3.send(()).unwrap();
+ });
+
+ assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
+ tx2.send(()).unwrap();
+ rx3.recv().unwrap();
+ assert_eq!(rx1.try_recv(), Ok(1));
+ assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty));
+ tx2.send(()).unwrap();
+ rx3.recv().unwrap();
+ assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected));
+}
+
+// This bug used to end up in a livelock inside of the Receiver destructor
+// because the internal state of the Shared packet was corrupted
+#[test]
+fn destroy_upgraded_shared_port_when_sender_still_active() {
+ let (tx, rx) = channel();
+ let (tx2, rx2) = channel();
+ let _t = thread::spawn(move || {
+ rx.recv().unwrap(); // wait on a oneshot
+ drop(rx); // destroy a shared
+ tx2.send(()).unwrap();
+ });
+ // make sure the other thread has gone to sleep
+ for _ in 0..5000 {
+ thread::yield_now();
+ }
+
+ // upgrade to a shared chan and send a message
+ let t = tx.clone();
+ drop(tx);
+ t.send(()).unwrap();
+
+ // wait for the child thread to exit before we exit
+ rx2.recv().unwrap();
+}
+
+#[test]
+fn issue_32114() {
+ let (tx, _) = channel();
+ let _ = tx.send(123);
+ assert_eq!(tx.send(123), Err(SendError(123)));
+}
diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs
index d7a4f00..e8f5a6f 100644
--- a/library/std/src/sync/mutex.rs
+++ b/library/std/src/sync/mutex.rs
@@ -1,3 +1,6 @@
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
use crate::cell::UnsafeCell;
use crate::fmt;
use crate::mem;
@@ -85,7 +88,7 @@
/// use std::thread;
///
/// let lock = Arc::new(Mutex::new(0_u32));
-/// let lock2 = lock.clone();
+/// let lock2 = Arc::clone(&lock);
///
/// let _ = thread::spawn(move || -> () {
/// // This thread will acquire the mutex first, unwrapping the result of
@@ -163,12 +166,7 @@
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "mutex_type")]
pub struct Mutex<T: ?Sized> {
- // Note that this mutex is in a *box*, not inlined into the struct itself.
- // Once a native mutex has been used once, its address can never change (it
- // can't be moved). This mutex type can be safely moved at any time, so to
- // ensure that the native mutex is used correctly we box the inner mutex to
- // give it a constant address.
- inner: Box<sys::Mutex>,
+ inner: sys::MovableMutex,
poison: poison::Flag,
data: UnsafeCell<T>,
}
@@ -215,15 +213,11 @@
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new(t: T) -> Mutex<T> {
- let mut m = Mutex {
- inner: box sys::Mutex::new(),
+ Mutex {
+ inner: sys::MovableMutex::new(),
poison: poison::Flag::new(),
data: UnsafeCell::new(t),
- };
- unsafe {
- m.inner.init();
}
- m
}
}
@@ -256,7 +250,7 @@
/// use std::thread;
///
/// let mutex = Arc::new(Mutex::new(0));
- /// let c_mutex = mutex.clone();
+ /// let c_mutex = Arc::clone(&mutex);
///
/// thread::spawn(move || {
/// *c_mutex.lock().unwrap() = 10;
@@ -292,7 +286,7 @@
/// use std::thread;
///
/// let mutex = Arc::new(Mutex::new(0));
- /// let c_mutex = mutex.clone();
+ /// let c_mutex = Arc::clone(&mutex);
///
/// thread::spawn(move || {
/// let mut lock = c_mutex.try_lock();
@@ -328,7 +322,7 @@
/// use std::thread;
///
/// let mutex = Arc::new(Mutex::new(0));
- /// let c_mutex = mutex.clone();
+ /// let c_mutex = Arc::clone(&mutex);
///
/// let _ = thread::spawn(move || {
/// let _lock = c_mutex.lock().unwrap();
@@ -375,7 +369,6 @@
(ptr::read(inner), ptr::read(poison), ptr::read(data))
};
mem::forget(self);
- inner.destroy(); // Keep in sync with the `Drop` impl.
drop(inner);
poison::map_result(poison.borrow(), |_| data.into_inner())
@@ -403,25 +396,11 @@
/// ```
#[stable(feature = "mutex_get_mut", since = "1.6.0")]
pub fn get_mut(&mut self) -> LockResult<&mut T> {
- // We know statically that there are no other references to `self`, so
- // there's no need to lock the inner mutex.
- let data = unsafe { &mut *self.data.get() };
+ let data = self.data.get_mut();
poison::map_result(self.poison.borrow(), |_| data)
}
}
-#[stable(feature = "rust1", since = "1.0.0")]
-unsafe impl<#[may_dangle] T: ?Sized> Drop for Mutex<T> {
- fn drop(&mut self) {
- // This is actually safe b/c we know that there is no further usage of
- // this mutex (it's up to the user to arrange for a mutex to get
- // dropped, that's not our job)
- //
- // IMPORTANT: This code must be kept in sync with `Mutex::into_inner`.
- unsafe { self.inner.destroy() }
- }
-}
-
#[stable(feature = "mutex_from", since = "1.24.0")]
impl<T> From<T> for Mutex<T> {
/// Creates a new mutex in an unlocked state ready for use.
@@ -508,252 +487,10 @@
}
}
-pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
+pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::MovableMutex {
&guard.lock.inner
}
pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag {
&guard.lock.poison
}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use crate::sync::atomic::{AtomicUsize, Ordering};
- use crate::sync::mpsc::channel;
- use crate::sync::{Arc, Condvar, Mutex};
- use crate::thread;
-
- struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
-
- #[derive(Eq, PartialEq, Debug)]
- struct NonCopy(i32);
-
- #[test]
- fn smoke() {
- let m = Mutex::new(());
- drop(m.lock().unwrap());
- drop(m.lock().unwrap());
- }
-
- #[test]
- fn lots_and_lots() {
- const J: u32 = 1000;
- const K: u32 = 3;
-
- let m = Arc::new(Mutex::new(0));
-
- fn inc(m: &Mutex<u32>) {
- for _ in 0..J {
- *m.lock().unwrap() += 1;
- }
- }
-
- let (tx, rx) = channel();
- for _ in 0..K {
- let tx2 = tx.clone();
- let m2 = m.clone();
- thread::spawn(move || {
- inc(&m2);
- tx2.send(()).unwrap();
- });
- let tx2 = tx.clone();
- let m2 = m.clone();
- thread::spawn(move || {
- inc(&m2);
- tx2.send(()).unwrap();
- });
- }
-
- drop(tx);
- for _ in 0..2 * K {
- rx.recv().unwrap();
- }
- assert_eq!(*m.lock().unwrap(), J * K * 2);
- }
-
- #[test]
- fn try_lock() {
- let m = Mutex::new(());
- *m.try_lock().unwrap() = ();
- }
-
- #[test]
- fn test_into_inner() {
- let m = Mutex::new(NonCopy(10));
- assert_eq!(m.into_inner().unwrap(), NonCopy(10));
- }
-
- #[test]
- fn test_into_inner_drop() {
- struct Foo(Arc<AtomicUsize>);
- impl Drop for Foo {
- fn drop(&mut self) {
- self.0.fetch_add(1, Ordering::SeqCst);
- }
- }
- let num_drops = Arc::new(AtomicUsize::new(0));
- let m = Mutex::new(Foo(num_drops.clone()));
- assert_eq!(num_drops.load(Ordering::SeqCst), 0);
- {
- let _inner = m.into_inner().unwrap();
- assert_eq!(num_drops.load(Ordering::SeqCst), 0);
- }
- assert_eq!(num_drops.load(Ordering::SeqCst), 1);
- }
-
- #[test]
- fn test_into_inner_poison() {
- let m = Arc::new(Mutex::new(NonCopy(10)));
- let m2 = m.clone();
- let _ = thread::spawn(move || {
- let _lock = m2.lock().unwrap();
- panic!("test panic in inner thread to poison mutex");
- })
- .join();
-
- assert!(m.is_poisoned());
- match Arc::try_unwrap(m).unwrap().into_inner() {
- Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
- Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {:?}", x),
- }
- }
-
- #[test]
- fn test_get_mut() {
- let mut m = Mutex::new(NonCopy(10));
- *m.get_mut().unwrap() = NonCopy(20);
- assert_eq!(m.into_inner().unwrap(), NonCopy(20));
- }
-
- #[test]
- fn test_get_mut_poison() {
- let m = Arc::new(Mutex::new(NonCopy(10)));
- let m2 = m.clone();
- let _ = thread::spawn(move || {
- let _lock = m2.lock().unwrap();
- panic!("test panic in inner thread to poison mutex");
- })
- .join();
-
- assert!(m.is_poisoned());
- match Arc::try_unwrap(m).unwrap().get_mut() {
- Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
- Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {:?}", x),
- }
- }
-
- #[test]
- fn test_mutex_arc_condvar() {
- let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
- let packet2 = Packet(packet.0.clone());
- let (tx, rx) = channel();
- let _t = thread::spawn(move || {
- // wait until parent gets in
- rx.recv().unwrap();
- let &(ref lock, ref cvar) = &*packet2.0;
- let mut lock = lock.lock().unwrap();
- *lock = true;
- cvar.notify_one();
- });
-
- let &(ref lock, ref cvar) = &*packet.0;
- let mut lock = lock.lock().unwrap();
- tx.send(()).unwrap();
- assert!(!*lock);
- while !*lock {
- lock = cvar.wait(lock).unwrap();
- }
- }
-
- #[test]
- fn test_arc_condvar_poison() {
- let packet = Packet(Arc::new((Mutex::new(1), Condvar::new())));
- let packet2 = Packet(packet.0.clone());
- let (tx, rx) = channel();
-
- let _t = thread::spawn(move || -> () {
- rx.recv().unwrap();
- let &(ref lock, ref cvar) = &*packet2.0;
- let _g = lock.lock().unwrap();
- cvar.notify_one();
- // Parent should fail when it wakes up.
- panic!();
- });
-
- let &(ref lock, ref cvar) = &*packet.0;
- let mut lock = lock.lock().unwrap();
- tx.send(()).unwrap();
- while *lock == 1 {
- match cvar.wait(lock) {
- Ok(l) => {
- lock = l;
- assert_eq!(*lock, 1);
- }
- Err(..) => break,
- }
- }
- }
-
- #[test]
- fn test_mutex_arc_poison() {
- let arc = Arc::new(Mutex::new(1));
- assert!(!arc.is_poisoned());
- let arc2 = arc.clone();
- let _ = thread::spawn(move || {
- let lock = arc2.lock().unwrap();
- assert_eq!(*lock, 2);
- })
- .join();
- assert!(arc.lock().is_err());
- assert!(arc.is_poisoned());
- }
-
- #[test]
- fn test_mutex_arc_nested() {
- // Tests nested mutexes and access
- // to underlying data.
- let arc = Arc::new(Mutex::new(1));
- let arc2 = Arc::new(Mutex::new(arc));
- let (tx, rx) = channel();
- let _t = thread::spawn(move || {
- let lock = arc2.lock().unwrap();
- let lock2 = lock.lock().unwrap();
- assert_eq!(*lock2, 1);
- tx.send(()).unwrap();
- });
- rx.recv().unwrap();
- }
-
- #[test]
- fn test_mutex_arc_access_in_unwind() {
- let arc = Arc::new(Mutex::new(1));
- let arc2 = arc.clone();
- let _ = thread::spawn(move || -> () {
- struct Unwinder {
- i: Arc<Mutex<i32>>,
- }
- impl Drop for Unwinder {
- fn drop(&mut self) {
- *self.i.lock().unwrap() += 1;
- }
- }
- let _u = Unwinder { i: arc2 };
- panic!();
- })
- .join();
- let lock = arc.lock().unwrap();
- assert_eq!(*lock, 2);
- }
-
- #[test]
- fn test_mutex_unsized() {
- let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]);
- {
- let b = &mut *mutex.lock().unwrap();
- b[0] = 4;
- b[2] = 5;
- }
- let comp: &[i32] = &[4, 2, 5];
- assert_eq!(&*mutex.lock().unwrap(), comp);
- }
-}
diff --git a/library/std/src/sync/mutex/tests.rs b/library/std/src/sync/mutex/tests.rs
new file mode 100644
index 0000000..a1b5aed
--- /dev/null
+++ b/library/std/src/sync/mutex/tests.rs
@@ -0,0 +1,238 @@
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sync::mpsc::channel;
+use crate::sync::{Arc, Condvar, Mutex};
+use crate::thread;
+
+struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
+
+#[derive(Eq, PartialEq, Debug)]
+struct NonCopy(i32);
+
+#[test]
+fn smoke() {
+ let m = Mutex::new(());
+ drop(m.lock().unwrap());
+ drop(m.lock().unwrap());
+}
+
+#[test]
+fn lots_and_lots() {
+ const J: u32 = 1000;
+ const K: u32 = 3;
+
+ let m = Arc::new(Mutex::new(0));
+
+ fn inc(m: &Mutex<u32>) {
+ for _ in 0..J {
+ *m.lock().unwrap() += 1;
+ }
+ }
+
+ let (tx, rx) = channel();
+ for _ in 0..K {
+ let tx2 = tx.clone();
+ let m2 = m.clone();
+ thread::spawn(move || {
+ inc(&m2);
+ tx2.send(()).unwrap();
+ });
+ let tx2 = tx.clone();
+ let m2 = m.clone();
+ thread::spawn(move || {
+ inc(&m2);
+ tx2.send(()).unwrap();
+ });
+ }
+
+ drop(tx);
+ for _ in 0..2 * K {
+ rx.recv().unwrap();
+ }
+ assert_eq!(*m.lock().unwrap(), J * K * 2);
+}
+
+#[test]
+fn try_lock() {
+ let m = Mutex::new(());
+ *m.try_lock().unwrap() = ();
+}
+
+#[test]
+fn test_into_inner() {
+ let m = Mutex::new(NonCopy(10));
+ assert_eq!(m.into_inner().unwrap(), NonCopy(10));
+}
+
+#[test]
+fn test_into_inner_drop() {
+ struct Foo(Arc<AtomicUsize>);
+ impl Drop for Foo {
+ fn drop(&mut self) {
+ self.0.fetch_add(1, Ordering::SeqCst);
+ }
+ }
+ let num_drops = Arc::new(AtomicUsize::new(0));
+ let m = Mutex::new(Foo(num_drops.clone()));
+ assert_eq!(num_drops.load(Ordering::SeqCst), 0);
+ {
+ let _inner = m.into_inner().unwrap();
+ assert_eq!(num_drops.load(Ordering::SeqCst), 0);
+ }
+ assert_eq!(num_drops.load(Ordering::SeqCst), 1);
+}
+
+#[test]
+fn test_into_inner_poison() {
+ let m = Arc::new(Mutex::new(NonCopy(10)));
+ let m2 = m.clone();
+ let _ = thread::spawn(move || {
+ let _lock = m2.lock().unwrap();
+ panic!("test panic in inner thread to poison mutex");
+ })
+ .join();
+
+ assert!(m.is_poisoned());
+ match Arc::try_unwrap(m).unwrap().into_inner() {
+ Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
+ Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {:?}", x),
+ }
+}
+
+#[test]
+fn test_get_mut() {
+ let mut m = Mutex::new(NonCopy(10));
+ *m.get_mut().unwrap() = NonCopy(20);
+ assert_eq!(m.into_inner().unwrap(), NonCopy(20));
+}
+
+#[test]
+fn test_get_mut_poison() {
+ let m = Arc::new(Mutex::new(NonCopy(10)));
+ let m2 = m.clone();
+ let _ = thread::spawn(move || {
+ let _lock = m2.lock().unwrap();
+ panic!("test panic in inner thread to poison mutex");
+ })
+ .join();
+
+ assert!(m.is_poisoned());
+ match Arc::try_unwrap(m).unwrap().get_mut() {
+ Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
+ Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {:?}", x),
+ }
+}
+
+#[test]
+fn test_mutex_arc_condvar() {
+ let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
+ let packet2 = Packet(packet.0.clone());
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move || {
+ // wait until parent gets in
+ rx.recv().unwrap();
+ let &(ref lock, ref cvar) = &*packet2.0;
+ let mut lock = lock.lock().unwrap();
+ *lock = true;
+ cvar.notify_one();
+ });
+
+ let &(ref lock, ref cvar) = &*packet.0;
+ let mut lock = lock.lock().unwrap();
+ tx.send(()).unwrap();
+ assert!(!*lock);
+ while !*lock {
+ lock = cvar.wait(lock).unwrap();
+ }
+}
+
+#[test]
+fn test_arc_condvar_poison() {
+ let packet = Packet(Arc::new((Mutex::new(1), Condvar::new())));
+ let packet2 = Packet(packet.0.clone());
+ let (tx, rx) = channel();
+
+ let _t = thread::spawn(move || -> () {
+ rx.recv().unwrap();
+ let &(ref lock, ref cvar) = &*packet2.0;
+ let _g = lock.lock().unwrap();
+ cvar.notify_one();
+ // Parent should fail when it wakes up.
+ panic!();
+ });
+
+ let &(ref lock, ref cvar) = &*packet.0;
+ let mut lock = lock.lock().unwrap();
+ tx.send(()).unwrap();
+ while *lock == 1 {
+ match cvar.wait(lock) {
+ Ok(l) => {
+ lock = l;
+ assert_eq!(*lock, 1);
+ }
+ Err(..) => break,
+ }
+ }
+}
+
+#[test]
+fn test_mutex_arc_poison() {
+ let arc = Arc::new(Mutex::new(1));
+ assert!(!arc.is_poisoned());
+ let arc2 = arc.clone();
+ let _ = thread::spawn(move || {
+ let lock = arc2.lock().unwrap();
+ assert_eq!(*lock, 2);
+ })
+ .join();
+ assert!(arc.lock().is_err());
+ assert!(arc.is_poisoned());
+}
+
+#[test]
+fn test_mutex_arc_nested() {
+ // Tests nested mutexes and access
+ // to underlying data.
+ let arc = Arc::new(Mutex::new(1));
+ let arc2 = Arc::new(Mutex::new(arc));
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move || {
+ let lock = arc2.lock().unwrap();
+ let lock2 = lock.lock().unwrap();
+ assert_eq!(*lock2, 1);
+ tx.send(()).unwrap();
+ });
+ rx.recv().unwrap();
+}
+
+#[test]
+fn test_mutex_arc_access_in_unwind() {
+ let arc = Arc::new(Mutex::new(1));
+ let arc2 = arc.clone();
+ let _ = thread::spawn(move || -> () {
+ struct Unwinder {
+ i: Arc<Mutex<i32>>,
+ }
+ impl Drop for Unwinder {
+ fn drop(&mut self) {
+ *self.i.lock().unwrap() += 1;
+ }
+ }
+ let _u = Unwinder { i: arc2 };
+ panic!();
+ })
+ .join();
+ let lock = arc.lock().unwrap();
+ assert_eq!(*lock, 2);
+}
+
+#[test]
+fn test_mutex_unsized() {
+ let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]);
+ {
+ let b = &mut *mutex.lock().unwrap();
+ b[0] = 4;
+ b[2] = 5;
+ }
+ let comp: &[i32] = &[4, 2, 5];
+ assert_eq!(&*mutex.lock().unwrap(), comp);
+}
diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs
index 714ec3e..de5ddf1 100644
--- a/library/std/src/sync/once.rs
+++ b/library/std/src/sync/once.rs
@@ -84,6 +84,9 @@
// processor. Because both use Acquire ordering such a reordering is not
// allowed, so no need for SeqCst.
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
use crate::cell::Cell;
use crate::fmt;
use crate::marker;
@@ -92,10 +95,7 @@
/// A synchronization primitive which can be used to run a one-time global
/// initialization. Useful for one-time initialization for FFI or related
-/// functionality. This type can only be constructed with the [`Once::new`]
-/// constructor.
-///
-/// [`Once::new`]: struct.Once.html#method.new
+/// functionality. This type can only be constructed with [`Once::new()`].
///
/// # Examples
///
@@ -123,11 +123,8 @@
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl Send for Once {}
-/// State yielded to [`call_once_force`]’s closure parameter. The state can be
-/// used to query the poison status of the [`Once`].
-///
-/// [`call_once_force`]: struct.Once.html#method.call_once_force
-/// [`Once`]: struct.Once.html
+/// State yielded to [`Once::call_once_force()`]’s closure parameter. The state
+/// can be used to query the poison status of the [`Once`].
#[unstable(feature = "once_poison", issue = "33577")]
#[derive(Debug)]
pub struct OnceState {
@@ -137,8 +134,6 @@
/// Initialization value for static [`Once`] values.
///
-/// [`Once`]: struct.Once.html
-///
/// # Examples
///
/// ```
@@ -188,6 +183,7 @@
impl Once {
/// Creates a new `Once` value.
+ #[inline]
#[stable(feature = "once_new", since = "1.2.0")]
#[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
pub const fn new() -> Once {
@@ -208,7 +204,7 @@
/// happens-before relation between the closure and code executing after the
/// return).
///
- /// If the given closure recursively invokes `call_once` on the same `Once`
+ /// If the given closure recursively invokes `call_once` on the same [`Once`]
/// instance the exact behavior is not specified, allowed outcomes are
/// a panic or a deadlock.
///
@@ -245,7 +241,7 @@
///
/// The closure `f` will only be executed once if this is called
/// concurrently amongst many threads. If that closure panics, however, then
- /// it will *poison* this `Once` instance, causing all future invocations of
+ /// it will *poison* this [`Once`] instance, causing all future invocations of
/// `call_once` to also panic.
///
/// This is similar to [poisoning with mutexes][poison].
@@ -265,21 +261,21 @@
self.call_inner(false, &mut |_| f.take().unwrap()());
}
- /// Performs the same function as [`call_once`] except ignores poisoning.
+ /// Performs the same function as [`call_once()`] except ignores poisoning.
///
- /// Unlike [`call_once`], if this `Once` has been poisoned (i.e., a previous
- /// call to `call_once` or `call_once_force` caused a panic), calling
- /// `call_once_force` will still invoke the closure `f` and will _not_
- /// result in an immediate panic. If `f` panics, the `Once` will remain
- /// in a poison state. If `f` does _not_ panic, the `Once` will no
- /// longer be in a poison state and all future calls to `call_once` or
- /// `call_once_force` will be no-ops.
+ /// Unlike [`call_once()`], if this [`Once`] has been poisoned (i.e., a previous
+ /// call to [`call_once()`] or [`call_once_force()`] caused a panic), calling
+ /// [`call_once_force()`] will still invoke the closure `f` and will _not_
+ /// result in an immediate panic. If `f` panics, the [`Once`] will remain
+ /// in a poison state. If `f` does _not_ panic, the [`Once`] will no
+ /// longer be in a poison state and all future calls to [`call_once()`] or
+ /// [`call_once_force()`] will be no-ops.
///
/// The closure `f` is yielded a [`OnceState`] structure which can be used
- /// to query the poison status of the `Once`.
+ /// to query the poison status of the [`Once`].
///
- /// [`call_once`]: struct.Once.html#method.call_once
- /// [`OnceState`]: struct.OnceState.html
+ /// [`call_once()`]: Once::call_once
+ /// [`call_once_force()`]: Once::call_once_force
///
/// # Examples
///
@@ -325,18 +321,20 @@
self.call_inner(true, &mut |p| f.take().unwrap()(p));
}
- /// Returns `true` if some `call_once` call has completed
+ /// Returns `true` if some [`call_once()`] call has completed
/// successfully. Specifically, `is_completed` will return false in
/// the following situations:
- /// * `call_once` was not called at all,
- /// * `call_once` was called, but has not yet completed,
- /// * the `Once` instance is poisoned
+ /// * [`call_once()`] was not called at all,
+ /// * [`call_once()`] was called, but has not yet completed,
+ /// * the [`Once`] instance is poisoned
///
- /// This function returning `false` does not mean that `Once` has not been
+ /// This function returning `false` does not mean that [`Once`] has not been
/// executed. For example, it may have been executed in the time between
/// when `is_completed` starts executing and when it returns, in which case
/// the `false` return value would be stale (but still permissible).
///
+ /// [`call_once()`]: Once::call_once
+ ///
/// # Examples
///
/// ```
@@ -515,14 +513,11 @@
impl OnceState {
/// Returns `true` if the associated [`Once`] was poisoned prior to the
- /// invocation of the closure passed to [`call_once_force`].
- ///
- /// [`call_once_force`]: struct.Once.html#method.call_once_force
- /// [`Once`]: struct.Once.html
+ /// invocation of the closure passed to [`Once::call_once_force()`].
///
/// # Examples
///
- /// A poisoned `Once`:
+ /// A poisoned [`Once`]:
///
/// ```
/// #![feature(once_poison)]
@@ -543,7 +538,7 @@
/// });
/// ```
///
- /// An unpoisoned `Once`:
+ /// An unpoisoned [`Once`]:
///
/// ```
/// #![feature(once_poison)]
@@ -561,130 +556,8 @@
}
/// Poison the associated [`Once`] without explicitly panicking.
- ///
- /// [`Once`]: struct.Once.html
// NOTE: This is currently only exposed for the `lazy` module
pub(crate) fn poison(&self) {
self.set_state_on_drop_to.set(POISONED);
}
}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use super::Once;
- use crate::panic;
- use crate::sync::mpsc::channel;
- use crate::thread;
-
- #[test]
- fn smoke_once() {
- static O: Once = Once::new();
- let mut a = 0;
- O.call_once(|| a += 1);
- assert_eq!(a, 1);
- O.call_once(|| a += 1);
- assert_eq!(a, 1);
- }
-
- #[test]
- fn stampede_once() {
- static O: Once = Once::new();
- static mut RUN: bool = false;
-
- let (tx, rx) = channel();
- for _ in 0..10 {
- let tx = tx.clone();
- thread::spawn(move || {
- for _ in 0..4 {
- thread::yield_now()
- }
- unsafe {
- O.call_once(|| {
- assert!(!RUN);
- RUN = true;
- });
- assert!(RUN);
- }
- tx.send(()).unwrap();
- });
- }
-
- unsafe {
- O.call_once(|| {
- assert!(!RUN);
- RUN = true;
- });
- assert!(RUN);
- }
-
- for _ in 0..10 {
- rx.recv().unwrap();
- }
- }
-
- #[test]
- fn poison_bad() {
- static O: Once = Once::new();
-
- // poison the once
- let t = panic::catch_unwind(|| {
- O.call_once(|| panic!());
- });
- assert!(t.is_err());
-
- // poisoning propagates
- let t = panic::catch_unwind(|| {
- O.call_once(|| {});
- });
- assert!(t.is_err());
-
- // we can subvert poisoning, however
- let mut called = false;
- O.call_once_force(|p| {
- called = true;
- assert!(p.poisoned())
- });
- assert!(called);
-
- // once any success happens, we stop propagating the poison
- O.call_once(|| {});
- }
-
- #[test]
- fn wait_for_force_to_finish() {
- static O: Once = Once::new();
-
- // poison the once
- let t = panic::catch_unwind(|| {
- O.call_once(|| panic!());
- });
- assert!(t.is_err());
-
- // make sure someone's waiting inside the once via a force
- let (tx1, rx1) = channel();
- let (tx2, rx2) = channel();
- let t1 = thread::spawn(move || {
- O.call_once_force(|p| {
- assert!(p.poisoned());
- tx1.send(()).unwrap();
- rx2.recv().unwrap();
- });
- });
-
- rx1.recv().unwrap();
-
- // put another waiter on the once
- let t2 = thread::spawn(|| {
- let mut called = false;
- O.call_once(|| {
- called = true;
- });
- assert!(!called);
- });
-
- tx2.send(()).unwrap();
-
- assert!(t1.join().is_ok());
- assert!(t2.join().is_ok());
- }
-}
diff --git a/library/std/src/sync/once/tests.rs b/library/std/src/sync/once/tests.rs
new file mode 100644
index 0000000..fae2752
--- /dev/null
+++ b/library/std/src/sync/once/tests.rs
@@ -0,0 +1,116 @@
+use super::Once;
+use crate::panic;
+use crate::sync::mpsc::channel;
+use crate::thread;
+
+#[test]
+fn smoke_once() {
+ static O: Once = Once::new();
+ let mut a = 0;
+ O.call_once(|| a += 1);
+ assert_eq!(a, 1);
+ O.call_once(|| a += 1);
+ assert_eq!(a, 1);
+}
+
+#[test]
+fn stampede_once() {
+ static O: Once = Once::new();
+ static mut RUN: bool = false;
+
+ let (tx, rx) = channel();
+ for _ in 0..10 {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ for _ in 0..4 {
+ thread::yield_now()
+ }
+ unsafe {
+ O.call_once(|| {
+ assert!(!RUN);
+ RUN = true;
+ });
+ assert!(RUN);
+ }
+ tx.send(()).unwrap();
+ });
+ }
+
+ unsafe {
+ O.call_once(|| {
+ assert!(!RUN);
+ RUN = true;
+ });
+ assert!(RUN);
+ }
+
+ for _ in 0..10 {
+ rx.recv().unwrap();
+ }
+}
+
+#[test]
+fn poison_bad() {
+ static O: Once = Once::new();
+
+ // poison the once
+ let t = panic::catch_unwind(|| {
+ O.call_once(|| panic!());
+ });
+ assert!(t.is_err());
+
+ // poisoning propagates
+ let t = panic::catch_unwind(|| {
+ O.call_once(|| {});
+ });
+ assert!(t.is_err());
+
+ // we can subvert poisoning, however
+ let mut called = false;
+ O.call_once_force(|p| {
+ called = true;
+ assert!(p.poisoned())
+ });
+ assert!(called);
+
+ // once any success happens, we stop propagating the poison
+ O.call_once(|| {});
+}
+
+#[test]
+fn wait_for_force_to_finish() {
+ static O: Once = Once::new();
+
+ // poison the once
+ let t = panic::catch_unwind(|| {
+ O.call_once(|| panic!());
+ });
+ assert!(t.is_err());
+
+ // make sure someone's waiting inside the once via a force
+ let (tx1, rx1) = channel();
+ let (tx2, rx2) = channel();
+ let t1 = thread::spawn(move || {
+ O.call_once_force(|p| {
+ assert!(p.poisoned());
+ tx1.send(()).unwrap();
+ rx2.recv().unwrap();
+ });
+ });
+
+ rx1.recv().unwrap();
+
+ // put another waiter on the once
+ let t2 = thread::spawn(|| {
+ let mut called = false;
+ O.call_once(|| {
+ called = true;
+ });
+ assert!(!called);
+ });
+
+ tx2.send(()).unwrap();
+
+ assert!(t1.join().is_ok());
+ assert!(t2.join().is_ok());
+}
diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs
index 586093c..d967779 100644
--- a/library/std/src/sync/rwlock.rs
+++ b/library/std/src/sync/rwlock.rs
@@ -1,3 +1,6 @@
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
use crate::cell::UnsafeCell;
use crate::fmt;
use crate::mem;
@@ -162,7 +165,7 @@
/// use std::thread;
///
/// let lock = Arc::new(RwLock::new(1));
- /// let c_lock = lock.clone();
+ /// let c_lock = Arc::clone(&lock);
///
/// let n = lock.read().unwrap();
/// assert_eq!(*n, 1);
@@ -318,7 +321,7 @@
/// use std::thread;
///
/// let lock = Arc::new(RwLock::new(0));
- /// let c_lock = lock.clone();
+ /// let c_lock = Arc::clone(&lock);
///
/// let _ = thread::spawn(move || {
/// let _lock = c_lock.write().unwrap();
@@ -401,9 +404,7 @@
/// ```
#[stable(feature = "rwlock_get_mut", since = "1.6.0")]
pub fn get_mut(&mut self) -> LockResult<&mut T> {
- // We know statically that there are no other references to `self`, so
- // there's no need to lock the inner lock.
- let data = unsafe { &mut *self.data.get() };
+ let data = self.data.get_mut();
poison::map_result(self.poison.borrow(), |_| data)
}
}
@@ -538,254 +539,3 @@
}
}
}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use crate::sync::atomic::{AtomicUsize, Ordering};
- use crate::sync::mpsc::channel;
- use crate::sync::{Arc, RwLock, TryLockError};
- use crate::thread;
- use rand::{self, Rng};
-
- #[derive(Eq, PartialEq, Debug)]
- struct NonCopy(i32);
-
- #[test]
- fn smoke() {
- let l = RwLock::new(());
- drop(l.read().unwrap());
- drop(l.write().unwrap());
- drop((l.read().unwrap(), l.read().unwrap()));
- drop(l.write().unwrap());
- }
-
- #[test]
- fn frob() {
- const N: u32 = 10;
- const M: usize = 1000;
-
- let r = Arc::new(RwLock::new(()));
-
- let (tx, rx) = channel::<()>();
- for _ in 0..N {
- let tx = tx.clone();
- let r = r.clone();
- thread::spawn(move || {
- let mut rng = rand::thread_rng();
- for _ in 0..M {
- if rng.gen_bool(1.0 / (N as f64)) {
- drop(r.write().unwrap());
- } else {
- drop(r.read().unwrap());
- }
- }
- drop(tx);
- });
- }
- drop(tx);
- let _ = rx.recv();
- }
-
- #[test]
- fn test_rw_arc_poison_wr() {
- let arc = Arc::new(RwLock::new(1));
- let arc2 = arc.clone();
- let _: Result<(), _> = thread::spawn(move || {
- let _lock = arc2.write().unwrap();
- panic!();
- })
- .join();
- assert!(arc.read().is_err());
- }
-
- #[test]
- fn test_rw_arc_poison_ww() {
- let arc = Arc::new(RwLock::new(1));
- assert!(!arc.is_poisoned());
- let arc2 = arc.clone();
- let _: Result<(), _> = thread::spawn(move || {
- let _lock = arc2.write().unwrap();
- panic!();
- })
- .join();
- assert!(arc.write().is_err());
- assert!(arc.is_poisoned());
- }
-
- #[test]
- fn test_rw_arc_no_poison_rr() {
- let arc = Arc::new(RwLock::new(1));
- let arc2 = arc.clone();
- let _: Result<(), _> = thread::spawn(move || {
- let _lock = arc2.read().unwrap();
- panic!();
- })
- .join();
- let lock = arc.read().unwrap();
- assert_eq!(*lock, 1);
- }
- #[test]
- fn test_rw_arc_no_poison_rw() {
- let arc = Arc::new(RwLock::new(1));
- let arc2 = arc.clone();
- let _: Result<(), _> = thread::spawn(move || {
- let _lock = arc2.read().unwrap();
- panic!()
- })
- .join();
- let lock = arc.write().unwrap();
- assert_eq!(*lock, 1);
- }
-
- #[test]
- fn test_rw_arc() {
- let arc = Arc::new(RwLock::new(0));
- let arc2 = arc.clone();
- let (tx, rx) = channel();
-
- thread::spawn(move || {
- let mut lock = arc2.write().unwrap();
- for _ in 0..10 {
- let tmp = *lock;
- *lock = -1;
- thread::yield_now();
- *lock = tmp + 1;
- }
- tx.send(()).unwrap();
- });
-
- // Readers try to catch the writer in the act
- let mut children = Vec::new();
- for _ in 0..5 {
- let arc3 = arc.clone();
- children.push(thread::spawn(move || {
- let lock = arc3.read().unwrap();
- assert!(*lock >= 0);
- }));
- }
-
- // Wait for children to pass their asserts
- for r in children {
- assert!(r.join().is_ok());
- }
-
- // Wait for writer to finish
- rx.recv().unwrap();
- let lock = arc.read().unwrap();
- assert_eq!(*lock, 10);
- }
-
- #[test]
- fn test_rw_arc_access_in_unwind() {
- let arc = Arc::new(RwLock::new(1));
- let arc2 = arc.clone();
- let _ = thread::spawn(move || -> () {
- struct Unwinder {
- i: Arc<RwLock<isize>>,
- }
- impl Drop for Unwinder {
- fn drop(&mut self) {
- let mut lock = self.i.write().unwrap();
- *lock += 1;
- }
- }
- let _u = Unwinder { i: arc2 };
- panic!();
- })
- .join();
- let lock = arc.read().unwrap();
- assert_eq!(*lock, 2);
- }
-
- #[test]
- fn test_rwlock_unsized() {
- let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]);
- {
- let b = &mut *rw.write().unwrap();
- b[0] = 4;
- b[2] = 5;
- }
- let comp: &[i32] = &[4, 2, 5];
- assert_eq!(&*rw.read().unwrap(), comp);
- }
-
- #[test]
- fn test_rwlock_try_write() {
- let lock = RwLock::new(0isize);
- let read_guard = lock.read().unwrap();
-
- let write_result = lock.try_write();
- match write_result {
- Err(TryLockError::WouldBlock) => (),
- Ok(_) => assert!(false, "try_write should not succeed while read_guard is in scope"),
- Err(_) => assert!(false, "unexpected error"),
- }
-
- drop(read_guard);
- }
-
- #[test]
- fn test_into_inner() {
- let m = RwLock::new(NonCopy(10));
- assert_eq!(m.into_inner().unwrap(), NonCopy(10));
- }
-
- #[test]
- fn test_into_inner_drop() {
- struct Foo(Arc<AtomicUsize>);
- impl Drop for Foo {
- fn drop(&mut self) {
- self.0.fetch_add(1, Ordering::SeqCst);
- }
- }
- let num_drops = Arc::new(AtomicUsize::new(0));
- let m = RwLock::new(Foo(num_drops.clone()));
- assert_eq!(num_drops.load(Ordering::SeqCst), 0);
- {
- let _inner = m.into_inner().unwrap();
- assert_eq!(num_drops.load(Ordering::SeqCst), 0);
- }
- assert_eq!(num_drops.load(Ordering::SeqCst), 1);
- }
-
- #[test]
- fn test_into_inner_poison() {
- let m = Arc::new(RwLock::new(NonCopy(10)));
- let m2 = m.clone();
- let _ = thread::spawn(move || {
- let _lock = m2.write().unwrap();
- panic!("test panic in inner thread to poison RwLock");
- })
- .join();
-
- assert!(m.is_poisoned());
- match Arc::try_unwrap(m).unwrap().into_inner() {
- Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
- Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {:?}", x),
- }
- }
-
- #[test]
- fn test_get_mut() {
- let mut m = RwLock::new(NonCopy(10));
- *m.get_mut().unwrap() = NonCopy(20);
- assert_eq!(m.into_inner().unwrap(), NonCopy(20));
- }
-
- #[test]
- fn test_get_mut_poison() {
- let m = Arc::new(RwLock::new(NonCopy(10)));
- let m2 = m.clone();
- let _ = thread::spawn(move || {
- let _lock = m2.write().unwrap();
- panic!("test panic in inner thread to poison RwLock");
- })
- .join();
-
- assert!(m.is_poisoned());
- match Arc::try_unwrap(m).unwrap().get_mut() {
- Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
- Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {:?}", x),
- }
- }
-}
diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs
new file mode 100644
index 0000000..e9b74fb
--- /dev/null
+++ b/library/std/src/sync/rwlock/tests.rs
@@ -0,0 +1,247 @@
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sync::mpsc::channel;
+use crate::sync::{Arc, RwLock, TryLockError};
+use crate::thread;
+use rand::{self, Rng};
+
+#[derive(Eq, PartialEq, Debug)]
+struct NonCopy(i32);
+
+#[test]
+fn smoke() {
+ let l = RwLock::new(());
+ drop(l.read().unwrap());
+ drop(l.write().unwrap());
+ drop((l.read().unwrap(), l.read().unwrap()));
+ drop(l.write().unwrap());
+}
+
+#[test]
+fn frob() {
+ const N: u32 = 10;
+ const M: usize = 1000;
+
+ let r = Arc::new(RwLock::new(()));
+
+ let (tx, rx) = channel::<()>();
+ for _ in 0..N {
+ let tx = tx.clone();
+ let r = r.clone();
+ thread::spawn(move || {
+ let mut rng = rand::thread_rng();
+ for _ in 0..M {
+ if rng.gen_bool(1.0 / (N as f64)) {
+ drop(r.write().unwrap());
+ } else {
+ drop(r.read().unwrap());
+ }
+ }
+ drop(tx);
+ });
+ }
+ drop(tx);
+ let _ = rx.recv();
+}
+
+#[test]
+fn test_rw_arc_poison_wr() {
+ let arc = Arc::new(RwLock::new(1));
+ let arc2 = arc.clone();
+ let _: Result<(), _> = thread::spawn(move || {
+ let _lock = arc2.write().unwrap();
+ panic!();
+ })
+ .join();
+ assert!(arc.read().is_err());
+}
+
+#[test]
+fn test_rw_arc_poison_ww() {
+ let arc = Arc::new(RwLock::new(1));
+ assert!(!arc.is_poisoned());
+ let arc2 = arc.clone();
+ let _: Result<(), _> = thread::spawn(move || {
+ let _lock = arc2.write().unwrap();
+ panic!();
+ })
+ .join();
+ assert!(arc.write().is_err());
+ assert!(arc.is_poisoned());
+}
+
+#[test]
+fn test_rw_arc_no_poison_rr() {
+ let arc = Arc::new(RwLock::new(1));
+ let arc2 = arc.clone();
+ let _: Result<(), _> = thread::spawn(move || {
+ let _lock = arc2.read().unwrap();
+ panic!();
+ })
+ .join();
+ let lock = arc.read().unwrap();
+ assert_eq!(*lock, 1);
+}
+#[test]
+fn test_rw_arc_no_poison_rw() {
+ let arc = Arc::new(RwLock::new(1));
+ let arc2 = arc.clone();
+ let _: Result<(), _> = thread::spawn(move || {
+ let _lock = arc2.read().unwrap();
+ panic!()
+ })
+ .join();
+ let lock = arc.write().unwrap();
+ assert_eq!(*lock, 1);
+}
+
+#[test]
+fn test_rw_arc() {
+ let arc = Arc::new(RwLock::new(0));
+ let arc2 = arc.clone();
+ let (tx, rx) = channel();
+
+ thread::spawn(move || {
+ let mut lock = arc2.write().unwrap();
+ for _ in 0..10 {
+ let tmp = *lock;
+ *lock = -1;
+ thread::yield_now();
+ *lock = tmp + 1;
+ }
+ tx.send(()).unwrap();
+ });
+
+ // Readers try to catch the writer in the act
+ let mut children = Vec::new();
+ for _ in 0..5 {
+ let arc3 = arc.clone();
+ children.push(thread::spawn(move || {
+ let lock = arc3.read().unwrap();
+ assert!(*lock >= 0);
+ }));
+ }
+
+ // Wait for children to pass their asserts
+ for r in children {
+ assert!(r.join().is_ok());
+ }
+
+ // Wait for writer to finish
+ rx.recv().unwrap();
+ let lock = arc.read().unwrap();
+ assert_eq!(*lock, 10);
+}
+
+#[test]
+fn test_rw_arc_access_in_unwind() {
+ let arc = Arc::new(RwLock::new(1));
+ let arc2 = arc.clone();
+ let _ = thread::spawn(move || -> () {
+ struct Unwinder {
+ i: Arc<RwLock<isize>>,
+ }
+ impl Drop for Unwinder {
+ fn drop(&mut self) {
+ let mut lock = self.i.write().unwrap();
+ *lock += 1;
+ }
+ }
+ let _u = Unwinder { i: arc2 };
+ panic!();
+ })
+ .join();
+ let lock = arc.read().unwrap();
+ assert_eq!(*lock, 2);
+}
+
+#[test]
+fn test_rwlock_unsized() {
+ let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]);
+ {
+ let b = &mut *rw.write().unwrap();
+ b[0] = 4;
+ b[2] = 5;
+ }
+ let comp: &[i32] = &[4, 2, 5];
+ assert_eq!(&*rw.read().unwrap(), comp);
+}
+
+#[test]
+fn test_rwlock_try_write() {
+ let lock = RwLock::new(0isize);
+ let read_guard = lock.read().unwrap();
+
+ let write_result = lock.try_write();
+ match write_result {
+ Err(TryLockError::WouldBlock) => (),
+ Ok(_) => assert!(false, "try_write should not succeed while read_guard is in scope"),
+ Err(_) => assert!(false, "unexpected error"),
+ }
+
+ drop(read_guard);
+}
+
+#[test]
+fn test_into_inner() {
+ let m = RwLock::new(NonCopy(10));
+ assert_eq!(m.into_inner().unwrap(), NonCopy(10));
+}
+
+#[test]
+fn test_into_inner_drop() {
+ struct Foo(Arc<AtomicUsize>);
+ impl Drop for Foo {
+ fn drop(&mut self) {
+ self.0.fetch_add(1, Ordering::SeqCst);
+ }
+ }
+ let num_drops = Arc::new(AtomicUsize::new(0));
+ let m = RwLock::new(Foo(num_drops.clone()));
+ assert_eq!(num_drops.load(Ordering::SeqCst), 0);
+ {
+ let _inner = m.into_inner().unwrap();
+ assert_eq!(num_drops.load(Ordering::SeqCst), 0);
+ }
+ assert_eq!(num_drops.load(Ordering::SeqCst), 1);
+}
+
+#[test]
+fn test_into_inner_poison() {
+ let m = Arc::new(RwLock::new(NonCopy(10)));
+ let m2 = m.clone();
+ let _ = thread::spawn(move || {
+ let _lock = m2.write().unwrap();
+ panic!("test panic in inner thread to poison RwLock");
+ })
+ .join();
+
+ assert!(m.is_poisoned());
+ match Arc::try_unwrap(m).unwrap().into_inner() {
+ Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
+ Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {:?}", x),
+ }
+}
+
+#[test]
+fn test_get_mut() {
+ let mut m = RwLock::new(NonCopy(10));
+ *m.get_mut().unwrap() = NonCopy(20);
+ assert_eq!(m.into_inner().unwrap(), NonCopy(20));
+}
+
+#[test]
+fn test_get_mut_poison() {
+ let m = Arc::new(RwLock::new(NonCopy(10)));
+ let m2 = m.clone();
+ let _ = thread::spawn(move || {
+ let _lock = m2.write().unwrap();
+ panic!("test panic in inner thread to poison RwLock");
+ })
+ .join();
+
+ assert!(m.is_poisoned());
+ match Arc::try_unwrap(m).unwrap().get_mut() {
+ Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
+ Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {:?}", x),
+ }
+}
diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs
index 72c1b85..7727293 100644
--- a/library/std/src/sys/hermit/args.rs
+++ b/library/std/src/sys/hermit/args.rs
@@ -57,11 +57,11 @@
use crate::ptr;
use crate::sys_common::os_str_bytes::*;
- use crate::sys_common::mutex::Mutex;
+ use crate::sys_common::mutex::StaticMutex;
static mut ARGC: isize = 0;
static mut ARGV: *const *const u8 = ptr::null();
- static LOCK: Mutex = Mutex::new();
+ static LOCK: StaticMutex = StaticMutex::new();
pub unsafe fn init(argc: isize, argv: *const *const u8) {
let _guard = LOCK.lock();
diff --git a/library/std/src/sys/sgx/abi/mem.rs b/library/std/src/sys/sgx/abi/mem.rs
index 57fd7ef..ffa234f 100644
--- a/library/std/src/sys/sgx/abi/mem.rs
+++ b/library/std/src/sys/sgx/abi/mem.rs
@@ -21,8 +21,15 @@
#[inline(always)]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn image_base() -> u64 {
- let base;
- unsafe { llvm_asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) };
+ let base: u64;
+ unsafe {
+ asm!(
+ "lea IMAGE_BASE(%rip), {}",
+ lateout(reg) base,
+ // NOTE(#76738): ATT syntax is used to support LLVM 8 and 9.
+ options(att_syntax, nostack, preserves_flags, nomem, pure),
+ )
+ };
base
}
diff --git a/library/std/src/sys/sgx/abi/tls.rs b/library/std/src/sys/sgx/abi/tls.rs
index 2b0485c..0d8952b 100644
--- a/library/std/src/sys/sgx/abi/tls.rs
+++ b/library/std/src/sys/sgx/abi/tls.rs
@@ -1,3 +1,5 @@
+mod sync_bitset;
+
use self::sync_bitset::*;
use crate::cell::Cell;
use crate::mem;
@@ -125,117 +127,3 @@
TLS_KEY_IN_USE.clear(key.to_index());
}
}
-
-mod sync_bitset {
- use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS};
- use crate::iter::{Enumerate, Peekable};
- use crate::slice::Iter;
- use crate::sync::atomic::{AtomicUsize, Ordering};
-
- /// A bitset that can be used synchronously.
- pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]);
-
- pub(super) const SYNC_BITSET_INIT: SyncBitset =
- SyncBitset([AtomicUsize::new(0), AtomicUsize::new(0)]);
-
- impl SyncBitset {
- pub fn get(&self, index: usize) -> bool {
- let (hi, lo) = Self::split(index);
- (self.0[hi].load(Ordering::Relaxed) & lo) != 0
- }
-
- /// Not atomic.
- pub fn iter(&self) -> SyncBitsetIter<'_> {
- SyncBitsetIter { iter: self.0.iter().enumerate().peekable(), elem_idx: 0 }
- }
-
- pub fn clear(&self, index: usize) {
- let (hi, lo) = Self::split(index);
- self.0[hi].fetch_and(!lo, Ordering::Relaxed);
- }
-
- /// Sets any unset bit. Not atomic. Returns `None` if all bits were
- /// observed to be set.
- pub fn set(&self) -> Option<usize> {
- 'elems: for (idx, elem) in self.0.iter().enumerate() {
- let mut current = elem.load(Ordering::Relaxed);
- loop {
- if 0 == !current {
- continue 'elems;
- }
- let trailing_ones = (!current).trailing_zeros() as usize;
- match elem.compare_exchange(
- current,
- current | (1 << trailing_ones),
- Ordering::AcqRel,
- Ordering::Relaxed,
- ) {
- Ok(_) => return Some(idx * USIZE_BITS + trailing_ones),
- Err(previous) => current = previous,
- }
- }
- }
- None
- }
-
- fn split(index: usize) -> (usize, usize) {
- (index / USIZE_BITS, 1 << (index % USIZE_BITS))
- }
- }
-
- pub(super) struct SyncBitsetIter<'a> {
- iter: Peekable<Enumerate<Iter<'a, AtomicUsize>>>,
- elem_idx: usize,
- }
-
- impl<'a> Iterator for SyncBitsetIter<'a> {
- type Item = usize;
-
- fn next(&mut self) -> Option<usize> {
- self.iter.peek().cloned().and_then(|(idx, elem)| {
- let elem = elem.load(Ordering::Relaxed);
- let low_mask = (1 << self.elem_idx) - 1;
- let next = elem & !low_mask;
- let next_idx = next.trailing_zeros() as usize;
- self.elem_idx = next_idx + 1;
- if self.elem_idx >= 64 {
- self.elem_idx = 0;
- self.iter.next();
- }
- match next_idx {
- 64 => self.next(),
- _ => Some(idx * USIZE_BITS + next_idx),
- }
- })
- }
- }
-
- #[cfg(test)]
- mod tests {
- use super::*;
-
- fn test_data(bitset: [usize; 2], bit_indices: &[usize]) {
- let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]);
- assert_eq!(set.iter().collect::<Vec<_>>(), bit_indices);
- for &i in bit_indices {
- assert!(set.get(i));
- }
- }
-
- #[test]
- fn iter() {
- test_data([0b0110_1001, 0], &[0, 3, 5, 6]);
- test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]);
- test_data([0, 0], &[]);
- }
-
- #[test]
- fn set_get_clear() {
- let set = SYNC_BITSET_INIT;
- let key = set.set().unwrap();
- assert!(set.get(key));
- set.clear(key);
- assert!(!set.get(key));
- }
- }
-}
diff --git a/library/std/src/sys/sgx/abi/tls/sync_bitset.rs b/library/std/src/sys/sgx/abi/tls/sync_bitset.rs
new file mode 100644
index 0000000..4eeff8f
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/tls/sync_bitset.rs
@@ -0,0 +1,85 @@
+#[cfg(test)]
+mod tests;
+
+use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS};
+use crate::iter::{Enumerate, Peekable};
+use crate::slice::Iter;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+
+/// A bitset that can be used synchronously.
+pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]);
+
+pub(super) const SYNC_BITSET_INIT: SyncBitset =
+ SyncBitset([AtomicUsize::new(0), AtomicUsize::new(0)]);
+
+impl SyncBitset {
+ pub fn get(&self, index: usize) -> bool {
+ let (hi, lo) = Self::split(index);
+ (self.0[hi].load(Ordering::Relaxed) & lo) != 0
+ }
+
+ /// Not atomic.
+ pub fn iter(&self) -> SyncBitsetIter<'_> {
+ SyncBitsetIter { iter: self.0.iter().enumerate().peekable(), elem_idx: 0 }
+ }
+
+ pub fn clear(&self, index: usize) {
+ let (hi, lo) = Self::split(index);
+ self.0[hi].fetch_and(!lo, Ordering::Relaxed);
+ }
+
+ /// Sets any unset bit. Not atomic. Returns `None` if all bits were
+ /// observed to be set.
+ pub fn set(&self) -> Option<usize> {
+ 'elems: for (idx, elem) in self.0.iter().enumerate() {
+ let mut current = elem.load(Ordering::Relaxed);
+ loop {
+ if 0 == !current {
+ continue 'elems;
+ }
+ let trailing_ones = (!current).trailing_zeros() as usize;
+ match elem.compare_exchange(
+ current,
+ current | (1 << trailing_ones),
+ Ordering::AcqRel,
+ Ordering::Relaxed,
+ ) {
+ Ok(_) => return Some(idx * USIZE_BITS + trailing_ones),
+ Err(previous) => current = previous,
+ }
+ }
+ }
+ None
+ }
+
+ fn split(index: usize) -> (usize, usize) {
+ (index / USIZE_BITS, 1 << (index % USIZE_BITS))
+ }
+}
+
+pub(super) struct SyncBitsetIter<'a> {
+ iter: Peekable<Enumerate<Iter<'a, AtomicUsize>>>,
+ elem_idx: usize,
+}
+
+impl<'a> Iterator for SyncBitsetIter<'a> {
+ type Item = usize;
+
+ fn next(&mut self) -> Option<usize> {
+ self.iter.peek().cloned().and_then(|(idx, elem)| {
+ let elem = elem.load(Ordering::Relaxed);
+ let low_mask = (1 << self.elem_idx) - 1;
+ let next = elem & !low_mask;
+ let next_idx = next.trailing_zeros() as usize;
+ self.elem_idx = next_idx + 1;
+ if self.elem_idx >= 64 {
+ self.elem_idx = 0;
+ self.iter.next();
+ }
+ match next_idx {
+ 64 => self.next(),
+ _ => Some(idx * USIZE_BITS + next_idx),
+ }
+ })
+ }
+}
diff --git a/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs b/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs
new file mode 100644
index 0000000..d7eb2e1
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs
@@ -0,0 +1,25 @@
+use super::*;
+
+fn test_data(bitset: [usize; 2], bit_indices: &[usize]) {
+ let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]);
+ assert_eq!(set.iter().collect::<Vec<_>>(), bit_indices);
+ for &i in bit_indices {
+ assert!(set.get(i));
+ }
+}
+
+#[test]
+fn iter() {
+ test_data([0b0110_1001, 0], &[0, 3, 5, 6]);
+ test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]);
+ test_data([0, 0], &[]);
+}
+
+#[test]
+fn set_get_clear() {
+ let set = SYNC_BITSET_INIT;
+ let key = set.set().unwrap();
+ assert!(set.get(key));
+ set.clear(key);
+ assert!(!set.get(key));
+}
diff --git a/library/std/src/sys/sgx/ext/arch.rs b/library/std/src/sys/sgx/ext/arch.rs
index 0c97a87..7488e7e 100644
--- a/library/std/src/sys/sgx/ext/arch.rs
+++ b/library/std/src/sys/sgx/ext/arch.rs
@@ -31,13 +31,13 @@
let mut out = MaybeUninit::uninit();
let error;
- llvm_asm!(
- "enclu"
- : "={eax}"(error)
- : "{eax}"(ENCLU_EGETKEY),
- "{rbx}"(request),
- "{rcx}"(out.as_mut_ptr())
- : "flags"
+ asm!(
+ "enclu",
+ inlateout("eax") ENCLU_EGETKEY => error,
+ in("rbx") request,
+ in("rcx") out.as_mut_ptr(),
+ // NOTE(#76738): ATT syntax is used to support LLVM 8 and 9.
+ options(att_syntax, nostack),
);
match error {
@@ -60,13 +60,14 @@
unsafe {
let mut report = MaybeUninit::uninit();
- llvm_asm!(
- "enclu"
- : /* no output registers */
- : "{eax}"(ENCLU_EREPORT),
- "{rbx}"(targetinfo),
- "{rcx}"(reportdata),
- "{rdx}"(report.as_mut_ptr())
+ asm!(
+ "enclu",
+ in("eax") ENCLU_EREPORT,
+ in("rbx") targetinfo,
+ in("rcx") reportdata,
+ in("rdx") report.as_mut_ptr(),
+ // NOTE(#76738): ATT syntax is used to support LLVM 8 and 9.
+ options(att_syntax, preserves_flags, nostack),
);
report.assume_init()
diff --git a/library/std/src/sys/sgx/fs.rs b/library/std/src/sys/sgx/fs.rs
deleted file mode 100644
index ecb5b51..0000000
--- a/library/std/src/sys/sgx/fs.rs
+++ /dev/null
@@ -1,308 +0,0 @@
-use crate::ffi::OsString;
-use crate::fmt;
-use crate::hash::{Hash, Hasher};
-use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
-use crate::path::{Path, PathBuf};
-use crate::sys::time::SystemTime;
-use crate::sys::{unsupported, Void};
-
-pub struct File(Void);
-
-pub struct FileAttr(Void);
-
-pub struct ReadDir(Void);
-
-pub struct DirEntry(Void);
-
-#[derive(Clone, Debug)]
-pub struct OpenOptions {}
-
-pub struct FilePermissions(Void);
-
-pub struct FileType(Void);
-
-#[derive(Debug)]
-pub struct DirBuilder {}
-
-impl FileAttr {
- pub fn size(&self) -> u64 {
- match self.0 {}
- }
-
- pub fn perm(&self) -> FilePermissions {
- match self.0 {}
- }
-
- pub fn file_type(&self) -> FileType {
- match self.0 {}
- }
-
- pub fn modified(&self) -> io::Result<SystemTime> {
- match self.0 {}
- }
-
- pub fn accessed(&self) -> io::Result<SystemTime> {
- match self.0 {}
- }
-
- pub fn created(&self) -> io::Result<SystemTime> {
- match self.0 {}
- }
-}
-
-impl Clone for FileAttr {
- fn clone(&self) -> FileAttr {
- match self.0 {}
- }
-}
-
-impl FilePermissions {
- pub fn readonly(&self) -> bool {
- match self.0 {}
- }
-
- pub fn set_readonly(&mut self, _readonly: bool) {
- match self.0 {}
- }
-}
-
-impl Clone for FilePermissions {
- fn clone(&self) -> FilePermissions {
- match self.0 {}
- }
-}
-
-impl PartialEq for FilePermissions {
- fn eq(&self, _other: &FilePermissions) -> bool {
- match self.0 {}
- }
-}
-
-impl Eq for FilePermissions {}
-
-impl fmt::Debug for FilePermissions {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
- }
-}
-
-impl FileType {
- pub fn is_dir(&self) -> bool {
- match self.0 {}
- }
-
- pub fn is_file(&self) -> bool {
- match self.0 {}
- }
-
- pub fn is_symlink(&self) -> bool {
- match self.0 {}
- }
-}
-
-impl Clone for FileType {
- fn clone(&self) -> FileType {
- match self.0 {}
- }
-}
-
-impl Copy for FileType {}
-
-impl PartialEq for FileType {
- fn eq(&self, _other: &FileType) -> bool {
- match self.0 {}
- }
-}
-
-impl Eq for FileType {}
-
-impl Hash for FileType {
- fn hash<H: Hasher>(&self, _h: &mut H) {
- match self.0 {}
- }
-}
-
-impl fmt::Debug for FileType {
- 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 {
- match self.0 {}
- }
-}
-
-impl Iterator for ReadDir {
- type Item = io::Result<DirEntry>;
-
- fn next(&mut self) -> Option<io::Result<DirEntry>> {
- match self.0 {}
- }
-}
-
-impl DirEntry {
- pub fn path(&self) -> PathBuf {
- match self.0 {}
- }
-
- pub fn file_name(&self) -> OsString {
- match self.0 {}
- }
-
- pub fn metadata(&self) -> io::Result<FileAttr> {
- match self.0 {}
- }
-
- pub fn file_type(&self) -> io::Result<FileType> {
- match self.0 {}
- }
-}
-
-impl OpenOptions {
- pub fn new() -> OpenOptions {
- OpenOptions {}
- }
-
- pub fn read(&mut self, _read: bool) {}
- pub fn write(&mut self, _write: bool) {}
- pub fn append(&mut self, _append: bool) {}
- pub fn truncate(&mut self, _truncate: bool) {}
- pub fn create(&mut self, _create: bool) {}
- pub fn create_new(&mut self, _create_new: bool) {}
-}
-
-impl File {
- pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
- unsupported()
- }
-
- pub fn file_attr(&self) -> io::Result<FileAttr> {
- match self.0 {}
- }
-
- pub fn fsync(&self) -> io::Result<()> {
- match self.0 {}
- }
-
- pub fn datasync(&self) -> io::Result<()> {
- match self.0 {}
- }
-
- pub fn truncate(&self, _size: u64) -> io::Result<()> {
- match self.0 {}
- }
-
- 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 is_read_vectored(&self) -> bool {
- 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 is_write_vectored(&self) -> bool {
- match self.0 {}
- }
-
- pub fn flush(&self) -> io::Result<()> {
- match self.0 {}
- }
-
- pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
- match self.0 {}
- }
-
- pub fn duplicate(&self) -> io::Result<File> {
- match self.0 {}
- }
-
- pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
- match self.0 {}
- }
-
- pub fn diverge(&self) -> ! {
- match self.0 {}
- }
-}
-
-impl DirBuilder {
- pub fn new() -> DirBuilder {
- DirBuilder {}
- }
-
- pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
- unsupported()
- }
-}
-
-impl fmt::Debug for File {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.0 {}
- }
-}
-
-pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
- unsupported()
-}
-
-pub fn unlink(_p: &Path) -> io::Result<()> {
- unsupported()
-}
-
-pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
- unsupported()
-}
-
-pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
- match perm.0 {}
-}
-
-pub fn rmdir(_p: &Path) -> io::Result<()> {
- unsupported()
-}
-
-pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
- unsupported()
-}
-
-pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
- unsupported()
-}
-
-pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> {
- unsupported()
-}
-
-pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
- unsupported()
-}
-
-pub fn stat(_p: &Path) -> io::Result<FileAttr> {
- unsupported()
-}
-
-pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
- unsupported()
-}
-
-pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
- unsupported()
-}
-
-pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
- unsupported()
-}
diff --git a/library/std/src/sys/sgx/io.rs b/library/std/src/sys/sgx/io.rs
deleted file mode 100644
index d5f475b..0000000
--- a/library/std/src/sys/sgx/io.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use crate::mem;
-
-#[derive(Copy, Clone)]
-pub struct IoSlice<'a>(&'a [u8]);
-
-impl<'a> IoSlice<'a> {
- #[inline]
- pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
- IoSlice(buf)
- }
-
- #[inline]
- pub fn advance(&mut self, n: usize) {
- self.0 = &self.0[n..]
- }
-
- #[inline]
- pub fn as_slice(&self) -> &[u8] {
- self.0
- }
-}
-
-pub struct IoSliceMut<'a>(&'a mut [u8]);
-
-impl<'a> IoSliceMut<'a> {
- #[inline]
- pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
- IoSliceMut(buf)
- }
-
- #[inline]
- 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]
- pub fn as_slice(&self) -> &[u8] {
- self.0
- }
-
- #[inline]
- pub fn as_mut_slice(&mut self) -> &mut [u8] {
- self.0
- }
-}
diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs
index 1d32eb2..1abd91e 100644
--- a/library/std/src/sys/sgx/mod.rs
+++ b/library/std/src/sys/sgx/mod.rs
@@ -17,14 +17,18 @@
pub mod env;
pub mod ext;
pub mod fd;
+#[path = "../unsupported/fs.rs"]
pub mod fs;
+#[path = "../unsupported/io.rs"]
pub mod io;
pub mod memchr;
pub mod mutex;
pub mod net;
pub mod os;
pub mod path;
+#[path = "../unsupported/pipe.rs"]
pub mod pipe;
+#[path = "../unsupported/process.rs"]
pub mod process;
pub mod rwlock;
pub mod stack_overflow;
diff --git a/library/std/src/sys/sgx/pipe.rs b/library/std/src/sys/sgx/pipe.rs
deleted file mode 100644
index 10d0925..0000000
--- a/library/std/src/sys/sgx/pipe.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-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 is_read_vectored(&self) -> bool {
- 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 is_write_vectored(&self) -> bool {
- 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/library/std/src/sys/sgx/process.rs b/library/std/src/sys/sgx/process.rs
deleted file mode 100644
index 4702e5c..0000000
--- a/library/std/src/sys/sgx/process.rs
+++ /dev/null
@@ -1,149 +0,0 @@
-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;
-
-pub use crate::ffi::OsString as EnvKey;
-
-////////////////////////////////////////////////////////////////////////////////
-// Command
-////////////////////////////////////////////////////////////////////////////////
-
-pub struct Command {
- env: CommandEnv,
-}
-
-// 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 {
- &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 {
- file.diverge()
- }
-}
-
-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/library/std/src/sys/sgx/rwlock.rs b/library/std/src/sys/sgx/rwlock.rs
index 722b4f5..3bf2a7d 100644
--- a/library/std/src/sys/sgx/rwlock.rs
+++ b/library/std/src/sys/sgx/rwlock.rs
@@ -1,3 +1,6 @@
+#[cfg(test)]
+mod tests;
+
use crate::num::NonZeroUsize;
use super::waitqueue::{
@@ -198,50 +201,3 @@
(*p).unlock();
return 0;
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::mem::{self, MaybeUninit};
- use core::array::FixedSizeArray;
-
- // 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() {
- #[rustfmt::skip]
- const RWLOCK_INIT: &[u8] = &[
- /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
- ];
-
- #[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; 144]>(init.assume_init()).as_slice(), RWLOCK_INIT)
- };
- }
-}
diff --git a/library/std/src/sys/sgx/rwlock/tests.rs b/library/std/src/sys/sgx/rwlock/tests.rs
new file mode 100644
index 0000000..17c9e72
--- /dev/null
+++ b/library/std/src/sys/sgx/rwlock/tests.rs
@@ -0,0 +1,31 @@
+use super::*;
+
+// Verify that the byte pattern libunwind uses to initialize an RWLock is
+// equivalent to the value of RWLock::new(). If the value changes,
+// `src/UnwindRustSgx.h` in libunwind needs to be changed too.
+#[test]
+fn test_c_rwlock_initializer() {
+ #[rustfmt::skip]
+ const C_RWLOCK_INIT: &[u8] = &[
+ /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ];
+
+ // For the test to work, we need the padding/unused bytes in RWLock to be
+ // initialized as 0. In practice, this is the case with statics.
+ static RUST_RWLOCK_INIT: RWLock = RWLock::new();
+
+ unsafe {
+ // If the assertion fails, that not necessarily an issue with the value
+ // of C_RWLOCK_INIT. It might just be an issue with the way padding
+ // bytes are initialized in the test code.
+ assert_eq!(&crate::mem::transmute_copy::<_, [u8; 144]>(&RUST_RWLOCK_INIT), C_RWLOCK_INIT);
+ };
+}
diff --git a/library/std/src/sys/sgx/waitqueue.rs b/library/std/src/sys/sgx/waitqueue.rs
index 070afa5..e464dc3 100644
--- a/library/std/src/sys/sgx/waitqueue.rs
+++ b/library/std/src/sys/sgx/waitqueue.rs
@@ -9,6 +9,18 @@
//! Since userspace may send spurious wake-ups, the wakeup event state is
//! recorded in the enclave. The wakeup event state is protected by a spinlock.
//! The queue and associated wait state are stored in a `WaitVariable`.
+
+#[cfg(test)]
+mod tests;
+
+/// A doubly-linked list where callers are in charge of memory allocation
+/// of the nodes in the list.
+mod unsafe_list;
+
+/// Trivial spinlock-based implementation of `sync::Mutex`.
+// FIXME: Perhaps use Intel TSX to avoid locking?
+mod spin_mutex;
+
use crate::num::NonZeroUsize;
use crate::ops::{Deref, DerefMut};
use crate::time::Duration;
@@ -231,389 +243,3 @@
}
}
}
-
-/// A doubly-linked list where callers are in charge of memory allocation
-/// of the nodes in the list.
-mod unsafe_list {
- use crate::mem;
- use crate::ptr::NonNull;
-
- pub struct UnsafeListEntry<T> {
- next: NonNull<UnsafeListEntry<T>>,
- prev: NonNull<UnsafeListEntry<T>>,
- value: Option<T>,
- }
-
- impl<T> UnsafeListEntry<T> {
- fn dummy() -> Self {
- UnsafeListEntry { next: NonNull::dangling(), prev: NonNull::dangling(), value: None }
- }
-
- pub fn new(value: T) -> Self {
- UnsafeListEntry { value: Some(value), ..Self::dummy() }
- }
- }
-
- pub struct UnsafeList<T> {
- head_tail: NonNull<UnsafeListEntry<T>>,
- head_tail_entry: Option<UnsafeListEntry<T>>,
- }
-
- impl<T> UnsafeList<T> {
- pub const fn new() -> Self {
- unsafe {
- UnsafeList { head_tail: NonNull::new_unchecked(1 as _), head_tail_entry: None }
- }
- }
-
- unsafe fn init(&mut self) {
- if self.head_tail_entry.is_none() {
- self.head_tail_entry = Some(UnsafeListEntry::dummy());
- self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap());
- self.head_tail.as_mut().next = self.head_tail;
- self.head_tail.as_mut().prev = self.head_tail;
- }
- }
-
- pub fn is_empty(&self) -> bool {
- unsafe {
- if self.head_tail_entry.is_some() {
- let first = self.head_tail.as_ref().next;
- if first == self.head_tail {
- // ,-------> /---------\ next ---,
- // | |head_tail| |
- // `--- prev \---------/ <-------`
- rtassert!(self.head_tail.as_ref().prev == first);
- true
- } else {
- false
- }
- } else {
- true
- }
- }
- }
-
- /// Pushes an entry onto the back of the 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. 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();
-
- // BEFORE:
- // /---------\ next ---> /---------\
- // ... |prev_tail| |head_tail| ...
- // \---------/ <--- prev \---------/
- //
- // AFTER:
- // /---------\ next ---> /-----\ next ---> /---------\
- // ... |prev_tail| |entry| |head_tail| ...
- // \---------/ <--- prev \-----/ <--- prev \---------/
- let mut entry = NonNull::new_unchecked(entry);
- let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry);
- 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()
- }
-
- /// Pops an entry from the front of the list.
- ///
- /// # Safety
- ///
- /// The caller must make sure to synchronize ending the borrow of the
- /// return value and deallocation of the containing entry.
- pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> {
- self.init();
-
- if self.is_empty() {
- None
- } else {
- // BEFORE:
- // /---------\ next ---> /-----\ next ---> /------\
- // ... |head_tail| |first| |second| ...
- // \---------/ <--- prev \-----/ <--- prev \------/
- //
- // AFTER:
- // /---------\ next ---> /------\
- // ... |head_tail| |second| ...
- // \---------/ <--- prev \------/
- let mut first = self.head_tail.as_mut().next;
- let mut second = first.as_mut().next;
- self.head_tail.as_mut().next = second;
- 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())
- }
- }
-
- /// Removes an entry from the list.
- ///
- /// # Safety
- ///
- /// The caller must ensure that `entry` has been pushed onto `self`
- /// prior to this call and has not moved since then.
- pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry<T>) {
- rtassert!(!self.is_empty());
- // BEFORE:
- // /----\ next ---> /-----\ next ---> /----\
- // ... |prev| |entry| |next| ...
- // \----/ <--- prev \-----/ <--- prev \----/
- //
- // AFTER:
- // /----\ next ---> /----\
- // ... |prev| |next| ...
- // \----/ <--- prev \----/
- let mut prev = entry.prev;
- let mut next = entry.next;
- prev.as_mut().next = next;
- next.as_mut().prev = prev;
- entry.next = NonNull::dangling();
- entry.prev = NonNull::dangling();
- }
- }
-
- #[cfg(test)]
- mod tests {
- use super::*;
- use crate::cell::Cell;
-
- unsafe fn assert_empty<T>(list: &mut UnsafeList<T>) {
- assert!(list.pop().is_none(), "assertion failed: list is not empty");
- }
-
- #[test]
- fn init_empty() {
- unsafe {
- assert_empty(&mut UnsafeList::<i32>::new());
- }
- }
-
- #[test]
- fn push_pop() {
- unsafe {
- let mut node = UnsafeListEntry::new(1234);
- let mut list = UnsafeList::new();
- assert_eq!(list.push(&mut node), &1234);
- assert_eq!(list.pop().unwrap(), &1234);
- assert_empty(&mut list);
- }
- }
-
- #[test]
- fn push_remove() {
- unsafe {
- let mut node = UnsafeListEntry::new(1234);
- let mut list = UnsafeList::new();
- assert_eq!(list.push(&mut node), &1234);
- list.remove(&mut node);
- assert_empty(&mut list);
- }
- }
-
- #[test]
- fn push_remove_pop() {
- unsafe {
- let mut node1 = UnsafeListEntry::new(11);
- let mut node2 = UnsafeListEntry::new(12);
- let mut node3 = UnsafeListEntry::new(13);
- let mut node4 = UnsafeListEntry::new(14);
- let mut node5 = UnsafeListEntry::new(15);
- let mut list = UnsafeList::new();
- assert_eq!(list.push(&mut node1), &11);
- assert_eq!(list.push(&mut node2), &12);
- assert_eq!(list.push(&mut node3), &13);
- assert_eq!(list.push(&mut node4), &14);
- assert_eq!(list.push(&mut node5), &15);
-
- list.remove(&mut node1);
- assert_eq!(list.pop().unwrap(), &12);
- list.remove(&mut node3);
- assert_eq!(list.pop().unwrap(), &14);
- list.remove(&mut node5);
- assert_empty(&mut list);
-
- assert_eq!(list.push(&mut node1), &11);
- assert_eq!(list.pop().unwrap(), &11);
- assert_empty(&mut list);
-
- assert_eq!(list.push(&mut node3), &13);
- assert_eq!(list.push(&mut node4), &14);
- list.remove(&mut node3);
- list.remove(&mut node4);
- assert_empty(&mut list);
- }
- }
-
- #[test]
- fn complex_pushes_pops() {
- unsafe {
- let mut node1 = UnsafeListEntry::new(1234);
- let mut node2 = UnsafeListEntry::new(4567);
- let mut node3 = UnsafeListEntry::new(9999);
- let mut node4 = UnsafeListEntry::new(8642);
- let mut list = UnsafeList::new();
- list.push(&mut node1);
- list.push(&mut node2);
- assert_eq!(list.pop().unwrap(), &1234);
- list.push(&mut node3);
- assert_eq!(list.pop().unwrap(), &4567);
- assert_eq!(list.pop().unwrap(), &9999);
- assert_empty(&mut list);
- list.push(&mut node4);
- assert_eq!(list.pop().unwrap(), &8642);
- assert_empty(&mut list);
- }
- }
-
- #[test]
- fn cell() {
- unsafe {
- let mut node = UnsafeListEntry::new(Cell::new(0));
- let mut list = UnsafeList::new();
- let noderef = list.push(&mut node);
- assert_eq!(noderef.get(), 0);
- list.pop().unwrap().set(1);
- assert_empty(&mut list);
- assert_eq!(noderef.get(), 1);
- }
- }
- }
-}
-
-/// Trivial spinlock-based implementation of `sync::Mutex`.
-// FIXME: Perhaps use Intel TSX to avoid locking?
-mod spin_mutex {
- use crate::cell::UnsafeCell;
- use crate::ops::{Deref, DerefMut};
- use crate::sync::atomic::{spin_loop_hint, AtomicBool, Ordering};
-
- #[derive(Default)]
- pub struct SpinMutex<T> {
- value: UnsafeCell<T>,
- lock: AtomicBool,
- }
-
- unsafe impl<T: Send> Send for SpinMutex<T> {}
- unsafe impl<T: Send> Sync for SpinMutex<T> {}
-
- pub struct SpinMutexGuard<'a, T: 'a> {
- mutex: &'a SpinMutex<T>,
- }
-
- impl<'a, T> !Send for SpinMutexGuard<'a, T> {}
- unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {}
-
- impl<T> SpinMutex<T> {
- pub const fn new(value: T) -> Self {
- SpinMutex { value: UnsafeCell::new(value), lock: AtomicBool::new(false) }
- }
-
- #[inline(always)]
- pub fn lock(&self) -> SpinMutexGuard<'_, T> {
- loop {
- match self.try_lock() {
- None => {
- while self.lock.load(Ordering::Relaxed) {
- spin_loop_hint()
- }
- }
- Some(guard) => return guard,
- }
- }
- }
-
- #[inline(always)]
- pub fn try_lock(&self) -> Option<SpinMutexGuard<'_, T>> {
- if !self.lock.compare_and_swap(false, true, Ordering::Acquire) {
- Some(SpinMutexGuard { mutex: self })
- } else {
- None
- }
- }
- }
-
- /// Lock the Mutex or return false.
- pub macro try_lock_or_false($e:expr) {
- if let Some(v) = $e.try_lock() { v } else { return false }
- }
-
- impl<'a, T> Deref for SpinMutexGuard<'a, T> {
- type Target = T;
-
- fn deref(&self) -> &T {
- unsafe { &*self.mutex.value.get() }
- }
- }
-
- impl<'a, T> DerefMut for SpinMutexGuard<'a, T> {
- fn deref_mut(&mut self) -> &mut T {
- unsafe { &mut *self.mutex.value.get() }
- }
- }
-
- impl<'a, T> Drop for SpinMutexGuard<'a, T> {
- fn drop(&mut self) {
- self.mutex.lock.store(false, Ordering::Release)
- }
- }
-
- #[cfg(test)]
- mod tests {
- #![allow(deprecated)]
-
- use super::*;
- use crate::sync::Arc;
- use crate::thread;
- use crate::time::Duration;
-
- #[test]
- fn sleep() {
- let mutex = Arc::new(SpinMutex::<i32>::default());
- let mutex2 = mutex.clone();
- let guard = mutex.lock();
- let t1 = thread::spawn(move || {
- *mutex2.lock() = 1;
- });
-
- thread::sleep(Duration::from_millis(50));
-
- assert_eq!(*guard, 0);
- drop(guard);
- t1.join().unwrap();
- assert_eq!(*mutex.lock(), 1);
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::sync::Arc;
- use crate::thread;
-
- #[test]
- fn queue() {
- let wq = Arc::new(SpinMutex::<WaitVariable<()>>::default());
- let wq2 = wq.clone();
-
- let locked = wq.lock();
-
- let t1 = thread::spawn(move || {
- // if we obtain the lock, the main thread should be waiting
- assert!(WaitQueue::notify_one(wq2.lock()).is_ok());
- });
-
- WaitQueue::wait(locked, || {});
-
- t1.join().unwrap();
- }
-}
diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs
new file mode 100644
index 0000000..d99ce89
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs
@@ -0,0 +1,76 @@
+#[cfg(test)]
+mod tests;
+
+use crate::cell::UnsafeCell;
+use crate::ops::{Deref, DerefMut};
+use crate::sync::atomic::{spin_loop_hint, AtomicBool, Ordering};
+
+#[derive(Default)]
+pub struct SpinMutex<T> {
+ value: UnsafeCell<T>,
+ lock: AtomicBool,
+}
+
+unsafe impl<T: Send> Send for SpinMutex<T> {}
+unsafe impl<T: Send> Sync for SpinMutex<T> {}
+
+pub struct SpinMutexGuard<'a, T: 'a> {
+ mutex: &'a SpinMutex<T>,
+}
+
+impl<'a, T> !Send for SpinMutexGuard<'a, T> {}
+unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {}
+
+impl<T> SpinMutex<T> {
+ pub const fn new(value: T) -> Self {
+ SpinMutex { value: UnsafeCell::new(value), lock: AtomicBool::new(false) }
+ }
+
+ #[inline(always)]
+ pub fn lock(&self) -> SpinMutexGuard<'_, T> {
+ loop {
+ match self.try_lock() {
+ None => {
+ while self.lock.load(Ordering::Relaxed) {
+ spin_loop_hint()
+ }
+ }
+ Some(guard) => return guard,
+ }
+ }
+ }
+
+ #[inline(always)]
+ pub fn try_lock(&self) -> Option<SpinMutexGuard<'_, T>> {
+ if !self.lock.compare_and_swap(false, true, Ordering::Acquire) {
+ Some(SpinMutexGuard { mutex: self })
+ } else {
+ None
+ }
+ }
+}
+
+/// Lock the Mutex or return false.
+pub macro try_lock_or_false($e:expr) {
+ if let Some(v) = $e.try_lock() { v } else { return false }
+}
+
+impl<'a, T> Deref for SpinMutexGuard<'a, T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ unsafe { &*self.mutex.value.get() }
+ }
+}
+
+impl<'a, T> DerefMut for SpinMutexGuard<'a, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ unsafe { &mut *self.mutex.value.get() }
+ }
+}
+
+impl<'a, T> Drop for SpinMutexGuard<'a, T> {
+ fn drop(&mut self) {
+ self.mutex.lock.store(false, Ordering::Release)
+ }
+}
diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs
new file mode 100644
index 0000000..4c5994b
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs
@@ -0,0 +1,23 @@
+#![allow(deprecated)]
+
+use super::*;
+use crate::sync::Arc;
+use crate::thread;
+use crate::time::Duration;
+
+#[test]
+fn sleep() {
+ let mutex = Arc::new(SpinMutex::<i32>::default());
+ let mutex2 = mutex.clone();
+ let guard = mutex.lock();
+ let t1 = thread::spawn(move || {
+ *mutex2.lock() = 1;
+ });
+
+ thread::sleep(Duration::from_millis(50));
+
+ assert_eq!(*guard, 0);
+ drop(guard);
+ t1.join().unwrap();
+ assert_eq!(*mutex.lock(), 1);
+}
diff --git a/library/std/src/sys/sgx/waitqueue/tests.rs b/library/std/src/sys/sgx/waitqueue/tests.rs
new file mode 100644
index 0000000..bf91fdd
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/tests.rs
@@ -0,0 +1,20 @@
+use super::*;
+use crate::sync::Arc;
+use crate::thread;
+
+#[test]
+fn queue() {
+ let wq = Arc::new(SpinMutex::<WaitVariable<()>>::default());
+ let wq2 = wq.clone();
+
+ let locked = wq.lock();
+
+ let t1 = thread::spawn(move || {
+ // if we obtain the lock, the main thread should be waiting
+ assert!(WaitQueue::notify_one(wq2.lock()).is_ok());
+ });
+
+ WaitQueue::wait(locked, || {});
+
+ t1.join().unwrap();
+}
diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs
new file mode 100644
index 0000000..7a24654
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs
@@ -0,0 +1,146 @@
+#[cfg(test)]
+mod tests;
+
+use crate::mem;
+use crate::ptr::NonNull;
+
+pub struct UnsafeListEntry<T> {
+ next: NonNull<UnsafeListEntry<T>>,
+ prev: NonNull<UnsafeListEntry<T>>,
+ value: Option<T>,
+}
+
+impl<T> UnsafeListEntry<T> {
+ fn dummy() -> Self {
+ UnsafeListEntry { next: NonNull::dangling(), prev: NonNull::dangling(), value: None }
+ }
+
+ pub fn new(value: T) -> Self {
+ UnsafeListEntry { value: Some(value), ..Self::dummy() }
+ }
+}
+
+pub struct UnsafeList<T> {
+ head_tail: NonNull<UnsafeListEntry<T>>,
+ head_tail_entry: Option<UnsafeListEntry<T>>,
+}
+
+impl<T> UnsafeList<T> {
+ pub const fn new() -> Self {
+ unsafe { UnsafeList { head_tail: NonNull::new_unchecked(1 as _), head_tail_entry: None } }
+ }
+
+ unsafe fn init(&mut self) {
+ if self.head_tail_entry.is_none() {
+ self.head_tail_entry = Some(UnsafeListEntry::dummy());
+ self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap());
+ self.head_tail.as_mut().next = self.head_tail;
+ self.head_tail.as_mut().prev = self.head_tail;
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ unsafe {
+ if self.head_tail_entry.is_some() {
+ let first = self.head_tail.as_ref().next;
+ if first == self.head_tail {
+ // ,-------> /---------\ next ---,
+ // | |head_tail| |
+ // `--- prev \---------/ <-------`
+ rtassert!(self.head_tail.as_ref().prev == first);
+ true
+ } else {
+ false
+ }
+ } else {
+ true
+ }
+ }
+ }
+
+ /// Pushes an entry onto the back of the 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. 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();
+
+ // BEFORE:
+ // /---------\ next ---> /---------\
+ // ... |prev_tail| |head_tail| ...
+ // \---------/ <--- prev \---------/
+ //
+ // AFTER:
+ // /---------\ next ---> /-----\ next ---> /---------\
+ // ... |prev_tail| |entry| |head_tail| ...
+ // \---------/ <--- prev \-----/ <--- prev \---------/
+ let mut entry = NonNull::new_unchecked(entry);
+ let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry);
+ 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()
+ }
+
+ /// Pops an entry from the front of the list.
+ ///
+ /// # Safety
+ ///
+ /// The caller must make sure to synchronize ending the borrow of the
+ /// return value and deallocation of the containing entry.
+ pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> {
+ self.init();
+
+ if self.is_empty() {
+ None
+ } else {
+ // BEFORE:
+ // /---------\ next ---> /-----\ next ---> /------\
+ // ... |head_tail| |first| |second| ...
+ // \---------/ <--- prev \-----/ <--- prev \------/
+ //
+ // AFTER:
+ // /---------\ next ---> /------\
+ // ... |head_tail| |second| ...
+ // \---------/ <--- prev \------/
+ let mut first = self.head_tail.as_mut().next;
+ let mut second = first.as_mut().next;
+ self.head_tail.as_mut().next = second;
+ 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())
+ }
+ }
+
+ /// Removes an entry from the list.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `entry` has been pushed onto `self`
+ /// prior to this call and has not moved since then.
+ pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry<T>) {
+ rtassert!(!self.is_empty());
+ // BEFORE:
+ // /----\ next ---> /-----\ next ---> /----\
+ // ... |prev| |entry| |next| ...
+ // \----/ <--- prev \-----/ <--- prev \----/
+ //
+ // AFTER:
+ // /----\ next ---> /----\
+ // ... |prev| |next| ...
+ // \----/ <--- prev \----/
+ let mut prev = entry.prev;
+ let mut next = entry.next;
+ prev.as_mut().next = next;
+ next.as_mut().prev = prev;
+ entry.next = NonNull::dangling();
+ entry.prev = NonNull::dangling();
+ }
+}
diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs
new file mode 100644
index 0000000..1f031ed
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs
@@ -0,0 +1,103 @@
+use super::*;
+use crate::cell::Cell;
+
+unsafe fn assert_empty<T>(list: &mut UnsafeList<T>) {
+ assert!(list.pop().is_none(), "assertion failed: list is not empty");
+}
+
+#[test]
+fn init_empty() {
+ unsafe {
+ assert_empty(&mut UnsafeList::<i32>::new());
+ }
+}
+
+#[test]
+fn push_pop() {
+ unsafe {
+ let mut node = UnsafeListEntry::new(1234);
+ let mut list = UnsafeList::new();
+ assert_eq!(list.push(&mut node), &1234);
+ assert_eq!(list.pop().unwrap(), &1234);
+ assert_empty(&mut list);
+ }
+}
+
+#[test]
+fn push_remove() {
+ unsafe {
+ let mut node = UnsafeListEntry::new(1234);
+ let mut list = UnsafeList::new();
+ assert_eq!(list.push(&mut node), &1234);
+ list.remove(&mut node);
+ assert_empty(&mut list);
+ }
+}
+
+#[test]
+fn push_remove_pop() {
+ unsafe {
+ let mut node1 = UnsafeListEntry::new(11);
+ let mut node2 = UnsafeListEntry::new(12);
+ let mut node3 = UnsafeListEntry::new(13);
+ let mut node4 = UnsafeListEntry::new(14);
+ let mut node5 = UnsafeListEntry::new(15);
+ let mut list = UnsafeList::new();
+ assert_eq!(list.push(&mut node1), &11);
+ assert_eq!(list.push(&mut node2), &12);
+ assert_eq!(list.push(&mut node3), &13);
+ assert_eq!(list.push(&mut node4), &14);
+ assert_eq!(list.push(&mut node5), &15);
+
+ list.remove(&mut node1);
+ assert_eq!(list.pop().unwrap(), &12);
+ list.remove(&mut node3);
+ assert_eq!(list.pop().unwrap(), &14);
+ list.remove(&mut node5);
+ assert_empty(&mut list);
+
+ assert_eq!(list.push(&mut node1), &11);
+ assert_eq!(list.pop().unwrap(), &11);
+ assert_empty(&mut list);
+
+ assert_eq!(list.push(&mut node3), &13);
+ assert_eq!(list.push(&mut node4), &14);
+ list.remove(&mut node3);
+ list.remove(&mut node4);
+ assert_empty(&mut list);
+ }
+}
+
+#[test]
+fn complex_pushes_pops() {
+ unsafe {
+ let mut node1 = UnsafeListEntry::new(1234);
+ let mut node2 = UnsafeListEntry::new(4567);
+ let mut node3 = UnsafeListEntry::new(9999);
+ let mut node4 = UnsafeListEntry::new(8642);
+ let mut list = UnsafeList::new();
+ list.push(&mut node1);
+ list.push(&mut node2);
+ assert_eq!(list.pop().unwrap(), &1234);
+ list.push(&mut node3);
+ assert_eq!(list.pop().unwrap(), &4567);
+ assert_eq!(list.pop().unwrap(), &9999);
+ assert_empty(&mut list);
+ list.push(&mut node4);
+ assert_eq!(list.pop().unwrap(), &8642);
+ assert_empty(&mut list);
+ }
+}
+
+#[test]
+fn cell() {
+ unsafe {
+ let mut node = UnsafeListEntry::new(Cell::new(0));
+ let mut list = UnsafeList::new();
+ let noderef = list.push(&mut node);
+ assert_eq!(noderef.get(), 0);
+ list.pop().unwrap().set(1);
+ assert_empty(&mut list);
+ assert_eq!(noderef.get(), 1);
+ }
+}
diff --git a/library/std/src/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs
index 8e19393..964abe8 100644
--- a/library/std/src/sys/unix/alloc.rs
+++ b/library/std/src/sys/unix/alloc.rs
@@ -52,46 +52,48 @@
}
}
-#[cfg(any(
- target_os = "android",
- target_os = "illumos",
- target_os = "redox",
- target_os = "solaris"
-))]
-#[inline]
-unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
- // On android we currently target API level 9 which unfortunately
- // doesn't have the `posix_memalign` API used below. Instead we use
- // `memalign`, but this unfortunately has the property on some systems
- // where the memory returned cannot be deallocated by `free`!
- //
- // Upon closer inspection, however, this appears to work just fine with
- // Android, so for this platform we should be fine to call `memalign`
- // (which is present in API level 9). Some helpful references could
- // possibly be chromium using memalign [1], attempts at documenting that
- // memalign + free is ok [2] [3], or the current source of chromium
- // which still uses memalign on android [4].
- //
- // [1]: https://codereview.chromium.org/10796020/
- // [2]: https://code.google.com/p/android/issues/detail?id=35391
- // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
- // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
- // /memory/aligned_memory.cc
- libc::memalign(layout.align(), layout.size()) as *mut u8
-}
-
-#[cfg(not(any(
- target_os = "android",
- target_os = "illumos",
- target_os = "redox",
- target_os = "solaris"
-)))]
-#[inline]
-unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
- let mut out = ptr::null_mut();
- // 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 { out as *mut u8 }
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "redox",
+ target_os = "solaris"
+ ))] {
+ #[inline]
+ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+ // On android we currently target API level 9 which unfortunately
+ // doesn't have the `posix_memalign` API used below. Instead we use
+ // `memalign`, but this unfortunately has the property on some systems
+ // where the memory returned cannot be deallocated by `free`!
+ //
+ // Upon closer inspection, however, this appears to work just fine with
+ // Android, so for this platform we should be fine to call `memalign`
+ // (which is present in API level 9). Some helpful references could
+ // possibly be chromium using memalign [1], attempts at documenting that
+ // memalign + free is ok [2] [3], or the current source of chromium
+ // which still uses memalign on android [4].
+ //
+ // [1]: https://codereview.chromium.org/10796020/
+ // [2]: https://code.google.com/p/android/issues/detail?id=35391
+ // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
+ // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
+ // /memory/aligned_memory.cc
+ libc::memalign(layout.align(), layout.size()) as *mut u8
+ }
+ } else if #[cfg(target_os = "wasi")] {
+ #[inline]
+ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+ libc::aligned_alloc(layout.align(), layout.size()) as *mut u8
+ }
+ } else {
+ #[inline]
+ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+ let mut out = ptr::null_mut();
+ // 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 { out as *mut u8 }
+ }
+ }
}
diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs
index 9bc44a5..f7c3f16 100644
--- a/library/std/src/sys/unix/args.rs
+++ b/library/std/src/sys/unix/args.rs
@@ -80,13 +80,13 @@
use crate::ptr;
use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
- use crate::sys_common::mutex::Mutex;
+ use crate::sys_common::mutex::StaticMutex;
static ARGC: AtomicIsize = AtomicIsize::new(0);
static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
// We never call `ENV_LOCK.init()`, so it is UB to attempt to
// acquire this mutex reentrantly!
- static LOCK: Mutex = Mutex::new();
+ static LOCK: StaticMutex = StaticMutex::new();
unsafe fn really_init(argc: isize, argv: *const *const u8) {
let _guard = LOCK.lock();
diff --git a/library/std/src/sys/unix/ext/ffi.rs b/library/std/src/sys/unix/ext/ffi.rs
index 76b34a6..123f85d 100644
--- a/library/std/src/sys/unix/ext/ffi.rs
+++ b/library/std/src/sys/unix/ext/ffi.rs
@@ -1,4 +1,4 @@
-//! Unix-specific extension to the primitives in the `std::ffi` module
+//! Unix-specific extension to the primitives in the `std::ffi` module.
//!
//! # Examples
//!
diff --git a/library/std/src/sys/unix/ext/fs.rs b/library/std/src/sys/unix/ext/fs.rs
index b590a02..4b9f4ce 100644
--- a/library/std/src/sys/unix/ext/fs.rs
+++ b/library/std/src/sys/unix/ext/fs.rs
@@ -8,6 +8,9 @@
use crate::sys;
use crate::sys::platform::fs::MetadataExt as UnixMetadataExt;
use crate::sys_common::{AsInner, AsInnerMut, FromInner};
+// Used for `File::read` on intra-doc links
+#[allow(unused_imports)]
+use io::{Read, Write};
/// Unix-specific extensions to [`fs::File`].
#[stable(feature = "file_offset", since = "1.15.0")]
@@ -24,7 +27,7 @@
/// 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
+ /// [`File::read`]: fs::File::read
///
/// # Examples
///
@@ -127,7 +130,7 @@
/// Note that similar to [`File::write`], it is not an error to return a
/// short write.
///
- /// [`File::write`]: ../../../../std/fs/struct.File.html#method.write
+ /// [`File::write`]: fs::File::write
///
/// # Examples
///
@@ -425,7 +428,7 @@
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
- /// use std::io;
+ /// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
@@ -833,15 +836,6 @@
///
/// 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
diff --git a/library/std/src/sys/unix/ext/io.rs b/library/std/src/sys/unix/ext/io.rs
index 5077e2e..ef3c689 100644
--- a/library/std/src/sys/unix/ext/io.rs
+++ b/library/std/src/sys/unix/ext/io.rs
@@ -1,4 +1,4 @@
-//! Unix-specific extensions to general I/O primitives
+//! Unix-specific extensions to general I/O primitives.
#![stable(feature = "rust1", since = "1.0.0")]
@@ -25,6 +25,19 @@
/// 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.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// # use std::io;
+ /// use std::os::unix::io::{AsRawFd, RawFd};
+ ///
+ /// let mut f = File::open("foo.txt")?;
+ /// // Note that `raw_fd` is only valid as long as `f` exists.
+ /// let raw_fd: RawFd = f.as_raw_fd();
+ /// # Ok::<(), io::Error>(())
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
fn as_raw_fd(&self) -> RawFd;
}
@@ -45,6 +58,21 @@
/// 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.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// # use std::io;
+ /// use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
+ ///
+ /// let f = File::open("foo.txt")?;
+ /// let raw_fd: RawFd = f.into_raw_fd();
+ /// // SAFETY: no other functions should call `from_raw_fd`, so there
+ /// // is only one owner for the file descriptor.
+ /// let f = unsafe { File::from_raw_fd(raw_fd) };
+ /// # Ok::<(), io::Error>(())
+ /// ```
#[stable(feature = "from_raw_os", since = "1.1.0")]
unsafe fn from_raw_fd(fd: RawFd) -> Self;
}
@@ -58,10 +86,41 @@
/// 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.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// # use std::io;
+ /// use std::os::unix::io::{IntoRawFd, RawFd};
+ ///
+ /// let f = File::open("foo.txt")?;
+ /// let raw_fd: RawFd = f.into_raw_fd();
+ /// # Ok::<(), io::Error>(())
+ /// ```
#[stable(feature = "into_raw_os", since = "1.4.0")]
fn into_raw_fd(self) -> RawFd;
}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl AsRawFd for RawFd {
+ fn as_raw_fd(&self) -> RawFd {
+ *self
+ }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl IntoRawFd for RawFd {
+ fn into_raw_fd(self) -> RawFd {
+ self
+ }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl FromRawFd for RawFd {
+ unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
+ fd
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawFd for fs::File {
fn as_raw_fd(&self) -> RawFd {
diff --git a/library/std/src/sys/unix/ext/mod.rs b/library/std/src/sys/unix/ext/mod.rs
index cbdb1c1..f435468 100644
--- a/library/std/src/sys/unix/ext/mod.rs
+++ b/library/std/src/sys/unix/ext/mod.rs
@@ -37,6 +37,18 @@
pub mod raw;
pub mod thread;
+#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+pub mod ucred;
+
/// A prelude for conveniently writing platform-specific code.
///
/// Includes all extension traits, and some important type definitions.
diff --git a/library/std/src/sys/unix/ext/net.rs b/library/std/src/sys/unix/ext/net.rs
index 55803dd..3d23665 100644
--- a/library/std/src/sys/unix/ext/net.rs
+++ b/library/std/src/sys/unix/ext/net.rs
@@ -1,6 +1,9 @@
+//! Unix-specific networking functionality.
+
#![stable(feature = "unix_socket", since = "1.10.0")]
-//! Unix-specific networking functionality
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
#[cfg(not(unix))]
@@ -28,6 +31,29 @@
use crate::time::Duration;
#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+use crate::os::unix::ucred;
+
+#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+pub use ucred::UCred;
+
+#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "dragonfly",
@@ -402,6 +428,34 @@
SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
}
+ /// Gets the peer credentials for this Unix domain socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(peer_credentials_unix_socket)]
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// let peer_cred = socket.peer_cred().expect("Couldn't get peer credentials");
+ /// Ok(())
+ /// }
+ /// ```
+ #[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+ ))]
+ pub fn peer_cred(&self) -> io::Result<UCred> {
+ ucred::peer_cred(self)
+ }
+
/// Sets the read timeout for the socket.
///
/// If the provided value is [`None`], then [`read`] calls will block
@@ -591,6 +645,32 @@
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
self.0.shutdown(how)
}
+
+ /// Receives data on the socket from the remote address to which it is
+ /// connected, without removing that data from the queue. On success,
+ /// returns the number of bytes peeked.
+ ///
+ /// Successive calls return the same data. This is accomplished by passing
+ /// `MSG_PEEK` as a flag to the underlying `recv` system call.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(unix_socket_peek)]
+ ///
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// let mut buf = [0; 10];
+ /// let len = socket.peek(&mut buf).expect("peek failed");
+ /// Ok(())
+ /// }
+ /// ```
+ #[unstable(feature = "unix_socket_peek", issue = "76923")]
+ pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.peek(buf)
+ }
}
#[stable(feature = "unix_socket", since = "1.10.0")]
@@ -1288,6 +1368,33 @@
SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
}
+ fn recv_from_flags(
+ &self,
+ buf: &mut [u8],
+ flags: libc::c_int,
+ ) -> 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(),
+ flags,
+ 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 and the address from
@@ -1308,26 +1415,7 @@
/// ```
#[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))
+ self.recv_from_flags(buf, 0)
}
/// Receives data from the socket.
@@ -1598,6 +1686,64 @@
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
self.0.shutdown(how)
}
+
+ /// Receives data on the socket from the remote address to which it is
+ /// connected, without removing that data from the queue. On success,
+ /// returns the number of bytes peeked.
+ ///
+ /// Successive calls return the same data. This is accomplished by passing
+ /// `MSG_PEEK` as a flag to the underlying `recv` system call.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(unix_socket_peek)]
+ ///
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixDatagram::bind("/tmp/sock")?;
+ /// let mut buf = [0; 10];
+ /// let len = socket.peek(&mut buf).expect("peek failed");
+ /// Ok(())
+ /// }
+ /// ```
+ #[unstable(feature = "unix_socket_peek", issue = "76923")]
+ pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.peek(buf)
+ }
+
+ /// Receives a single datagram message on the socket, without removing it from the
+ /// queue. On success, returns the number of bytes read and the origin.
+ ///
+ /// The function must be called with valid byte array `buf` of sufficient size to
+ /// hold the message bytes. If a message is too long to fit in the supplied buffer,
+ /// excess bytes may be discarded.
+ ///
+ /// Successive calls return the same data. This is accomplished by passing
+ /// `MSG_PEEK` as a flag to the underlying `recvfrom` system call.
+ ///
+ /// Do not use this function to implement busy waiting, instead use `libc::poll` to
+ /// synchronize IO events on one or more sockets.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(unix_socket_peek)]
+ ///
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixDatagram::bind("/tmp/sock")?;
+ /// let mut buf = [0; 10];
+ /// let (len, addr) = socket.peek_from(&mut buf).expect("peek failed");
+ /// Ok(())
+ /// }
+ /// ```
+ #[unstable(feature = "unix_socket_peek", issue = "76923")]
+ pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.recv_from_flags(buf, libc::MSG_PEEK)
+ }
}
#[stable(feature = "unix_socket", since = "1.10.0")]
@@ -1620,382 +1766,3 @@
self.0.into_inner()
}
}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod test {
- use crate::io::prelude::*;
- use crate::io::{self, ErrorKind};
- use crate::sys_common::io::test::tmpdir;
- use crate::thread;
- use crate::time::Duration;
-
- 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/library/std/src/sys/unix/ext/net/tests.rs b/library/std/src/sys/unix/ext/net/tests.rs
new file mode 100644
index 0000000..ee73a6e
--- /dev/null
+++ b/library/std/src/sys/unix/ext/net/tests.rs
@@ -0,0 +1,454 @@
+use crate::io::prelude::*;
+use crate::io::{self, ErrorKind};
+use crate::sys_common::io::test::tmpdir;
+use crate::thread;
+use crate::time::Duration;
+
+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());
+}
+
+#[test]
+fn test_unix_stream_peek() {
+ let (txdone, rxdone) = crate::sync::mpsc::channel();
+
+ let dir = tmpdir();
+ let path = dir.path().join("sock");
+
+ let listener = or_panic!(UnixListener::bind(&path));
+ let thread = thread::spawn(move || {
+ let mut stream = or_panic!(listener.accept()).0;
+ or_panic!(stream.write_all(&[1, 3, 3, 7]));
+ or_panic!(rxdone.recv());
+ });
+
+ let mut stream = or_panic!(UnixStream::connect(&path));
+ let mut buf = [0; 10];
+ for _ in 0..2 {
+ assert_eq!(or_panic!(stream.peek(&mut buf)), 4);
+ }
+ assert_eq!(or_panic!(stream.read(&mut buf)), 4);
+
+ or_panic!(stream.set_nonblocking(true));
+ match stream.peek(&mut buf) {
+ Ok(_) => panic!("expected error"),
+ Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
+ Err(e) => panic!("unexpected error: {}", e),
+ }
+
+ or_panic!(txdone.send(()));
+ thread.join().unwrap();
+}
+
+#[test]
+fn test_unix_datagram_peek() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock");
+
+ 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));
+ for _ in 0..2 {
+ let mut buf = [0; 11];
+ let size = or_panic!(sock1.peek(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+ }
+
+ let mut buf = [0; 11];
+ let size = or_panic!(sock1.recv(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+}
+
+#[test]
+fn test_unix_datagram_peek_from() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock");
+
+ 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));
+ for _ in 0..2 {
+ let mut buf = [0; 11];
+ let (size, _) = or_panic!(sock1.peek_from(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+ }
+
+ let mut buf = [0; 11];
+ let size = or_panic!(sock1.recv(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+}
diff --git a/library/std/src/sys/unix/ext/raw.rs b/library/std/src/sys/unix/ext/raw.rs
index 40fa53d..3199a0b 100644
--- a/library/std/src/sys/unix/ext/raw.rs
+++ b/library/std/src/sys/unix/ext/raw.rs
@@ -1,4 +1,4 @@
-//! Unix-specific primitives available on all unix platforms
+//! Unix-specific primitives available on all unix platforms.
#![stable(feature = "raw_ext", since = "1.1.0")]
#![rustc_deprecated(
diff --git a/library/std/src/sys/unix/ext/ucred.rs b/library/std/src/sys/unix/ext/ucred.rs
new file mode 100644
index 0000000..ed7516c
--- /dev/null
+++ b/library/std/src/sys/unix/ext/ucred.rs
@@ -0,0 +1,97 @@
+//! Unix peer credentials.
+
+// NOTE: Code in this file is heavily based on work done in PR 13 from the tokio-uds repository on
+// GitHub.
+//
+// For reference, the link is here: https://github.com/tokio-rs/tokio-uds/pull/13
+// Credit to Martin Habovštiak (GitHub username Kixunil) and contributors for this work.
+
+use libc::{gid_t, pid_t, uid_t};
+
+/// Credentials for a UNIX process for credentials passing.
+#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct UCred {
+ /// The UID part of the peer credential. This is the effective UID of the process at the domain
+ /// socket's endpoint.
+ pub uid: uid_t,
+ /// The GID part of the peer credential. This is the effective GID of the process at the domain
+ /// socket's endpoint.
+ pub gid: gid_t,
+ /// The PID part of the peer credential. This field is optional because the PID part of the
+ /// peer credentials is not supported on every platform. On platforms where the mechanism to
+ /// discover the PID exists, this field will be populated to the PID of the process at the
+ /// domain socket's endpoint. Otherwise, it will be set to None.
+ pub pid: Option<pid_t>,
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use self::impl_linux::peer_cred;
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+pub use self::impl_bsd::peer_cred;
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub mod impl_linux {
+ use super::UCred;
+ use crate::os::unix::io::AsRawFd;
+ use crate::os::unix::net::UnixStream;
+ use crate::{io, mem};
+ use libc::{c_void, getsockopt, socklen_t, ucred, SOL_SOCKET, SO_PEERCRED};
+
+ pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
+ let ucred_size = mem::size_of::<ucred>();
+
+ // Trivial sanity checks.
+ assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
+ assert!(ucred_size <= u32::MAX as usize);
+
+ let mut ucred_size = ucred_size as socklen_t;
+ let mut ucred: ucred = ucred { pid: 1, uid: 1, gid: 1 };
+
+ unsafe {
+ let ret = getsockopt(
+ socket.as_raw_fd(),
+ SOL_SOCKET,
+ SO_PEERCRED,
+ &mut ucred as *mut ucred as *mut c_void,
+ &mut ucred_size,
+ );
+
+ if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
+ Ok(UCred { uid: ucred.uid, gid: ucred.gid, pid: Some(ucred.pid) })
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "openbsd"
+))]
+pub mod impl_bsd {
+ use super::UCred;
+ use crate::io;
+ use crate::os::unix::io::AsRawFd;
+ use crate::os::unix::net::UnixStream;
+
+ pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
+ let mut cred = UCred { uid: 1, gid: 1, pid: None };
+ unsafe {
+ let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid);
+
+ if ret == 0 { Ok(cred) } else { Err(io::Error::last_os_error()) }
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/ext/ucred/tests.rs b/library/std/src/sys/unix/ext/ucred/tests.rs
new file mode 100644
index 0000000..451b534
--- /dev/null
+++ b/library/std/src/sys/unix/ext/ucred/tests.rs
@@ -0,0 +1,25 @@
+use crate::os::unix::net::UnixStream;
+use libc::{getegid, geteuid};
+
+#[test]
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+fn test_socket_pair() {
+ // Create two connected sockets and get their peer credentials. They should be equal.
+ let (sock_a, sock_b) = UnixStream::pair().unwrap();
+ let (cred_a, cred_b) = (sock_a.peer_cred().unwrap(), sock_b.peer_cred().unwrap());
+ assert_eq!(cred_a, cred_b);
+
+ // Check that the UID and GIDs match up.
+ let uid = unsafe { geteuid() };
+ let gid = unsafe { getegid() };
+ assert_eq!(cred_a.uid, uid);
+ assert_eq!(cred_a.gid, gid);
+}
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index ba169b2..2224a05 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -1,10 +1,11 @@
#![unstable(reason = "not public", issue = "none", feature = "fd")]
+#[cfg(test)]
+mod tests;
+
use crate::cmp;
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
use crate::mem;
-#[cfg(not(any(target_os = "redox", target_env = "newlib")))]
-use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::cvt;
use crate::sys_common::AsInner;
@@ -28,24 +29,35 @@
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
-#[cfg(not(any(target_os = "redox", target_env = "newlib")))]
-fn max_iov() -> usize {
- static LIM: AtomicUsize = AtomicUsize::new(0);
-
- let mut lim = LIM.load(Ordering::Relaxed);
- if lim == 0 {
- let ret = unsafe { libc::sysconf(libc::_SC_IOV_MAX) };
-
- // 16 is the minimum value required by POSIX.
- lim = if ret > 0 { ret as usize } else { 16 };
- LIM.store(lim, Ordering::Relaxed);
- }
-
- lim
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+const fn max_iov() -> usize {
+ libc::IOV_MAX as usize
}
-#[cfg(any(target_os = "redox", target_env = "newlib"))]
-fn max_iov() -> usize {
+#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
+const fn max_iov() -> usize {
+ libc::UIO_MAXIOV as usize
+}
+
+#[cfg(not(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+)))]
+const fn max_iov() -> usize {
16 // The minimum value required by POSIX.
}
@@ -279,16 +291,3 @@
let _ = unsafe { libc::close(self.fd) };
}
}
-
-#[cfg(test)]
-mod tests {
- use super::{FileDesc, IoSlice};
- use core::mem::ManuallyDrop;
-
- #[test]
- fn limit_vector_count() {
- let stdout = ManuallyDrop::new(FileDesc { fd: 1 });
- let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
- assert!(stdout.write_vectored(&bufs).is_ok());
- }
-}
diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs
new file mode 100644
index 0000000..a932043
--- /dev/null
+++ b/library/std/src/sys/unix/fd/tests.rs
@@ -0,0 +1,9 @@
+use super::{FileDesc, IoSlice};
+use core::mem::ManuallyDrop;
+
+#[test]
+fn limit_vector_count() {
+ let stdout = ManuallyDrop::new(FileDesc { fd: 1 });
+ let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
+ assert!(stdout.write_vectored(&bufs).is_ok());
+}
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index acb18e6..0185826 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -1126,7 +1126,7 @@
// Kernel prior to 4.5 don't have copy_file_range
// We store the availability in a global to avoid unnecessary syscalls
- static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true);
+ static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(false);
unsafe fn copy_file_range(
fd_in: libc::c_int,
@@ -1140,14 +1140,14 @@
}
let (mut reader, reader_metadata) = open_from(from)?;
- let len = reader_metadata.len();
+ let max_len = u64::MAX;
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;
- while written < len {
+ while written < max_len {
let copy_result = if has_copy_file_range {
- let bytes_to_copy = cmp::min(len - written, usize::MAX as u64) as usize;
+ let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64) as usize;
let copy_result = unsafe {
// We actually don't have to adjust the offsets,
// because copy_file_range adjusts the file offset automatically
@@ -1162,7 +1162,7 @@
};
if let Err(ref copy_err) = copy_result {
match copy_err.raw_os_error() {
- Some(libc::ENOSYS) | Some(libc::EPERM) => {
+ Some(libc::ENOSYS | libc::EPERM | libc::EOPNOTSUPP) => {
HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
}
_ => {}
@@ -1173,18 +1173,25 @@
Err(io::Error::from_raw_os_error(libc::ENOSYS))
};
match copy_result {
+ Ok(0) if written == 0 => {
+ // fallback to work around several kernel bugs where copy_file_range will fail to
+ // copy any bytes and return 0 instead of an error if
+ // - reading virtual files from the proc filesystem which appear to have 0 size
+ // but are not empty. noted in coreutils to affect kernels at least up to 5.6.19.
+ // - copying from an overlay filesystem in docker. reported to occur on fedora 32.
+ return io::copy(&mut reader, &mut writer);
+ }
+ Ok(0) => return Ok(written), // reached EOF
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::EINVAL
- || os_err == libc::EPERM =>
- {
+ Some(
+ libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP,
+ ) => {
// Try fallback io::copy if either:
// - Kernel version is < 4.5 (ENOSYS)
// - Files are mounted on different fs (EXDEV)
+ // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
// - 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);
diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs
new file mode 100644
index 0000000..e6f0c48
--- /dev/null
+++ b/library/std/src/sys/unix/futex.rs
@@ -0,0 +1,37 @@
+#![cfg(any(target_os = "linux", target_os = "android"))]
+
+use crate::convert::TryInto;
+use crate::ptr::null;
+use crate::sync::atomic::AtomicI32;
+use crate::time::Duration;
+
+pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) {
+ let timespec = timeout.and_then(|d| {
+ Some(libc::timespec {
+ // Sleep forever if the timeout is longer than fits in a timespec.
+ tv_sec: d.as_secs().try_into().ok()?,
+ // This conversion never truncates, as subsec_nanos is always <1e9.
+ tv_nsec: d.subsec_nanos() as _,
+ })
+ });
+ unsafe {
+ libc::syscall(
+ libc::SYS_futex,
+ futex as *const AtomicI32,
+ libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG,
+ expected,
+ timespec.as_ref().map_or(null(), |d| d as *const libc::timespec),
+ );
+ }
+}
+
+pub fn futex_wake(futex: &AtomicI32) {
+ unsafe {
+ libc::syscall(
+ libc::SYS_futex,
+ futex as *const AtomicI32,
+ libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
+ 1,
+ );
+ }
+}
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index eddf00d..776f4f1 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -49,6 +49,7 @@
pub mod ext;
pub mod fd;
pub mod fs;
+pub mod futex;
pub mod io;
#[cfg(target_os = "l4re")]
mod l4re;
@@ -75,6 +76,13 @@
#[cfg(not(test))]
pub fn init() {
+ // The standard streams might be closed on application startup. To prevent
+ // std::io::{stdin, stdout,stderr} objects from using other unrelated file
+ // resources opened later, we reopen standards streams when they are closed.
+ unsafe {
+ sanitize_standard_fds();
+ }
+
// 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
@@ -86,6 +94,62 @@
reset_sigpipe();
}
+ cfg_if::cfg_if! {
+ if #[cfg(miri)] {
+ // The standard fds are always available in Miri.
+ unsafe fn sanitize_standard_fds() {}
+ } else if #[cfg(not(any(
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ // The poll on Darwin doesn't set POLLNVAL for closed fds.
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "redox",
+ )))] {
+ // In the case when all file descriptors are open, the poll has been
+ // observed to perform better than fcntl (on GNU/Linux).
+ unsafe fn sanitize_standard_fds() {
+ use crate::sys::os::errno;
+ let pfds: &mut [_] = &mut [
+ libc::pollfd { fd: 0, events: 0, revents: 0 },
+ libc::pollfd { fd: 1, events: 0, revents: 0 },
+ libc::pollfd { fd: 2, events: 0, revents: 0 },
+ ];
+ while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 {
+ if errno() == libc::EINTR {
+ continue;
+ }
+ libc::abort();
+ }
+ for pfd in pfds {
+ if pfd.revents & libc::POLLNVAL == 0 {
+ continue;
+ }
+ if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
+ // If the stream is closed but we failed to reopen it, abort the
+ // process. Otherwise we wouldn't preserve the safety of
+ // operations on the corresponding Rust object Stdin, Stdout, or
+ // Stderr.
+ libc::abort();
+ }
+ }
+ }
+ } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
+ unsafe fn sanitize_standard_fds() {
+ use crate::sys::os::errno;
+ for fd in 0..3 {
+ if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
+ if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
+ libc::abort();
+ }
+ }
+ }
+ }
+ } else {
+ unsafe fn sanitize_standard_fds() {}
+ }
+ }
+
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
unsafe fn reset_sigpipe() {
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 2fcb5b9..c9f9ed0 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -2,6 +2,9 @@
#![allow(unused_imports)] // lots of cfg code here
+#[cfg(all(test, target_env = "gnu"))]
+mod tests;
+
use crate::os::unix::prelude::*;
use crate::error::Error as StdError;
@@ -18,7 +21,7 @@
use crate::str;
use crate::sys::cvt;
use crate::sys::fd;
-use crate::sys_common::mutex::{Mutex, MutexGuard};
+use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard};
use crate::vec;
use libc::{c_char, c_int, c_void};
@@ -467,10 +470,9 @@
&mut environ
}
-pub unsafe fn env_lock() -> MutexGuard<'static> {
- // We never call `ENV_LOCK.init()`, so it is UB to attempt to
- // acquire this mutex reentrantly!
- static ENV_LOCK: Mutex = Mutex::new();
+pub unsafe fn env_lock() -> StaticMutexGuard<'static> {
+ // It is UB to attempt to acquire this mutex reentrantly!
+ static ENV_LOCK: StaticMutex = StaticMutex::new();
ENV_LOCK.lock()
}
@@ -645,30 +647,3 @@
_ => None,
}
}
-
-#[cfg(all(test, target_env = "gnu"))]
-mod test {
- use super::*;
-
- #[test]
- fn test_glibc_version() {
- // This mostly just tests that the weak linkage doesn't panic wildly...
- glibc_version();
- }
-
- #[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/library/std/src/sys/unix/os/tests.rs b/library/std/src/sys/unix/os/tests.rs
new file mode 100644
index 0000000..0e1dcb3
--- /dev/null
+++ b/library/std/src/sys/unix/os/tests.rs
@@ -0,0 +1,23 @@
+use super::*;
+
+#[test]
+fn test_glibc_version() {
+ // This mostly just tests that the weak linkage doesn't panic wildly...
+ glibc_version();
+}
+
+#[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/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs
index 553e980..1b7b93f 100644
--- a/library/std/src/sys/unix/process/mod.rs
+++ b/library/std/src/sys/unix/process/mod.rs
@@ -1,6 +1,7 @@
-pub use self::process_common::{Command, ExitCode, Stdio, StdioPipes};
+pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
pub use self::process_inner::{ExitStatus, Process};
pub use crate::ffi::OsString as EnvKey;
+pub use crate::sys_common::process::CommandEnvs;
mod process_common;
#[cfg(not(target_os = "fuchsia"))]
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index 6e33cdd..9ddd4ad 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -1,14 +1,18 @@
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
use crate::os::unix::prelude::*;
use crate::collections::BTreeMap;
use crate::ffi::{CStr, CString, OsStr, OsString};
use crate::fmt;
use crate::io;
+use crate::path::Path;
use crate::ptr;
use crate::sys::fd::FileDesc;
use crate::sys::fs::File;
use crate::sys::pipe::{self, AnonPipe};
-use crate::sys_common::process::CommandEnv;
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
#[cfg(not(target_os = "fuchsia"))]
use crate::sys::fs::OpenOptions;
@@ -181,11 +185,30 @@
pub fn saw_nul(&self) -> bool {
self.saw_nul
}
+
+ pub fn get_program(&self) -> &OsStr {
+ OsStr::from_bytes(self.program.as_bytes())
+ }
+
+ pub fn get_args(&self) -> CommandArgs<'_> {
+ let mut iter = self.args.iter();
+ iter.next();
+ CommandArgs { iter }
+ }
+
+ pub fn get_envs(&self) -> CommandEnvs<'_> {
+ self.env.iter()
+ }
+
+ pub fn get_current_dir(&self) -> Option<&Path> {
+ self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes())))
+ }
+
pub fn get_argv(&self) -> &Vec<*const c_char> {
&self.argv.0
}
- pub fn get_program(&self) -> &CStr {
+ pub fn get_program_cstr(&self) -> &CStr {
&*self.program
}
@@ -400,70 +423,31 @@
}
}
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use super::*;
+pub struct CommandArgs<'a> {
+ iter: crate::slice::Iter<'a, CString>,
+}
- use crate::ffi::OsStr;
- use crate::mem;
- use crate::ptr;
- use crate::sys::cvt;
-
- macro_rules! t {
- ($e:expr) => {
- match $e {
- Ok(t) => t,
- Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
- }
- };
+impl<'a> Iterator for CommandArgs<'a> {
+ type Item = &'a OsStr;
+ fn next(&mut self) -> Option<&'a OsStr> {
+ self.iter.next().map(|cs| OsStr::from_bytes(cs.as_bytes()))
}
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
- // See #14232 for more information, but it appears that signal delivery to a
- // newly spawned process may just be raced in the macOS, so to prevent this
- // test from being flaky we ignore it on macOS.
- #[test]
- #[cfg_attr(target_os = "macos", ignore)]
- // When run under our current QEMU emulation test suite this test fails,
- // although the reason isn't very clear as to why. For now this test is
- // ignored there.
- #[cfg_attr(target_arch = "arm", ignore)]
- #[cfg_attr(target_arch = "aarch64", ignore)]
- #[cfg_attr(target_arch = "riscv64", ignore)]
- fn test_process_mask() {
- unsafe {
- // Test to make sure that a signal mask does not get inherited.
- let mut cmd = Command::new(OsStr::new("cat"));
+impl<'a> ExactSizeIterator for CommandArgs<'a> {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
+}
- 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);
-
- let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
- let stdin_write = pipes.stdin.take().unwrap();
- let stdout_read = pipes.stdout.take().unwrap();
-
- 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)));
- // We need to wait until SIGINT is definitely delivered. The
- // easiest way is to write something to cat, and try to read it
- // back: if SIGINT is unmasked, it'll get delivered when cat is
- // next scheduled.
- let _ = stdin_write.write(b"Hello");
- drop(stdin_write);
-
- // Either EOF or failure (EPIPE) is okay.
- let mut buf = [0; 5];
- if let Ok(ret) = stdout_read.read(&mut buf) {
- assert_eq!(ret, 0);
- }
-
- t!(cat.wait());
- }
+impl<'a> fmt::Debug for CommandArgs<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(self.iter.clone()).finish()
}
}
diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs
new file mode 100644
index 0000000..e72fbf0
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_common/tests.rs
@@ -0,0 +1,64 @@
+use super::*;
+
+use crate::ffi::OsStr;
+use crate::mem;
+use crate::ptr;
+use crate::sys::cvt;
+
+macro_rules! t {
+ ($e:expr) => {
+ match $e {
+ Ok(t) => t,
+ Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
+ }
+ };
+}
+
+// See #14232 for more information, but it appears that signal delivery to a
+// newly spawned process may just be raced in the macOS, so to prevent this
+// test from being flaky we ignore it on macOS.
+#[test]
+#[cfg_attr(target_os = "macos", ignore)]
+// When run under our current QEMU emulation test suite this test fails,
+// although the reason isn't very clear as to why. For now this test is
+// ignored there.
+#[cfg_attr(target_arch = "arm", ignore)]
+#[cfg_attr(target_arch = "aarch64", ignore)]
+#[cfg_attr(target_arch = "riscv64", ignore)]
+fn test_process_mask() {
+ unsafe {
+ // Test to make sure that a signal mask does not get inherited.
+ let mut cmd = Command::new(OsStr::new("cat"));
+
+ 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);
+
+ let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
+ let stdin_write = pipes.stdin.take().unwrap();
+ let stdout_read = pipes.stdout.take().unwrap();
+
+ 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)));
+ // We need to wait until SIGINT is definitely delivered. The
+ // easiest way is to write something to cat, and try to read it
+ // back: if SIGINT is unmasked, it'll get delivered when cat is
+ // next scheduled.
+ let _ = stdin_write.write(b"Hello");
+ drop(stdin_write);
+
+ // Either EOF or failure (EPIPE) is okay.
+ let mut buf = [0; 5];
+ if let Ok(ret) = stdout_read.read(&mut buf) {
+ assert_eq!(ret, 0);
+ }
+
+ t!(cat.wait());
+ }
+}
diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs
index 6daf288..b64636c 100644
--- a/library/std/src/sys/unix/process/process_fuchsia.rs
+++ b/library/std/src/sys/unix/process/process_fuchsia.rs
@@ -118,8 +118,9 @@
FDIO_SPAWN_CLONE_JOB
| FDIO_SPAWN_CLONE_LDSVC
| FDIO_SPAWN_CLONE_NAMESPACE
- | FDIO_SPAWN_CLONE_ENVIRON, // this is ignored when envp is non-null
- self.get_program().as_ptr(),
+ | FDIO_SPAWN_CLONE_ENVIRON // this is ignored when envp is non-null
+ | FDIO_SPAWN_CLONE_UTC_CLOCK,
+ self.get_program_cstr().as_ptr(),
self.get_argv().as_ptr(),
envp,
actions.len() as size_t,
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 0f349df..32f4562 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -67,7 +67,7 @@
// 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());
+ rtassert!(output.write(&bytes).is_ok());
libc::_exit(1)
}
n => n,
@@ -245,14 +245,15 @@
*sys::os::environ() = envp.as_ptr();
}
- libc::execvp(self.get_program().as_ptr(), self.get_argv().as_ptr());
+ libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
Err(io::Error::last_os_error())
}
#[cfg(not(any(
target_os = "macos",
target_os = "freebsd",
- all(target_os = "linux", target_env = "gnu")
+ all(target_os = "linux", target_env = "gnu"),
+ all(target_os = "linux", target_env = "musl"),
)))]
fn posix_spawn(
&mut self,
@@ -267,7 +268,8 @@
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
- all(target_os = "linux", target_env = "gnu")
+ all(target_os = "linux", target_env = "gnu"),
+ all(target_os = "linux", target_env = "musl"),
))]
fn posix_spawn(
&mut self,
@@ -297,10 +299,10 @@
}
}
- // Solaris and glibc 2.29+ can set a new working directory, and maybe
- // others will gain this non-POSIX function too. We'll check for this
- // weak symbol as soon as it's needed, so we can return early otherwise
- // to do a manual chdir before exec.
+ // Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory,
+ // and maybe others will gain this non-POSIX function too. We'll check
+ // for this weak symbol as soon as it's needed, so we can return early
+ // otherwise to do a manual chdir before exec.
weak! {
fn posix_spawn_file_actions_addchdir_np(
*mut libc::posix_spawn_file_actions_t,
@@ -383,7 +385,7 @@
let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
let ret = libc::posix_spawnp(
&mut p.pid,
- self.get_program().as_ptr(),
+ self.get_program_cstr().as_ptr(),
file_actions.0.as_ptr(),
attrs.0.as_ptr(),
self.get_argv().as_ptr() as *const _,
@@ -459,7 +461,15 @@
}
fn exited(&self) -> bool {
- unsafe { libc::WIFEXITED(self.0) }
+ // On Linux-like OSes this function is safe, on others it is not. See
+ // libc issue: https://github.com/rust-lang/libc/issues/1888.
+ #[cfg_attr(
+ any(target_os = "linux", target_os = "android", target_os = "emscripten"),
+ allow(unused_unsafe)
+ )]
+ unsafe {
+ libc::WIFEXITED(self.0)
+ }
}
pub fn success(&self) -> bool {
@@ -467,10 +477,22 @@
}
pub fn code(&self) -> Option<i32> {
+ // On Linux-like OSes this function is safe, on others it is not. See
+ // libc issue: https://github.com/rust-lang/libc/issues/1888.
+ #[cfg_attr(
+ any(target_os = "linux", target_os = "android", target_os = "emscripten"),
+ allow(unused_unsafe)
+ )]
if self.exited() { Some(unsafe { libc::WEXITSTATUS(self.0) }) } else { None }
}
pub fn signal(&self) -> Option<i32> {
+ // On Linux-like OSes this function is safe, on others it is not. See
+ // libc issue: https://github.com/rust-lang/libc/issues/1888.
+ #[cfg_attr(
+ any(target_os = "linux", target_os = "android", target_os = "emscripten"),
+ allow(unused_unsafe)
+ )]
if !self.exited() { Some(unsafe { libc::WTERMSIG(self.0) }) } else { None }
}
}
diff --git a/library/std/src/sys/unix/process/zircon.rs b/library/std/src/sys/unix/process/zircon.rs
index 750b8f0..69ec275 100644
--- a/library/std/src/sys/unix/process/zircon.rs
+++ b/library/std/src/sys/unix/process/zircon.rs
@@ -138,6 +138,7 @@
pub const FDIO_SPAWN_CLONE_NAMESPACE: u32 = 0x0004;
pub const FDIO_SPAWN_CLONE_STDIO: u32 = 0x0008;
pub const FDIO_SPAWN_CLONE_ENVIRON: u32 = 0x0010;
+pub const FDIO_SPAWN_CLONE_UTC_CLOCK: u32 = 0x0020;
pub const FDIO_SPAWN_CLONE_ALL: u32 = 0xFFFF;
// fdio_spawn_etc actions
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 04da981..652219e 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -294,6 +294,7 @@
unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
let mut ret = None;
let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
+ #[cfg(target_os = "freebsd")]
assert_eq!(libc::pthread_attr_init(&mut attr), 0);
#[cfg(target_os = "freebsd")]
let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
@@ -305,7 +306,9 @@
assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0);
ret = Some(stackaddr);
}
- assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+ if e == 0 || cfg!(target_os = "freebsd") {
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+ }
ret
}
@@ -403,6 +406,7 @@
pub unsafe fn current() -> Option<Guard> {
let mut ret = None;
let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
+ #[cfg(target_os = "freebsd")]
assert_eq!(libc::pthread_attr_init(&mut attr), 0);
#[cfg(target_os = "freebsd")]
let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
@@ -446,7 +450,9 @@
Some(stackaddr..stackaddr + guardsize)
};
}
- assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+ if e == 0 || cfg!(target_os = "freebsd") {
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+ }
ret
}
}
diff --git a/library/std/src/sys/unsupported/fs.rs b/library/std/src/sys/unsupported/fs.rs
index ecb5b51..faa53b6 100644
--- a/library/std/src/sys/unsupported/fs.rs
+++ b/library/std/src/sys/unsupported/fs.rs
@@ -233,10 +233,6 @@
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
match self.0 {}
}
-
- pub fn diverge(&self) -> ! {
- match self.0 {}
- }
}
impl DirBuilder {
diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs
index 87f655e..8ba870c 100644
--- a/library/std/src/sys/unsupported/mod.rs
+++ b/library/std/src/sys/unsupported/mod.rs
@@ -8,6 +8,7 @@
pub mod mutex;
pub mod net;
pub mod os;
+#[path = "../unix/path.rs"]
pub mod path;
pub mod pipe;
pub mod process;
diff --git a/library/std/src/sys/unsupported/path.rs b/library/std/src/sys/unsupported/path.rs
deleted file mode 100644
index 840a7ae..0000000
--- a/library/std/src/sys/unsupported/path.rs
+++ /dev/null
@@ -1,19 +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(_: &OsStr) -> Option<Prefix<'_>> {
- None
-}
-
-pub const MAIN_SEP_STR: &str = "/";
-pub const MAIN_SEP: char = '/';
diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs
index 4702e5c..3ede229 100644
--- a/library/std/src/sys/unsupported/process.rs
+++ b/library/std/src/sys/unsupported/process.rs
@@ -1,10 +1,12 @@
use crate::ffi::OsStr;
use crate::fmt;
use crate::io;
+use crate::marker::PhantomData;
+use crate::path::Path;
use crate::sys::fs::File;
use crate::sys::pipe::AnonPipe;
use crate::sys::{unsupported, Void};
-use crate::sys_common::process::CommandEnv;
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
pub use crate::ffi::OsString as EnvKey;
@@ -49,6 +51,22 @@
pub fn stderr(&mut self, _stderr: Stdio) {}
+ pub fn get_program(&self) -> &OsStr {
+ panic!("unsupported")
+ }
+
+ pub fn get_args(&self) -> CommandArgs<'_> {
+ CommandArgs { _p: PhantomData }
+ }
+
+ pub fn get_envs(&self) -> CommandEnvs<'_> {
+ self.env.iter()
+ }
+
+ pub fn get_current_dir(&self) -> Option<&Path> {
+ None
+ }
+
pub fn spawn(
&mut self,
_default: Stdio,
@@ -65,8 +83,8 @@
}
impl From<File> for Stdio {
- fn from(file: File) -> Stdio {
- file.diverge()
+ fn from(_file: File) -> Stdio {
+ panic!("unsupported")
}
}
@@ -147,3 +165,22 @@
match self.0 {}
}
}
+
+pub struct CommandArgs<'a> {
+ _p: PhantomData<&'a ()>,
+}
+
+impl<'a> Iterator for CommandArgs<'a> {
+ type Item = &'a OsStr;
+ fn next(&mut self) -> Option<&'a OsStr> {
+ None
+ }
+}
+
+impl<'a> ExactSizeIterator for CommandArgs<'a> {}
+
+impl<'a> fmt::Debug for CommandArgs<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().finish()
+ }
+}
diff --git a/library/std/src/sys/vxworks/args.rs b/library/std/src/sys/vxworks/args.rs
index adff6c4..30cf7a7 100644
--- a/library/std/src/sys/vxworks/args.rs
+++ b/library/std/src/sys/vxworks/args.rs
@@ -57,11 +57,11 @@
use crate::marker::PhantomData;
use crate::ptr;
- use crate::sys_common::mutex::Mutex;
+ use crate::sys_common::mutex::StaticMutex;
static mut ARGC: isize = 0;
static mut ARGV: *const *const u8 = ptr::null();
- static LOCK: Mutex = Mutex::new();
+ static LOCK: StaticMutex = StaticMutex::new();
pub unsafe fn init(argc: isize, argv: *const *const u8) {
let _guard = LOCK.lock();
diff --git a/library/std/src/sys/vxworks/ext/fs.rs b/library/std/src/sys/vxworks/ext/fs.rs
index 4ff86da..68dc21b 100644
--- a/library/std/src/sys/vxworks/ext/fs.rs
+++ b/library/std/src/sys/vxworks/ext/fs.rs
@@ -427,7 +427,7 @@
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
- /// use std::io;
+ /// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
@@ -774,15 +774,6 @@
///
/// 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
diff --git a/library/std/src/sys/vxworks/ext/io.rs b/library/std/src/sys/vxworks/ext/io.rs
index 25c6e26..8b5a2d1 100644
--- a/library/std/src/sys/vxworks/ext/io.rs
+++ b/library/std/src/sys/vxworks/ext/io.rs
@@ -63,6 +63,25 @@
fn into_raw_fd(self) -> RawFd;
}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl AsRawFd for RawFd {
+ fn as_raw_fd(&self) -> RawFd {
+ *self
+ }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl IntoRawFd for RawFd {
+ fn into_raw_fd(self) -> RawFd {
+ self
+ }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl FromRawFd for RawFd {
+ unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
+ fd
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawFd for fs::File {
fn as_raw_fd(&self) -> RawFd {
diff --git a/library/std/src/sys/vxworks/fd.rs b/library/std/src/sys/vxworks/fd.rs
index ea18684..d58468a 100644
--- a/library/std/src/sys/vxworks/fd.rs
+++ b/library/std/src/sys/vxworks/fd.rs
@@ -53,7 +53,7 @@
}
#[inline]
- fn is_read_vectored(&self) -> bool {
+ pub fn is_read_vectored(&self) -> bool {
true
}
diff --git a/library/std/src/sys/vxworks/net.rs b/library/std/src/sys/vxworks/net.rs
index 32c27ab..7613fbe 100644
--- a/library/std/src/sys/vxworks/net.rs
+++ b/library/std/src/sys/vxworks/net.rs
@@ -1,3 +1,6 @@
+#[cfg(all(test, taget_env = "gnu"))]
+mod tests;
+
use crate::cmp;
use crate::ffi::CStr;
use crate::io;
@@ -330,30 +333,3 @@
#[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/library/std/src/sys/vxworks/net/tests.rs b/library/std/src/sys/vxworks/net/tests.rs
new file mode 100644
index 0000000..e7c6e34
--- /dev/null
+++ b/library/std/src/sys/vxworks/net/tests.rs
@@ -0,0 +1,23 @@
+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/library/std/src/sys/vxworks/os.rs b/library/std/src/sys/vxworks/os.rs
index 1fadf71..08394a8 100644
--- a/library/std/src/sys/vxworks/os.rs
+++ b/library/std/src/sys/vxworks/os.rs
@@ -10,7 +10,7 @@
use crate::slice;
use crate::str;
use crate::sys::cvt;
-use crate::sys_common::mutex::{Mutex, MutexGuard};
+use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard};
use libc::{self, c_char /*,c_void */, c_int};
/*use sys::fd; this one is probably important */
use crate::vec;
@@ -212,10 +212,9 @@
&mut environ
}
-pub unsafe fn env_lock() -> MutexGuard<'static> {
- // We never call `ENV_LOCK.init()`, so it is UB to attempt to
- // acquire this mutex reentrantly!
- static ENV_LOCK: Mutex = Mutex::new();
+pub unsafe fn env_lock() -> StaticMutexGuard<'static> {
+ // It is UB to attempt to acquire this mutex reentrantly!
+ static ENV_LOCK: StaticMutex = StaticMutex::new();
ENV_LOCK.lock()
}
diff --git a/library/std/src/sys/vxworks/process/process_common.rs b/library/std/src/sys/vxworks/process/process_common.rs
index bbbd5ed..6473a0c 100644
--- a/library/std/src/sys/vxworks/process/process_common.rs
+++ b/library/std/src/sys/vxworks/process/process_common.rs
@@ -351,8 +351,7 @@
}
fn exited(&self) -> bool {
- /*unsafe*/
- { libc::WIFEXITED(self.0) }
+ libc::WIFEXITED(self.0)
}
pub fn success(&self) -> bool {
@@ -360,19 +359,11 @@
}
pub fn code(&self) -> Option<i32> {
- if self.exited() {
- Some(/*unsafe*/ { libc::WEXITSTATUS(self.0) })
- } else {
- None
- }
+ if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
}
pub fn signal(&self) -> Option<i32> {
- if !self.exited() {
- Some(/*unsafe*/ { libc::WTERMSIG(self.0) })
- } else {
- None
- }
+ if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
}
}
diff --git a/library/std/src/sys/vxworks/thread_local_dtor.rs b/library/std/src/sys/vxworks/thread_local_dtor.rs
index 3f73f6c..5391ed8 100644
--- a/library/std/src/sys/vxworks/thread_local_dtor.rs
+++ b/library/std/src/sys/vxworks/thread_local_dtor.rs
@@ -2,6 +2,6 @@
#![unstable(feature = "thread_local_internals", issue = "none")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- use crate::sys_common::thread_local::register_dtor_fallback;
+ use crate::sys_common::thread_local_dtor::register_dtor_fallback;
register_dtor_fallback(t, dtor);
}
diff --git a/library/std/src/sys/wasi/alloc.rs b/library/std/src/sys/wasi/alloc.rs
deleted file mode 100644
index 5718785..0000000
--- a/library/std/src/sys/wasi/alloc.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use crate::alloc::{GlobalAlloc, Layout, System};
-use crate::ptr;
-use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN};
-
-#[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.align(), layout.size()) 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/library/std/src/sys/wasi/args.rs b/library/std/src/sys/wasi/args.rs
index 02aa68d..9a27218 100644
--- a/library/std/src/sys/wasi/args.rs
+++ b/library/std/src/sys/wasi/args.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
use crate::ffi::{CStr, OsStr, OsString};
use crate::marker::PhantomData;
use crate::os::wasi::ffi::OsStrExt;
diff --git a/library/std/src/sys/wasi/ext/fs.rs b/library/std/src/sys/wasi/ext/fs.rs
index f41c662..4f7cf60 100644
--- a/library/std/src/sys/wasi/ext/fs.rs
+++ b/library/std/src/sys/wasi/ext/fs.rs
@@ -1,5 +1,6 @@
//! WASI-specific extensions to primitives in the `std::fs` module.
+#![deny(unsafe_op_in_unsafe_fn)]
#![unstable(feature = "wasi_ext", issue = "none")]
use crate::fs::{self, File, Metadata, OpenOptions};
@@ -9,8 +10,6 @@
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.
///
@@ -23,8 +22,6 @@
///
/// 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
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
let bufs = &mut [IoSliceMut::new(buf)];
self.read_vectored_at(bufs, offset)
@@ -41,8 +38,6 @@
///
/// 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_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize>;
/// Reads the exact number of byte required to fill `buf` from the given offset.
@@ -54,8 +49,7 @@
///
/// 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
+ /// [`read_at`]: FileExt::read_at
///
/// # Errors
///
@@ -73,9 +67,6 @@
/// 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
#[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() {
@@ -111,8 +102,6 @@
///
/// 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
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
let bufs = &[IoSlice::new(buf)];
self.write_vectored_at(bufs, offset)
@@ -132,8 +121,6 @@
///
/// 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_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>;
/// Attempts to write an entire buffer starting from a given offset.
@@ -155,8 +142,7 @@
/// 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
+ /// [`write_at`]: FileExt::write_at
#[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() {
@@ -289,8 +275,6 @@
}
/// WASI-specific extensions to [`fs::OpenOptions`].
-///
-/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html
pub trait OpenOptionsExt {
/// Pass custom `dirflags` argument to `path_open`.
///
@@ -406,8 +390,6 @@
}
/// WASI-specific extensions to [`fs::Metadata`].
-///
-/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
pub trait MetadataExt {
/// Returns the `st_dev` field of the internal `filestat_t`
fn dev(&self) -> u64;
@@ -448,8 +430,6 @@
///
/// 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;
@@ -477,8 +457,6 @@
}
/// 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 `dirent_t`
fn ino(&self) -> u64;
diff --git a/library/std/src/sys/wasi/ext/io.rs b/library/std/src/sys/wasi/ext/io.rs
index e849400..661214e 100644
--- a/library/std/src/sys/wasi/ext/io.rs
+++ b/library/std/src/sys/wasi/ext/io.rs
@@ -1,5 +1,6 @@
//! WASI-specific extensions to general I/O primitives
+#![deny(unsafe_op_in_unsafe_fn)]
#![unstable(feature = "wasi_ext", issue = "none")]
use crate::fs;
@@ -51,6 +52,25 @@
fn into_raw_fd(self) -> RawFd;
}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl AsRawFd for RawFd {
+ fn as_raw_fd(&self) -> RawFd {
+ *self
+ }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl IntoRawFd for RawFd {
+ fn into_raw_fd(self) -> RawFd {
+ self
+ }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl FromRawFd for RawFd {
+ unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
+ fd
+ }
+}
+
impl AsRawFd for net::TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().as_raw()
diff --git a/library/std/src/sys/wasi/ext/mod.rs b/library/std/src/sys/wasi/ext/mod.rs
index 58c8c46..1cda30e 100644
--- a/library/std/src/sys/wasi/ext/mod.rs
+++ b/library/std/src/sys/wasi/ext/mod.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
pub mod ffi;
pub mod fs;
pub mod io;
diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs
index 8458ded..ba66eba 100644
--- a/library/std/src/sys/wasi/fd.rs
+++ b/library/std/src/sys/wasi/fd.rs
@@ -1,3 +1,4 @@
+#![deny(unsafe_op_in_unsafe_fn)]
#![allow(dead_code)]
use super::err2io;
diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
index 8408756..93a92b4 100644
--- a/library/std/src/sys/wasi/fs.rs
+++ b/library/std/src/sys/wasi/fs.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
use crate::ffi::{CStr, CString, OsStr, OsString};
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
diff --git a/library/std/src/sys/wasi/io.rs b/library/std/src/sys/wasi/io.rs
index 0ad2e15..ee017d1 100644
--- a/library/std/src/sys/wasi/io.rs
+++ b/library/std/src/sys/wasi/io.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
use crate::marker::PhantomData;
use crate::slice;
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
index 2704ff4..a7a4407 100644
--- a/library/std/src/sys/wasi/mod.rs
+++ b/library/std/src/sys/wasi/mod.rs
@@ -17,6 +17,7 @@
use crate::io as std_io;
use crate::mem;
+#[path = "../unix/alloc.rs"]
pub mod alloc;
pub mod args;
#[path = "../unsupported/cmath.rs"]
@@ -33,8 +34,11 @@
pub mod os;
pub use crate::sys_common::os_str_bytes as os_str;
pub mod ext;
+#[path = "../unix/path.rs"]
pub mod path;
+#[path = "../unsupported/pipe.rs"]
pub mod pipe;
+#[path = "../unsupported/process.rs"]
pub mod process;
#[path = "../unsupported/rwlock.rs"]
pub mod rwlock;
diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs
index e186453..8fd4bb7 100644
--- a/library/std/src/sys/wasi/net.rs
+++ b/library/std/src/sys/wasi/net.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
use crate::convert::TryFrom;
use crate::fmt;
use crate::io::{self, IoSlice, IoSliceMut};
diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs
index 8052c0a..33c796a 100644
--- a/library/std/src/sys/wasi/os.rs
+++ b/library/std/src/sys/wasi/os.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
use crate::any::Any;
use crate::error::Error as StdError;
use crate::ffi::{CStr, CString, OsStr, OsString};
diff --git a/library/std/src/sys/wasi/path.rs b/library/std/src/sys/wasi/path.rs
deleted file mode 100644
index 840a7ae..0000000
--- a/library/std/src/sys/wasi/path.rs
+++ /dev/null
@@ -1,19 +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(_: &OsStr) -> Option<Prefix<'_>> {
- None
-}
-
-pub const MAIN_SEP_STR: &str = "/";
-pub const MAIN_SEP: char = '/';
diff --git a/library/std/src/sys/wasi/pipe.rs b/library/std/src/sys/wasi/pipe.rs
deleted file mode 100644
index 10d0925..0000000
--- a/library/std/src/sys/wasi/pipe.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-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 is_read_vectored(&self) -> bool {
- 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 is_write_vectored(&self) -> bool {
- 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/library/std/src/sys/wasi/process.rs b/library/std/src/sys/wasi/process.rs
deleted file mode 100644
index 7156c9a..0000000
--- a/library/std/src/sys/wasi/process.rs
+++ /dev/null
@@ -1,149 +0,0 @@
-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;
-
-pub use crate::ffi::OsString as EnvKey;
-
-////////////////////////////////////////////////////////////////////////////////
-// Command
-////////////////////////////////////////////////////////////////////////////////
-
-pub struct Command {
- env: CommandEnv,
-}
-
-// 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 {
- &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/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs
index 23baafa..d82f6f4 100644
--- a/library/std/src/sys/wasi/stdio.rs
+++ b/library/std/src/sys/wasi/stdio.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
use crate::io::{self, IoSlice, IoSliceMut};
use crate::mem::ManuallyDrop;
use crate::sys::fd::WasiFd;
diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs
index 0d39b1ce..8eaa5f0 100644
--- a/library/std/src/sys/wasi/thread.rs
+++ b/library/std/src/sys/wasi/thread.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
use crate::ffi::CStr;
use crate::io;
use crate::mem;
diff --git a/library/std/src/sys/wasi/time.rs b/library/std/src/sys/wasi/time.rs
index 80ec317..2e720d1 100644
--- a/library/std/src/sys/wasi/time.rs
+++ b/library/std/src/sys/wasi/time.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
use crate::time::Duration;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs
index 3de5890..2934ea5 100644
--- a/library/std/src/sys/wasm/mod.rs
+++ b/library/std/src/sys/wasm/mod.rs
@@ -27,7 +27,7 @@
pub mod net;
#[path = "../unsupported/os.rs"]
pub mod os;
-#[path = "../unsupported/path.rs"]
+#[path = "../unix/path.rs"]
pub mod path;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index 5fbea2a..bcc2ea9 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -1,5 +1,8 @@
#![allow(dead_code)] // runtime init functions not used during testing
+#[cfg(test)]
+mod tests;
+
use crate::ffi::OsString;
use crate::fmt;
use crate::os::windows::prelude::*;
@@ -198,69 +201,3 @@
self.parsed_args_list.len()
}
}
-
-#[cfg(test)]
-mod tests {
- use crate::ffi::OsString;
- use crate::sys::windows::args::*;
-
- fn chk(string: &str, parts: &[&str]) {
- let mut wide: Vec<u16> = OsString::from(string).encode_wide().collect();
- wide.push(0);
- let parsed = unsafe {
- parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE"))
- };
- let expected: Vec<OsString> = parts.iter().map(|k| OsString::from(k)).collect();
- assert_eq!(parsed.as_slice(), expected.as_slice());
- }
-
- #[test]
- fn empty() {
- chk("", &["TEST.EXE"]);
- chk("\0", &["TEST.EXE"]);
- }
-
- #[test]
- fn single_words() {
- chk("EXE one_word", &["EXE", "one_word"]);
- chk("EXE a", &["EXE", "a"]);
- chk("EXE š
", &["EXE", "š
"]);
- chk("EXE š
š¤¦", &["EXE", "š
š¤¦"]);
- }
-
- #[test]
- fn official_examples() {
- chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]);
- chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]);
- chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]);
- chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]);
- }
-
- #[test]
- fn whitespace_behavior() {
- chk(r#" test"#, &["", "test"]);
- chk(r#" test"#, &["", "test"]);
- chk(r#" test test2"#, &["", "test", "test2"]);
- chk(r#" test test2"#, &["", "test", "test2"]);
- chk(r#"test test2 "#, &["test", "test2"]);
- chk(r#"test test2 "#, &["test", "test2"]);
- chk(r#"test "#, &["test"]);
- }
-
- #[test]
- fn genius_quotes() {
- chk(r#"EXE "" """#, &["EXE", "", ""]);
- chk(r#"EXE "" """"#, &["EXE", "", "\""]);
- chk(
- r#"EXE "this is """all""" in the same argument""#,
- &["EXE", "this is \"all\" in the same argument"],
- );
- chk(r#"EXE "a"""#, &["EXE", "a\""]);
- chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]);
- // quotes cannot be escaped in command names
- chk(r#""EXE" check"#, &["EXE", "check"]);
- chk(r#""EXE check""#, &["EXE check"]);
- chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]);
- chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]);
- }
-}
diff --git a/library/std/src/sys/windows/args/tests.rs b/library/std/src/sys/windows/args/tests.rs
new file mode 100644
index 0000000..756a436
--- /dev/null
+++ b/library/std/src/sys/windows/args/tests.rs
@@ -0,0 +1,61 @@
+use crate::ffi::OsString;
+use crate::sys::windows::args::*;
+
+fn chk(string: &str, parts: &[&str]) {
+ let mut wide: Vec<u16> = OsString::from(string).encode_wide().collect();
+ wide.push(0);
+ let parsed =
+ unsafe { parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE")) };
+ let expected: Vec<OsString> = parts.iter().map(|k| OsString::from(k)).collect();
+ assert_eq!(parsed.as_slice(), expected.as_slice());
+}
+
+#[test]
+fn empty() {
+ chk("", &["TEST.EXE"]);
+ chk("\0", &["TEST.EXE"]);
+}
+
+#[test]
+fn single_words() {
+ chk("EXE one_word", &["EXE", "one_word"]);
+ chk("EXE a", &["EXE", "a"]);
+ chk("EXE š
", &["EXE", "š
"]);
+ chk("EXE š
š¤¦", &["EXE", "š
š¤¦"]);
+}
+
+#[test]
+fn official_examples() {
+ chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]);
+ chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]);
+ chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]);
+ chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]);
+}
+
+#[test]
+fn whitespace_behavior() {
+ chk(r#" test"#, &["", "test"]);
+ chk(r#" test"#, &["", "test"]);
+ chk(r#" test test2"#, &["", "test", "test2"]);
+ chk(r#" test test2"#, &["", "test", "test2"]);
+ chk(r#"test test2 "#, &["test", "test2"]);
+ chk(r#"test test2 "#, &["test", "test2"]);
+ chk(r#"test "#, &["test"]);
+}
+
+#[test]
+fn genius_quotes() {
+ chk(r#"EXE "" """#, &["EXE", "", ""]);
+ chk(r#"EXE "" """"#, &["EXE", "", "\""]);
+ chk(
+ r#"EXE "this is """all""" in the same argument""#,
+ &["EXE", "this is \"all\" in the same argument"],
+ );
+ chk(r#"EXE "a"""#, &["EXE", "a\""]);
+ chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]);
+ // quotes cannot be escaped in command names
+ chk(r#""EXE" check"#, &["EXE", "check"]);
+ chk(r#""EXE check""#, &["EXE check"]);
+ chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]);
+ chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]);
+}
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index f440442..559c4dc 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -1032,7 +1032,7 @@
// Functions that aren't available on every version of Windows that we support,
// but we still use them and just provide some form of a fallback implementation.
compat_fn! {
- kernel32:
+ "kernel32":
pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
_lpTargetFileName: LPCWSTR,
diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs
index d6d433f..3f25f05 100644
--- a/library/std/src/sys/windows/compat.rs
+++ b/library/std/src/sys/windows/compat.rs
@@ -12,7 +12,6 @@
//! function is available but afterwards it's just a load and a jump.
use crate::ffi::CString;
-use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::c;
pub fn lookup(module: &str, symbol: &str) -> Option<usize> {
@@ -28,45 +27,69 @@
}
}
-pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, fallback: usize) -> usize {
- let value = lookup(module, symbol).unwrap_or(fallback);
- ptr.store(value, Ordering::SeqCst);
- value
-}
-
macro_rules! compat_fn {
- ($module:ident: $(
+ ($module:literal: $(
$(#[$meta:meta])*
- pub fn $symbol:ident($($argname:ident: $argtype:ty),*)
- -> $rettype:ty {
- $($body:expr);*
- }
+ pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $body:block
)*) => ($(
- #[allow(unused_variables)]
$(#[$meta])*
- pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
+ pub mod $symbol {
+ use super::*;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::mem;
+
type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
static PTR: AtomicUsize = AtomicUsize::new(0);
+ #[allow(unused_variables)]
+ unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype $body
+
+ /// This address is stored in `PTR` to incidate an unavailable API.
+ ///
+ /// This way, call() will end up calling fallback() if it is unavailable.
+ ///
+ /// This is a `static` to avoid rustc duplicating `fn fallback()`
+ /// into both load() and is_available(), which would break
+ /// is_available()'s comparison. By using the same static variable
+ /// in both places, they'll refer to the same (copy of the)
+ /// function.
+ ///
+ /// LLVM merging the address of fallback with other functions
+ /// (because of unnamed_addr) is fine, since it's only compared to
+ /// an address from GetProcAddress from an external dll.
+ static FALLBACK: F = fallback;
+
+ #[cold]
fn load() -> usize {
- crate::sys::compat::store_func(&PTR,
- stringify!($module),
- stringify!($symbol),
- fallback as usize)
- }
- unsafe extern "system" fn fallback($($argname: $argtype),*)
- -> $rettype {
- $($body);*
+ // There is no locking here. It's okay if this is executed by multiple threads in
+ // parallel. `lookup` will result in the same value, and it's okay if they overwrite
+ // eachothers result as long as they do so atomically. We don't need any guarantees
+ // about memory ordering, as this involves just a single atomic variable which is
+ // not used to protect or order anything else.
+ let addr = crate::sys::compat::lookup($module, stringify!($symbol))
+ .unwrap_or(FALLBACK as usize);
+ PTR.store(addr, Ordering::Relaxed);
+ addr
}
- let addr = match PTR.load(Ordering::SeqCst) {
- 0 => load(),
- n => n,
- };
- mem::transmute::<usize, F>(addr)($($argname),*)
+ fn addr() -> usize {
+ match PTR.load(Ordering::Relaxed) {
+ 0 => load(),
+ addr => addr,
+ }
+ }
+
+ #[allow(dead_code)]
+ pub fn is_available() -> bool {
+ addr() != FALLBACK as usize
+ }
+
+ pub unsafe fn call($($argname: $argtype),*) -> $rettype {
+ mem::transmute::<usize, F>(addr())($($argname),*)
+ }
}
+
+ pub use $symbol::call as $symbol;
)*)
}
diff --git a/library/std/src/sys/windows/ext/io.rs b/library/std/src/sys/windows/ext/io.rs
index 4573ee5..e75f9a4 100644
--- a/library/std/src/sys/windows/ext/io.rs
+++ b/library/std/src/sys/windows/ext/io.rs
@@ -1,3 +1,5 @@
+//! Windows-specific extensions to general I/O primitives.
+
#![stable(feature = "rust1", since = "1.0.0")]
use crate::fs;
diff --git a/library/std/src/sys/windows/ext/raw.rs b/library/std/src/sys/windows/ext/raw.rs
index 7f2a287..5014e00 100644
--- a/library/std/src/sys/windows/ext/raw.rs
+++ b/library/std/src/sys/windows/ext/raw.rs
@@ -1,4 +1,4 @@
-//! Windows-specific primitives
+//! Windows-specific primitives.
#![stable(feature = "raw_ext", since = "1.1.0")]
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index a0d5a74..8178e68 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -306,10 +306,20 @@
/// that function for more information on `__fastfail`
#[allow(unreachable_code)]
pub fn abort_internal() -> ! {
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ const FAST_FAIL_FATAL_APP_EXIT: usize = 7;
unsafe {
- llvm_asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT
- crate::intrinsics::unreachable();
+ cfg_if::cfg_if! {
+ if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
+ asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT);
+ crate::intrinsics::unreachable();
+ } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] {
+ asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT);
+ crate::intrinsics::unreachable();
+ } else if #[cfg(target_arch = "aarch64")] {
+ asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT);
+ crate::intrinsics::unreachable();
+ }
+ }
}
crate::intrinsics::abort();
}
diff --git a/library/std/src/sys/windows/mutex.rs b/library/std/src/sys/windows/mutex.rs
index 63dfc64..e2aaca5 100644
--- a/library/std/src/sys/windows/mutex.rs
+++ b/library/std/src/sys/windows/mutex.rs
@@ -19,24 +19,28 @@
//! CriticalSection is used and we keep track of who's holding the mutex to
//! detect recursive locks.
-use crate::cell::UnsafeCell;
+use crate::cell::{Cell, UnsafeCell};
use crate::mem::{self, MaybeUninit};
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::c;
-use crate::sys::compat;
pub struct Mutex {
+ // This is either directly an SRWLOCK (if supported), or a Box<Inner> otherwise.
lock: AtomicUsize,
- held: UnsafeCell<bool>,
}
unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}
+struct Inner {
+ remutex: ReentrantMutex,
+ held: Cell<bool>,
+}
+
#[derive(Clone, Copy)]
enum Kind {
- SRWLock = 1,
- CriticalSection = 2,
+ SRWLock,
+ CriticalSection,
}
#[inline]
@@ -51,7 +55,6 @@
// This works because SRWLOCK_INIT is 0 (wrapped in a struct), so we are also properly
// initializing an SRWLOCK here.
lock: AtomicUsize::new(0),
- held: UnsafeCell::new(false),
}
}
#[inline]
@@ -60,10 +63,11 @@
match kind() {
Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)),
Kind::CriticalSection => {
- let re = self.remutex();
- (*re).lock();
- if !self.flag_locked() {
- (*re).unlock();
+ let inner = &*self.inner();
+ inner.remutex.lock();
+ if inner.held.replace(true) {
+ // It was already locked, so we got a recursive lock which we do not want.
+ inner.remutex.unlock();
panic!("cannot recursively lock a mutex");
}
}
@@ -73,23 +77,27 @@
match kind() {
Kind::SRWLock => c::TryAcquireSRWLockExclusive(raw(self)) != 0,
Kind::CriticalSection => {
- let re = self.remutex();
- if !(*re).try_lock() {
+ let inner = &*self.inner();
+ if !inner.remutex.try_lock() {
false
- } else if self.flag_locked() {
- true
+ } else if inner.held.replace(true) {
+ // It was already locked, so we got a recursive lock which we do not want.
+ inner.remutex.unlock();
+ false
} else {
- (*re).unlock();
- false
+ true
}
}
}
}
pub unsafe fn unlock(&self) {
- *self.held.get() = false;
match kind() {
Kind::SRWLock => c::ReleaseSRWLockExclusive(raw(self)),
- Kind::CriticalSection => (*self.remutex()).unlock(),
+ Kind::CriticalSection => {
+ let inner = &*(self.lock.load(Ordering::SeqCst) as *const Inner);
+ inner.held.set(false);
+ inner.remutex.unlock();
+ }
}
}
pub unsafe fn destroy(&self) {
@@ -97,60 +105,35 @@
Kind::SRWLock => {}
Kind::CriticalSection => match self.lock.load(Ordering::SeqCst) {
0 => {}
- n => {
- Box::from_raw(n as *mut ReentrantMutex).destroy();
- }
+ n => Box::from_raw(n as *mut Inner).remutex.destroy(),
},
}
}
- unsafe fn remutex(&self) -> *mut ReentrantMutex {
+ unsafe fn inner(&self) -> *const Inner {
match self.lock.load(Ordering::SeqCst) {
0 => {}
- n => return n as *mut _,
+ n => return n as *const _,
}
- let re = box ReentrantMutex::uninitialized();
- re.init();
- let re = Box::into_raw(re);
- match self.lock.compare_and_swap(0, re as usize, Ordering::SeqCst) {
- 0 => re,
+ let inner = box Inner { remutex: ReentrantMutex::uninitialized(), held: Cell::new(false) };
+ inner.remutex.init();
+ let inner = Box::into_raw(inner);
+ match self.lock.compare_and_swap(0, inner as usize, Ordering::SeqCst) {
+ 0 => inner,
n => {
- Box::from_raw(re).destroy();
- n as *mut _
+ Box::from_raw(inner).remutex.destroy();
+ n as *const _
}
}
}
-
- unsafe fn flag_locked(&self) -> bool {
- if *self.held.get() {
- false
- } else {
- *self.held.get() = true;
- true
- }
- }
}
fn kind() -> Kind {
- static KIND: AtomicUsize = AtomicUsize::new(0);
-
- let val = KIND.load(Ordering::SeqCst);
- if val == Kind::SRWLock as usize {
- return Kind::SRWLock;
- } else if val == Kind::CriticalSection as usize {
- return Kind::CriticalSection;
- }
-
- let ret = match compat::lookup("kernel32", "AcquireSRWLockExclusive") {
- None => Kind::CriticalSection,
- Some(..) => Kind::SRWLock,
- };
- KIND.store(ret as usize, Ordering::SeqCst);
- ret
+ if c::AcquireSRWLockExclusive::is_available() { Kind::SRWLock } else { Kind::CriticalSection }
}
pub struct ReentrantMutex {
- inner: UnsafeCell<MaybeUninit<c::CRITICAL_SECTION>>,
+ inner: MaybeUninit<UnsafeCell<c::CRITICAL_SECTION>>,
}
unsafe impl Send for ReentrantMutex {}
@@ -158,27 +141,27 @@
impl ReentrantMutex {
pub const fn uninitialized() -> ReentrantMutex {
- ReentrantMutex { inner: UnsafeCell::new(MaybeUninit::uninit()) }
+ ReentrantMutex { inner: MaybeUninit::uninit() }
}
pub unsafe fn init(&self) {
- c::InitializeCriticalSection((&mut *self.inner.get()).as_mut_ptr());
+ c::InitializeCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr()));
}
pub unsafe fn lock(&self) {
- c::EnterCriticalSection((&mut *self.inner.get()).as_mut_ptr());
+ c::EnterCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr()));
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
- c::TryEnterCriticalSection((&mut *self.inner.get()).as_mut_ptr()) != 0
+ c::TryEnterCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr())) != 0
}
pub unsafe fn unlock(&self) {
- c::LeaveCriticalSection((&mut *self.inner.get()).as_mut_ptr());
+ c::LeaveCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr()));
}
pub unsafe fn destroy(&self) {
- c::DeleteCriticalSection((&mut *self.inner.get()).as_mut_ptr());
+ c::DeleteCriticalSection(UnsafeCell::raw_get(self.inner.as_ptr()));
}
}
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
index a0da2498..77c378a 100644
--- a/library/std/src/sys/windows/os.rs
+++ b/library/std/src/sys/windows/os.rs
@@ -2,6 +2,9 @@
#![allow(nonstandard_style)]
+#[cfg(test)]
+mod tests;
+
use crate::os::windows::prelude::*;
use crate::error::Error as StdError;
@@ -328,20 +331,3 @@
pub fn getpid() -> u32 {
unsafe { c::GetCurrentProcessId() as u32 }
}
-
-#[cfg(test)]
-mod tests {
- use crate::io::Error;
- use crate::sys::c;
-
- // tests `error_string` above
- #[test]
- fn ntstatus_error() {
- const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001;
- assert!(
- !Error::from_raw_os_error((STATUS_UNSUCCESSFUL | c::FACILITY_NT_BIT) as _)
- .to_string()
- .contains("FormatMessageW() returned error")
- );
- }
-}
diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs
new file mode 100644
index 0000000..458d6e1
--- /dev/null
+++ b/library/std/src/sys/windows/os/tests.rs
@@ -0,0 +1,13 @@
+use crate::io::Error;
+use crate::sys::c;
+
+// tests `error_string` above
+#[test]
+fn ntstatus_error() {
+ const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001;
+ assert!(
+ !Error::from_raw_os_error((STATUS_UNSUCCESSFUL | c::FACILITY_NT_BIT) as _)
+ .to_string()
+ .contains("FormatMessageW() returned error")
+ );
+}
diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs
index 2f5fc72..7e09a4f 100644
--- a/library/std/src/sys/windows/os_str.rs
+++ b/library/std/src/sys/windows/os_str.rs
@@ -77,14 +77,14 @@
}
pub fn as_slice(&self) -> &Slice {
- // Safety: Slice is just a wrapper for Wtf8,
+ // SAFETY: Slice is just a wrapper for Wtf8,
// and self.inner.as_slice() returns &Wtf8.
// Therefore, transmuting &Wtf8 to &Slice is safe.
unsafe { mem::transmute(self.inner.as_slice()) }
}
pub fn as_mut_slice(&mut self) -> &mut Slice {
- // Safety: Slice is just a wrapper for Wtf8,
+ // SAFETY: Slice is just a wrapper for Wtf8,
// and self.inner.as_mut_slice() returns &mut Wtf8.
// Therefore, transmuting &mut Wtf8 to &mut Slice is safe.
// Additionally, care should be taken to ensure the slice
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index 7d6d477..243065b 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -1,5 +1,8 @@
#![unstable(feature = "process_internals", issue = "none")]
+#[cfg(test)]
+mod tests;
+
use crate::borrow::Borrow;
use crate::collections::BTreeMap;
use crate::env;
@@ -19,7 +22,7 @@
use crate::sys::mutex::Mutex;
use crate::sys::pipe::{self, AnonPipe};
use crate::sys::stdio;
-use crate::sys_common::process::CommandEnv;
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
use crate::sys_common::AsInner;
use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
@@ -131,6 +134,23 @@
self.flags = flags;
}
+ pub fn get_program(&self) -> &OsStr {
+ &self.program
+ }
+
+ pub fn get_args(&self) -> CommandArgs<'_> {
+ let iter = self.args.iter();
+ CommandArgs { iter }
+ }
+
+ pub fn get_envs(&self) -> CommandEnvs<'_> {
+ self.env.iter()
+ }
+
+ pub fn get_current_dir(&self) -> Option<&Path> {
+ self.cwd.as_ref().map(|cwd| Path::new(cwd))
+ }
+
pub fn spawn(
&mut self,
default: Stdio,
@@ -527,40 +547,31 @@
}
}
-#[cfg(test)]
-mod tests {
- use super::make_command_line;
- use crate::ffi::{OsStr, OsString};
+pub struct CommandArgs<'a> {
+ iter: crate::slice::Iter<'a, OsString>,
+}
- #[test]
- fn test_make_command_line() {
- fn test_wrapper(prog: &str, args: &[&str]) -> String {
- let command_line = &make_command_line(
- OsStr::new(prog),
- &args.iter().map(|a| OsString::from(a)).collect::<Vec<OsString>>(),
- )
- .unwrap();
- String::from_utf16(command_line).unwrap()
- }
+impl<'a> Iterator for CommandArgs<'a> {
+ type Item = &'a OsStr;
+ fn next(&mut self) -> Option<&'a OsStr> {
+ self.iter.next().map(|s| s.as_ref())
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
- assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"]), "\"prog\" aaa bbb ccc");
+impl<'a> ExactSizeIterator for CommandArgs<'a> {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
+}
- assert_eq!(
- test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
- "\"C:\\Program Files\\blah\\blah.exe\" aaa"
- );
- assert_eq!(
- test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
- "\"C:\\Program Files\\test\" aa\\\"bb"
- );
- assert_eq!(test_wrapper("echo", &["a b c"]), "\"echo\" \"a b c\"");
- assert_eq!(
- test_wrapper("echo", &["\" \\\" \\", "\\"]),
- "\"echo\" \"\\\" \\\\\\\" \\\\\" \\"
- );
- assert_eq!(
- test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
- "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
- );
+impl<'a> fmt::Debug for CommandArgs<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(self.iter.clone()).finish()
}
}
diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs
new file mode 100644
index 0000000..81627ad
--- /dev/null
+++ b/library/std/src/sys/windows/process/tests.rs
@@ -0,0 +1,31 @@
+use super::make_command_line;
+use crate::ffi::{OsStr, OsString};
+
+#[test]
+fn test_make_command_line() {
+ fn test_wrapper(prog: &str, args: &[&str]) -> String {
+ let command_line = &make_command_line(
+ OsStr::new(prog),
+ &args.iter().map(|a| OsString::from(a)).collect::<Vec<OsString>>(),
+ )
+ .unwrap();
+ String::from_utf16(command_line).unwrap()
+ }
+
+ assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"]), "\"prog\" aaa bbb ccc");
+
+ assert_eq!(
+ test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
+ "\"C:\\Program Files\\blah\\blah.exe\" aaa"
+ );
+ assert_eq!(
+ test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
+ "\"C:\\Program Files\\test\" aa\\\"bb"
+ );
+ assert_eq!(test_wrapper("echo", &["a b c"]), "\"echo\" \"a b c\"");
+ assert_eq!(test_wrapper("echo", &["\" \\\" \\", "\\"]), "\"echo\" \"\\\" \\\\\\\" \\\\\" \\");
+ assert_eq!(
+ test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
+ "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
+ );
+}
diff --git a/library/std/src/sys_common/alloc.rs b/library/std/src/sys_common/alloc.rs
index c669410..6c1bc0d 100644
--- a/library/std/src/sys_common/alloc.rs
+++ b/library/std/src/sys_common/alloc.rs
@@ -12,9 +12,11 @@
target_arch = "mips",
target_arch = "powerpc",
target_arch = "powerpc64",
+ target_arch = "sparc",
target_arch = "asmjs",
target_arch = "wasm32",
- target_arch = "hexagon"
+ target_arch = "hexagon",
+ target_arch = "riscv32"
)))]
pub const MIN_ALIGN: usize = 8;
#[cfg(all(any(
diff --git a/library/std/src/sys_common/at_exit_imp.rs b/library/std/src/sys_common/at_exit_imp.rs
index 6b799db..90d5d3a 100644
--- a/library/std/src/sys_common/at_exit_imp.rs
+++ b/library/std/src/sys_common/at_exit_imp.rs
@@ -4,7 +4,7 @@
use crate::mem;
use crate::ptr;
-use crate::sys_common::mutex::Mutex;
+use crate::sys_common::mutex::StaticMutex;
type Queue = Vec<Box<dyn FnOnce()>>;
@@ -12,9 +12,8 @@
// on poisoning and this module needs to operate at a lower level than requiring
// the thread infrastructure to be in place (useful on the borders of
// initialization/destruction).
-// We never call `LOCK.init()`, so it is UB to attempt to
-// acquire this mutex reentrantly!
-static LOCK: Mutex = Mutex::new();
+// It is UB to attempt to acquire this mutex reentrantly!
+static LOCK: StaticMutex = StaticMutex::new();
static mut QUEUE: *mut Queue = ptr::null_mut();
const DONE: *mut Queue = 1_usize as *mut _;
diff --git a/library/std/src/sys_common/bytestring.rs b/library/std/src/sys_common/bytestring.rs
index dccc3bc..97fba60 100644
--- a/library/std/src/sys_common/bytestring.rs
+++ b/library/std/src/sys_common/bytestring.rs
@@ -1,5 +1,8 @@
#![allow(dead_code)]
+#[cfg(test)]
+mod tests;
+
use crate::fmt::{Formatter, Result, Write};
use core::str::lossy::{Utf8Lossy, Utf8LossyChunk};
@@ -21,26 +24,3 @@
}
f.write_str("\"")
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::fmt::{Debug, Formatter, Result};
-
- #[test]
- fn smoke() {
- struct Helper<'a>(&'a [u8]);
-
- impl Debug for Helper<'_> {
- fn fmt(&self, f: &mut Formatter<'_>) -> Result {
- debug_fmt_bytestring(self.0, f)
- }
- }
-
- let input = b"\xF0hello,\tworld";
- let expected = r#""\xF0hello,\tworld""#;
- let output = format!("{:?}", Helper(input));
-
- assert!(output == expected);
- }
-}
diff --git a/library/std/src/sys_common/bytestring/tests.rs b/library/std/src/sys_common/bytestring/tests.rs
new file mode 100644
index 0000000..1685f08
--- /dev/null
+++ b/library/std/src/sys_common/bytestring/tests.rs
@@ -0,0 +1,19 @@
+use super::*;
+use crate::fmt::{Debug, Formatter, Result};
+
+#[test]
+fn smoke() {
+ struct Helper<'a>(&'a [u8]);
+
+ impl Debug for Helper<'_> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ debug_fmt_bytestring(self.0, f)
+ }
+ }
+
+ let input = b"\xF0hello,\tworld";
+ let expected = r#""\xF0hello,\tworld""#;
+ let output = format!("{:?}", Helper(input));
+
+ assert!(output == expected);
+}
diff --git a/library/std/src/sys_common/condvar.rs b/library/std/src/sys_common/condvar.rs
index f9611bc..a48d301 100644
--- a/library/std/src/sys_common/condvar.rs
+++ b/library/std/src/sys_common/condvar.rs
@@ -1,5 +1,5 @@
use crate::sys::condvar as imp;
-use crate::sys_common::mutex::{self, Mutex};
+use crate::sys_common::mutex::MovableMutex;
use crate::time::Duration;
/// An OS-based condition variable.
@@ -46,8 +46,8 @@
/// Behavior is also undefined if more than one mutex is used concurrently
/// on this condition variable.
#[inline]
- pub unsafe fn wait(&self, mutex: &Mutex) {
- self.0.wait(mutex::raw(mutex))
+ pub unsafe fn wait(&self, mutex: &MovableMutex) {
+ self.0.wait(mutex.raw())
}
/// Waits for a signal on the specified mutex with a timeout duration
@@ -57,8 +57,8 @@
/// Behavior is also undefined if more than one mutex is used concurrently
/// on this condition variable.
#[inline]
- pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
- self.0.wait_timeout(mutex::raw(mutex), dur)
+ pub unsafe fn wait_timeout(&self, mutex: &MovableMutex, dur: Duration) -> bool {
+ self.0.wait_timeout(mutex.raw(), dur)
}
/// Deallocates all resources associated with this condition variable.
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index 840f909..234b257 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -15,6 +15,9 @@
#![allow(missing_docs)]
#![allow(missing_debug_implementations)]
+#[cfg(test)]
+mod tests;
+
use crate::sync::Once;
use crate::sys;
@@ -63,6 +66,7 @@
pub mod thread_info;
pub mod thread_local_dtor;
pub mod thread_local_key;
+pub mod thread_parker;
pub mod util;
pub mod wtf8;
@@ -141,8 +145,3 @@
// r < denom, so (denom*numer) is the upper bound of (r*numer)
q * numer + r * numer / denom
}
-
-#[test]
-fn test_muldiv() {
- assert_eq!(mul_div_u64(1_000_000_000_001, 1_000_000_000, 1_000_000), 1_000_000_000_001_000);
-}
diff --git a/library/std/src/sys_common/mutex.rs b/library/std/src/sys_common/mutex.rs
index e66d899..93ec7d8 100644
--- a/library/std/src/sys_common/mutex.rs
+++ b/library/std/src/sys_common/mutex.rs
@@ -1,101 +1,106 @@
use crate::sys::mutex as imp;
-/// An OS-based mutual exclusion lock.
+/// An OS-based mutual exclusion lock, meant for use in static variables.
///
-/// This is the thinnest cross-platform wrapper around OS mutexes. All usage of
-/// this mutex is unsafe and it is recommended to instead use the safe wrapper
-/// at the top level of the crate instead of this type.
-pub struct Mutex(imp::Mutex);
+/// This mutex has a const constructor ([`StaticMutex::new`]), does not
+/// implement `Drop` to cleanup resources, and causes UB when moved or used
+/// reentrantly.
+///
+/// This mutex does not implement poisoning.
+///
+/// This is a wrapper around `imp::Mutex` that does *not* call `init()` and
+/// `destroy()`.
+pub struct StaticMutex(imp::Mutex);
-unsafe impl Sync for Mutex {}
+unsafe impl Sync for StaticMutex {}
-impl Mutex {
+impl StaticMutex {
/// Creates a new mutex for use.
///
/// Behavior is undefined if the mutex is moved after it is
/// first used with any of the functions below.
- /// Also, until `init` is called, behavior is undefined if this
- /// mutex is ever used reentrantly, i.e., `raw_lock` or `try_lock`
- /// are called by the thread currently holding the lock.
+ /// Also, the behavior is undefined if this mutex is ever used reentrantly,
+ /// i.e., `lock` is called by the thread currently holding the lock.
#[rustc_const_stable(feature = "const_sys_mutex_new", since = "1.0.0")]
- pub const fn new() -> Mutex {
- Mutex(imp::Mutex::new())
- }
-
- /// Prepare the mutex for use.
- ///
- /// This should be called once the mutex is at a stable memory address.
- /// If called, this must be the very first thing that happens to the mutex.
- /// Calling it in parallel with or after any operation (including another
- /// `init()`) is undefined behavior.
- #[inline]
- pub unsafe fn init(&mut self) {
- self.0.init()
- }
-
- /// Locks the mutex blocking the current thread until it is available.
- ///
- /// Behavior is undefined if the mutex has been moved between this and any
- /// previous function call.
- #[inline]
- pub unsafe fn raw_lock(&self) {
- self.0.lock()
+ pub const fn new() -> Self {
+ Self(imp::Mutex::new())
}
/// Calls raw_lock() and then returns an RAII guard to guarantee the mutex
/// will be unlocked.
+ ///
+ /// It is undefined behaviour to call this function while locked, or if the
+ /// mutex has been moved since the last time this was called.
#[inline]
- pub unsafe fn lock(&self) -> MutexGuard<'_> {
- self.raw_lock();
- MutexGuard(&self.0)
+ pub unsafe fn lock(&self) -> StaticMutexGuard<'_> {
+ self.0.lock();
+ StaticMutexGuard(&self.0)
+ }
+}
+
+#[must_use]
+pub struct StaticMutexGuard<'a>(&'a imp::Mutex);
+
+impl Drop for StaticMutexGuard<'_> {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ self.0.unlock();
+ }
+ }
+}
+
+/// An OS-based mutual exclusion lock.
+///
+/// This mutex does *not* have a const constructor, cleans up its resources in
+/// its `Drop` implementation, may safely be moved (when not borrowed), and
+/// does not cause UB when used reentrantly.
+///
+/// This mutex does not implement poisoning.
+///
+/// This is a wrapper around `Box<imp::Mutex>`, to allow the object to be moved
+/// without moving the raw mutex.
+pub struct MovableMutex(Box<imp::Mutex>);
+
+unsafe impl Sync for MovableMutex {}
+
+impl MovableMutex {
+ /// Creates a new mutex.
+ pub fn new() -> Self {
+ let mut mutex = box imp::Mutex::new();
+ unsafe { mutex.init() };
+ Self(mutex)
+ }
+
+ pub(crate) fn raw(&self) -> &imp::Mutex {
+ &self.0
+ }
+
+ /// Locks the mutex blocking the current thread until it is available.
+ #[inline]
+ pub fn raw_lock(&self) {
+ unsafe { self.0.lock() }
}
/// Attempts to lock the mutex without blocking, returning whether it was
/// successfully acquired or not.
- ///
- /// Behavior is undefined if the mutex has been moved between this and any
- /// previous function call.
#[inline]
- pub unsafe fn try_lock(&self) -> bool {
- self.0.try_lock()
+ pub fn try_lock(&self) -> bool {
+ unsafe { self.0.try_lock() }
}
/// Unlocks the mutex.
///
/// Behavior is undefined if the current thread does not actually hold the
/// mutex.
- ///
- /// Consider switching from the pair of raw_lock() and raw_unlock() to
- /// lock() whenever possible.
#[inline]
pub unsafe fn raw_unlock(&self) {
self.0.unlock()
}
-
- /// Deallocates all resources associated with this mutex.
- ///
- /// Behavior is undefined if there are current or will be future users of
- /// this mutex.
- #[inline]
- pub unsafe fn destroy(&self) {
- self.0.destroy()
- }
}
-// not meant to be exported to the outside world, just the containing module
-pub fn raw(mutex: &Mutex) -> &imp::Mutex {
- &mutex.0
-}
-
-#[must_use]
-/// A simple RAII utility for the above Mutex without the poisoning semantics.
-pub struct MutexGuard<'a>(&'a imp::Mutex);
-
-impl Drop for MutexGuard<'_> {
- #[inline]
+impl Drop for MovableMutex {
fn drop(&mut self) {
- unsafe {
- self.0.unlock();
- }
+ unsafe { self.0.destroy() };
}
}
diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs
index 0bb1360..48ba4dd 100644
--- a/library/std/src/sys_common/net.rs
+++ b/library/std/src/sys_common/net.rs
@@ -1,3 +1,6 @@
+#[cfg(test)]
+mod tests;
+
use crate::cmp;
use crate::convert::{TryFrom, TryInto};
use crate::ffi::CString;
@@ -672,26 +675,3 @@
res.field(name, &self.inner.as_inner()).finish()
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::collections::HashMap;
-
- #[test]
- fn no_lookup_host_duplicates() {
- let mut addrs = HashMap::new();
- let lh = match LookupHost::try_from(("localhost", 0)) {
- Ok(lh) => lh,
- Err(e) => panic!("couldn't resolve `localhost': {}", e),
- };
- for sa in lh {
- *addrs.entry(sa).or_insert(0) += 1;
- }
- assert_eq!(
- addrs.iter().filter(|&(_, &v)| v > 1).collect::<Vec<_>>(),
- vec![],
- "There should be no duplicate localhost entries"
- );
- }
-}
diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys_common/net/tests.rs
new file mode 100644
index 0000000..7d45621
--- /dev/null
+++ b/library/std/src/sys_common/net/tests.rs
@@ -0,0 +1,19 @@
+use super::*;
+use crate::collections::HashMap;
+
+#[test]
+fn no_lookup_host_duplicates() {
+ let mut addrs = HashMap::new();
+ let lh = match LookupHost::try_from(("localhost", 0)) {
+ Ok(lh) => lh,
+ Err(e) => panic!("couldn't resolve `localhost': {}", e),
+ };
+ for sa in lh {
+ *addrs.entry(sa).or_insert(0) += 1;
+ }
+ assert_eq!(
+ addrs.iter().filter(|&(_, &v)| v > 1).collect::<Vec<_>>(),
+ vec![],
+ "There should be no duplicate localhost entries"
+ );
+}
diff --git a/library/std/src/sys_common/os_str_bytes.rs b/library/std/src/sys_common/os_str_bytes.rs
index 984c032..497e5fc 100644
--- a/library/std/src/sys_common/os_str_bytes.rs
+++ b/library/std/src/sys_common/os_str_bytes.rs
@@ -106,7 +106,7 @@
#[inline]
pub fn as_slice(&self) -> &Slice {
- // Safety: Slice just wraps [u8],
+ // SAFETY: Slice just wraps [u8],
// and &*self.inner is &[u8], therefore
// transmuting &[u8] to &Slice is safe.
unsafe { mem::transmute(&*self.inner) }
@@ -114,7 +114,7 @@
#[inline]
pub fn as_mut_slice(&mut self) -> &mut Slice {
- // Safety: Slice just wraps [u8],
+ // SAFETY: Slice just wraps [u8],
// and &mut *self.inner is &mut [u8], therefore
// transmuting &mut [u8] to &mut Slice is safe.
unsafe { mem::transmute(&mut *self.inner) }
@@ -232,23 +232,17 @@
}
/// 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 documentation 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 documentation for an example.
- ///
- /// [`OsString`]: ../../../ffi/struct.OsString.html
#[stable(feature = "rust1", since = "1.0.0")]
fn into_vec(self) -> Vec<u8>;
}
@@ -264,23 +258,17 @@
}
/// 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 documentation 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 documentation for an example.
- ///
- /// [`OsStr`]: ../../../ffi/struct.OsStr.html
#[stable(feature = "rust1", since = "1.0.0")]
fn as_bytes(&self) -> &[u8];
}
diff --git a/library/std/src/sys_common/poison.rs b/library/std/src/sys_common/poison.rs
index 285851d..2ab2c70 100644
--- a/library/std/src/sys_common/poison.rs
+++ b/library/std/src/sys_common/poison.rs
@@ -3,6 +3,9 @@
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::thread;
+#[allow(unused_imports)] // for intra-doc links
+use crate::sync::{Mutex, RwLock};
+
pub struct Flag {
failed: AtomicBool,
}
@@ -62,7 +65,7 @@
/// let mutex = Arc::new(Mutex::new(1));
///
/// // poison the mutex
-/// let c_mutex = mutex.clone();
+/// let c_mutex = Arc::clone(&mutex);
/// let _ = thread::spawn(move || {
/// let mut data = c_mutex.lock().unwrap();
/// *data = 2;
@@ -77,9 +80,6 @@
/// }
/// };
/// ```
-///
-/// [`Mutex`]: ../../std/sync/struct.Mutex.html
-/// [`RwLock`]: ../../std/sync/struct.RwLock.html
#[stable(feature = "rust1", since = "1.0.0")]
pub struct PoisonError<T> {
guard: T,
@@ -89,12 +89,9 @@
/// can occur while trying to acquire a lock, from the [`try_lock`] method on a
/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`].
///
-/// [`Mutex`]: struct.Mutex.html
-/// [`RwLock`]: struct.RwLock.html
-/// [`TryLockResult`]: type.TryLockResult.html
-/// [`try_lock`]: struct.Mutex.html#method.try_lock
-/// [`try_read`]: struct.RwLock.html#method.try_read
-/// [`try_write`]: struct.RwLock.html#method.try_write
+/// [`try_lock`]: Mutex::try_lock
+/// [`try_read`]: RwLock::try_read
+/// [`try_write`]: RwLock::try_write
#[stable(feature = "rust1", since = "1.0.0")]
pub enum TryLockError<T> {
/// The lock could not be acquired because another thread failed while holding
@@ -115,9 +112,7 @@
/// the associated guard, and it can be acquired through the [`into_inner`]
/// method.
///
-/// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok
-/// [`Err`]: ../../std/result/enum.Result.html#variant.Err
-/// [`into_inner`]: ../../std/sync/struct.PoisonError.html#method.into_inner
+/// [`into_inner`]: PoisonError::into_inner
#[stable(feature = "rust1", since = "1.0.0")]
pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
@@ -126,9 +121,6 @@
/// For more information, see [`LockResult`]. A `TryLockResult` doesn't
/// necessarily hold the associated guard in the [`Err`] type as the lock may not
/// have been acquired for other reasons.
-///
-/// [`LockResult`]: ../../std/sync/type.LockResult.html
-/// [`Err`]: ../../std/result/enum.Result.html#variant.Err
#[stable(feature = "rust1", since = "1.0.0")]
pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
@@ -158,9 +150,6 @@
/// Creates a `PoisonError`.
///
/// This is generally created by methods like [`Mutex::lock`] or [`RwLock::read`].
- ///
- /// [`Mutex::lock`]: ../../std/sync/struct.Mutex.html#method.lock
- /// [`RwLock::read`]: ../../std/sync/struct.RwLock.html#method.read
#[stable(feature = "sync_poison", since = "1.2.0")]
pub fn new(guard: T) -> PoisonError<T> {
PoisonError { guard }
@@ -179,7 +168,7 @@
/// let mutex = Arc::new(Mutex::new(HashSet::new()));
///
/// // poison the mutex
- /// let c_mutex = mutex.clone();
+ /// let c_mutex = Arc::clone(&mutex);
/// let _ = thread::spawn(move || {
/// let mut data = c_mutex.lock().unwrap();
/// data.insert(10);
diff --git a/library/std/src/sys_common/process.rs b/library/std/src/sys_common/process.rs
index f3a2962..fe89b11 100644
--- a/library/std/src/sys_common/process.rs
+++ b/library/std/src/sys_common/process.rs
@@ -92,4 +92,41 @@
self.saw_path = true;
}
}
+
+ pub fn iter(&self) -> CommandEnvs<'_> {
+ let iter = self.vars.iter();
+ CommandEnvs { iter }
+ }
+}
+
+/// An iterator over the command environment variables.
+///
+/// This struct is created by
+/// [`Command::get_envs`][crate::process::Command::get_envs]. See its
+/// documentation for more.
+#[unstable(feature = "command_access", issue = "44434")]
+#[derive(Debug)]
+pub struct CommandEnvs<'a> {
+ iter: crate::collections::btree_map::Iter<'a, EnvKey, Option<OsString>>,
+}
+
+#[unstable(feature = "command_access", issue = "44434")]
+impl<'a> Iterator for CommandEnvs<'a> {
+ type Item = (&'a OsStr, Option<&'a OsStr>);
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next().map(|(key, value)| (key.as_ref(), value.as_deref()))
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+#[unstable(feature = "command_access", issue = "44434")]
+impl<'a> ExactSizeIterator for CommandEnvs<'a> {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
}
diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sys_common/remutex.rs
index 4f19bbc..475bfca 100644
--- a/library/std/src/sys_common/remutex.rs
+++ b/library/std/src/sys_common/remutex.rs
@@ -1,7 +1,10 @@
-use crate::fmt;
-use crate::marker;
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
+use crate::marker::PhantomPinned;
use crate::ops::Deref;
use crate::panic::{RefUnwindSafe, UnwindSafe};
+use crate::pin::Pin;
use crate::sys::mutex as sys;
/// A re-entrant mutual exclusion
@@ -12,6 +15,7 @@
pub struct ReentrantMutex<T> {
inner: sys::ReentrantMutex,
data: T,
+ _pinned: PhantomPinned,
}
unsafe impl<T: Send> Send for ReentrantMutex<T> {}
@@ -34,12 +38,10 @@
/// guarded data.
#[must_use = "if unused the ReentrantMutex will immediately unlock"]
pub struct ReentrantMutexGuard<'a, T: 'a> {
- // funny underscores due to how Deref currently works (it disregards field
- // privacy).
- __lock: &'a ReentrantMutex<T>,
+ lock: Pin<&'a ReentrantMutex<T>>,
}
-impl<T> !marker::Send for ReentrantMutexGuard<'_, T> {}
+impl<T> !Send for ReentrantMutexGuard<'_, T> {}
impl<T> ReentrantMutex<T> {
/// Creates a new reentrant mutex in an unlocked state.
@@ -50,7 +52,11 @@
/// once this mutex is in its final resting place, and only then are the
/// lock/unlock methods safe.
pub const unsafe fn new(t: T) -> ReentrantMutex<T> {
- ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t }
+ ReentrantMutex {
+ inner: sys::ReentrantMutex::uninitialized(),
+ data: t,
+ _pinned: PhantomPinned,
+ }
}
/// Initializes this mutex so it's ready for use.
@@ -59,8 +65,8 @@
///
/// Unsafe to call more than once, and must be called after this will no
/// longer move in memory.
- pub unsafe fn init(&self) {
- self.inner.init();
+ pub unsafe fn init(self: Pin<&mut Self>) {
+ self.get_unchecked_mut().inner.init()
}
/// Acquires a mutex, blocking the current thread until it is able to do so.
@@ -75,9 +81,9 @@
/// 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) -> ReentrantMutexGuard<'_, T> {
+ pub fn lock(self: Pin<&Self>) -> ReentrantMutexGuard<'_, T> {
unsafe { self.inner.lock() }
- ReentrantMutexGuard::new(&self)
+ ReentrantMutexGuard { lock: self }
}
/// Attempts to acquire this lock.
@@ -92,8 +98,12 @@
/// 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) -> Option<ReentrantMutexGuard<'_, T>> {
- if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None }
+ pub fn try_lock(self: Pin<&Self>) -> Option<ReentrantMutexGuard<'_, T>> {
+ if unsafe { self.inner.try_lock() } {
+ Some(ReentrantMutexGuard { lock: self })
+ } else {
+ None
+ }
}
}
@@ -106,35 +116,11 @@
}
}
-impl<T: fmt::Debug + 'static> fmt::Debug for ReentrantMutex<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.try_lock() {
- Some(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(),
- None => {
- struct LockedPlaceholder;
- impl fmt::Debug for LockedPlaceholder {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("<locked>")
- }
- }
-
- f.debug_struct("ReentrantMutex").field("data", &LockedPlaceholder).finish()
- }
- }
- }
-}
-
-impl<'mutex, T> ReentrantMutexGuard<'mutex, T> {
- fn new(lock: &'mutex ReentrantMutex<T>) -> ReentrantMutexGuard<'mutex, T> {
- ReentrantMutexGuard { __lock: lock }
- }
-}
-
impl<T> Deref for ReentrantMutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
- &self.__lock.data
+ &self.lock.data
}
}
@@ -142,83 +128,7 @@
#[inline]
fn drop(&mut self) {
unsafe {
- self.__lock.inner.unlock();
- }
- }
-}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use crate::cell::RefCell;
- use crate::sync::Arc;
- use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
- use crate::thread;
-
- #[test]
- fn smoke() {
- let m = unsafe {
- let m = ReentrantMutex::new(());
- m.init();
- m
- };
- {
- let a = m.lock();
- {
- let b = m.lock();
- {
- let c = m.lock();
- assert_eq!(*c, ());
- }
- assert_eq!(*b, ());
- }
- assert_eq!(*a, ());
- }
- }
-
- #[test]
- fn is_mutex() {
- let m = unsafe {
- let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
- m.init();
- m
- };
- let m2 = m.clone();
- let lock = m.lock();
- let child = thread::spawn(move || {
- let lock = m2.lock();
- assert_eq!(*lock.borrow(), 4950);
- });
- for i in 0..100 {
- let lock = m.lock();
- *lock.borrow_mut() += i;
- }
- drop(lock);
- child.join().unwrap();
- }
-
- #[test]
- fn trylock_works() {
- let m = unsafe {
- let m = Arc::new(ReentrantMutex::new(()));
- m.init();
- m
- };
- let m2 = m.clone();
- let _lock = m.try_lock();
- let _lock2 = m.try_lock();
- thread::spawn(move || {
- let lock = m2.try_lock();
- assert!(lock.is_none());
- })
- .join()
- .unwrap();
- let _lock3 = m.try_lock();
- }
-
- pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
- impl Drop for Answer<'_> {
- fn drop(&mut self) {
- *self.0.borrow_mut() = 42;
+ self.lock.inner.unlock();
}
}
}
diff --git a/library/std/src/sys_common/remutex/tests.rs b/library/std/src/sys_common/remutex/tests.rs
new file mode 100644
index 0000000..88453de
--- /dev/null
+++ b/library/std/src/sys_common/remutex/tests.rs
@@ -0,0 +1,77 @@
+use crate::boxed::Box;
+use crate::cell::RefCell;
+use crate::pin::Pin;
+use crate::sync::Arc;
+use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
+use crate::thread;
+
+#[test]
+fn smoke() {
+ let m = unsafe {
+ let mut m = Box::pin(ReentrantMutex::new(()));
+ m.as_mut().init();
+ m
+ };
+ let m = m.as_ref();
+ {
+ let a = m.lock();
+ {
+ let b = m.lock();
+ {
+ let c = m.lock();
+ assert_eq!(*c, ());
+ }
+ assert_eq!(*b, ());
+ }
+ assert_eq!(*a, ());
+ }
+}
+
+#[test]
+fn is_mutex() {
+ let m = unsafe {
+ // FIXME: Simplify this if Arc gets a Arc::get_pin_mut.
+ let mut m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
+ Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
+ Pin::new_unchecked(m)
+ };
+ let m2 = m.clone();
+ let lock = m.as_ref().lock();
+ let child = thread::spawn(move || {
+ let lock = m2.as_ref().lock();
+ assert_eq!(*lock.borrow(), 4950);
+ });
+ for i in 0..100 {
+ let lock = m.as_ref().lock();
+ *lock.borrow_mut() += i;
+ }
+ drop(lock);
+ child.join().unwrap();
+}
+
+#[test]
+fn trylock_works() {
+ let m = unsafe {
+ // FIXME: Simplify this if Arc gets a Arc::get_pin_mut.
+ let mut m = Arc::new(ReentrantMutex::new(()));
+ Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
+ Pin::new_unchecked(m)
+ };
+ let m2 = m.clone();
+ let _lock = m.as_ref().try_lock();
+ let _lock2 = m.as_ref().try_lock();
+ thread::spawn(move || {
+ let lock = m2.as_ref().try_lock();
+ assert!(lock.is_none());
+ })
+ .join()
+ .unwrap();
+ let _lock3 = m.as_ref().try_lock();
+}
+
+pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
+impl Drop for Answer<'_> {
+ fn drop(&mut self) {
+ *self.0.borrow_mut() = 42;
+ }
+}
diff --git a/library/std/src/sys_common/tests.rs b/library/std/src/sys_common/tests.rs
new file mode 100644
index 0000000..1b6446d
--- /dev/null
+++ b/library/std/src/sys_common/tests.rs
@@ -0,0 +1,6 @@
+use super::mul_div_u64;
+
+#[test]
+fn test_muldiv() {
+ assert_eq!(mul_div_u64(1_000_000_000_001, 1_000_000_000, 1_000_000), 1_000_000_000_001_000);
+}
diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs
index ac5b128..dbcb7b3 100644
--- a/library/std/src/sys_common/thread_local_key.rs
+++ b/library/std/src/sys_common/thread_local_key.rs
@@ -48,9 +48,12 @@
#![unstable(feature = "thread_local_internals", issue = "none")]
#![allow(dead_code)] // sys isn't exported yet
+#[cfg(test)]
+mod tests;
+
use crate::sync::atomic::{self, AtomicUsize, Ordering};
use crate::sys::thread_local_key as imp;
-use crate::sys_common::mutex::Mutex;
+use crate::sys_common::mutex::StaticMutex;
/// A type for TLS keys that are statically allocated.
///
@@ -114,6 +117,7 @@
pub const INIT: StaticKey = StaticKey::new(None);
impl StaticKey {
+ #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> StaticKey {
StaticKey { key: atomic::AtomicUsize::new(0), dtor }
}
@@ -153,7 +157,7 @@
if imp::requires_synchronized_create() {
// We never call `INIT_LOCK.init()`, so it is UB to attempt to
// acquire this mutex reentrantly!
- static INIT_LOCK: Mutex = Mutex::new();
+ static INIT_LOCK: StaticMutex = StaticMutex::new();
let _guard = INIT_LOCK.lock();
let mut key = self.key.load(Ordering::SeqCst);
if key == 0 {
@@ -231,41 +235,3 @@
// unsafe { imp::destroy(self.key) }
}
}
-
-#[cfg(test)]
-mod tests {
- use super::{Key, StaticKey};
-
- fn assert_sync<T: Sync>() {}
- fn assert_send<T: Send>() {}
-
- #[test]
- fn smoke() {
- assert_sync::<Key>();
- assert_send::<Key>();
-
- let k1 = Key::new(None);
- let k2 = Key::new(None);
- assert!(k1.get().is_null());
- assert!(k2.get().is_null());
- k1.set(1 as *mut _);
- k2.set(2 as *mut _);
- assert_eq!(k1.get() as usize, 1);
- assert_eq!(k2.get() as usize, 2);
- }
-
- #[test]
- fn statik() {
- static K1: StaticKey = StaticKey::new(None);
- static K2: StaticKey = StaticKey::new(None);
-
- unsafe {
- assert!(K1.get().is_null());
- assert!(K2.get().is_null());
- K1.set(1 as *mut _);
- K2.set(2 as *mut _);
- assert_eq!(K1.get() as usize, 1);
- assert_eq!(K2.get() as usize, 2);
- }
- }
-}
diff --git a/library/std/src/sys_common/thread_local_key/tests.rs b/library/std/src/sys_common/thread_local_key/tests.rs
new file mode 100644
index 0000000..968738a
--- /dev/null
+++ b/library/std/src/sys_common/thread_local_key/tests.rs
@@ -0,0 +1,34 @@
+use super::{Key, StaticKey};
+
+fn assert_sync<T: Sync>() {}
+fn assert_send<T: Send>() {}
+
+#[test]
+fn smoke() {
+ assert_sync::<Key>();
+ assert_send::<Key>();
+
+ let k1 = Key::new(None);
+ let k2 = Key::new(None);
+ assert!(k1.get().is_null());
+ assert!(k2.get().is_null());
+ k1.set(1 as *mut _);
+ k2.set(2 as *mut _);
+ assert_eq!(k1.get() as usize, 1);
+ assert_eq!(k2.get() as usize, 2);
+}
+
+#[test]
+fn statik() {
+ static K1: StaticKey = StaticKey::new(None);
+ static K2: StaticKey = StaticKey::new(None);
+
+ unsafe {
+ assert!(K1.get().is_null());
+ assert!(K2.get().is_null());
+ K1.set(1 as *mut _);
+ K2.set(2 as *mut _);
+ assert_eq!(K1.get() as usize, 1);
+ assert_eq!(K2.get() as usize, 2);
+ }
+}
diff --git a/library/std/src/sys_common/thread_parker/futex.rs b/library/std/src/sys_common/thread_parker/futex.rs
new file mode 100644
index 0000000..a5d4927
--- /dev/null
+++ b/library/std/src/sys_common/thread_parker/futex.rs
@@ -0,0 +1,93 @@
+use crate::sync::atomic::AtomicI32;
+use crate::sync::atomic::Ordering::{Acquire, Release};
+use crate::sys::futex::{futex_wait, futex_wake};
+use crate::time::Duration;
+
+const PARKED: i32 = -1;
+const EMPTY: i32 = 0;
+const NOTIFIED: i32 = 1;
+
+pub struct Parker {
+ state: AtomicI32,
+}
+
+// Notes about memory ordering:
+//
+// Memory ordering is only relevant for the relative ordering of operations
+// between different variables. Even Ordering::Relaxed guarantees a
+// monotonic/consistent order when looking at just a single atomic variable.
+//
+// So, since this parker is just a single atomic variable, we only need to look
+// at the ordering guarantees we need to provide to the 'outside world'.
+//
+// The only memory ordering guarantee that parking and unparking provide, is
+// that things which happened before unpark() are visible on the thread
+// returning from park() afterwards. Otherwise, it was effectively unparked
+// before unpark() was called while still consuming the 'token'.
+//
+// In other words, unpark() needs to synchronize with the part of park() that
+// consumes the token and returns.
+//
+// This is done with a release-acquire synchronization, by using
+// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
+// Ordering::Acquire when checking for this state in park().
+impl Parker {
+ #[inline]
+ pub const fn new() -> Self {
+ Parker { state: AtomicI32::new(EMPTY) }
+ }
+
+ // Assumes this is only called by the thread that owns the Parker,
+ // which means that `self.state != PARKED`.
+ pub unsafe fn park(&self) {
+ // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
+ // first case.
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+ loop {
+ // Wait for something to happen, assuming it's still set to PARKED.
+ futex_wait(&self.state, PARKED, None);
+ // Change NOTIFIED=>EMPTY and return in that case.
+ if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED {
+ return;
+ } else {
+ // Spurious wake up. We loop to try again.
+ }
+ }
+ }
+
+ // Assumes this is only called by the thread that owns the Parker,
+ // which means that `self.state != PARKED`.
+ pub unsafe fn park_timeout(&self, timeout: Duration) {
+ // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
+ // first case.
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+ // Wait for something to happen, assuming it's still set to PARKED.
+ futex_wait(&self.state, PARKED, Some(timeout));
+ // This is not just a store, because we need to establish a
+ // release-acquire ordering with unpark().
+ if self.state.swap(EMPTY, Acquire) == NOTIFIED {
+ // Woke up because of unpark().
+ } else {
+ // Timeout or spurious wake up.
+ // We return either way, because we can't easily tell if it was the
+ // timeout or not.
+ }
+ }
+
+ #[inline]
+ pub fn unpark(&self) {
+ // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
+ // wake the thread in the first case.
+ //
+ // Note that even NOTIFIED=>NOTIFIED results in a write. This is on
+ // purpose, to make sure every unpark() has a release-acquire ordering
+ // with park().
+ if self.state.swap(NOTIFIED, Release) == PARKED {
+ futex_wake(&self.state);
+ }
+ }
+}
diff --git a/library/std/src/sys_common/thread_parker/generic.rs b/library/std/src/sys_common/thread_parker/generic.rs
new file mode 100644
index 0000000..14cfa95
--- /dev/null
+++ b/library/std/src/sys_common/thread_parker/generic.rs
@@ -0,0 +1,119 @@
+//! Parker implementaiton based on a Mutex and Condvar.
+
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use crate::sync::{Condvar, Mutex};
+use crate::time::Duration;
+
+const EMPTY: usize = 0;
+const PARKED: usize = 1;
+const NOTIFIED: usize = 2;
+
+pub struct Parker {
+ state: AtomicUsize,
+ lock: Mutex<()>,
+ cvar: Condvar,
+}
+
+impl Parker {
+ pub fn new() -> Self {
+ Parker { state: AtomicUsize::new(EMPTY), lock: Mutex::new(()), cvar: Condvar::new() }
+ }
+
+ // This implementaiton doesn't require `unsafe`, but other implementations
+ // may assume this is only called by the thread that owns the Parker.
+ pub unsafe fn park(&self) {
+ // If we were previously notified then we consume this notification and
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+
+ // Otherwise we need to coordinate going to sleep
+ let mut m = self.lock.lock().unwrap();
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read here, even though we know it will be `NOTIFIED`.
+ // This is because `unpark` may have been called again since we read
+ // `NOTIFIED` in the `compare_exchange` above. We must perform an
+ // acquire operation that synchronizes with that `unpark` to observe
+ // any writes it made before the call to unpark. To do that we must
+ // read from the write it made to `state`.
+ let old = self.state.swap(EMPTY, SeqCst);
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => panic!("inconsistent park state"),
+ }
+ loop {
+ m = self.cvar.wait(m).unwrap();
+ match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
+ Ok(_) => return, // got a notification
+ Err(_) => {} // spurious wakeup, go back to sleep
+ }
+ }
+ }
+
+ // This implementaiton doesn't require `unsafe`, but other implementations
+ // may assume this is only called by the thread that owns the Parker.
+ pub unsafe fn park_timeout(&self, dur: Duration) {
+ // Like `park` above we have a fast path for an already-notified thread, and
+ // afterwards we start coordinating for a sleep.
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+ let m = self.lock.lock().unwrap();
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read again here, see `park`.
+ let old = self.state.swap(EMPTY, SeqCst);
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => panic!("inconsistent park_timeout state"),
+ }
+
+ // Wait with a timeout, and if we spuriously wake up or otherwise wake up
+ // from a notification we just want to unconditionally set the state back to
+ // empty, either consuming a notification or un-flagging ourselves as
+ // parked.
+ let (_m, _result) = self.cvar.wait_timeout(m, dur).unwrap();
+ match self.state.swap(EMPTY, SeqCst) {
+ NOTIFIED => {} // got a notification, hurray!
+ PARKED => {} // no notification, alas
+ n => panic!("inconsistent park_timeout state: {}", n),
+ }
+ }
+
+ pub fn unpark(&self) {
+ // To ensure the unparked thread will observe any writes we made
+ // before this call, we must perform a release operation that `park`
+ // can synchronize with. To do that we must write `NOTIFIED` even if
+ // `state` is already `NOTIFIED`. That is why this must be a swap
+ // rather than a compare-and-swap that returns if it reads `NOTIFIED`
+ // on failure.
+ match self.state.swap(NOTIFIED, SeqCst) {
+ EMPTY => return, // no one was waiting
+ NOTIFIED => return, // already unparked
+ PARKED => {} // gotta go wake someone up
+ _ => panic!("inconsistent state in unpark"),
+ }
+
+ // There is a period between when the parked thread sets `state` to
+ // `PARKED` (or last checked `state` in the case of a spurious wake
+ // up) and when it actually waits on `cvar`. If we were to notify
+ // during this period it would be ignored and then when the parked
+ // thread went to sleep it would never wake up. Fortunately, it has
+ // `lock` locked at this stage so we can acquire `lock` to wait until
+ // it is ready to receive the notification.
+ //
+ // Releasing `lock` before the call to `notify_one` means that when the
+ // parked thread wakes it doesn't get woken only to have to wait for us
+ // to release `lock`.
+ drop(self.lock.lock().unwrap());
+ self.cvar.notify_one()
+ }
+}
diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs
new file mode 100644
index 0000000..23c17c8
--- /dev/null
+++ b/library/std/src/sys_common/thread_parker/mod.rs
@@ -0,0 +1,9 @@
+cfg_if::cfg_if! {
+ if #[cfg(any(target_os = "linux", target_os = "android"))] {
+ mod futex;
+ pub use futex::Parker;
+ } else {
+ mod generic;
+ pub use generic::Parker;
+ }
+}
diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs
index bdb6a05..7d4b0d5 100644
--- a/library/std/src/sys_common/wtf8.rs
+++ b/library/std/src/sys_common/wtf8.rs
@@ -15,6 +15,9 @@
// unix (it's mostly used on windows), so don't worry about dead code here.
#![allow(dead_code)]
+#[cfg(test)]
+mod tests;
+
use core::str::next_code_point;
use crate::borrow::Cow;
@@ -879,407 +882,3 @@
0xfeu8.hash(state)
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::borrow::Cow;
-
- #[test]
- fn code_point_from_u32() {
- assert!(CodePoint::from_u32(0).is_some());
- assert!(CodePoint::from_u32(0xD800).is_some());
- assert!(CodePoint::from_u32(0x10FFFF).is_some());
- assert!(CodePoint::from_u32(0x110000).is_none());
- }
-
- #[test]
- fn code_point_to_u32() {
- fn c(value: u32) -> CodePoint {
- CodePoint::from_u32(value).unwrap()
- }
- assert_eq!(c(0).to_u32(), 0);
- assert_eq!(c(0xD800).to_u32(), 0xD800);
- assert_eq!(c(0x10FFFF).to_u32(), 0x10FFFF);
- }
-
- #[test]
- fn code_point_from_char() {
- assert_eq!(CodePoint::from_char('a').to_u32(), 0x61);
- assert_eq!(CodePoint::from_char('š©').to_u32(), 0x1F4A9);
- }
-
- #[test]
- fn code_point_to_string() {
- assert_eq!(format!("{:?}", CodePoint::from_char('a')), "U+0061");
- assert_eq!(format!("{:?}", CodePoint::from_char('š©')), "U+1F4A9");
- }
-
- #[test]
- fn code_point_to_char() {
- fn c(value: u32) -> CodePoint {
- CodePoint::from_u32(value).unwrap()
- }
- assert_eq!(c(0x61).to_char(), Some('a'));
- assert_eq!(c(0x1F4A9).to_char(), Some('š©'));
- assert_eq!(c(0xD800).to_char(), None);
- }
-
- #[test]
- fn code_point_to_char_lossy() {
- fn c(value: u32) -> CodePoint {
- CodePoint::from_u32(value).unwrap()
- }
- assert_eq!(c(0x61).to_char_lossy(), 'a');
- assert_eq!(c(0x1F4A9).to_char_lossy(), 'š©');
- assert_eq!(c(0xD800).to_char_lossy(), '\u{FFFD}');
- }
-
- #[test]
- fn wtf8buf_new() {
- assert_eq!(Wtf8Buf::new().bytes, b"");
- }
-
- #[test]
- fn wtf8buf_from_str() {
- assert_eq!(Wtf8Buf::from_str("").bytes, b"");
- assert_eq!(Wtf8Buf::from_str("aé š©").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
- }
-
- #[test]
- fn wtf8buf_from_string() {
- assert_eq!(Wtf8Buf::from_string(String::from("")).bytes, b"");
- assert_eq!(
- Wtf8Buf::from_string(String::from("aé š©")).bytes,
- b"a\xC3\xA9 \xF0\x9F\x92\xA9"
- );
- }
-
- #[test]
- fn wtf8buf_from_wide() {
- assert_eq!(Wtf8Buf::from_wide(&[]).bytes, b"");
- assert_eq!(
- Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]).bytes,
- b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9"
- );
- }
-
- #[test]
- fn wtf8buf_push_str() {
- let mut string = Wtf8Buf::new();
- assert_eq!(string.bytes, b"");
- string.push_str("aé š©");
- assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
- }
-
- #[test]
- fn wtf8buf_push_char() {
- let mut string = Wtf8Buf::from_str("aé ");
- assert_eq!(string.bytes, b"a\xC3\xA9 ");
- string.push_char('š©');
- assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
- }
-
- #[test]
- fn wtf8buf_push() {
- let mut string = Wtf8Buf::from_str("aé ");
- assert_eq!(string.bytes, b"a\xC3\xA9 ");
- string.push(CodePoint::from_char('š©'));
- assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
-
- fn c(value: u32) -> CodePoint {
- CodePoint::from_u32(value).unwrap()
- }
-
- let mut string = Wtf8Buf::new();
- string.push(c(0xD83D)); // lead
- string.push(c(0xDCA9)); // trail
- assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic!
-
- let mut string = Wtf8Buf::new();
- string.push(c(0xD83D)); // lead
- string.push(c(0x20)); // not surrogate
- string.push(c(0xDCA9)); // trail
- assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
-
- let mut string = Wtf8Buf::new();
- string.push(c(0xD800)); // lead
- string.push(c(0xDBFF)); // lead
- assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF");
-
- let mut string = Wtf8Buf::new();
- string.push(c(0xD800)); // lead
- string.push(c(0xE000)); // not surrogate
- assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80");
-
- let mut string = Wtf8Buf::new();
- string.push(c(0xD7FF)); // not surrogate
- string.push(c(0xDC00)); // trail
- assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80");
-
- let mut string = Wtf8Buf::new();
- string.push(c(0x61)); // not surrogate, < 3 bytes
- string.push(c(0xDC00)); // trail
- assert_eq!(string.bytes, b"\x61\xED\xB0\x80");
-
- let mut string = Wtf8Buf::new();
- string.push(c(0xDC00)); // trail
- assert_eq!(string.bytes, b"\xED\xB0\x80");
- }
-
- #[test]
- fn wtf8buf_push_wtf8() {
- let mut string = Wtf8Buf::from_str("aé");
- assert_eq!(string.bytes, b"a\xC3\xA9");
- string.push_wtf8(Wtf8::from_str(" š©"));
- assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
-
- fn w(v: &[u8]) -> &Wtf8 {
- unsafe { Wtf8::from_bytes_unchecked(v) }
- }
-
- let mut string = Wtf8Buf::new();
- string.push_wtf8(w(b"\xED\xA0\xBD")); // lead
- string.push_wtf8(w(b"\xED\xB2\xA9")); // trail
- assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic!
-
- let mut string = Wtf8Buf::new();
- string.push_wtf8(w(b"\xED\xA0\xBD")); // lead
- string.push_wtf8(w(b" ")); // not surrogate
- string.push_wtf8(w(b"\xED\xB2\xA9")); // trail
- assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
-
- let mut string = Wtf8Buf::new();
- string.push_wtf8(w(b"\xED\xA0\x80")); // lead
- string.push_wtf8(w(b"\xED\xAF\xBF")); // lead
- assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF");
-
- let mut string = Wtf8Buf::new();
- string.push_wtf8(w(b"\xED\xA0\x80")); // lead
- string.push_wtf8(w(b"\xEE\x80\x80")); // not surrogate
- assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80");
-
- let mut string = Wtf8Buf::new();
- string.push_wtf8(w(b"\xED\x9F\xBF")); // not surrogate
- string.push_wtf8(w(b"\xED\xB0\x80")); // trail
- assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80");
-
- let mut string = Wtf8Buf::new();
- string.push_wtf8(w(b"a")); // not surrogate, < 3 bytes
- string.push_wtf8(w(b"\xED\xB0\x80")); // trail
- assert_eq!(string.bytes, b"\x61\xED\xB0\x80");
-
- let mut string = Wtf8Buf::new();
- string.push_wtf8(w(b"\xED\xB0\x80")); // trail
- assert_eq!(string.bytes, b"\xED\xB0\x80");
- }
-
- #[test]
- fn wtf8buf_truncate() {
- let mut string = Wtf8Buf::from_str("aé");
- string.truncate(1);
- assert_eq!(string.bytes, b"a");
- }
-
- #[test]
- #[should_panic]
- fn wtf8buf_truncate_fail_code_point_boundary() {
- let mut string = Wtf8Buf::from_str("aé");
- string.truncate(2);
- }
-
- #[test]
- #[should_panic]
- fn wtf8buf_truncate_fail_longer() {
- let mut string = Wtf8Buf::from_str("aé");
- string.truncate(4);
- }
-
- #[test]
- fn wtf8buf_into_string() {
- let mut string = Wtf8Buf::from_str("aé š©");
- assert_eq!(string.clone().into_string(), Ok(String::from("aé š©")));
- string.push(CodePoint::from_u32(0xD800).unwrap());
- assert_eq!(string.clone().into_string(), Err(string));
- }
-
- #[test]
- fn wtf8buf_into_string_lossy() {
- let mut string = Wtf8Buf::from_str("aé š©");
- assert_eq!(string.clone().into_string_lossy(), String::from("aé š©"));
- string.push(CodePoint::from_u32(0xD800).unwrap());
- assert_eq!(string.clone().into_string_lossy(), String::from("aé š©ļæ½"));
- }
-
- #[test]
- fn wtf8buf_from_iterator() {
- fn f(values: &[u32]) -> Wtf8Buf {
- values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::<Wtf8Buf>()
- }
- assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
-
- assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic!
- assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
- assert_eq!(f(&[0xD800, 0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF");
- assert_eq!(f(&[0xD800, 0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80");
- assert_eq!(f(&[0xD7FF, 0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80");
- assert_eq!(f(&[0x61, 0xDC00]).bytes, b"\x61\xED\xB0\x80");
- assert_eq!(f(&[0xDC00]).bytes, b"\xED\xB0\x80");
- }
-
- #[test]
- fn wtf8buf_extend() {
- fn e(initial: &[u32], extended: &[u32]) -> Wtf8Buf {
- fn c(value: &u32) -> CodePoint {
- CodePoint::from_u32(*value).unwrap()
- }
- let mut string = initial.iter().map(c).collect::<Wtf8Buf>();
- string.extend(extended.iter().map(c));
- string
- }
-
- assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
-
- assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic!
- assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
- assert_eq!(e(&[0xD800], &[0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF");
- assert_eq!(e(&[0xD800], &[0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80");
- assert_eq!(e(&[0xD7FF], &[0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80");
- assert_eq!(e(&[0x61], &[0xDC00]).bytes, b"\x61\xED\xB0\x80");
- assert_eq!(e(&[], &[0xDC00]).bytes, b"\xED\xB0\x80");
- }
-
- #[test]
- fn wtf8buf_show() {
- let mut string = Wtf8Buf::from_str("a\té \u{7f}š©\r");
- string.push(CodePoint::from_u32(0xD800).unwrap());
- assert_eq!(format!("{:?}", string), "\"a\\té \\u{7f}\u{1f4a9}\\r\\u{d800}\"");
- }
-
- #[test]
- fn wtf8buf_as_slice() {
- assert_eq!(Wtf8Buf::from_str("aé").as_slice(), Wtf8::from_str("aé"));
- }
-
- #[test]
- fn wtf8buf_show_str() {
- let text = "a\té š©\r";
- let string = Wtf8Buf::from_str(text);
- assert_eq!(format!("{:?}", text), format!("{:?}", string));
- }
-
- #[test]
- fn wtf8_from_str() {
- assert_eq!(&Wtf8::from_str("").bytes, b"");
- assert_eq!(&Wtf8::from_str("aé š©").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
- }
-
- #[test]
- fn wtf8_len() {
- assert_eq!(Wtf8::from_str("").len(), 0);
- assert_eq!(Wtf8::from_str("aé š©").len(), 8);
- }
-
- #[test]
- fn wtf8_slice() {
- assert_eq!(&Wtf8::from_str("aé š©")[1..4].bytes, b"\xC3\xA9 ");
- }
-
- #[test]
- #[should_panic]
- fn wtf8_slice_not_code_point_boundary() {
- &Wtf8::from_str("aé š©")[2..4];
- }
-
- #[test]
- fn wtf8_slice_from() {
- assert_eq!(&Wtf8::from_str("aé š©")[1..].bytes, b"\xC3\xA9 \xF0\x9F\x92\xA9");
- }
-
- #[test]
- #[should_panic]
- fn wtf8_slice_from_not_code_point_boundary() {
- &Wtf8::from_str("aé š©")[2..];
- }
-
- #[test]
- fn wtf8_slice_to() {
- assert_eq!(&Wtf8::from_str("aé š©")[..4].bytes, b"a\xC3\xA9 ");
- }
-
- #[test]
- #[should_panic]
- fn wtf8_slice_to_not_code_point_boundary() {
- &Wtf8::from_str("aé š©")[5..];
- }
-
- #[test]
- fn wtf8_ascii_byte_at() {
- let slice = Wtf8::from_str("aé š©");
- assert_eq!(slice.ascii_byte_at(0), b'a');
- assert_eq!(slice.ascii_byte_at(1), b'\xFF');
- assert_eq!(slice.ascii_byte_at(2), b'\xFF');
- assert_eq!(slice.ascii_byte_at(3), b' ');
- assert_eq!(slice.ascii_byte_at(4), b'\xFF');
- }
-
- #[test]
- fn wtf8_code_points() {
- fn c(value: u32) -> CodePoint {
- CodePoint::from_u32(value).unwrap()
- }
- fn cp(string: &Wtf8Buf) -> Vec<Option<char>> {
- string.code_points().map(|c| c.to_char()).collect::<Vec<_>>()
- }
- let mut string = Wtf8Buf::from_str("é ");
- assert_eq!(cp(&string), [Some('é'), Some(' ')]);
- string.push(c(0xD83D));
- assert_eq!(cp(&string), [Some('é'), Some(' '), None]);
- string.push(c(0xDCA9));
- assert_eq!(cp(&string), [Some('é'), Some(' '), Some('š©')]);
- }
-
- #[test]
- fn wtf8_as_str() {
- assert_eq!(Wtf8::from_str("").as_str(), Some(""));
- assert_eq!(Wtf8::from_str("aé š©").as_str(), Some("aé š©"));
- let mut string = Wtf8Buf::new();
- string.push(CodePoint::from_u32(0xD800).unwrap());
- assert_eq!(string.as_str(), None);
- }
-
- #[test]
- fn wtf8_to_string_lossy() {
- assert_eq!(Wtf8::from_str("").to_string_lossy(), Cow::Borrowed(""));
- 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é š©ļæ½"));
- assert_eq!(string.to_string_lossy(), expected);
- }
-
- #[test]
- fn wtf8_display() {
- fn d(b: &[u8]) -> String {
- (&unsafe { Wtf8::from_bytes_unchecked(b) }).to_string()
- }
-
- assert_eq!("", d("".as_bytes()));
- assert_eq!("aé š©", d("aé š©".as_bytes()));
-
- let mut string = Wtf8Buf::from_str("aé š©");
- string.push(CodePoint::from_u32(0xD800).unwrap());
- assert_eq!("aé š©ļæ½", d(string.as_inner()));
- }
-
- #[test]
- fn wtf8_encode_wide() {
- let mut string = Wtf8Buf::from_str("aé ");
- string.push(CodePoint::from_u32(0xD83D).unwrap());
- string.push_char('š©');
- assert_eq!(
- string.encode_wide().collect::<Vec<_>>(),
- vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]
- );
- }
-}
diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs
new file mode 100644
index 0000000..385e01f
--- /dev/null
+++ b/library/std/src/sys_common/wtf8/tests.rs
@@ -0,0 +1,397 @@
+use super::*;
+use crate::borrow::Cow;
+
+#[test]
+fn code_point_from_u32() {
+ assert!(CodePoint::from_u32(0).is_some());
+ assert!(CodePoint::from_u32(0xD800).is_some());
+ assert!(CodePoint::from_u32(0x10FFFF).is_some());
+ assert!(CodePoint::from_u32(0x110000).is_none());
+}
+
+#[test]
+fn code_point_to_u32() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ assert_eq!(c(0).to_u32(), 0);
+ assert_eq!(c(0xD800).to_u32(), 0xD800);
+ assert_eq!(c(0x10FFFF).to_u32(), 0x10FFFF);
+}
+
+#[test]
+fn code_point_from_char() {
+ assert_eq!(CodePoint::from_char('a').to_u32(), 0x61);
+ assert_eq!(CodePoint::from_char('š©').to_u32(), 0x1F4A9);
+}
+
+#[test]
+fn code_point_to_string() {
+ assert_eq!(format!("{:?}", CodePoint::from_char('a')), "U+0061");
+ assert_eq!(format!("{:?}", CodePoint::from_char('š©')), "U+1F4A9");
+}
+
+#[test]
+fn code_point_to_char() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ assert_eq!(c(0x61).to_char(), Some('a'));
+ assert_eq!(c(0x1F4A9).to_char(), Some('š©'));
+ assert_eq!(c(0xD800).to_char(), None);
+}
+
+#[test]
+fn code_point_to_char_lossy() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ assert_eq!(c(0x61).to_char_lossy(), 'a');
+ assert_eq!(c(0x1F4A9).to_char_lossy(), 'š©');
+ assert_eq!(c(0xD800).to_char_lossy(), '\u{FFFD}');
+}
+
+#[test]
+fn wtf8buf_new() {
+ assert_eq!(Wtf8Buf::new().bytes, b"");
+}
+
+#[test]
+fn wtf8buf_from_str() {
+ assert_eq!(Wtf8Buf::from_str("").bytes, b"");
+ assert_eq!(Wtf8Buf::from_str("aé š©").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8buf_from_string() {
+ assert_eq!(Wtf8Buf::from_string(String::from("")).bytes, b"");
+ assert_eq!(Wtf8Buf::from_string(String::from("aé š©")).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8buf_from_wide() {
+ assert_eq!(Wtf8Buf::from_wide(&[]).bytes, b"");
+ assert_eq!(
+ Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]).bytes,
+ b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9"
+ );
+}
+
+#[test]
+fn wtf8buf_push_str() {
+ let mut string = Wtf8Buf::new();
+ assert_eq!(string.bytes, b"");
+ string.push_str("aé š©");
+ assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8buf_push_char() {
+ let mut string = Wtf8Buf::from_str("aé ");
+ assert_eq!(string.bytes, b"a\xC3\xA9 ");
+ string.push_char('š©');
+ assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8buf_push() {
+ let mut string = Wtf8Buf::from_str("aé ");
+ assert_eq!(string.bytes, b"a\xC3\xA9 ");
+ string.push(CodePoint::from_char('š©'));
+ assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD83D)); // lead
+ string.push(c(0xDCA9)); // trail
+ assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic!
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD83D)); // lead
+ string.push(c(0x20)); // not surrogate
+ string.push(c(0xDCA9)); // trail
+ assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD800)); // lead
+ string.push(c(0xDBFF)); // lead
+ assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD800)); // lead
+ string.push(c(0xE000)); // not surrogate
+ assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xD7FF)); // not surrogate
+ string.push(c(0xDC00)); // trail
+ assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0x61)); // not surrogate, < 3 bytes
+ string.push(c(0xDC00)); // trail
+ assert_eq!(string.bytes, b"\x61\xED\xB0\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push(c(0xDC00)); // trail
+ assert_eq!(string.bytes, b"\xED\xB0\x80");
+}
+
+#[test]
+fn wtf8buf_push_wtf8() {
+ let mut string = Wtf8Buf::from_str("aé");
+ assert_eq!(string.bytes, b"a\xC3\xA9");
+ string.push_wtf8(Wtf8::from_str(" š©"));
+ assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+ fn w(v: &[u8]) -> &Wtf8 {
+ unsafe { Wtf8::from_bytes_unchecked(v) }
+ }
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xA0\xBD")); // lead
+ string.push_wtf8(w(b"\xED\xB2\xA9")); // trail
+ assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic!
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xA0\xBD")); // lead
+ string.push_wtf8(w(b" ")); // not surrogate
+ string.push_wtf8(w(b"\xED\xB2\xA9")); // trail
+ assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xA0\x80")); // lead
+ string.push_wtf8(w(b"\xED\xAF\xBF")); // lead
+ assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xA0\x80")); // lead
+ string.push_wtf8(w(b"\xEE\x80\x80")); // not surrogate
+ assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\x9F\xBF")); // not surrogate
+ string.push_wtf8(w(b"\xED\xB0\x80")); // trail
+ assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"a")); // not surrogate, < 3 bytes
+ string.push_wtf8(w(b"\xED\xB0\x80")); // trail
+ assert_eq!(string.bytes, b"\x61\xED\xB0\x80");
+
+ let mut string = Wtf8Buf::new();
+ string.push_wtf8(w(b"\xED\xB0\x80")); // trail
+ assert_eq!(string.bytes, b"\xED\xB0\x80");
+}
+
+#[test]
+fn wtf8buf_truncate() {
+ let mut string = Wtf8Buf::from_str("aé");
+ string.truncate(1);
+ assert_eq!(string.bytes, b"a");
+}
+
+#[test]
+#[should_panic]
+fn wtf8buf_truncate_fail_code_point_boundary() {
+ let mut string = Wtf8Buf::from_str("aé");
+ string.truncate(2);
+}
+
+#[test]
+#[should_panic]
+fn wtf8buf_truncate_fail_longer() {
+ let mut string = Wtf8Buf::from_str("aé");
+ string.truncate(4);
+}
+
+#[test]
+fn wtf8buf_into_string() {
+ let mut string = Wtf8Buf::from_str("aé š©");
+ assert_eq!(string.clone().into_string(), Ok(String::from("aé š©")));
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!(string.clone().into_string(), Err(string));
+}
+
+#[test]
+fn wtf8buf_into_string_lossy() {
+ let mut string = Wtf8Buf::from_str("aé š©");
+ assert_eq!(string.clone().into_string_lossy(), String::from("aé š©"));
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!(string.clone().into_string_lossy(), String::from("aé š©ļæ½"));
+}
+
+#[test]
+fn wtf8buf_from_iterator() {
+ fn f(values: &[u32]) -> Wtf8Buf {
+ values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::<Wtf8Buf>()
+ }
+ assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+ assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic!
+ assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
+ assert_eq!(f(&[0xD800, 0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF");
+ assert_eq!(f(&[0xD800, 0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80");
+ assert_eq!(f(&[0xD7FF, 0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80");
+ assert_eq!(f(&[0x61, 0xDC00]).bytes, b"\x61\xED\xB0\x80");
+ assert_eq!(f(&[0xDC00]).bytes, b"\xED\xB0\x80");
+}
+
+#[test]
+fn wtf8buf_extend() {
+ fn e(initial: &[u32], extended: &[u32]) -> Wtf8Buf {
+ fn c(value: &u32) -> CodePoint {
+ CodePoint::from_u32(*value).unwrap()
+ }
+ let mut string = initial.iter().map(c).collect::<Wtf8Buf>();
+ string.extend(extended.iter().map(c));
+ string
+ }
+
+ assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+
+ assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic!
+ assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]).bytes, b"\xED\xA0\xBD \xED\xB2\xA9");
+ assert_eq!(e(&[0xD800], &[0xDBFF]).bytes, b"\xED\xA0\x80\xED\xAF\xBF");
+ assert_eq!(e(&[0xD800], &[0xE000]).bytes, b"\xED\xA0\x80\xEE\x80\x80");
+ assert_eq!(e(&[0xD7FF], &[0xDC00]).bytes, b"\xED\x9F\xBF\xED\xB0\x80");
+ assert_eq!(e(&[0x61], &[0xDC00]).bytes, b"\x61\xED\xB0\x80");
+ assert_eq!(e(&[], &[0xDC00]).bytes, b"\xED\xB0\x80");
+}
+
+#[test]
+fn wtf8buf_show() {
+ let mut string = Wtf8Buf::from_str("a\té \u{7f}š©\r");
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!(format!("{:?}", string), "\"a\\té \\u{7f}\u{1f4a9}\\r\\u{d800}\"");
+}
+
+#[test]
+fn wtf8buf_as_slice() {
+ assert_eq!(Wtf8Buf::from_str("aé").as_slice(), Wtf8::from_str("aé"));
+}
+
+#[test]
+fn wtf8buf_show_str() {
+ let text = "a\té š©\r";
+ let string = Wtf8Buf::from_str(text);
+ assert_eq!(format!("{:?}", text), format!("{:?}", string));
+}
+
+#[test]
+fn wtf8_from_str() {
+ assert_eq!(&Wtf8::from_str("").bytes, b"");
+ assert_eq!(&Wtf8::from_str("aé š©").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+fn wtf8_len() {
+ assert_eq!(Wtf8::from_str("").len(), 0);
+ assert_eq!(Wtf8::from_str("aé š©").len(), 8);
+}
+
+#[test]
+fn wtf8_slice() {
+ assert_eq!(&Wtf8::from_str("aé š©")[1..4].bytes, b"\xC3\xA9 ");
+}
+
+#[test]
+#[should_panic]
+fn wtf8_slice_not_code_point_boundary() {
+ &Wtf8::from_str("aé š©")[2..4];
+}
+
+#[test]
+fn wtf8_slice_from() {
+ assert_eq!(&Wtf8::from_str("aé š©")[1..].bytes, b"\xC3\xA9 \xF0\x9F\x92\xA9");
+}
+
+#[test]
+#[should_panic]
+fn wtf8_slice_from_not_code_point_boundary() {
+ &Wtf8::from_str("aé š©")[2..];
+}
+
+#[test]
+fn wtf8_slice_to() {
+ assert_eq!(&Wtf8::from_str("aé š©")[..4].bytes, b"a\xC3\xA9 ");
+}
+
+#[test]
+#[should_panic]
+fn wtf8_slice_to_not_code_point_boundary() {
+ &Wtf8::from_str("aé š©")[5..];
+}
+
+#[test]
+fn wtf8_ascii_byte_at() {
+ let slice = Wtf8::from_str("aé š©");
+ assert_eq!(slice.ascii_byte_at(0), b'a');
+ assert_eq!(slice.ascii_byte_at(1), b'\xFF');
+ assert_eq!(slice.ascii_byte_at(2), b'\xFF');
+ assert_eq!(slice.ascii_byte_at(3), b' ');
+ assert_eq!(slice.ascii_byte_at(4), b'\xFF');
+}
+
+#[test]
+fn wtf8_code_points() {
+ fn c(value: u32) -> CodePoint {
+ CodePoint::from_u32(value).unwrap()
+ }
+ fn cp(string: &Wtf8Buf) -> Vec<Option<char>> {
+ string.code_points().map(|c| c.to_char()).collect::<Vec<_>>()
+ }
+ let mut string = Wtf8Buf::from_str("é ");
+ assert_eq!(cp(&string), [Some('é'), Some(' ')]);
+ string.push(c(0xD83D));
+ assert_eq!(cp(&string), [Some('é'), Some(' '), None]);
+ string.push(c(0xDCA9));
+ assert_eq!(cp(&string), [Some('é'), Some(' '), Some('š©')]);
+}
+
+#[test]
+fn wtf8_as_str() {
+ assert_eq!(Wtf8::from_str("").as_str(), Some(""));
+ assert_eq!(Wtf8::from_str("aé š©").as_str(), Some("aé š©"));
+ let mut string = Wtf8Buf::new();
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!(string.as_str(), None);
+}
+
+#[test]
+fn wtf8_to_string_lossy() {
+ assert_eq!(Wtf8::from_str("").to_string_lossy(), Cow::Borrowed(""));
+ 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é š©ļæ½"));
+ assert_eq!(string.to_string_lossy(), expected);
+}
+
+#[test]
+fn wtf8_display() {
+ fn d(b: &[u8]) -> String {
+ (&unsafe { Wtf8::from_bytes_unchecked(b) }).to_string()
+ }
+
+ assert_eq!("", d("".as_bytes()));
+ assert_eq!("aé š©", d("aé š©".as_bytes()));
+
+ let mut string = Wtf8Buf::from_str("aé š©");
+ string.push(CodePoint::from_u32(0xD800).unwrap());
+ assert_eq!("aé š©ļæ½", d(string.as_inner()));
+}
+
+#[test]
+fn wtf8_encode_wide() {
+ let mut string = Wtf8Buf::from_str("aé ");
+ string.push(CodePoint::from_u32(0xD83D).unwrap());
+ string.push_char('š©');
+ assert_eq!(
+ string.encode_wide().collect::<Vec<_>>(),
+ vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]
+ );
+}
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index a456296..d8db5d1 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -2,6 +2,12 @@
#![unstable(feature = "thread_local_internals", issue = "none")]
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
+#[cfg(test)]
+mod dynamic_tests;
+
use crate::error::Error;
use crate::fmt;
@@ -219,6 +225,7 @@
reason = "recently added to create a key",
issue = "none"
)]
+ #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
pub const unsafe fn new(inner: unsafe fn() -> Option<&'static T>) -> LocalKey<T> {
LocalKey { inner }
}
@@ -282,15 +289,23 @@
}
pub unsafe fn get(&self) -> Option<&'static T> {
- (*self.inner.get()).as_ref()
+ // SAFETY: The caller must ensure no reference is ever handed out to
+ // the inner cell nor mutable reference to the Option<T> inside said
+ // cell. This make it safe to hand a reference, though the lifetime
+ // of 'static is itself unsafe, making the get method unsafe.
+ unsafe { (*self.inner.get()).as_ref() }
}
+ /// The caller must ensure that no reference is active: this method
+ /// needs unique access.
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();
+ // SAFETY:
+ //
// note that this can in theory just be `*ptr = Some(value)`, but due to
// the compiler will currently codegen that pattern with something like:
//
@@ -303,22 +318,36 @@
// 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.
- let _ = mem::replace(&mut *ptr, Some(value));
+ //
+ // The precondition also ensures that we are the only one accessing
+ // `self` at the moment so replacing is fine.
+ unsafe {
+ let _ = 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(),
+ // SAFETY: With the call to `mem::replace` it is guaranteed there is
+ // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked`
+ // will never be reached.
+ unsafe {
+ // 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(),
+ }
}
}
+ /// The other methods hand out references while taking &self.
+ /// As such, callers of this method must ensure no `&` and `&mut` are
+ /// available and used at the same time.
#[allow(unused)]
pub unsafe fn take(&mut self) -> Option<T> {
- (*self.inner.get()).take()
+ // SAFETY: See doc comment for this method.
+ unsafe { (*self.inner.get()).take() }
}
}
}
@@ -349,10 +378,17 @@
}
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),
+ // SAFETY: The caller must ensure no reference is ever handed out to
+ // the inner cell nor mutable reference to the Option<T> inside said
+ // cell. This make it safe to hand a reference, though the lifetime
+ // of 'static is itself unsafe, making the get method unsafe.
+ let value = unsafe {
+ match self.inner.get() {
+ Some(ref value) => value,
+ None => self.inner.initialize(init),
+ }
};
+
Some(value)
}
}
@@ -407,9 +443,18 @@
}
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),
+ // SAFETY: See the definitions of `LazyKeyInner::get` and
+ // `try_initialize` for more informations.
+ //
+ // The caller must ensure no mutable references are ever active to
+ // the inner cell or the inner T when this is called.
+ // The `try_initialize` is dependant on the passed `init` function
+ // for this.
+ unsafe {
+ match self.inner.get() {
+ Some(val) => Some(val),
+ None => self.try_initialize(init),
+ }
}
}
@@ -418,13 +463,14 @@
// 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.
+ // be performed for every call to `Key::get`.
// LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
- #[cold]
+ #[inline(never)]
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))
+ // SAFETY: See comment above (this function doc).
+ if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } {
+ // SAFETY: See comment above (his function doc).
+ Some(unsafe { self.inner.initialize(init) })
} else {
None
}
@@ -436,8 +482,12 @@
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>);
+ // SAFETY: dtor registration happens before initialization.
+ // Passing `self` as a pointer while using `destroy_value<T>`
+ // is safe because the function will build a pointer to a
+ // Key<T>, which is the type of self and so find the correct
+ // size.
+ unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) };
self.dtor_state.set(DtorState::Registered);
true
}
@@ -453,13 +503,21 @@
unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
let ptr = ptr as *mut Key<T>;
+ // SAFETY:
+ //
+ // The pointer `ptr` has been built just above and comes from
+ // `try_register_dtor` where it is originally a Key<T> coming from `self`,
+ // making it non-NUL and of the correct type.
+ //
// 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);
+ unsafe {
+ let value = (*ptr).inner.take();
+ (*ptr).dtor_state.set(DtorState::RunningOrHasRun);
+ drop(value);
+ }
}
}
@@ -492,25 +550,35 @@
}
impl<T: 'static> Key<T> {
+ #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
pub const fn new() -> Key<T> {
Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData }
}
+ /// It is a requirement for the caller to ensure that no mutable
+ /// reference is active when this method is called.
pub unsafe fn get(&'static self, init: fn() -> T) -> Option<&'static T> {
- let ptr = self.os.get() as *mut Value<T>;
+ // SAFETY: See the documentation for this method.
+ let ptr = unsafe { self.os.get() as *mut Value<T> };
if ptr as usize > 1 {
- if let Some(ref value) = (*ptr).inner.get() {
+ // SAFETY: the check ensured the pointer is safe (its destructor
+ // is not running) + it is coming from a trusted source (self).
+ if let Some(ref value) = unsafe { (*ptr).inner.get() } {
return Some(value);
}
}
- self.try_initialize(init)
+ // SAFETY: At this point we are sure we have no value and so
+ // initializing (or trying to) is safe.
+ unsafe { self.try_initialize(init) }
}
// `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>;
+ // SAFETY: No mutable references are ever handed out meaning getting
+ // the value is ok.
+ let ptr = unsafe { self.os.get() as *mut Value<T> };
if ptr as usize == 1 {
// destructor is running
return None;
@@ -521,18 +589,26 @@
// 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);
+ // SAFETY: At this point we are sure there is no value inside
+ // ptr so setting it will not affect anyone else.
+ unsafe {
+ self.os.set(ptr as *mut u8);
+ }
ptr
} else {
// recursive initialization
ptr
};
- Some((*ptr).inner.initialize(init))
+ // SAFETY: ptr has been ensured as non-NUL just above an so can be
+ // dereferenced safely.
+ unsafe { Some((*ptr).inner.initialize(init)) }
}
}
unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
+ // SAFETY:
+ //
// The OS TLS ensures that this key contains a NULL value when this
// destructor starts to run. We set it back to a sentinel value of 1 to
// ensure that any future calls to `get` for this thread will return
@@ -540,210 +616,12 @@
//
// Note that to prevent an infinite loop we reset it back to null right
// before we return from the destructor ourselves.
- let ptr = Box::from_raw(ptr as *mut Value<T>);
- let key = ptr.key;
- key.os.set(1 as *mut u8);
- drop(ptr);
- key.os.set(ptr::null_mut());
- }
-}
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use crate::cell::{Cell, UnsafeCell};
- use crate::sync::mpsc::{channel, Sender};
- use crate::thread;
-
- struct Foo(Sender<()>);
-
- impl Drop for Foo {
- fn drop(&mut self) {
- let Foo(ref s) = *self;
- s.send(()).unwrap();
+ unsafe {
+ let ptr = Box::from_raw(ptr as *mut Value<T>);
+ let key = ptr.key;
+ key.os.set(1 as *mut u8);
+ drop(ptr);
+ key.os.set(ptr::null_mut());
}
}
-
- #[test]
- fn smoke_no_dtor() {
- thread_local!(static FOO: Cell<i32> = Cell::new(1));
-
- FOO.with(|f| {
- assert_eq!(f.get(), 1);
- f.set(2);
- });
- let (tx, rx) = channel();
- let _t = thread::spawn(move || {
- FOO.with(|f| {
- assert_eq!(f.get(), 1);
- });
- tx.send(()).unwrap();
- });
- rx.recv().unwrap();
-
- FOO.with(|f| {
- assert_eq!(f.get(), 2);
- });
- }
-
- #[test]
- fn states() {
- struct Foo;
- impl Drop for Foo {
- fn drop(&mut self) {
- assert!(FOO.try_with(|_| ()).is_err());
- }
- }
- thread_local!(static FOO: Foo = Foo);
-
- thread::spawn(|| {
- assert!(FOO.try_with(|_| ()).is_ok());
- })
- .join()
- .ok()
- .expect("thread panicked");
- }
-
- #[test]
- fn smoke_dtor() {
- thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell::new(None));
-
- let (tx, rx) = channel();
- let _t = thread::spawn(move || unsafe {
- let mut tx = Some(tx);
- FOO.with(|f| {
- *f.get() = Some(Foo(tx.take().unwrap()));
- });
- });
- rx.recv().unwrap();
- }
-
- #[test]
- fn circular() {
- struct S1;
- struct S2;
- thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
- thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell::new(None));
- static mut HITS: u32 = 0;
-
- impl Drop for S1 {
- fn drop(&mut self) {
- unsafe {
- HITS += 1;
- if K2.try_with(|_| ()).is_err() {
- assert_eq!(HITS, 3);
- } else {
- if HITS == 1 {
- K2.with(|s| *s.get() = Some(S2));
- } else {
- assert_eq!(HITS, 3);
- }
- }
- }
- }
- }
- impl Drop for S2 {
- fn drop(&mut self) {
- unsafe {
- HITS += 1;
- assert!(K1.try_with(|_| ()).is_ok());
- assert_eq!(HITS, 2);
- K1.with(|s| *s.get() = Some(S1));
- }
- }
- }
-
- thread::spawn(move || {
- drop(S1);
- })
- .join()
- .ok()
- .expect("thread panicked");
- }
-
- #[test]
- fn self_referential() {
- struct S1;
- thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
-
- impl Drop for S1 {
- fn drop(&mut self) {
- assert!(K1.try_with(|_| ()).is_err());
- }
- }
-
- thread::spawn(move || unsafe {
- K1.with(|s| *s.get() = Some(S1));
- })
- .join()
- .ok()
- .expect("thread panicked");
- }
-
- // Note that this test will deadlock if TLS destructors aren't run (this
- // requires the destructor to be run to pass the test).
- #[test]
- fn dtors_in_dtors_in_dtors() {
- struct S1(Sender<()>);
- thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
- thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell::new(None));
-
- impl Drop for S1 {
- fn drop(&mut self) {
- let S1(ref tx) = *self;
- unsafe {
- let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone())));
- }
- }
- }
-
- let (tx, rx) = channel();
- let _t = thread::spawn(move || unsafe {
- let mut tx = Some(tx);
- K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
- });
- rx.recv().unwrap();
- }
-}
-
-#[cfg(test)]
-mod dynamic_tests {
- use crate::cell::RefCell;
- use crate::collections::HashMap;
-
- #[test]
- fn smoke() {
- fn square(i: i32) -> i32 {
- i * i
- }
- thread_local!(static FOO: i32 = square(3));
-
- FOO.with(|f| {
- assert_eq!(*f, 9);
- });
- }
-
- #[test]
- fn hashmap() {
- fn map() -> RefCell<HashMap<i32, i32>> {
- let mut m = HashMap::new();
- m.insert(1, 2);
- RefCell::new(m)
- }
- thread_local!(static FOO: RefCell<HashMap<i32, i32>> = map());
-
- FOO.with(|map| {
- assert_eq!(map.borrow()[&1], 2);
- });
- }
-
- #[test]
- fn refcell_vec() {
- thread_local!(static FOO: RefCell<Vec<u32>> = RefCell::new(vec![1, 2, 3]));
-
- FOO.with(|vec| {
- assert_eq!(vec.borrow().len(), 3);
- vec.borrow_mut().push(4);
- assert_eq!(vec.borrow()[3], 4);
- });
- }
}
diff --git a/library/std/src/thread/local/dynamic_tests.rs b/library/std/src/thread/local/dynamic_tests.rs
new file mode 100644
index 0000000..dd18004
--- /dev/null
+++ b/library/std/src/thread/local/dynamic_tests.rs
@@ -0,0 +1,40 @@
+use crate::cell::RefCell;
+use crate::collections::HashMap;
+use crate::thread_local;
+
+#[test]
+fn smoke() {
+ fn square(i: i32) -> i32 {
+ i * i
+ }
+ thread_local!(static FOO: i32 = square(3));
+
+ FOO.with(|f| {
+ assert_eq!(*f, 9);
+ });
+}
+
+#[test]
+fn hashmap() {
+ fn map() -> RefCell<HashMap<i32, i32>> {
+ let mut m = HashMap::new();
+ m.insert(1, 2);
+ RefCell::new(m)
+ }
+ thread_local!(static FOO: RefCell<HashMap<i32, i32>> = map());
+
+ FOO.with(|map| {
+ assert_eq!(map.borrow()[&1], 2);
+ });
+}
+
+#[test]
+fn refcell_vec() {
+ thread_local!(static FOO: RefCell<Vec<u32>> = RefCell::new(vec![1, 2, 3]));
+
+ FOO.with(|vec| {
+ assert_eq!(vec.borrow().len(), 3);
+ vec.borrow_mut().push(4);
+ assert_eq!(vec.borrow()[3], 4);
+ });
+}
diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs
new file mode 100644
index 0000000..4fb0a08
--- /dev/null
+++ b/library/std/src/thread/local/tests.rs
@@ -0,0 +1,154 @@
+use crate::cell::{Cell, UnsafeCell};
+use crate::sync::mpsc::{channel, Sender};
+use crate::thread;
+use crate::thread_local;
+
+struct Foo(Sender<()>);
+
+impl Drop for Foo {
+ fn drop(&mut self) {
+ let Foo(ref s) = *self;
+ s.send(()).unwrap();
+ }
+}
+
+#[test]
+fn smoke_no_dtor() {
+ thread_local!(static FOO: Cell<i32> = Cell::new(1));
+
+ FOO.with(|f| {
+ assert_eq!(f.get(), 1);
+ f.set(2);
+ });
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move || {
+ FOO.with(|f| {
+ assert_eq!(f.get(), 1);
+ });
+ tx.send(()).unwrap();
+ });
+ rx.recv().unwrap();
+
+ FOO.with(|f| {
+ assert_eq!(f.get(), 2);
+ });
+}
+
+#[test]
+fn states() {
+ struct Foo;
+ impl Drop for Foo {
+ fn drop(&mut self) {
+ assert!(FOO.try_with(|_| ()).is_err());
+ }
+ }
+ thread_local!(static FOO: Foo = Foo);
+
+ thread::spawn(|| {
+ assert!(FOO.try_with(|_| ()).is_ok());
+ })
+ .join()
+ .ok()
+ .expect("thread panicked");
+}
+
+#[test]
+fn smoke_dtor() {
+ thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell::new(None));
+
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move || unsafe {
+ let mut tx = Some(tx);
+ FOO.with(|f| {
+ *f.get() = Some(Foo(tx.take().unwrap()));
+ });
+ });
+ rx.recv().unwrap();
+}
+
+#[test]
+fn circular() {
+ struct S1;
+ struct S2;
+ thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
+ thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell::new(None));
+ static mut HITS: u32 = 0;
+
+ impl Drop for S1 {
+ fn drop(&mut self) {
+ unsafe {
+ HITS += 1;
+ if K2.try_with(|_| ()).is_err() {
+ assert_eq!(HITS, 3);
+ } else {
+ if HITS == 1 {
+ K2.with(|s| *s.get() = Some(S2));
+ } else {
+ assert_eq!(HITS, 3);
+ }
+ }
+ }
+ }
+ }
+ impl Drop for S2 {
+ fn drop(&mut self) {
+ unsafe {
+ HITS += 1;
+ assert!(K1.try_with(|_| ()).is_ok());
+ assert_eq!(HITS, 2);
+ K1.with(|s| *s.get() = Some(S1));
+ }
+ }
+ }
+
+ thread::spawn(move || {
+ drop(S1);
+ })
+ .join()
+ .ok()
+ .expect("thread panicked");
+}
+
+#[test]
+fn self_referential() {
+ struct S1;
+ thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
+
+ impl Drop for S1 {
+ fn drop(&mut self) {
+ assert!(K1.try_with(|_| ()).is_err());
+ }
+ }
+
+ thread::spawn(move || unsafe {
+ K1.with(|s| *s.get() = Some(S1));
+ })
+ .join()
+ .ok()
+ .expect("thread panicked");
+}
+
+// Note that this test will deadlock if TLS destructors aren't run (this
+// requires the destructor to be run to pass the test).
+#[test]
+fn dtors_in_dtors_in_dtors() {
+ struct S1(Sender<()>);
+ thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
+ thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell::new(None));
+
+ impl Drop for S1 {
+ fn drop(&mut self) {
+ let S1(ref tx) = *self;
+ unsafe {
+ let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone())));
+ }
+ }
+ }
+
+ let (tx, rx) = channel();
+ let _t = thread::spawn(move || unsafe {
+ let mut tx = Some(tx);
+ K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
+ });
+ rx.recv().unwrap();
+}
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 0b98495..087175b 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -144,6 +144,10 @@
//! [`with`]: LocalKey::with
#![stable(feature = "rust1", since = "1.0.0")]
+#![deny(unsafe_op_in_unsafe_fn)]
+
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
use crate::any::Any;
use crate::cell::UnsafeCell;
@@ -155,13 +159,12 @@
use crate::panic;
use crate::panicking;
use crate::str;
-use crate::sync::atomic::AtomicUsize;
-use crate::sync::atomic::Ordering::SeqCst;
-use crate::sync::{Arc, Condvar, Mutex};
+use crate::sync::Arc;
use crate::sys::thread as imp;
use crate::sys_common::mutex;
use crate::sys_common::thread;
use crate::sys_common::thread_info;
+use crate::sys_common::thread_parker::Parker;
use crate::sys_common::{AsInner, IntoInner};
use crate::time::Duration;
@@ -453,14 +456,23 @@
imp::Thread::set_name(name);
}
- thread_info::set(imp::guard::current(), their_thread);
+ // SAFETY: the stack guard passed is the one for the current thread.
+ // This means the current thread's stack and the new thread's stack
+ // are properly set and protected from each other.
+ thread_info::set(unsafe { imp::guard::current() }, their_thread);
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
crate::sys_common::backtrace::__rust_begin_short_backtrace(f)
}));
- *their_packet.get() = Some(try_result);
+ // SAFETY: `their_packet` as been built just above and moved by the
+ // closure (it is an Arc<...>) and `my_packet` will be stored in the
+ // same `JoinInner` as this closure meaning the mutation will be
+ // safe (not modify it and affect a value far away).
+ unsafe { *their_packet.get() = Some(try_result) };
};
Ok(JoinHandle(JoinInner {
+ // SAFETY:
+ //
// `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed
// through FFI or otherwise used with low-level threading primitives that have no
// notion of or way to enforce lifetimes.
@@ -472,12 +484,14 @@
// Similarly, the `sys` implementation must guarantee that no references to the closure
// exist after the thread has terminated, which is signaled by `Thread::join`
// returning.
- native: Some(imp::Thread::new(
- stack_size,
- mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(Box::new(
- main,
- )),
- )?),
+ native: unsafe {
+ Some(imp::Thread::new(
+ stack_size,
+ mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(
+ Box::new(main),
+ ),
+ )?)
+ },
thread: my_thread,
packet: Packet(my_packet),
}))
@@ -652,6 +666,8 @@
///
/// [`channel`]: crate::sync::mpsc
/// [`join`]: JoinHandle::join
+/// [`Condvar`]: crate::sync::Condvar
+/// [`Mutex`]: crate::sync::Mutex
#[stable(feature = "rust1", since = "1.0.0")]
pub fn yield_now() {
imp::Thread::yield_now()
@@ -697,6 +713,8 @@
/// panic!()
/// }
/// ```
+///
+/// [Mutex]: crate::sync::Mutex
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn panicking() -> bool {
@@ -764,11 +782,6 @@
imp::Thread::sleep(dur)
}
-// constants for park/unpark
-const EMPTY: usize = 0;
-const PARKED: usize = 1;
-const NOTIFIED: usize = 2;
-
/// Blocks unless or until the current thread's token is made available.
///
/// A call to `park` does not guarantee that the thread will remain parked
@@ -855,45 +868,11 @@
///
/// [`unpark`]: Thread::unpark
/// [`thread::park_timeout`]: park_timeout
-//
-// The implementation currently uses the trivial strategy of a Mutex+Condvar
-// with wakeup flag, which does not actually allow spurious wakeups. In the
-// future, this will be implemented in a more efficient way, perhaps along the lines of
-// http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp
-// or futuxes, and in either case may allow spurious wakeups.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn park() {
- let thread = current();
-
- // If we were previously notified then we consume this notification and
- // return quickly.
- if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
- return;
- }
-
- // Otherwise we need to coordinate going to sleep
- let mut m = thread.inner.lock.lock().unwrap();
- match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
- Ok(_) => {}
- Err(NOTIFIED) => {
- // We must read here, even though we know it will be `NOTIFIED`.
- // This is because `unpark` may have been called again since we read
- // `NOTIFIED` in the `compare_exchange` above. We must perform an
- // acquire operation that synchronizes with that `unpark` to observe
- // any writes it made before the call to unpark. To do that we must
- // read from the write it made to `state`.
- let old = thread.inner.state.swap(EMPTY, SeqCst);
- assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
- return;
- } // should consume this notification, so prohibit spurious wakeups in next park.
- Err(_) => panic!("inconsistent park state"),
- }
- loop {
- m = thread.inner.cvar.wait(m).unwrap();
- match thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
- Ok(_) => return, // got a notification
- Err(_) => {} // spurious wakeup, go back to sleep
- }
+ // SAFETY: park_timeout is called on the parker owned by this thread.
+ unsafe {
+ current().inner.parker.park();
}
}
@@ -955,35 +934,9 @@
/// ```
#[stable(feature = "park_timeout", since = "1.4.0")]
pub fn park_timeout(dur: Duration) {
- let thread = current();
-
- // Like `park` above we have a fast path for an already-notified thread, and
- // afterwards we start coordinating for a sleep.
- // return quickly.
- if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
- return;
- }
- let m = thread.inner.lock.lock().unwrap();
- match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
- Ok(_) => {}
- Err(NOTIFIED) => {
- // We must read again here, see `park`.
- let old = thread.inner.state.swap(EMPTY, SeqCst);
- assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
- return;
- } // should consume this notification, so prohibit spurious wakeups in next park.
- Err(_) => panic!("inconsistent park_timeout state"),
- }
-
- // Wait with a timeout, and if we spuriously wake up or otherwise wake up
- // from a notification we just want to unconditionally set the state back to
- // empty, either consuming a notification or un-flagging ourselves as
- // parked.
- let (_m, _result) = thread.inner.cvar.wait_timeout(m, dur).unwrap();
- match thread.inner.state.swap(EMPTY, SeqCst) {
- NOTIFIED => {} // got a notification, hurray!
- PARKED => {} // no notification, alas
- n => panic!("inconsistent park_timeout state: {}", n),
+ // SAFETY: park_timeout is called on the parker owned by this thread.
+ unsafe {
+ current().inner.parker.park_timeout(dur);
}
}
@@ -1019,9 +972,8 @@
impl ThreadId {
// Generate a new unique thread ID.
fn new() -> ThreadId {
- // We never call `GUARD.init()`, so it is UB to attempt to
- // acquire this mutex reentrantly!
- static GUARD: mutex::Mutex = mutex::Mutex::new();
+ // It is UB to attempt to acquire this mutex reentrantly!
+ static GUARD: mutex::StaticMutex = mutex::StaticMutex::new();
static mut COUNTER: u64 = 1;
unsafe {
@@ -1062,11 +1014,7 @@
struct Inner {
name: Option<CString>, // Guaranteed to be UTF-8
id: ThreadId,
-
- // state for thread park/unpark
- state: AtomicUsize,
- lock: Mutex<()>,
- cvar: Condvar,
+ parker: Parker,
}
#[derive(Clone)]
@@ -1100,13 +1048,7 @@
let cname =
name.map(|n| CString::new(n).expect("thread name may not contain interior null bytes"));
Thread {
- inner: Arc::new(Inner {
- name: cname,
- id: ThreadId::new(),
- state: AtomicUsize::new(EMPTY),
- lock: Mutex::new(()),
- cvar: Condvar::new(),
- }),
+ inner: Arc::new(Inner { name: cname, id: ThreadId::new(), parker: Parker::new() }),
}
}
@@ -1141,33 +1083,9 @@
/// parked_thread.join().unwrap();
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
+ #[inline]
pub fn unpark(&self) {
- // To ensure the unparked thread will observe any writes we made
- // before this call, we must perform a release operation that `park`
- // can synchronize with. To do that we must write `NOTIFIED` even if
- // `state` is already `NOTIFIED`. That is why this must be a swap
- // rather than a compare-and-swap that returns if it reads `NOTIFIED`
- // on failure.
- match self.inner.state.swap(NOTIFIED, SeqCst) {
- EMPTY => return, // no one was waiting
- NOTIFIED => return, // already unparked
- PARKED => {} // gotta go wake someone up
- _ => panic!("inconsistent state in unpark"),
- }
-
- // There is a period between when the parked thread sets `state` to
- // `PARKED` (or last checked `state` in the case of a spurious wake
- // up) and when it actually waits on `cvar`. If we were to notify
- // during this period it would be ignored and then when the parked
- // thread went to sleep it would never wake up. Fortunately, it has
- // `lock` locked at this stage so we can acquire `lock` to wait until
- // it is ready to receive the notification.
- //
- // Releasing `lock` before the call to `notify_one` means that when the
- // parked thread wakes it doesn't get woken only to have to wait for us
- // to release `lock`.
- drop(self.inner.lock.lock().unwrap());
- self.inner.cvar.notify_one()
+ self.inner.parker.unpark();
}
/// Gets the thread's unique identifier.
@@ -1470,273 +1388,3 @@
_assert_both::<JoinHandle<()>>();
_assert_both::<Thread>();
}
-
-////////////////////////////////////////////////////////////////////////////////
-// Tests
-////////////////////////////////////////////////////////////////////////////////
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
- use super::Builder;
- use crate::any::Any;
- use crate::mem;
- use crate::result;
- use crate::sync::mpsc::{channel, Sender};
- use crate::thread::{self, ThreadId};
- use crate::time::Duration;
-
- // !!! These tests are dangerous. If something is buggy, they will hang, !!!
- // !!! instead of exiting cleanly. This might wedge the buildbots. !!!
-
- #[test]
- fn test_unnamed_thread() {
- thread::spawn(move || {
- assert!(thread::current().name().is_none());
- })
- .join()
- .ok()
- .expect("thread panicked");
- }
-
- #[test]
- fn test_named_thread() {
- Builder::new()
- .name("ada lovelace".to_string())
- .spawn(move || {
- assert!(thread::current().name().unwrap() == "ada lovelace".to_string());
- })
- .unwrap()
- .join()
- .unwrap();
- }
-
- #[test]
- #[should_panic]
- fn test_invalid_named_thread() {
- let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {});
- }
-
- #[test]
- fn test_run_basic() {
- let (tx, rx) = channel();
- thread::spawn(move || {
- tx.send(()).unwrap();
- });
- rx.recv().unwrap();
- }
-
- #[test]
- fn test_join_panic() {
- match thread::spawn(move || panic!()).join() {
- result::Result::Err(_) => (),
- result::Result::Ok(()) => panic!(),
- }
- }
-
- #[test]
- fn test_spawn_sched() {
- let (tx, rx) = channel();
-
- fn f(i: i32, tx: Sender<()>) {
- let tx = tx.clone();
- thread::spawn(move || {
- if i == 0 {
- tx.send(()).unwrap();
- } else {
- f(i - 1, tx);
- }
- });
- }
- f(10, tx);
- rx.recv().unwrap();
- }
-
- #[test]
- fn test_spawn_sched_childs_on_default_sched() {
- let (tx, rx) = channel();
-
- thread::spawn(move || {
- thread::spawn(move || {
- tx.send(()).unwrap();
- });
- });
-
- rx.recv().unwrap();
- }
-
- fn avoid_copying_the_body<F>(spawnfn: F)
- where
- F: FnOnce(Box<dyn Fn() + Send>),
- {
- let (tx, rx) = channel();
-
- let x: Box<_> = box 1;
- let x_in_parent = (&*x) as *const i32 as usize;
-
- spawnfn(Box::new(move || {
- let x_in_child = (&*x) as *const i32 as usize;
- tx.send(x_in_child).unwrap();
- }));
-
- let x_in_child = rx.recv().unwrap();
- assert_eq!(x_in_parent, x_in_child);
- }
-
- #[test]
- fn test_avoid_copying_the_body_spawn() {
- avoid_copying_the_body(|v| {
- thread::spawn(move || v());
- });
- }
-
- #[test]
- fn test_avoid_copying_the_body_thread_spawn() {
- avoid_copying_the_body(|f| {
- thread::spawn(move || {
- f();
- });
- })
- }
-
- #[test]
- fn test_avoid_copying_the_body_join() {
- avoid_copying_the_body(|f| {
- let _ = thread::spawn(move || f()).join();
- })
- }
-
- #[test]
- fn test_child_doesnt_ref_parent() {
- // If the child refcounts the parent thread, this will stack overflow when
- // climbing the thread tree to dereference each ancestor. (See #1789)
- // (well, it would if the constant were 8000+ - I lowered it to be more
- // valgrind-friendly. try this at home, instead..!)
- const GENERATIONS: u32 = 16;
- fn child_no(x: u32) -> Box<dyn Fn() + Send> {
- return Box::new(move || {
- if x < GENERATIONS {
- thread::spawn(move || child_no(x + 1)());
- }
- });
- }
- thread::spawn(|| child_no(0)());
- }
-
- #[test]
- fn test_simple_newsched_spawn() {
- thread::spawn(move || {});
- }
-
- #[test]
- fn test_try_panic_message_static_str() {
- match thread::spawn(move || {
- panic!("static string");
- })
- .join()
- {
- Err(e) => {
- type T = &'static str;
- assert!(e.is::<T>());
- assert_eq!(*e.downcast::<T>().unwrap(), "static string");
- }
- Ok(()) => panic!(),
- }
- }
-
- #[test]
- fn test_try_panic_message_owned_str() {
- match thread::spawn(move || {
- panic!("owned string".to_string());
- })
- .join()
- {
- Err(e) => {
- type T = String;
- assert!(e.is::<T>());
- assert_eq!(*e.downcast::<T>().unwrap(), "owned string".to_string());
- }
- Ok(()) => panic!(),
- }
- }
-
- #[test]
- fn test_try_panic_message_any() {
- match thread::spawn(move || {
- panic!(box 413u16 as Box<dyn Any + Send>);
- })
- .join()
- {
- Err(e) => {
- type T = Box<dyn Any + Send>;
- assert!(e.is::<T>());
- let any = e.downcast::<T>().unwrap();
- assert!(any.is::<u16>());
- assert_eq!(*any.downcast::<u16>().unwrap(), 413);
- }
- Ok(()) => panic!(),
- }
- }
-
- #[test]
- fn test_try_panic_message_unit_struct() {
- struct Juju;
-
- match thread::spawn(move || panic!(Juju)).join() {
- Err(ref e) if e.is::<Juju>() => {}
- Err(_) | Ok(()) => panic!(),
- }
- }
-
- #[test]
- fn test_park_timeout_unpark_before() {
- for _ in 0..10 {
- thread::current().unpark();
- thread::park_timeout(Duration::from_millis(u32::MAX as u64));
- }
- }
-
- #[test]
- fn test_park_timeout_unpark_not_called() {
- for _ in 0..10 {
- thread::park_timeout(Duration::from_millis(10));
- }
- }
-
- #[test]
- fn test_park_timeout_unpark_called_other_thread() {
- for _ in 0..10 {
- let th = thread::current();
-
- let _guard = thread::spawn(move || {
- super::sleep(Duration::from_millis(50));
- th.unpark();
- });
-
- thread::park_timeout(Duration::from_millis(u32::MAX as u64));
- }
- }
-
- #[test]
- 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());
- }
-
- #[test]
- fn test_thread_id_not_equal() {
- let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap();
- assert!(thread::current().id() != spawned_id);
- }
-
- // NOTE: the corresponding test for stderr is in ui/thread-stderr, due
- // to the test harness apparently interfering with stderr configuration.
-}
diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs
new file mode 100644
index 0000000..16ad366
--- /dev/null
+++ b/library/std/src/thread/tests.rs
@@ -0,0 +1,262 @@
+use super::Builder;
+use crate::any::Any;
+use crate::mem;
+use crate::result;
+use crate::sync::mpsc::{channel, Sender};
+use crate::thread::{self, ThreadId};
+use crate::time::Duration;
+
+// !!! These tests are dangerous. If something is buggy, they will hang, !!!
+// !!! instead of exiting cleanly. This might wedge the buildbots. !!!
+
+#[test]
+fn test_unnamed_thread() {
+ thread::spawn(move || {
+ assert!(thread::current().name().is_none());
+ })
+ .join()
+ .ok()
+ .expect("thread panicked");
+}
+
+#[test]
+fn test_named_thread() {
+ Builder::new()
+ .name("ada lovelace".to_string())
+ .spawn(move || {
+ assert!(thread::current().name().unwrap() == "ada lovelace".to_string());
+ })
+ .unwrap()
+ .join()
+ .unwrap();
+}
+
+#[test]
+#[should_panic]
+fn test_invalid_named_thread() {
+ let _ = Builder::new().name("ada l\0velace".to_string()).spawn(|| {});
+}
+
+#[test]
+fn test_run_basic() {
+ let (tx, rx) = channel();
+ thread::spawn(move || {
+ tx.send(()).unwrap();
+ });
+ rx.recv().unwrap();
+}
+
+#[test]
+fn test_join_panic() {
+ match thread::spawn(move || panic!()).join() {
+ result::Result::Err(_) => (),
+ result::Result::Ok(()) => panic!(),
+ }
+}
+
+#[test]
+fn test_spawn_sched() {
+ let (tx, rx) = channel();
+
+ fn f(i: i32, tx: Sender<()>) {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ if i == 0 {
+ tx.send(()).unwrap();
+ } else {
+ f(i - 1, tx);
+ }
+ });
+ }
+ f(10, tx);
+ rx.recv().unwrap();
+}
+
+#[test]
+fn test_spawn_sched_childs_on_default_sched() {
+ let (tx, rx) = channel();
+
+ thread::spawn(move || {
+ thread::spawn(move || {
+ tx.send(()).unwrap();
+ });
+ });
+
+ rx.recv().unwrap();
+}
+
+fn avoid_copying_the_body<F>(spawnfn: F)
+where
+ F: FnOnce(Box<dyn Fn() + Send>),
+{
+ let (tx, rx) = channel();
+
+ let x: Box<_> = box 1;
+ let x_in_parent = (&*x) as *const i32 as usize;
+
+ spawnfn(Box::new(move || {
+ let x_in_child = (&*x) as *const i32 as usize;
+ tx.send(x_in_child).unwrap();
+ }));
+
+ let x_in_child = rx.recv().unwrap();
+ assert_eq!(x_in_parent, x_in_child);
+}
+
+#[test]
+fn test_avoid_copying_the_body_spawn() {
+ avoid_copying_the_body(|v| {
+ thread::spawn(move || v());
+ });
+}
+
+#[test]
+fn test_avoid_copying_the_body_thread_spawn() {
+ avoid_copying_the_body(|f| {
+ thread::spawn(move || {
+ f();
+ });
+ })
+}
+
+#[test]
+fn test_avoid_copying_the_body_join() {
+ avoid_copying_the_body(|f| {
+ let _ = thread::spawn(move || f()).join();
+ })
+}
+
+#[test]
+fn test_child_doesnt_ref_parent() {
+ // If the child refcounts the parent thread, this will stack overflow when
+ // climbing the thread tree to dereference each ancestor. (See #1789)
+ // (well, it would if the constant were 8000+ - I lowered it to be more
+ // valgrind-friendly. try this at home, instead..!)
+ const GENERATIONS: u32 = 16;
+ fn child_no(x: u32) -> Box<dyn Fn() + Send> {
+ return Box::new(move || {
+ if x < GENERATIONS {
+ thread::spawn(move || child_no(x + 1)());
+ }
+ });
+ }
+ thread::spawn(|| child_no(0)());
+}
+
+#[test]
+fn test_simple_newsched_spawn() {
+ thread::spawn(move || {});
+}
+
+#[test]
+fn test_try_panic_message_static_str() {
+ match thread::spawn(move || {
+ panic!("static string");
+ })
+ .join()
+ {
+ Err(e) => {
+ type T = &'static str;
+ assert!(e.is::<T>());
+ assert_eq!(*e.downcast::<T>().unwrap(), "static string");
+ }
+ Ok(()) => panic!(),
+ }
+}
+
+#[test]
+fn test_try_panic_message_owned_str() {
+ match thread::spawn(move || {
+ panic!("owned string".to_string());
+ })
+ .join()
+ {
+ Err(e) => {
+ type T = String;
+ assert!(e.is::<T>());
+ assert_eq!(*e.downcast::<T>().unwrap(), "owned string".to_string());
+ }
+ Ok(()) => panic!(),
+ }
+}
+
+#[test]
+fn test_try_panic_message_any() {
+ match thread::spawn(move || {
+ panic!(box 413u16 as Box<dyn Any + Send>);
+ })
+ .join()
+ {
+ Err(e) => {
+ type T = Box<dyn Any + Send>;
+ assert!(e.is::<T>());
+ let any = e.downcast::<T>().unwrap();
+ assert!(any.is::<u16>());
+ assert_eq!(*any.downcast::<u16>().unwrap(), 413);
+ }
+ Ok(()) => panic!(),
+ }
+}
+
+#[test]
+fn test_try_panic_message_unit_struct() {
+ struct Juju;
+
+ match thread::spawn(move || panic!(Juju)).join() {
+ Err(ref e) if e.is::<Juju>() => {}
+ Err(_) | Ok(()) => panic!(),
+ }
+}
+
+#[test]
+fn test_park_timeout_unpark_before() {
+ for _ in 0..10 {
+ thread::current().unpark();
+ thread::park_timeout(Duration::from_millis(u32::MAX as u64));
+ }
+}
+
+#[test]
+fn test_park_timeout_unpark_not_called() {
+ for _ in 0..10 {
+ thread::park_timeout(Duration::from_millis(10));
+ }
+}
+
+#[test]
+fn test_park_timeout_unpark_called_other_thread() {
+ for _ in 0..10 {
+ let th = thread::current();
+
+ let _guard = thread::spawn(move || {
+ super::sleep(Duration::from_millis(50));
+ th.unpark();
+ });
+
+ thread::park_timeout(Duration::from_millis(u32::MAX as u64));
+ }
+}
+
+#[test]
+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());
+}
+
+#[test]
+fn test_thread_id_not_equal() {
+ let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap();
+ assert!(thread::current().id() != spawned_id);
+}
+
+// NOTE: the corresponding test for stderr is in ui/thread-stderr, due
+// to the test harness apparently interfering with stderr configuration.
diff --git a/library/std/src/time.rs b/library/std/src/time.rs
index 02161ec..e7df384 100644
--- a/library/std/src/time.rs
+++ b/library/std/src/time.rs
@@ -12,12 +12,15 @@
#![stable(feature = "time", since = "1.3.0")]
+#[cfg(test)]
+mod tests;
+
use crate::cmp;
use crate::error::Error;
use crate::fmt;
use crate::ops::{Add, AddAssign, Sub, SubAssign};
use crate::sys::time;
-use crate::sys_common::mutex::Mutex;
+use crate::sys_common::mutex::StaticMutex;
use crate::sys_common::FromInner;
#[stable(feature = "time", since = "1.3.0")]
@@ -156,10 +159,10 @@
/// | CloudABI | [clock_time_get (Realtime Clock)] |
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
/// | UNIX | [clock_gettime (Realtime Clock)] |
-/// | DARWIN | [gettimeofday] |
+/// | Darwin | [gettimeofday] |
/// | VXWorks | [clock_gettime (Realtime Clock)] |
/// | WASI | [__wasi_clock_time_get (Realtime Clock)] |
-/// | Windows | [GetSystemTimeAsFileTime] |
+/// | Windows | [GetSystemTimePreciseAsFileTime] / [GetSystemTimeAsFileTime] |
///
/// [clock_time_get (Realtime Clock)]: https://nuxi.nl/cloudabi/#clock_time_get
/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
@@ -167,6 +170,7 @@
/// [gettimeofday]: http://man7.org/linux/man-pages/man2/gettimeofday.2.html
/// [clock_gettime (Realtime Clock)]: https://linux.die.net/man/3/clock_gettime
/// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#clock_time_get
+/// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
/// [GetSystemTimeAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime
///
/// **Disclaimer:** These system calls might change over time.
@@ -239,7 +243,7 @@
return Instant(os_now);
}
- static LOCK: Mutex = Mutex::new();
+ static LOCK: StaticMutex = StaticMutex::new();
static mut LAST_NOW: time::Instant = time::Instant::zero();
unsafe {
let _lock = LOCK.lock();
@@ -456,12 +460,13 @@
///
/// # Examples
///
- /// ```
+ /// ```no_run
/// use std::time::SystemTime;
///
/// let sys_time = SystemTime::now();
- /// let difference = sys_time.duration_since(sys_time)
- /// .expect("Clock may have gone backwards");
+ /// let new_sys_time = SystemTime::now();
+ /// let difference = new_sys_time.duration_since(sys_time)
+ /// .expect("Clock may have gone backwards");
/// println!("{:?}", difference);
/// ```
#[stable(feature = "time2", since = "1.8.0")]
@@ -629,172 +634,3 @@
SystemTime(time)
}
}
-
-#[cfg(test)]
-mod tests {
- use super::{Duration, Instant, SystemTime, UNIX_EPOCH};
-
- macro_rules! assert_almost_eq {
- ($a:expr, $b:expr) => {{
- let (a, b) = ($a, $b);
- if a != b {
- let (a, b) = if a > b { (a, b) } else { (b, a) };
- assert!(a - Duration::new(0, 1000) <= b, "{:?} is not almost equal to {:?}", a, b);
- }
- }};
- }
-
- #[test]
- fn instant_monotonic() {
- let a = Instant::now();
- let b = Instant::now();
- assert!(b >= a);
- }
-
- #[test]
- fn instant_elapsed() {
- let a = Instant::now();
- a.elapsed();
- }
-
- #[test]
- fn instant_math() {
- let a = Instant::now();
- let b = Instant::now();
- println!("a: {:?}", a);
- println!("b: {:?}", b);
- let dur = b.duration_since(a);
- println!("dur: {:?}", dur);
- assert_almost_eq!(b - dur, a);
- assert_almost_eq!(a + dur, b);
-
- let second = Duration::new(1, 0);
- assert_almost_eq!(a - second + second, a);
- assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a);
-
- // checked_add_duration will not panic on overflow
- let mut maybe_t = Some(Instant::now());
- let max_duration = Duration::from_secs(u64::MAX);
- // in case `Instant` can store `>= now + max_duration`.
- for _ in 0..2 {
- maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration));
- }
- assert_eq!(maybe_t, None);
-
- // checked_add_duration calculates the right time and will work for another year
- let year = Duration::from_secs(60 * 60 * 24 * 365);
- assert_eq!(a + year, a.checked_add(year).unwrap());
- }
-
- #[test]
- fn instant_math_is_associative() {
- let now = Instant::now();
- let offset = Duration::from_millis(5);
- // Changing the order of instant math shouldn't change the results,
- // especially when the expression reduces to X + identity.
- assert_eq!((now + offset) - now, (now - now) + offset);
- }
-
- #[test]
- #[should_panic]
- fn instant_duration_since_panic() {
- let a = Instant::now();
- (a - Duration::new(1, 0)).duration_since(a);
- }
-
- #[test]
- 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 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));
- }
-
- #[test]
- fn system_time_math() {
- let a = SystemTime::now();
- let b = SystemTime::now();
- match b.duration_since(a) {
- Ok(dur) if dur == Duration::new(0, 0) => {
- assert_almost_eq!(a, b);
- }
- Ok(dur) => {
- assert!(b > a);
- assert_almost_eq!(b - dur, a);
- assert_almost_eq!(a + dur, b);
- }
- Err(dur) => {
- let dur = dur.duration();
- assert!(a > b);
- assert_almost_eq!(b + dur, a);
- assert_almost_eq!(a - dur, b);
- }
- }
-
- let second = Duration::new(1, 0);
- assert_almost_eq!(a.duration_since(a - second).unwrap(), second);
- assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second);
-
- assert_almost_eq!(a - second + second, a);
- assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), 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);
- assert_eq!(one_second_from_epoch, one_second_from_epoch2);
-
- // checked_add_duration will not panic on overflow
- let mut maybe_t = Some(SystemTime::UNIX_EPOCH);
- let max_duration = Duration::from_secs(u64::MAX);
- // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`.
- for _ in 0..2 {
- maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration));
- }
- assert_eq!(maybe_t, None);
-
- // checked_add_duration calculates the right time and will work for another year
- let year = Duration::from_secs(60 * 60 * 24 * 365);
- assert_eq!(a + year, a.checked_add(year).unwrap());
- }
-
- #[test]
- fn system_time_elapsed() {
- let a = SystemTime::now();
- drop(a.elapsed());
- }
-
- #[test]
- fn since_epoch() {
- let ts = SystemTime::now();
- 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));
-
- let thirty_years = Duration::new(1, 0) * 60 * 60 * 24 * 365 * 30;
-
- // Right now for CI this test is run in an emulator, and apparently the
- // aarch64 emulator's sense of time is that we're still living in the
- // 70s. This is also true for riscv (also qemu)
- //
- // Otherwise let's assume that we're all running computers later than
- // 2000.
- if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "riscv64") {
- assert!(a > thirty_years);
- }
-
- // let's assume that we're all running computers earlier than 2090.
- // Should give us ~70 years to fix this!
- let hundred_twenty_years = thirty_years * 4;
- assert!(a < hundred_twenty_years);
- }
-}
diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs
new file mode 100644
index 0000000..783bf49
--- /dev/null
+++ b/library/std/src/time/tests.rs
@@ -0,0 +1,165 @@
+use super::{Duration, Instant, SystemTime, UNIX_EPOCH};
+
+macro_rules! assert_almost_eq {
+ ($a:expr, $b:expr) => {{
+ let (a, b) = ($a, $b);
+ if a != b {
+ let (a, b) = if a > b { (a, b) } else { (b, a) };
+ assert!(a - Duration::new(0, 1000) <= b, "{:?} is not almost equal to {:?}", a, b);
+ }
+ }};
+}
+
+#[test]
+fn instant_monotonic() {
+ let a = Instant::now();
+ let b = Instant::now();
+ assert!(b >= a);
+}
+
+#[test]
+fn instant_elapsed() {
+ let a = Instant::now();
+ a.elapsed();
+}
+
+#[test]
+fn instant_math() {
+ let a = Instant::now();
+ let b = Instant::now();
+ println!("a: {:?}", a);
+ println!("b: {:?}", b);
+ let dur = b.duration_since(a);
+ println!("dur: {:?}", dur);
+ assert_almost_eq!(b - dur, a);
+ assert_almost_eq!(a + dur, b);
+
+ let second = Duration::new(1, 0);
+ assert_almost_eq!(a - second + second, a);
+ assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a);
+
+ // checked_add_duration will not panic on overflow
+ let mut maybe_t = Some(Instant::now());
+ let max_duration = Duration::from_secs(u64::MAX);
+ // in case `Instant` can store `>= now + max_duration`.
+ for _ in 0..2 {
+ maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration));
+ }
+ assert_eq!(maybe_t, None);
+
+ // checked_add_duration calculates the right time and will work for another year
+ let year = Duration::from_secs(60 * 60 * 24 * 365);
+ assert_eq!(a + year, a.checked_add(year).unwrap());
+}
+
+#[test]
+fn instant_math_is_associative() {
+ let now = Instant::now();
+ let offset = Duration::from_millis(5);
+ // Changing the order of instant math shouldn't change the results,
+ // especially when the expression reduces to X + identity.
+ assert_eq!((now + offset) - now, (now - now) + offset);
+}
+
+#[test]
+#[should_panic]
+fn instant_duration_since_panic() {
+ let a = Instant::now();
+ (a - Duration::new(1, 0)).duration_since(a);
+}
+
+#[test]
+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 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));
+}
+
+#[test]
+fn system_time_math() {
+ let a = SystemTime::now();
+ let b = SystemTime::now();
+ match b.duration_since(a) {
+ Ok(dur) if dur == Duration::new(0, 0) => {
+ assert_almost_eq!(a, b);
+ }
+ Ok(dur) => {
+ assert!(b > a);
+ assert_almost_eq!(b - dur, a);
+ assert_almost_eq!(a + dur, b);
+ }
+ Err(dur) => {
+ let dur = dur.duration();
+ assert!(a > b);
+ assert_almost_eq!(b + dur, a);
+ assert_almost_eq!(a - dur, b);
+ }
+ }
+
+ let second = Duration::new(1, 0);
+ assert_almost_eq!(a.duration_since(a - second).unwrap(), second);
+ assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second);
+
+ assert_almost_eq!(a - second + second, a);
+ assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), 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);
+ assert_eq!(one_second_from_epoch, one_second_from_epoch2);
+
+ // checked_add_duration will not panic on overflow
+ let mut maybe_t = Some(SystemTime::UNIX_EPOCH);
+ let max_duration = Duration::from_secs(u64::MAX);
+ // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`.
+ for _ in 0..2 {
+ maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration));
+ }
+ assert_eq!(maybe_t, None);
+
+ // checked_add_duration calculates the right time and will work for another year
+ let year = Duration::from_secs(60 * 60 * 24 * 365);
+ assert_eq!(a + year, a.checked_add(year).unwrap());
+}
+
+#[test]
+fn system_time_elapsed() {
+ let a = SystemTime::now();
+ drop(a.elapsed());
+}
+
+#[test]
+fn since_epoch() {
+ let ts = SystemTime::now();
+ 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));
+
+ let thirty_years = Duration::new(1, 0) * 60 * 60 * 24 * 365 * 30;
+
+ // Right now for CI this test is run in an emulator, and apparently the
+ // aarch64 emulator's sense of time is that we're still living in the
+ // 70s. This is also true for riscv (also qemu)
+ //
+ // Otherwise let's assume that we're all running computers later than
+ // 2000.
+ if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "riscv64") {
+ assert!(a > thirty_years);
+ }
+
+ // let's assume that we're all running computers earlier than 2090.
+ // Should give us ~70 years to fix this!
+ let hundred_twenty_years = thirty_years * 4;
+ assert!(a < hundred_twenty_years);
+}