diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:50 +0000 |
commit | 2e00214b3efbdfeefaa0fe9e8b8fd519de7adc35 (patch) | |
tree | d325add32978dbdc1db975a438b3a77d571b1ab8 /library/std | |
parent | Releasing progress-linux version 1.68.2+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-2e00214b3efbdfeefaa0fe9e8b8fd519de7adc35.tar.xz rustc-2e00214b3efbdfeefaa0fe9e8b8fd519de7adc35.zip |
Merging upstream version 1.69.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std')
112 files changed, 1920 insertions, 5436 deletions
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index adf521d9b..598a4bf92 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -15,8 +15,8 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } -libc = { version = "0.2.138", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.85" } +libc = { version = "0.2.139", default-features = false, features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.87" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.12", default-features = false, features = ['rustc-dep-of-std'] } @@ -43,7 +43,7 @@ dlmalloc = { version = "0.2.3", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'] } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.2.6", features = ['rustc-dep-of-std'] } +hermit-abi = { version = "0.3.0", features = ['rustc-dep-of-std'] } [target.wasm32-wasi.dependencies] wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } diff --git a/library/std/build.rs b/library/std/build.rs index 8b1a06ee7..ea8796675 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -31,6 +31,7 @@ fn main() { || target.contains("espidf") || target.contains("solid") || target.contains("nintendo-3ds") + || target.contains("nto") { // These platforms don't have any special requirements. } else { diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index df4903588..742c4cc7c 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -238,7 +238,7 @@ impl<K, V> HashMap<K, V, RandomState> { /// /// The hash map will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is 0, the hash set will not allocate. + /// `capacity`. If `capacity` is 0, the hash map will not allocate. /// /// # Examples /// diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 4e3007624..6b1f0cba8 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -69,8 +69,8 @@ impl f32 { unsafe { intrinsics::ceilf32(self) } } - /// Returns the nearest integer to `self`. Round half-way cases away from - /// `0.0`. + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. /// /// # Examples /// diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index ec67fdad4..16359766b 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -69,8 +69,8 @@ impl f64 { unsafe { intrinsics::ceilf64(self) } } - /// Returns the nearest integer to `self`. Round half-way cases away from - /// `0.0`. + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. /// /// # Examples /// diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 286ad68fd..c550378e7 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -334,6 +334,10 @@ impl File { /// /// See the [`OpenOptions::open`] method for more details. /// + /// If you only need to read the entire file contents, + /// consider [`std::fs::read()`][self::read] or + /// [`std::fs::read_to_string()`][self::read_to_string] instead. + /// /// # Errors /// /// This function will return an error if `path` does not already exist. @@ -343,9 +347,12 @@ impl File { /// /// ```no_run /// use std::fs::File; + /// use std::io::Read; /// /// fn main() -> std::io::Result<()> { /// let mut f = File::open("foo.txt")?; + /// let mut data = vec![]; + /// f.read_to_end(&mut data)?; /// Ok(()) /// } /// ``` @@ -361,16 +368,20 @@ impl File { /// /// Depending on the platform, this function may fail if the /// full directory path does not exist. - /// /// See the [`OpenOptions::open`] function for more details. /// + /// See also [`std::fs::write()`][self::write] for a simple function to + /// create a file with a given data. + /// /// # Examples /// /// ```no_run /// use std::fs::File; + /// use std::io::Write; /// /// fn main() -> std::io::Result<()> { /// let mut f = File::create("foo.txt")?; + /// f.write_all(&1234_u32.to_be_bytes())?; /// Ok(()) /// } /// ``` @@ -397,9 +408,11 @@ impl File { /// #![feature(file_create_new)] /// /// use std::fs::File; + /// use std::io::Write; /// /// fn main() -> std::io::Result<()> { /// let mut f = File::create_new("foo.txt")?; + /// f.write_all("Hello, world!".as_bytes())?; /// Ok(()) /// } /// ``` @@ -426,9 +439,11 @@ impl File { /// /// ```no_run /// use std::fs::File; + /// use std::io::Write; /// /// fn main() -> std::io::Result<()> { /// let mut f = File::options().append(true).open("example.log")?; + /// writeln!(&mut f, "new line")?; /// Ok(()) /// } /// ``` @@ -966,6 +981,9 @@ impl OpenOptions { /// In order for the file to be created, [`OpenOptions::write`] or /// [`OpenOptions::append`] access must be used. /// + /// See also [`std::fs::write()`][self::write] for a simple function to + /// create a file with a given data. + /// /// # Examples /// /// ```no_run diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index eb582be01..909d9bf40 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -12,6 +12,8 @@ use crate::time::{Duration, Instant}; use rand::RngCore; +#[cfg(target_os = "macos")] +use crate::ffi::{c_char, c_int}; #[cfg(unix)] use crate::os::unix::fs::symlink as symlink_dir; #[cfg(unix)] @@ -24,8 +26,6 @@ use crate::os::windows::fs::{symlink_dir, symlink_file}; use crate::sys::fs::symlink_junction; #[cfg(target_os = "macos")] use crate::sys::weak::weak; -#[cfg(target_os = "macos")] -use libc::{c_char, c_int}; macro_rules! check { ($e:expr) => { @@ -1595,3 +1595,19 @@ fn test_read_dir_infinite_loop() { // Check for duplicate errors assert!(dir.filter(|e| e.is_err()).take(2).count() < 2); } + +#[test] +fn rename_directory() { + let tmpdir = tmpdir(); + let old_path = tmpdir.join("foo/bar/baz"); + fs::create_dir_all(&old_path).unwrap(); + let test_file = &old_path.join("temp.txt"); + + File::create(test_file).unwrap(); + + let new_path = tmpdir.join("quux/blat"); + fs::create_dir_all(&new_path).unwrap(); + fs::rename(&old_path, &new_path.join("newdir")).unwrap(); + assert!(new_path.join("newdir").is_dir()); + assert!(new_path.join("newdir/temp.txt").exists()); +} diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 3cabf2449..7f07e4fdd 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -88,12 +88,23 @@ impl From<alloc::ffi::NulError> for Error { // doesn't accidentally get printed. #[cfg_attr(test, derive(Debug))] enum ErrorData<C> { - Os(i32), + Os(RawOsError), Simple(ErrorKind), SimpleMessage(&'static SimpleMessage), Custom(C), } +/// The type of raw OS error codes returned by [`Error::raw_os_error`]. +/// +/// This is an [`i32`] on all currently supported platforms, but platforms +/// added in the future (such as UEFI) may use a different primitive type like +/// [`usize`]. Use `as`or [`into`] conversions where applicable to ensure maximum +/// portability. +/// +/// [`into`]: Into::into +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub type RawOsError = i32; + // `#[repr(align(4))]` is probably redundant, it should have that value or // higher already. We include it just because repr_bitpacked.rs's encoding // requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the @@ -579,7 +590,7 @@ impl Error { #[must_use] #[inline] pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::os::errno() as i32) + Error::from_raw_os_error(sys::os::errno()) } /// Creates a new instance of an [`Error`] from a particular OS error code. @@ -610,7 +621,7 @@ impl Error { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] - pub fn from_raw_os_error(code: i32) -> Error { + pub fn from_raw_os_error(code: RawOsError) -> Error { Error { repr: Repr::new_os(code) } } @@ -646,7 +657,7 @@ impl Error { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] - pub fn raw_os_error(&self) -> Option<i32> { + pub fn raw_os_error(&self) -> Option<RawOsError> { match self.repr.data() { ErrorData::Os(i) => Some(i), ErrorData::Custom(..) => None, diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 358148405..f94f88bac 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -102,7 +102,7 @@ //! to use a pointer type to store something that may hold an integer, some of //! the time. -use super::{Custom, ErrorData, ErrorKind, SimpleMessage}; +use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; use alloc::boxed::Box; use core::marker::PhantomData; use core::mem::{align_of, size_of}; @@ -172,7 +172,7 @@ impl Repr { } #[inline] - pub(super) fn new_os(code: i32) -> Self { + pub(super) fn new_os(code: RawOsError) -> Self { let utagged = ((code as usize) << 32) | TAG_OS; // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. let res = Self(unsafe { NonNull::new_unchecked(ptr::invalid_mut(utagged)) }, PhantomData); @@ -250,7 +250,7 @@ where let bits = ptr.as_ptr().addr(); match bits & TAG_MASK { TAG_OS => { - let code = ((bits as i64) >> 32) as i32; + let code = ((bits as i64) >> 32) as RawOsError; ErrorData::Os(code) } TAG_SIMPLE => { @@ -374,6 +374,9 @@ static_assert!((TAG_MASK + 1).is_power_of_two()); static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1); static_assert!(align_of::<Custom>() >= TAG_MASK + 1); +// `RawOsError` must be an alias for `i32`. +const _: fn(RawOsError) -> i32 = |os| os; + static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE); static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM); static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS); diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/std/src/io/error/repr_unpacked.rs index d6ad55b99..093fde337 100644 --- a/library/std/src/io/error/repr_unpacked.rs +++ b/library/std/src/io/error/repr_unpacked.rs @@ -2,7 +2,7 @@ //! non-64bit targets, where the packed 64 bit representation wouldn't work, and //! would have no benefit. -use super::{Custom, ErrorData, ErrorKind, SimpleMessage}; +use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; use alloc::boxed::Box; type Inner = ErrorData<Box<Custom>>; @@ -18,7 +18,7 @@ impl Repr { Self(Inner::Custom(b)) } #[inline] - pub(super) fn new_os(code: i32) -> Self { + pub(super) fn new_os(code: RawOsError) -> Self { Self(Inner::Os(code)) } #[inline] diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index 16c634e9a..36d52aef0 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -71,7 +71,7 @@ fn test_const() { #[test] fn test_os_packing() { - for code in -20i32..20i32 { + for code in -20..20 { let e = Error::from_raw_os_error(code); assert_eq!(e.raw_os_error(), Some(code)); assert_matches!( @@ -190,5 +190,5 @@ fn test_std_io_error_downcast() { let io_error = io_error.downcast::<E>().unwrap_err(); assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); - assert_eq!(SIMPLE_MESSAGE.message, &*format!("{io_error}")); + assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}")); } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index de528e853..b2b6d8613 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -262,6 +262,8 @@ use crate::sys_common::memchr; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use self::error::RawOsError; pub(crate) use self::stdio::attempt_print_to_stderr; #[unstable(feature = "internal_output_capture", issue = "none")] #[doc(no_inline, hidden)] diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index e35145c4a..203c490fa 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -1568,7 +1568,7 @@ mod static_keyword {} /// /// # Style conventions /// -/// Structs are always written in CamelCase, with few exceptions. While the trailing comma on a +/// Structs are always written in UpperCamelCase, with few exceptions. While the trailing comma on a /// struct's list of fields can be omitted, it's usually kept for convenience in adding and /// removing fields down the line. /// diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index a7e13f5b8..b62f3ad29 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -232,13 +232,13 @@ all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] +#![cfg_attr(windows, feature(round_char_boundary))] // // Language features: #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] -#![feature(box_syntax)] #![feature(c_unwind)] #![feature(cfg_target_thread_local)] #![feature(concat_idents)] @@ -274,13 +274,9 @@ #![feature(utf8_chunks)] // // Library features (core): -#![feature(array_error_internals)] #![feature(atomic_mut_ptr)] -#![feature(char_error_internals)] #![feature(char_internals)] #![feature(core_intrinsics)] -#![feature(cstr_from_bytes_until_nul)] -#![feature(cstr_internals)] #![feature(duration_constants)] #![feature(error_generic_member_access)] #![feature(error_in_core)] @@ -292,7 +288,8 @@ #![feature(float_next_up_down)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] -#![feature(int_error_internals)] +#![feature(ip)] +#![feature(ip_in_core)] #![feature(is_some_and)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] @@ -359,7 +356,6 @@ #![feature(const_ip)] #![feature(const_ipv4)] #![feature(const_ipv6)] -#![feature(const_socketaddr)] #![feature(thread_local_internals)] // #![default_lib_allocator] diff --git a/library/std/src/net/display_buffer.rs b/library/std/src/net/display_buffer.rs deleted file mode 100644 index 7aadf06e9..000000000 --- a/library/std/src/net/display_buffer.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::fmt; -use crate::mem::MaybeUninit; -use crate::str; - -/// Used for slow path in `Display` implementations when alignment is required. -pub struct DisplayBuffer<const SIZE: usize> { - buf: [MaybeUninit<u8>; SIZE], - len: usize, -} - -impl<const SIZE: usize> DisplayBuffer<SIZE> { - #[inline] - pub const fn new() -> Self { - Self { buf: MaybeUninit::uninit_array(), len: 0 } - } - - #[inline] - pub fn as_str(&self) -> &str { - // SAFETY: `buf` is only written to by the `fmt::Write::write_str` implementation - // which writes a valid UTF-8 string to `buf` and correctly sets `len`. - unsafe { - let s = MaybeUninit::slice_assume_init_ref(&self.buf[..self.len]); - str::from_utf8_unchecked(s) - } - } -} - -impl<const SIZE: usize> fmt::Write for DisplayBuffer<SIZE> { - fn write_str(&mut self, s: &str) -> fmt::Result { - let bytes = s.as_bytes(); - - if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) { - MaybeUninit::write_slice(buf, bytes); - self.len += bytes.len(); - Ok(()) - } else { - Err(fmt::Error) - } - } -} diff --git a/library/std/src/net/ip_addr.rs b/library/std/src/net/ip_addr.rs index 07f08c1b5..e167fbd1b 100644 --- a/library/std/src/net/ip_addr.rs +++ b/library/std/src/net/ip_addr.rs @@ -2,2101 +2,40 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use crate::cmp::Ordering; -use crate::fmt::{self, Write}; -use crate::mem::transmute; use crate::sys::net::netc as c; use crate::sys_common::{FromInner, IntoInner}; -use super::display_buffer::DisplayBuffer; - -/// An IP address, either IPv4 or IPv6. -/// -/// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their -/// respective documentation for more details. -/// -/// # Examples -/// -/// ``` -/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -/// -/// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); -/// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); -/// -/// assert_eq!("127.0.0.1".parse(), Ok(localhost_v4)); -/// assert_eq!("::1".parse(), Ok(localhost_v6)); -/// -/// assert_eq!(localhost_v4.is_ipv6(), false); -/// assert_eq!(localhost_v4.is_ipv4(), true); -/// ``` -#[cfg_attr(not(test), rustc_diagnostic_item = "IpAddr")] #[stable(feature = "ip_addr", since = "1.7.0")] -#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] -pub enum IpAddr { - /// An IPv4 address. - #[stable(feature = "ip_addr", since = "1.7.0")] - V4(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv4Addr), - /// An IPv6 address. - #[stable(feature = "ip_addr", since = "1.7.0")] - V6(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv6Addr), -} +pub use core::net::IpAddr; -/// An IPv4 address. -/// -/// IPv4 addresses are defined as 32-bit integers in [IETF RFC 791]. -/// They are usually represented as four octets. -/// -/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. -/// -/// [IETF RFC 791]: https://tools.ietf.org/html/rfc791 -/// -/// # Textual representation -/// -/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal -/// notation, divided by `.` (this is called "dot-decimal notation"). -/// Notably, octal numbers (which are indicated with a leading `0`) and hexadecimal numbers (which -/// are indicated with a leading `0x`) are not allowed per [IETF RFC 6943]. -/// -/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1 -/// [`FromStr`]: crate::str::FromStr -/// -/// # Examples -/// -/// ``` -/// use std::net::Ipv4Addr; -/// -/// let localhost = Ipv4Addr::new(127, 0, 0, 1); -/// assert_eq!("127.0.0.1".parse(), Ok(localhost)); -/// assert_eq!(localhost.is_loopback(), true); -/// assert!("012.004.002.000".parse::<Ipv4Addr>().is_err()); // all octets are in octal -/// assert!("0000000.0.0.0".parse::<Ipv4Addr>().is_err()); // first octet is a zero in octal -/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Ipv4Addr { - octets: [u8; 4], -} +pub use core::net::{Ipv4Addr, Ipv6Addr}; -/// An IPv6 address. -/// -/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291]. -/// They are usually represented as eight 16-bit segments. -/// -/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 -/// -/// # Embedding IPv4 Addresses -/// -/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. -/// -/// To assist in the transition from IPv4 to IPv6 two types of IPv6 addresses that embed an IPv4 address were defined: -/// IPv4-compatible and IPv4-mapped addresses. Of these IPv4-compatible addresses have been officially deprecated. -/// -/// Both types of addresses are not assigned any special meaning by this implementation, -/// other than what the relevant standards prescribe. This means that an address like `::ffff:127.0.0.1`, -/// while representing an IPv4 loopback address, is not itself an IPv6 loopback address; only `::1` is. -/// To handle these so called "IPv4-in-IPv6" addresses, they have to first be converted to their canonical IPv4 address. -/// -/// ### IPv4-Compatible IPv6 Addresses -/// -/// IPv4-compatible IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.1], and have been officially deprecated. -/// The RFC describes the format of an "IPv4-Compatible IPv6 address" as follows: -/// -/// ```text -/// | 80 bits | 16 | 32 bits | -/// +--------------------------------------+--------------------------+ -/// |0000..............................0000|0000| IPv4 address | -/// +--------------------------------------+----+---------------------+ -/// ``` -/// So `::a.b.c.d` would be an IPv4-compatible IPv6 address representing the IPv4 address `a.b.c.d`. -/// -/// To convert from an IPv4 address to an IPv4-compatible IPv6 address, use [`Ipv4Addr::to_ipv6_compatible`]. -/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-compatible IPv6 address to the canonical IPv4 address. -/// -/// [IETF RFC 4291 Section 2.5.5.1]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1 -/// -/// ### IPv4-Mapped IPv6 Addresses -/// -/// IPv4-mapped IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.2]. -/// The RFC describes the format of an "IPv4-Mapped IPv6 address" as follows: -/// -/// ```text -/// | 80 bits | 16 | 32 bits | -/// +--------------------------------------+--------------------------+ -/// |0000..............................0000|FFFF| IPv4 address | -/// +--------------------------------------+----+---------------------+ -/// ``` -/// So `::ffff:a.b.c.d` would be an IPv4-mapped IPv6 address representing the IPv4 address `a.b.c.d`. -/// -/// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`]. -/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address. -/// Note that this will also convert the IPv6 loopback address `::1` to `0.0.0.1`. Use -/// [`Ipv6Addr::to_ipv4_mapped`] to avoid this. -/// -/// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2 -/// -/// # Textual representation -/// -/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent -/// an IPv6 address in text, but in general, each segments is written in hexadecimal -/// notation, and segments are separated by `:`. For more information, see -/// [IETF RFC 5952]. -/// -/// [`FromStr`]: crate::str::FromStr -/// [IETF RFC 5952]: https://tools.ietf.org/html/rfc5952 -/// -/// # Examples -/// -/// ``` -/// use std::net::Ipv6Addr; -/// -/// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); -/// assert_eq!("::1".parse(), Ok(localhost)); -/// assert_eq!(localhost.is_loopback(), true); -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Ipv6Addr { - octets: [u8; 16], -} - -/// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2]. -/// -/// # Stability Guarantees -/// -/// Not all possible values for a multicast scope have been assigned. -/// Future RFCs may introduce new scopes, which will be added as variants to this enum; -/// because of this the enum is marked as `#[non_exhaustive]`. -/// -/// # Examples -/// ``` -/// #![feature(ip)] -/// -/// use std::net::Ipv6Addr; -/// use std::net::Ipv6MulticastScope::*; -/// -/// // An IPv6 multicast address with global scope (`ff0e::`). -/// let address = Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0); -/// -/// // Will print "Global scope". -/// match address.multicast_scope() { -/// Some(InterfaceLocal) => println!("Interface-Local scope"), -/// Some(LinkLocal) => println!("Link-Local scope"), -/// Some(RealmLocal) => println!("Realm-Local scope"), -/// Some(AdminLocal) => println!("Admin-Local scope"), -/// Some(SiteLocal) => println!("Site-Local scope"), -/// Some(OrganizationLocal) => println!("Organization-Local scope"), -/// Some(Global) => println!("Global scope"), -/// Some(_) => println!("Unknown scope"), -/// None => println!("Not a multicast address!") -/// } -/// -/// ``` -/// -/// [IPv6 multicast address]: Ipv6Addr -/// [IETF RFC 7346 section 2]: https://tools.ietf.org/html/rfc7346#section-2 -#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] #[unstable(feature = "ip", issue = "27709")] -#[non_exhaustive] -pub enum Ipv6MulticastScope { - /// Interface-Local scope. - InterfaceLocal, - /// Link-Local scope. - LinkLocal, - /// Realm-Local scope. - RealmLocal, - /// Admin-Local scope. - AdminLocal, - /// Site-Local scope. - SiteLocal, - /// Organization-Local scope. - OrganizationLocal, - /// Global scope. - Global, -} - -impl IpAddr { - /// Returns [`true`] for the special 'unspecified' address. - /// - /// See the documentation for [`Ipv4Addr::is_unspecified()`] and - /// [`Ipv6Addr::is_unspecified()`] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// 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_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ip_shared", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn is_unspecified(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_unspecified(), - IpAddr::V6(ip) => ip.is_unspecified(), - } - } - - /// Returns [`true`] if this is a loopback address. - /// - /// See the documentation for [`Ipv4Addr::is_loopback()`] and - /// [`Ipv6Addr::is_loopback()`] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// 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_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ip_shared", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn is_loopback(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_loopback(), - IpAddr::V6(ip) => ip.is_loopback(), - } - } - - /// Returns [`true`] if the address appears to be globally routable. - /// - /// See the documentation for [`Ipv4Addr::is_global()`] and - /// [`Ipv6Addr::is_global()`] for more details. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// 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); - /// ``` - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_global(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_global(), - IpAddr::V6(ip) => ip.is_global(), - } - } - - /// Returns [`true`] if this is a multicast address. - /// - /// See the documentation for [`Ipv4Addr::is_multicast()`] and - /// [`Ipv6Addr::is_multicast()`] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// 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_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ip_shared", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn is_multicast(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_multicast(), - IpAddr::V6(ip) => ip.is_multicast(), - } - } - - /// Returns [`true`] if this address is in a range designated for documentation. - /// - /// See the documentation for [`Ipv4Addr::is_documentation()`] and - /// [`Ipv6Addr::is_documentation()`] for more details. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_documentation(), true); - /// assert_eq!( - /// IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_documentation(), - /// true - /// ); - /// ``` - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_documentation(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_documentation(), - IpAddr::V6(ip) => ip.is_documentation(), - } - } - - /// Returns [`true`] if this address is in a range designated for benchmarking. - /// - /// See the documentation for [`Ipv4Addr::is_benchmarking()`] and - /// [`Ipv6Addr::is_benchmarking()`] for more details. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(198, 19, 255, 255)).is_benchmarking(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0)).is_benchmarking(), true); - /// ``` - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_benchmarking(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_benchmarking(), - IpAddr::V6(ip) => ip.is_benchmarking(), - } - } - - /// Returns [`true`] if this address is an [`IPv4` address], and [`false`] - /// otherwise. - /// - /// [`IPv4` address]: IpAddr::V4 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv4(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv4(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ipaddr_checker", since = "1.16.0")] - #[must_use] - #[inline] - pub const fn is_ipv4(&self) -> bool { - matches!(self, IpAddr::V4(_)) - } - - /// Returns [`true`] if this address is an [`IPv6` address], and [`false`] - /// otherwise. - /// - /// [`IPv6` address]: IpAddr::V6 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv6(), false); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv6(), true); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ipaddr_checker", since = "1.16.0")] - #[must_use] - #[inline] - pub const fn is_ipv6(&self) -> bool { - matches!(self, IpAddr::V6(_)) - } - - /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped IPv6 addresses, otherwise it - /// return `self` as-is. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).to_canonical().is_loopback(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).is_loopback(), false); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).to_canonical().is_loopback(), true); - /// ``` - #[inline] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - pub const fn to_canonical(&self) -> IpAddr { - match self { - &v4 @ IpAddr::V4(_) => v4, - IpAddr::V6(v6) => v6.to_canonical(), - } - } -} - -impl Ipv4Addr { - /// Creates a new IPv4 address from four eight-bit octets. - /// - /// The result will represent the IP address `a`.`b`.`c`.`d`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::new(127, 0, 0, 1); - /// ``` - #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { - Ipv4Addr { octets: [a, b, c, d] } - } - - /// An IPv4 address with the address pointing to localhost: `127.0.0.1` - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::LOCALHOST; - /// assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1)); - /// ``` - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); - - /// An IPv4 address representing an unspecified address: `0.0.0.0` - /// - /// This corresponds to the constant `INADDR_ANY` in other languages. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::UNSPECIFIED; - /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0)); - /// ``` - #[doc(alias = "INADDR_ANY")] - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); - - /// An IPv4 address representing the broadcast address: `255.255.255.255` - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::BROADCAST; - /// assert_eq!(addr, Ipv4Addr::new(255, 255, 255, 255)); - /// ``` - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); - - /// Returns the four eight-bit integers that make up this address. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::new(127, 0, 0, 1); - /// assert_eq!(addr.octets(), [127, 0, 0, 1]); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub const fn octets(&self) -> [u8; 4] { - self.octets - } - - /// Returns [`true`] for the special 'unspecified' address (`0.0.0.0`). - /// - /// This property is defined in _UNIX Network Programming, Second Edition_, - /// W. Richard Stevens, p. 891; see also [ip7]. - /// - /// [ip7]: https://man7.org/linux/man-pages/man7/ip.7.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true); - /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] - #[stable(feature = "ip_shared", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn is_unspecified(&self) -> bool { - u32::from_be_bytes(self.octets) == 0 - } - - /// Returns [`true`] if this is a loopback address (`127.0.0.0/8`). - /// - /// This property is defined by [IETF RFC 1122]. - /// - /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true); - /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_loopback(&self) -> bool { - self.octets()[0] == 127 - } - - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true); - /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false); - /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true); - /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_private(&self) -> bool { - match self.octets() { - [10, ..] => true, - [172, b, ..] if b >= 16 && b <= 31 => true, - [192, 168, ..] => true, - _ => false, - } - } - - /// Returns [`true`] if the address is link-local (`169.254.0.0/16`). - /// - /// This property is defined by [IETF RFC 3927]. - /// - /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(169, 254, 0, 0).is_link_local(), true); - /// 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_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_link_local(&self) -> bool { - matches!(self.octets(), [169, 254, ..]) - } - - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv4Addr; - /// - /// // Most IPv4 addresses are globally reachable: - /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true); - /// - /// // However some addresses have been assigned a special meaning - /// // that makes them not globally reachable. Some examples are: - /// - /// // The unspecified address (`0.0.0.0`) - /// assert_eq!(Ipv4Addr::UNSPECIFIED.is_global(), false); - /// - /// // Addresses reserved for private use (`10.0.0.0/8`, `172.16.0.0/12`, 192.168.0.0/16) - /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false); - /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false); - /// - /// // Addresses in the shared address space (`100.64.0.0/10`) - /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false); - /// - /// // The loopback addresses (`127.0.0.0/8`) - /// assert_eq!(Ipv4Addr::LOCALHOST.is_global(), false); - /// - /// // Link-local addresses (`169.254.0.0/16`) - /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false); - /// - /// // Addresses reserved for documentation (`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`) - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false); - /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false); - /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false); - /// - /// // Addresses reserved for benchmarking (`198.18.0.0/15`) - /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false); - /// - /// // Reserved addresses (`240.0.0.0/4`) - /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false); - /// - /// // The broadcast address (`255.255.255.255`) - /// assert_eq!(Ipv4Addr::BROADCAST.is_global(), false); - /// - /// // For a complete overview see the IANA IPv4 Special-Purpose Address Registry. - /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_global(&self) -> bool { - !(self.octets()[0] == 0 // "This network" - || self.is_private() - || self.is_shared() - || self.is_loopback() - || self.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) - || self.is_documentation() - || self.is_benchmarking() - || self.is_reserved() - || self.is_broadcast()) - } - - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true); - /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true); - /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); - /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_shared(&self) -> bool { - self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false); - /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true); - /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true); - /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); - /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_benchmarking(&self) -> bool { - self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 - } - - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true); - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true); - /// - /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false); - /// // The broadcast address is not considered as reserved for future use by this implementation - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); - /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_reserved(&self) -> bool { - self.octets()[0] & 240 == 240 && !self.is_broadcast() - } - - /// Returns [`true`] if this is a multicast address (`224.0.0.0/4`). - /// - /// Multicast addresses have a most significant octet between `224` and `239`, - /// and is defined by [IETF RFC 5771]. - /// - /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(224, 254, 0, 0).is_multicast(), true); - /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_multicast(&self) -> bool { - self.octets()[0] >= 224 && self.octets()[0] <= 239 - } - - /// Returns [`true`] if this is a broadcast address (`255.255.255.255`). - /// - /// A broadcast address has all octets set to `255` as defined in [IETF RFC 919]. - /// - /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true); - /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - 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. - /// - /// This is defined in [IETF RFC 5737]: - /// - /// - `192.0.2.0/24` (TEST-NET-1) - /// - `198.51.100.0/24` (TEST-NET-2) - /// - `203.0.113.0/24` (TEST-NET-3) - /// - /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_documentation(), true); - /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_documentation(), true); - /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true); - /// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_documentation(&self) -> bool { - matches!(self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _]) - } - - /// Converts this address to an [IPv4-compatible] [`IPv6` address]. - /// - /// `a.b.c.d` becomes `::a.b.c.d` - /// - /// Note that IPv4-compatible addresses have been officially deprecated. - /// If you don't explicitly need an IPv4-compatible address for legacy reasons, consider using `to_ipv6_mapped` instead. - /// - /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses - /// [`IPv6` address]: Ipv6Addr - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!( - /// Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(), - /// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x2ff) - /// ); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { - let [a, b, c, d] = self.octets(); - Ipv6Addr { octets: [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]. - /// - /// `a.b.c.d` becomes `::ffff:a.b.c.d` - /// - /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses - /// [`IPv6` address]: Ipv6Addr - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(), - /// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x2ff)); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { - let [a, b, c, d] = self.octets(); - Ipv6Addr { octets: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] } - } -} - -#[stable(feature = "ip_addr", since = "1.7.0")] -impl fmt::Display for IpAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - IpAddr::V4(ip) => ip.fmt(fmt), - IpAddr::V6(ip) => ip.fmt(fmt), - } - } -} - -#[stable(feature = "ip_addr", since = "1.7.0")] -impl fmt::Debug for IpAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From<Ipv4Addr> for IpAddr { - /// Copies this address to a new `IpAddr::V4`. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr}; - /// - /// let addr = Ipv4Addr::new(127, 0, 0, 1); - /// - /// assert_eq!( - /// IpAddr::V4(addr), - /// IpAddr::from(addr) - /// ) - /// ``` - #[inline] - fn from(ipv4: Ipv4Addr) -> IpAddr { - IpAddr::V4(ipv4) - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From<Ipv6Addr> for IpAddr { - /// Copies this address to a new `IpAddr::V6`. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr}; - /// - /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); - /// - /// assert_eq!( - /// IpAddr::V6(addr), - /// IpAddr::from(addr) - /// ); - /// ``` - #[inline] - fn from(ipv6: Ipv6Addr) -> IpAddr { - IpAddr::V6(ipv6) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Ipv4Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let octets = self.octets(); - - // If there are no alignment requirements, write the IP address directly to `f`. - // Otherwise, write it to a local buffer and then use `f.pad`. - if fmt.precision().is_none() && fmt.width().is_none() { - write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) - } else { - const LONGEST_IPV4_ADDR: &str = "255.255.255.255"; - - let mut buf = DisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new(); - // Buffer is long enough for the longest possible IPv4 address, so this should never fail. - write!(buf, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap(); - - fmt.pad(buf.as_str()) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Ipv4Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq<Ipv4Addr> for IpAddr { - #[inline] - fn eq(&self, other: &Ipv4Addr) -> bool { - match self { - IpAddr::V4(v4) => v4 == other, - IpAddr::V6(_) => false, - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq<IpAddr> for Ipv4Addr { - #[inline] - fn eq(&self, other: &IpAddr) -> bool { - match other { - IpAddr::V4(v4) => self == v4, - IpAddr::V6(_) => false, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Ipv4Addr { - #[inline] - fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd<Ipv4Addr> for IpAddr { - #[inline] - fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> { - match self { - IpAddr::V4(v4) => v4.partial_cmp(other), - IpAddr::V6(_) => Some(Ordering::Greater), - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd<IpAddr> for Ipv4Addr { - #[inline] - fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> { - match other { - IpAddr::V4(v4) => self.partial_cmp(v4), - IpAddr::V6(_) => Some(Ordering::Less), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Ipv4Addr { - #[inline] - fn cmp(&self, other: &Ipv4Addr) -> Ordering { - self.octets.cmp(&other.octets) - } -} +pub use core::net::Ipv6MulticastScope; impl IntoInner<c::in_addr> for Ipv4Addr { #[inline] fn into_inner(self) -> c::in_addr { // `s_addr` is stored as BE on all machines and the array is in BE order. // So the native endian conversion method is used so that it's never swapped. - c::in_addr { s_addr: u32::from_ne_bytes(self.octets) } + c::in_addr { s_addr: u32::from_ne_bytes(self.octets()) } } } impl FromInner<c::in_addr> for Ipv4Addr { fn from_inner(addr: c::in_addr) -> Ipv4Addr { - Ipv4Addr { octets: addr.s_addr.to_ne_bytes() } - } -} - -#[stable(feature = "ip_u32", since = "1.1.0")] -impl From<Ipv4Addr> for u32 { - /// Converts an `Ipv4Addr` into a host byte order `u32`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78); - /// assert_eq!(0x12345678, u32::from(addr)); - /// ``` - #[inline] - fn from(ip: Ipv4Addr) -> u32 { - u32::from_be_bytes(ip.octets) - } -} - -#[stable(feature = "ip_u32", since = "1.1.0")] -impl From<u32> for Ipv4Addr { - /// Converts a host byte order `u32` into an `Ipv4Addr`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::from(0x12345678); - /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr); - /// ``` - #[inline] - fn from(ip: u32) -> Ipv4Addr { - Ipv4Addr { octets: ip.to_be_bytes() } - } -} - -#[stable(feature = "from_slice_v4", since = "1.9.0")] -impl From<[u8; 4]> for Ipv4Addr { - /// Creates an `Ipv4Addr` from a four element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::from([13u8, 12u8, 11u8, 10u8]); - /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); - /// ``` - #[inline] - fn from(octets: [u8; 4]) -> Ipv4Addr { - Ipv4Addr { octets } - } -} - -#[stable(feature = "ip_from_slice", since = "1.17.0")] -impl From<[u8; 4]> for IpAddr { - /// Creates an `IpAddr::V4` from a four element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr}; - /// - /// let addr = IpAddr::from([13u8, 12u8, 11u8, 10u8]); - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(13, 12, 11, 10)), addr); - /// ``` - #[inline] - fn from(octets: [u8; 4]) -> IpAddr { - IpAddr::V4(Ipv4Addr::from(octets)) - } -} - -impl Ipv6Addr { - /// Creates a new IPv6 address from eight 16-bit segments. - /// - /// The result will represent the IP address `a:b:c:d:e:f:g:h`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); - /// ``` - #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { - let addr16 = [ - a.to_be(), - b.to_be(), - c.to_be(), - d.to_be(), - e.to_be(), - f.to_be(), - g.to_be(), - h.to_be(), - ]; - Ipv6Addr { - // All elements in `addr16` are big endian. - // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`. - octets: unsafe { transmute::<_, [u8; 16]>(addr16) }, - } - } - - /// An IPv6 address representing localhost: `::1`. - /// - /// This corresponds to constant `IN6ADDR_LOOPBACK_INIT` or `in6addr_loopback` in other - /// languages. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::LOCALHOST; - /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - /// ``` - #[doc(alias = "IN6ADDR_LOOPBACK_INIT")] - #[doc(alias = "in6addr_loopback")] - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); - - /// An IPv6 address representing the unspecified address: `::` - /// - /// This corresponds to constant `IN6ADDR_ANY_INIT` or `in6addr_any` in other languages. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::UNSPECIFIED; - /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); - /// ``` - #[doc(alias = "IN6ADDR_ANY_INIT")] - #[doc(alias = "in6addr_any")] - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); - - /// Returns the eight 16-bit segments that make up this address. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(), - /// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub const fn segments(&self) -> [u16; 8] { - // All elements in `self.octets` 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.octets) }; - // We want native endian u16 - [ - u16::from_be(a), - u16::from_be(b), - u16::from_be(c), - u16::from_be(d), - u16::from_be(e), - u16::from_be(f), - u16::from_be(g), - u16::from_be(h), - ] - } - - /// Returns [`true`] for the special 'unspecified' address (`::`). - /// - /// This property is defined in [IETF RFC 4291]. - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// 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_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - 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 the [loopback address] (`::1`), - /// as defined in [IETF RFC 4291 section 2.5.3]. - /// - /// Contrary to IPv4, in IPv6 there is only one loopback address. - /// - /// [loopback address]: Ipv6Addr::LOCALHOST - /// [IETF RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// 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_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - 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 reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// // Most IPv6 addresses are globally reachable: - /// assert_eq!(Ipv6Addr::new(0x26, 0, 0x1c9, 0, 0, 0xafc8, 0x10, 0x1).is_global(), true); - /// - /// // However some addresses have been assigned a special meaning - /// // that makes them not globally reachable. Some examples are: - /// - /// // The unspecified address (`::`) - /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_global(), false); - /// - /// // The loopback address (`::1`) - /// assert_eq!(Ipv6Addr::LOCALHOST.is_global(), false); - /// - /// // IPv4-mapped addresses (`::ffff:0:0/96`) - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), false); - /// - /// // Addresses reserved for benchmarking (`2001:2::/48`) - /// assert_eq!(Ipv6Addr::new(0x2001, 2, 0, 0, 0, 0, 0, 1,).is_global(), false); - /// - /// // Addresses reserved for documentation (`2001:db8::/32`) - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1).is_global(), false); - /// - /// // Unique local addresses (`fc00::/7`) - /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 1).is_global(), false); - /// - /// // Unicast addresses with link-local scope (`fe80::/10`) - /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 1).is_global(), false); - /// - /// // For a complete overview see the IANA IPv6 Special-Purpose Address Registry. - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_global(&self) -> bool { - !(self.is_unspecified() - || self.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) - )) - || self.is_documentation() - || self.is_unique_local() - || self.is_unicast_link_local()) - } - - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// 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); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_unique_local(&self) -> bool { - (self.segments()[0] & 0xfe00) == 0xfc00 - } - - /// Returns [`true`] if this is a unicast address, as defined by [IETF RFC 4291]. - /// Any address that is not a [multicast address] (`ff00::/8`) is unicast. - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [multicast address]: Ipv6Addr::is_multicast - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// // The unspecified and loopback addresses are unicast. - /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_unicast(), true); - /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast(), true); - /// - /// // Any address that is not a multicast address (`ff00::/8`) is unicast. - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast(), true); - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_unicast(), false); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_unicast(&self) -> bool { - !self.is_multicast() - } - - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// // The loopback address (`::1`) does not actually have link-local scope. - /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false); - /// - /// // Only addresses in `fe80::/10` have link-local scope. - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false); - /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); - /// - /// // Addresses outside the stricter `fe80::/64` also have link-local scope. - /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true); - /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_unicast_link_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfe80 - } - - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// 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); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) - } - - /// Returns [`true`] if this is an address reserved for benchmarking (`2001:2::/48`). - /// - /// This property is defined in [IETF RFC 5180], where it is mistakenly specified as covering the range `2001:0200::/48`. - /// This is corrected in [IETF RFC Errata 1752] to `2001:0002::/48`. - /// - /// [IETF RFC 5180]: https://tools.ietf.org/html/rfc5180 - /// [IETF RFC Errata 1752]: https://www.rfc-editor.org/errata_search.php?eid=1752 - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc613, 0x0).is_benchmarking(), false); - /// assert_eq!(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0).is_benchmarking(), true); - /// ``` - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_benchmarking(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0) - } - - /// Returns [`true`] if the address is a globally routable unicast address. - /// - /// The following return false: - /// - /// - the loopback address - /// - the link-local addresses - /// - unique local addresses - /// - the unspecified address - /// - the address range reserved for documentation - /// - /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7] - /// - /// ```no_rust - /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer - /// be supported in new implementations (i.e., new implementations must treat this prefix as - /// Global Unicast). - /// ``` - /// - /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// 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); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_unicast_global(&self) -> bool { - self.is_unicast() - && !self.is_loopback() - && !self.is_unicast_link_local() - && !self.is_unique_local() - && !self.is_unspecified() - && !self.is_documentation() - && !self.is_benchmarking() - } - - /// Returns the address's multicast scope if the address is multicast. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{Ipv6Addr, Ipv6MulticastScope}; - /// - /// assert_eq!( - /// Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).multicast_scope(), - /// Some(Ipv6MulticastScope::Global) - /// ); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn multicast_scope(&self) -> Option<Ipv6MulticastScope> { - if self.is_multicast() { - match self.segments()[0] & 0x000f { - 1 => Some(Ipv6MulticastScope::InterfaceLocal), - 2 => Some(Ipv6MulticastScope::LinkLocal), - 3 => Some(Ipv6MulticastScope::RealmLocal), - 4 => Some(Ipv6MulticastScope::AdminLocal), - 5 => Some(Ipv6MulticastScope::SiteLocal), - 8 => Some(Ipv6MulticastScope::OrganizationLocal), - 14 => Some(Ipv6MulticastScope::Global), - _ => None, - } - } else { - None - } - } - - /// Returns [`true`] if this is a multicast address (`ff00::/8`). - /// - /// This property is defined by [IETF RFC 4291]. - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// 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_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_multicast(&self) -> bool { - (self.segments()[0] & 0xff00) == 0xff00 - } - - /// Converts this address to an [`IPv4` address] if it's an [IPv4-mapped] address, - /// as defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`]. - /// - /// `::ffff:a.b.c.d` becomes `a.b.c.d`. - /// All addresses *not* starting with `::ffff` will return `None`. - /// - /// [`IPv4` address]: Ipv4Addr - /// [IPv4-mapped]: Ipv6Addr - /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4_mapped(), None); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(), - /// Some(Ipv4Addr::new(192, 10, 2, 255))); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[stable(feature = "ipv6_to_ipv4_mapped", since = "1.63.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - 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)) - } - _ => None, - } - } - - /// Converts this address to an [`IPv4` address] if it is either - /// an [IPv4-compatible] address as defined in [IETF RFC 4291 section 2.5.5.1], - /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2], - /// otherwise returns [`None`]. - /// - /// Note that this will return an [`IPv4` address] for the IPv6 loopback address `::1`. Use - /// [`Ipv6Addr::to_ipv4_mapped`] to avoid this. - /// - /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`. `::1` becomes `0.0.0.1`. - /// All addresses *not* starting with either all zeroes or `::ffff` will return `None`. - /// - /// [`IPv4` address]: Ipv4Addr - /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses - /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses - /// [IETF RFC 4291 section 2.5.5.1]: https://tools.ietf.org/html/rfc4291#section-2.5.5.1 - /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(), - /// Some(Ipv4Addr::new(192, 10, 2, 255))); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(), - /// Some(Ipv4Addr::new(0, 0, 0, 1))); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - 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(); - Some(Ipv4Addr::new(a, b, c, d)) - } else { - None - } - } - - /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped addresses, otherwise it - /// returns self wrapped in an `IpAddr::V6`. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).is_loopback(), false); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).to_canonical().is_loopback(), true); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn to_canonical(&self) -> IpAddr { - if let Some(mapped) = self.to_ipv4_mapped() { - return IpAddr::V4(mapped); - } - IpAddr::V6(*self) - } - - /// Returns the sixteen eight-bit integers the IPv6 address consists of. - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(), - /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - /// ``` - #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] - #[stable(feature = "ipv6_to_octets", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn octets(&self) -> [u8; 16] { - self.octets - } -} - -/// Write an Ipv6Addr, conforming to the canonical style described by -/// [RFC 5952](https://tools.ietf.org/html/rfc5952). -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Ipv6Addr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If there are no alignment requirements, write the IP address directly to `f`. - // Otherwise, write it to a local buffer and then use `f.pad`. - if f.precision().is_none() && f.width().is_none() { - let segments = self.segments(); - - // Special case for :: and ::1; otherwise they get written with the - // IPv4 formatter - if self.is_unspecified() { - f.write_str("::") - } else if self.is_loopback() { - f.write_str("::1") - } else if let Some(ipv4) = self.to_ipv4() { - match segments[5] { - // IPv4 Compatible address - 0 => write!(f, "::{}", ipv4), - // IPv4 Mapped address - 0xffff => write!(f, "::ffff:{}", ipv4), - _ => unreachable!(), - } - } else { - #[derive(Copy, Clone, Default)] - struct Span { - start: usize, - len: usize, - } - - // Find the inner 0 span - let zeroes = { - let mut longest = Span::default(); - let mut current = Span::default(); - - for (i, &segment) in segments.iter().enumerate() { - if segment == 0 { - if current.len == 0 { - current.start = i; - } - - current.len += 1; - - if current.len > longest.len { - longest = current; - } - } else { - current = Span::default(); - } - } - - longest - }; - - /// Write a colon-separated part of the address - #[inline] - fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { - if let Some((first, tail)) = chunk.split_first() { - write!(f, "{:x}", first)?; - for segment in tail { - f.write_char(':')?; - write!(f, "{:x}", segment)?; - } - } - Ok(()) - } - - if zeroes.len > 1 { - fmt_subslice(f, &segments[..zeroes.start])?; - f.write_str("::")?; - fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) - } else { - fmt_subslice(f, &segments) - } - } - } else { - const LONGEST_IPV6_ADDR: &str = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; - - let mut buf = DisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new(); - // Buffer is long enough for the longest possible IPv6 address, so this should never fail. - write!(buf, "{}", self).unwrap(); - - f.pad(buf.as_str()) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Ipv6Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq<IpAddr> for Ipv6Addr { - #[inline] - fn eq(&self, other: &IpAddr) -> bool { - match other { - IpAddr::V4(_) => false, - IpAddr::V6(v6) => self == v6, - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq<Ipv6Addr> for IpAddr { - #[inline] - fn eq(&self, other: &Ipv6Addr) -> bool { - match self { - IpAddr::V4(_) => false, - IpAddr::V6(v6) => v6 == other, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Ipv6Addr { - #[inline] - fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd<Ipv6Addr> for IpAddr { - #[inline] - fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> { - match self { - IpAddr::V4(_) => Some(Ordering::Less), - IpAddr::V6(v6) => v6.partial_cmp(other), - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd<IpAddr> for Ipv6Addr { - #[inline] - fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> { - match other { - IpAddr::V4(_) => Some(Ordering::Greater), - IpAddr::V6(v6) => self.partial_cmp(v6), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Ipv6Addr { - #[inline] - fn cmp(&self, other: &Ipv6Addr) -> Ordering { - self.segments().cmp(&other.segments()) + Ipv4Addr::from(addr.s_addr.to_ne_bytes()) } } impl IntoInner<c::in6_addr> for Ipv6Addr { fn into_inner(self) -> c::in6_addr { - c::in6_addr { s6_addr: self.octets } + c::in6_addr { s6_addr: self.octets() } } } impl FromInner<c::in6_addr> for Ipv6Addr { #[inline] fn from_inner(addr: c::in6_addr) -> Ipv6Addr { - Ipv6Addr { octets: addr.s6_addr } - } -} - -#[stable(feature = "i128", since = "1.26.0")] -impl From<Ipv6Addr> for u128 { - /// Convert an `Ipv6Addr` into a host byte order `u128`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::new( - /// 0x1020, 0x3040, 0x5060, 0x7080, - /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, - /// ); - /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr)); - /// ``` - #[inline] - fn from(ip: Ipv6Addr) -> u128 { - u128::from_be_bytes(ip.octets) - } -} -#[stable(feature = "i128", since = "1.26.0")] -impl From<u128> for Ipv6Addr { - /// Convert a host byte order `u128` into an `Ipv6Addr`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128); - /// assert_eq!( - /// Ipv6Addr::new( - /// 0x1020, 0x3040, 0x5060, 0x7080, - /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, - /// ), - /// addr); - /// ``` - #[inline] - fn from(ip: u128) -> Ipv6Addr { - Ipv6Addr::from(ip.to_be_bytes()) - } -} - -#[stable(feature = "ipv6_from_octets", since = "1.9.0")] -impl From<[u8; 16]> for Ipv6Addr { - /// Creates an `Ipv6Addr` from a sixteen element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::from([ - /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, - /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, - /// ]); - /// assert_eq!( - /// Ipv6Addr::new( - /// 0x1918, 0x1716, - /// 0x1514, 0x1312, - /// 0x1110, 0x0f0e, - /// 0x0d0c, 0x0b0a - /// ), - /// addr - /// ); - /// ``` - #[inline] - fn from(octets: [u8; 16]) -> Ipv6Addr { - Ipv6Addr { octets } - } -} - -#[stable(feature = "ipv6_from_segments", since = "1.16.0")] -impl From<[u16; 8]> for Ipv6Addr { - /// Creates an `Ipv6Addr` from an eight element 16-bit array. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::from([ - /// 525u16, 524u16, 523u16, 522u16, - /// 521u16, 520u16, 519u16, 518u16, - /// ]); - /// assert_eq!( - /// Ipv6Addr::new( - /// 0x20d, 0x20c, - /// 0x20b, 0x20a, - /// 0x209, 0x208, - /// 0x207, 0x206 - /// ), - /// addr - /// ); - /// ``` - #[inline] - fn from(segments: [u16; 8]) -> Ipv6Addr { - let [a, b, c, d, e, f, g, h] = segments; - Ipv6Addr::new(a, b, c, d, e, f, g, h) - } -} - -#[stable(feature = "ip_from_slice", since = "1.17.0")] -impl From<[u8; 16]> for IpAddr { - /// Creates an `IpAddr::V6` from a sixteen element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr}; - /// - /// let addr = IpAddr::from([ - /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, - /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, - /// ]); - /// assert_eq!( - /// IpAddr::V6(Ipv6Addr::new( - /// 0x1918, 0x1716, - /// 0x1514, 0x1312, - /// 0x1110, 0x0f0e, - /// 0x0d0c, 0x0b0a - /// )), - /// addr - /// ); - /// ``` - #[inline] - fn from(octets: [u8; 16]) -> IpAddr { - IpAddr::V6(Ipv6Addr::from(octets)) - } -} - -#[stable(feature = "ip_from_slice", since = "1.17.0")] -impl From<[u16; 8]> for IpAddr { - /// Creates an `IpAddr::V6` from an eight element 16-bit array. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr}; - /// - /// let addr = IpAddr::from([ - /// 525u16, 524u16, 523u16, 522u16, - /// 521u16, 520u16, 519u16, 518u16, - /// ]); - /// assert_eq!( - /// IpAddr::V6(Ipv6Addr::new( - /// 0x20d, 0x20c, - /// 0x20b, 0x20a, - /// 0x209, 0x208, - /// 0x207, 0x206 - /// )), - /// addr - /// ); - /// ``` - #[inline] - fn from(segments: [u16; 8]) -> IpAddr { - IpAddr::V6(Ipv6Addr::from(segments)) + Ipv6Addr::from(addr.s6_addr) } } diff --git a/library/std/src/net/ip_addr/tests.rs b/library/std/src/net/ip_addr/tests.rs index 7c3430b2b..ab99c0c2f 100644 --- a/library/std/src/net/ip_addr/tests.rs +++ b/library/std/src/net/ip_addr/tests.rs @@ -1,1039 +1,8 @@ -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); - // octal - let none: Option<Ipv4Addr> = "255.0.0.01".parse().ok(); - assert_eq!(None, none); - // octal zero - let none: Option<Ipv4Addr> = "255.0.0.00".parse().ok(); - assert_eq!(None, none); - let none: Option<Ipv4Addr> = "255.0.00.0".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()); - - // don't prefix `0x` to each segment in `dbg!`. - assert_eq!("1::4:5:0:0:8", &format!("{:#?}", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8))); -} - -#[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; - let benchmarking: u8 = 1 << 5; - - 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()); - } - - if ($mask & benchmarking) == benchmarking { - assert!(ip!($s).is_benchmarking()); - } else { - assert!(!ip!($s).is_benchmarking()); - } - }}; - } - - 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; - let benchmarking: u8 = 1 << 5; - - 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", benchmarking); - check!("198.18.54.2", benchmarking); - check!("198.19.255.255", benchmarking); - // 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::", global | multicast); - check!("ff02::", global | multicast); - check!("ff03::", global | multicast); - check!("ff04::", global | multicast); - check!("ff05::", global | multicast); - check!("ff08::", global | multicast); - check!("ff0e::", global | multicast); - check!("2001:db8:85a3::8a2e:370:7334", doc); - check!("2001:2::ac32:23ff:21", benchmarking); - 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 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 & 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 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"); - check!("192.0.0.255"); - check!("192.0.0.100"); - 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: u32 = 1 << 0; - let loopback: u32 = 1 << 1; - let unique_local: u32 = 1 << 2; - let global: u32 = 1 << 3; - let unicast_link_local: u32 = 1 << 4; - let unicast_global: u32 = 1 << 7; - let documentation: u32 = 1 << 8; - let benchmarking: u32 = 1 << 16; - let multicast_interface_local: u32 = 1 << 9; - let multicast_link_local: u32 = 1 << 10; - let multicast_realm_local: u32 = 1 << 11; - let multicast_admin_local: u32 = 1 << 12; - let multicast_site_local: u32 = 1 << 13; - let multicast_organization_local: u32 = 1 << 14; - let multicast_global: u32 = 1 << 15; - let multicast: u32 = 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_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 & benchmarking) == benchmarking { - assert!(ip!($s).is_benchmarking()); - } else { - assert!(!ip!($s).is_benchmarking()); - } - 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: u32 = 1 << 0; - let loopback: u32 = 1 << 1; - let unique_local: u32 = 1 << 2; - let global: u32 = 1 << 3; - let unicast_link_local: u32 = 1 << 4; - let unicast_global: u32 = 1 << 7; - let documentation: u32 = 1 << 8; - let benchmarking: u32 = 1 << 16; - let multicast_interface_local: u32 = 1 << 9; - let multicast_link_local: u32 = 1 << 10; - let multicast_realm_local: u32 = 1 << 11; - let multicast_admin_local: u32 = 1 << 12; - let multicast_site_local: u32 = 1 << 13; - let multicast_organization_local: u32 = 1 << 14; - let multicast_global: u32 = 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!( - "::ffff:127.0.0.1", - &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1], - unicast_global - ); - - check!( - "64:ff9b:1::", - &[0, 0x64, 0xff, 0x9b, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_global - ); - - check!("100::", &[0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); - - check!("2001::", &[0x20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); - - check!( - "2001:1::1", - &[0x20, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - global | unicast_global - ); - - check!( - "2001:1::2", - &[0x20, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], - global | unicast_global - ); - - check!( - "2001:3::", - &[0x20, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - global | unicast_global - ); - - check!( - "2001:4:112::", - &[0x20, 1, 0, 4, 1, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - global | unicast_global - ); - - check!( - "2001:20::", - &[0x20, 1, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - global | unicast_global - ); - - check!("2001:30::", &[0x20, 1, 0, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); - - check!( - "2001:200::", - &[0x20, 1, 2, 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); - - 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 - ); - - 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_global | global - ); - - check!( - "ff01::", - &[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_interface_local | global - ); - - check!( - "ff02::", - &[0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_link_local | global - ); - - check!( - "ff03::", - &[0xff, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_realm_local | global - ); - - check!( - "ff04::", - &[0xff, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_admin_local | global - ); - - check!( - "ff05::", - &[0xff, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_site_local | global - ); - - check!( - "ff08::", - &[0xff, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_organization_local | global - ); - - 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!( - "2001:2::ac32:23ff:21", - &[0x20, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0xac, 0x32, 0x23, 0xff, 0, 0x21], - benchmarking - ); - - 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 - ); -} +use crate::net::test::{sa4, tsa}; +use crate::net::Ipv4Addr; #[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_constructors() { - 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_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: bool = IP_ADDRESS.is_unicast_link_local(); - assert!(!IS_UNICAST_LINK_LOCAL); - - const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation(); - assert!(!IS_DOCUMENTATION); - - const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking(); - assert!(!IS_BENCHMARKING); - - 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); - - const IS_IP_V4: bool = IP_ADDRESS.is_ipv4(); - assert!(IS_IP_V4); - - const IS_IP_V6: bool = IP_ADDRESS.is_ipv6(); - assert!(!IS_IP_V6); -} - -#[test] -fn structural_match() { - // test that all IP types can be structurally matched upon - - const IPV4: Ipv4Addr = Ipv4Addr::LOCALHOST; - match IPV4 { - Ipv4Addr::LOCALHOST => {} - _ => unreachable!(), - } - - const IPV6: Ipv6Addr = Ipv6Addr::LOCALHOST; - match IPV6 { - Ipv6Addr::LOCALHOST => {} - _ => unreachable!(), - } - - const IP: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); - match IP { - IpAddr::V4(Ipv4Addr::LOCALHOST) => {} - _ => unreachable!(), - } -} diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 19d90e7ec..bcab15db3 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -26,8 +26,6 @@ use crate::io::{self, ErrorKind}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::parser::AddrParseError; -#[stable(feature = "rust1", since = "1.0.0")] pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; #[unstable(feature = "tcplistener_into_incoming", issue = "88339")] pub use self::tcp::IntoIncoming; @@ -35,10 +33,10 @@ pub use self::tcp::IntoIncoming; pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::udp::UdpSocket; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::net::AddrParseError; -mod display_buffer; mod ip_addr; -mod parser; mod socket_addr; mod tcp; #[cfg(test)] diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs deleted file mode 100644 index a38031c48..000000000 --- a/library/std/src/net/parser.rs +++ /dev/null @@ -1,500 +0,0 @@ -//! A private parser implementation of IPv4, IPv6, and socket addresses. -//! -//! 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}; -use crate::str::FromStr; - -trait ReadNumberHelper: crate::marker::Sized { - const ZERO: Self; - fn checked_mul(&self, other: u32) -> Option<Self>; - fn checked_add(&self, other: u32) -> Option<Self>; -} - -macro_rules! impl_helper { - ($($t:ty)*) => ($(impl ReadNumberHelper for $t { - const ZERO: Self = 0; - #[inline] - fn checked_mul(&self, other: u32) -> Option<Self> { - Self::checked_mul(*self, other.try_into().ok()?) - } - #[inline] - fn checked_add(&self, other: u32) -> Option<Self> { - Self::checked_add(*self, other.try_into().ok()?) - } - })*) -} - -impl_helper! { u8 u16 u32 } - -struct Parser<'a> { - // Parsing as ASCII, so can use byte array. - state: &'a [u8], -} - -impl<'a> Parser<'a> { - fn new(input: &'a [u8]) -> Parser<'a> { - Parser { state: input } - } - - /// Run a parser, and restore the pre-parse state if it fails. - fn read_atomically<T, F>(&mut self, inner: F) -> Option<T> - where - F: FnOnce(&mut Parser<'_>) -> Option<T>, - { - let state = self.state; - let result = inner(self); - if result.is_none() { - self.state = state; - } - result - } - - /// Run a parser, but fail if the entire input wasn't consumed. - /// Doesn't run atomically. - fn parse_with<T, F>(&mut self, inner: F, kind: AddrKind) -> Result<T, AddrParseError> - where - F: FnOnce(&mut Parser<'_>) -> Option<T>, - { - let result = inner(self); - if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(kind)) - } - - /// Peek the next character from the input - fn peek_char(&self) -> Option<char> { - self.state.first().map(|&b| char::from(b)) - } - - /// Read the next character from the input - fn read_char(&mut self) -> Option<char> { - self.state.split_first().map(|(&b, tail)| { - self.state = tail; - char::from(b) - }) - } - - #[must_use] - /// Read the next character from the input if it matches the target. - fn read_given_char(&mut self, target: char) -> Option<()> { - self.read_atomically(|p| { - p.read_char().and_then(|c| if c == target { Some(()) } else { None }) - }) - } - - /// Helper for reading separators in an indexed loop. Reads the separator - /// character iff index > 0, then runs the parser. When used in a loop, - /// the separator character will only be read on index > 0 (see - /// read_ipv4_addr for an example) - fn read_separator<T, F>(&mut self, sep: char, index: usize, inner: F) -> Option<T> - where - F: FnOnce(&mut Parser<'_>) -> Option<T>, - { - self.read_atomically(move |p| { - if index > 0 { - p.read_given_char(sep)?; - } - inner(p) - }) - } - - // Read a number off the front of the input in the given radix, stopping - // at the first non-digit character or eof. Fails if the number has more - // digits than max_digits or if there is no number. - fn read_number<T: ReadNumberHelper>( - &mut self, - radix: u32, - max_digits: Option<usize>, - allow_zero_prefix: bool, - ) -> Option<T> { - self.read_atomically(move |p| { - let mut result = T::ZERO; - let mut digit_count = 0; - let has_leading_zero = p.peek_char() == Some('0'); - - while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { - result = result.checked_mul(radix)?; - result = result.checked_add(digit)?; - digit_count += 1; - if let Some(max_digits) = max_digits { - if digit_count > max_digits { - return None; - } - } - } - - if digit_count == 0 { - None - } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { - None - } else { - Some(result) - } - }) - } - - /// Read an IPv4 address. - fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> { - self.read_atomically(|p| { - let mut groups = [0; 4]; - - for (i, slot) in groups.iter_mut().enumerate() { - *slot = p.read_separator('.', i, |p| { - // Disallow octal number in IP string. - // https://tools.ietf.org/html/rfc6943#section-3.1.1 - p.read_number(10, Some(3), false) - })?; - } - - Some(groups.into()) - }) - } - - /// Read an IPv6 Address. - fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> { - /// Read a chunk of an IPv6 address into `groups`. Returns the number - /// of groups read, along with a bool indicating if an embedded - /// trailing IPv4 address was read. Specifically, read a series of - /// colon-separated IPv6 groups (0x0000 - 0xFFFF), with an optional - /// trailing embedded IPv4 address. - fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) { - let limit = groups.len(); - - for (i, slot) in groups.iter_mut().enumerate() { - // Try to read a trailing embedded IPv4 address. There must be - // at least two groups left. - if i < limit - 1 { - let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr()); - - if let Some(v4_addr) = ipv4 { - let [one, two, three, four] = v4_addr.octets(); - groups[i + 0] = u16::from_be_bytes([one, two]); - groups[i + 1] = u16::from_be_bytes([three, four]); - return (i + 2, true); - } - } - - let group = p.read_separator(':', i, |p| p.read_number(16, Some(4), true)); - - match group { - Some(g) => *slot = g, - None => return (i, false), - } - } - (groups.len(), false) - } - - self.read_atomically(|p| { - // Read the front part of the address; either the whole thing, or up - // to the first :: - let mut head = [0; 8]; - let (head_size, head_ipv4) = read_groups(p, &mut head); - - if head_size == 8 { - return Some(head.into()); - } - - // IPv4 part is not allowed before `::` - if head_ipv4 { - return None; - } - - // Read `::` if previous code parsed less than 8 groups. - // `::` indicates one or more groups of 16 bits of zeros. - p.read_given_char(':')?; - p.read_given_char(':')?; - - // Read the back part of the address. The :: must contain at least one - // set of zeroes, so our max length is 7. - let mut tail = [0; 7]; - let limit = 8 - (head_size + 1); - let (tail_size, _) = read_groups(p, &mut tail[..limit]); - - // Concat the head and tail of the IP address - head[(8 - tail_size)..8].copy_from_slice(&tail[..tail_size]); - - Some(head.into()) - }) - } - - /// Read an IP Address, either IPv4 or IPv6. - fn read_ip_addr(&mut self) -> Option<IpAddr> { - self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6)) - } - - /// Read a `:` followed by a port in base 10. - fn read_port(&mut self) -> Option<u16> { - self.read_atomically(|p| { - p.read_given_char(':')?; - p.read_number(10, None, true) - }) - } - - /// Read a `%` followed by a scope ID in base 10. - fn read_scope_id(&mut self) -> Option<u32> { - self.read_atomically(|p| { - p.read_given_char('%')?; - p.read_number(10, None, true) - }) - } - - /// Read an IPv4 address with a port. - fn read_socket_addr_v4(&mut self) -> Option<SocketAddrV4> { - self.read_atomically(|p| { - let ip = p.read_ipv4_addr()?; - let port = p.read_port()?; - Some(SocketAddrV4::new(ip, port)) - }) - } - - /// Read an IPv6 address with a port. - fn read_socket_addr_v6(&mut self) -> Option<SocketAddrV6> { - self.read_atomically(|p| { - p.read_given_char('[')?; - let ip = p.read_ipv6_addr()?; - let scope_id = p.read_scope_id().unwrap_or(0); - p.read_given_char(']')?; - - let port = p.read_port()?; - Some(SocketAddrV6::new(ip, port, 0, scope_id)) - }) - } - - /// Read an IP address with a port - fn read_socket_addr(&mut self) -> Option<SocketAddr> { - self.read_socket_addr_v4() - .map(SocketAddr::V4) - .or_else(|| self.read_socket_addr_v6().map(SocketAddr::V6)) - } -} - -impl IpAddr { - /// Parse an IP address from a slice of bytes. - /// - /// ``` - /// #![feature(addr_parse_ascii)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); - /// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - /// - /// assert_eq!(IpAddr::parse_ascii(b"127.0.0.1"), Ok(localhost_v4)); - /// assert_eq!(IpAddr::parse_ascii(b"::1"), Ok(localhost_v6)); - /// ``` - #[unstable(feature = "addr_parse_ascii", issue = "101035")] - pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { - Parser::new(b).parse_with(|p| p.read_ip_addr(), AddrKind::Ip) - } -} - -#[stable(feature = "ip_addr", since = "1.7.0")] -impl FromStr for IpAddr { - type Err = AddrParseError; - fn from_str(s: &str) -> Result<IpAddr, AddrParseError> { - Self::parse_ascii(s.as_bytes()) - } -} - -impl Ipv4Addr { - /// Parse an IPv4 address from a slice of bytes. - /// - /// ``` - /// #![feature(addr_parse_ascii)] - /// - /// use std::net::Ipv4Addr; - /// - /// let localhost = Ipv4Addr::new(127, 0, 0, 1); - /// - /// assert_eq!(Ipv4Addr::parse_ascii(b"127.0.0.1"), Ok(localhost)); - /// ``` - #[unstable(feature = "addr_parse_ascii", issue = "101035")] - pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { - // don't try to parse if too long - if b.len() > 15 { - Err(AddrParseError(AddrKind::Ipv4)) - } else { - Parser::new(b).parse_with(|p| p.read_ipv4_addr(), AddrKind::Ipv4) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromStr for Ipv4Addr { - type Err = AddrParseError; - fn from_str(s: &str) -> Result<Ipv4Addr, AddrParseError> { - Self::parse_ascii(s.as_bytes()) - } -} - -impl Ipv6Addr { - /// Parse an IPv6 address from a slice of bytes. - /// - /// ``` - /// #![feature(addr_parse_ascii)] - /// - /// use std::net::Ipv6Addr; - /// - /// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); - /// - /// assert_eq!(Ipv6Addr::parse_ascii(b"::1"), Ok(localhost)); - /// ``` - #[unstable(feature = "addr_parse_ascii", issue = "101035")] - pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { - Parser::new(b).parse_with(|p| p.read_ipv6_addr(), AddrKind::Ipv6) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromStr for Ipv6Addr { - type Err = AddrParseError; - fn from_str(s: &str) -> Result<Ipv6Addr, AddrParseError> { - Self::parse_ascii(s.as_bytes()) - } -} - -impl SocketAddrV4 { - /// Parse an IPv4 socket address from a slice of bytes. - /// - /// ``` - /// #![feature(addr_parse_ascii)] - /// - /// use std::net::{Ipv4Addr, SocketAddrV4}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// - /// assert_eq!(SocketAddrV4::parse_ascii(b"127.0.0.1:8080"), Ok(socket)); - /// ``` - #[unstable(feature = "addr_parse_ascii", issue = "101035")] - pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { - Parser::new(b).parse_with(|p| p.read_socket_addr_v4(), AddrKind::SocketV4) - } -} - -#[stable(feature = "socket_addr_from_str", since = "1.5.0")] -impl FromStr for SocketAddrV4 { - type Err = AddrParseError; - fn from_str(s: &str) -> Result<SocketAddrV4, AddrParseError> { - Self::parse_ascii(s.as_bytes()) - } -} - -impl SocketAddrV6 { - /// Parse an IPv6 socket address from a slice of bytes. - /// - /// ``` - /// #![feature(addr_parse_ascii)] - /// - /// use std::net::{Ipv6Addr, SocketAddrV6}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// - /// assert_eq!(SocketAddrV6::parse_ascii(b"[2001:db8::1]:8080"), Ok(socket)); - /// ``` - #[unstable(feature = "addr_parse_ascii", issue = "101035")] - pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { - Parser::new(b).parse_with(|p| p.read_socket_addr_v6(), AddrKind::SocketV6) - } -} - -#[stable(feature = "socket_addr_from_str", since = "1.5.0")] -impl FromStr for SocketAddrV6 { - type Err = AddrParseError; - fn from_str(s: &str) -> Result<SocketAddrV6, AddrParseError> { - Self::parse_ascii(s.as_bytes()) - } -} - -impl SocketAddr { - /// Parse a socket address from a slice of bytes. - /// - /// ``` - /// #![feature(addr_parse_ascii)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; - /// - /// let socket_v4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// let socket_v6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080); - /// - /// assert_eq!(SocketAddr::parse_ascii(b"127.0.0.1:8080"), Ok(socket_v4)); - /// assert_eq!(SocketAddr::parse_ascii(b"[::1]:8080"), Ok(socket_v6)); - /// ``` - #[unstable(feature = "addr_parse_ascii", issue = "101035")] - pub fn parse_ascii(b: &[u8]) -> Result<Self, AddrParseError> { - Parser::new(b).parse_with(|p| p.read_socket_addr(), AddrKind::Socket) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromStr for SocketAddr { - type Err = AddrParseError; - fn from_str(s: &str) -> Result<SocketAddr, AddrParseError> { - Self::parse_ascii(s.as_bytes()) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -enum AddrKind { - Ip, - Ipv4, - Ipv6, - Socket, - SocketV4, - SocketV6, -} - -/// An error which can be returned when parsing an IP address or a socket address. -/// -/// This error is used as the error type for the [`FromStr`] implementation for -/// [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`], [`SocketAddr`], [`SocketAddrV4`], and -/// [`SocketAddrV6`]. -/// -/// # Potential causes -/// -/// `AddrParseError` may be thrown because the provided string does not parse as the given type, -/// often because it includes information only handled by a different address type. -/// -/// ```should_panic -/// use std::net::IpAddr; -/// let _foo: IpAddr = "127.0.0.1:8080".parse().expect("Cannot handle the socket port"); -/// ``` -/// -/// [`IpAddr`] doesn't handle the port. Use [`SocketAddr`] instead. -/// -/// ``` -/// use std::net::SocketAddr; -/// -/// // No problem, the `panic!` message has disappeared. -/// let _foo: SocketAddr = "127.0.0.1:8080".parse().expect("unreachable panic"); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AddrParseError(AddrKind); - -#[stable(feature = "addr_parse_error_error", since = "1.4.0")] -impl fmt::Display for AddrParseError { - #[allow(deprecated, deprecated_in_future)] - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(self.description()) - } -} - -#[stable(feature = "addr_parse_error_error", since = "1.4.0")] -impl Error for AddrParseError { - #[allow(deprecated)] - fn description(&self) -> &str { - match self.0 { - AddrKind::Ip => "invalid IP address syntax", - AddrKind::Ipv4 => "invalid IPv4 address syntax", - AddrKind::Ipv6 => "invalid IPv6 address syntax", - AddrKind::Socket => "invalid socket address syntax", - AddrKind::SocketV4 => "invalid IPv4 socket address syntax", - AddrKind::SocketV6 => "invalid IPv6 socket address syntax", - } - } -} diff --git a/library/std/src/net/parser/tests.rs b/library/std/src/net/parser/tests.rs deleted file mode 100644 index 6d2d48eca..000000000 --- a/library/std/src/net/parser/tests.rs +++ /dev/null @@ -1,149 +0,0 @@ -// 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 SCOPE_ID: u32 = 1337; - -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 IPV4_STR_WITH_OCTAL: &str = "0127.0.0.1"; -const IPV4_STR_WITH_HEX: &str = "0x10.0.0.1"; - -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_V4_WITH_OCTAL: &str = "2001:db8::0127.0.0.1"; -const IPV6_STR_V4_WITH_HEX: &str = "2001:db8::0x10.0.0.1"; -const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080"; -const IPV6_STR_PORT_SCOPE_ID: &str = "[2001:db8::c0a8:1%1337]: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(IPV4_STR_WITH_OCTAL).is_err()); - assert!(Ipv4Addr::from_str(IPV4_STR_WITH_HEX).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(IPV6_STR_V4_WITH_OCTAL).is_err()); - assert!(Ipv6Addr::from_str(IPV6_STR_V4_WITH_HEX).is_err()); - 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() { - assert_eq!(IPV6_STR_PORT.parse(), Ok(SocketAddrV6::new(IPV6, PORT, 0, 0))); - assert_eq!(IPV6_STR_PORT_SCOPE_ID.parse(), Ok(SocketAddrV6::new(IPV6, PORT, 0, SCOPE_ID))); - - 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/socket_addr.rs b/library/std/src/net/socket_addr.rs index 33b0dfa03..421fed907 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -1,9 +1,7 @@ +// Tests for this module #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use crate::cmp::Ordering; -use crate::fmt::{self, Write}; -use crate::hash; use crate::io; use crate::iter; use crate::mem; @@ -15,533 +13,23 @@ use crate::sys_common::net::LookupHost; use crate::sys_common::{FromInner, IntoInner}; use crate::vec; -use super::display_buffer::DisplayBuffer; - -/// An internet socket address, either IPv4 or IPv6. -/// -/// Internet socket addresses consist of an [IP address], a 16-bit port number, as well -/// as possibly some version-dependent additional information. See [`SocketAddrV4`]'s and -/// [`SocketAddrV6`]'s respective documentation for more details. -/// -/// The size of a `SocketAddr` instance may vary depending on the target operating -/// system. -/// -/// [IP address]: IpAddr -/// -/// # Examples -/// -/// ``` -/// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -/// -/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); -/// -/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); -/// assert_eq!(socket.port(), 8080); -/// assert_eq!(socket.is_ipv4(), true); -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum SocketAddr { - /// An IPv4 socket address. - #[stable(feature = "rust1", since = "1.0.0")] - V4(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV4), - /// An IPv6 socket address. - #[stable(feature = "rust1", since = "1.0.0")] - V6(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV6), -} - -/// An IPv4 socket address. -/// -/// IPv4 socket addresses consist of an [`IPv4` address] and a 16-bit port number, as -/// stated in [IETF RFC 793]. -/// -/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. -/// -/// The size of a `SocketAddrV4` struct may vary depending on the target operating -/// system. Do not assume that this type has the same memory layout as the underlying -/// system representation. -/// -/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 -/// [`IPv4` address]: Ipv4Addr -/// -/// # Examples -/// -/// ``` -/// use std::net::{Ipv4Addr, SocketAddrV4}; -/// -/// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); -/// -/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); -/// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); -/// assert_eq!(socket.port(), 8080); -/// ``` -#[derive(Copy, Clone, Eq, PartialEq)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SocketAddrV4 { - ip: Ipv4Addr, - port: u16, -} - -/// An IPv6 socket address. -/// -/// IPv6 socket addresses consist of an [`IPv6` address], a 16-bit port number, as well -/// as fields containing the traffic class, the flow label, and a scope identifier -/// (see [IETF RFC 2553, Section 3.3] for more details). -/// -/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. -/// -/// The size of a `SocketAddrV6` struct may vary depending on the target operating -/// system. Do not assume that this type has the same memory layout as the underlying -/// system representation. -/// -/// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 -/// [`IPv6` address]: Ipv6Addr -/// -/// # Examples -/// -/// ``` -/// use std::net::{Ipv6Addr, SocketAddrV6}; -/// -/// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0); -/// -/// assert_eq!("[2001:db8::1]:8080".parse(), Ok(socket)); -/// assert_eq!(socket.ip(), &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); -/// assert_eq!(socket.port(), 8080); -/// ``` -#[derive(Copy, Clone, Eq, PartialEq)] #[stable(feature = "rust1", since = "1.0.0")] -pub struct SocketAddrV6 { - ip: Ipv6Addr, - port: u16, - flowinfo: u32, - scope_id: u32, -} - -impl SocketAddr { - /// Creates a new socket address from an [IP address] and a port number. - /// - /// [IP address]: IpAddr - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[stable(feature = "ip_addr", since = "1.7.0")] - #[must_use] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn new(ip: IpAddr, port: u16) -> SocketAddr { - match ip { - IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)), - IpAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0)), - } - } - - /// Returns the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); - /// ``` - #[must_use] - #[stable(feature = "ip_addr", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn ip(&self) -> IpAddr { - match *self { - SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()), - SocketAddr::V6(ref a) => IpAddr::V6(*a.ip()), - } - } - - /// Changes the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// socket.set_ip(IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); - /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_ip(&mut self, new_ip: IpAddr) { - // `match (*self, new_ip)` would have us mutate a copy of self only to throw it away. - match (self, new_ip) { - (&mut SocketAddr::V4(ref mut a), IpAddr::V4(new_ip)) => a.set_ip(new_ip), - (&mut SocketAddr::V6(ref mut a), IpAddr::V6(new_ip)) => a.set_ip(new_ip), - (self_, new_ip) => *self_ = Self::new(new_ip, self_.port()), - } - } - - /// Returns the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn port(&self) -> u16 { - match *self { - SocketAddr::V4(ref a) => a.port(), - SocketAddr::V6(ref a) => a.port(), - } - } - - /// Changes the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// socket.set_port(1025); - /// assert_eq!(socket.port(), 1025); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_port(&mut self, new_port: u16) { - match *self { - SocketAddr::V4(ref mut a) => a.set_port(new_port), - SocketAddr::V6(ref mut a) => a.set_port(new_port), - } - } - - /// Returns [`true`] if the [IP address] in this `SocketAddr` is an - /// [`IPv4` address], and [`false`] otherwise. - /// - /// [IP address]: IpAddr - /// [`IPv4` address]: IpAddr::V4 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.is_ipv4(), true); - /// assert_eq!(socket.is_ipv6(), false); - /// ``` - #[must_use] - #[stable(feature = "sockaddr_checker", since = "1.16.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn is_ipv4(&self) -> bool { - matches!(*self, SocketAddr::V4(_)) - } - - /// Returns [`true`] if the [IP address] in this `SocketAddr` is an - /// [`IPv6` address], and [`false`] otherwise. - /// - /// [IP address]: IpAddr - /// [`IPv6` address]: IpAddr::V6 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 0, 1)), 8080); - /// assert_eq!(socket.is_ipv4(), false); - /// assert_eq!(socket.is_ipv6(), true); - /// ``` - #[must_use] - #[stable(feature = "sockaddr_checker", since = "1.16.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn is_ipv6(&self) -> bool { - matches!(*self, SocketAddr::V6(_)) - } -} - -impl SocketAddrV4 { - /// Creates a new socket address from an [`IPv4` address] and a port number. - /// - /// [`IPv4` address]: Ipv4Addr - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 { - SocketAddrV4 { ip, port } - } - - /// Returns the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn ip(&self) -> &Ipv4Addr { - &self.ip - } - - /// Changes the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// socket.set_ip(Ipv4Addr::new(192, 168, 0, 1)); - /// assert_eq!(socket.ip(), &Ipv4Addr::new(192, 168, 0, 1)); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_ip(&mut self, new_ip: Ipv4Addr) { - self.ip = new_ip; - } - - /// Returns the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn port(&self) -> u16 { - self.port - } - - /// Changes the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// socket.set_port(4242); - /// assert_eq!(socket.port(), 4242); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_port(&mut self, new_port: u16) { - self.port = new_port; - } -} - -impl SocketAddrV6 { - /// Creates a new socket address from an [`IPv6` address], a 16-bit port number, - /// and the `flowinfo` and `scope_id` fields. - /// - /// For more information on the meaning and layout of the `flowinfo` and `scope_id` - /// parameters, see [IETF RFC 2553, Section 3.3]. - /// - /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 - /// [`IPv6` address]: Ipv6Addr - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 { - SocketAddrV6 { ip, port, flowinfo, scope_id } - } - - /// Returns the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// assert_eq!(socket.ip(), &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn ip(&self) -> &Ipv6Addr { - &self.ip - } - - /// Changes the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// socket.set_ip(Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); - /// assert_eq!(socket.ip(), &Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_ip(&mut self, new_ip: Ipv6Addr) { - self.ip = new_ip; - } - - /// Returns the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn port(&self) -> u16 { - self.port - } - - /// Changes the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// socket.set_port(4242); - /// assert_eq!(socket.port(), 4242); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_port(&mut self, new_port: u16) { - self.port = new_port; - } - - /// Returns the flow information associated with this address. - /// - /// This information corresponds to the `sin6_flowinfo` field in C's `netinet/in.h`, - /// as specified in [IETF RFC 2553, Section 3.3]. - /// It combines information about the flow label and the traffic class as specified - /// in [IETF RFC 2460], respectively [Section 6] and [Section 7]. - /// - /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 - /// [IETF RFC 2460]: https://tools.ietf.org/html/rfc2460 - /// [Section 6]: https://tools.ietf.org/html/rfc2460#section-6 - /// [Section 7]: https://tools.ietf.org/html/rfc2460#section-7 - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); - /// assert_eq!(socket.flowinfo(), 10); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn flowinfo(&self) -> u32 { - self.flowinfo - } - - /// Changes the flow information associated with this socket address. - /// - /// See [`SocketAddrV6::flowinfo`]'s documentation for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); - /// socket.set_flowinfo(56); - /// assert_eq!(socket.flowinfo(), 56); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_flowinfo(&mut self, new_flowinfo: u32) { - self.flowinfo = new_flowinfo; - } - - /// Returns the scope ID associated with this address. - /// - /// This information corresponds to the `sin6_scope_id` field in C's `netinet/in.h`, - /// as specified in [IETF RFC 2553, Section 3.3]. - /// - /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); - /// assert_eq!(socket.scope_id(), 78); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")] - pub const fn scope_id(&self) -> u32 { - self.scope_id - } - - /// Changes the scope ID associated with this socket address. - /// - /// See [`SocketAddrV6::scope_id`]'s documentation for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); - /// socket.set_scope_id(42); - /// assert_eq!(socket.scope_id(), 42); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_scope_id(&mut self, new_scope_id: u32) { - self.scope_id = new_scope_id; - } -} +pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; impl FromInner<c::sockaddr_in> for SocketAddrV4 { fn from_inner(addr: c::sockaddr_in) -> SocketAddrV4 { - SocketAddrV4 { ip: Ipv4Addr::from_inner(addr.sin_addr), port: u16::from_be(addr.sin_port) } + SocketAddrV4::new(Ipv4Addr::from_inner(addr.sin_addr), u16::from_be(addr.sin_port)) } } impl FromInner<c::sockaddr_in6> for SocketAddrV6 { fn from_inner(addr: c::sockaddr_in6) -> SocketAddrV6 { - SocketAddrV6 { - ip: Ipv6Addr::from_inner(addr.sin6_addr), - port: u16::from_be(addr.sin6_port), - flowinfo: addr.sin6_flowinfo, - scope_id: addr.sin6_scope_id, - } + SocketAddrV6::new( + Ipv6Addr::from_inner(addr.sin6_addr), + u16::from_be(addr.sin6_port), + addr.sin6_flowinfo, + addr.sin6_scope_id, + ) } } @@ -549,8 +37,8 @@ impl IntoInner<c::sockaddr_in> for SocketAddrV4 { fn into_inner(self) -> c::sockaddr_in { c::sockaddr_in { sin_family: c::AF_INET as c::sa_family_t, - sin_port: self.port.to_be(), - sin_addr: self.ip.into_inner(), + sin_port: self.port().to_be(), + sin_addr: self.ip().into_inner(), ..unsafe { mem::zeroed() } } } @@ -560,162 +48,15 @@ impl IntoInner<c::sockaddr_in6> for SocketAddrV6 { fn into_inner(self) -> c::sockaddr_in6 { c::sockaddr_in6 { sin6_family: c::AF_INET6 as c::sa_family_t, - sin6_port: self.port.to_be(), - sin6_addr: self.ip.into_inner(), - sin6_flowinfo: self.flowinfo, - sin6_scope_id: self.scope_id, + sin6_port: self.port().to_be(), + sin6_addr: self.ip().into_inner(), + sin6_flowinfo: self.flowinfo(), + sin6_scope_id: self.scope_id(), ..unsafe { mem::zeroed() } } } } -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From<SocketAddrV4> for SocketAddr { - /// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`]. - fn from(sock4: SocketAddrV4) -> SocketAddr { - SocketAddr::V4(sock4) - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From<SocketAddrV6> for SocketAddr { - /// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`]. - fn from(sock6: SocketAddrV6) -> SocketAddr { - SocketAddr::V6(sock6) - } -} - -#[stable(feature = "addr_from_into_ip", since = "1.17.0")] -impl<I: Into<IpAddr>> From<(I, u16)> for SocketAddr { - /// Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`]. - /// - /// This conversion creates a [`SocketAddr::V4`] for an [`IpAddr::V4`] - /// and creates a [`SocketAddr::V6`] for an [`IpAddr::V6`]. - /// - /// `u16` is treated as port of the newly created [`SocketAddr`]. - fn from(pieces: (I, u16)) -> SocketAddr { - SocketAddr::new(pieces.0.into(), pieces.1) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SocketAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - SocketAddr::V4(ref a) => a.fmt(f), - SocketAddr::V6(ref a) => a.fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SocketAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SocketAddrV4 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If there are no alignment requirements, write the socket address directly to `f`. - // Otherwise, write it to a local buffer and then use `f.pad`. - if f.precision().is_none() && f.width().is_none() { - write!(f, "{}:{}", self.ip(), self.port()) - } else { - const LONGEST_IPV4_SOCKET_ADDR: &str = "255.255.255.255:65536"; - - let mut buf = DisplayBuffer::<{ LONGEST_IPV4_SOCKET_ADDR.len() }>::new(); - // Buffer is long enough for the longest possible IPv4 socket address, so this should never fail. - write!(buf, "{}:{}", self.ip(), self.port()).unwrap(); - - f.pad(buf.as_str()) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SocketAddrV4 { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SocketAddrV6 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If there are no alignment requirements, write the socket address directly to `f`. - // Otherwise, write it to a local buffer and then use `f.pad`. - if f.precision().is_none() && f.width().is_none() { - match self.scope_id() { - 0 => write!(f, "[{}]:{}", self.ip(), self.port()), - scope_id => write!(f, "[{}%{}]:{}", self.ip(), scope_id, self.port()), - } - } else { - const LONGEST_IPV6_SOCKET_ADDR: &str = - "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967296]:65536"; - - let mut buf = DisplayBuffer::<{ LONGEST_IPV6_SOCKET_ADDR.len() }>::new(); - match self.scope_id() { - 0 => write!(buf, "[{}]:{}", self.ip(), self.port()), - scope_id => write!(buf, "[{}%{}]:{}", self.ip(), scope_id, self.port()), - } - // Buffer is long enough for the longest possible IPv6 socket address, so this should never fail. - .unwrap(); - - f.pad(buf.as_str()) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SocketAddrV6 { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl PartialOrd for SocketAddrV4 { - fn partial_cmp(&self, other: &SocketAddrV4) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl PartialOrd for SocketAddrV6 { - fn partial_cmp(&self, other: &SocketAddrV6) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl Ord for SocketAddrV4 { - fn cmp(&self, other: &SocketAddrV4) -> Ordering { - self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl Ord for SocketAddrV6 { - fn cmp(&self, other: &SocketAddrV6) -> Ordering { - self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl hash::Hash for SocketAddrV4 { - fn hash<H: hash::Hasher>(&self, s: &mut H) { - (self.port, self.ip).hash(s) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl hash::Hash for SocketAddrV6 { - fn hash<H: hash::Hasher>(&self, s: &mut H) { - (self.port, &self.ip, self.flowinfo, self.scope_id).hash(s) - } -} - /// A trait for objects which can be converted or resolved to one or more /// [`SocketAddr`] values. /// diff --git a/library/std/src/net/socket_addr/tests.rs b/library/std/src/net/socket_addr/tests.rs index 15211f819..dfc6dabbe 100644 --- a/library/std/src/net/socket_addr/tests.rs +++ b/library/std/src/net/socket_addr/tests.rs @@ -64,11 +64,11 @@ fn ipv4_socket_addr_to_string() { // Test padding. assert_eq!( - &format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), + format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), "1.1.1.1:53 " ); assert_eq!( - &format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), + format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), " 1.1.1.1:53" ); } @@ -111,11 +111,11 @@ fn ipv6_socket_addr_to_string() { // Test padding. assert_eq!( - &format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), + format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), "[1:2:3:4:5:6:7:8]:9 " ); assert_eq!( - &format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), + format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), " [1:2:3:4:5:6:7:8]:9" ); } diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 69b72a81c..ac09a8059 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -829,7 +829,7 @@ impl TcpListener { /// } /// /// fn main() -> std::io::Result<()> { - /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// let listener = TcpListener::bind("127.0.0.1:80")?; /// /// for stream in listener.incoming() { /// match stream { diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 8c0adcfb0..e019bc0b6 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -670,7 +670,10 @@ fn debug() { // 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( + any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"), + ignore +)] #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 #[test] fn timeouts() { diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index f82904ffb..892fe2ba8 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -180,7 +180,10 @@ fn debug() { // 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)] +#[cfg_attr( + any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"), + ignore +)] #[test] fn timeouts() { let addr = next_test_ip4(); diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index c41e093a7..99a4e0b51 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -9,7 +9,7 @@ use crate::fs; use crate::io; use crate::marker::PhantomData; use crate::mem::forget; -#[cfg(not(any(target_arch = "wasm32", target_env = "sgx")))] +#[cfg(not(any(target_arch = "wasm32", target_env = "sgx", target_os = "hermit")))] use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -89,7 +89,7 @@ impl OwnedFd { impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. - #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(any(target_arch = "wasm32", target_os = "hermit")))] #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> { // We want to atomically duplicate this file descriptor and set the @@ -112,7 +112,7 @@ impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. - #[cfg(target_arch = "wasm32")] + #[cfg(any(target_arch = "wasm32", target_os = "hermit"))] #[stable(feature = "io_safety", since = "1.63.0")] pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> { Err(crate::io::const_io_error!( @@ -174,7 +174,10 @@ impl Drop for OwnedFd { // the file descriptor was closed or not, and if we retried (for // something like EINTR), we might close another valid file descriptor // opened after we closed ours. + #[cfg(not(target_os = "hermit"))] let _ = libc::close(self.fd); + #[cfg(target_os = "hermit")] + let _ = hermit_abi::close(self.fd); } } } @@ -396,6 +399,14 @@ impl<T: AsFd> AsFd for crate::sync::Arc<T> { } } +#[stable(feature = "asfd_rc", since = "1.69.0")] +impl<T: AsFd> AsFd for crate::rc::Rc<T> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + (**self).as_fd() + } +} + #[stable(feature = "asfd_ptrs", since = "1.64.0")] impl<T: AsFd> AsFd for Box<T> { #[inline] diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index f92a05066..592e072ad 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -4,6 +4,9 @@ use crate::fs; use crate::io; +#[cfg(target_os = "hermit")] +use crate::os::hermit::io::OwnedFd; +#[cfg(not(target_os = "hermit"))] use crate::os::raw; #[cfg(all(doc, not(target_arch = "wasm32")))] use crate::os::unix::io::AsFd; @@ -12,11 +15,18 @@ use crate::os::unix::io::OwnedFd; #[cfg(target_os = "wasi")] use crate::os::wasi::io::OwnedFd; use crate::sys_common::{AsInner, IntoInner}; +#[cfg(target_os = "hermit")] +use hermit_abi as libc; /// Raw file descriptors. #[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(target_os = "hermit"))] pub type RawFd = raw::c_int; +#[rustc_allowed_through_unstable_modules] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg(target_os = "hermit")] +pub type RawFd = i32; /// A trait to extract the raw file descriptor from an underlying object. /// @@ -244,6 +254,14 @@ impl<T: AsRawFd> AsRawFd for crate::sync::Arc<T> { } } +#[stable(feature = "asfd_rc", since = "1.69.0")] +impl<T: AsRawFd> AsRawFd for crate::rc::Rc<T> { + #[inline] + fn as_raw_fd(&self) -> RawFd { + (**self).as_raw_fd() + } +} + #[stable(feature = "asrawfd_ptrs", since = "1.63.0")] impl<T: AsRawFd> AsRawFd for Box<T> { #[inline] diff --git a/library/std/src/os/fuchsia/raw.rs b/library/std/src/os/fuchsia/raw.rs index 060d6e86b..ea6b94f2f 100644 --- a/library/std/src/os/fuchsia/raw.rs +++ b/library/std/src/os/fuchsia/raw.rs @@ -24,12 +24,7 @@ pub type pthread_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; -#[cfg(any( - target_arch = "x86", - target_arch = "le32", - target_arch = "powerpc", - target_arch = "arm" -))] +#[cfg(any(target_arch = "x86", target_arch = "powerpc", target_arch = "arm"))] mod arch { use crate::os::raw::{c_long, c_short, c_uint}; diff --git a/library/std/src/os/hermit/io/mod.rs b/library/std/src/os/hermit/io/mod.rs new file mode 100644 index 000000000..524dfae0d --- /dev/null +++ b/library/std/src/os/hermit/io/mod.rs @@ -0,0 +1,13 @@ +#![stable(feature = "os_fd", since = "1.66.0")] + +mod net; +#[path = "../../fd/owned.rs"] +mod owned; +#[path = "../../fd/raw.rs"] +mod raw; + +// Export the types and traits for the public API. +#[stable(feature = "os_fd", since = "1.66.0")] +pub use owned::*; +#[stable(feature = "os_fd", since = "1.66.0")] +pub use raw::*; diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs new file mode 100644 index 000000000..8f3802d78 --- /dev/null +++ b/library/std/src/os/hermit/io/net.rs @@ -0,0 +1,46 @@ +use crate::os::hermit::io::OwnedFd; +use crate::os::hermit::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::{net, sys}; + +macro_rules! impl_as_raw_fd { + ($($t:ident)*) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl AsRawFd for net::$t { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.as_inner().socket().as_raw_fd() + } + } + )*}; +} +impl_as_raw_fd! { TcpStream TcpListener UdpSocket } + +macro_rules! impl_from_raw_fd { + ($($t:ident)*) => {$( + #[stable(feature = "from_raw_os", since = "1.1.0")] + impl FromRawFd for net::$t { + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> net::$t { + unsafe { + let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); + net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + } + } + } + )*}; +} +impl_from_raw_fd! { TcpStream TcpListener UdpSocket } + +macro_rules! impl_into_raw_fd { + ($($t:ident)*) => {$( + #[stable(feature = "into_raw_os", since = "1.4.0")] + impl IntoRawFd for net::$t { + #[inline] + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_socket().into_inner().into_inner().into_raw_fd() + } + } + )*}; +} +impl_into_raw_fd! { TcpStream TcpListener UdpSocket } diff --git a/library/std/src/os/hermit/mod.rs b/library/std/src/os/hermit/mod.rs index 4657b545a..89b1b8319 100644 --- a/library/std/src/os/hermit/mod.rs +++ b/library/std/src/os/hermit/mod.rs @@ -1,6 +1,11 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[allow(unused_extern_crates)] +#[stable(feature = "rust1", since = "1.0.0")] +pub extern crate hermit_abi as abi; + pub mod ffi; +pub mod io; /// A prelude for conveniently writing platform-specific code. /// diff --git a/library/std/src/os/l4re/raw.rs b/library/std/src/os/l4re/raw.rs index 699e8be33..b3f7439f8 100644 --- a/library/std/src/os/l4re/raw.rs +++ b/library/std/src/os/l4re/raw.rs @@ -26,7 +26,6 @@ pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; #[cfg(any( target_arch = "x86", - target_arch = "le32", target_arch = "m68k", target_arch = "powerpc", target_arch = "sparc", diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index c73791d14..f46028c3a 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -26,7 +26,6 @@ pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; #[cfg(any( target_arch = "x86", - target_arch = "le32", target_arch = "m68k", target_arch = "powerpc", target_arch = "sparc", diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 42773805c..b148d8a00 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -60,16 +60,6 @@ pub mod windows {} all(target_vendor = "fortanix", target_env = "sgx") ) )))] -#[cfg(target_os = "hermit")] -#[path = "hermit/mod.rs"] -pub mod unix; -#[cfg(not(all( - doc, - any( - all(target_arch = "wasm32", not(target_os = "wasi")), - all(target_vendor = "fortanix", target_env = "sgx") - ) -)))] #[cfg(all(not(target_os = "hermit"), any(unix, doc)))] pub mod unix; @@ -123,6 +113,8 @@ pub mod freebsd; pub mod fuchsia; #[cfg(target_os = "haiku")] pub mod haiku; +#[cfg(target_os = "hermit")] +pub mod hermit; #[cfg(target_os = "horizon")] pub mod horizon; #[cfg(target_os = "illumos")] @@ -135,6 +127,8 @@ pub mod l4re; pub mod macos; #[cfg(target_os = "netbsd")] pub mod netbsd; +#[cfg(target_os = "nto")] +pub mod nto; #[cfg(target_os = "openbsd")] pub mod openbsd; #[cfg(target_os = "redox")] diff --git a/library/std/src/os/net/mod.rs b/library/std/src/os/net/mod.rs index 5ec267c41..b7046dd7c 100644 --- a/library/std/src/os/net/mod.rs +++ b/library/std/src/os/net/mod.rs @@ -1,4 +1,13 @@ //! OS-specific networking functionality. +// See cfg macros in `library/std/src/os/mod.rs` for why these platforms must +// be special-cased during rustdoc generation. +#[cfg(not(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +)))] #[cfg(any(target_os = "linux", target_os = "android", doc))] pub(super) mod linux_ext; diff --git a/library/std/src/os/nto/fs.rs b/library/std/src/os/nto/fs.rs new file mode 100644 index 000000000..8f915b08c --- /dev/null +++ b/library/std/src/os/nto/fs.rs @@ -0,0 +1,92 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_sec as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_sec as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_sec as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/library/std/src/os/nto/mod.rs b/library/std/src/os/nto/mod.rs new file mode 100644 index 000000000..3e591dace --- /dev/null +++ b/library/std/src/os/nto/mod.rs @@ -0,0 +1,4 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod fs; +pub(super) mod raw; diff --git a/library/std/src/os/nto/raw.rs b/library/std/src/os/nto/raw.rs new file mode 100644 index 000000000..90e9ad546 --- /dev/null +++ b/library/std/src/os/nto/raw.rs @@ -0,0 +1,40 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] +#![deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +use crate::os::raw::c_int; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = u32; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_int; + +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, time_t}; + +mod arch { + use crate::os::raw::c_long; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = i32; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u32; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = c_long; +} diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 3fc6cc44c..a0e664acd 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -17,6 +17,10 @@ use crate::sealed::Sealed; #[allow(unused_imports)] use io::{Read, Write}; +// Tests for this module +#[cfg(test)] +mod tests; + /// Unix-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { @@ -54,6 +58,16 @@ pub trait FileExt { #[stable(feature = "file_offset", since = "1.15.0")] fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; + /// Like `read_at`, except that it reads into a slice of buffers. + /// + /// Data is copied to fill each buffer in order, with the final buffer + /// written to possibly being only partially filled. This method must behave + /// equivalently to a single call to read with concatenated buffers. + #[unstable(feature = "unix_file_vectored_at", issue = "89517")] + fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + io::default_read_vectored(|b| self.read_at(b, offset), bufs) + } + /// Reads the exact number of byte required to fill `buf` from the given offset. /// /// The offset is relative to the start of the file and thus independent @@ -155,6 +169,16 @@ pub trait FileExt { #[stable(feature = "file_offset", since = "1.15.0")] fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>; + /// Like `write_at`, except that it writes from a slice of buffers. + /// + /// Data is copied from each buffer in order, with the final buffer read + /// from possibly being only partially consumed. This method must behave as + /// a call to `write_at` with the buffers concatenated would. + #[unstable(feature = "unix_file_vectored_at", issue = "89517")] + fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> { + io::default_write_vectored(|b| self.write_at(b, offset), bufs) + } + /// Attempts to write an entire buffer starting from a given offset. /// /// The offset is relative to the start of the file and thus independent @@ -218,9 +242,15 @@ impl FileExt for fs::File { fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { self.as_inner().read_at(buf, offset) } + fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + self.as_inner().read_vectored_at(bufs, offset) + } fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { self.as_inner().write_at(buf, offset) } + fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> { + self.as_inner().write_vectored_at(bufs, offset) + } } /// Unix-specific extensions to [`fs::Permissions`]. diff --git a/library/std/src/os/unix/fs/tests.rs b/library/std/src/os/unix/fs/tests.rs new file mode 100644 index 000000000..67f607bd4 --- /dev/null +++ b/library/std/src/os/unix/fs/tests.rs @@ -0,0 +1,57 @@ +use super::*; + +#[test] +fn read_vectored_at() { + let msg = b"preadv is working!"; + let dir = crate::sys_common::io::test::tmpdir(); + + let filename = dir.join("preadv.txt"); + { + let mut file = fs::File::create(&filename).unwrap(); + file.write_all(msg).unwrap(); + } + { + let file = fs::File::open(&filename).unwrap(); + let mut buf0 = [0; 4]; + let mut buf1 = [0; 3]; + + let mut iovec = [io::IoSliceMut::new(&mut buf0), io::IoSliceMut::new(&mut buf1)]; + + let n = file.read_vectored_at(&mut iovec, 4).unwrap(); + + assert!(n == 4 || n == 7); + assert_eq!(&buf0, b"dv i"); + + if n == 7 { + assert_eq!(&buf1, b"s w"); + } + } +} + +#[test] +fn write_vectored_at() { + let msg = b"pwritev is not working!"; + let dir = crate::sys_common::io::test::tmpdir(); + + let filename = dir.join("preadv.txt"); + { + let mut file = fs::File::create(&filename).unwrap(); + file.write_all(msg).unwrap(); + } + let expected = { + let file = fs::File::options().write(true).open(&filename).unwrap(); + let buf0 = b" "; + let buf1 = b"great "; + + let iovec = [io::IoSlice::new(buf0), io::IoSlice::new(buf1)]; + + let n = file.write_vectored_at(&iovec, 11).unwrap(); + + assert!(n == 4 || n == 11); + + if n == 4 { b"pwritev is working!" } else { b"pwritev is great !" } + }; + + let content = fs::read(&filename).unwrap(); + assert_eq!(&content, expected); +} diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index f97fa0fb0..eb2d7ce11 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -65,6 +65,8 @@ mod platform { pub use crate::os::macos::*; #[cfg(target_os = "netbsd")] pub use crate::os::netbsd::*; + #[cfg(target_os = "nto")] + pub use crate::os::nto::*; #[cfg(target_os = "openbsd")] pub use crate::os::openbsd::*; #[cfg(target_os = "redox")] @@ -95,7 +97,8 @@ pub mod thread; target_os = "watchos", target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "nto", ))] pub mod ucred; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index f758f88d0..272b4f5dc 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -19,7 +19,8 @@ use crate::{fmt, io}; target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "haiku" + target_os = "haiku", + target_os = "nto", ))] use libc::MSG_NOSIGNAL; #[cfg(not(any( @@ -29,7 +30,8 @@ use libc::MSG_NOSIGNAL; target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "haiku" + target_os = "haiku", + target_os = "nto", )))] const MSG_NOSIGNAL: libc::c_int = 0x0; diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 37fcfa844..f8c29a6d3 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -167,6 +167,7 @@ fn long_path() { } #[test] +#[cfg(not(target_os = "nto"))] fn timeouts() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 09b2bfe39..729c63d18 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -12,15 +12,23 @@ use crate::sealed::Sealed; use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; -#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))] -type UserId = u32; -#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))] -type GroupId = u32; - -#[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] -type UserId = u16; -#[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] -type GroupId = u16; +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] { + type UserId = u16; + type GroupId = u16; + } else if #[cfg(target_os = "nto")] { + // Both IDs are signed, see `sys/target_nto.h` of the QNX Neutrino SDP. + // Only positive values should be used, see e.g. + // https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/s/setuid.html + type UserId = i32; + type GroupId = i32; + } else { + type UserId = u32; + type GroupId = u32; + } +} /// Unix-specific extensions to the [`process::Command`] builder. /// diff --git a/library/std/src/os/unix/ucred.rs b/library/std/src/os/unix/ucred.rs index ae4faf27b..95967eac2 100644 --- a/library/std/src/os/unix/ucred.rs +++ b/library/std/src/os/unix/ucred.rs @@ -79,7 +79,8 @@ pub mod impl_linux { target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", - target_os = "netbsd" + target_os = "netbsd", + target_os = "nto", ))] pub mod impl_bsd { use super::UCred; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index b0db3112e..e59f32af7 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -95,13 +95,16 @@ impl Default for Hook { static HOOK: RwLock<Hook> = RwLock::new(Hook::Default); -/// Registers a custom panic hook, replacing any that was previously registered. +/// Registers a custom panic hook, replacing the previously registered hook. /// /// The panic hook is invoked when a thread panics, but before the panic runtime /// is invoked. As such, the hook will run with both the aborting and unwinding -/// runtimes. The default hook prints a message to standard error and generates -/// a backtrace if requested, but this behavior can be customized with the -/// `set_hook` and [`take_hook`] functions. +/// runtimes. +/// +/// The default hook, which is registered at startup, prints a message to standard error and +/// generates a backtrace if requested. This behavior can be customized using the `set_hook` function. +/// The current hook can be retrieved while reinstating the default hook with the [`take_hook`] +/// function. /// /// [`take_hook`]: ./fn.take_hook.html /// @@ -143,13 +146,14 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) { drop(old); } -/// Unregisters the current panic hook, returning it. +/// Unregisters the current panic hook and returns it, registering the default hook +/// in its place. /// /// *See also the function [`set_hook`].* /// /// [`set_hook`]: ./fn.set_hook.html /// -/// If no custom hook is registered, the default hook will be returned. +/// If the default hook is registered it will be returned, but remain registered. /// /// # Panics /// diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 4778114b4..cd6b393a2 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2531,6 +2531,8 @@ impl Path { /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. /// + /// If `path` is absolute, it replaces the current path. + /// /// See [`PathBuf::push`] for more details on what it means to adjoin a path. /// /// # Examples @@ -2539,6 +2541,7 @@ impl Path { /// use std::path::{Path, PathBuf}; /// /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd")); + /// assert_eq!(Path::new("/etc").join("/bin/sh"), PathBuf::from("/bin/sh")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use] @@ -2685,6 +2688,7 @@ impl Path { /// escapes the path please use [`Debug`] instead. /// /// [`Display`]: fmt::Display + /// [`Debug`]: fmt::Debug /// /// # Examples /// diff --git a/library/std/src/personality/emcc.rs b/library/std/src/personality/emcc.rs index f942bdf18..cb52ae89b 100644 --- a/library/std/src/personality/emcc.rs +++ b/library/std/src/personality/emcc.rs @@ -1,7 +1,7 @@ //! On Emscripten Rust panics are wrapped in C++ exceptions, so we just forward //! to `__gxx_personality_v0` which is provided by Emscripten. -use libc::c_int; +use crate::ffi::c_int; use unwind as uw; // This is required by the compiler to exist (e.g., it's a lang item), but it's diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs index 5fc1b91a1..41c0fe725 100644 --- a/library/std/src/personality/gcc.rs +++ b/library/std/src/personality/gcc.rs @@ -37,7 +37,8 @@ //! and the last personality routine transfers control to the catch block. use super::dwarf::eh::{self, EHAction, EHContext}; -use libc::{c_int, uintptr_t}; +use crate::ffi::c_int; +use libc::uintptr_t; use unwind as uw; // Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister() diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index d6e9da187..6f78811a1 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -587,8 +587,10 @@ mod prim_pointer {} /// There are two syntactic forms for creating an array: /// /// * 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`]. +/// * A repeat expression `[expr; N]` where `N` is how many times to repeat `expr` in the array. `expr` must either be: +/// +/// * A value of a type implementing the [`Copy`] trait +/// * A `const` value /// /// Note that `[expr; 0]` is allowed, and produces an empty array. /// This will still evaluate `expr`, however, and immediately drop the resulting value, so diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 62ce2cb33..1952e19e6 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1416,7 +1416,7 @@ impl From<fs::File> for Stdio { /// use std::fs::File; /// use std::process::Command; /// - /// // With the `foo.txt` file containing `Hello, world!" + /// // With the `foo.txt` file containing "Hello, world!" /// let file = File::open("foo.txt").unwrap(); /// /// let reverse = Command::new("rev") diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 4a1530530..7e85d6a06 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -1,8 +1,21 @@ -use crate::cell::Cell; +use crate::cell::UnsafeCell; use crate::fmt; +use crate::mem::ManuallyDrop; use crate::ops::Deref; use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sync::OnceLock; +use crate::sync::Once; + +use super::once::ExclusiveState; + +// We use the state of a Once as discriminant value. Upon creation, the state is +// "incomplete" and `f` contains the initialization closure. In the first call to +// `call_once`, `f` is taken and run. If it succeeds, `value` is set and the state +// is changed to "complete". If it panics, the Once is poisoned, so none of the +// two fields is initialized. +union Data<T, F> { + value: ManuallyDrop<T>, + f: ManuallyDrop<F>, +} /// A value which is initialized on the first access. /// @@ -43,16 +56,17 @@ use crate::sync::OnceLock; /// ``` #[unstable(feature = "once_cell", issue = "74465")] pub struct LazyLock<T, F = fn() -> T> { - cell: OnceLock<T>, - init: Cell<Option<F>>, + once: Once, + data: UnsafeCell<Data<T, F>>, } + impl<T, F: FnOnce() -> T> LazyLock<T, F> { /// Creates a new lazy value with the given initializing /// function. #[inline] #[unstable(feature = "once_cell", issue = "74465")] pub const fn new(f: F) -> LazyLock<T, F> { - LazyLock { cell: OnceLock::new(), init: Cell::new(Some(f)) } + LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) } } /// Forces the evaluation of this lazy value and @@ -74,10 +88,50 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { #[inline] #[unstable(feature = "once_cell", issue = "74465")] pub fn force(this: &LazyLock<T, F>) -> &T { - this.cell.get_or_init(|| match this.init.take() { - Some(f) => f(), - None => panic!("Lazy instance has previously been poisoned"), - }) + this.once.call_once(|| { + // SAFETY: `call_once` only runs this closure once, ever. + let data = unsafe { &mut *this.data.get() }; + let f = unsafe { ManuallyDrop::take(&mut data.f) }; + let value = f(); + data.value = ManuallyDrop::new(value); + }); + + // SAFETY: + // There are four possible scenarios: + // * the closure was called and initialized `value`. + // * the closure was called and panicked, so this point is never reached. + // * the closure was not called, but a previous call initialized `value`. + // * the closure was not called because the Once is poisoned, so this point + // is never reached. + // So `value` has definitely been initialized and will not be modified again. + unsafe { &*(*this.data.get()).value } + } +} + +impl<T, F> LazyLock<T, F> { + /// Get the inner value if it has already been initialized. + fn get(&self) -> Option<&T> { + if self.once.is_completed() { + // SAFETY: + // The closure has been run successfully, so `value` has been initialized + // and will not be modified again. + Some(unsafe { &*(*self.data.get()).value }) + } else { + None + } + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl<T, F> Drop for LazyLock<T, F> { + fn drop(&mut self) { + match self.once.state() { + ExclusiveState::Incomplete => unsafe { ManuallyDrop::drop(&mut self.data.get_mut().f) }, + ExclusiveState::Complete => unsafe { + ManuallyDrop::drop(&mut self.data.get_mut().value) + }, + ExclusiveState::Poisoned => {} + } } } @@ -103,23 +157,23 @@ impl<T: Default> Default for LazyLock<T> { #[unstable(feature = "once_cell", issue = "74465")] impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Lazy").field("cell", &self.cell).finish_non_exhaustive() + match self.get() { + Some(v) => f.debug_tuple("LazyLock").field(v).finish(), + None => f.write_str("LazyLock(Uninit)"), + } } } // We never create a `&F` from a `&LazyLock<T, F>` so it is fine // to not impl `Sync` for `F` -// we do create a `&mut Option<F>` in `force`, but this is -// properly synchronized, so it only happens once -// so it also does not contribute to this impl. #[unstable(feature = "once_cell", issue = "74465")] -unsafe impl<T, F: Send> Sync for LazyLock<T, F> where OnceLock<T>: Sync {} +unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {} // auto-derived `Send` impl is OK. #[unstable(feature = "once_cell", issue = "74465")] -impl<T, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> where OnceLock<T>: RefUnwindSafe {} +impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {} #[unstable(feature = "once_cell", issue = "74465")] -impl<T, F: UnwindSafe> UnwindSafe for LazyLock<T, F> where OnceLock<T>: UnwindSafe {} +impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {} #[cfg(test)] mod tests; diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index ba20bab87..4edc95617 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -186,7 +186,7 @@ mod condvar; mod lazy_lock; mod mpmc; mod mutex; -mod once; +pub(crate) mod once; mod once_lock; mod poison; mod remutex; diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs index c1e3e48b0..c6bb09b04 100644 --- a/library/std/src/sync/mpmc/array.rs +++ b/library/std/src/sync/mpmc/array.rs @@ -319,19 +319,10 @@ impl<T> Channel<T> { ) -> Result<(), SendTimeoutError<T>> { let token = &mut Token::default(); loop { - // Try sending a message several times. - let backoff = Backoff::new(); - loop { - if self.start_send(token) { - let res = unsafe { self.write(token, msg) }; - return res.map_err(SendTimeoutError::Disconnected); - } - - if backoff.is_completed() { - break; - } else { - backoff.spin_light(); - } + // Try sending a message. + if self.start_send(token) { + let res = unsafe { self.write(token, msg) }; + return res.map_err(SendTimeoutError::Disconnected); } if let Some(d) = deadline { @@ -379,6 +370,7 @@ impl<T> Channel<T> { pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> { let token = &mut Token::default(); loop { + // Try receiving a message. if self.start_recv(token) { let res = unsafe { self.read(token) }; return res.map_err(|_| RecvTimeoutError::Disconnected); diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs index cfe42750d..d053d69e2 100644 --- a/library/std/src/sync/mpmc/utils.rs +++ b/library/std/src/sync/mpmc/utils.rs @@ -105,10 +105,8 @@ impl Backoff { /// Backs off using lightweight spinning. /// - /// This method should be used for: - /// - Retrying an operation because another thread made progress. i.e. on CAS failure. - /// - Waiting for an operation to complete by spinning optimistically for a few iterations - /// before falling back to parking the thread (see `Backoff::is_completed`). + /// This method should be used for retrying an operation because another thread made + /// progress. i.e. on CAS failure. #[inline] pub fn spin_light(&self) { let step = self.step.get().min(SPIN_LIMIT); @@ -134,10 +132,4 @@ impl Backoff { self.step.set(self.step.get() + 1); } - - /// Returns `true` if quadratic backoff has completed and parking the thread is advised. - #[inline] - pub fn is_completed(&self) -> bool { - self.step.get() > SPIN_LIMIT - } } diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 0f25417d6..1b17c3108 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -43,6 +43,12 @@ pub struct OnceState { pub(crate) inner: sys::OnceState, } +pub(crate) enum ExclusiveState { + Incomplete, + Poisoned, + Complete, +} + /// Initialization value for static [`Once`] values. /// /// # Examples @@ -248,6 +254,16 @@ impl Once { pub fn is_completed(&self) -> bool { self.inner.is_completed() } + + /// Returns the current state of the `Once` instance. + /// + /// Since this takes a mutable reference, no initialization can currently + /// be running, so the state must be either "incomplete", "poisoned" or + /// "complete". + #[inline] + pub(crate) fn state(&mut self) -> ExclusiveState { + self.inner.state() + } } #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs index 3edbe7280..403a5e627 100644 --- a/library/std/src/sys/common/alloc.rs +++ b/library/std/src/sys/common/alloc.rs @@ -7,6 +7,7 @@ use crate::ptr; #[cfg(any( target_arch = "x86", target_arch = "arm", + target_arch = "m68k", target_arch = "mips", target_arch = "powerpc", target_arch = "powerpc64", diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs index afcae6c90..220a76e4b 100644 --- a/library/std/src/sys/hermit/args.rs +++ b/library/std/src/sys/hermit/args.rs @@ -1,6 +1,6 @@ use crate::ffi::{c_char, CStr, OsString}; use crate::fmt; -use crate::os::unix::ffi::OsStringExt; +use crate::os::hermit::ffi::OsStringExt; use crate::ptr; use crate::sync::atomic::{ AtomicIsize, AtomicPtr, diff --git a/library/std/src/sys/hermit/fd.rs b/library/std/src/sys/hermit/fd.rs index c400f5f2c..3a2cdd301 100644 --- a/library/std/src/sys/hermit/fd.rs +++ b/library/std/src/sys/hermit/fd.rs @@ -1,36 +1,23 @@ #![unstable(reason = "not public", issue = "none", feature = "fd")] use crate::io::{self, Read}; -use crate::mem; +use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd}; use crate::sys::cvt; use crate::sys::hermit::abi; use crate::sys::unsupported; -use crate::sys_common::AsInner; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +use crate::os::hermit::io::*; #[derive(Debug)] pub struct FileDesc { - fd: i32, + fd: OwnedFd, } impl FileDesc { - pub fn new(fd: i32) -> FileDesc { - FileDesc { fd } - } - - pub fn raw(&self) -> i32 { - self.fd - } - - /// Extracts the actual file descriptor without closing it. - pub fn into_raw(self) -> i32 { - let fd = self.fd; - mem::forget(self); - fd - } - pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { - let result = unsafe { abi::read(self.fd, buf.as_mut_ptr(), buf.len()) }; - cvt(result as i32) + let result = cvt(unsafe { abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?; + Ok(result as usize) } pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { @@ -39,8 +26,8 @@ impl FileDesc { } pub fn write(&self, buf: &[u8]) -> io::Result<usize> { - let result = unsafe { abi::write(self.fd, buf.as_ptr(), buf.len()) }; - cvt(result as i32) + let result = cvt(unsafe { abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?; + Ok(result as usize) } pub fn duplicate(&self) -> io::Result<FileDesc> { @@ -69,19 +56,45 @@ impl<'a> Read for &'a FileDesc { } } -impl AsInner<i32> for FileDesc { - fn as_inner(&self) -> &i32 { +impl IntoInner<OwnedFd> for FileDesc { + fn into_inner(self) -> OwnedFd { + self.fd + } +} + +impl FromInner<OwnedFd> for FileDesc { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self { fd: owned_fd } + } +} + +impl FromRawFd for FileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self { fd: FromRawFd::from_raw_fd(raw_fd) } + } +} + +impl AsInner<OwnedFd> for FileDesc { + fn as_inner(&self) -> &OwnedFd { &self.fd } } -impl Drop for FileDesc { - fn drop(&mut self) { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // (opened after we closed ours. - let _ = unsafe { abi::close(self.fd) }; +impl AsFd for FileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +impl AsRawFd for FileDesc { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} + +impl IntoRawFd for FileDesc { + fn into_raw_fd(self) -> RawFd { + self.fd.into_raw_fd() } } diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 6fb92c037..c966f2177 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -3,14 +3,17 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; -use crate::sys::hermit::abi; -use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; +use crate::sys::hermit::abi::{ + self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, +}; use crate::sys::hermit::fd::FileDesc; use crate::sys::time::SystemTime; use crate::sys::unsupported; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; pub use crate::sys_common::fs::{copy, try_exists}; //pub use crate::sys_common::fs::remove_dir_all; @@ -283,7 +286,7 @@ impl File { } let fd = unsafe { cvt(abi::open(path.as_ptr(), flags, mode))? }; - Ok(File(FileDesc::new(fd as i32))) + Ok(File(unsafe { FileDesc::from_raw_fd(fd as i32) })) } pub fn file_attr(&self) -> io::Result<FileAttr> { @@ -363,6 +366,54 @@ impl DirBuilder { } } +impl AsInner<FileDesc> for File { + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl AsInnerMut<FileDesc> for File { + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl IntoInner<FileDesc> for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner<FileDesc> for File { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for File { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) + } +} + pub fn readdir(_p: &Path) -> io::Result<ReadDir> { unsupported() } diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index 6811fadb0..d34a4cfed 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -13,7 +13,7 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! -#![allow(unsafe_op_in_unsafe_fn)] +#![allow(missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn)] use crate::intrinsics; use crate::os::raw::c_char; @@ -57,9 +57,7 @@ pub mod locks { } use crate::io::ErrorKind; - -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as abi; +use crate::os::hermit::abi; pub fn unsupported<T>() -> crate::io::Result<T> { Err(unsupported_err()) @@ -72,11 +70,6 @@ pub fn unsupported_err() -> crate::io::Error { ) } -#[no_mangle] -pub extern "C" fn floor(x: f64) -> f64 { - unsafe { intrinsics::floorf64(x) } -} - pub fn abort_internal() -> ! { unsafe { abi::abort(); @@ -131,25 +124,72 @@ pub unsafe extern "C" fn runtime_entry( pub fn decode_error_kind(errno: i32) -> ErrorKind { match errno { - x if x == 13 as i32 => ErrorKind::PermissionDenied, - x if x == 98 as i32 => ErrorKind::AddrInUse, - x if x == 99 as i32 => ErrorKind::AddrNotAvailable, - x if x == 11 as i32 => ErrorKind::WouldBlock, - x if x == 103 as i32 => ErrorKind::ConnectionAborted, - x if x == 111 as i32 => ErrorKind::ConnectionRefused, - x if x == 104 as i32 => ErrorKind::ConnectionReset, - x if x == 17 as i32 => ErrorKind::AlreadyExists, - x if x == 4 as i32 => ErrorKind::Interrupted, - x if x == 22 as i32 => ErrorKind::InvalidInput, - x if x == 2 as i32 => ErrorKind::NotFound, - x if x == 107 as i32 => ErrorKind::NotConnected, - x if x == 1 as i32 => ErrorKind::PermissionDenied, - x if x == 32 as i32 => ErrorKind::BrokenPipe, - x if x == 110 as i32 => ErrorKind::TimedOut, + abi::errno::EACCES => ErrorKind::PermissionDenied, + abi::errno::EADDRINUSE => ErrorKind::AddrInUse, + abi::errno::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + abi::errno::EAGAIN => ErrorKind::WouldBlock, + abi::errno::ECONNABORTED => ErrorKind::ConnectionAborted, + abi::errno::ECONNREFUSED => ErrorKind::ConnectionRefused, + abi::errno::ECONNRESET => ErrorKind::ConnectionReset, + abi::errno::EEXIST => ErrorKind::AlreadyExists, + abi::errno::EINTR => ErrorKind::Interrupted, + abi::errno::EINVAL => ErrorKind::InvalidInput, + abi::errno::ENOENT => ErrorKind::NotFound, + abi::errno::ENOTCONN => ErrorKind::NotConnected, + abi::errno::EPERM => ErrorKind::PermissionDenied, + abi::errno::EPIPE => ErrorKind::BrokenPipe, + abi::errno::ETIMEDOUT => ErrorKind::TimedOut, _ => ErrorKind::Uncategorized, } } -pub fn cvt(result: i32) -> crate::io::Result<usize> { - if result < 0 { Err(crate::io::Error::from_raw_os_error(-result)) } else { Ok(result as usize) } +#[doc(hidden)] +pub trait IsNegative { + fn is_negative(&self) -> bool; + fn negate(&self) -> i32; +} + +macro_rules! impl_is_negative { + ($($t:ident)*) => ($(impl IsNegative for $t { + fn is_negative(&self) -> bool { + *self < 0 + } + + fn negate(&self) -> i32 { + i32::try_from(-(*self)).unwrap() + } + })*) +} + +impl IsNegative for i32 { + fn is_negative(&self) -> bool { + *self < 0 + } + + fn negate(&self) -> i32 { + -(*self) + } +} +impl_is_negative! { i8 i16 i64 isize } + +pub fn cvt<T: IsNegative>(t: T) -> crate::io::Result<T> { + if t.is_negative() { + let e = decode_error_kind(t.negate()); + Err(crate::io::Error::from(e)) + } else { + Ok(t) + } +} + +pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T> +where + T: IsNegative, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + other => return other, + } + } } diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs index 8a13879d8..5fb6281aa 100644 --- a/library/std/src/sys/hermit/net.rs +++ b/library/std/src/sys/hermit/net.rs @@ -1,490 +1,353 @@ -use crate::fmt; -use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::str; -use crate::sync::Arc; -use crate::sys::hermit::abi; -use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6}; -use crate::sys::unsupported; -use crate::sys_common::AsInner; +#![allow(dead_code)] + +use crate::cmp; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::mem; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; +use crate::sys::hermit::fd::FileDesc; +use crate::sys::time::Instant; +use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; -/// Checks whether the HermitCore's socket interface has been started already, and -/// if not, starts it. -pub fn init() -> io::Result<()> { - if abi::network_init() < 0 { - return Err(io::const_io_error!( - ErrorKind::Uncategorized, - "Unable to initialize network interface", - )); - } - - Ok(()) -} - -#[derive(Debug, Clone)] -pub struct Socket(abi::Handle); - -impl AsInner<abi::Handle> for Socket { - fn as_inner(&self) -> &abi::Handle { - &self.0 - } -} +use core::ffi::c_int; -impl Drop for Socket { - fn drop(&mut self) { - let _ = abi::tcpstream::close(self.0); - } -} +#[allow(unused_extern_crates)] +pub extern crate hermit_abi as netc; -// Arc is used to count the number of used sockets. -// Only if all sockets are released, the drop -// method will close the socket. -#[derive(Clone)] -pub struct TcpStream(Arc<Socket>); - -impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> { - let addr = addr?; - - match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) { - Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))), - _ => Err(io::const_io_error!( - ErrorKind::Uncategorized, - "Unable to initiate a connection on a socket", - )), - } - } +pub use crate::sys::{cvt, cvt_r}; - pub fn connect_timeout(saddr: &SocketAddr, duration: Duration) -> io::Result<TcpStream> { - match abi::tcpstream::connect( - saddr.ip().to_string().as_bytes(), - saddr.port(), - Some(duration.as_millis() as u64), - ) { - Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))), - _ => Err(io::const_io_error!( - ErrorKind::Uncategorized, - "Unable to initiate a connection on a socket", - )), - } - } +pub type wrlen_t = usize; - pub fn set_read_timeout(&self, duration: Option<Duration>) -> io::Result<()> { - abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64)) - .map_err(|_| { - io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value") - }) +pub fn cvt_gai(err: i32) -> io::Result<()> { + if err == 0 { + return Ok(()); } - pub fn set_write_timeout(&self, duration: Option<Duration>) -> io::Result<()> { - abi::tcpstream::set_write_timeout( - *self.0.as_inner(), - duration.map(|d| d.as_millis() as u64), - ) - .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value")) - } + let detail = ""; - pub fn read_timeout(&self) -> io::Result<Option<Duration>> { - let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| { - io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value") - })?; + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {detail}")[..], + )) +} - Ok(duration.map(|d| Duration::from_millis(d))) +/// Checks whether the HermitCore's socket interface has been started already, and +/// if not, starts it. +pub fn init() { + if unsafe { netc::network_init() } < 0 { + panic!("Unable to initialize network interface"); } +} - pub fn write_timeout(&self) -> io::Result<Option<Duration>> { - let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| { - io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value") - })?; +#[derive(Debug)] +pub struct Socket(FileDesc); - Ok(duration.map(|d| Duration::from_millis(d))) +impl Socket { + pub fn new(addr: &SocketAddr, ty: i32) -> io::Result<Socket> { + let fam = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + Socket::new_raw(fam, ty) } - pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { - abi::tcpstream::peek(*self.0.as_inner(), buf) - .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peek failed")) + pub fn new_raw(fam: i32, ty: i32) -> io::Result<Socket> { + let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) } - pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> { - self.read_vectored(&mut [IoSliceMut::new(buffer)]) + pub fn new_pair(_fam: i32, _ty: i32) -> io::Result<(Socket, Socket)> { + unimplemented!() } - pub fn read_vectored(&self, ioslice: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - let mut size: usize = 0; - - for i in ioslice.iter_mut() { - let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]).map_err(|_| { - io::const_io_error!(ErrorKind::Uncategorized, "Unable to read on socket") - })?; + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addr, len) = addr.into_inner(); + cvt(netc::connect(self.as_raw_fd(), addr.as_ptr(), len)) + }; + self.set_nonblocking(false)?; - if ret != 0 { - size += ret; - } + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(netc::errno::EINPROGRESS) => {} + Err(e) => return Err(e), } - Ok(size) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn write(&self, buffer: &[u8]) -> io::Result<usize> { - self.write_vectored(&[IoSlice::new(buffer)]) - } + let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; - pub fn write_vectored(&self, ioslice: &[IoSlice<'_>]) -> io::Result<usize> { - let mut size: usize = 0; - - for i in ioslice.iter() { - size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| { - io::const_io_error!(ErrorKind::Uncategorized, "Unable to write on socket") - })?; + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout", + )); } - Ok(size) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn peer_addr(&self) -> io::Result<SocketAddr> { - let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner()) - .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"))?; + let start = Instant::now(); - let saddr = match ipaddr { - Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port), - Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port), - _ => { - return Err(io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed")); + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")); } - }; - - Ok(saddr) - } - - pub fn socket_addr(&self) -> io::Result<SocketAddr> { - unsupported() - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - abi::tcpstream::shutdown(*self.0.as_inner(), how as i32) - .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to shutdown socket")) - } - - pub fn duplicate(&self) -> io::Result<TcpStream> { - Ok(self.clone()) - } - - pub fn set_linger(&self, _linger: Option<Duration>) -> io::Result<()> { - unsupported() - } - - pub fn linger(&self) -> io::Result<Option<Duration>> { - unsupported() - } - - pub fn set_nodelay(&self, mode: bool) -> io::Result<()> { - abi::tcpstream::set_nodelay(*self.0.as_inner(), mode) - .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "set_nodelay failed")) - } - - pub fn nodelay(&self) -> io::Result<bool> { - abi::tcpstream::nodelay(*self.0.as_inner()) - .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "nodelay failed")) - } - - pub fn set_ttl(&self, tll: u32) -> io::Result<()> { - abi::tcpstream::set_tll(*self.0.as_inner(), tll) - .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to set TTL")) - } - - pub fn ttl(&self) -> io::Result<u32> { - abi::tcpstream::get_tll(*self.0.as_inner()) - .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to get TTL")) - } - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unsupported() - } - - pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> { - abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode).map_err(|_| { - io::const_io_error!(ErrorKind::Uncategorized, "unable to set blocking mode") - }) - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -#[derive(Clone)] -pub struct TcpListener(SocketAddr); - -impl TcpListener { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> { - let addr = addr?; - - Ok(TcpListener(*addr)) - } - - pub fn socket_addr(&self) -> io::Result<SocketAddr> { - Ok(self.0) - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port()) - .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "accept failed"))?; - let saddr = match ipaddr { - Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port), - Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port), - _ => { - return Err(io::const_io_error!(ErrorKind::Uncategorized, "accept failed")); + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; } - }; - - Ok((TcpStream(Arc::new(Socket(handle))), saddr)) - } - pub fn duplicate(&self) -> io::Result<TcpListener> { - Ok(self.clone()) - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result<u32> { - unsupported() - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unsupported() + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { netc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if err.kind() != io::ErrorKind::Interrupted { + return Err(err); + } + } + 0 => {} + _ => { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP rather than read readiness + if pollfd.revents & netc::POLLHUP != 0 { + let e = self.take_error()?.unwrap_or_else(|| { + io::const_io_error!( + io::ErrorKind::Uncategorized, + "no error set after POLLHUP", + ) + }); + return Err(e); + } + + return Ok(()); + } + } + } } - pub fn only_v6(&self) -> io::Result<bool> { - unsupported() + pub fn accept( + &self, + storage: *mut netc::sockaddr, + len: *mut netc::socklen_t, + ) -> io::Result<Socket> { + let fd = cvt(unsafe { netc::accept(self.0.as_raw_fd(), storage, len) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) } - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unsupported() + pub fn duplicate(&self) -> io::Result<Socket> { + let fd = cvt(unsafe { netc::dup(self.0.as_raw_fd()) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) } - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() + fn recv_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<usize> { + let ret = + cvt(unsafe { netc::recv(self.0.as_raw_fd(), buf.as_mut_ptr(), buf.len(), flags) })?; + Ok(ret as usize) } -} -impl fmt::Debug for TcpListener { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, 0) } -} - -pub struct UdpSocket(abi::Handle); -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { - unsupported() + pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, netc::MSG_PEEK) } - pub fn peer_addr(&self) -> io::Result<SocketAddr> { - unsupported() - } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + let mut size: isize = 0; - pub fn socket_addr(&self) -> io::Result<SocketAddr> { - unsupported() - } + for i in bufs.iter_mut() { + let ret: isize = + cvt(unsafe { netc::read(self.0.as_raw_fd(), i.as_mut_ptr(), i.len()) })?; - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unsupported() - } + if ret != 0 { + size += ret; + } + } - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unsupported() + Ok(size.try_into().unwrap()) } - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> { - unsupported() + #[inline] + pub fn is_read_vectored(&self) -> bool { + true } - pub fn duplicate(&self) -> io::Result<UdpSocket> { - unsupported() - } + fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { + let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; - pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unsupported() + let n = cvt(unsafe { + netc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr(), + buf.len(), + flags, + &mut storage as *mut _ as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) } - pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unsupported() + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) } - pub fn read_timeout(&self) -> io::Result<Option<Duration>> { - unsupported() + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, netc::MSG_PEEK) } - pub fn write_timeout(&self) -> io::Result<Option<Duration>> { - unsupported() + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + let sz = cvt(unsafe { netc::write(self.0.as_raw_fd(), buf.as_ptr(), buf.len()) })?; + Ok(sz.try_into().unwrap()) } - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unsupported() - } + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let mut size: isize = 0; - pub fn broadcast(&self) -> io::Result<bool> { - unsupported() - } + for i in bufs.iter() { + size += cvt(unsafe { netc::write(self.0.as_raw_fd(), i.as_ptr(), i.len()) })?; + } - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unsupported() + Ok(size.try_into().unwrap()) } - pub fn multicast_loop_v4(&self) -> io::Result<bool> { - unsupported() + pub fn is_write_vectored(&self) -> bool { + true } - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unsupported() - } + pub fn set_timeout(&self, dur: Option<Duration>, kind: i32) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout", + )); + } + + let secs = if dur.as_secs() > netc::time_t::MAX as u64 { + netc::time_t::MAX + } else { + dur.as_secs() as netc::time_t + }; + let mut timeout = netc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as netc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => netc::timeval { tv_sec: 0, tv_usec: 0 }, + }; - pub fn multicast_ttl_v4(&self) -> io::Result<u32> { - unsupported() + setsockopt(self, netc::SOL_SOCKET, kind, timeout) } - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unsupported() + pub fn timeout(&self, kind: i32) -> io::Result<Option<Duration>> { + let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } } - pub fn multicast_loop_v6(&self) -> io::Result<bool> { - unsupported() + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => netc::SHUT_WR, + Shutdown::Read => netc::SHUT_RD, + Shutdown::Both => netc::SHUT_RDWR, + }; + cvt(unsafe { netc::shutdown_socket(self.as_raw_fd(), how) })?; + Ok(()) } - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unsupported() - } + pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> { + let linger = netc::linger { + l_onoff: linger.is_some() as i32, + l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, + }; - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unsupported() + setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) } - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unsupported() - } + pub fn linger(&self) -> io::Result<Option<Duration>> { + let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unsupported() + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + let value: i32 = if nodelay { 1 } else { 0 }; + setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) } - pub fn ttl(&self) -> io::Result<u32> { - unsupported() + pub fn nodelay(&self) -> io::Result<bool> { + let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking: i32 = if nonblocking { 1 } else { 0 }; + cvt(unsafe { + netc::ioctl( + self.as_raw_fd(), + netc::FIONBIO, + &mut nonblocking as *mut _ as *mut core::ffi::c_void, + ) + }) + .map(drop) } pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unsupported() - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> { - unsupported() - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { - unsupported() + unimplemented!() } - pub fn send(&self, _: &[u8]) -> io::Result<usize> { - unsupported() - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unsupported() + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.0.as_raw_fd() } } -impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) +impl AsInner<FileDesc> for Socket { + fn as_inner(&self) -> &FileDesc { + &self.0 } } -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { +impl IntoInner<FileDesc> for Socket { + fn into_inner(self) -> FileDesc { self.0 } } -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option<SocketAddr> { - self.0 +impl FromInner<FileDesc> for Socket { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result<LookupHost> { - unsupported() +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() } } -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { - unsupported() +impl AsRawFd for Socket { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() } } - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr {} -} diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs index 8f927df85..e53dbae61 100644 --- a/library/std/src/sys/hermit/os.rs +++ b/library/std/src/sys/hermit/os.rs @@ -4,7 +4,7 @@ use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io; use crate::marker::PhantomData; -use crate::os::unix::ffi::OsStringExt; +use crate::os::hermit::ffi::OsStringExt; use crate::path::{self, PathBuf}; use crate::str; use crate::sync::Mutex; diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs index 8f65544a9..2507f7069 100644 --- a/library/std/src/sys/hermit/thread.rs +++ b/library/std/src/sys/hermit/thread.rs @@ -27,10 +27,10 @@ impl Thread { p: Box<dyn FnOnce()>, core_id: isize, ) -> io::Result<Thread> { - let p = Box::into_raw(box p); + let p = Box::into_raw(Box::new(p)); let tid = abi::spawn2( thread_start, - p as usize, + p.expose_addr(), abi::Priority::into(abi::NORMAL_PRIO), stack, core_id, diff --git a/library/std/src/sys/hermit/thread_local_dtor.rs b/library/std/src/sys/hermit/thread_local_dtor.rs index 9b683fce1..613266b95 100644 --- a/library/std/src/sys/hermit/thread_local_dtor.rs +++ b/library/std/src/sys/hermit/thread_local_dtor.rs @@ -5,32 +5,23 @@ // The this solution works like the implementation of macOS and // doesn't additional OS support -use crate::cell::Cell; -use crate::ptr; +use crate::mem; #[thread_local] -static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); - -type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; +static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - if DTORS.get().is_null() { - let v: Box<List> = box Vec::new(); - DTORS.set(Box::into_raw(v)); - } - - let list: &mut List = &mut *DTORS.get(); + let list = &mut DTORS; list.push((t, dtor)); } // every thread call this function to run through all possible destructors pub unsafe fn run_dtors() { - let mut ptr = DTORS.replace(ptr::null_mut()); - while !ptr.is_null() { - let list = Box::from_raw(ptr); - for (ptr, dtor) in list.into_iter() { + let mut list = mem::take(&mut DTORS); + while !list.is_empty() { + for (ptr, dtor) in list { dtor(ptr); } - ptr = DTORS.replace(ptr::null_mut()); + list = mem::take(&mut DTORS); } } diff --git a/library/std/src/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs index c17e6c8af..32ddc4346 100644 --- a/library/std/src/sys/hermit/time.rs +++ b/library/std/src/sys/hermit/time.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] use crate::cmp::Ordering; +use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::sys::hermit::abi; use crate::sys::hermit::abi::timespec; use crate::sys::hermit::abi::{CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC}; @@ -102,55 +103,122 @@ impl Hash for Timespec { } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct Instant { - t: Timespec, -} +pub struct Instant(Timespec); impl Instant { pub fn now() -> Instant { let mut time: Timespec = Timespec::zero(); let _ = unsafe { abi::clock_gettime(CLOCK_MONOTONIC, &mut time.t as *mut timespec) }; - Instant { t: time } + Instant(time) + } + + #[stable(feature = "time2", since = "1.8.0")] + pub fn elapsed(&self) -> Duration { + Instant::now() - *self + } + + pub fn duration_since(&self, earlier: Instant) -> Duration { + self.checked_duration_since(earlier).unwrap_or_default() + } + + pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> { + self.checked_sub_instant(&earlier) } pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { - self.t.sub_timespec(&other.t).ok() + self.0.sub_timespec(&other.0).ok() } pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { - Some(Instant { t: self.t.checked_add_duration(other)? }) + Some(Instant(self.0.checked_add_duration(other)?)) } pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { - Some(Instant { t: self.t.checked_sub_duration(other)? }) + Some(Instant(self.0.checked_sub_duration(other)?)) + } + + pub fn checked_add(&self, duration: Duration) -> Option<Instant> { + self.0.checked_add_duration(&duration).map(Instant) + } + + pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { + self.0.checked_sub_duration(&duration).map(Instant) } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct SystemTime { - t: Timespec, +impl Add<Duration> for Instant { + type Output = Instant; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`Instant::checked_add`] for a version without panic. + fn add(self, other: Duration) -> Instant { + self.checked_add(other).expect("overflow when adding duration to instant") + } } -pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; +impl AddAssign<Duration> for Instant { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + +impl Sub<Duration> for Instant { + type Output = Instant; + + fn sub(self, other: Duration) -> Instant { + self.checked_sub(other).expect("overflow when subtracting duration from instant") + } +} + +impl SubAssign<Duration> for Instant { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + +impl Sub<Instant> for Instant { + type Output = Duration; + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + /// + /// # Panics + /// + /// Previous rust versions panicked when `other` was later than `self`. Currently this + /// method saturates. Future versions may reintroduce the panic in some circumstances. + /// See [Monotonicity]. + /// + /// [Monotonicity]: Instant#monotonicity + fn sub(self, other: Instant) -> Duration { + self.duration_since(other) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct SystemTime(Timespec); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, &mut time.t as *mut timespec) }; - SystemTime { t: time } + SystemTime(time) } pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { - self.t.sub_timespec(&other.t) + self.0.sub_timespec(&other.0) } pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) + Some(SystemTime(self.0.checked_add_duration(other)?)) } pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + Some(SystemTime(self.0.checked_sub_duration(other)?)) } } diff --git a/library/std/src/sys/itron/thread.rs b/library/std/src/sys/itron/thread.rs index 19350b83f..ae0f71853 100644 --- a/library/std/src/sys/itron/thread.rs +++ b/library/std/src/sys/itron/thread.rs @@ -247,7 +247,7 @@ impl Thread { // [FINISHED → JOINED] // To synchronize with the child task's memory accesses to // `inner` up to the point of the assignment of `FINISHED`, - // `Ordering::Acquire` must be used for the above `swap` call`. + // `Ordering::Acquire` must be used for the above `swap` call. } _ => unsafe { hint::unreachable_unchecked() }, } diff --git a/library/std/src/sys/itron/thread_parking.rs b/library/std/src/sys/itron/thread_parking.rs new file mode 100644 index 000000000..fe9934439 --- /dev/null +++ b/library/std/src/sys/itron/thread_parking.rs @@ -0,0 +1,37 @@ +use super::abi; +use super::error::expect_success_aborting; +use super::time::with_tmos; +use crate::time::Duration; + +pub type ThreadId = abi::ID; + +pub use super::task::current_task_id_aborting as current; + +pub fn park(_hint: usize) { + match unsafe { abi::slp_tsk() } { + abi::E_OK | abi::E_RLWAI => {} + err => { + expect_success_aborting(err, &"slp_tsk"); + } + } +} + +pub fn park_timeout(dur: Duration, _hint: usize) { + match with_tmos(dur, |tmo| unsafe { abi::tslp_tsk(tmo) }) { + abi::E_OK | abi::E_RLWAI | abi::E_TMOUT => {} + err => { + expect_success_aborting(err, &"tslp_tsk"); + } + } +} + +pub fn unpark(id: ThreadId, _hint: usize) { + match unsafe { abi::wup_tsk(id) } { + // It is allowed to try to wake up a destroyed or unrelated task, so we ignore all + // errors that could result from that situation. + abi::E_OK | abi::E_NOEXS | abi::E_OBJ | abi::E_QOVR => {} + err => { + expect_success_aborting(err, &"wup_tsk"); + } + } +} diff --git a/library/std/src/sys/itron/wait_flag.rs b/library/std/src/sys/itron/wait_flag.rs deleted file mode 100644 index e432edd20..000000000 --- a/library/std/src/sys/itron/wait_flag.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::mem::MaybeUninit; -use crate::time::Duration; - -use super::{ - abi, - error::{expect_success, fail}, - time::with_tmos, -}; - -const CLEAR: abi::FLGPTN = 0; -const RAISED: abi::FLGPTN = 1; - -/// A thread parking primitive that is not susceptible to race conditions, -/// but provides no atomic ordering guarantees and allows only one `raise` per wait. -pub struct WaitFlag { - flag: abi::ID, -} - -impl WaitFlag { - /// Creates a new wait flag. - pub fn new() -> WaitFlag { - let flag = expect_success( - unsafe { - abi::acre_flg(&abi::T_CFLG { - flgatr: abi::TA_FIFO | abi::TA_WSGL | abi::TA_CLR, - iflgptn: CLEAR, - }) - }, - &"acre_flg", - ); - - WaitFlag { flag } - } - - /// Wait for the wait flag to be raised. - pub fn wait(&self) { - let mut token = MaybeUninit::uninit(); - expect_success( - unsafe { abi::wai_flg(self.flag, RAISED, abi::TWF_ORW, token.as_mut_ptr()) }, - &"wai_flg", - ); - } - - /// Wait for the wait flag to be raised or the timeout to occur. - /// - /// Returns whether the flag was raised (`true`) or the operation timed out (`false`). - pub fn wait_timeout(&self, dur: Duration) -> bool { - let mut token = MaybeUninit::uninit(); - let res = with_tmos(dur, |tmout| unsafe { - abi::twai_flg(self.flag, RAISED, abi::TWF_ORW, token.as_mut_ptr(), tmout) - }); - - match res { - abi::E_OK => true, - abi::E_TMOUT => false, - error => fail(error, &"twai_flg"), - } - } - - /// Raise the wait flag. - /// - /// Calls to this function should be balanced with the number of successful waits. - pub fn raise(&self) { - expect_success(unsafe { abi::set_flg(self.flag, RAISED) }, &"set_flg"); - } -} - -impl Drop for WaitFlag { - fn drop(&mut self) { - expect_success(unsafe { abi::del_flg(self.flag) }, &"del_flg"); - } -} diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs index 5867979a2..923d27fd9 100644 --- a/library/std/src/sys/solid/mod.rs +++ b/library/std/src/sys/solid/mod.rs @@ -13,9 +13,9 @@ mod itron { pub(super) mod spin; pub(super) mod task; pub mod thread; + pub mod thread_parking; pub(super) mod time; use super::unsupported; - pub mod wait_flag; } pub mod alloc; @@ -43,8 +43,8 @@ pub use self::itron::thread; pub mod memchr; pub mod thread_local_dtor; pub mod thread_local_key; +pub use self::itron::thread_parking; pub mod time; -pub use self::itron::wait_flag; mod rwlock; diff --git a/library/std/src/sys/solid/thread_local_dtor.rs b/library/std/src/sys/solid/thread_local_dtor.rs index 973564570..bad14bb37 100644 --- a/library/std/src/sys/solid/thread_local_dtor.rs +++ b/library/std/src/sys/solid/thread_local_dtor.rs @@ -5,43 +5,35 @@ use super::{abi, itron::task}; use crate::cell::Cell; -use crate::ptr; +use crate::mem; #[thread_local] -static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); +static REGISTERED: Cell<bool> = Cell::new(false); -type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; +#[thread_local] +static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - if DTORS.get().is_null() { + if !REGISTERED.get() { let tid = task::current_task_id_aborting(); - let v: Box<List> = box Vec::new(); - DTORS.set(Box::into_raw(v)); - // Register `tls_dtor` to make sure the TLS destructors are called // for tasks created by other means than `std::thread` unsafe { abi::SOLID_TLS_AddDestructor(tid as i32, tls_dtor) }; + REGISTERED.set(true); } - let list: &mut List = unsafe { &mut *DTORS.get() }; + let list = unsafe { &mut DTORS }; list.push((t, dtor)); } pub unsafe fn run_dtors() { - let ptr = DTORS.get(); - if !ptr.is_null() { - // Swap the destructor list, call all registered destructors, - // and repeat this until the list becomes permanently empty. - while let Some(list) = Some(crate::mem::replace(unsafe { &mut *ptr }, Vec::new())) - .filter(|list| !list.is_empty()) - { - for (ptr, dtor) in list.into_iter() { - unsafe { dtor(ptr) }; - } + let mut list = mem::take(unsafe { &mut DTORS }); + while !list.is_empty() { + for (ptr, dtor) in list { + unsafe { dtor(ptr) }; } - // Drop the destructor list - unsafe { Box::from_raw(DTORS.replace(ptr::null_mut())) }; + list = mem::take(unsafe { &mut DTORS }); } } diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index a342f0f5e..3d79058b3 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -69,7 +69,8 @@ impl DoubleEndedIterator for Args { target_os = "fuchsia", target_os = "redox", target_os = "vxworks", - target_os = "horizon" + target_os = "horizon", + target_os = "nto", ))] mod imp { use super::Args; @@ -141,12 +142,28 @@ mod imp { // list. let argv = ARGV.load(Ordering::Relaxed); let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; - (0..argc) - .map(|i| { - let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char); - OsStringExt::from_vec(cstr.to_bytes().to_vec()) - }) - .collect() + let mut args = Vec::with_capacity(argc as usize); + for i in 0..argc { + let ptr = *argv.offset(i) as *const libc::c_char; + + // Some C commandline parsers (e.g. GLib and Qt) are replacing already + // handled arguments in `argv` with `NULL` and move them to the end. That + // means that `argc` might be bigger than the actual number of non-`NULL` + // pointers in `argv` at this point. + // + // To handle this we simply stop iterating at the first `NULL` argument. + // + // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments + // after the first `NULL` can safely be ignored. + if ptr.is_null() { + break; + } + + let cstr = CStr::from_ptr(ptr); + args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); + } + + args } } } diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index c9ba661c8..1a9276f11 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -185,6 +185,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "nto")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nto"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "redox")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index dbaa3c33e..9874af4d3 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -53,7 +53,12 @@ const fn max_iov() -> usize { libc::IOV_MAX as usize } -#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] +#[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "linux", + target_os = "nto", +))] const fn max_iov() -> usize { libc::UIO_MAXIOV as usize } @@ -67,6 +72,7 @@ const fn max_iov() -> usize { target_os = "linux", target_os = "macos", target_os = "netbsd", + target_os = "nto", target_os = "openbsd", target_os = "horizon", target_os = "watchos", @@ -92,7 +98,7 @@ impl FileDesc { let ret = cvt(unsafe { libc::readv( self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, cmp::min(bufs.len(), max_iov()) as libc::c_int, ) })?; @@ -101,7 +107,7 @@ impl FileDesc { #[cfg(any(target_os = "espidf", target_os = "horizon"))] pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - return crate::io::default_read_vectored(|b| self.read(b), bufs); + io::default_read_vectored(|b| self.read(b), bufs) } #[inline] @@ -147,6 +153,95 @@ impl FileDesc { Ok(()) } + #[cfg(any( + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + ))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + )))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + io::default_read_vectored(|b| self.read_at(b, offset), bufs) + } + + // We support some old Android versions that do not have `preadv` in libc, + // so we use weak linkage and fallback to a direct syscall if not available. + // + // On 32-bit targets, we don't want to deal with weird ABI issues around + // passing 64-bits parameters to syscalls, so we fallback to the default + // implementation if `preadv` is not available. + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + super::weak::syscall! { + fn preadv( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t + ) -> isize + } + + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + // We support old MacOS and iOS versions that do not have `preadv`. There is + // no `syscall` possible in these platform. + #[cfg(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "ios", + target_os = "macos", + ))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match preadv64.get() { + Some(preadv) => { + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), + } + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let ret = cvt(unsafe { libc::write( @@ -172,7 +267,7 @@ impl FileDesc { #[cfg(any(target_os = "espidf", target_os = "horizon"))] pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { - return crate::io::default_write_vectored(|b| self.write(b), bufs); + io::default_write_vectored(|b| self.write(b), bufs) } #[inline] @@ -197,9 +292,93 @@ impl FileDesc { } } - #[cfg(target_os = "linux")] - pub fn get_cloexec(&self) -> io::Result<bool> { - unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } + #[cfg(any( + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + ))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + )))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + io::default_write_vectored(|b| self.write_at(b, offset), bufs) + } + + // We support some old Android versions that do not have `pwritev` in libc, + // so we use weak linkage and fallback to a direct syscall if not available. + // + // On 32-bit targets, we don't want to deal with weird ABI issues around + // passing 64-bits parameters to syscalls, so we fallback to the default + // implementation if `pwritev` is not available. + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + super::weak::syscall! { + fn pwritev( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t + ) -> isize + } + + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + // We support old MacOS and iOS versions that do not have `pwritev`. There is + // no `syscall` possible in these platform. + #[cfg(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "ios", + target_os = "macos", + ))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match pwritev64.get() { + Some(pwritev) => { + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), + } } #[cfg(not(any( @@ -212,7 +391,8 @@ impl FileDesc { target_os = "linux", target_os = "haiku", target_os = "redox", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nto", )))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { @@ -230,7 +410,8 @@ impl FileDesc { target_os = "linux", target_os = "haiku", target_os = "redox", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nto", ))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { @@ -284,6 +465,10 @@ impl<'a> Read for &'a FileDesc { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) } + + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf(cursor) + } } impl AsInner<OwnedFd> for FileDesc { diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 8e1f35d6c..7566fafda 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -13,7 +13,8 @@ use crate::mem; target_os = "solaris", target_os = "fuchsia", target_os = "redox", - target_os = "illumos" + target_os = "illumos", + target_os = "nto", ))] use crate::mem::MaybeUninit; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; @@ -54,7 +55,8 @@ use libc::fstatat64; target_os = "solaris", target_os = "fuchsia", target_os = "redox", - target_os = "illumos" + target_os = "illumos", + target_os = "nto", ))] use libc::readdir as readdir64; #[cfg(target_os = "linux")] @@ -69,7 +71,8 @@ use libc::readdir64_r; target_os = "illumos", target_os = "l4re", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", )))] use libc::readdir_r as readdir64_r; #[cfg(target_os = "android")] @@ -277,7 +280,8 @@ unsafe impl Sync for Dir {} target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", ))] pub struct DirEntry { dir: Arc<InnerReadDir>, @@ -297,11 +301,12 @@ pub struct DirEntry { target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", ))] struct dirent64_min { d_ino: u64, - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "nto")))] d_type: u8, } @@ -311,7 +316,8 @@ struct dirent64_min { target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", )))] pub struct DirEntry { dir: Arc<InnerReadDir>, @@ -438,7 +444,7 @@ impl FileAttr { } } -#[cfg(not(target_os = "netbsd"))] +#[cfg(not(any(target_os = "netbsd", target_os = "nto")))] impl FileAttr { #[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))] pub fn modified(&self) -> io::Result<SystemTime> { @@ -524,6 +530,21 @@ impl FileAttr { } } +#[cfg(target_os = "nto")] +impl FileAttr { + pub fn modified(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec)) + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec)) + } + + pub fn created(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec)) + } +} + impl AsInner<stat64> for FileAttr { fn as_inner(&self) -> &stat64 { &self.stat @@ -603,7 +624,8 @@ impl Iterator for ReadDir { target_os = "solaris", target_os = "fuchsia", target_os = "redox", - target_os = "illumos" + target_os = "illumos", + target_os = "nto", ))] fn next(&mut self) -> Option<io::Result<DirEntry>> { if self.end_of_stream { @@ -686,7 +708,11 @@ impl Iterator for ReadDir { let entry = dirent64_min { d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "nto", + )))] d_type: *offset_ptr!(entry_ptr, d_type) as u8, }; @@ -705,7 +731,8 @@ impl Iterator for ReadDir { target_os = "solaris", target_os = "fuchsia", target_os = "redox", - target_os = "illumos" + target_os = "illumos", + target_os = "nto", )))] fn next(&mut self) -> Option<io::Result<DirEntry>> { if self.end_of_stream { @@ -794,7 +821,8 @@ impl DirEntry { target_os = "solaris", target_os = "illumos", target_os = "haiku", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nto", ))] pub fn file_type(&self) -> io::Result<FileType> { self.metadata().map(|m| m.file_type()) @@ -804,7 +832,8 @@ impl DirEntry { target_os = "solaris", target_os = "illumos", target_os = "haiku", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nto", )))] pub fn file_type(&self) -> io::Result<FileType> { match self.entry.d_type { @@ -834,7 +863,8 @@ impl DirEntry { target_os = "redox", target_os = "vxworks", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_os = "nto", ))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 @@ -887,7 +917,8 @@ impl DirEntry { target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", )))] fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } @@ -898,7 +929,8 @@ impl DirEntry { target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", ))] fn name_cstr(&self) -> &CStr { &self.name @@ -1051,7 +1083,8 @@ impl File { target_os = "linux", target_os = "android", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "nto", ))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) @@ -1065,6 +1098,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "watchos", + target_os = "nto", )))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) @@ -1098,6 +1132,10 @@ impl File { self.0.read_buf(cursor) } + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + self.0.read_vectored_at(bufs, offset) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } @@ -1115,6 +1153,10 @@ impl File { self.0.write_at(buf, offset) } + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + self.0.write_vectored_at(bufs, offset) + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } @@ -1750,13 +1792,25 @@ pub fn chroot(dir: &Path) -> io::Result<()> { pub use remove_dir_impl::remove_dir_all; // Fallback for REDOX, ESP-ID, Horizon, and Miri -#[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri))] +#[cfg(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "nto", + miri +))] mod remove_dir_impl { pub use crate::sys_common::fs::remove_dir_all; } // Modern implementation using openat(), unlinkat() and fdopendir() -#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri)))] +#[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "nto", + miri +)))] mod remove_dir_impl { use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir}; use crate::ffi::CStr; diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs index 6be1abc2b..192fa216d 100644 --- a/library/std/src/sys/unix/locks/pthread_condvar.rs +++ b/library/std/src/sys/unix/locks/pthread_condvar.rs @@ -2,7 +2,10 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; use crate::sys::locks::{pthread_mutex, Mutex}; +#[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; +#[cfg(target_os = "nto")] +use crate::sys::time::TIMESPEC_MAX_CAPPED; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; use crate::time::Duration; @@ -132,10 +135,18 @@ impl Condvar { let mutex = pthread_mutex::raw(mutex); self.verify(mutex); + #[cfg(not(target_os = "nto"))] let timeout = Timespec::now(libc::CLOCK_MONOTONIC) .checked_add_duration(&dur) .and_then(|t| t.to_timespec()) .unwrap_or(TIMESPEC_MAX); + + #[cfg(target_os = "nto")] + let timeout = Timespec::now(libc::CLOCK_MONOTONIC) + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec_capped()) + .unwrap_or(TIMESPEC_MAX_CAPPED); + let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout); assert!(r == libc::ETIMEDOUT || r == 0); r == 0 diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 30a96be14..68c9520cc 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -329,7 +329,7 @@ pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> { // do so. In 1003.1-2004 this was fixed. // // glibc's implementation did the flush, unsafely, before glibc commit -// 91e7cf982d01 `abort: Do not flush stdio streams [BZ #15436]' by Florian +// 91e7cf982d01 `abort: Do not flush stdio streams [BZ #15436]` by Florian // Weimer. According to glibc's NEWS: // // The abort function terminates the process immediately, without flushing diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index c86f80972..8e05b618d 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -78,6 +78,7 @@ impl Socket { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "nto", ))] { // On platforms that support it we pass the SOCK_CLOEXEC // flag to atomically create the socket and set it as @@ -115,6 +116,7 @@ impl Socket { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "nto", ))] { // Like above, set cloexec atomically cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 2f2663db6..21b035fb3 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -62,6 +62,7 @@ extern "C" { link_name = "__errno" )] #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] + #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")] #[cfg_attr( any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "watchos"), link_name = "__error" @@ -361,6 +362,17 @@ pub fn current_exe() -> io::Result<PathBuf> { } } +#[cfg(target_os = "nto")] +pub fn current_exe() -> io::Result<PathBuf> { + let mut e = crate::fs::read("/proc/self/exefile")?; + // Current versions of QNX Neutrino provide a null-terminated path. + // Ensure the trailing null byte is not returned here. + if let Some(0) = e.last() { + e.pop(); + } + Ok(PathBuf::from(OsString::from_vec(e))) +} + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] pub fn current_exe() -> io::Result<PathBuf> { unsafe { diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 3bc17b775..ceaff5966 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -18,6 +18,7 @@ use crate::sys::weak::raw_syscall; target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), + target_os = "nto", ))] use crate::sys::weak::weak; @@ -30,6 +31,15 @@ use libc::{c_int, pid_t}; #[cfg(not(any(target_os = "vxworks", target_os = "l4re")))] use libc::{gid_t, uid_t}; +cfg_if::cfg_if! { + if #[cfg(all(target_os = "nto", target_env = "nto71"))] { + use crate::thread; + use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; + // arbitrary number of tries: + const MAX_FORKSPAWN_TRIES: u32 = 4; + } +} + //////////////////////////////////////////////////////////////////////////////// // Command //////////////////////////////////////////////////////////////////////////////// @@ -140,11 +150,31 @@ impl Command { // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", all(target_os = "nto", target_env = "nto71"))))] unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { cvt(libc::fork()).map(|res| (res, -1)) } + // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened + // or closed a file descriptor while the fork() was occurring". + // Documentation says "... or try calling fork() again". This is what we do here. + // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html + #[cfg(all(target_os = "nto", target_env = "nto71"))] + unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { + use crate::sys::os::errno; + + let mut tries_left = MAX_FORKSPAWN_TRIES; + loop { + let r = libc::fork(); + if r == -1 as libc::pid_t && tries_left > 0 && errno() as libc::c_int == libc::EBADF { + thread::yield_now(); + tries_left -= 1; + } else { + return cvt(r).map(|res| (res, -1)); + } + } + } + // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, child_pidfd)) in the parent. #[cfg(target_os = "linux")] @@ -389,6 +419,7 @@ impl Command { target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), + target_os = "nto", )))] fn posix_spawn( &mut self, @@ -405,6 +436,7 @@ impl Command { target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), + target_os = "nto", ))] fn posix_spawn( &mut self, @@ -436,6 +468,34 @@ impl Command { } } + // On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened + // or closed a file descriptor while the posix_spawn() was occurring". + // Documentation says "... or try calling posix_spawn() again". This is what we do here. + // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html + #[cfg(all(target_os = "nto", target_env = "nto71"))] + unsafe fn retrying_libc_posix_spawnp( + pid: *mut pid_t, + file: *const c_char, + file_actions: *const posix_spawn_file_actions_t, + attrp: *const posix_spawnattr_t, + argv: *const *mut c_char, + envp: *const *mut c_char, + ) -> i32 { + let mut tries_left = MAX_FORKSPAWN_TRIES; + loop { + match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { + libc::EBADF if tries_left > 0 => { + thread::yield_now(); + tries_left -= 1; + continue; + } + r => { + return r; + } + } + } + } + // 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 @@ -555,7 +615,12 @@ impl Command { // Make sure we synchronize access to the global `environ` resource let _env_lock = sys::os::env_read_lock(); let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); - cvt_nz(libc::posix_spawnp( + + #[cfg(not(target_os = "nto"))] + let spawn_fn = libc::posix_spawnp; + #[cfg(target_os = "nto")] + let spawn_fn = retrying_libc_posix_spawnp; + cvt_nz(spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(), file_actions.0.as_ptr(), @@ -760,7 +825,7 @@ fn signal_string(signal: i32) -> &'static str { ) ))] libc::SIGSTKFLT => " (SIGSTKFLT)", - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "nto"))] libc::SIGPWR => " (SIGPWR)", #[cfg(any( target_os = "macos", @@ -769,7 +834,8 @@ fn signal_string(signal: i32) -> &'static str { target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", - target_os = "dragonfly" + target_os = "dragonfly", + target_os = "nto", ))] libc::SIGEMT => " (SIGEMT)", #[cfg(any( diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 2a1830d06..15070b1f6 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -9,7 +9,7 @@ use crate::time::Duration; #[cfg(all(target_os = "linux", target_env = "gnu"))] use crate::sys::weak::dlsym; -#[cfg(any(target_os = "solaris", target_os = "illumos"))] +#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] use crate::sys::weak::weak; #[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))] pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -49,7 +49,7 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { - let p = Box::into_raw(box p); + let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); assert_eq!(libc::pthread_attr_init(&mut attr), 0); @@ -173,7 +173,7 @@ impl Thread { } } - #[cfg(any(target_os = "solaris", target_os = "illumos"))] + #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] pub fn set_name(name: &CStr) { weak! { fn pthread_setname_np( @@ -381,6 +381,17 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> { } Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }) + } else if #[cfg(target_os = "nto")] { + unsafe { + use libc::_syspage_ptr; + if _syspage_ptr.is_null() { + Err(io::const_io_error!(io::ErrorKind::NotFound, "No syspage available")) + } else { + let cpus = (*_syspage_ptr).num_cpu; + NonZeroUsize::new(cpus as usize) + .ok_or(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")) + } + } } else if #[cfg(target_os = "haiku")] { // system_info cpu_count field gets the static data set at boot time with `smp_set_num_cpus` // `get_system_info` calls then `smp_get_num_cpus` diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs index d7fd2130f..236d2f2ee 100644 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -11,13 +11,7 @@ // Note, however, that we run on lots older linuxes, as well as cross // compiling from a newer linux to an older linux, so we also have a // fallback implementation to use as well. -#[cfg(any( - target_os = "linux", - target_os = "fuchsia", - target_os = "redox", - target_os = "emscripten" -))] -#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten) +#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox"))] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::mem; use crate::sys_common::thread_local_dtor::register_dtor_fallback; @@ -57,44 +51,40 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { #[cfg(target_os = "macos")] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::cell::Cell; + use crate::mem; use crate::ptr; #[thread_local] static REGISTERED: Cell<bool> = Cell::new(false); + + #[thread_local] + static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); + if !REGISTERED.get() { _tlv_atexit(run_dtors, ptr::null_mut()); REGISTERED.set(true); } - type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; - - #[thread_local] - static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); - if DTORS.get().is_null() { - let v: Box<List> = box Vec::new(); - DTORS.set(Box::into_raw(v)); - } - extern "C" { fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); } - let list: &mut List = &mut *DTORS.get(); + let list = &mut DTORS; list.push((t, dtor)); unsafe extern "C" fn run_dtors(_: *mut u8) { - let mut ptr = DTORS.replace(ptr::null_mut()); - while !ptr.is_null() { - let list = Box::from_raw(ptr); - for (ptr, dtor) in list.into_iter() { + let mut list = mem::take(&mut DTORS); + while !list.is_empty() { + for (ptr, dtor) in list { dtor(ptr); } - ptr = DTORS.replace(ptr::null_mut()); + list = mem::take(&mut DTORS); } } } -#[cfg(any(target_os = "vxworks", target_os = "horizon"))] +#[cfg(any(target_os = "vxworks", target_os = "horizon", target_os = "emscripten"))] +#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten) pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::sys_common::thread_local_dtor::register_dtor_fallback; register_dtor_fallback(t, dtor); diff --git a/library/std/src/sys/unix/thread_parking/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs index 082d25e68..43046ed07 100644 --- a/library/std/src/sys/unix/thread_parking/pthread.rs +++ b/library/std/src/sys/unix/thread_parking/pthread.rs @@ -6,7 +6,10 @@ use crate::pin::Pin; use crate::ptr::addr_of_mut; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::SeqCst; +#[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; +#[cfg(target_os = "nto")] +use crate::sys::time::TIMESPEC_MAX_CAPPED; use crate::time::Duration; const EMPTY: usize = 0; @@ -80,8 +83,14 @@ unsafe fn wait_timeout( (Timespec::now(libc::CLOCK_MONOTONIC), dur) }; + #[cfg(not(target_os = "nto"))] let timeout = now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); + #[cfg(target_os = "nto")] + let timeout = now + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec_capped()) + .unwrap_or(TIMESPEC_MAX_CAPPED); let r = libc::pthread_cond_timedwait(cond, lock, &timeout); debug_assert!(r == libc::ETIMEDOUT || r == 0); } diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 2daad981b..0f11de8f5 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -9,6 +9,14 @@ pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; pub const TIMESPEC_MAX: libc::timespec = libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 }; +// This additional constant is only used when calling +// `libc::pthread_cond_timedwait`. +#[cfg(target_os = "nto")] +pub(super) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { + tv_sec: (u64::MAX / NSEC_PER_SEC) as i64, + tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, +}; + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(0)] @@ -144,6 +152,20 @@ impl Timespec { tv_nsec: self.tv_nsec.0.try_into().ok()?, }) } + + // On QNX Neutrino, the maximum timespec for e.g. pthread_cond_timedwait + // is 2^64 nanoseconds + #[cfg(target_os = "nto")] + pub(super) fn to_timespec_capped(&self) -> Option<libc::timespec> { + // Check if timeout in nanoseconds would fit into an u64 + if (self.tv_nsec.0 as u64) + .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) + .is_none() + { + return None; + } + self.to_timespec() + } } impl From<libc::timespec> for Timespec { diff --git a/library/std/src/sys/unsupported/once.rs b/library/std/src/sys/unsupported/once.rs index b4bb4975f..11fde1888 100644 --- a/library/std/src/sys/unsupported/once.rs +++ b/library/std/src/sys/unsupported/once.rs @@ -1,5 +1,6 @@ use crate::cell::Cell; use crate::sync as public; +use crate::sync::once::ExclusiveState; pub struct Once { state: Cell<State>, @@ -44,6 +45,16 @@ impl Once { self.state.get() == State::Complete } + #[inline] + pub(crate) fn state(&mut self) -> ExclusiveState { + match self.state.get() { + State::Incomplete => ExclusiveState::Incomplete, + State::Poisoned => ExclusiveState::Poisoned, + State::Complete => ExclusiveState::Complete, + _ => unreachable!("invalid Once state"), + } + } + #[cold] #[track_caller] pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) { diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index f5513e999..9919dc708 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -21,6 +21,7 @@ mod libc { extern "C" { pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; pub fn chdir(dir: *const c_char) -> c_int; + pub fn __wasilibc_get_environ() -> *mut *mut c_char; } } @@ -161,7 +162,12 @@ impl Iterator for Env { pub fn env() -> Env { unsafe { let _guard = env_read_lock(); - let mut environ = libc::environ; + + // Use `__wasilibc_get_environ` instead of `environ` here so that we + // don't require wasi-libc to eagerly initialize the environment + // variables. + let mut environ = libc::__wasilibc_get_environ(); + let mut result = Vec::new(); if !environ.is_null() { while !(*environ).is_null() { diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs index 6741ae46d..30356fa85 100644 --- a/library/std/src/sys/windows/args.rs +++ b/library/std/src/sys/windows/args.rs @@ -270,7 +270,7 @@ pub(crate) fn make_bat_command_line( // It is necessary to surround the command in an extra pair of quotes, // hence the trailing quote here. It will be closed after all arguments // have been added. - let mut cmd: Vec<u16> = "cmd.exe /c \"".encode_utf16().collect(); + let mut cmd: Vec<u16> = "cmd.exe /d /c \"".encode_utf16().collect(); // Push the script name surrounded by its quote pair. cmd.push(b'"' as u16); @@ -290,6 +290,15 @@ pub(crate) fn make_bat_command_line( // reconstructed by the batch script by default. for arg in args { cmd.push(' ' as u16); + // Make sure to always quote special command prompt characters, including: + // * Characters `cmd /?` says require quotes. + // * `%` for environment variables, as in `%TMP%`. + // * `|<>` pipe/redirect characters. + const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>"; + let force_quotes = match arg { + Arg::Regular(arg) if !force_quotes => arg.bytes().iter().any(|c| SPECIAL.contains(c)), + _ => force_quotes, + }; append_arg(&mut cmd, arg, force_quotes)?; } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index f58dcf128..5d150eca0 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -6,13 +6,15 @@ use crate::ffi::CStr; use crate::mem; -use crate::os::raw::{c_char, c_int, c_long, c_longlong, c_uint, c_ulong, c_ushort}; +use crate::os::raw::{c_char, c_long, c_longlong, c_uint, c_ulong, c_ushort}; use crate::os::windows::io::{BorrowedHandle, HandleOrInvalid, HandleOrNull}; use crate::ptr; use core::ffi::NonZero_c_ulong; use libc::{c_void, size_t, wchar_t}; +pub use crate::os::raw::c_int; + #[path = "c/errors.rs"] // c.rs is included from two places so we need to specify this mod errors; pub use errors::*; @@ -47,16 +49,19 @@ pub type ACCESS_MASK = DWORD; pub type LPBOOL = *mut BOOL; pub type LPBYTE = *mut BYTE; +pub type LPCCH = *const CHAR; pub type LPCSTR = *const CHAR; +pub type LPCWCH = *const WCHAR; pub type LPCWSTR = *const WCHAR; +pub type LPCVOID = *const c_void; pub type LPDWORD = *mut DWORD; pub type LPHANDLE = *mut HANDLE; pub type LPOVERLAPPED = *mut OVERLAPPED; pub type LPPROCESS_INFORMATION = *mut PROCESS_INFORMATION; pub type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES; pub type LPSTARTUPINFO = *mut STARTUPINFO; +pub type LPSTR = *mut CHAR; pub type LPVOID = *mut c_void; -pub type LPCVOID = *const c_void; pub type LPWCH = *mut WCHAR; pub type LPWIN32_FIND_DATAW = *mut WIN32_FIND_DATAW; pub type LPWSADATA = *mut WSADATA; @@ -132,6 +137,10 @@ pub const MAX_PATH: usize = 260; pub const FILE_TYPE_PIPE: u32 = 3; +pub const CP_UTF8: DWORD = 65001; +pub const MB_ERR_INVALID_CHARS: DWORD = 0x08; +pub const WC_ERR_INVALID_CHARS: DWORD = 0x80; + #[repr(C)] #[derive(Copy)] pub struct WIN32_FIND_DATAW { @@ -539,14 +548,6 @@ pub struct SYMBOLIC_LINK_REPARSE_BUFFER { pub PathBuffer: WCHAR, } -/// NB: Use carefully! In general using this as a reference is likely to get the -/// provenance wrong for the `PathBuffer` field! -#[repr(C)] -pub struct FILE_NAME_INFO { - pub FileNameLength: DWORD, - pub FileName: [WCHAR; 1], -} - #[repr(C)] pub struct MOUNT_POINT_REPARSE_BUFFER { pub SubstituteNameOffset: c_ushort, @@ -1155,6 +1156,25 @@ extern "system" { lpFilePart: *mut LPWSTR, ) -> DWORD; pub fn GetFileAttributesW(lpFileName: LPCWSTR) -> DWORD; + + pub fn MultiByteToWideChar( + CodePage: UINT, + dwFlags: DWORD, + lpMultiByteStr: LPCCH, + cbMultiByte: c_int, + lpWideCharStr: LPWSTR, + cchWideChar: c_int, + ) -> c_int; + pub fn WideCharToMultiByte( + CodePage: UINT, + dwFlags: DWORD, + lpWideCharStr: LPCWCH, + cchWideChar: c_int, + lpMultiByteStr: LPSTR, + cbMultiByte: c_int, + lpDefaultChar: LPCCH, + lpUsedDefaultChar: LPBOOL, + ) -> c_int; } #[link(name = "ws2_32")] diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 378098038..d2c597664 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -1266,7 +1266,12 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> { // If the fallback fails for any reason we return the original error. match File::open(path, &opts) { Ok(file) => file.file_attr(), - Err(e) if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _) => { + Err(e) + if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)] + .contains(&e.raw_os_error()) => + { + // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource. + // One such example is `System Volume Information` as default but can be created as well // `ERROR_SHARING_VIOLATION` will almost never be returned. // Usually if a file is locked you can still read some metadata. // However, there are special system files, such as @@ -1393,6 +1398,8 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let data_ptr = data.0.as_mut_ptr(); let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>(); + // Zero the header to ensure it's fully initialized, including reserved parameters. + *db = mem::zeroed(); let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>(); let mut i = 0; // FIXME: this conversion is very hacky diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs index 2cc34c986..7fdd1f702 100644 --- a/library/std/src/sys/windows/io.rs +++ b/library/std/src/sys/windows/io.rs @@ -2,8 +2,7 @@ use crate::marker::PhantomData; use crate::mem::size_of; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; use crate::slice; -use crate::sys::{c, Align8}; -use core; +use crate::sys::c; use libc; #[derive(Copy, Clone)] @@ -125,22 +124,33 @@ unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { return false; } - const SIZE: usize = size_of::<c::FILE_NAME_INFO>() + c::MAX_PATH * size_of::<c::WCHAR>(); - let mut name_info_bytes = Align8([0u8; SIZE]); + /// Mirrors [`FILE_NAME_INFO`], giving it a fixed length that we can stack + /// allocate + /// + /// [`FILE_NAME_INFO`]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_name_info + #[repr(C)] + #[allow(non_snake_case)] + struct FILE_NAME_INFO { + FileNameLength: u32, + FileName: [u16; c::MAX_PATH as usize], + } + let mut name_info = FILE_NAME_INFO { FileNameLength: 0, FileName: [0; c::MAX_PATH as usize] }; + // Safety: buffer length is fixed. let res = c::GetFileInformationByHandleEx( handle, c::FileNameInfo, - name_info_bytes.0.as_mut_ptr() as *mut libc::c_void, - SIZE as u32, + &mut name_info as *mut _ as *mut libc::c_void, + size_of::<FILE_NAME_INFO>() as u32, ); if res == 0 { return false; } - let name_info: &c::FILE_NAME_INFO = &*(name_info_bytes.0.as_ptr() as *const c::FILE_NAME_INFO); - let name_len = name_info.FileNameLength as usize / 2; - // Offset to get the `FileName` field. - let name_ptr = name_info_bytes.0.as_ptr().offset(size_of::<c::DWORD>() as isize).cast::<u16>(); - let s = core::slice::from_raw_parts(name_ptr, name_len); + + // Use `get` because `FileNameLength` can be out of range. + let s = match name_info.FileName.get(..name_info.FileNameLength as usize / 2) { + None => return false, + Some(s) => s, + }; let name = String::from_utf16_lossy(s); // Get the file name only. let name = name.rsplit('\\').next().unwrap_or(&name); diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index 70c9b14a0..32c6ccffb 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -1,6 +1,5 @@ #![unstable(issue = "none", feature = "windows_stdio")] -use crate::char::decode_utf16; use crate::cmp; use crate::io; use crate::mem::MaybeUninit; @@ -170,14 +169,27 @@ fn write( } fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usize> { + debug_assert!(!utf8.is_empty()); + let mut utf16 = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2]; - let mut len_utf16 = 0; - for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) { - *dest = MaybeUninit::new(chr); - len_utf16 += 1; - } - // Safety: We've initialized `len_utf16` values. - let utf16: &[u16] = unsafe { MaybeUninit::slice_assume_init_ref(&utf16[..len_utf16]) }; + let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len())]; + + let utf16: &[u16] = unsafe { + // Note that this theoretically checks validity twice in the (most common) case + // where the underlying byte sequence is valid utf-8 (given the check in `write()`). + let result = c::MultiByteToWideChar( + c::CP_UTF8, // CodePage + c::MB_ERR_INVALID_CHARS, // dwFlags + utf8.as_ptr() as c::LPCCH, // lpMultiByteStr + utf8.len() as c::c_int, // cbMultiByte + utf16.as_mut_ptr() as c::LPWSTR, // lpWideCharStr + utf16.len() as c::c_int, // cchWideChar + ); + assert!(result != 0, "Unexpected error in MultiByteToWideChar"); + + // Safety: MultiByteToWideChar initializes `result` values. + MaybeUninit::slice_assume_init_ref(&utf16[..result as usize]) + }; let mut written = write_u16s(handle, &utf16)?; @@ -190,8 +202,8 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usiz // a missing surrogate can be produced (and also because of the UTF-8 validation above), // write the missing surrogate out now. // Buffering it would mean we have to lie about the number of bytes written. - let first_char_remaining = utf16[written]; - if first_char_remaining >= 0xDCEE && first_char_remaining <= 0xDFFF { + let first_code_unit_remaining = utf16[written]; + if first_code_unit_remaining >= 0xDCEE && first_code_unit_remaining <= 0xDFFF { // low surrogate // We just hope this works, and give up otherwise let _ = write_u16s(handle, &utf16[written..written + 1]); @@ -213,6 +225,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usiz } fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result<usize> { + debug_assert!(data.len() < u32::MAX as usize); let mut written = 0; cvt(unsafe { c::WriteConsoleW( @@ -366,26 +379,32 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usiz Ok(amount as usize) } -#[allow(unused)] fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> { - let mut written = 0; - for chr in decode_utf16(utf16.iter().cloned()) { - match chr { - Ok(chr) => { - chr.encode_utf8(&mut utf8[written..]); - written += chr.len_utf8(); - } - Err(_) => { - // We can't really do any better than forget all data and return an error. - return Err(io::const_io_error!( - io::ErrorKind::InvalidData, - "Windows stdin in console mode does not support non-UTF-16 input; \ - encountered unpaired surrogate", - )); - } - } + debug_assert!(utf16.len() <= c::c_int::MAX as usize); + debug_assert!(utf8.len() <= c::c_int::MAX as usize); + + let result = unsafe { + c::WideCharToMultiByte( + c::CP_UTF8, // CodePage + c::WC_ERR_INVALID_CHARS, // dwFlags + utf16.as_ptr(), // lpWideCharStr + utf16.len() as c::c_int, // cchWideChar + utf8.as_mut_ptr() as c::LPSTR, // lpMultiByteStr + utf8.len() as c::c_int, // cbMultiByte + ptr::null(), // lpDefaultChar + ptr::null_mut(), // lpUsedDefaultChar + ) + }; + if result == 0 { + // We can't really do any better than forget all data and return an error. + Err(io::const_io_error!( + io::ErrorKind::InvalidData, + "Windows stdin in console mode does not support non-UTF-16 input; \ + encountered unpaired surrogate", + )) + } else { + Ok(result as usize) } - Ok(written) } impl IncompleteUtf8 { diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs index 1cb576c95..ed58c47e0 100644 --- a/library/std/src/sys/windows/thread.rs +++ b/library/std/src/sys/windows/thread.rs @@ -22,7 +22,7 @@ pub struct Thread { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { - let p = Box::into_raw(box p); + let p = Box::into_raw(Box::new(p)); // FIXME On UNIX, we guard against stack sizes that are too small but // that's because pthreads enforces that stacks are at least diff --git a/library/std/src/sys/windows/thread_parking.rs b/library/std/src/sys/windows/thread_parking.rs index 5d43676ad..eb9167cd8 100644 --- a/library/std/src/sys/windows/thread_parking.rs +++ b/library/std/src/sys/windows/thread_parking.rs @@ -221,7 +221,7 @@ impl Parker { fn keyed_event_handle() -> c::HANDLE { const INVALID: c::HANDLE = ptr::invalid_mut(!0); - static HANDLE: AtomicPtr<libc::c_void> = AtomicPtr::new(INVALID); + static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(INVALID); match HANDLE.load(Relaxed) { INVALID => { let mut handle = c::INVALID_HANDLE_VALUE; diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs index f1d804ef4..8752f46ff 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys_common/backtrace.rs @@ -91,6 +91,19 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: if stop { return false; } + #[cfg(target_os = "nto")] + if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { + if !hit && start { + use crate::backtrace_rs::SymbolName; + res = bt_fmt.frame().print_raw( + frame.ip(), + Some(SymbolName::new("__my_thread_exit".as_bytes())), + None, + None, + ); + } + return false; + } if !hit && start { res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); } diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 6b24b0e9a..e9c727cbb 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -44,7 +44,6 @@ cfg_if::cfg_if! { cfg_if::cfg_if! { if #[cfg(any(target_os = "l4re", - target_os = "hermit", feature = "restricted-std", all(target_family = "wasm", not(target_os = "emscripten")), all(target_vendor = "fortanix", target_env = "sgx")))] { diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index fad4a6333..85ecc1def 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -14,14 +14,14 @@ use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; -use libc::{c_int, c_void}; +use crate::ffi::{c_int, c_void}; cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "watchos", target_os = "openbsd", target_os = "netbsd", target_os = "illumos", - target_os = "solaris", target_os = "haiku", target_os = "l4re"))] { + target_os = "solaris", target_os = "haiku", target_os = "l4re", target_os = "nto"))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; } else { @@ -35,7 +35,7 @@ cfg_if::cfg_if! { target_os = "linux", target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "haiku"))] { + target_os = "haiku", target_os = "nto"))] { use libc::MSG_NOSIGNAL; } else { const MSG_NOSIGNAL: c_int = 0x0; @@ -46,8 +46,9 @@ cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "solaris", target_os = "illumos"))] { - use libc::c_uchar; + target_os = "solaris", target_os = "illumos", + target_os = "nto"))] { + use crate::ffi::c_uchar; type IpV4MultiCastType = c_uchar; } else { type IpV4MultiCastType = c_int; @@ -127,8 +128,8 @@ fn to_ipv6mr_interface(value: u32) -> c_int { } #[cfg(not(target_os = "android"))] -fn to_ipv6mr_interface(value: u32) -> libc::c_uint { - value as libc::c_uint +fn to_ipv6mr_interface(value: u32) -> crate::ffi::c_uint { + value as crate::ffi::c_uint } //////////////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys_common/net/tests.rs index ac75d9ebf..fc236b802 100644 --- a/library/std/src/sys_common/net/tests.rs +++ b/library/std/src/sys_common/net/tests.rs @@ -6,7 +6,7 @@ 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}"), + Err(e) => panic!("couldn't resolve `localhost`: {e}"), }; for sa in lh { *addrs.entry(sa).or_insert(0) += 1; diff --git a/library/std/src/sys_common/once/futex.rs b/library/std/src/sys_common/once/futex.rs index 5c7e6c013..42db5fad4 100644 --- a/library/std/src/sys_common/once/futex.rs +++ b/library/std/src/sys_common/once/futex.rs @@ -4,6 +4,7 @@ use crate::sync::atomic::{ AtomicU32, Ordering::{Acquire, Relaxed, Release}, }; +use crate::sync::once::ExclusiveState; use crate::sys::futex::{futex_wait, futex_wake_all}; // On some platforms, the OS is very nice and handles the waiter queue for us. @@ -78,6 +79,16 @@ impl Once { self.state.load(Acquire) == COMPLETE } + #[inline] + pub(crate) fn state(&mut self) -> ExclusiveState { + match *self.state.get_mut() { + INCOMPLETE => ExclusiveState::Incomplete, + POISONED => ExclusiveState::Poisoned, + COMPLETE => ExclusiveState::Complete, + _ => unreachable!("invalid Once state"), + } + } + // This uses FnMut to match the API of the generic implementation. As this // implementation is quite light-weight, it is generic over the closure and // so avoids the cost of dynamic dispatch. diff --git a/library/std/src/sys_common/once/queue.rs b/library/std/src/sys_common/once/queue.rs index d953a6745..def0bcd6f 100644 --- a/library/std/src/sys_common/once/queue.rs +++ b/library/std/src/sys_common/once/queue.rs @@ -60,6 +60,7 @@ use crate::fmt; use crate::ptr; use crate::sync as public; use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::sync::once::ExclusiveState; use crate::thread::{self, Thread}; type Masked = (); @@ -121,6 +122,16 @@ impl Once { self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE } + #[inline] + pub(crate) fn state(&mut self) -> ExclusiveState { + match self.state_and_queue.get_mut().addr() { + INCOMPLETE => ExclusiveState::Incomplete, + POISONED => ExclusiveState::Poisoned, + COMPLETE => ExclusiveState::Complete, + _ => unreachable!("invalid Once state"), + } + } + // This is a non-generic function to reduce the monomorphization cost of // using `call_once` (this isn't exactly a trivial or small implementation). // diff --git a/library/std/src/sys_common/thread_local_dtor.rs b/library/std/src/sys_common/thread_local_dtor.rs index 1d13a7171..844946eda 100644 --- a/library/std/src/sys_common/thread_local_dtor.rs +++ b/library/std/src/sys_common/thread_local_dtor.rs @@ -30,7 +30,7 @@ pub unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern "C" fn(*mut static DTORS: StaticKey = StaticKey::new(Some(run_dtors)); type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; if DTORS.get().is_null() { - let v: Box<List> = box Vec::new(); + let v: Box<List> = Box::new(Vec::new()); DTORS.set(Box::into_raw(v) as *mut u8); } let list: &mut List = &mut *(DTORS.get() as *mut List); diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs index 2672a2a75..89360e456 100644 --- a/library/std/src/sys_common/thread_local_key.rs +++ b/library/std/src/sys_common/thread_local_key.rs @@ -117,9 +117,15 @@ pub struct Key { /// This value specifies no destructor by default. pub const INIT: StaticKey = StaticKey::new(None); -// Define a sentinel value that is unlikely to be returned -// as a TLS key (but it may be returned). +// Define a sentinel value that is likely not to be returned +// as a TLS key. +#[cfg(not(target_os = "nto"))] const KEY_SENTVAL: usize = 0; +// On QNX Neutrino, 0 is always returned when currently not in use. +// Using 0 would mean to always create two keys and remote the first +// one (with value of 0) immediately afterwards. +#[cfg(target_os = "nto")] +const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1; impl StaticKey { #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] diff --git a/library/std/src/sys_common/thread_parking/id.rs b/library/std/src/sys_common/thread_parking/id.rs index e98169597..575988ec7 100644 --- a/library/std/src/sys_common/thread_parking/id.rs +++ b/library/std/src/sys_common/thread_parking/id.rs @@ -60,7 +60,7 @@ impl Parker { if state == PARKED { // Loop to guard against spurious wakeups. while state == PARKED { - park(self.state.as_mut_ptr().addr()); + park(self.state.as_ptr().addr()); state = self.state.load(Acquire); } @@ -76,7 +76,7 @@ impl Parker { let state = self.state.fetch_sub(1, Acquire).wrapping_sub(1); if state == PARKED { - park_timeout(dur, self.state.as_mut_ptr().addr()); + park_timeout(dur, self.state.as_ptr().addr()); // Swap to ensure that we observe all state changes with acquire // ordering, even if the state has been changed after the timeout // occured. @@ -99,7 +99,7 @@ impl Parker { // and terminated before this call is made. This call then returns an // error or wakes up an unrelated thread. The platform API and // environment does allow this, however. - unpark(tid, self.state.as_mut_ptr().addr()); + unpark(tid, self.state.as_ptr().addr()); } } } diff --git a/library/std/src/sys_common/thread_parking/mod.rs b/library/std/src/sys_common/thread_parking/mod.rs index 0ead6633c..e8e028bb3 100644 --- a/library/std/src/sys_common/thread_parking/mod.rs +++ b/library/std/src/sys_common/thread_parking/mod.rs @@ -14,12 +14,10 @@ cfg_if::cfg_if! { } else if #[cfg(any( target_os = "netbsd", all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", ))] { mod id; pub use id::Parker; - } else if #[cfg(target_os = "solid_asp3")] { - mod wait_flag; - pub use wait_flag::Parker; } else if #[cfg(any(windows, target_family = "unix"))] { pub use crate::sys::thread_parking::Parker; } else { diff --git a/library/std/src/sys_common/thread_parking/wait_flag.rs b/library/std/src/sys_common/thread_parking/wait_flag.rs deleted file mode 100644 index d0f8899a9..000000000 --- a/library/std/src/sys_common/thread_parking/wait_flag.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! A wait-flag-based thread parker. -//! -//! Some operating systems provide low-level parking primitives like wait counts, -//! event flags or semaphores which are not susceptible to race conditions (meaning -//! the wakeup can occur before the wait operation). To implement the `std` thread -//! parker on top of these primitives, we only have to ensure that parking is fast -//! when the thread token is available, the atomic ordering guarantees are maintained -//! and spurious wakeups are minimized. -//! -//! To achieve this, this parker uses an atomic variable with three states: `EMPTY`, -//! `PARKED` and `NOTIFIED`: -//! * `EMPTY` means the token has not been made available, but the thread is not -//! currently waiting on it. -//! * `PARKED` means the token is not available and the thread is parked. -//! * `NOTIFIED` means the token is available. -//! -//! `park` and `park_timeout` change the state from `EMPTY` to `PARKED` and from -//! `NOTIFIED` to `EMPTY`. If the state was `NOTIFIED`, the thread was unparked and -//! execution can continue without calling into the OS. If the state was `EMPTY`, -//! the token is not available and the thread waits on the primitive (here called -//! "wait flag"). -//! -//! `unpark` changes the state to `NOTIFIED`. If the state was `PARKED`, the thread -//! is or will be sleeping on the wait flag, so we raise it. - -use crate::pin::Pin; -use crate::sync::atomic::AtomicI8; -use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sys::wait_flag::WaitFlag; -use crate::time::Duration; - -const EMPTY: i8 = 0; -const PARKED: i8 = -1; -const NOTIFIED: i8 = 1; - -pub struct Parker { - state: AtomicI8, - wait_flag: WaitFlag, -} - -impl Parker { - /// Construct a parker for the current thread. The UNIX parker - /// implementation requires this to happen in-place. - pub unsafe fn new_in_place(parker: *mut Parker) { - parker.write(Parker { state: AtomicI8::new(EMPTY), wait_flag: WaitFlag::new() }) - } - - // This implementation doesn't require `unsafe` and `Pin`, but other implementations do. - pub unsafe fn park(self: Pin<&Self>) { - match self.state.fetch_sub(1, Acquire) { - // NOTIFIED => EMPTY - NOTIFIED => return, - // EMPTY => PARKED - EMPTY => (), - _ => panic!("inconsistent park state"), - } - - // Avoid waking up from spurious wakeups (these are quite likely, see below). - loop { - self.wait_flag.wait(); - - match self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed) { - Ok(_) => return, - Err(PARKED) => (), - Err(_) => panic!("inconsistent park state"), - } - } - } - - // This implementation doesn't require `unsafe` and `Pin`, but other implementations do. - pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) { - match self.state.fetch_sub(1, Acquire) { - NOTIFIED => return, - EMPTY => (), - _ => panic!("inconsistent park state"), - } - - self.wait_flag.wait_timeout(dur); - - // Either a wakeup or a timeout occurred. Wakeups may be spurious, as there can be - // a race condition when `unpark` is performed between receiving the timeout and - // resetting the state, resulting in the eventflag being set unnecessarily. `park` - // is protected against this by looping until the token is actually given, but - // here we cannot easily tell. - - // Use `swap` to provide acquire ordering. - match self.state.swap(EMPTY, Acquire) { - NOTIFIED => (), - PARKED => (), - _ => panic!("inconsistent park state"), - } - } - - // This implementation doesn't require `Pin`, but other implementations do. - pub fn unpark(self: Pin<&Self>) { - let state = self.state.swap(NOTIFIED, Release); - - if state == PARKED { - self.wait_flag.raise(); - } - } -} diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index dd53767d4..bc588bdbb 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -18,10 +18,10 @@ #[cfg(test)] mod tests; +use core::char::{encode_utf16_raw, encode_utf8_raw}; use core::str::next_code_point; use crate::borrow::Cow; -use crate::char; use crate::collections::TryReserveError; use crate::fmt; use crate::hash::{Hash, Hasher}; @@ -235,7 +235,7 @@ impl Wtf8Buf { /// This does **not** include the WTF-8 concatenation check or `is_known_utf8` check. fn push_code_point_unchecked(&mut self, code_point: CodePoint) { let mut bytes = [0; 4]; - let bytes = char::encode_utf8_raw(code_point.value, &mut bytes); + let bytes = encode_utf8_raw(code_point.value, &mut bytes); self.bytes.extend_from_slice(bytes) } @@ -594,7 +594,7 @@ impl Wtf8 { } /// Returns the code point at `position` if it is in the ASCII range, - /// or `b'\xFF' otherwise. + /// or `b'\xFF'` otherwise. /// /// # Panics /// @@ -939,7 +939,7 @@ impl<'a> Iterator for EncodeWide<'a> { let mut buf = [0; 2]; self.code_points.next().map(|code_point| { - let n = char::encode_utf16_raw(code_point.value, &mut buf).len(); + let n = encode_utf16_raw(code_point.value, &mut buf).len(); if n == 2 { self.extra = buf[1]; } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index b30bb7b77..cf7c2e05a 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -1110,8 +1110,7 @@ pub mod os { let ptr = if ptr.is_null() { // If the lookup returned null, we haven't initialized our own // local copy, so do that now. - let ptr: Box<Value<T>> = box Value { inner: LazyKeyInner::new(), key: self }; - let ptr = Box::into_raw(ptr); + let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self })); // SAFETY: At this point we are sure there is no value inside // ptr so setting it will not affect anyone else. unsafe { diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 692ff0cbc..489af7767 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -124,8 +124,10 @@ //! //! ## Stack size //! -//! The default stack size is platform-dependent and subject to change. Currently it is 2MB on all -//! Tier-1 platforms. There are two ways to manually specify the stack size for spawned threads: +//! The default stack size is platform-dependent and subject to change. +//! Currently, it is 2 MiB on all Tier-1 platforms. +//! +//! There are two ways to manually specify the stack size for spawned threads: //! //! * Build the thread with [`Builder`] and pass the desired stack size to [`Builder::stack_size`]. //! * Set the `RUST_MIN_STACK` environment variable to an integer representing the desired stack diff --git a/library/std/src/time.rs b/library/std/src/time.rs index ecd06ebf7..5c2e9da70 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -1,6 +1,6 @@ //! Temporal quantification. //! -//! # Examples: +//! # Examples //! //! There are multiple ways to create a new [`Duration`]: //! @@ -352,7 +352,7 @@ impl Instant { self.checked_duration_since(earlier).unwrap_or_default() } - /// Returns the amount of time elapsed since this instant was created. + /// Returns the amount of time elapsed since this instant. /// /// # Panics /// @@ -525,8 +525,8 @@ impl SystemTime { self.0.sub_time(&earlier.0).map_err(SystemTimeError) } - /// Returns the difference between the clock time when this - /// system time was created, and the current clock time. + /// Returns the difference from this system time to the + /// current clock time. /// /// This function may fail as the underlying system clock is susceptible to /// drift and updates (e.g., the system clock could go backwards), so this diff --git a/library/std/tests/common/mod.rs b/library/std/tests/common/mod.rs new file mode 100644 index 000000000..fce220223 --- /dev/null +++ b/library/std/tests/common/mod.rs @@ -0,0 +1,58 @@ +#![allow(unused)] + +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; +use std::thread; +use rand::RngCore; + +/// Copied from `std::test_helpers::test_rng`, since these tests rely on the +/// seed not being the same for every RNG invocation too. +#[track_caller] +pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { + use core::hash::{BuildHasher, Hash, Hasher}; + let mut hasher = std::collections::hash_map::RandomState::new().build_hasher(); + core::panic::Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + rand::SeedableRng::from_seed(seed) +} + +// Copied from std::sys_common::io +pub struct TempDir(PathBuf); + +impl TempDir { + pub fn join(&self, path: &str) -> PathBuf { + let TempDir(ref p) = *self; + p.join(path) + } + + pub fn path(&self) -> &Path { + let TempDir(ref p) = *self; + p + } +} + +impl Drop for TempDir { + fn drop(&mut self) { + // Gee, seeing how we're testing the fs module I sure hope that we + // at least implement this correctly! + let TempDir(ref p) = *self; + let result = fs::remove_dir_all(p); + // Avoid panicking while panicking as this causes the process to + // immediately abort, without displaying test results. + if !thread::panicking() { + result.unwrap(); + } + } +} + +#[track_caller] // for `test_rng` +pub fn tmpdir() -> TempDir { + let p = env::temp_dir(); + let mut r = test_rng(); + let ret = p.join(&format!("rust-{}", r.next_u32())); + fs::create_dir(&ret).unwrap(); + TempDir(ret) +} diff --git a/library/std/tests/create_dir_all_bare.rs b/library/std/tests/create_dir_all_bare.rs new file mode 100644 index 000000000..fe789323f --- /dev/null +++ b/library/std/tests/create_dir_all_bare.rs @@ -0,0 +1,39 @@ +#![cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx"))))] + +//! Note that this test changes the current directory so +//! should not be in the same process as other tests. +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; + +mod common; + +// On some platforms, setting the current directory will prevent deleting it. +// So this helper ensures the current directory is reset. +struct CurrentDir(PathBuf); +impl CurrentDir { + fn new() -> Self { + Self(env::current_dir().unwrap()) + } + fn set(&self, path: &Path) { + env::set_current_dir(path).unwrap(); + } + fn with(path: &Path, f: impl FnOnce()) { + let current_dir = Self::new(); + current_dir.set(path); + f(); + } +} +impl Drop for CurrentDir { + fn drop(&mut self) { + env::set_current_dir(&self.0).unwrap(); + } +} + +#[test] +fn create_dir_all_bare() { + let tmpdir = common::tmpdir(); + CurrentDir::with(tmpdir.path(), || { + fs::create_dir_all("create-dir-all-bare").unwrap(); + }); +} diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index aae2c49d8..96b4f372b 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -3,18 +3,8 @@ use std::ffi::{OsStr, OsString}; use rand::distributions::{Alphanumeric, DistString}; -/// Copied from `std::test_helpers::test_rng`, since these tests rely on the -/// seed not being the same for every RNG invocation too. -#[track_caller] -pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { - use core::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = std::collections::hash_map::RandomState::new().build_hasher(); - core::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - rand::SeedableRng::from_seed(seed) -} +mod common; +use common::test_rng; #[track_caller] fn make_rand_name() -> OsString { diff --git a/library/std/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs index 1a2c12556..bf3c81fcc 100644 --- a/library/std/tests/run-time-detect.rs +++ b/library/std/tests/run-time-detect.rs @@ -120,16 +120,13 @@ fn x86_all() { println!("avx512dq: {:?}", is_x86_feature_detected!("avx512dq")); println!("avx512er: {:?}", is_x86_feature_detected!("avx512er")); println!("avx512f: {:?}", is_x86_feature_detected!("avx512f")); - println!("avx512gfni: {:?}", is_x86_feature_detected!("avx512gfni")); println!("avx512ifma: {:?}", is_x86_feature_detected!("avx512ifma")); println!("avx512pf: {:?}", is_x86_feature_detected!("avx512pf")); - println!("avx512vaes: {:?}", is_x86_feature_detected!("avx512vaes")); println!("avx512vbmi2: {:?}", is_x86_feature_detected!("avx512vbmi2")); println!("avx512vbmi: {:?}", is_x86_feature_detected!("avx512vbmi")); println!("avx512vl: {:?}", is_x86_feature_detected!("avx512vl")); println!("avx512vnni: {:?}", is_x86_feature_detected!("avx512vnni")); println!("avx512vp2intersect: {:?}", is_x86_feature_detected!("avx512vp2intersect")); - println!("avx512vpclmulqdq: {:?}", is_x86_feature_detected!("avx512vpclmulqdq")); println!("avx512vpopcntdq: {:?}", is_x86_feature_detected!("avx512vpopcntdq")); println!("avx: {:?}", is_x86_feature_detected!("avx")); println!("bmi1: {:?}", is_x86_feature_detected!("bmi1")); @@ -138,6 +135,7 @@ fn x86_all() { println!("f16c: {:?}", is_x86_feature_detected!("f16c")); println!("fma: {:?}", is_x86_feature_detected!("fma")); println!("fxsr: {:?}", is_x86_feature_detected!("fxsr")); + println!("gfni: {:?}", is_x86_feature_detected!("gfni")); println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt")); //println!("movbe: {:?}", is_x86_feature_detected!("movbe")); // movbe is unsupported as a target feature println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq")); @@ -154,6 +152,8 @@ fn x86_all() { println!("sse: {:?}", is_x86_feature_detected!("sse")); println!("ssse3: {:?}", is_x86_feature_detected!("ssse3")); println!("tbm: {:?}", is_x86_feature_detected!("tbm")); + println!("vaes: {:?}", is_x86_feature_detected!("vaes")); + println!("vpclmulqdq: {:?}", is_x86_feature_detected!("vpclmulqdq")); println!("xsave: {:?}", is_x86_feature_detected!("xsave")); println!("xsavec: {:?}", is_x86_feature_detected!("xsavec")); println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt")); |