Add more from the std
This commit is contained in:
@@ -6,7 +6,9 @@ edition = "2024"
|
||||
[dependencies]
|
||||
cfg-if = { version = "1.0" }
|
||||
rustc-demangle = { version = "0.1.27" }
|
||||
hashbrown = "0.16"
|
||||
std_detect = { path = "crates/std_detect" }
|
||||
panic_abort = { path = "crates/panic_abort" }
|
||||
hashbrown = { version = "0.16.1", default-features = false, features = ["nightly", "rustc-internal-api"] }
|
||||
os-std-macros = { path = "../os-std-macros" }
|
||||
shared = { path = "../shared", features = ["user"] }
|
||||
io = { path = "../io", features = ["alloc"] }
|
||||
io_crate = { package = "io", path = "../io", features = ["alloc"] }
|
||||
|
||||
19
crates/std/crates/panic_abort/Cargo.toml
Normal file
19
crates/std/crates/panic_abort/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "panic_abort"
|
||||
version = "0.0.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/rust-lang/rust.git"
|
||||
description = "Implementation of Rust panics via process aborts"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
test = false
|
||||
bench = false
|
||||
doc = false
|
||||
|
||||
[dependencies]
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
libc = { version = "0.2", default-features = false }
|
||||
|
||||
[target.'cfg(any(target_os = "android", target_os = "zkvm"))'.dependencies]
|
||||
51
crates/std/crates/panic_abort/src/android.rs
Normal file
51
crates/std/crates/panic_abort/src/android.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use alloc::string::String;
|
||||
use core::mem::transmute;
|
||||
use core::panic::PanicPayload;
|
||||
use core::ptr::copy_nonoverlapping;
|
||||
|
||||
const ANDROID_SET_ABORT_MESSAGE: &[u8] = b"android_set_abort_message\0";
|
||||
type SetAbortMessageType = unsafe extern "C" fn(*const libc::c_char) -> ();
|
||||
|
||||
// Forward the abort message to libc's android_set_abort_message. We try our best to populate the
|
||||
// message but as this function may already be called as part of a failed allocation, it might not be
|
||||
// possible to do so.
|
||||
//
|
||||
// Some methods of core are on purpose avoided (such as try_reserve) as these rely on the correct
|
||||
// resolution of rust_eh_personality which is loosely defined in panic_abort.
|
||||
//
|
||||
// Weakly resolve the symbol for android_set_abort_message. This function is only available
|
||||
// for API >= 21.
|
||||
pub(crate) unsafe fn android_set_abort_message(payload: &mut dyn PanicPayload) {
|
||||
let func_addr = unsafe {
|
||||
libc::dlsym(libc::RTLD_DEFAULT, ANDROID_SET_ABORT_MESSAGE.as_ptr() as *const libc::c_char)
|
||||
as usize
|
||||
};
|
||||
if func_addr == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let payload = payload.get();
|
||||
let msg = match payload.downcast_ref::<&'static str>() {
|
||||
Some(msg) => msg.as_bytes(),
|
||||
None => match payload.downcast_ref::<String>() {
|
||||
Some(msg) => msg.as_bytes(),
|
||||
None => &[],
|
||||
},
|
||||
};
|
||||
if msg.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate a new buffer to append the null byte.
|
||||
let size = msg.len() + 1usize;
|
||||
let buf = unsafe { libc::malloc(size) as *mut libc::c_char };
|
||||
if buf.is_null() {
|
||||
return; // allocation failure
|
||||
}
|
||||
unsafe {
|
||||
copy_nonoverlapping(msg.as_ptr(), buf as *mut u8, msg.len());
|
||||
buf.add(msg.len()).write(0);
|
||||
let func = transmute::<usize, SetAbortMessageType>(func_addr);
|
||||
func(buf);
|
||||
}
|
||||
}
|
||||
94
crates/std/crates/panic_abort/src/lib.rs
Normal file
94
crates/std/crates/panic_abort/src/lib.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
//! Implementation of Rust panics via process aborts
|
||||
//!
|
||||
//! When compared to the implementation via unwinding, this crate is *much*
|
||||
//! simpler! That being said, it's not quite as versatile, but here goes!
|
||||
|
||||
#![no_std]
|
||||
#![unstable(feature = "panic_abort", issue = "32837")]
|
||||
#![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]
|
||||
#![panic_runtime]
|
||||
#![feature(panic_runtime)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
mod android;
|
||||
|
||||
#[cfg(target_os = "zkvm")]
|
||||
mod zkvm;
|
||||
|
||||
use core::any::Any;
|
||||
use core::panic::PanicPayload;
|
||||
|
||||
#[rustc_std_internal_symbol]
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// "Leak" the payload and shim to the relevant abort on the platform in question.
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 {
|
||||
// Android has the ability to attach a message as part of the abort.
|
||||
#[cfg(target_os = "android")]
|
||||
unsafe {
|
||||
android::android_set_abort_message(_payload);
|
||||
}
|
||||
#[cfg(target_os = "zkvm")]
|
||||
unsafe {
|
||||
zkvm::zkvm_set_abort_message(_payload);
|
||||
}
|
||||
|
||||
unsafe extern "Rust" {
|
||||
// This is defined in std::rt.
|
||||
#[rustc_std_internal_symbol]
|
||||
safe fn __rust_abort() -> !;
|
||||
}
|
||||
|
||||
__rust_abort()
|
||||
}
|
||||
|
||||
// This... is a bit of an oddity. The tl;dr; is that this is required to link
|
||||
// correctly, the longer explanation is below.
|
||||
//
|
||||
// Right now the binaries of core/std that we ship are all compiled with
|
||||
// `-C panic=unwind`. This is done to ensure that the binaries are maximally
|
||||
// compatible with as many situations as possible. The compiler, however,
|
||||
// requires a "personality function" for all functions compiled with `-C
|
||||
// panic=unwind`. This personality function is hardcoded to the symbol
|
||||
// `rust_eh_personality` and is defined by the `eh_personality` lang item.
|
||||
//
|
||||
// So... why not just define that lang item here? Good question! The way that
|
||||
// panic runtimes are linked in is actually a little subtle in that they're
|
||||
// "sort of" in the compiler's crate store, but only actually linked if another
|
||||
// isn't actually linked. This ends up meaning that both this crate and the
|
||||
// panic_unwind crate can appear in the compiler's crate store, and if both
|
||||
// define the `eh_personality` lang item then that'll hit an error.
|
||||
//
|
||||
// To handle this the compiler only requires the `eh_personality` is defined if
|
||||
// the panic runtime being linked in is the unwinding runtime, and otherwise
|
||||
// it's not required to be defined (rightfully so). In this case, however, this
|
||||
// library just defines this symbol so there's at least some personality
|
||||
// somewhere.
|
||||
//
|
||||
// Essentially this symbol is just defined to get wired up to core/std
|
||||
// binaries, but it should never be called as we don't link in an unwinding
|
||||
// runtime at all.
|
||||
pub mod personalities {
|
||||
// In the past this module used to contain stubs for the personality
|
||||
// functions of various platforms, but these where removed when personality
|
||||
// functions were moved to std.
|
||||
|
||||
// This corresponds to the `eh_catch_typeinfo` lang item
|
||||
// that's only used on Emscripten currently.
|
||||
//
|
||||
// Since panics don't generate exceptions and foreign exceptions are
|
||||
// currently UB with -C panic=abort (although this may be subject to
|
||||
// change), any catch_unwind calls will never use this typeinfo.
|
||||
#[rustc_std_internal_symbol]
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[cfg(target_os = "emscripten")]
|
||||
static rust_eh_catch_typeinfo: [usize; 2] = [0; 2];
|
||||
}
|
||||
26
crates/std/crates/panic_abort/src/zkvm.rs
Normal file
26
crates/std/crates/panic_abort/src/zkvm.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use alloc::string::String;
|
||||
use core::panic::PanicPayload;
|
||||
|
||||
// Forward the abort message to zkVM's sys_panic. This is implemented by RISC Zero's
|
||||
// platform crate which exposes system calls specifically for the zkVM.
|
||||
pub(crate) unsafe fn zkvm_set_abort_message(payload: &mut dyn PanicPayload) {
|
||||
let payload = payload.get();
|
||||
let msg = match payload.downcast_ref::<&'static str>() {
|
||||
Some(msg) => msg.as_bytes(),
|
||||
None => match payload.downcast_ref::<String>() {
|
||||
Some(msg) => msg.as_bytes(),
|
||||
None => &[],
|
||||
},
|
||||
};
|
||||
if msg.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
fn sys_panic(msg_ptr: *const u8, len: usize) -> !;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
sys_panic(msg.as_ptr(), msg.len());
|
||||
}
|
||||
}
|
||||
24
crates/std/crates/std_detect/Cargo.toml
Normal file
24
crates/std/crates/std_detect/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "std_detect"
|
||||
version = "0.1.5"
|
||||
authors = [
|
||||
"Alex Crichton <alex@alexcrichton.com>",
|
||||
"Andrew Gallant <jamslam@gmail.com>",
|
||||
"Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>",
|
||||
]
|
||||
description = "`std::detect` - Rust's standard library run-time CPU feature detection."
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2024"
|
||||
|
||||
[badges]
|
||||
is-it-maintained-issue-resolution = { repository = "rust-lang/stdarch" }
|
||||
is-it-maintained-open-issues = { repository = "rust-lang/stdarch" }
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
[dependencies]
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
libc = { version = "0.2.0", default-features = false }
|
||||
|
||||
[features]
|
||||
std_detect_env_override = []
|
||||
69
crates/std/crates/std_detect/README.md
Normal file
69
crates/std/crates/std_detect/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
`std::detect` - Rust's standard library run-time CPU feature detection
|
||||
=======
|
||||
|
||||
The private `std::detect` module implements run-time feature detection in Rust's
|
||||
standard library. This allows detecting whether the CPU the binary runs on
|
||||
supports certain features, like SIMD instructions.
|
||||
|
||||
# Usage
|
||||
|
||||
`std::detect` APIs are available as part of `libstd`. Prefer using it via the
|
||||
standard library than through this crate. Unstable features of `std::detect` are
|
||||
available on nightly Rust behind various feature-gates.
|
||||
|
||||
If you need run-time feature detection in `#[no_std]` environments, Rust `core`
|
||||
library cannot help you. By design, Rust `core` is platform independent, but
|
||||
performing run-time feature detection requires a certain level of cooperation
|
||||
from the platform.
|
||||
|
||||
You can then manually include `std_detect` as a dependency to get similar
|
||||
run-time feature detection support than the one offered by Rust's standard
|
||||
library. We intend to make `std_detect` more flexible and configurable in this
|
||||
regard to better serve the needs of `#[no_std]` targets.
|
||||
|
||||
# Platform support
|
||||
|
||||
* All `x86`/`x86_64` targets are supported on all platforms by querying the
|
||||
`cpuid` instruction directly for the features supported by the hardware and
|
||||
the operating system. `std_detect` assumes that the binary is an user-space
|
||||
application.
|
||||
|
||||
* Linux/Android:
|
||||
* `arm{32, 64}`, `mips{32,64}{,el}`, `powerpc{32,64}{,le}`, `loongarch{32,64}`, `s390x`:
|
||||
`std_detect` supports these on Linux by querying ELF auxiliary vectors (using `getauxval`
|
||||
when available), and if that fails, by querying `/proc/self/auxv`.
|
||||
* `arm64`: partial support for doing run-time feature detection by directly
|
||||
querying `mrs` is implemented for Linux >= 4.11, but not enabled by default.
|
||||
* `riscv{32,64}`:
|
||||
`std_detect` supports these on Linux by querying `riscv_hwprobe`, and
|
||||
by querying ELF auxiliary vectors (using `getauxval` when available).
|
||||
|
||||
* FreeBSD:
|
||||
* `arm32`, `powerpc64`: `std_detect` supports these on FreeBSD by querying ELF
|
||||
auxiliary vectors using `elf_aux_info`.
|
||||
* `arm64`: run-time feature detection is implemented by directly querying `mrs`.
|
||||
|
||||
* OpenBSD:
|
||||
* `powerpc64`: `std_detect` supports these on OpenBSD by querying ELF auxiliary
|
||||
vectors using `elf_aux_info`.
|
||||
* `arm64`: run-time feature detection is implemented by querying `sysctl`.
|
||||
|
||||
* Windows:
|
||||
* `arm64`: run-time feature detection is implemented by querying `IsProcessorFeaturePresent`.
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
# Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in `std_detect` by you, as defined in the Apache-2.0 license,
|
||||
shall be dual licensed as above, without any additional terms or conditions.
|
||||
264
crates/std/crates/std_detect/src/detect/arch/aarch64.rs
Normal file
264
crates/std/crates/std_detect/src/detect/arch/aarch64.rs
Normal file
@@ -0,0 +1,264 @@
|
||||
//! Aarch64 run-time features.
|
||||
|
||||
features! {
|
||||
@TARGET: aarch64;
|
||||
@CFG: any(target_arch = "aarch64", target_arch = "arm64ec");
|
||||
@MACRO_NAME: is_aarch64_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
///
|
||||
/// This macro takes one argument which is a string literal of the feature being tested for.
|
||||
/// The feature names are mostly taken from their FEAT_* definitions in the [ARM Architecture
|
||||
/// Reference Manual][docs].
|
||||
///
|
||||
/// Currently most features are only supported on linux-based platforms: on other platforms the
|
||||
/// runtime check will always return `false`.
|
||||
///
|
||||
/// ## Supported arguments
|
||||
///
|
||||
/// * `"aes"` - FEAT_AES & FEAT_PMULL
|
||||
/// * `"asimd"` or "neon" - FEAT_AdvSIMD
|
||||
/// * `"bf16"` - FEAT_BF16
|
||||
/// * `"bti"` - FEAT_BTI
|
||||
/// * `"crc"` - FEAT_CRC
|
||||
/// * `"cssc"` - FEAT_CSSC
|
||||
/// * `"dit"` - FEAT_DIT
|
||||
/// * `"dotprod"` - FEAT_DotProd
|
||||
/// * `"dpb"` - FEAT_DPB
|
||||
/// * `"dpb2"` - FEAT_DPB2
|
||||
/// * `"ecv"` - FEAT_ECV
|
||||
/// * `"f32mm"` - FEAT_F32MM
|
||||
/// * `"f64mm"` - FEAT_F64MM
|
||||
/// * `"faminmax"` - FEAT_FAMINMAX
|
||||
/// * `"fcma"` - FEAT_FCMA
|
||||
/// * `"fhm"` - FEAT_FHM
|
||||
/// * `"flagm"` - FEAT_FLAGM
|
||||
/// * `"flagm2"` - FEAT_FLAGM2
|
||||
/// * `"fp"` - FEAT_FP
|
||||
/// * `"fp16"` - FEAT_FP16
|
||||
/// * `"fp8"` - FEAT_FP8
|
||||
/// * `"fp8dot2"` - FEAT_FP8DOT2
|
||||
/// * `"fp8dot4"` - FEAT_FP8DOT4
|
||||
/// * `"fp8fma"` - FEAT_FP8FMA
|
||||
/// * `"fpmr"` - FEAT_FPMR
|
||||
/// * `"frintts"` - FEAT_FRINTTS
|
||||
/// * `"hbc"` - FEAT_HBC
|
||||
/// * `"i8mm"` - FEAT_I8MM
|
||||
/// * `"jsconv"` - FEAT_JSCVT
|
||||
/// * `"lse"` - FEAT_LSE
|
||||
/// * `"lse128"` - FEAT_LSE128
|
||||
/// * `"lse2"` - FEAT_LSE2
|
||||
/// * `"lut"` - FEAT_LUT
|
||||
/// * `"mops"` - FEAT_MOPS
|
||||
/// * `"mte"` - FEAT_MTE & FEAT_MTE2
|
||||
/// * `"paca"` - FEAT_PAuth (address authentication)
|
||||
/// * `"pacg"` - FEAT_Pauth (generic authentication)
|
||||
/// * `"pauth-lr"` - FEAT_PAuth_LR
|
||||
/// * `"pmull"` - FEAT_PMULL
|
||||
/// * `"rand"` - FEAT_RNG
|
||||
/// * `"rcpc"` - FEAT_LRCPC
|
||||
/// * `"rcpc2"` - FEAT_LRCPC2
|
||||
/// * `"rcpc3"` - FEAT_LRCPC3
|
||||
/// * `"rdm"` - FEAT_RDM
|
||||
/// * `"sb"` - FEAT_SB
|
||||
/// * `"sha2"` - FEAT_SHA1 & FEAT_SHA256
|
||||
/// * `"sha3"` - FEAT_SHA512 & FEAT_SHA3
|
||||
/// * `"sm4"` - FEAT_SM3 & FEAT_SM4
|
||||
/// * `"sme"` - FEAT_SME
|
||||
/// * `"sme-b16b16"` - FEAT_SME_B16B16
|
||||
/// * `"sme-f16f16"` - FEAT_SME_F16F16
|
||||
/// * `"sme-f64f64"` - FEAT_SME_F64F64
|
||||
/// * `"sme-f8f16"` - FEAT_SME_F8F16
|
||||
/// * `"sme-f8f32"` - FEAT_SME_F8F32
|
||||
/// * `"sme-fa64"` - FEAT_SME_FA64
|
||||
/// * `"sme-i16i64"` - FEAT_SME_I16I64
|
||||
/// * `"sme-lutv2"` - FEAT_SME_LUTv2
|
||||
/// * `"sme2"` - FEAT_SME2
|
||||
/// * `"sme2p1"` - FEAT_SME2p1
|
||||
/// * `"ssbs"` - FEAT_SSBS & FEAT_SSBS2
|
||||
/// * `"ssve-fp8dot2"` - FEAT_SSVE_FP8DOT2
|
||||
/// * `"ssve-fp8dot4"` - FEAT_SSVE_FP8DOT4
|
||||
/// * `"ssve-fp8fma"` - FEAT_SSVE_FP8FMA
|
||||
/// * `"sve"` - FEAT_SVE
|
||||
/// * `"sve-b16b16"` - FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions)
|
||||
/// * `"sve2"` - FEAT_SVE2
|
||||
/// * `"sve2-aes"` - FEAT_SVE_AES & FEAT_SVE_PMULL128 (SVE2 AES crypto)
|
||||
/// * `"sve2-bitperm"` - FEAT_SVE2_BitPerm
|
||||
/// * `"sve2-sha3"` - FEAT_SVE2_SHA3
|
||||
/// * `"sve2-sm4"` - FEAT_SVE2_SM4
|
||||
/// * `"sve2p1"` - FEAT_SVE2p1
|
||||
/// * `"tme"` - FEAT_TME
|
||||
/// * `"wfxt"` - FEAT_WFxT
|
||||
///
|
||||
/// [docs]: https://developer.arm.com/documentation/ddi0487/latest
|
||||
#[stable(feature = "simd_aarch64", since = "1.60.0")]
|
||||
@BIND_FEATURE_NAME: "asimd"; "neon";
|
||||
@NO_RUNTIME_DETECTION: "ras";
|
||||
@NO_RUNTIME_DETECTION: "v8.1a";
|
||||
@NO_RUNTIME_DETECTION: "v8.2a";
|
||||
@NO_RUNTIME_DETECTION: "v8.3a";
|
||||
@NO_RUNTIME_DETECTION: "v8.4a";
|
||||
@NO_RUNTIME_DETECTION: "v8.5a";
|
||||
@NO_RUNTIME_DETECTION: "v8.6a";
|
||||
@NO_RUNTIME_DETECTION: "v8.7a";
|
||||
@NO_RUNTIME_DETECTION: "v8.8a";
|
||||
@NO_RUNTIME_DETECTION: "v8.9a";
|
||||
@NO_RUNTIME_DETECTION: "v9.1a";
|
||||
@NO_RUNTIME_DETECTION: "v9.2a";
|
||||
@NO_RUNTIME_DETECTION: "v9.3a";
|
||||
@NO_RUNTIME_DETECTION: "v9.4a";
|
||||
@NO_RUNTIME_DETECTION: "v9.5a";
|
||||
@NO_RUNTIME_DETECTION: "v9a";
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] asimd: "neon";
|
||||
/// FEAT_AdvSIMD (Advanced SIMD/NEON)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] pmull: "pmull";
|
||||
implied by target_features: ["aes"];
|
||||
/// FEAT_PMULL (Polynomial Multiply) - Implied by `aes` target_feature
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp: "fp";
|
||||
implied by target_features: ["neon"];
|
||||
/// FEAT_FP (Floating point support) - Implied by `neon` target_feature
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] aes: "aes";
|
||||
/// FEAT_AES (AES SIMD instructions) & FEAT_PMULL (PMULL{2}, 64-bit operand variants)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bf16: "bf16";
|
||||
/// FEAT_BF16 (BFloat16 type, plus MM instructions, plus ASIMD support)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bti: "bti";
|
||||
/// FEAT_BTI (Branch Target Identification)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] crc: "crc";
|
||||
/// FEAT_CRC32 (Cyclic Redundancy Check)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] cssc: "cssc";
|
||||
/// FEAT_CSSC (Common Short Sequence Compression instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dit: "dit";
|
||||
/// FEAT_DIT (Data Independent Timing instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb: "dpb";
|
||||
/// FEAT_DPB (aka dcpop - data cache clean to point of persistence)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb2: "dpb2";
|
||||
/// FEAT_DPB2 (aka dcpodp - data cache clean to point of deep persistence)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dotprod: "dotprod";
|
||||
/// FEAT_DotProd (Vector Dot-Product - ASIMDDP)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ecv: "ecv";
|
||||
/// FEAT_ECV (Enhanced Counter Virtualization)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f32mm: "f32mm";
|
||||
/// FEAT_F32MM (single-precision matrix multiplication)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f64mm: "f64mm";
|
||||
/// FEAT_F64MM (double-precision matrix multiplication)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] faminmax: "faminmax";
|
||||
/// FEAT_FAMINMAX (FAMIN and FAMAX SIMD/SVE/SME instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fcma: "fcma";
|
||||
/// FEAT_FCMA (float complex number operations)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fhm: "fhm";
|
||||
/// FEAT_FHM (fp16 multiplication instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] flagm: "flagm";
|
||||
/// FEAT_FLAGM (flag manipulation instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] flagm2: "flagm2";
|
||||
/// FEAT_FLAGM2 (flag manipulation instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp16: "fp16";
|
||||
/// FEAT_FP16 (Half-float support)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8: "fp8";
|
||||
/// FEAT_FP8 (F8CVT Instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8dot2: "fp8dot2";
|
||||
/// FEAT_FP8DOT2 (F8DP2 Instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8dot4: "fp8dot4";
|
||||
/// FEAT_FP8DOT4 (F8DP4 Instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8fma: "fp8fma";
|
||||
/// FEAT_FP8FMA (F8FMA Instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fpmr: "fpmr";
|
||||
without cfg check: true;
|
||||
/// FEAT_FPMR (Special-purpose AArch64-FPMR register)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] frintts: "frintts";
|
||||
/// FEAT_FRINTTS (float to integer rounding instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] hbc: "hbc";
|
||||
/// FEAT_HBC (Hinted conditional branches)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] i8mm: "i8mm";
|
||||
/// FEAT_I8MM (integer matrix multiplication, plus ASIMD support)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] jsconv: "jsconv";
|
||||
/// FEAT_JSCVT (JavaScript float conversion instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse: "lse";
|
||||
/// FEAT_LSE (Large System Extension - atomics)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] lse128: "lse128";
|
||||
/// FEAT_LSE128 (128-bit atomics)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse2: "lse2";
|
||||
/// FEAT_LSE2 (unaligned and register-pair atomics)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] lut: "lut";
|
||||
/// FEAT_LUT (Lookup Table Instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] mops: "mops";
|
||||
/// FEAT_MOPS (Standardization of memory operations)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] mte: "mte";
|
||||
/// FEAT_MTE & FEAT_MTE2 (Memory Tagging Extension)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] paca: "paca";
|
||||
/// FEAT_PAuth (address authentication)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] pacg: "pacg";
|
||||
/// FEAT_PAuth (generic authentication)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] pauth_lr: "pauth-lr";
|
||||
/// FEAT_PAuth_LR
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rand: "rand";
|
||||
/// FEAT_RNG (Random Number Generator)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc: "rcpc";
|
||||
/// FEAT_LRCPC (Release consistent Processor consistent)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc2: "rcpc2";
|
||||
/// FEAT_LRCPC2 (RCPC with immediate offsets)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] rcpc3: "rcpc3";
|
||||
/// FEAT_LRCPC3 (RCPC Instructions v3)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rdm: "rdm";
|
||||
/// FEAT_RDM (Rounding Doubling Multiply - ASIMDRDM)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sb: "sb";
|
||||
/// FEAT_SB (speculation barrier)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha2: "sha2";
|
||||
/// FEAT_SHA1 & FEAT_SHA256 (SHA1 & SHA2-256 instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha3: "sha3";
|
||||
/// FEAT_SHA512 & FEAT_SHA3 (SHA2-512 & SHA3 instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sm4: "sm4";
|
||||
/// FEAT_SM3 & FEAT_SM4 (SM3 & SM4 instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme: "sme";
|
||||
/// FEAT_SME (Scalable Matrix Extension)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme2: "sme2";
|
||||
/// FEAT_SME2 (SME Version 2)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme2p1: "sme2p1";
|
||||
/// FEAT_SME2p1 (SME Version 2.1)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_b16b16: "sme-b16b16";
|
||||
/// FEAT_SME_B16B16
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f16f16: "sme-f16f16";
|
||||
/// FEAT_SME_F16F16 (Non-widening half-precision FP16 to FP16 arithmetic for SME2)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f64f64: "sme-f64f64";
|
||||
/// FEAT_SME_F64F64 (Double-precision floating-point outer product instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f8f16: "sme-f8f16";
|
||||
/// FEAT_SME_F8F16
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f8f32: "sme-f8f32";
|
||||
/// FEAT_SME_F8F32
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_fa64: "sme-fa64";
|
||||
/// FEAT_SME_FA64 (Full A64 instruction set support in Streaming SVE mode)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_i16i64: "sme-i16i64";
|
||||
/// FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_lutv2: "sme-lutv2";
|
||||
/// FEAT_SME_LUTv2 (LUTI4 Instruction)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] ssbs: "ssbs";
|
||||
/// FEAT_SSBS & FEAT_SSBS2 (speculative store bypass safe)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ssve_fp8dot2: "ssve-fp8dot2";
|
||||
/// FEAT_SSVE_FP8DOT2
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ssve_fp8dot4: "ssve-fp8dot4";
|
||||
/// FEAT_SSVE_FP8DOT4
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ssve_fp8fma: "ssve-fp8fma";
|
||||
/// FEAT_SSVE_FP8FMA
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve: "sve";
|
||||
/// FEAT_SVE (Scalable Vector Extension)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2: "sve2";
|
||||
/// FEAT_SVE2 (Scalable Vector Extension 2)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sve2p1: "sve2p1";
|
||||
/// FEAT_SVE2p1 (Scalable Vector Extension 2.1)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_aes: "sve2-aes";
|
||||
/// FEAT_SVE_AES & FEAT_SVE_PMULL128 (SVE2 AES crypto)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sve_b16b16: "sve-b16b16";
|
||||
/// FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_bitperm: "sve2-bitperm";
|
||||
/// FEAT_SVE_BitPerm (SVE2 bit permutation instructions)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sha3: "sve2-sha3";
|
||||
/// FEAT_SVE_SHA3 (SVE2 SHA3 crypto)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sm4: "sve2-sm4";
|
||||
/// FEAT_SVE_SM4 (SVE2 SM4 crypto)
|
||||
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] tme: "tme";
|
||||
/// FEAT_TME (Transactional Memory Extensions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] wfxt: "wfxt";
|
||||
/// FEAT_WFxT (WFET and WFIT Instructions)
|
||||
}
|
||||
32
crates/std/crates/std_detect/src/detect/arch/arm.rs
Normal file
32
crates/std/crates/std_detect/src/detect/arch/arm.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
//! Run-time feature detection on ARM Aarch32.
|
||||
|
||||
features! {
|
||||
@TARGET: arm;
|
||||
@CFG: target_arch = "arm";
|
||||
@MACRO_NAME: is_arm_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
#[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")]
|
||||
@NO_RUNTIME_DETECTION: "v7";
|
||||
@NO_RUNTIME_DETECTION: "vfp2";
|
||||
@NO_RUNTIME_DETECTION: "vfp3";
|
||||
@NO_RUNTIME_DETECTION: "vfp4";
|
||||
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] neon: "neon";
|
||||
/// ARM Advanced SIMD (NEON) - Aarch32
|
||||
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] pmull: "pmull";
|
||||
without cfg check: true;
|
||||
/// Polynomial Multiply
|
||||
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] crc: "crc";
|
||||
/// CRC32 (Cyclic Redundancy Check)
|
||||
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] aes: "aes";
|
||||
/// FEAT_AES (AES instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] sha2: "sha2";
|
||||
/// FEAT_SHA1 & FEAT_SHA256 (SHA1 & SHA2-256 instructions)
|
||||
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] i8mm: "i8mm";
|
||||
/// FEAT_I8MM (integer matrix multiplication, plus ASIMD support)
|
||||
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] dotprod: "dotprod";
|
||||
/// FEAT_DotProd (Vector Dot-Product - ASIMDDP)
|
||||
}
|
||||
58
crates/std/crates/std_detect/src/detect/arch/loongarch.rs
Normal file
58
crates/std/crates/std_detect/src/detect/arch/loongarch.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
//! Run-time feature detection on LoongArch.
|
||||
|
||||
features! {
|
||||
@TARGET: loongarch;
|
||||
@CFG: any(target_arch = "loongarch32", target_arch = "loongarch64");
|
||||
@MACRO_NAME: is_loongarch_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
///
|
||||
/// Supported arguments are:
|
||||
///
|
||||
/// * `"32s"`
|
||||
/// * `"f"`
|
||||
/// * `"d"`
|
||||
/// * `"frecipe"`
|
||||
/// * `"div32"`
|
||||
/// * `"lsx"`
|
||||
/// * `"lasx"`
|
||||
/// * `"lam-bh"`
|
||||
/// * `"lamcas"`
|
||||
/// * `"ld-seq-sa"`
|
||||
/// * `"scq"`
|
||||
/// * `"lbt"`
|
||||
/// * `"lvz"`
|
||||
/// * `"ual"`
|
||||
#[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")]
|
||||
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] _32s: "32s";
|
||||
/// 32S
|
||||
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] f: "f";
|
||||
/// F
|
||||
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] d: "d";
|
||||
/// D
|
||||
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] frecipe: "frecipe";
|
||||
/// Frecipe
|
||||
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] div32: "div32";
|
||||
/// Div32
|
||||
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lsx: "lsx";
|
||||
/// LSX
|
||||
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lasx: "lasx";
|
||||
/// LASX
|
||||
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] lam_bh: "lam-bh";
|
||||
/// LAM-BH
|
||||
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] lamcas: "lamcas";
|
||||
/// LAM-CAS
|
||||
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] ld_seq_sa: "ld-seq-sa";
|
||||
/// LD-SEQ-SA
|
||||
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] scq: "scq";
|
||||
/// SCQ
|
||||
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lbt: "lbt";
|
||||
/// LBT
|
||||
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lvz: "lvz";
|
||||
/// LVZ
|
||||
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] ual: "ual";
|
||||
/// UAL
|
||||
}
|
||||
15
crates/std/crates/std_detect/src/detect/arch/mips.rs
Normal file
15
crates/std/crates/std_detect/src/detect/arch/mips.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
//! Run-time feature detection on MIPS.
|
||||
|
||||
features! {
|
||||
@TARGET: mips;
|
||||
@CFG: target_arch = "mips";
|
||||
@MACRO_NAME: is_mips_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
#[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")]
|
||||
@FEATURE: #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] msa: "msa";
|
||||
/// MIPS SIMD Architecture (MSA)
|
||||
}
|
||||
15
crates/std/crates/std_detect/src/detect/arch/mips64.rs
Normal file
15
crates/std/crates/std_detect/src/detect/arch/mips64.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
//! Run-time feature detection on MIPS64.
|
||||
|
||||
features! {
|
||||
@TARGET: mips64;
|
||||
@CFG: target_arch = "mips64";
|
||||
@MACRO_NAME: is_mips64_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
#[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")]
|
||||
@FEATURE: #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] msa: "msa";
|
||||
/// MIPS SIMD Architecture (MSA)
|
||||
}
|
||||
83
crates/std/crates/std_detect/src/detect/arch/mod.rs
Normal file
83
crates/std/crates/std_detect/src/detect/arch/mod.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
// Export the macros for all supported architectures.
|
||||
#[macro_use]
|
||||
mod x86;
|
||||
#[macro_use]
|
||||
mod arm;
|
||||
#[macro_use]
|
||||
mod aarch64;
|
||||
#[macro_use]
|
||||
mod riscv;
|
||||
#[macro_use]
|
||||
mod powerpc;
|
||||
#[macro_use]
|
||||
mod powerpc64;
|
||||
#[macro_use]
|
||||
mod mips;
|
||||
#[macro_use]
|
||||
mod mips64;
|
||||
#[macro_use]
|
||||
mod loongarch;
|
||||
#[macro_use]
|
||||
mod s390x;
|
||||
|
||||
cfg_select! {
|
||||
any(target_arch = "x86", target_arch = "x86_64") => {
|
||||
#[stable(feature = "simd_x86", since = "1.27.0")]
|
||||
pub use x86::*;
|
||||
}
|
||||
target_arch = "arm" => {
|
||||
#[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")]
|
||||
pub use arm::*;
|
||||
}
|
||||
any(target_arch = "aarch64", target_arch = "arm64ec") => {
|
||||
#[stable(feature = "simd_aarch64", since = "1.60.0")]
|
||||
pub use aarch64::*;
|
||||
}
|
||||
any(target_arch = "riscv32", target_arch = "riscv64") => {
|
||||
#[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")]
|
||||
pub use riscv::*;
|
||||
}
|
||||
target_arch = "powerpc" => {
|
||||
#[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")]
|
||||
pub use powerpc::*;
|
||||
}
|
||||
target_arch = "powerpc64" => {
|
||||
#[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")]
|
||||
pub use powerpc64::*;
|
||||
}
|
||||
target_arch = "mips" => {
|
||||
#[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")]
|
||||
pub use mips::*;
|
||||
}
|
||||
target_arch = "mips64" => {
|
||||
#[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")]
|
||||
pub use mips64::*;
|
||||
}
|
||||
any(target_arch = "loongarch32", target_arch = "loongarch64") => {
|
||||
#[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")]
|
||||
pub use loongarch::*;
|
||||
}
|
||||
target_arch = "s390x" => {
|
||||
#[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")]
|
||||
pub use s390x::*;
|
||||
}
|
||||
_ => {
|
||||
// Unimplemented architecture:
|
||||
#[doc(hidden)]
|
||||
pub(crate) enum Feature {
|
||||
Null
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "stdarch_internal", issue = "none")]
|
||||
pub mod __is_feature_detected {}
|
||||
|
||||
impl Feature {
|
||||
#[doc(hidden)]
|
||||
pub(crate) fn from_str(_s: &str) -> Result<Feature, ()> { Err(()) }
|
||||
#[doc(hidden)]
|
||||
pub(crate) fn to_str(self) -> &'static str { "" }
|
||||
}
|
||||
}
|
||||
}
|
||||
33
crates/std/crates/std_detect/src/detect/arch/powerpc.rs
Normal file
33
crates/std/crates/std_detect/src/detect/arch/powerpc.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
//! Run-time feature detection on PowerPC.
|
||||
|
||||
features! {
|
||||
@TARGET: powerpc;
|
||||
@CFG: target_arch = "powerpc";
|
||||
@MACRO_NAME: is_powerpc_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
#[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")]
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] altivec: "altivec";
|
||||
/// Altivec
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] vsx: "vsx";
|
||||
/// VSX
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8: "power8";
|
||||
without cfg check: true;
|
||||
/// Power8
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_altivec: "power8-altivec";
|
||||
/// Power8 altivec
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_vector: "power8-vector";
|
||||
/// Power8 vector
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_crypto: "power8-crypto";
|
||||
/// Power8 crypto
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9: "power9";
|
||||
without cfg check: true;
|
||||
/// Power9
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_altivec: "power9-altivec";
|
||||
/// Power9 altivec
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_vector: "power9-vector";
|
||||
/// Power9 vector
|
||||
}
|
||||
33
crates/std/crates/std_detect/src/detect/arch/powerpc64.rs
Normal file
33
crates/std/crates/std_detect/src/detect/arch/powerpc64.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
//! Run-time feature detection on PowerPC64.
|
||||
|
||||
features! {
|
||||
@TARGET: powerpc64;
|
||||
@CFG: target_arch = "powerpc64";
|
||||
@MACRO_NAME: is_powerpc64_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
#[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")]
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] altivec: "altivec";
|
||||
/// Altivec
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] vsx: "vsx";
|
||||
/// VSX
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8: "power8";
|
||||
without cfg check: true;
|
||||
/// Power8
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_altivec: "power8-altivec";
|
||||
/// Power8 altivec
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_vector: "power8-vector";
|
||||
/// Power8 vector
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_crypto: "power8-crypto";
|
||||
/// Power8 crypto
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9: "power9";
|
||||
without cfg check: true;
|
||||
/// Power9
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_altivec: "power9-altivec";
|
||||
/// Power9 altivec
|
||||
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_vector: "power9-vector";
|
||||
/// Power9 vector
|
||||
}
|
||||
380
crates/std/crates/std_detect/src/detect/arch/riscv.rs
Normal file
380
crates/std/crates/std_detect/src/detect/arch/riscv.rs
Normal file
@@ -0,0 +1,380 @@
|
||||
//! Run-time feature detection on RISC-V.
|
||||
|
||||
features! {
|
||||
@TARGET: riscv;
|
||||
@CFG: any(target_arch = "riscv32", target_arch = "riscv64");
|
||||
@MACRO_NAME: is_riscv_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
///
|
||||
/// RISC-V standard defined the base sets and the extension sets.
|
||||
/// The base sets are RV32I, RV64I, RV32E or RV128I. Any RISC-V platform
|
||||
/// must support one base set and/or multiple extension sets.
|
||||
///
|
||||
/// Any RISC-V standard instruction sets can be in state of either ratified,
|
||||
/// frozen or draft. The version and status of current standard instruction
|
||||
/// sets can be checked out from preface section of the [ISA manual].
|
||||
///
|
||||
/// Platform may define and support their own custom instruction sets with
|
||||
/// ISA prefix X. These sets are highly platform specific and should be
|
||||
/// detected with their own platform support crates.
|
||||
///
|
||||
/// [ISA manual]: https://riscv.org/specifications/ratified/
|
||||
///
|
||||
/// # Platform-specific/agnostic Behavior and Availability
|
||||
///
|
||||
/// Runtime detection depends on the platform-specific feature detection
|
||||
/// facility and its availability per feature is
|
||||
/// highly platform/version-specific.
|
||||
///
|
||||
/// Still, a best-effort attempt is performed to enable subset/dependent
|
||||
/// features if a superset feature is enabled regardless of the platform.
|
||||
/// For instance, if the A extension (`"a"`) is enabled, its subsets (the
|
||||
/// Zalrsc and Zaamo extensions; `"zalrsc"` and `"zaamo"`) are also enabled.
|
||||
/// Likewise, if the F extension (`"f"`) is enabled, one of its dependencies
|
||||
/// (the Zicsr extension `"zicsr"`) is also enabled.
|
||||
///
|
||||
/// # Unprivileged Specification
|
||||
///
|
||||
/// The supported ratified RISC-V instruction sets are as follows (OS
|
||||
/// columns denote runtime feature detection support with or without the
|
||||
/// minimum supported version):
|
||||
///
|
||||
/// | Literal | Base | Linux |
|
||||
/// |:---------- |:------- |:---------- |
|
||||
/// | `"rv32e"` | RV32E | No |
|
||||
/// | `"rv32i"` | RV32I | Yes [^ima] |
|
||||
/// | `"rv64i"` | RV64I | Yes [^ima] |
|
||||
///
|
||||
/// | Literal | Extension | Linux |
|
||||
/// |:--------------- |:----------- |:------------------- |
|
||||
/// | `"a"` | A | Yes [^ima] |
|
||||
/// | `"b"` | B | 6.5 |
|
||||
/// | `"c"` | C | Yes |
|
||||
/// | `"d"` | D | Yes |
|
||||
/// | `"f"` | F | Yes |
|
||||
/// | `"m"` | M | Yes [^ima] |
|
||||
/// | `"q"` | Q | No |
|
||||
/// | `"v"` | V | 6.5 |
|
||||
/// | `"zaamo"` | Zaamo | 6.15 [^ima] [^dep] |
|
||||
/// | `"zabha"` | Zabha | 6.16 |
|
||||
/// | `"zacas"` | Zacas | 6.8 |
|
||||
/// | `"zalrsc"` | Zalrsc | 6.15 [^ima] [^dep] |
|
||||
/// | `"zawrs"` | Zawrs | 6.11 |
|
||||
/// | `"zba"` | Zba | 6.5 |
|
||||
/// | `"zbb"` | Zbb | 6.5 |
|
||||
/// | `"zbc"` | Zbc | 6.8 |
|
||||
/// | `"zbkb"` | Zbkb | 6.8 |
|
||||
/// | `"zbkc"` | Zbkc | 6.8 |
|
||||
/// | `"zbkx"` | Zbkx | 6.8 |
|
||||
/// | `"zbs"` | Zbs | 6.5 |
|
||||
/// | `"zca"` | Zca | 6.11 [^dep] |
|
||||
/// | `"zcb"` | Zcb | 6.11 |
|
||||
/// | `"zcd"` | Zcd | 6.11 [^dep] |
|
||||
/// | `"zcf"` | Zcf | 6.11 [^dep] |
|
||||
/// | `"zcmop"` | Zcmop | 6.11 |
|
||||
/// | `"zdinx"` | Zdinx | No |
|
||||
/// | `"zfa"` | Zfa | 6.8 |
|
||||
/// | `"zfbfmin"` | Zfbfmin | 6.15 |
|
||||
/// | `"zfh"` | Zfh | 6.8 |
|
||||
/// | `"zfhmin"` | Zfhmin | 6.8 |
|
||||
/// | `"zfinx"` | Zfinx | No |
|
||||
/// | `"zhinx"` | Zhinx | No |
|
||||
/// | `"zhinxmin"` | Zhinxmin | No |
|
||||
/// | `"zicbom"` | Zicbom | 6.15 |
|
||||
/// | `"zicboz"` | Zicboz | 6.7 |
|
||||
/// | `"zicntr"` | Zicntr | 6.15 [^ima] [^cntr] |
|
||||
/// | `"zicond"` | Zicond | 6.8 |
|
||||
/// | `"zicsr"` | Zicsr | No [^ima] [^dep] |
|
||||
/// | `"zifencei"` | Zifencei | No [^ima] |
|
||||
/// | `"zihintntl"` | Zihintntl | 6.8 |
|
||||
/// | `"zihintpause"` | Zihintpause | 6.10 |
|
||||
/// | `"zihpm"` | Zihpm | 6.15 [^cntr] |
|
||||
/// | `"zimop"` | Zimop | 6.11 |
|
||||
/// | `"zk"` | Zk | No [^zkr] |
|
||||
/// | `"zkn"` | Zkn | 6.8 |
|
||||
/// | `"zknd"` | Zknd | 6.8 |
|
||||
/// | `"zkne"` | Zkne | 6.8 |
|
||||
/// | `"zknh"` | Zknh | 6.8 |
|
||||
/// | `"zkr"` | Zkr | No [^zkr] |
|
||||
/// | `"zks"` | Zks | 6.8 |
|
||||
/// | `"zksed"` | Zksed | 6.8 |
|
||||
/// | `"zksh"` | Zksh | 6.8 |
|
||||
/// | `"zkt"` | Zkt | 6.8 |
|
||||
/// | `"ztso"` | Ztso | 6.8 |
|
||||
/// | `"zvbb"` | Zvbb | 6.8 |
|
||||
/// | `"zvbc"` | Zvbc | 6.8 |
|
||||
/// | `"zve32f"` | Zve32f | 6.11 [^dep] |
|
||||
/// | `"zve32x"` | Zve32x | 6.11 [^dep] |
|
||||
/// | `"zve64d"` | Zve64d | 6.11 [^dep] |
|
||||
/// | `"zve64f"` | Zve64f | 6.11 [^dep] |
|
||||
/// | `"zve64x"` | Zve64x | 6.11 [^dep] |
|
||||
/// | `"zvfbfmin"` | Zvfbfmin | 6.15 |
|
||||
/// | `"zvfbfwma"` | Zvfbfwma | 6.15 |
|
||||
/// | `"zvfh"` | Zvfh | 6.8 |
|
||||
/// | `"zvfhmin"` | Zvfhmin | 6.8 |
|
||||
/// | `"zvkb"` | Zvkb | 6.8 |
|
||||
/// | `"zvkg"` | Zvkg | 6.8 |
|
||||
/// | `"zvkn"` | Zvkn | 6.8 |
|
||||
/// | `"zvknc"` | Zvknc | 6.8 |
|
||||
/// | `"zvkned"` | Zvkned | 6.8 |
|
||||
/// | `"zvkng"` | Zvkng | 6.8 |
|
||||
/// | `"zvknha"` | Zvknha | 6.8 |
|
||||
/// | `"zvknhb"` | Zvknhb | 6.8 |
|
||||
/// | `"zvks"` | Zvks | 6.8 |
|
||||
/// | `"zvksc"` | Zvksc | 6.8 |
|
||||
/// | `"zvksed"` | Zvksed | 6.8 |
|
||||
/// | `"zvksg"` | Zvksg | 6.8 |
|
||||
/// | `"zvksh"` | Zvksh | 6.8 |
|
||||
/// | `"zvkt"` | Zvkt | 6.8 |
|
||||
///
|
||||
/// [^ima]: Or enabled when the IMA base behavior is detected on the Linux
|
||||
/// kernel version 6.4 or later (for bases, the only matching one -- either
|
||||
/// `"rv32i"` or `"rv64i"` -- is enabled).
|
||||
///
|
||||
/// [^cntr]: Even if this extension is available, it does not necessarily
|
||||
/// mean all performance counters are accessible.
|
||||
/// For example, accesses to all performance counters except `time`
|
||||
/// (wall-clock) are blocked by default on the Linux kernel
|
||||
/// version 6.6 or later.
|
||||
/// Also beware that, even if performance counters like `cycle` and
|
||||
/// `instret` are accessible, their value can be unreliable (e.g. returning
|
||||
/// the constant value) under certain circumstances.
|
||||
///
|
||||
/// [^dep]: Or enabled as a dependency of another extension (a superset)
|
||||
/// even if runtime detection of this feature itself is not supported (as
|
||||
/// long as the runtime detection of the superset is supported).
|
||||
///
|
||||
/// [^zkr]: Linux does not report existence of this extension even if
|
||||
/// supported by the hardware mainly because the `seed` CSR on the Zkr
|
||||
/// extension (which provides hardware-based randomness) is normally
|
||||
/// inaccessible from the user mode.
|
||||
/// For the Zk extension features except this CSR, check existence of both
|
||||
/// `"zkn"` and `"zkt"` features instead.
|
||||
///
|
||||
/// There's also bases and extensions marked as standard instruction set,
|
||||
/// but they are in frozen or draft state. These instruction sets are also
|
||||
/// reserved by this macro and can be detected in the future platforms.
|
||||
///
|
||||
/// Draft RISC-V instruction sets:
|
||||
///
|
||||
/// * RV128I: `"rv128i"`
|
||||
/// * J: `"j"`
|
||||
/// * P: `"p"`
|
||||
/// * Zam: `"zam"`
|
||||
///
|
||||
/// # Performance Hints
|
||||
///
|
||||
/// The two features below define performance hints for unaligned
|
||||
/// scalar/vector memory accesses, respectively. If enabled, it denotes that
|
||||
/// corresponding unaligned memory access is reasonably fast.
|
||||
///
|
||||
/// * `"unaligned-scalar-mem"`
|
||||
/// * Runtime detection requires Linux kernel version 6.4 or later.
|
||||
/// * `"unaligned-vector-mem"`
|
||||
/// * Runtime detection requires Linux kernel version 6.13 or later.
|
||||
#[stable(feature = "riscv_ratified", since = "1.78.0")]
|
||||
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv32i: "rv32i";
|
||||
without cfg check: true;
|
||||
/// RV32I Base Integer Instruction Set
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv32e: "rv32e";
|
||||
without cfg check: true;
|
||||
/// RV32E Base Integer Instruction Set
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv64i: "rv64i";
|
||||
without cfg check: true;
|
||||
/// RV64I Base Integer Instruction Set
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv128i: "rv128i";
|
||||
without cfg check: true;
|
||||
/// RV128I Base Integer Instruction Set
|
||||
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_scalar_mem: "unaligned-scalar-mem";
|
||||
/// Has reasonably performant unaligned scalar
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_vector_mem: "unaligned-vector-mem";
|
||||
/// Has reasonably performant unaligned vector
|
||||
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicsr: "zicsr";
|
||||
/// "Zicsr" Extension for Control and Status Register (CSR) Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicntr: "zicntr";
|
||||
/// "Zicntr" Extension for Base Counters and Timers
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zihpm: "zihpm";
|
||||
/// "Zihpm" Extension for Hardware Performance Counters
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zifencei: "zifencei";
|
||||
/// "Zifencei" Extension for Instruction-Fetch Fence
|
||||
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zihintntl: "zihintntl";
|
||||
/// "Zihintntl" Extension for Non-Temporal Locality Hints
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zihintpause: "zihintpause";
|
||||
/// "Zihintpause" Extension for Pause Hint
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zimop: "zimop";
|
||||
/// "Zimop" Extension for May-Be-Operations
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicbom: "zicbom";
|
||||
/// "Zicbom" Extension for Cache-Block Management Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicboz: "zicboz";
|
||||
/// "Zicboz" Extension for Cache-Block Zero Instruction
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicond: "zicond";
|
||||
/// "Zicond" Extension for Integer Conditional Operations
|
||||
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] m: "m";
|
||||
/// "M" Extension for Integer Multiplication and Division
|
||||
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] a: "a";
|
||||
/// "A" Extension for Atomic Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zalrsc: "zalrsc";
|
||||
/// "Zalrsc" Extension for Load-Reserved/Store-Conditional Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zaamo: "zaamo";
|
||||
/// "Zaamo" Extension for Atomic Memory Operations
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zawrs: "zawrs";
|
||||
/// "Zawrs" Extension for Wait-on-Reservation-Set Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zabha: "zabha";
|
||||
/// "Zabha" Extension for Byte and Halfword Atomic Memory Operations
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zacas: "zacas";
|
||||
/// "Zacas" Extension for Atomic Compare-and-Swap (CAS) Instructions
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zam: "zam";
|
||||
without cfg check: true;
|
||||
/// "Zam" Extension for Misaligned Atomics
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] ztso: "ztso";
|
||||
/// "Ztso" Extension for Total Store Ordering
|
||||
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] f: "f";
|
||||
/// "F" Extension for Single-Precision Floating-Point
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] d: "d";
|
||||
/// "D" Extension for Double-Precision Floating-Point
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] q: "q";
|
||||
without cfg check: true;
|
||||
/// "Q" Extension for Quad-Precision Floating-Point
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfh: "zfh";
|
||||
/// "Zfh" Extension for Half-Precision Floating-Point
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfhmin: "zfhmin";
|
||||
/// "Zfhmin" Extension for Minimal Half-Precision Floating-Point
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfa: "zfa";
|
||||
/// "Zfa" Extension for Additional Floating-Point Instructions
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfbfmin: "zfbfmin";
|
||||
/// "Zfbfmin" Extension for Scalar BF16 Converts
|
||||
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfinx: "zfinx";
|
||||
/// "Zfinx" Extension for Single-Precision Floating-Point in Integer Registers
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zdinx: "zdinx";
|
||||
/// "Zdinx" Extension for Double-Precision Floating-Point in Integer Registers
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zhinx: "zhinx";
|
||||
/// "Zhinx" Extension for Half-Precision Floating-Point in Integer Registers
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zhinxmin: "zhinxmin";
|
||||
/// "Zhinxmin" Extension for Minimal Half-Precision Floating-Point in Integer Registers
|
||||
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] c: "c";
|
||||
/// "C" Extension for Compressed Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zca: "zca";
|
||||
/// "Zca" Compressed Instructions excluding Floating-Point Loads/Stores
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcf: "zcf";
|
||||
without cfg check: true;
|
||||
/// "Zcf" Compressed Instructions for Single-Precision Floating-Point Loads/Stores on RV32
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcd: "zcd";
|
||||
without cfg check: true;
|
||||
/// "Zcd" Compressed Instructions for Double-Precision Floating-Point Loads/Stores
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zcb: "zcb";
|
||||
/// "Zcb" Simple Code-size Saving Compressed Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zcmop: "zcmop";
|
||||
/// "Zcmop" Extension for Compressed May-Be-Operations
|
||||
|
||||
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] b: "b";
|
||||
/// "B" Extension for Bit Manipulation
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zba: "zba";
|
||||
/// "Zba" Extension for Address Generation
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbb: "zbb";
|
||||
/// "Zbb" Extension for Basic Bit-Manipulation
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbc: "zbc";
|
||||
/// "Zbc" Extension for Carry-less Multiplication
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbs: "zbs";
|
||||
/// "Zbs" Extension for Single-Bit Instructions
|
||||
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbkb: "zbkb";
|
||||
/// "Zbkb" Extension for Bit-Manipulation for Cryptography
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbkc: "zbkc";
|
||||
/// "Zbkc" Extension for Carry-less Multiplication for Cryptography
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbkx: "zbkx";
|
||||
/// "Zbkx" Extension for Crossbar Permutations
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zknd: "zknd";
|
||||
/// "Zknd" Cryptography Extension for NIST Suite: AES Decryption
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkne: "zkne";
|
||||
/// "Zkne" Cryptography Extension for NIST Suite: AES Encryption
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zknh: "zknh";
|
||||
/// "Zknh" Cryptography Extension for NIST Suite: Hash Function Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zksed: "zksed";
|
||||
/// "Zksed" Cryptography Extension for ShangMi Suite: SM4 Block Cipher Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zksh: "zksh";
|
||||
/// "Zksh" Cryptography Extension for ShangMi Suite: SM3 Hash Function Instructions
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkr: "zkr";
|
||||
/// "Zkr" Entropy Source Extension
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkn: "zkn";
|
||||
/// "Zkn" Cryptography Extension for NIST Algorithm Suite
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zks: "zks";
|
||||
/// "Zks" Cryptography Extension for ShangMi Algorithm Suite
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zk: "zk";
|
||||
/// "Zk" Cryptography Extension for Standard Scalar Cryptography
|
||||
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkt: "zkt";
|
||||
/// "Zkt" Cryptography Extension for Data Independent Execution Latency
|
||||
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] v: "v";
|
||||
/// "V" Extension for Vector Operations
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32x: "zve32x";
|
||||
/// "Zve32x" Vector Extension for Embedded Processors (32-bit+; Integer)
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32f: "zve32f";
|
||||
/// "Zve32f" Vector Extension for Embedded Processors (32-bit+; with Single-Precision Floating-Point)
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64x: "zve64x";
|
||||
/// "Zve64x" Vector Extension for Embedded Processors (64-bit+; Integer)
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64f: "zve64f";
|
||||
/// "Zve64f" Vector Extension for Embedded Processors (64-bit+; with Single-Precision Floating-Point)
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64d: "zve64d";
|
||||
/// "Zve64d" Vector Extension for Embedded Processors (64-bit+; with Double-Precision Floating-Point)
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfh: "zvfh";
|
||||
/// "Zvfh" Vector Extension for Half-Precision Floating-Point
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfhmin: "zvfhmin";
|
||||
/// "Zvfhmin" Vector Extension for Minimal Half-Precision Floating-Point
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfbfmin: "zvfbfmin";
|
||||
/// "Zvfbfmin" Vector Extension for BF16 Converts
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfbfwma: "zvfbfwma";
|
||||
/// "Zvfbfwma" Vector Extension for BF16 Widening Multiply-Add
|
||||
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbb: "zvbb";
|
||||
/// "Zvbb" Extension for Vector Basic Bit-Manipulation
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbc: "zvbc";
|
||||
/// "Zvbc" Extension for Vector Carryless Multiplication
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkb: "zvkb";
|
||||
/// "Zvkb" Extension for Vector Cryptography Bit-Manipulation
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkg: "zvkg";
|
||||
/// "Zvkg" Cryptography Extension for Vector GCM/GMAC
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkned: "zvkned";
|
||||
/// "Zvkned" Cryptography Extension for NIST Suite: Vector AES Block Cipher
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknha: "zvknha";
|
||||
/// "Zvknha" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256)
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknhb: "zvknhb";
|
||||
/// "Zvknhb" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256/512)
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksed: "zvksed";
|
||||
/// "Zvksed" Cryptography Extension for ShangMi Suite: Vector SM4 Block Cipher
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksh: "zvksh";
|
||||
/// "Zvksh" Cryptography Extension for ShangMi Suite: Vector SM3 Secure Hash
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkn: "zvkn";
|
||||
/// "Zvkn" Cryptography Extension for NIST Algorithm Suite
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknc: "zvknc";
|
||||
/// "Zvknc" Cryptography Extension for NIST Algorithm Suite with Carryless Multiply
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkng: "zvkng";
|
||||
/// "Zvkng" Cryptography Extension for NIST Algorithm Suite with GCM
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvks: "zvks";
|
||||
/// "Zvks" Cryptography Extension for ShangMi Algorithm Suite
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksc: "zvksc";
|
||||
/// "Zvksc" Cryptography Extension for ShangMi Algorithm Suite with Carryless Multiply
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksg: "zvksg";
|
||||
/// "Zvksg" Cryptography Extension for ShangMi Algorithm Suite with GCM
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkt: "zvkt";
|
||||
/// "Zvkt" Extension for Vector Data-Independent Execution Latency
|
||||
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] j: "j";
|
||||
without cfg check: true;
|
||||
/// "J" Extension for Dynamically Translated Languages
|
||||
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] p: "p";
|
||||
without cfg check: true;
|
||||
/// "P" Extension for Packed-SIMD Instructions
|
||||
}
|
||||
61
crates/std/crates/std_detect/src/detect/arch/s390x.rs
Normal file
61
crates/std/crates/std_detect/src/detect/arch/s390x.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
//! Run-time feature detection on s390x.
|
||||
|
||||
features! {
|
||||
@TARGET: s390x;
|
||||
@CFG: target_arch = "s390x";
|
||||
@MACRO_NAME: is_s390x_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
#[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")]
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] concurrent_functions: "concurrent-functions";
|
||||
/// s390x concurrent-functions facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] deflate_conversion: "deflate-conversion";
|
||||
/// s390x deflate-conversion facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] enhanced_sort: "enhanced-sort";
|
||||
/// s390x enhanced-sort facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] guarded_storage: "guarded-storage";
|
||||
/// s390x guarded-storage facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] high_word: "high-word";
|
||||
/// s390x high-word facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension3: "message-security-assist-extension3";
|
||||
/// s390x message-security-assist-extension3 facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension4: "message-security-assist-extension4";
|
||||
/// s390x message-security-assist-extension4 facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension5: "message-security-assist-extension5";
|
||||
/// s390x message-security-assist-extension5 facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension8: "message-security-assist-extension8";
|
||||
/// s390x message-security-assist-extension8 facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension9: "message-security-assist-extension9";
|
||||
/// s390x message-security-assist-extension9 facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension12: "message-security-assist-extension12";
|
||||
/// s390x message-security-assist-extension12 facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_2: "miscellaneous-extensions-2";
|
||||
/// s390x miscellaneous-extensions-2 facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_3: "miscellaneous-extensions-3";
|
||||
/// s390x miscellaneous-extensions-3 facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_4: "miscellaneous-extensions-4";
|
||||
/// s390x miscellaneous-extensions-4 facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] nnp_assist: "nnp-assist";
|
||||
/// s390x nnp-assist facility
|
||||
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] transactional_execution: "transactional-execution";
|
||||
/// s390x transactional-execution facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector: "vector";
|
||||
/// s390x vector facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_1: "vector-enhancements-1";
|
||||
/// s390x vector-enhancements-1 facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_2: "vector-enhancements-2";
|
||||
/// s390x vector-enhancements-2 facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_3: "vector-enhancements-3";
|
||||
/// s390x vector-enhancements-3 facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal: "vector-packed-decimal";
|
||||
/// s390x vector-packed-decimal facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement: "vector-packed-decimal-enhancement";
|
||||
/// s390x vector-packed-decimal-enhancement facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement_2: "vector-packed-decimal-enhancement-2";
|
||||
/// s390x vector-packed-decimal-enhancement-2 facility
|
||||
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement_3: "vector-packed-decimal-enhancement-3";
|
||||
/// s390x vector-packed-decimal-enhancement-3 facility
|
||||
}
|
||||
280
crates/std/crates/std_detect/src/detect/arch/x86.rs
Normal file
280
crates/std/crates/std_detect/src/detect/arch/x86.rs
Normal file
@@ -0,0 +1,280 @@
|
||||
//! This module implements minimal run-time feature detection for x86.
|
||||
//!
|
||||
//! The features are detected using the `detect_features` function below.
|
||||
//! This function uses the CPUID instruction to read the feature flags from the
|
||||
//! CPU and encodes them in a `usize` where each bit position represents
|
||||
//! whether a feature is available (bit is set) or unavailable (bit is cleared).
|
||||
//!
|
||||
//! The enum `Feature` is used to map bit positions to feature names, and the
|
||||
//! the `__crate::detect::check_for!` macro is used to map string literals (e.g.,
|
||||
//! "avx") to these bit positions (e.g., `Feature::avx`).
|
||||
//!
|
||||
//! The run-time feature detection is performed by the
|
||||
//! `__crate::detect::check_for(Feature) -> bool` function. On its first call,
|
||||
//! this functions queries the CPU for the available features and stores them
|
||||
//! in a global `AtomicUsize` variable. The query is performed by just checking
|
||||
//! whether the feature bit in this global variable is set or cleared.
|
||||
|
||||
features! {
|
||||
@TARGET: x86;
|
||||
@CFG: any(target_arch = "x86", target_arch = "x86_64");
|
||||
@MACRO_NAME: is_x86_feature_detected;
|
||||
@MACRO_ATTRS:
|
||||
/// Check for the presence of a CPU feature at runtime.
|
||||
///
|
||||
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
|
||||
/// the macro expands to `true`.
|
||||
///
|
||||
/// Runtime detection currently relies mostly on the `cpuid` instruction.
|
||||
///
|
||||
/// This macro only takes one argument which is a string literal of the feature
|
||||
/// being tested for. The feature names supported are the lowercase versions of
|
||||
/// the ones defined by Intel in [their documentation][docs].
|
||||
///
|
||||
/// ## Supported arguments
|
||||
///
|
||||
/// This macro supports the same names that `#[target_feature]` supports. Unlike
|
||||
/// `#[target_feature]`, however, this macro does not support names separated
|
||||
/// with a comma. Instead testing for multiple features must be done through
|
||||
/// separate macro invocations for now.
|
||||
///
|
||||
/// Supported arguments are:
|
||||
///
|
||||
/// * `"aes"`
|
||||
/// * `"pclmulqdq"`
|
||||
/// * `"rdrand"`
|
||||
/// * `"rdseed"`
|
||||
/// * `"tsc"`
|
||||
/// * `"mmx"`
|
||||
/// * `"sse"`
|
||||
/// * `"sse2"`
|
||||
/// * `"sse3"`
|
||||
/// * `"ssse3"`
|
||||
/// * `"sse4.1"`
|
||||
/// * `"sse4.2"`
|
||||
/// * `"sse4a"`
|
||||
/// * `"sha"`
|
||||
/// * `"avx"`
|
||||
/// * `"avx2"`
|
||||
/// * `"sha512"`
|
||||
/// * `"sm3"`
|
||||
/// * `"sm4"`
|
||||
/// * `"avx512f"`
|
||||
/// * `"avx512cd"`
|
||||
/// * `"avx512er"`
|
||||
/// * `"avx512pf"`
|
||||
/// * `"avx512bw"`
|
||||
/// * `"avx512dq"`
|
||||
/// * `"avx512vl"`
|
||||
/// * `"avx512ifma"`
|
||||
/// * `"avx512vbmi"`
|
||||
/// * `"avx512vpopcntdq"`
|
||||
/// * `"avx512vbmi2"`
|
||||
/// * `"gfni"`
|
||||
/// * `"vaes"`
|
||||
/// * `"vpclmulqdq"`
|
||||
/// * `"avx512vnni"`
|
||||
/// * `"avx512bitalg"`
|
||||
/// * `"avx512bf16"`
|
||||
/// * `"avx512vp2intersect"`
|
||||
/// * `"avx512fp16"`
|
||||
/// * `"avxvnni"`
|
||||
/// * `"avxifma"`
|
||||
/// * `"avxneconvert"`
|
||||
/// * `"avxvnniint8"`
|
||||
/// * `"avxvnniint16"`
|
||||
/// * `"amx-tile"`
|
||||
/// * `"amx-int8"`
|
||||
/// * `"amx-bf16"`
|
||||
/// * `"amx-fp16"`
|
||||
/// * `"amx-complex"`
|
||||
/// * `"amx-avx512"`
|
||||
/// * `"amx-fp8"`
|
||||
/// * `"amx-movrs"`
|
||||
/// * `"amx-tf32"`
|
||||
/// * `"f16c"`
|
||||
/// * `"fma"`
|
||||
/// * `"bmi1"`
|
||||
/// * `"bmi2"`
|
||||
/// * `"abm"`
|
||||
/// * `"lzcnt"`
|
||||
/// * `"tbm"`
|
||||
/// * `"popcnt"`
|
||||
/// * `"fxsr"`
|
||||
/// * `"xsave"`
|
||||
/// * `"xsaveopt"`
|
||||
/// * `"xsaves"`
|
||||
/// * `"xsavec"`
|
||||
/// * `"cmpxchg16b"`
|
||||
/// * `"kl"`
|
||||
/// * `"widekl"`
|
||||
/// * `"adx"`
|
||||
/// * `"rtm"`
|
||||
/// * `"movbe"`
|
||||
/// * `"ermsb"`
|
||||
/// * `"movrs"`
|
||||
/// * `"xop"`
|
||||
///
|
||||
/// [docs]: https://software.intel.com/sites/landingpage/IntrinsicsGuide
|
||||
#[stable(feature = "simd_x86", since = "1.27.0")]
|
||||
@BIND_FEATURE_NAME: "abm"; "lzcnt"; // abm is a synonym for lzcnt
|
||||
@BIND_FEATURE_NAME: "avx512gfni"; "gfni"; #[deprecated(since = "1.67.0", note = "the `avx512gfni` feature has been renamed to `gfni`")];
|
||||
@BIND_FEATURE_NAME: "avx512vaes"; "vaes"; #[deprecated(since = "1.67.0", note = "the `avx512vaes` feature has been renamed to `vaes`")];
|
||||
@BIND_FEATURE_NAME: "avx512vpclmulqdq"; "vpclmulqdq"; #[deprecated(since = "1.67.0", note = "the `avx512vpclmulqdq` feature has been renamed to `vpclmulqdq`")];
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] aes: "aes";
|
||||
/// AES (Advanced Encryption Standard New Instructions AES-NI)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] pclmulqdq: "pclmulqdq";
|
||||
/// CLMUL (Carry-less Multiplication)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rdrand: "rdrand";
|
||||
/// RDRAND
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rdseed: "rdseed";
|
||||
/// RDSEED
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] tsc: "tsc";
|
||||
without cfg check: true;
|
||||
/// TSC (Time Stamp Counter)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] mmx: "mmx";
|
||||
without cfg check: true;
|
||||
/// MMX (MultiMedia eXtensions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse: "sse";
|
||||
/// SSE (Streaming SIMD Extensions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse2: "sse2";
|
||||
/// SSE2 (Streaming SIMD Extensions 2)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse3: "sse3";
|
||||
/// SSE3 (Streaming SIMD Extensions 3)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] ssse3: "ssse3";
|
||||
/// SSSE3 (Supplemental Streaming SIMD Extensions 3)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_1: "sse4.1";
|
||||
/// SSE4.1 (Streaming SIMD Extensions 4.1)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_2: "sse4.2";
|
||||
/// SSE4.2 (Streaming SIMD Extensions 4.2)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4a: "sse4a";
|
||||
/// SSE4a (Streaming SIMD Extensions 4a)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sha: "sha";
|
||||
/// SHA
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx: "avx";
|
||||
/// AVX (Advanced Vector Extensions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx2: "avx2";
|
||||
/// AVX2 (Advanced Vector Extensions 2)
|
||||
@FEATURE: #[stable(feature = "sha512_sm_x86", since = "1.89.0")] sha512: "sha512";
|
||||
/// SHA512
|
||||
@FEATURE: #[stable(feature = "sha512_sm_x86", since = "1.89.0")] sm3: "sm3";
|
||||
/// SM3
|
||||
@FEATURE: #[stable(feature = "sha512_sm_x86", since = "1.89.0")] sm4: "sm4";
|
||||
/// SM4
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512f: "avx512f" ;
|
||||
/// AVX-512 F (Foundation)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512cd: "avx512cd" ;
|
||||
/// AVX-512 CD (Conflict Detection Instructions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512er: "avx512er";
|
||||
without cfg check: true;
|
||||
/// AVX-512 ER (Expo nential and Reciprocal Instructions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512pf: "avx512pf";
|
||||
without cfg check: true;
|
||||
/// AVX-512 PF (Prefetch Instructions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bw: "avx512bw";
|
||||
/// AVX-512 BW (Byte and Word Instructions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512dq: "avx512dq";
|
||||
/// AVX-512 DQ (Doubleword and Quadword)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vl: "avx512vl";
|
||||
/// AVX-512 VL (Vector Length Extensions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512ifma: "avx512ifma";
|
||||
/// AVX-512 IFMA (Integer Fused Multiply Add)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vbmi: "avx512vbmi";
|
||||
/// AVX-512 VBMI (Vector Byte Manipulation Instructions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vpopcntdq: "avx512vpopcntdq";
|
||||
/// AVX-512 VPOPCNTDQ (Vector Population Count Doubleword and Quadword)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vbmi2: "avx512vbmi2";
|
||||
/// AVX-512 VBMI2 (Additional byte, word, dword and qword capabilities)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] gfni: "gfni";
|
||||
/// AVX-512 GFNI (Galois Field New Instruction)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] vaes: "vaes";
|
||||
/// AVX-512 VAES (Vector AES instruction)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] vpclmulqdq: "vpclmulqdq";
|
||||
/// AVX-512 VPCLMULQDQ (Vector PCLMULQDQ instructions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vnni: "avx512vnni";
|
||||
/// AVX-512 VNNI (Vector Neural Network Instructions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bitalg: "avx512bitalg";
|
||||
/// AVX-512 BITALG (Support for VPOPCNT\[B,W\] and VPSHUFBITQMB)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bf16: "avx512bf16";
|
||||
/// AVX-512 BF16 (BFLOAT16 instructions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vp2intersect: "avx512vp2intersect";
|
||||
/// AVX-512 P2INTERSECT
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512fp16: "avx512fp16";
|
||||
/// AVX-512 FP16 (FLOAT16 instructions)
|
||||
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxifma: "avxifma";
|
||||
/// AVX-IFMA (Integer Fused Multiply Add)
|
||||
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxneconvert: "avxneconvert";
|
||||
/// AVX-NE-CONVERT (Exceptionless Convert)
|
||||
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxvnni: "avxvnni";
|
||||
/// AVX-VNNI (Vector Neural Network Instructions)
|
||||
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxvnniint16: "avxvnniint16";
|
||||
/// AVX-VNNI_INT8 (VNNI with 16-bit Integers)
|
||||
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxvnniint8: "avxvnniint8";
|
||||
/// AVX-VNNI_INT16 (VNNI with 8-bit integers)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tile: "amx-tile";
|
||||
/// AMX (Advanced Matrix Extensions) - Tile load/store
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_int8: "amx-int8";
|
||||
/// AMX-INT8 (Operations on 8-bit integers)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_bf16: "amx-bf16";
|
||||
/// AMX-BF16 (BFloat16 Operations)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_fp16: "amx-fp16";
|
||||
/// AMX-FP16 (Float16 Operations)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_complex: "amx-complex";
|
||||
/// AMX-COMPLEX (Complex number Operations)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_avx512: "amx-avx512";
|
||||
/// AMX-AVX512 (AVX512 operations extended to matrices)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_fp8: "amx-fp8";
|
||||
/// AMX-FP8 (Float8 Operations)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_movrs: "amx-movrs";
|
||||
/// AMX-MOVRS (Matrix MOVERS operations)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tf32: "amx-tf32";
|
||||
/// AMX-TF32 (TensorFloat32 Operations)
|
||||
@FEATURE: #[unstable(feature = "apx_target_feature", issue = "139284")] apxf: "apxf";
|
||||
/// APX-F (Advanced Performance Extensions - Foundation)
|
||||
@FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_1: "avx10.1";
|
||||
/// AVX10.1
|
||||
@FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_2: "avx10.2";
|
||||
/// AVX10.2
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] f16c: "f16c";
|
||||
/// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fma: "fma";
|
||||
/// FMA (Fused Multiply Add)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi1: "bmi1" ;
|
||||
/// BMI1 (Bit Manipulation Instructions 1)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi2: "bmi2" ;
|
||||
/// BMI2 (Bit Manipulation Instructions 2)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] lzcnt: "lzcnt";
|
||||
/// ABM (Advanced Bit Manipulation) / LZCNT (Leading Zero Count)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] tbm: "tbm";
|
||||
/// TBM (Trailing Bit Manipulation)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] popcnt: "popcnt";
|
||||
/// POPCNT (Population Count)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fxsr: "fxsr";
|
||||
/// FXSR (Floating-point context fast save and restore)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsave: "xsave";
|
||||
/// XSAVE (Save Processor Extended States)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaveopt: "xsaveopt";
|
||||
/// XSAVEOPT (Save Processor Extended States Optimized)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaves: "xsaves";
|
||||
/// XSAVES (Save Processor Extended States Supervisor)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsavec: "xsavec";
|
||||
/// XSAVEC (Save Processor Extended States Compacted)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] cmpxchg16b: "cmpxchg16b";
|
||||
/// CMPXCH16B (16-byte compare-and-swap instruction)
|
||||
@FEATURE: #[stable(feature = "keylocker_x86", since = "1.89.0")] kl: "kl";
|
||||
/// Intel Key Locker
|
||||
@FEATURE: #[stable(feature = "keylocker_x86", since = "1.89.0")] widekl: "widekl";
|
||||
/// Intel Key Locker Wide
|
||||
@FEATURE: #[stable(feature = "simd_x86_adx", since = "1.33.0")] adx: "adx";
|
||||
/// ADX, Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rtm: "rtm";
|
||||
/// RTM, Intel (Restricted Transactional Memory)
|
||||
@FEATURE: #[stable(feature = "movbe_target_feature", since = "1.67.0")] movbe: "movbe";
|
||||
/// MOVBE (Move Data After Swapping Bytes)
|
||||
@FEATURE: #[unstable(feature = "movrs_target_feature", issue = "137976")] movrs: "movrs";
|
||||
/// MOVRS (Move data with the read-shared hint)
|
||||
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] ermsb: "ermsb";
|
||||
/// ERMSB, Enhanced REP MOVSB and STOSB
|
||||
@FEATURE: #[unstable(feature = "xop_target_feature", issue = "127208")] xop: "xop";
|
||||
/// XOP: eXtended Operations (AMD)
|
||||
}
|
||||
9
crates/std/crates/std_detect/src/detect/bit.rs
Normal file
9
crates/std/crates/std_detect/src/detect/bit.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
//! Bit manipulation utilities.
|
||||
|
||||
/// Tests the `bit` of `x`.
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn test(x: usize, bit: u32) -> bool {
|
||||
debug_assert!(bit < usize::BITS, "bit index out-of-bounds");
|
||||
x & (1 << bit) != 0
|
||||
}
|
||||
203
crates/std/crates/std_detect/src/detect/cache.rs
Normal file
203
crates/std/crates/std_detect/src/detect/cache.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
//! Caches run-time feature detection so that it only needs to be computed
|
||||
//! once.
|
||||
|
||||
#![allow(dead_code)] // not used on all platforms
|
||||
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
/// Sets the `bit` of `x`.
|
||||
#[inline]
|
||||
const fn set_bit(x: u128, bit: u32) -> u128 {
|
||||
x | 1 << bit
|
||||
}
|
||||
|
||||
/// Tests the `bit` of `x`.
|
||||
#[inline]
|
||||
const fn test_bit(x: u128, bit: u32) -> bool {
|
||||
x & (1 << bit) != 0
|
||||
}
|
||||
|
||||
/// Unset the `bit of `x`.
|
||||
#[inline]
|
||||
const fn unset_bit(x: u128, bit: u32) -> u128 {
|
||||
x & !(1 << bit)
|
||||
}
|
||||
|
||||
/// Maximum number of features that can be cached.
|
||||
const CACHE_CAPACITY: u32 = 93;
|
||||
|
||||
/// This type is used to initialize the cache
|
||||
// The derived `Default` implementation will initialize the field to zero,
|
||||
// which is what we want.
|
||||
#[derive(Copy, Clone, Default, PartialEq, Eq)]
|
||||
pub(crate) struct Initializer(u128);
|
||||
|
||||
// NOTE: the `debug_assert!` would catch that we do not add more Features than
|
||||
// the one fitting our cache.
|
||||
impl Initializer {
|
||||
/// Tests the `bit` of the cache.
|
||||
#[inline]
|
||||
pub(crate) fn test(self, bit: u32) -> bool {
|
||||
debug_assert!(bit < CACHE_CAPACITY, "too many features, time to increase the cache size!");
|
||||
test_bit(self.0, bit)
|
||||
}
|
||||
|
||||
/// Sets the `bit` of the cache.
|
||||
#[inline]
|
||||
pub(crate) fn set(&mut self, bit: u32) {
|
||||
debug_assert!(bit < CACHE_CAPACITY, "too many features, time to increase the cache size!");
|
||||
let v = self.0;
|
||||
self.0 = set_bit(v, bit);
|
||||
}
|
||||
|
||||
/// Unsets the `bit` of the cache.
|
||||
#[inline]
|
||||
pub(crate) fn unset(&mut self, bit: u32) {
|
||||
debug_assert!(bit < CACHE_CAPACITY, "too many features, time to increase the cache size!");
|
||||
let v = self.0;
|
||||
self.0 = unset_bit(v, bit);
|
||||
}
|
||||
}
|
||||
|
||||
/// This global variable is a cache of the features supported by the CPU.
|
||||
// Note: the third slot is only used in x86
|
||||
// Another Slot can be added if needed without any change to `Initializer`
|
||||
static CACHE: [Cache; 3] = [Cache::uninitialized(), Cache::uninitialized(), Cache::uninitialized()];
|
||||
|
||||
/// Feature cache with capacity for `size_of::<usize>() * 8 - 1` features.
|
||||
///
|
||||
/// Note: 0 is used to represent an uninitialized cache, and (at least) the most
|
||||
/// significant bit is set on any cache which has been initialized.
|
||||
///
|
||||
/// Note: we use `Relaxed` atomic operations, because we are only interested in
|
||||
/// the effects of operations on a single memory location. That is, we only need
|
||||
/// "modification order", and not the full-blown "happens before".
|
||||
struct Cache(AtomicUsize);
|
||||
|
||||
impl Cache {
|
||||
const CAPACITY: u32 = (core::mem::size_of::<usize>() * 8 - 1) as u32;
|
||||
const MASK: usize = (1 << Cache::CAPACITY) - 1;
|
||||
const INITIALIZED_BIT: usize = 1usize << Cache::CAPACITY;
|
||||
|
||||
/// Creates an uninitialized cache.
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const fn uninitialized() -> Self {
|
||||
Cache(AtomicUsize::new(0))
|
||||
}
|
||||
|
||||
/// Is the `bit` in the cache set? Returns `None` if the cache has not been initialized.
|
||||
#[inline]
|
||||
pub(crate) fn test(&self, bit: u32) -> Option<bool> {
|
||||
let cached = self.0.load(Ordering::Relaxed);
|
||||
if cached == 0 { None } else { Some(test_bit(cached as u128, bit)) }
|
||||
}
|
||||
|
||||
/// Initializes the cache.
|
||||
#[inline]
|
||||
fn initialize(&self, value: usize) -> usize {
|
||||
debug_assert_eq!((value & !Cache::MASK), 0);
|
||||
self.0.store(value | Cache::INITIALIZED_BIT, Ordering::Relaxed);
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
cfg_select! {
|
||||
feature = "std_detect_env_override" => {
|
||||
#[inline]
|
||||
fn disable_features(disable: &[u8], value: &mut Initializer) {
|
||||
if let Ok(disable) = core::str::from_utf8(disable) {
|
||||
for v in disable.split(" ") {
|
||||
let _ = super::Feature::from_str(v).map(|v| value.unset(v as u32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initialize(mut value: Initializer) -> Initializer {
|
||||
use core::ffi::CStr;
|
||||
const RUST_STD_DETECT_UNSTABLE: &CStr = c"RUST_STD_DETECT_UNSTABLE";
|
||||
cfg_select! {
|
||||
windows => {
|
||||
use alloc::vec;
|
||||
#[link(name = "kernel32")]
|
||||
unsafe extern "system" {
|
||||
fn GetEnvironmentVariableA(name: *const u8, buffer: *mut u8, size: u32) -> u32;
|
||||
}
|
||||
let len = unsafe { GetEnvironmentVariableA(RUST_STD_DETECT_UNSTABLE.as_ptr().cast::<u8>(), core::ptr::null_mut(), 0) };
|
||||
if len > 0 {
|
||||
// +1 to include the null terminator.
|
||||
let mut env = vec![0; len as usize + 1];
|
||||
let len = unsafe { GetEnvironmentVariableA(RUST_STD_DETECT_UNSTABLE.as_ptr().cast::<u8>(), env.as_mut_ptr(), len + 1) };
|
||||
if len > 0 {
|
||||
disable_features(&env[..len as usize], &mut value);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let env = unsafe {
|
||||
libc::getenv(RUST_STD_DETECT_UNSTABLE.as_ptr())
|
||||
};
|
||||
if !env.is_null() {
|
||||
let len = unsafe { libc::strlen(env) };
|
||||
let env = unsafe { core::slice::from_raw_parts(env as *const u8, len) };
|
||||
disable_features(env, &mut value);
|
||||
}
|
||||
}
|
||||
}
|
||||
do_initialize(value);
|
||||
value
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
#[inline]
|
||||
fn initialize(value: Initializer) -> Initializer {
|
||||
do_initialize(value);
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_initialize(value: Initializer) {
|
||||
CACHE[0].initialize((value.0) as usize & Cache::MASK);
|
||||
CACHE[1].initialize((value.0 >> Cache::CAPACITY) as usize & Cache::MASK);
|
||||
CACHE[2].initialize((value.0 >> (2 * Cache::CAPACITY)) as usize & Cache::MASK);
|
||||
}
|
||||
|
||||
// We only have to detect features once, and it's fairly costly, so hint to LLVM
|
||||
// that it should assume that cache hits are more common than misses (which is
|
||||
// the point of caching). It's possibly unfortunate that this function needs to
|
||||
// reach across modules like this to call `os::detect_features`, but it produces
|
||||
// the best code out of several attempted variants.
|
||||
//
|
||||
// The `Initializer` that the cache was initialized with is returned, so that
|
||||
// the caller can call `test()` on it without having to load the value from the
|
||||
// cache again.
|
||||
#[cold]
|
||||
fn detect_and_initialize() -> Initializer {
|
||||
initialize(super::os::detect_features())
|
||||
}
|
||||
|
||||
/// Tests the `bit` of the storage. If the storage has not been initialized,
|
||||
/// initializes it with the result of `os::detect_features()`.
|
||||
///
|
||||
/// On its first invocation, it detects the CPU features and caches them in the
|
||||
/// `CACHE` global variable as an `AtomicU64`.
|
||||
///
|
||||
/// It uses the `Feature` variant to index into this variable as a bitset. If
|
||||
/// the bit is set, the feature is enabled, and otherwise it is disabled.
|
||||
///
|
||||
/// If the feature `std_detect_env_override` is enabled looks for the env
|
||||
/// variable `RUST_STD_DETECT_UNSTABLE` and uses its content to disable
|
||||
/// Features that would had been otherwise detected.
|
||||
#[inline]
|
||||
pub(crate) fn test(bit: u32) -> bool {
|
||||
let (relative_bit, idx) = if bit < Cache::CAPACITY {
|
||||
(bit, 0)
|
||||
} else if bit < 2 * Cache::CAPACITY {
|
||||
(bit - Cache::CAPACITY, 1)
|
||||
} else {
|
||||
(bit - 2 * Cache::CAPACITY, 2)
|
||||
};
|
||||
CACHE[idx].test(relative_bit).unwrap_or_else(|| detect_and_initialize().test(bit))
|
||||
}
|
||||
203
crates/std/crates/std_detect/src/detect/macros.rs
Normal file
203
crates/std/crates/std_detect/src/detect/macros.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
#[macro_export]
|
||||
#[allow_internal_unstable(stdarch_internal)]
|
||||
#[unstable(feature = "stdarch_internal", issue = "none")]
|
||||
macro_rules! detect_feature {
|
||||
($feature:tt, $feature_lit:tt) => {
|
||||
$crate::detect_feature!($feature, $feature_lit : $feature_lit)
|
||||
};
|
||||
($feature:tt, $feature_lit:tt : $($target_feature_lit:tt),*) => {
|
||||
$(cfg!(target_feature = $target_feature_lit) ||)*
|
||||
$crate::detect::__is_feature_detected::$feature()
|
||||
};
|
||||
($feature:tt, $feature_lit:tt, without cfg check: true) => {
|
||||
$crate::detect::__is_feature_detected::$feature()
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_macros, reason = "it's used in the features! macro below")]
|
||||
macro_rules! check_cfg_feature {
|
||||
($feature:tt, $feature_lit:tt) => {
|
||||
check_cfg_feature!($feature, $feature_lit : $feature_lit)
|
||||
};
|
||||
($feature:tt, $feature_lit:tt : $($target_feature_lit:tt),*) => {
|
||||
$(cfg!(target_feature = $target_feature_lit);)*
|
||||
};
|
||||
($feature:tt, $feature_lit:tt, without cfg check: $feature_cfg_check:literal) => {
|
||||
#[allow(unexpected_cfgs, reason = $feature_lit)]
|
||||
{ cfg!(target_feature = $feature_lit) }
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! features {
|
||||
(
|
||||
@TARGET: $target:ident;
|
||||
@CFG: $cfg:meta;
|
||||
@MACRO_NAME: $macro_name:ident;
|
||||
@MACRO_ATTRS: $(#[$macro_attrs:meta])*
|
||||
$(@BIND_FEATURE_NAME: $bind_feature:tt; $feature_impl:tt; $(#[$deprecate_attr:meta];)?)*
|
||||
$(@NO_RUNTIME_DETECTION: $nort_feature:tt; )*
|
||||
$(@FEATURE: #[$stability_attr:meta] $feature:ident: $feature_lit:tt;
|
||||
$(without cfg check: $feature_cfg_check:tt;)?
|
||||
$(implied by target_features: [$($target_feature_lit:tt),*];)?
|
||||
$(#[$feature_comment:meta])*)*
|
||||
) => {
|
||||
#[macro_export]
|
||||
$(#[$macro_attrs])*
|
||||
#[allow_internal_unstable(stdarch_internal)]
|
||||
#[cfg($cfg)]
|
||||
#[doc(cfg($cfg))]
|
||||
macro_rules! $macro_name {
|
||||
$(
|
||||
($feature_lit) => {
|
||||
$crate::detect_feature!($feature, $feature_lit $(, without cfg check: $feature_cfg_check)? $(: $($target_feature_lit),*)?)
|
||||
};
|
||||
)*
|
||||
$(
|
||||
($bind_feature) => {
|
||||
{
|
||||
$(
|
||||
#[$deprecate_attr] macro_rules! deprecated_feature { {} => {}; }
|
||||
deprecated_feature! {};
|
||||
)?
|
||||
$crate::$macro_name!($feature_impl)
|
||||
}
|
||||
};
|
||||
)*
|
||||
$(
|
||||
($nort_feature) => {
|
||||
compile_error!(
|
||||
concat!(
|
||||
stringify!($nort_feature),
|
||||
" feature cannot be detected at run-time"
|
||||
)
|
||||
)
|
||||
};
|
||||
)*
|
||||
($t:tt,) => {
|
||||
$crate::$macro_name!($t);
|
||||
};
|
||||
($t:tt) => {
|
||||
compile_error!(
|
||||
concat!(
|
||||
concat!("unknown ", stringify!($target)),
|
||||
concat!(" target feature: ", $t)
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
$(#[$macro_attrs])*
|
||||
#[macro_export]
|
||||
#[cfg(not($cfg))]
|
||||
#[doc(cfg($cfg))]
|
||||
macro_rules! $macro_name {
|
||||
$(
|
||||
($feature_lit) => {
|
||||
compile_error!(
|
||||
concat!(
|
||||
r#"This macro cannot be used on the current target.
|
||||
You can prevent it from being used in other architectures by
|
||||
guarding it behind a cfg("#,
|
||||
stringify!($cfg),
|
||||
")."
|
||||
)
|
||||
)
|
||||
};
|
||||
)*
|
||||
$(
|
||||
($bind_feature) => { $crate::$macro_name!($feature_impl) };
|
||||
)*
|
||||
$(
|
||||
($nort_feature) => {
|
||||
compile_error!(
|
||||
concat!(
|
||||
stringify!($nort_feature),
|
||||
" feature cannot be detected at run-time"
|
||||
)
|
||||
)
|
||||
};
|
||||
)*
|
||||
($t:tt,) => {
|
||||
$crate::$macro_name!($t);
|
||||
};
|
||||
($t:tt) => {
|
||||
compile_error!(
|
||||
concat!(
|
||||
concat!("unknown ", stringify!($target)),
|
||||
concat!(" target feature: ", $t)
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[deny(unexpected_cfgs)]
|
||||
#[deny(unfulfilled_lint_expectations)]
|
||||
const _: () = {
|
||||
$(
|
||||
check_cfg_feature!($feature, $feature_lit $(, without cfg check: $feature_cfg_check)? $(: $($target_feature_lit),*)?);
|
||||
)*
|
||||
};
|
||||
|
||||
/// Each variant denotes a position in a bitset for a particular feature.
|
||||
///
|
||||
/// PLEASE: do not use this, it is an implementation detail subject
|
||||
/// to change.
|
||||
#[doc(hidden)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
#[unstable(feature = "stdarch_internal", issue = "none")]
|
||||
#[cfg($cfg)]
|
||||
pub(crate) enum Feature {
|
||||
$(
|
||||
$(#[$feature_comment])*
|
||||
$feature,
|
||||
)*
|
||||
|
||||
// Do not add variants after last:
|
||||
_last
|
||||
}
|
||||
|
||||
#[cfg($cfg)]
|
||||
impl Feature {
|
||||
pub(crate) fn to_str(self) -> &'static str {
|
||||
match self {
|
||||
$(Feature::$feature => $feature_lit,)*
|
||||
Feature::_last => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std_detect_env_override")]
|
||||
pub(crate) fn from_str(s: &str) -> Result<Feature, ()> {
|
||||
match s {
|
||||
$($feature_lit => Ok(Feature::$feature),)*
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Each function performs run-time feature detection for a single
|
||||
/// feature. This allow us to use stability attributes on a per feature
|
||||
/// basis.
|
||||
///
|
||||
/// PLEASE: do not use this, it is an implementation detail subject
|
||||
/// to change.
|
||||
#[doc(hidden)]
|
||||
#[cfg($cfg)]
|
||||
#[unstable(feature = "stdarch_internal", issue = "none")]
|
||||
pub mod __is_feature_detected {
|
||||
$(
|
||||
|
||||
/// PLEASE: do not use this, it is an implementation detail
|
||||
/// subject to change.
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
#[$stability_attr]
|
||||
pub fn $feature() -> bool {
|
||||
$crate::detect::check_for($crate::detect::Feature::$feature)
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
125
crates/std/crates/std_detect/src/detect/mod.rs
Normal file
125
crates/std/crates/std_detect/src/detect/mod.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
//! This module implements run-time feature detection.
|
||||
//!
|
||||
//! The `is_{arch}_feature_detected!("feature-name")` macros take the name of a
|
||||
//! feature as a string-literal, and return a boolean indicating whether the
|
||||
//! feature is enabled at run-time or not.
|
||||
//!
|
||||
//! These macros do two things:
|
||||
//! * map the string-literal into an integer stored as a `Feature` enum,
|
||||
//! * call a `os::check_for(x: Feature)` function that returns `true` if the
|
||||
//! feature is enabled.
|
||||
//!
|
||||
//! The `Feature` enums are also implemented in the `arch/{target_arch}.rs`
|
||||
//! modules.
|
||||
//!
|
||||
//! The `check_for` functions are, in general, Operating System dependent. Most
|
||||
//! architectures do not allow user-space programs to query the feature bits
|
||||
//! due to security concerns (x86 is the big exception). These functions are
|
||||
//! implemented in the `os/{target_os}.rs` modules.
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod arch;
|
||||
|
||||
// This module needs to be public because the `is_{arch}_feature_detected!`
|
||||
// macros expand calls to items within it in user crates.
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "stdarch_internal", issue = "none")]
|
||||
pub use self::arch::__is_feature_detected;
|
||||
pub(crate) use self::arch::Feature;
|
||||
|
||||
mod bit;
|
||||
mod cache;
|
||||
|
||||
cfg_select! {
|
||||
miri => {
|
||||
// When running under miri all target-features that are not enabled at
|
||||
// compile-time are reported as disabled at run-time.
|
||||
//
|
||||
// For features for which `cfg(target_feature)` returns true,
|
||||
// this run-time detection logic is never called.
|
||||
#[path = "os/other.rs"]
|
||||
mod os;
|
||||
}
|
||||
any(target_arch = "x86", target_arch = "x86_64") => {
|
||||
// On x86/x86_64 no OS specific functionality is required.
|
||||
#[path = "os/x86.rs"]
|
||||
mod os;
|
||||
}
|
||||
any(target_os = "linux", target_os = "android") => {
|
||||
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
|
||||
#[path = "os/riscv.rs"]
|
||||
mod riscv;
|
||||
#[path = "os/linux/mod.rs"]
|
||||
mod os;
|
||||
}
|
||||
target_os = "freebsd" => {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[path = "os/aarch64.rs"]
|
||||
mod aarch64;
|
||||
#[path = "os/freebsd/mod.rs"]
|
||||
mod os;
|
||||
}
|
||||
target_os = "openbsd" => {
|
||||
#[allow(dead_code)] // we don't use code that calls the mrs instruction.
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[path = "os/aarch64.rs"]
|
||||
mod aarch64;
|
||||
#[path = "os/openbsd/mod.rs"]
|
||||
mod os;
|
||||
}
|
||||
all(target_os = "windows", any(target_arch = "aarch64", target_arch = "arm64ec")) => {
|
||||
#[path = "os/windows/aarch64.rs"]
|
||||
mod os;
|
||||
}
|
||||
all(target_vendor = "apple", target_arch = "aarch64") => {
|
||||
#[path = "os/darwin/aarch64.rs"]
|
||||
mod os;
|
||||
}
|
||||
_ => {
|
||||
#[path = "os/other.rs"]
|
||||
mod os;
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs run-time feature detection.
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn check_for(x: Feature) -> bool {
|
||||
cache::test(x as u32)
|
||||
}
|
||||
|
||||
/// Returns an `Iterator<Item=(&'static str, bool)>` where
|
||||
/// `Item.0` is the feature name, and `Item.1` is a `bool` which
|
||||
/// is `true` if the feature is supported by the host and `false` otherwise.
|
||||
#[unstable(feature = "stdarch_internal", issue = "none")]
|
||||
pub fn features() -> impl Iterator<Item = (&'static str, bool)> {
|
||||
cfg_select! {
|
||||
any(
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
target_arch = "arm",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm64ec",
|
||||
target_arch = "riscv32",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
target_arch = "loongarch32",
|
||||
target_arch = "loongarch64",
|
||||
target_arch = "s390x",
|
||||
) => {
|
||||
(0_u8..Feature::_last as u8).map(|discriminant: u8| {
|
||||
#[allow(bindings_with_variant_name)] // RISC-V has Feature::f
|
||||
let f: Feature = unsafe { core::mem::transmute(discriminant) };
|
||||
let name: &'static str = f.to_str();
|
||||
let enabled: bool = check_for(f);
|
||||
(name, enabled)
|
||||
})
|
||||
}
|
||||
_ => None.into_iter(),
|
||||
}
|
||||
}
|
||||
127
crates/std/crates/std_detect/src/detect/os/aarch64.rs
Normal file
127
crates/std/crates/std_detect/src/detect/os/aarch64.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
//! Run-time feature detection for Aarch64 on any OS that emulates the mrs instruction.
|
||||
//!
|
||||
//! On FreeBSD >= 12.0, Linux >= 4.11 and other operating systems, it is possible to use
|
||||
//! privileged system registers from userspace to check CPU feature support.
|
||||
//!
|
||||
//! AArch64 system registers ID_AA64ISAR0_EL1, ID_AA64PFR0_EL1, ID_AA64ISAR1_EL1
|
||||
//! have bits dedicated to features like AdvSIMD, CRC32, AES, atomics (LSE), etc.
|
||||
//! Each part of the register indicates the level of support for a certain feature, e.g.
|
||||
//! when ID_AA64ISAR0_EL1\[7:4\] is >= 1, AES is supported; when it's >= 2, PMULL is supported.
|
||||
//!
|
||||
//! For proper support of [SoCs where different cores have different capabilities](https://medium.com/@jadr2ddude/a-big-little-problem-a-tale-of-big-little-gone-wrong-e7778ce744bb),
|
||||
//! the OS has to always report only the features supported by all cores, like [FreeBSD does](https://reviews.freebsd.org/D17137#393947).
|
||||
//!
|
||||
//! References:
|
||||
//!
|
||||
//! - [Zircon implementation](https://fuchsia.googlesource.com/zircon/+/master/kernel/arch/arm64/feature.cpp)
|
||||
//! - [Linux documentation](https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt)
|
||||
//! - [ARM documentation](https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers?lang=en)
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
use crate::detect::{Feature, cache};
|
||||
|
||||
/// Try to read the features from the system registers.
|
||||
///
|
||||
/// This will cause SIGILL if the current OS is not trapping the mrs instruction.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
|
||||
let aa64isar0: u64;
|
||||
unsafe {
|
||||
asm!(
|
||||
"mrs {}, ID_AA64ISAR0_EL1",
|
||||
out(reg) aa64isar0,
|
||||
options(pure, nomem, preserves_flags, nostack)
|
||||
);
|
||||
}
|
||||
|
||||
// ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1
|
||||
let aa64isar1: u64;
|
||||
unsafe {
|
||||
asm!(
|
||||
"mrs {}, ID_AA64ISAR1_EL1",
|
||||
out(reg) aa64isar1,
|
||||
options(pure, nomem, preserves_flags, nostack)
|
||||
);
|
||||
}
|
||||
|
||||
// ID_AA64MMFR2_EL1 - AArch64 Memory Model Feature Register 2
|
||||
let aa64mmfr2: u64;
|
||||
unsafe {
|
||||
asm!(
|
||||
"mrs {}, ID_AA64MMFR2_EL1",
|
||||
out(reg) aa64mmfr2,
|
||||
options(pure, nomem, preserves_flags, nostack)
|
||||
);
|
||||
}
|
||||
|
||||
// ID_AA64PFR0_EL1 - Processor Feature Register 0
|
||||
let aa64pfr0: u64;
|
||||
unsafe {
|
||||
asm!(
|
||||
"mrs {}, ID_AA64PFR0_EL1",
|
||||
out(reg) aa64pfr0,
|
||||
options(pure, nomem, preserves_flags, nostack)
|
||||
);
|
||||
}
|
||||
|
||||
parse_system_registers(aa64isar0, aa64isar1, aa64mmfr2, Some(aa64pfr0))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_system_registers(
|
||||
aa64isar0: u64,
|
||||
aa64isar1: u64,
|
||||
aa64mmfr2: u64,
|
||||
aa64pfr0: Option<u64>,
|
||||
) -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
|
||||
let mut enable_feature = |f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
|
||||
enable_feature(Feature::pmull, bits_shift(aa64isar0, 7, 4) >= 2);
|
||||
enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 2);
|
||||
enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1);
|
||||
|
||||
// ID_AA64PFR0_EL1 - Processor Feature Register 0
|
||||
if let Some(aa64pfr0) = aa64pfr0 {
|
||||
let fp = bits_shift(aa64pfr0, 19, 16) < 0xF;
|
||||
let fphp = bits_shift(aa64pfr0, 19, 16) >= 1;
|
||||
let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF;
|
||||
let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1;
|
||||
enable_feature(Feature::fp, fp);
|
||||
enable_feature(Feature::fp16, fphp);
|
||||
// SIMD support requires float support - if half-floats are
|
||||
// supported, it also requires half-float support:
|
||||
enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp));
|
||||
// SIMD extensions require SIMD support:
|
||||
enable_feature(Feature::aes, asimd && bits_shift(aa64isar0, 7, 4) >= 2);
|
||||
let sha1 = bits_shift(aa64isar0, 11, 8) >= 1;
|
||||
let sha2 = bits_shift(aa64isar0, 15, 12) >= 1;
|
||||
enable_feature(Feature::sha2, asimd && sha1 && sha2);
|
||||
enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1);
|
||||
enable_feature(Feature::dotprod, asimd && bits_shift(aa64isar0, 47, 44) >= 1);
|
||||
enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1);
|
||||
}
|
||||
|
||||
// ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1
|
||||
// Check for either APA or API field
|
||||
enable_feature(Feature::paca, bits_shift(aa64isar1, 11, 4) >= 1);
|
||||
enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1);
|
||||
// Check for either GPA or GPI field
|
||||
enable_feature(Feature::pacg, bits_shift(aa64isar1, 31, 24) >= 1);
|
||||
|
||||
// ID_AA64MMFR2_EL1 - AArch64 Memory Model Feature Register 2
|
||||
enable_feature(Feature::lse2, bits_shift(aa64mmfr2, 35, 32) >= 1);
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bits_shift(x: u64, high: usize, low: usize) -> u64 {
|
||||
(x >> low) & ((1 << (high - low + 1)) - 1)
|
||||
}
|
||||
166
crates/std/crates/std_detect/src/detect/os/darwin/aarch64.rs
Normal file
166
crates/std/crates/std_detect/src/detect/os/darwin/aarch64.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
//! Run-time feature detection for aarch64 on Darwin (macOS/iOS/tvOS/watchOS/visionOS).
|
||||
//!
|
||||
//! <https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics>
|
||||
|
||||
use core::ffi::CStr;
|
||||
|
||||
use crate::detect::{Feature, cache};
|
||||
|
||||
#[inline]
|
||||
fn _sysctlbyname(name: &CStr) -> bool {
|
||||
use libc;
|
||||
|
||||
let mut enabled: i32 = 0;
|
||||
let mut enabled_len: usize = 4;
|
||||
let enabled_ptr = &mut enabled as *mut i32 as *mut libc::c_void;
|
||||
|
||||
let ret = unsafe {
|
||||
libc::sysctlbyname(name.as_ptr(), enabled_ptr, &mut enabled_len, core::ptr::null_mut(), 0)
|
||||
};
|
||||
|
||||
match ret {
|
||||
0 => enabled != 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to read the features using sysctlbyname.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
|
||||
let mut enable_feature = |f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// Armv8.0 features not using the standard identifiers
|
||||
let fp = _sysctlbyname(c"hw.optional.floatingpoint");
|
||||
let asimd = _sysctlbyname(c"hw.optional.AdvSIMD");
|
||||
let crc_old = _sysctlbyname(c"hw.optional.armv8_crc32");
|
||||
|
||||
// Armv8 and Armv9 features using the standard identifiers
|
||||
let aes = _sysctlbyname(c"hw.optional.arm.FEAT_AES");
|
||||
let bf16 = _sysctlbyname(c"hw.optional.arm.FEAT_BF16");
|
||||
let bti = _sysctlbyname(c"hw.optional.arm.FEAT_BTI");
|
||||
let crc = _sysctlbyname(c"hw.optional.arm.FEAT_CRC32");
|
||||
let cssc = _sysctlbyname(c"hw.optional.arm.FEAT_CSSC");
|
||||
let dit = _sysctlbyname(c"hw.optional.arm.FEAT_DIT");
|
||||
let dotprod = _sysctlbyname(c"hw.optional.arm.FEAT_DotProd");
|
||||
let dpb = _sysctlbyname(c"hw.optional.arm.FEAT_DPB");
|
||||
let dpb2 = _sysctlbyname(c"hw.optional.arm.FEAT_DPB2");
|
||||
let ecv = _sysctlbyname(c"hw.optional.arm.FEAT_ECV");
|
||||
let fcma = _sysctlbyname(c"hw.optional.arm.FEAT_FCMA");
|
||||
let fhm = _sysctlbyname(c"hw.optional.arm.FEAT_FHM");
|
||||
let flagm = _sysctlbyname(c"hw.optional.arm.FEAT_FlagM");
|
||||
let flagm2 = _sysctlbyname(c"hw.optional.arm.FEAT_FlagM2");
|
||||
let fp16 = _sysctlbyname(c"hw.optional.arm.FEAT_FP16");
|
||||
let frintts = _sysctlbyname(c"hw.optional.arm.FEAT_FRINTTS");
|
||||
let hbc = _sysctlbyname(c"hw.optional.arm.FEAT_HBC");
|
||||
let i8mm = _sysctlbyname(c"hw.optional.arm.FEAT_I8MM");
|
||||
let jsconv = _sysctlbyname(c"hw.optional.arm.FEAT_JSCVT");
|
||||
let rcpc = _sysctlbyname(c"hw.optional.arm.FEAT_LRCPC");
|
||||
let rcpc2 = _sysctlbyname(c"hw.optional.arm.FEAT_LRCPC2");
|
||||
let lse = _sysctlbyname(c"hw.optional.arm.FEAT_LSE");
|
||||
let lse2 = _sysctlbyname(c"hw.optional.arm.FEAT_LSE2");
|
||||
let mte = _sysctlbyname(c"hw.optional.arm.FEAT_MTE");
|
||||
let mte2 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE2");
|
||||
let pauth = _sysctlbyname(c"hw.optional.arm.FEAT_PAuth");
|
||||
let pmull = _sysctlbyname(c"hw.optional.arm.FEAT_PMULL");
|
||||
let rdm = _sysctlbyname(c"hw.optional.arm.FEAT_RDM");
|
||||
let sb = _sysctlbyname(c"hw.optional.arm.FEAT_SB");
|
||||
let sha1 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA1");
|
||||
let sha256 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA256");
|
||||
let sha3 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA3");
|
||||
let sha512 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA512");
|
||||
let sme = _sysctlbyname(c"hw.optional.arm.FEAT_SME");
|
||||
let sme2 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2");
|
||||
let sme2p1 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2p1");
|
||||
let sme_b16b16 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_B16B16");
|
||||
let sme_f16f16 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_F16F16");
|
||||
let sme_f64f64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_F64F64");
|
||||
let sme_i16i64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_I16I64");
|
||||
let ssbs = _sysctlbyname(c"hw.optional.arm.FEAT_SSBS");
|
||||
let wfxt = _sysctlbyname(c"hw.optional.arm.FEAT_WFxT");
|
||||
|
||||
// The following features are not exposed by `is_aarch64_feature_detected`,
|
||||
// but *are* reported by `sysctl`. They are here as documentation that they
|
||||
// exist, and may potentially be exposed later.
|
||||
/*
|
||||
let afp = _sysctlbyname(c"hw.optional.arm.FEAT_AFP");
|
||||
let csv2 = _sysctlbyname(c"hw.optional.arm.FEAT_CSV2");
|
||||
let csv3 = _sysctlbyname(c"hw.optional.arm.FEAT_CSV3");
|
||||
let ebf16 = _sysctlbyname(c"hw.optional.arm.FEAT_EBF16");
|
||||
let fpac = _sysctlbyname(c"hw.optional.arm.FEAT_FPAC");
|
||||
let fpaccombine = _sysctlbyname(c"hw.optional.arm.FEAT_FPACCOMBINE");
|
||||
let mte_async = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_ASYNC");
|
||||
let mte_canonical_tags = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_CANONICAL_TAGS");
|
||||
let mte_no_address_tags = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_NO_ADDRESS_TAGS");
|
||||
let mte_store_only = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_STORE_ONLY");
|
||||
let mte3 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE3");
|
||||
let mte4 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE4");
|
||||
let pacimp = _sysctlbyname(c"hw.optional.arm.FEAT_PACIMP");
|
||||
let pauth2 = _sysctlbyname(c"hw.optional.arm.FEAT_PAuth2");
|
||||
let rpres = _sysctlbyname(c"hw.optional.arm.FEAT_RPRES");
|
||||
let specres = _sysctlbyname(c"hw.optional.arm.FEAT_SPECRES");
|
||||
let specres2 = _sysctlbyname(c"hw.optional.arm.FEAT_SPECRES2");
|
||||
*/
|
||||
|
||||
// The following "features" are reported by `sysctl` but are mandatory parts
|
||||
// of SME or SME2, and so are not exposed separately by
|
||||
// `is_aarch64_feature_detected`. They are here to document their
|
||||
// existence, in case they're needed in the future.
|
||||
/*
|
||||
let sme_b16f32 = _sysctlbyname(c"hw.optional.arm.SME_B16F32");
|
||||
let sme_bi32i32 = _sysctlbyname(c"hw.optional.arm.SME_BI32I32");
|
||||
let sme_f16f32 = _sysctlbyname(c"hw.optional.arm.SME_F16F32");
|
||||
let sme_f32f32 = _sysctlbyname(c"hw.optional.arm.SME_F32F32");
|
||||
let sme_i16i32 = _sysctlbyname(c"hw.optional.arm.SME_I16I32");
|
||||
let sme_i8i32 = _sysctlbyname(c"hw.optional.arm.SME_I8I32");
|
||||
*/
|
||||
|
||||
enable_feature(Feature::aes, aes && pmull);
|
||||
enable_feature(Feature::asimd, asimd);
|
||||
enable_feature(Feature::bf16, bf16);
|
||||
enable_feature(Feature::bti, bti);
|
||||
enable_feature(Feature::crc, crc_old || crc);
|
||||
enable_feature(Feature::cssc, cssc);
|
||||
enable_feature(Feature::dit, dit);
|
||||
enable_feature(Feature::dotprod, dotprod);
|
||||
enable_feature(Feature::dpb, dpb);
|
||||
enable_feature(Feature::dpb2, dpb2);
|
||||
enable_feature(Feature::ecv, ecv);
|
||||
enable_feature(Feature::fcma, fcma);
|
||||
enable_feature(Feature::fhm, fhm);
|
||||
enable_feature(Feature::flagm, flagm);
|
||||
enable_feature(Feature::flagm2, flagm2);
|
||||
enable_feature(Feature::fp, fp);
|
||||
enable_feature(Feature::fp16, fp16);
|
||||
enable_feature(Feature::frintts, frintts);
|
||||
enable_feature(Feature::hbc, hbc);
|
||||
enable_feature(Feature::i8mm, i8mm);
|
||||
enable_feature(Feature::jsconv, jsconv);
|
||||
enable_feature(Feature::lse, lse);
|
||||
enable_feature(Feature::lse2, lse2);
|
||||
enable_feature(Feature::mte, mte && mte2);
|
||||
enable_feature(Feature::paca, pauth);
|
||||
enable_feature(Feature::pacg, pauth);
|
||||
enable_feature(Feature::pmull, aes && pmull);
|
||||
enable_feature(Feature::rcpc, rcpc);
|
||||
enable_feature(Feature::rcpc2, rcpc2);
|
||||
enable_feature(Feature::rdm, rdm);
|
||||
enable_feature(Feature::sb, sb);
|
||||
enable_feature(Feature::sha2, sha1 && sha256 && asimd);
|
||||
enable_feature(Feature::sha3, sha512 && sha3 && asimd);
|
||||
enable_feature(Feature::sme, sme);
|
||||
enable_feature(Feature::sme2, sme2);
|
||||
enable_feature(Feature::sme2p1, sme2p1);
|
||||
enable_feature(Feature::sme_b16b16, sme_b16b16);
|
||||
enable_feature(Feature::sme_f16f16, sme_f16f16);
|
||||
enable_feature(Feature::sme_f64f64, sme_f64f64);
|
||||
enable_feature(Feature::sme_i16i64, sme_i16i64);
|
||||
enable_feature(Feature::ssbs, ssbs);
|
||||
enable_feature(Feature::wfxt, wfxt);
|
||||
|
||||
value
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
//! Run-time feature detection for Aarch64 on FreeBSD.
|
||||
|
||||
pub(crate) use super::super::aarch64::detect_features;
|
||||
36
crates/std/crates/std_detect/src/detect/os/freebsd/arm.rs
Normal file
36
crates/std/crates/std_detect/src/detect/os/freebsd/arm.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
//! Run-time feature detection for ARM on FreeBSD
|
||||
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, cache};
|
||||
|
||||
// Defined in machine/elf.h.
|
||||
// https://github.com/freebsd/freebsd-src/blob/deb63adf945d446ed91a9d84124c71f15ae571d1/sys/arm/include/elf.h
|
||||
const HWCAP_NEON: usize = 0x00001000;
|
||||
const HWCAP2_AES: usize = 0x00000001;
|
||||
const HWCAP2_PMULL: usize = 0x00000002;
|
||||
const HWCAP2_SHA1: usize = 0x00000004;
|
||||
const HWCAP2_SHA2: usize = 0x00000008;
|
||||
const HWCAP2_CRC32: usize = 0x00000010;
|
||||
|
||||
/// Try to read the features from the auxiliary vector
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
let enable_feature = |value: &mut cache::Initializer, f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(auxv) = auxvec::auxv() {
|
||||
enable_feature(&mut value, Feature::neon, auxv.hwcap & HWCAP_NEON != 0);
|
||||
enable_feature(&mut value, Feature::pmull, auxv.hwcap2 & HWCAP2_PMULL != 0);
|
||||
enable_feature(&mut value, Feature::crc, auxv.hwcap2 & HWCAP2_CRC32 != 0);
|
||||
enable_feature(&mut value, Feature::aes, auxv.hwcap2 & HWCAP2_AES != 0);
|
||||
// SHA2 requires SHA1 & SHA2 features
|
||||
let sha1 = auxv.hwcap2 & HWCAP2_SHA1 != 0;
|
||||
let sha2 = auxv.hwcap2 & HWCAP2_SHA2 != 0;
|
||||
enable_feature(&mut value, Feature::sha2, sha1 && sha2);
|
||||
return value;
|
||||
}
|
||||
value
|
||||
}
|
||||
63
crates/std/crates/std_detect/src/detect/os/freebsd/auxvec.rs
Normal file
63
crates/std/crates/std_detect/src/detect/os/freebsd/auxvec.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
//! Parses ELF auxiliary vectors.
|
||||
#![cfg_attr(
|
||||
any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "riscv64"
|
||||
),
|
||||
allow(dead_code)
|
||||
)]
|
||||
|
||||
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
|
||||
///
|
||||
/// If an entry cannot be read all the bits in the bitfield are set to zero.
|
||||
/// This should be interpreted as all the features being disabled.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct AuxVec {
|
||||
pub hwcap: usize,
|
||||
pub hwcap2: usize,
|
||||
}
|
||||
|
||||
/// ELF Auxiliary Vector
|
||||
///
|
||||
/// The auxiliary vector is a memory region in a running ELF program's stack
|
||||
/// composed of (key: usize, value: usize) pairs.
|
||||
///
|
||||
/// The keys used in the aux vector are platform dependent. For FreeBSD, they are
|
||||
/// defined in [sys/elf_common.h][elf_common_h]. The hardware capabilities of a given
|
||||
/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
|
||||
///
|
||||
/// Note that run-time feature detection is not invoked for features that can
|
||||
/// be detected at compile-time.
|
||||
///
|
||||
/// [elf_common.h]: https://svnweb.freebsd.org/base/release/12.0.0/sys/sys/elf_common.h?revision=341707
|
||||
pub(crate) fn auxv() -> Result<AuxVec, ()> {
|
||||
let hwcap = archauxv(libc::AT_HWCAP);
|
||||
let hwcap2 = archauxv(libc::AT_HWCAP2);
|
||||
// Zero could indicate that no features were detected, but it's also used to
|
||||
// indicate an error. In particular, on many platforms AT_HWCAP2 will be
|
||||
// legitimately zero, since it contains the most recent feature flags.
|
||||
if hwcap != 0 || hwcap2 != 0 {
|
||||
return Ok(AuxVec { hwcap, hwcap2 });
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
||||
/// Tries to read the `key` from the auxiliary vector.
|
||||
fn archauxv(key: libc::c_int) -> usize {
|
||||
const OUT_LEN: libc::c_int = core::mem::size_of::<libc::c_ulong>() as libc::c_int;
|
||||
let mut out: libc::c_ulong = 0;
|
||||
unsafe {
|
||||
// elf_aux_info is available on FreeBSD 12.0+ and 11.4+:
|
||||
// https://github.com/freebsd/freebsd-src/commit/0b08ae2120cdd08c20a2b806e2fcef4d0a36c470
|
||||
// https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/sys/auxv.h
|
||||
// FreeBSD 11 support in std has been removed in Rust 1.75 (https://github.com/rust-lang/rust/pull/114521),
|
||||
// so we can safely use this function.
|
||||
let res =
|
||||
libc::elf_aux_info(key, &mut out as *mut libc::c_ulong as *mut libc::c_void, OUT_LEN);
|
||||
// If elf_aux_info fails, `out` will be left at zero (which is the proper default value).
|
||||
debug_assert!(res == 0 || out == 0);
|
||||
}
|
||||
out as usize
|
||||
}
|
||||
25
crates/std/crates/std_detect/src/detect/os/freebsd/mod.rs
Normal file
25
crates/std/crates/std_detect/src/detect/os/freebsd/mod.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
//! Run-time feature detection on FreeBSD
|
||||
|
||||
mod auxvec;
|
||||
|
||||
cfg_select! {
|
||||
target_arch = "aarch64" => {
|
||||
mod aarch64;
|
||||
pub(crate) use self::aarch64::detect_features;
|
||||
}
|
||||
target_arch = "arm" => {
|
||||
mod arm;
|
||||
pub(crate) use self::arm::detect_features;
|
||||
}
|
||||
target_arch = "powerpc64" => {
|
||||
mod powerpc;
|
||||
pub(crate) use self::powerpc::detect_features;
|
||||
}
|
||||
_ => {
|
||||
use crate::detect::cache;
|
||||
/// Performs run-time feature detection.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
cache::Initializer::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//! Run-time feature detection for PowerPC on FreeBSD.
|
||||
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, cache};
|
||||
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
let enable_feature = |value: &mut cache::Initializer, f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(auxv) = auxvec::auxv() {
|
||||
enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0);
|
||||
enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0);
|
||||
enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0);
|
||||
return value;
|
||||
}
|
||||
value
|
||||
}
|
||||
409
crates/std/crates/std_detect/src/detect/os/linux/aarch64.rs
Normal file
409
crates/std/crates/std_detect/src/detect/os/linux/aarch64.rs
Normal file
@@ -0,0 +1,409 @@
|
||||
//! Run-time feature detection for Aarch64 on Linux.
|
||||
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, bit, cache};
|
||||
|
||||
/// Try to read the features from the auxiliary vector.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
#[cfg(target_os = "android")]
|
||||
let is_exynos9810 = {
|
||||
// Samsung Exynos 9810 has a bug that big and little cores have different
|
||||
// ISAs. And on older Android (pre-9), the kernel incorrectly reports
|
||||
// that features available only on some cores are available on all cores.
|
||||
// https://reviews.llvm.org/D114523
|
||||
let mut arch = [0_u8; libc::PROP_VALUE_MAX as usize];
|
||||
let len = unsafe {
|
||||
libc::__system_property_get(c"ro.arch".as_ptr(), arch.as_mut_ptr() as *mut libc::c_char)
|
||||
};
|
||||
// On Exynos, ro.arch is not available on Android 12+, but it is fine
|
||||
// because Android 9+ includes the fix.
|
||||
len > 0 && arch.starts_with(b"exynos9810")
|
||||
};
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let is_exynos9810 = false;
|
||||
|
||||
if let Ok(auxv) = auxvec::auxv() {
|
||||
let hwcap: AtHwcap = auxv.into();
|
||||
return hwcap.cache(is_exynos9810);
|
||||
}
|
||||
cache::Initializer::default()
|
||||
}
|
||||
|
||||
/// These values are part of the platform-specific [asm/hwcap.h][hwcap] .
|
||||
///
|
||||
/// The names match those used for cpuinfo.
|
||||
///
|
||||
/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
struct AtHwcap {
|
||||
// AT_HWCAP
|
||||
fp: bool,
|
||||
asimd: bool,
|
||||
// evtstrm: No LLVM support.
|
||||
aes: bool,
|
||||
pmull: bool,
|
||||
sha1: bool,
|
||||
sha2: bool,
|
||||
crc32: bool,
|
||||
atomics: bool,
|
||||
fphp: bool,
|
||||
asimdhp: bool,
|
||||
// cpuid: No LLVM support.
|
||||
asimdrdm: bool,
|
||||
jscvt: bool,
|
||||
fcma: bool,
|
||||
lrcpc: bool,
|
||||
dcpop: bool,
|
||||
sha3: bool,
|
||||
sm3: bool,
|
||||
sm4: bool,
|
||||
asimddp: bool,
|
||||
sha512: bool,
|
||||
sve: bool,
|
||||
fhm: bool,
|
||||
dit: bool,
|
||||
uscat: bool,
|
||||
ilrcpc: bool,
|
||||
flagm: bool,
|
||||
ssbs: bool,
|
||||
sb: bool,
|
||||
paca: bool,
|
||||
pacg: bool,
|
||||
|
||||
// AT_HWCAP2
|
||||
dcpodp: bool,
|
||||
sve2: bool,
|
||||
sveaes: bool,
|
||||
svepmull: bool,
|
||||
svebitperm: bool,
|
||||
svesha3: bool,
|
||||
svesm4: bool,
|
||||
flagm2: bool,
|
||||
frint: bool,
|
||||
// svei8mm: See i8mm feature.
|
||||
svef32mm: bool,
|
||||
svef64mm: bool,
|
||||
// svebf16: See bf16 feature.
|
||||
i8mm: bool,
|
||||
bf16: bool,
|
||||
// dgh: No LLVM support.
|
||||
rng: bool,
|
||||
bti: bool,
|
||||
mte: bool,
|
||||
ecv: bool,
|
||||
// afp: bool,
|
||||
// rpres: bool,
|
||||
// mte3: bool,
|
||||
sme: bool,
|
||||
smei16i64: bool,
|
||||
smef64f64: bool,
|
||||
// smei8i32: bool,
|
||||
// smef16f32: bool,
|
||||
// smeb16f32: bool,
|
||||
// smef32f32: bool,
|
||||
smefa64: bool,
|
||||
wfxt: bool,
|
||||
// ebf16: bool,
|
||||
// sveebf16: bool,
|
||||
cssc: bool,
|
||||
// rprfm: bool,
|
||||
sve2p1: bool,
|
||||
sme2: bool,
|
||||
sme2p1: bool,
|
||||
// smei16i32: bool,
|
||||
// smebi32i32: bool,
|
||||
smeb16b16: bool,
|
||||
smef16f16: bool,
|
||||
mops: bool,
|
||||
hbc: bool,
|
||||
sveb16b16: bool,
|
||||
lrcpc3: bool,
|
||||
lse128: bool,
|
||||
fpmr: bool,
|
||||
lut: bool,
|
||||
faminmax: bool,
|
||||
f8cvt: bool,
|
||||
f8fma: bool,
|
||||
f8dp4: bool,
|
||||
f8dp2: bool,
|
||||
f8e4m3: bool,
|
||||
f8e5m2: bool,
|
||||
smelutv2: bool,
|
||||
smef8f16: bool,
|
||||
smef8f32: bool,
|
||||
smesf8fma: bool,
|
||||
smesf8dp4: bool,
|
||||
smesf8dp2: bool,
|
||||
// pauthlr: bool,
|
||||
}
|
||||
|
||||
impl From<auxvec::AuxVec> for AtHwcap {
|
||||
/// Reads AtHwcap from the auxiliary vector.
|
||||
fn from(auxv: auxvec::AuxVec) -> Self {
|
||||
let mut cap = AtHwcap {
|
||||
fp: bit::test(auxv.hwcap, 0),
|
||||
asimd: bit::test(auxv.hwcap, 1),
|
||||
// evtstrm: bit::test(auxv.hwcap, 2),
|
||||
aes: bit::test(auxv.hwcap, 3),
|
||||
pmull: bit::test(auxv.hwcap, 4),
|
||||
sha1: bit::test(auxv.hwcap, 5),
|
||||
sha2: bit::test(auxv.hwcap, 6),
|
||||
crc32: bit::test(auxv.hwcap, 7),
|
||||
atomics: bit::test(auxv.hwcap, 8),
|
||||
fphp: bit::test(auxv.hwcap, 9),
|
||||
asimdhp: bit::test(auxv.hwcap, 10),
|
||||
// cpuid: bit::test(auxv.hwcap, 11),
|
||||
asimdrdm: bit::test(auxv.hwcap, 12),
|
||||
jscvt: bit::test(auxv.hwcap, 13),
|
||||
fcma: bit::test(auxv.hwcap, 14),
|
||||
lrcpc: bit::test(auxv.hwcap, 15),
|
||||
dcpop: bit::test(auxv.hwcap, 16),
|
||||
sha3: bit::test(auxv.hwcap, 17),
|
||||
sm3: bit::test(auxv.hwcap, 18),
|
||||
sm4: bit::test(auxv.hwcap, 19),
|
||||
asimddp: bit::test(auxv.hwcap, 20),
|
||||
sha512: bit::test(auxv.hwcap, 21),
|
||||
sve: bit::test(auxv.hwcap, 22),
|
||||
fhm: bit::test(auxv.hwcap, 23),
|
||||
dit: bit::test(auxv.hwcap, 24),
|
||||
uscat: bit::test(auxv.hwcap, 25),
|
||||
ilrcpc: bit::test(auxv.hwcap, 26),
|
||||
flagm: bit::test(auxv.hwcap, 27),
|
||||
ssbs: bit::test(auxv.hwcap, 28),
|
||||
sb: bit::test(auxv.hwcap, 29),
|
||||
paca: bit::test(auxv.hwcap, 30),
|
||||
pacg: bit::test(auxv.hwcap, 31),
|
||||
|
||||
// AT_HWCAP2
|
||||
dcpodp: bit::test(auxv.hwcap2, 0),
|
||||
sve2: bit::test(auxv.hwcap2, 1),
|
||||
sveaes: bit::test(auxv.hwcap2, 2),
|
||||
svepmull: bit::test(auxv.hwcap2, 3),
|
||||
svebitperm: bit::test(auxv.hwcap2, 4),
|
||||
svesha3: bit::test(auxv.hwcap2, 5),
|
||||
svesm4: bit::test(auxv.hwcap2, 6),
|
||||
flagm2: bit::test(auxv.hwcap2, 7),
|
||||
frint: bit::test(auxv.hwcap2, 8),
|
||||
// svei8mm: bit::test(auxv.hwcap2, 9),
|
||||
svef32mm: bit::test(auxv.hwcap2, 10),
|
||||
svef64mm: bit::test(auxv.hwcap2, 11),
|
||||
// svebf16: bit::test(auxv.hwcap2, 12),
|
||||
i8mm: bit::test(auxv.hwcap2, 13),
|
||||
bf16: bit::test(auxv.hwcap2, 14),
|
||||
// dgh: bit::test(auxv.hwcap2, 15),
|
||||
rng: bit::test(auxv.hwcap2, 16),
|
||||
bti: bit::test(auxv.hwcap2, 17),
|
||||
mte: bit::test(auxv.hwcap2, 18),
|
||||
ecv: bit::test(auxv.hwcap2, 19),
|
||||
// afp: bit::test(auxv.hwcap2, 20),
|
||||
// rpres: bit::test(auxv.hwcap2, 21),
|
||||
// mte3: bit::test(auxv.hwcap2, 22),
|
||||
sme: bit::test(auxv.hwcap2, 23),
|
||||
smei16i64: bit::test(auxv.hwcap2, 24),
|
||||
smef64f64: bit::test(auxv.hwcap2, 25),
|
||||
// smei8i32: bit::test(auxv.hwcap2, 26),
|
||||
// smef16f32: bit::test(auxv.hwcap2, 27),
|
||||
// smeb16f32: bit::test(auxv.hwcap2, 28),
|
||||
// smef32f32: bit::test(auxv.hwcap2, 29),
|
||||
smefa64: bit::test(auxv.hwcap2, 30),
|
||||
wfxt: bit::test(auxv.hwcap2, 31),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Hardware capabilities from bits 32 to 63 should only
|
||||
// be tested on LP64 targets with 64 bits `usize`.
|
||||
// On ILP32 targets like `aarch64-unknown-linux-gnu_ilp32`,
|
||||
// these hardware capabilities will default to `false`.
|
||||
// https://github.com/rust-lang/rust/issues/146230
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
{
|
||||
// cap.ebf16: bit::test(auxv.hwcap2, 32);
|
||||
// cap.sveebf16: bit::test(auxv.hwcap2, 33);
|
||||
cap.cssc = bit::test(auxv.hwcap2, 34);
|
||||
// cap.rprfm: bit::test(auxv.hwcap2, 35);
|
||||
cap.sve2p1 = bit::test(auxv.hwcap2, 36);
|
||||
cap.sme2 = bit::test(auxv.hwcap2, 37);
|
||||
cap.sme2p1 = bit::test(auxv.hwcap2, 38);
|
||||
// cap.smei16i32 = bit::test(auxv.hwcap2, 39);
|
||||
// cap.smebi32i32 = bit::test(auxv.hwcap2, 40);
|
||||
cap.smeb16b16 = bit::test(auxv.hwcap2, 41);
|
||||
cap.smef16f16 = bit::test(auxv.hwcap2, 42);
|
||||
cap.mops = bit::test(auxv.hwcap2, 43);
|
||||
cap.hbc = bit::test(auxv.hwcap2, 44);
|
||||
cap.sveb16b16 = bit::test(auxv.hwcap2, 45);
|
||||
cap.lrcpc3 = bit::test(auxv.hwcap2, 46);
|
||||
cap.lse128 = bit::test(auxv.hwcap2, 47);
|
||||
cap.fpmr = bit::test(auxv.hwcap2, 48);
|
||||
cap.lut = bit::test(auxv.hwcap2, 49);
|
||||
cap.faminmax = bit::test(auxv.hwcap2, 50);
|
||||
cap.f8cvt = bit::test(auxv.hwcap2, 51);
|
||||
cap.f8fma = bit::test(auxv.hwcap2, 52);
|
||||
cap.f8dp4 = bit::test(auxv.hwcap2, 53);
|
||||
cap.f8dp2 = bit::test(auxv.hwcap2, 54);
|
||||
cap.f8e4m3 = bit::test(auxv.hwcap2, 55);
|
||||
cap.f8e5m2 = bit::test(auxv.hwcap2, 56);
|
||||
cap.smelutv2 = bit::test(auxv.hwcap2, 57);
|
||||
cap.smef8f16 = bit::test(auxv.hwcap2, 58);
|
||||
cap.smef8f32 = bit::test(auxv.hwcap2, 59);
|
||||
cap.smesf8fma = bit::test(auxv.hwcap2, 60);
|
||||
cap.smesf8dp4 = bit::test(auxv.hwcap2, 61);
|
||||
cap.smesf8dp2 = bit::test(auxv.hwcap2, 62);
|
||||
// cap.pauthlr = bit::test(auxv.hwcap2, ??);
|
||||
}
|
||||
cap
|
||||
}
|
||||
}
|
||||
|
||||
impl AtHwcap {
|
||||
/// Initializes the cache from the feature -bits.
|
||||
///
|
||||
/// The feature dependencies here come directly from LLVM's feature definitions:
|
||||
/// https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/AArch64/AArch64.td
|
||||
fn cache(self, is_exynos9810: bool) -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
{
|
||||
let mut enable_feature = |f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// Samsung Exynos 9810 has a bug that big and little cores have different
|
||||
// ISAs. And on older Android (pre-9), the kernel incorrectly reports
|
||||
// that features available only on some cores are available on all cores.
|
||||
// So, only check features that are known to be available on exynos-m3:
|
||||
// $ rustc --print cfg --target aarch64-linux-android -C target-cpu=exynos-m3 | grep target_feature
|
||||
// See also https://github.com/rust-lang/stdarch/pull/1378#discussion_r1103748342.
|
||||
if is_exynos9810 {
|
||||
enable_feature(Feature::fp, self.fp);
|
||||
enable_feature(Feature::crc, self.crc32);
|
||||
// ASIMD support requires float support - if half-floats are
|
||||
// supported, it also requires half-float support:
|
||||
let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp);
|
||||
enable_feature(Feature::asimd, asimd);
|
||||
// Cryptographic extensions require ASIMD
|
||||
// AES also covers FEAT_PMULL
|
||||
enable_feature(Feature::aes, self.aes && self.pmull && asimd);
|
||||
enable_feature(Feature::sha2, self.sha1 && self.sha2 && asimd);
|
||||
return value;
|
||||
}
|
||||
|
||||
enable_feature(Feature::fp, self.fp);
|
||||
// Half-float support requires float support
|
||||
enable_feature(Feature::fp16, self.fp && self.fphp);
|
||||
// FHM (fp16fml in LLVM) requires half float support
|
||||
enable_feature(Feature::fhm, self.fphp && self.fhm);
|
||||
enable_feature(Feature::pmull, self.pmull);
|
||||
enable_feature(Feature::crc, self.crc32);
|
||||
enable_feature(Feature::lse, self.atomics);
|
||||
enable_feature(Feature::lse2, self.uscat);
|
||||
enable_feature(Feature::lse128, self.lse128 && self.atomics);
|
||||
enable_feature(Feature::rcpc, self.lrcpc);
|
||||
// RCPC2 (rcpc-immo in LLVM) requires RCPC support
|
||||
let rcpc2 = self.ilrcpc && self.lrcpc;
|
||||
enable_feature(Feature::rcpc2, rcpc2);
|
||||
enable_feature(Feature::rcpc3, self.lrcpc3 && rcpc2);
|
||||
enable_feature(Feature::dit, self.dit);
|
||||
enable_feature(Feature::flagm, self.flagm);
|
||||
enable_feature(Feature::flagm2, self.flagm2);
|
||||
enable_feature(Feature::ssbs, self.ssbs);
|
||||
enable_feature(Feature::sb, self.sb);
|
||||
enable_feature(Feature::paca, self.paca);
|
||||
enable_feature(Feature::pacg, self.pacg);
|
||||
// enable_feature(Feature::pauth_lr, self.pauthlr);
|
||||
enable_feature(Feature::dpb, self.dcpop);
|
||||
enable_feature(Feature::dpb2, self.dcpodp);
|
||||
enable_feature(Feature::rand, self.rng);
|
||||
enable_feature(Feature::bti, self.bti);
|
||||
enable_feature(Feature::mte, self.mte);
|
||||
// jsconv requires float support
|
||||
enable_feature(Feature::jsconv, self.jscvt && self.fp);
|
||||
enable_feature(Feature::rdm, self.asimdrdm);
|
||||
enable_feature(Feature::dotprod, self.asimddp);
|
||||
enable_feature(Feature::frintts, self.frint);
|
||||
|
||||
// FEAT_I8MM & FEAT_BF16 also include optional SVE components which linux exposes
|
||||
// separately. We ignore that distinction here.
|
||||
enable_feature(Feature::i8mm, self.i8mm);
|
||||
enable_feature(Feature::bf16, self.bf16);
|
||||
|
||||
// ASIMD support requires float support - if half-floats are
|
||||
// supported, it also requires half-float support:
|
||||
let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp);
|
||||
enable_feature(Feature::asimd, asimd);
|
||||
// ASIMD extensions require ASIMD support:
|
||||
enable_feature(Feature::fcma, self.fcma && asimd);
|
||||
enable_feature(Feature::sve, self.sve && asimd);
|
||||
|
||||
// SVE extensions require SVE & ASIMD
|
||||
enable_feature(Feature::f32mm, self.svef32mm && self.sve && asimd);
|
||||
enable_feature(Feature::f64mm, self.svef64mm && self.sve && asimd);
|
||||
|
||||
// Cryptographic extensions require ASIMD
|
||||
enable_feature(Feature::aes, self.aes && asimd);
|
||||
enable_feature(Feature::sha2, self.sha1 && self.sha2 && asimd);
|
||||
// SHA512/SHA3 require SHA1 & SHA256
|
||||
enable_feature(
|
||||
Feature::sha3,
|
||||
self.sha512 && self.sha3 && self.sha1 && self.sha2 && asimd,
|
||||
);
|
||||
enable_feature(Feature::sm4, self.sm3 && self.sm4 && asimd);
|
||||
|
||||
// SVE2 requires SVE
|
||||
let sve2 = self.sve2 && self.sve && asimd;
|
||||
enable_feature(Feature::sve2, sve2);
|
||||
enable_feature(Feature::sve2p1, self.sve2p1 && sve2);
|
||||
// SVE2 extensions require SVE2 and crypto features
|
||||
enable_feature(Feature::sve2_aes, self.sveaes && self.svepmull && sve2 && self.aes);
|
||||
enable_feature(Feature::sve2_sm4, self.svesm4 && sve2 && self.sm3 && self.sm4);
|
||||
enable_feature(
|
||||
Feature::sve2_sha3,
|
||||
self.svesha3 && sve2 && self.sha512 && self.sha3 && self.sha1 && self.sha2,
|
||||
);
|
||||
enable_feature(Feature::sve2_bitperm, self.svebitperm && self.sve2);
|
||||
enable_feature(Feature::sve_b16b16, self.bf16 && self.sveb16b16);
|
||||
enable_feature(Feature::hbc, self.hbc);
|
||||
enable_feature(Feature::mops, self.mops);
|
||||
enable_feature(Feature::ecv, self.ecv);
|
||||
enable_feature(Feature::lut, self.lut);
|
||||
enable_feature(Feature::cssc, self.cssc);
|
||||
enable_feature(Feature::fpmr, self.fpmr);
|
||||
enable_feature(Feature::faminmax, self.faminmax);
|
||||
let fp8 = self.f8cvt && self.faminmax && self.lut && self.bf16;
|
||||
enable_feature(Feature::fp8, fp8);
|
||||
let fp8fma = self.f8fma && fp8;
|
||||
enable_feature(Feature::fp8fma, fp8fma);
|
||||
let fp8dot4 = self.f8dp4 && fp8fma;
|
||||
enable_feature(Feature::fp8dot4, fp8dot4);
|
||||
enable_feature(Feature::fp8dot2, self.f8dp2 && fp8dot4);
|
||||
enable_feature(Feature::wfxt, self.wfxt);
|
||||
let sme = self.sme && self.bf16;
|
||||
enable_feature(Feature::sme, sme);
|
||||
enable_feature(Feature::sme_i16i64, self.smei16i64 && sme);
|
||||
enable_feature(Feature::sme_f64f64, self.smef64f64 && sme);
|
||||
enable_feature(Feature::sme_fa64, self.smefa64 && sme && sve2);
|
||||
let sme2 = self.sme2 && sme;
|
||||
enable_feature(Feature::sme2, sme2);
|
||||
enable_feature(Feature::sme2p1, self.sme2p1 && sme2);
|
||||
enable_feature(
|
||||
Feature::sme_b16b16,
|
||||
sme2 && self.bf16 && self.sveb16b16 && self.smeb16b16,
|
||||
);
|
||||
enable_feature(Feature::sme_f16f16, self.smef16f16 && sme2);
|
||||
enable_feature(Feature::sme_lutv2, self.smelutv2);
|
||||
let sme_f8f32 = self.smef8f32 && sme2 && fp8;
|
||||
enable_feature(Feature::sme_f8f32, sme_f8f32);
|
||||
enable_feature(Feature::sme_f8f16, self.smef8f16 && sme_f8f32);
|
||||
let ssve_fp8fma = self.smesf8fma && sme2 && fp8;
|
||||
enable_feature(Feature::ssve_fp8fma, ssve_fp8fma);
|
||||
let ssve_fp8dot4 = self.smesf8dp4 && ssve_fp8fma;
|
||||
enable_feature(Feature::ssve_fp8dot4, ssve_fp8dot4);
|
||||
enable_feature(Feature::ssve_fp8dot2, self.smesf8dp2 && ssve_fp8dot4);
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_endian = "little")]
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -0,0 +1,70 @@
|
||||
use super::auxvec::auxv_from_file;
|
||||
use super::*;
|
||||
// The baseline hwcaps used in the (artificial) auxv test files.
|
||||
fn baseline_hwcaps() -> AtHwcap {
|
||||
AtHwcap {
|
||||
fp: true,
|
||||
asimd: true,
|
||||
aes: true,
|
||||
pmull: true,
|
||||
sha1: true,
|
||||
sha2: true,
|
||||
crc32: true,
|
||||
atomics: true,
|
||||
fphp: true,
|
||||
asimdhp: true,
|
||||
asimdrdm: true,
|
||||
lrcpc: true,
|
||||
dcpop: true,
|
||||
asimddp: true,
|
||||
ssbs: true,
|
||||
..AtHwcap::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linux_empty_hwcap2_aarch64() {
|
||||
let file = concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/src/detect/test_data/linux-empty-hwcap2-aarch64.auxv"
|
||||
);
|
||||
println!("file: {file}");
|
||||
let v = auxv_from_file(file).unwrap();
|
||||
println!("HWCAP : 0x{:0x}", v.hwcap);
|
||||
println!("HWCAP2: 0x{:0x}", v.hwcap2);
|
||||
assert_eq!(AtHwcap::from(v), baseline_hwcaps());
|
||||
}
|
||||
#[test]
|
||||
fn linux_no_hwcap2_aarch64() {
|
||||
let file =
|
||||
concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-no-hwcap2-aarch64.auxv");
|
||||
println!("file: {file}");
|
||||
let v = auxv_from_file(file).unwrap();
|
||||
println!("HWCAP : 0x{:0x}", v.hwcap);
|
||||
println!("HWCAP2: 0x{:0x}", v.hwcap2);
|
||||
assert_eq!(AtHwcap::from(v), baseline_hwcaps());
|
||||
}
|
||||
#[test]
|
||||
fn linux_hwcap2_aarch64() {
|
||||
let file =
|
||||
concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-hwcap2-aarch64.auxv");
|
||||
println!("file: {file}");
|
||||
let v = auxv_from_file(file).unwrap();
|
||||
println!("HWCAP : 0x{:0x}", v.hwcap);
|
||||
println!("HWCAP2: 0x{:0x}", v.hwcap2);
|
||||
assert_eq!(
|
||||
AtHwcap::from(v),
|
||||
AtHwcap {
|
||||
// Some other HWCAP bits.
|
||||
paca: true,
|
||||
pacg: true,
|
||||
// HWCAP2-only bits.
|
||||
dcpodp: true,
|
||||
frint: true,
|
||||
rng: true,
|
||||
bti: true,
|
||||
mte: true,
|
||||
..baseline_hwcaps()
|
||||
}
|
||||
);
|
||||
}
|
||||
34
crates/std/crates/std_detect/src/detect/os/linux/arm.rs
Normal file
34
crates/std/crates/std_detect/src/detect/os/linux/arm.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
//! Run-time feature detection for ARM on Linux.
|
||||
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, bit, cache};
|
||||
|
||||
/// Try to read the features from the auxiliary vector.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
let enable_feature = |value: &mut cache::Initializer, f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
|
||||
//
|
||||
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm/include/uapi/asm/hwcap.h
|
||||
if let Ok(auxv) = auxvec::auxv() {
|
||||
enable_feature(&mut value, Feature::i8mm, bit::test(auxv.hwcap, 27));
|
||||
enable_feature(&mut value, Feature::dotprod, bit::test(auxv.hwcap, 24));
|
||||
enable_feature(&mut value, Feature::neon, bit::test(auxv.hwcap, 12));
|
||||
enable_feature(&mut value, Feature::pmull, bit::test(auxv.hwcap2, 1));
|
||||
enable_feature(&mut value, Feature::crc, bit::test(auxv.hwcap2, 4));
|
||||
enable_feature(&mut value, Feature::aes, bit::test(auxv.hwcap2, 0));
|
||||
// SHA2 requires SHA1 & SHA2 features
|
||||
enable_feature(
|
||||
&mut value,
|
||||
Feature::sha2,
|
||||
bit::test(auxv.hwcap2, 2) && bit::test(auxv.hwcap2, 3),
|
||||
);
|
||||
return value;
|
||||
}
|
||||
value
|
||||
}
|
||||
221
crates/std/crates/std_detect/src/detect/os/linux/auxvec.rs
Normal file
221
crates/std/crates/std_detect/src/detect/os/linux/auxvec.rs
Normal file
@@ -0,0 +1,221 @@
|
||||
//! Parses ELF auxiliary vectors.
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub(crate) const AT_NULL: usize = 0;
|
||||
|
||||
/// Key to access the CPU Hardware capabilities bitfield.
|
||||
pub(crate) const AT_HWCAP: usize = 16;
|
||||
/// Key to access the CPU Hardware capabilities 2 bitfield.
|
||||
#[cfg(any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
))]
|
||||
pub(crate) const AT_HWCAP2: usize = 26;
|
||||
|
||||
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
|
||||
///
|
||||
/// If an entry cannot be read all the bits in the bitfield are set to zero.
|
||||
/// This should be interpreted as all the features being disabled.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub(crate) struct AuxVec {
|
||||
pub hwcap: usize,
|
||||
#[cfg(any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
))]
|
||||
pub hwcap2: usize,
|
||||
}
|
||||
|
||||
/// ELF Auxiliary Vector
|
||||
///
|
||||
/// The auxiliary vector is a memory region in a running ELF program's stack
|
||||
/// composed of (key: usize, value: usize) pairs.
|
||||
///
|
||||
/// The keys used in the aux vector are platform dependent. For Linux, they are
|
||||
/// defined in [linux/auxvec.h][auxvec_h]. The hardware capabilities of a given
|
||||
/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
|
||||
///
|
||||
/// There is no perfect way of reading the auxiliary vector.
|
||||
///
|
||||
/// - If [`getauxval`] is linked to the binary we use it, and otherwise it will
|
||||
/// try to read `/proc/self/auxv`.
|
||||
/// - If that fails, this function returns an error.
|
||||
///
|
||||
/// Note that run-time feature detection is not invoked for features that can
|
||||
/// be detected at compile-time.
|
||||
///
|
||||
/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos}*` and
|
||||
/// `*-android*` targets rather than `dlsym` it because we can safely assume
|
||||
/// `getauxval` is linked to the binary.
|
||||
/// - `*-linux-gnu*` targets ([since Rust 1.64](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html))
|
||||
/// have glibc requirements higher than [glibc 2.16 that added `getauxval`](https://sourceware.org/legacy-ml/libc-announce/2012/msg00000.html).
|
||||
/// - `*-linux-musl*` targets ([at least since Rust 1.15](https://github.com/rust-lang/rust/blob/1.15.0/src/ci/docker/x86_64-musl/build-musl.sh#L15))
|
||||
/// use musl newer than [musl 1.1.0 that added `getauxval`](https://git.musl-libc.org/cgit/musl/tree/WHATSNEW?h=v1.1.0#n1197)
|
||||
/// - `*-linux-ohos*` targets use a [fork of musl 1.2](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/native-lib/musl.md)
|
||||
/// - `*-android*` targets ([since Rust 1.68](https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html))
|
||||
/// have the minimum supported API level higher than [Android 4.3 (API level 18) that added `getauxval`](https://github.com/aosp-mirror/platform_bionic/blob/d3ebc2f7c49a9893b114124d4a6b315f3a328764/libc/include/sys/auxv.h#L49).
|
||||
///
|
||||
/// For more information about when `getauxval` is available check the great
|
||||
/// [`auxv` crate documentation][auxv_docs].
|
||||
///
|
||||
/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h
|
||||
/// [auxv_docs]: https://docs.rs/auxv/0.3.3/auxv/
|
||||
/// [`getauxval`]: https://man7.org/linux/man-pages/man3/getauxval.3.html
|
||||
pub(crate) fn auxv() -> Result<AuxVec, ()> {
|
||||
// Try to call a getauxval function.
|
||||
if let Ok(hwcap) = getauxval(AT_HWCAP) {
|
||||
// Targets with only AT_HWCAP:
|
||||
#[cfg(any(
|
||||
target_arch = "riscv32",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
target_arch = "loongarch32",
|
||||
target_arch = "loongarch64",
|
||||
))]
|
||||
{
|
||||
// Zero could indicate that no features were detected, but it's also used to indicate
|
||||
// an error. In either case, try the fallback.
|
||||
if hwcap != 0 {
|
||||
return Ok(AuxVec { hwcap });
|
||||
}
|
||||
}
|
||||
|
||||
// Targets with AT_HWCAP and AT_HWCAP2:
|
||||
#[cfg(any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
))]
|
||||
{
|
||||
if let Ok(hwcap2) = getauxval(AT_HWCAP2) {
|
||||
// Zero could indicate that no features were detected, but it's also used to indicate
|
||||
// an error. In particular, on many platforms AT_HWCAP2 will be legitimately zero,
|
||||
// since it contains the most recent feature flags. Use the fallback only if no
|
||||
// features were detected at all.
|
||||
if hwcap != 0 || hwcap2 != 0 {
|
||||
return Ok(AuxVec { hwcap, hwcap2 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Intentionnaly not used
|
||||
let _ = hwcap;
|
||||
}
|
||||
|
||||
// If calling getauxval fails, try to read the auxiliary vector from
|
||||
// its file:
|
||||
auxv_from_file("/proc/self/auxv").map_err(|_| ())
|
||||
}
|
||||
|
||||
/// Tries to read the `key` from the auxiliary vector by calling the
|
||||
/// `getauxval` function. If the function is not linked, this function return `Err`.
|
||||
fn getauxval(key: usize) -> Result<usize, ()> {
|
||||
type F = unsafe extern "C" fn(libc::c_ulong) -> libc::c_ulong;
|
||||
cfg_select! {
|
||||
any(
|
||||
all(
|
||||
target_os = "linux",
|
||||
any(target_env = "gnu", target_env = "musl", target_env = "ohos"),
|
||||
),
|
||||
target_os = "android",
|
||||
) => {
|
||||
let ffi_getauxval: F = libc::getauxval;
|
||||
}
|
||||
_ => {
|
||||
let ffi_getauxval: F = unsafe {
|
||||
let ptr = libc::dlsym(libc::RTLD_DEFAULT, c"getauxval".as_ptr());
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
core::mem::transmute(ptr)
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(unsafe { ffi_getauxval(key as libc::c_ulong) as usize })
|
||||
}
|
||||
|
||||
/// Tries to read the auxiliary vector from the `file`. If this fails, this
|
||||
/// function returns `Err`.
|
||||
pub(super) fn auxv_from_file(file: &str) -> Result<AuxVec, alloc::string::String> {
|
||||
let file = super::read_file(file)?;
|
||||
auxv_from_file_bytes(&file)
|
||||
}
|
||||
|
||||
/// Read auxiliary vector from a slice of bytes.
|
||||
pub(super) fn auxv_from_file_bytes(bytes: &[u8]) -> Result<AuxVec, alloc::string::String> {
|
||||
// See <https://github.com/torvalds/linux/blob/v5.15/include/uapi/linux/auxvec.h>.
|
||||
//
|
||||
// The auxiliary vector contains at most 34 (key,value) fields: from
|
||||
// `AT_MINSIGSTKSZ` to `AT_NULL`, but its number may increase.
|
||||
let len = bytes.len();
|
||||
let mut buf = alloc::vec![0_usize; 1 + len / core::mem::size_of::<usize>()];
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr() as *mut u8, len);
|
||||
}
|
||||
|
||||
auxv_from_buf(&buf)
|
||||
}
|
||||
|
||||
/// Tries to interpret the `buffer` as an auxiliary vector. If that fails, this
|
||||
/// function returns `Err`.
|
||||
fn auxv_from_buf(buf: &[usize]) -> Result<AuxVec, alloc::string::String> {
|
||||
// Targets with only AT_HWCAP:
|
||||
#[cfg(any(
|
||||
target_arch = "riscv32",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips64",
|
||||
target_arch = "loongarch32",
|
||||
target_arch = "loongarch64",
|
||||
))]
|
||||
{
|
||||
for el in buf.chunks(2) {
|
||||
match el[0] {
|
||||
AT_NULL => break,
|
||||
AT_HWCAP => return Ok(AuxVec { hwcap: el[1] }),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Targets with AT_HWCAP and AT_HWCAP2:
|
||||
#[cfg(any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
))]
|
||||
{
|
||||
let mut hwcap = None;
|
||||
// For some platforms, AT_HWCAP2 was added recently, so let it default to zero.
|
||||
let mut hwcap2 = 0;
|
||||
for el in buf.chunks(2) {
|
||||
match el[0] {
|
||||
AT_NULL => break,
|
||||
AT_HWCAP => hwcap = Some(el[1]),
|
||||
AT_HWCAP2 => hwcap2 = el[1],
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(hwcap) = hwcap {
|
||||
return Ok(AuxVec { hwcap, hwcap2 });
|
||||
}
|
||||
}
|
||||
// Suppress unused variable
|
||||
let _ = buf;
|
||||
Err(alloc::string::String::from("hwcap not found"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
108
crates/std/crates/std_detect/src/detect/os/linux/auxvec/tests.rs
Normal file
108
crates/std/crates/std_detect/src/detect/os/linux/auxvec/tests.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use super::*;
|
||||
|
||||
// FIXME: on mips/mips64 getauxval returns 0, and /proc/self/auxv
|
||||
// does not always contain the AT_HWCAP key under qemu.
|
||||
#[cfg(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
))]
|
||||
#[test]
|
||||
fn auxv_crate() {
|
||||
let v = auxv();
|
||||
if let Ok(hwcap) = getauxval(AT_HWCAP) {
|
||||
let rt_hwcap = v.expect("failed to find hwcap key").hwcap;
|
||||
assert_eq!(rt_hwcap, hwcap);
|
||||
}
|
||||
|
||||
// Targets with AT_HWCAP and AT_HWCAP2:
|
||||
#[cfg(any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
))]
|
||||
{
|
||||
if let Ok(hwcap2) = getauxval(AT_HWCAP2) {
|
||||
let rt_hwcap2 = v.expect("failed to find hwcap2 key").hwcap2;
|
||||
assert_eq!(rt_hwcap2, hwcap2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auxv_dump() {
|
||||
if let Ok(auxvec) = auxv() {
|
||||
println!("{:?}", auxvec);
|
||||
} else {
|
||||
println!("both getauxval() and reading /proc/self/auxv failed!");
|
||||
}
|
||||
}
|
||||
|
||||
cfg_select! {
|
||||
target_arch = "arm" => {
|
||||
// The tests below can be executed under qemu, where we do not have access to the test
|
||||
// files on disk, so we need to embed them with `include_bytes!`.
|
||||
#[test]
|
||||
fn linux_rpi3() {
|
||||
let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-rpi3.auxv"));
|
||||
let v = auxv_from_file_bytes(auxv).unwrap();
|
||||
assert_eq!(v.hwcap, 4174038);
|
||||
assert_eq!(v.hwcap2, 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linux_macos_vb() {
|
||||
let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv"));
|
||||
// The file contains HWCAP but not HWCAP2. In that case, we treat HWCAP2 as zero.
|
||||
let v = auxv_from_file_bytes(auxv).unwrap();
|
||||
assert_eq!(v.hwcap, 126614527);
|
||||
assert_eq!(v.hwcap2, 0);
|
||||
}
|
||||
}
|
||||
target_arch = "aarch64" => {
|
||||
#[cfg(target_endian = "little")]
|
||||
#[test]
|
||||
fn linux_artificial_aarch64() {
|
||||
let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-artificial-aarch64.auxv"));
|
||||
let v = auxv_from_file_bytes(auxv).unwrap();
|
||||
assert_eq!(v.hwcap, 0x0123456789abcdef);
|
||||
assert_eq!(v.hwcap2, 0x02468ace13579bdf);
|
||||
}
|
||||
#[cfg(target_endian = "little")]
|
||||
#[test]
|
||||
fn linux_no_hwcap2_aarch64() {
|
||||
let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-no-hwcap2-aarch64.auxv"));
|
||||
let v = auxv_from_file_bytes(auxv).unwrap();
|
||||
// An absent HWCAP2 is treated as zero, and does not prevent acceptance of HWCAP.
|
||||
assert_ne!(v.hwcap, 0);
|
||||
assert_eq!(v.hwcap2, 0);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auxv_dump_procfs() {
|
||||
if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") {
|
||||
println!("{:?}", auxvec);
|
||||
} else {
|
||||
println!("reading /proc/self/auxv failed!");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
))]
|
||||
#[test]
|
||||
fn auxv_crate_procfs() {
|
||||
if let Ok(procfs_auxv) = auxv_from_file("/proc/self/auxv") {
|
||||
assert_eq!(auxv().unwrap(), procfs_auxv);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//! Run-time feature detection for LoongArch on Linux.
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, bit, cache};
|
||||
|
||||
/// Try to read the features from the auxiliary vector.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
let enable_feature = |value: &mut cache::Initializer, feature, enable| {
|
||||
if enable {
|
||||
value.set(feature as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// The values are part of the platform-specific [cpucfg]
|
||||
//
|
||||
// [cpucfg]: LoongArch Reference Manual Volume 1: Basic Architecture v1.1
|
||||
let cpucfg1: usize;
|
||||
let cpucfg2: usize;
|
||||
let cpucfg3: usize;
|
||||
unsafe {
|
||||
asm!(
|
||||
"cpucfg {}, {}",
|
||||
"cpucfg {}, {}",
|
||||
"cpucfg {}, {}",
|
||||
out(reg) cpucfg1, in(reg) 1,
|
||||
out(reg) cpucfg2, in(reg) 2,
|
||||
out(reg) cpucfg3, in(reg) 3,
|
||||
options(pure, nomem, preserves_flags, nostack)
|
||||
);
|
||||
}
|
||||
enable_feature(&mut value, Feature::_32s, bit::test(cpucfg1, 0) || bit::test(cpucfg1, 1));
|
||||
enable_feature(&mut value, Feature::frecipe, bit::test(cpucfg2, 25));
|
||||
enable_feature(&mut value, Feature::div32, bit::test(cpucfg2, 26));
|
||||
enable_feature(&mut value, Feature::lam_bh, bit::test(cpucfg2, 27));
|
||||
enable_feature(&mut value, Feature::lamcas, bit::test(cpucfg2, 28));
|
||||
enable_feature(&mut value, Feature::scq, bit::test(cpucfg2, 30));
|
||||
enable_feature(&mut value, Feature::ld_seq_sa, bit::test(cpucfg3, 23));
|
||||
|
||||
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
|
||||
//
|
||||
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/loongarch/include/uapi/asm/hwcap.h
|
||||
if let Ok(auxv) = auxvec::auxv() {
|
||||
enable_feature(&mut value, Feature::f, bit::test(cpucfg2, 1) && bit::test(auxv.hwcap, 3));
|
||||
enable_feature(&mut value, Feature::d, bit::test(cpucfg2, 2) && bit::test(auxv.hwcap, 3));
|
||||
enable_feature(&mut value, Feature::lsx, bit::test(auxv.hwcap, 4));
|
||||
enable_feature(&mut value, Feature::lasx, bit::test(auxv.hwcap, 5));
|
||||
enable_feature(
|
||||
&mut value,
|
||||
Feature::lbt,
|
||||
bit::test(auxv.hwcap, 10) && bit::test(auxv.hwcap, 11) && bit::test(auxv.hwcap, 12),
|
||||
);
|
||||
enable_feature(&mut value, Feature::lvz, bit::test(auxv.hwcap, 9));
|
||||
enable_feature(&mut value, Feature::ual, bit::test(auxv.hwcap, 2));
|
||||
return value;
|
||||
}
|
||||
value
|
||||
}
|
||||
23
crates/std/crates/std_detect/src/detect/os/linux/mips.rs
Normal file
23
crates/std/crates/std_detect/src/detect/os/linux/mips.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
//! Run-time feature detection for MIPS on Linux.
|
||||
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, bit, cache};
|
||||
|
||||
/// Try to read the features from the auxiliary vector.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
let enable_feature = |value: &mut cache::Initializer, f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
|
||||
//
|
||||
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/mips/include/uapi/asm/hwcap.h
|
||||
if let Ok(auxv) = auxvec::auxv() {
|
||||
enable_feature(&mut value, Feature::msa, bit::test(auxv.hwcap, 1));
|
||||
return value;
|
||||
}
|
||||
value
|
||||
}
|
||||
74
crates/std/crates/std_detect/src/detect/os/linux/mod.rs
Normal file
74
crates/std/crates/std_detect/src/detect/os/linux/mod.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
//! Run-time feature detection on Linux
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
mod auxvec;
|
||||
|
||||
fn read_file(orig_path: &str) -> Result<Vec<u8>, alloc::string::String> {
|
||||
use alloc::format;
|
||||
|
||||
let mut path = Vec::from(orig_path.as_bytes());
|
||||
path.push(0);
|
||||
|
||||
unsafe {
|
||||
let file = libc::open(path.as_ptr() as *const libc::c_char, libc::O_RDONLY);
|
||||
if file == -1 {
|
||||
return Err(format!("Cannot open file at {orig_path}"));
|
||||
}
|
||||
|
||||
let mut data = Vec::new();
|
||||
loop {
|
||||
data.reserve(4096);
|
||||
let spare = data.spare_capacity_mut();
|
||||
match libc::read(file, spare.as_mut_ptr() as *mut _, spare.len()) {
|
||||
-1 => {
|
||||
libc::close(file);
|
||||
return Err(format!("Error while reading from file at {orig_path}"));
|
||||
}
|
||||
0 => break,
|
||||
n => data.set_len(data.len() + n as usize),
|
||||
}
|
||||
}
|
||||
|
||||
libc::close(file);
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
||||
cfg_select! {
|
||||
target_arch = "aarch64" => {
|
||||
mod aarch64;
|
||||
pub(crate) use self::aarch64::detect_features;
|
||||
}
|
||||
target_arch = "arm" => {
|
||||
mod arm;
|
||||
pub(crate) use self::arm::detect_features;
|
||||
}
|
||||
any(target_arch = "riscv32", target_arch = "riscv64") => {
|
||||
mod riscv;
|
||||
pub(crate) use self::riscv::detect_features;
|
||||
}
|
||||
any(target_arch = "mips", target_arch = "mips64") => {
|
||||
mod mips;
|
||||
pub(crate) use self::mips::detect_features;
|
||||
}
|
||||
any(target_arch = "powerpc", target_arch = "powerpc64") => {
|
||||
mod powerpc;
|
||||
pub(crate) use self::powerpc::detect_features;
|
||||
}
|
||||
any(target_arch = "loongarch32", target_arch = "loongarch64") => {
|
||||
mod loongarch;
|
||||
pub(crate) use self::loongarch::detect_features;
|
||||
}
|
||||
target_arch = "s390x" => {
|
||||
mod s390x;
|
||||
pub(crate) use self::s390x::detect_features;
|
||||
}
|
||||
_ => {
|
||||
use crate::detect::cache;
|
||||
/// Performs run-time feature detection.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
cache::Initializer::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
35
crates/std/crates/std_detect/src/detect/os/linux/powerpc.rs
Normal file
35
crates/std/crates/std_detect/src/detect/os/linux/powerpc.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
//! Run-time feature detection for PowerPC on Linux.
|
||||
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, cache};
|
||||
|
||||
/// Try to read the features from the auxiliary vector.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
let enable_feature = |value: &mut cache::Initializer, f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// The values are part of the platform-specific [asm/cputable.h][cputable]
|
||||
//
|
||||
// [cputable]: https://github.com/torvalds/linux/blob/master/arch/powerpc/include/uapi/asm/cputable.h
|
||||
if let Ok(auxv) = auxvec::auxv() {
|
||||
// note: the PowerPC values are the mask to do the test (instead of the
|
||||
// index of the bit to test like in ARM and Aarch64)
|
||||
enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0);
|
||||
enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0);
|
||||
let power8_features = auxv.hwcap2 & 0x80000000 != 0;
|
||||
enable_feature(&mut value, Feature::power8, power8_features);
|
||||
enable_feature(&mut value, Feature::power8_altivec, power8_features);
|
||||
enable_feature(&mut value, Feature::power8_crypto, power8_features);
|
||||
enable_feature(&mut value, Feature::power8_vector, power8_features);
|
||||
let power9_features = auxv.hwcap2 & 0x00800000 != 0;
|
||||
enable_feature(&mut value, Feature::power9, power9_features);
|
||||
enable_feature(&mut value, Feature::power9_altivec, power9_features);
|
||||
enable_feature(&mut value, Feature::power9_vector, power9_features);
|
||||
return value;
|
||||
}
|
||||
value
|
||||
}
|
||||
323
crates/std/crates/std_detect/src/detect/os/linux/riscv.rs
Normal file
323
crates/std/crates/std_detect/src/detect/os/linux/riscv.rs
Normal file
@@ -0,0 +1,323 @@
|
||||
//! Run-time feature detection for RISC-V on Linux.
|
||||
//!
|
||||
//! On RISC-V, detection using auxv only supports single-letter extensions.
|
||||
//! So, we use riscv_hwprobe that supports multi-letter extensions if available.
|
||||
//! <https://www.kernel.org/doc/html/latest/arch/riscv/hwprobe.html>
|
||||
|
||||
use core::ptr;
|
||||
|
||||
use super::super::riscv::imply_features;
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, bit, cache};
|
||||
|
||||
// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/prctl.h?h=v6.16>
|
||||
// for runtime status query constants.
|
||||
const PR_RISCV_V_GET_CONTROL: libc::c_int = 70;
|
||||
const PR_RISCV_V_VSTATE_CTRL_ON: libc::c_int = 2;
|
||||
const PR_RISCV_V_VSTATE_CTRL_CUR_MASK: libc::c_int = 3;
|
||||
|
||||
// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwprobe.h?h=v6.16>
|
||||
// for riscv_hwprobe struct and hardware probing constants.
|
||||
|
||||
#[repr(C)]
|
||||
struct riscv_hwprobe {
|
||||
key: i64,
|
||||
value: u64,
|
||||
}
|
||||
|
||||
impl riscv_hwprobe {
|
||||
// key is overwritten to -1 if not supported by riscv_hwprobe syscall.
|
||||
pub fn get(&self) -> Option<u64> {
|
||||
(self.key != -1).then_some(self.value)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const __NR_riscv_hwprobe: libc::c_long = 258;
|
||||
|
||||
const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3;
|
||||
const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0;
|
||||
|
||||
const RISCV_HWPROBE_KEY_IMA_EXT_0: i64 = 4;
|
||||
const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0;
|
||||
const RISCV_HWPROBE_IMA_C: u64 = 1 << 1;
|
||||
const RISCV_HWPROBE_IMA_V: u64 = 1 << 2;
|
||||
const RISCV_HWPROBE_EXT_ZBA: u64 = 1 << 3;
|
||||
const RISCV_HWPROBE_EXT_ZBB: u64 = 1 << 4;
|
||||
const RISCV_HWPROBE_EXT_ZBS: u64 = 1 << 5;
|
||||
const RISCV_HWPROBE_EXT_ZICBOZ: u64 = 1 << 6;
|
||||
const RISCV_HWPROBE_EXT_ZBC: u64 = 1 << 7;
|
||||
const RISCV_HWPROBE_EXT_ZBKB: u64 = 1 << 8;
|
||||
const RISCV_HWPROBE_EXT_ZBKC: u64 = 1 << 9;
|
||||
const RISCV_HWPROBE_EXT_ZBKX: u64 = 1 << 10;
|
||||
const RISCV_HWPROBE_EXT_ZKND: u64 = 1 << 11;
|
||||
const RISCV_HWPROBE_EXT_ZKNE: u64 = 1 << 12;
|
||||
const RISCV_HWPROBE_EXT_ZKNH: u64 = 1 << 13;
|
||||
const RISCV_HWPROBE_EXT_ZKSED: u64 = 1 << 14;
|
||||
const RISCV_HWPROBE_EXT_ZKSH: u64 = 1 << 15;
|
||||
const RISCV_HWPROBE_EXT_ZKT: u64 = 1 << 16;
|
||||
const RISCV_HWPROBE_EXT_ZVBB: u64 = 1 << 17;
|
||||
const RISCV_HWPROBE_EXT_ZVBC: u64 = 1 << 18;
|
||||
const RISCV_HWPROBE_EXT_ZVKB: u64 = 1 << 19;
|
||||
const RISCV_HWPROBE_EXT_ZVKG: u64 = 1 << 20;
|
||||
const RISCV_HWPROBE_EXT_ZVKNED: u64 = 1 << 21;
|
||||
const RISCV_HWPROBE_EXT_ZVKNHA: u64 = 1 << 22;
|
||||
const RISCV_HWPROBE_EXT_ZVKNHB: u64 = 1 << 23;
|
||||
const RISCV_HWPROBE_EXT_ZVKSED: u64 = 1 << 24;
|
||||
const RISCV_HWPROBE_EXT_ZVKSH: u64 = 1 << 25;
|
||||
const RISCV_HWPROBE_EXT_ZVKT: u64 = 1 << 26;
|
||||
const RISCV_HWPROBE_EXT_ZFH: u64 = 1 << 27;
|
||||
const RISCV_HWPROBE_EXT_ZFHMIN: u64 = 1 << 28;
|
||||
const RISCV_HWPROBE_EXT_ZIHINTNTL: u64 = 1 << 29;
|
||||
const RISCV_HWPROBE_EXT_ZVFH: u64 = 1 << 30;
|
||||
const RISCV_HWPROBE_EXT_ZVFHMIN: u64 = 1 << 31;
|
||||
const RISCV_HWPROBE_EXT_ZFA: u64 = 1 << 32;
|
||||
const RISCV_HWPROBE_EXT_ZTSO: u64 = 1 << 33;
|
||||
const RISCV_HWPROBE_EXT_ZACAS: u64 = 1 << 34;
|
||||
const RISCV_HWPROBE_EXT_ZICOND: u64 = 1 << 35;
|
||||
const RISCV_HWPROBE_EXT_ZIHINTPAUSE: u64 = 1 << 36;
|
||||
const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37;
|
||||
const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38;
|
||||
const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39;
|
||||
const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40;
|
||||
const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41;
|
||||
const RISCV_HWPROBE_EXT_ZIMOP: u64 = 1 << 42;
|
||||
const RISCV_HWPROBE_EXT_ZCA: u64 = 1 << 43;
|
||||
const RISCV_HWPROBE_EXT_ZCB: u64 = 1 << 44;
|
||||
const RISCV_HWPROBE_EXT_ZCD: u64 = 1 << 45;
|
||||
const RISCV_HWPROBE_EXT_ZCF: u64 = 1 << 46;
|
||||
const RISCV_HWPROBE_EXT_ZCMOP: u64 = 1 << 47;
|
||||
const RISCV_HWPROBE_EXT_ZAWRS: u64 = 1 << 48;
|
||||
// Excluded because it only reports the existence of `prctl`-based pointer masking control.
|
||||
// const RISCV_HWPROBE_EXT_SUPM: u64 = 1 << 49;
|
||||
const RISCV_HWPROBE_EXT_ZICNTR: u64 = 1 << 50;
|
||||
const RISCV_HWPROBE_EXT_ZIHPM: u64 = 1 << 51;
|
||||
const RISCV_HWPROBE_EXT_ZFBFMIN: u64 = 1 << 52;
|
||||
const RISCV_HWPROBE_EXT_ZVFBFMIN: u64 = 1 << 53;
|
||||
const RISCV_HWPROBE_EXT_ZVFBFWMA: u64 = 1 << 54;
|
||||
const RISCV_HWPROBE_EXT_ZICBOM: u64 = 1 << 55;
|
||||
const RISCV_HWPROBE_EXT_ZAAMO: u64 = 1 << 56;
|
||||
const RISCV_HWPROBE_EXT_ZALRSC: u64 = 1 << 57;
|
||||
const RISCV_HWPROBE_EXT_ZABHA: u64 = 1 << 58;
|
||||
|
||||
const RISCV_HWPROBE_KEY_CPUPERF_0: i64 = 5;
|
||||
const RISCV_HWPROBE_MISALIGNED_FAST: u64 = 3;
|
||||
const RISCV_HWPROBE_MISALIGNED_MASK: u64 = 7;
|
||||
|
||||
const RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF: i64 = 9;
|
||||
const RISCV_HWPROBE_MISALIGNED_SCALAR_FAST: u64 = 3;
|
||||
|
||||
const RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF: i64 = 10;
|
||||
const RISCV_HWPROBE_MISALIGNED_VECTOR_FAST: u64 = 3;
|
||||
|
||||
// syscall returns an unsupported error if riscv_hwprobe is not supported,
|
||||
// so we can safely use this function on older versions of Linux.
|
||||
fn _riscv_hwprobe(out: &mut [riscv_hwprobe]) -> bool {
|
||||
unsafe fn __riscv_hwprobe(
|
||||
pairs: *mut riscv_hwprobe,
|
||||
pair_count: libc::size_t,
|
||||
cpu_set_size: libc::size_t,
|
||||
cpus: *mut libc::c_ulong,
|
||||
flags: libc::c_uint,
|
||||
) -> libc::c_long {
|
||||
unsafe { libc::syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_set_size, cpus, flags) }
|
||||
}
|
||||
|
||||
unsafe { __riscv_hwprobe(out.as_mut_ptr(), out.len(), 0, ptr::null_mut(), 0) == 0 }
|
||||
}
|
||||
|
||||
/// Read list of supported features from (1) the auxiliary vector
|
||||
/// and (2) the results of `riscv_hwprobe` and `prctl` system calls.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
let mut enable_feature = |feature, enable| {
|
||||
if enable {
|
||||
value.set(feature as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// Use auxiliary vector to enable single-letter ISA extensions.
|
||||
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
|
||||
//
|
||||
// [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.16
|
||||
let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform
|
||||
let mut has_i = bit::test(auxv.hwcap, (b'i' - b'a').into());
|
||||
#[allow(clippy::eq_op)]
|
||||
enable_feature(Feature::a, bit::test(auxv.hwcap, (b'a' - b'a').into()));
|
||||
enable_feature(Feature::c, bit::test(auxv.hwcap, (b'c' - b'a').into()));
|
||||
enable_feature(Feature::d, bit::test(auxv.hwcap, (b'd' - b'a').into()));
|
||||
enable_feature(Feature::f, bit::test(auxv.hwcap, (b'f' - b'a').into()));
|
||||
enable_feature(Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into()));
|
||||
let has_v = bit::test(auxv.hwcap, (b'v' - b'a').into());
|
||||
let mut is_v_set = false;
|
||||
|
||||
// Use riscv_hwprobe syscall to query more extensions and
|
||||
// performance-related capabilities.
|
||||
'hwprobe: {
|
||||
macro_rules! init {
|
||||
{ $($name: ident : $key: expr),* $(,)? } => {
|
||||
#[repr(usize)]
|
||||
enum Indices { $($name),* }
|
||||
let mut t = [$(riscv_hwprobe { key: $key, value: 0 }),*];
|
||||
macro_rules! data_mut { () => { &mut t } }
|
||||
macro_rules! query { [$idx: ident] => { t[Indices::$idx as usize].get() } }
|
||||
}
|
||||
}
|
||||
init! {
|
||||
BaseBehavior: RISCV_HWPROBE_KEY_BASE_BEHAVIOR,
|
||||
Extensions: RISCV_HWPROBE_KEY_IMA_EXT_0,
|
||||
MisalignedScalarPerf: RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF,
|
||||
MisalignedVectorPerf: RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF,
|
||||
MisalignedScalarPerfFallback: RISCV_HWPROBE_KEY_CPUPERF_0,
|
||||
};
|
||||
if !_riscv_hwprobe(data_mut!()) {
|
||||
break 'hwprobe;
|
||||
}
|
||||
|
||||
// Query scalar misaligned behavior.
|
||||
if let Some(value) = query![MisalignedScalarPerf] {
|
||||
enable_feature(
|
||||
Feature::unaligned_scalar_mem,
|
||||
value == RISCV_HWPROBE_MISALIGNED_SCALAR_FAST,
|
||||
);
|
||||
} else if let Some(value) = query![MisalignedScalarPerfFallback] {
|
||||
// Deprecated method for fallback
|
||||
enable_feature(
|
||||
Feature::unaligned_scalar_mem,
|
||||
value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST,
|
||||
);
|
||||
}
|
||||
|
||||
// Query vector misaligned behavior.
|
||||
if let Some(value) = query![MisalignedVectorPerf] {
|
||||
enable_feature(
|
||||
Feature::unaligned_vector_mem,
|
||||
value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST,
|
||||
);
|
||||
}
|
||||
|
||||
// Query whether "I" base and extensions "M" and "A" (as in the ISA
|
||||
// manual version 2.2) are enabled. "I" base at that time corresponds
|
||||
// to "I", "Zicsr", "Zicntr" and "Zifencei" (as in the ISA manual version
|
||||
// 20240411).
|
||||
// This is a current requirement of
|
||||
// `RISCV_HWPROBE_KEY_IMA_EXT_0`-based tests.
|
||||
if query![BaseBehavior].is_none_or(|value| value & RISCV_HWPROBE_BASE_BEHAVIOR_IMA == 0) {
|
||||
break 'hwprobe;
|
||||
}
|
||||
has_i = true;
|
||||
enable_feature(Feature::zicsr, true);
|
||||
enable_feature(Feature::zicntr, true);
|
||||
enable_feature(Feature::zifencei, true);
|
||||
enable_feature(Feature::m, true);
|
||||
enable_feature(Feature::a, true);
|
||||
|
||||
// Enable features based on `RISCV_HWPROBE_KEY_IMA_EXT_0`.
|
||||
let Some(ima_ext_0) = query![Extensions] else {
|
||||
break 'hwprobe;
|
||||
};
|
||||
let test = |mask| (ima_ext_0 & mask) != 0;
|
||||
|
||||
enable_feature(Feature::d, test(RISCV_HWPROBE_IMA_FD)); // F is implied.
|
||||
enable_feature(Feature::c, test(RISCV_HWPROBE_IMA_C));
|
||||
|
||||
enable_feature(Feature::zicntr, test(RISCV_HWPROBE_EXT_ZICNTR));
|
||||
enable_feature(Feature::zihpm, test(RISCV_HWPROBE_EXT_ZIHPM));
|
||||
|
||||
enable_feature(Feature::zihintntl, test(RISCV_HWPROBE_EXT_ZIHINTNTL));
|
||||
enable_feature(Feature::zihintpause, test(RISCV_HWPROBE_EXT_ZIHINTPAUSE));
|
||||
enable_feature(Feature::zimop, test(RISCV_HWPROBE_EXT_ZIMOP));
|
||||
enable_feature(Feature::zicbom, test(RISCV_HWPROBE_EXT_ZICBOM));
|
||||
enable_feature(Feature::zicboz, test(RISCV_HWPROBE_EXT_ZICBOZ));
|
||||
enable_feature(Feature::zicond, test(RISCV_HWPROBE_EXT_ZICOND));
|
||||
|
||||
enable_feature(Feature::zalrsc, test(RISCV_HWPROBE_EXT_ZALRSC));
|
||||
enable_feature(Feature::zaamo, test(RISCV_HWPROBE_EXT_ZAAMO));
|
||||
enable_feature(Feature::zawrs, test(RISCV_HWPROBE_EXT_ZAWRS));
|
||||
enable_feature(Feature::zabha, test(RISCV_HWPROBE_EXT_ZABHA));
|
||||
enable_feature(Feature::zacas, test(RISCV_HWPROBE_EXT_ZACAS));
|
||||
enable_feature(Feature::ztso, test(RISCV_HWPROBE_EXT_ZTSO));
|
||||
|
||||
enable_feature(Feature::zba, test(RISCV_HWPROBE_EXT_ZBA));
|
||||
enable_feature(Feature::zbb, test(RISCV_HWPROBE_EXT_ZBB));
|
||||
enable_feature(Feature::zbs, test(RISCV_HWPROBE_EXT_ZBS));
|
||||
enable_feature(Feature::zbc, test(RISCV_HWPROBE_EXT_ZBC));
|
||||
|
||||
enable_feature(Feature::zbkb, test(RISCV_HWPROBE_EXT_ZBKB));
|
||||
enable_feature(Feature::zbkc, test(RISCV_HWPROBE_EXT_ZBKC));
|
||||
enable_feature(Feature::zbkx, test(RISCV_HWPROBE_EXT_ZBKX));
|
||||
enable_feature(Feature::zknd, test(RISCV_HWPROBE_EXT_ZKND));
|
||||
enable_feature(Feature::zkne, test(RISCV_HWPROBE_EXT_ZKNE));
|
||||
enable_feature(Feature::zknh, test(RISCV_HWPROBE_EXT_ZKNH));
|
||||
enable_feature(Feature::zksed, test(RISCV_HWPROBE_EXT_ZKSED));
|
||||
enable_feature(Feature::zksh, test(RISCV_HWPROBE_EXT_ZKSH));
|
||||
enable_feature(Feature::zkt, test(RISCV_HWPROBE_EXT_ZKT));
|
||||
|
||||
enable_feature(Feature::zcmop, test(RISCV_HWPROBE_EXT_ZCMOP));
|
||||
enable_feature(Feature::zca, test(RISCV_HWPROBE_EXT_ZCA));
|
||||
enable_feature(Feature::zcf, test(RISCV_HWPROBE_EXT_ZCF));
|
||||
enable_feature(Feature::zcd, test(RISCV_HWPROBE_EXT_ZCD));
|
||||
enable_feature(Feature::zcb, test(RISCV_HWPROBE_EXT_ZCB));
|
||||
|
||||
enable_feature(Feature::zfh, test(RISCV_HWPROBE_EXT_ZFH));
|
||||
enable_feature(Feature::zfhmin, test(RISCV_HWPROBE_EXT_ZFHMIN));
|
||||
enable_feature(Feature::zfa, test(RISCV_HWPROBE_EXT_ZFA));
|
||||
enable_feature(Feature::zfbfmin, test(RISCV_HWPROBE_EXT_ZFBFMIN));
|
||||
|
||||
// Use prctl (if any) to determine whether the vector extension
|
||||
// is enabled on the current thread (assuming the entire process
|
||||
// share the same status). If prctl fails (e.g. QEMU userland emulator
|
||||
// as of version 9.2.3), use auxiliary vector to retrieve the default
|
||||
// vector status on the process startup.
|
||||
let has_vectors = {
|
||||
let v_status = unsafe { libc::prctl(PR_RISCV_V_GET_CONTROL) };
|
||||
if v_status >= 0 {
|
||||
(v_status & PR_RISCV_V_VSTATE_CTRL_CUR_MASK) == PR_RISCV_V_VSTATE_CTRL_ON
|
||||
} else {
|
||||
has_v
|
||||
}
|
||||
};
|
||||
if has_vectors {
|
||||
enable_feature(Feature::v, test(RISCV_HWPROBE_IMA_V));
|
||||
enable_feature(Feature::zve32x, test(RISCV_HWPROBE_EXT_ZVE32X));
|
||||
enable_feature(Feature::zve32f, test(RISCV_HWPROBE_EXT_ZVE32F));
|
||||
enable_feature(Feature::zve64x, test(RISCV_HWPROBE_EXT_ZVE64X));
|
||||
enable_feature(Feature::zve64f, test(RISCV_HWPROBE_EXT_ZVE64F));
|
||||
enable_feature(Feature::zve64d, test(RISCV_HWPROBE_EXT_ZVE64D));
|
||||
|
||||
enable_feature(Feature::zvbb, test(RISCV_HWPROBE_EXT_ZVBB));
|
||||
enable_feature(Feature::zvbc, test(RISCV_HWPROBE_EXT_ZVBC));
|
||||
enable_feature(Feature::zvkb, test(RISCV_HWPROBE_EXT_ZVKB));
|
||||
enable_feature(Feature::zvkg, test(RISCV_HWPROBE_EXT_ZVKG));
|
||||
enable_feature(Feature::zvkned, test(RISCV_HWPROBE_EXT_ZVKNED));
|
||||
enable_feature(Feature::zvknha, test(RISCV_HWPROBE_EXT_ZVKNHA));
|
||||
enable_feature(Feature::zvknhb, test(RISCV_HWPROBE_EXT_ZVKNHB));
|
||||
enable_feature(Feature::zvksed, test(RISCV_HWPROBE_EXT_ZVKSED));
|
||||
enable_feature(Feature::zvksh, test(RISCV_HWPROBE_EXT_ZVKSH));
|
||||
enable_feature(Feature::zvkt, test(RISCV_HWPROBE_EXT_ZVKT));
|
||||
|
||||
enable_feature(Feature::zvfh, test(RISCV_HWPROBE_EXT_ZVFH));
|
||||
enable_feature(Feature::zvfhmin, test(RISCV_HWPROBE_EXT_ZVFHMIN));
|
||||
enable_feature(Feature::zvfbfmin, test(RISCV_HWPROBE_EXT_ZVFBFMIN));
|
||||
enable_feature(Feature::zvfbfwma, test(RISCV_HWPROBE_EXT_ZVFBFWMA));
|
||||
}
|
||||
is_v_set = true;
|
||||
};
|
||||
|
||||
// Set V purely depending on the auxiliary vector
|
||||
// only if no fine-grained vector extension detection is available.
|
||||
if !is_v_set {
|
||||
enable_feature(Feature::v, has_v);
|
||||
}
|
||||
|
||||
// Handle base ISA.
|
||||
// If future RV128I is supported, implement with `enable_feature` here.
|
||||
// Note that we should use `target_arch` instead of `target_pointer_width`
|
||||
// to avoid misdetection caused by experimental ABIs such as RV64ILP32.
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
enable_feature(Feature::rv64i, has_i);
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
enable_feature(Feature::rv32i, has_i);
|
||||
|
||||
imply_features(value)
|
||||
}
|
||||
152
crates/std/crates/std_detect/src/detect/os/linux/s390x.rs
Normal file
152
crates/std/crates/std_detect/src/detect/os/linux/s390x.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
//! Run-time feature detection for s390x on Linux.
|
||||
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, bit, cache};
|
||||
|
||||
/// Try to read the features from the auxiliary vector
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let opt_hwcap: Option<AtHwcap> = auxvec::auxv().ok().map(Into::into);
|
||||
let facilities = ExtendedFacilityList::new();
|
||||
cache(opt_hwcap, facilities)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
struct AtHwcap {
|
||||
esan3: bool,
|
||||
zarch: bool,
|
||||
stfle: bool,
|
||||
msa: bool,
|
||||
ldisp: bool,
|
||||
eimm: bool,
|
||||
dfp: bool,
|
||||
hpage: bool,
|
||||
etf3eh: bool,
|
||||
high_gprs: bool,
|
||||
te: bool,
|
||||
vxrs: bool,
|
||||
vxrs_bcd: bool,
|
||||
vxrs_ext: bool,
|
||||
gs: bool,
|
||||
vxrs_ext2: bool,
|
||||
vxrs_pde: bool,
|
||||
sort: bool,
|
||||
dflt: bool,
|
||||
vxrs_pde2: bool,
|
||||
nnpa: bool,
|
||||
pci_mio: bool,
|
||||
sie: bool,
|
||||
}
|
||||
|
||||
impl From<auxvec::AuxVec> for AtHwcap {
|
||||
/// Reads AtHwcap from the auxiliary vector.
|
||||
fn from(auxv: auxvec::AuxVec) -> Self {
|
||||
AtHwcap {
|
||||
esan3: bit::test(auxv.hwcap, 0),
|
||||
zarch: bit::test(auxv.hwcap, 1),
|
||||
stfle: bit::test(auxv.hwcap, 2),
|
||||
msa: bit::test(auxv.hwcap, 3),
|
||||
ldisp: bit::test(auxv.hwcap, 4),
|
||||
eimm: bit::test(auxv.hwcap, 5),
|
||||
dfp: bit::test(auxv.hwcap, 6),
|
||||
hpage: bit::test(auxv.hwcap, 7),
|
||||
etf3eh: bit::test(auxv.hwcap, 8),
|
||||
high_gprs: bit::test(auxv.hwcap, 9),
|
||||
te: bit::test(auxv.hwcap, 10),
|
||||
vxrs: bit::test(auxv.hwcap, 11),
|
||||
vxrs_bcd: bit::test(auxv.hwcap, 12),
|
||||
vxrs_ext: bit::test(auxv.hwcap, 13),
|
||||
gs: bit::test(auxv.hwcap, 14),
|
||||
vxrs_ext2: bit::test(auxv.hwcap, 15),
|
||||
vxrs_pde: bit::test(auxv.hwcap, 16),
|
||||
sort: bit::test(auxv.hwcap, 17),
|
||||
dflt: bit::test(auxv.hwcap, 18),
|
||||
vxrs_pde2: bit::test(auxv.hwcap, 19),
|
||||
nnpa: bit::test(auxv.hwcap, 20),
|
||||
pci_mio: bit::test(auxv.hwcap, 21),
|
||||
sie: bit::test(auxv.hwcap, 22),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ExtendedFacilityList([u64; 4]);
|
||||
|
||||
impl ExtendedFacilityList {
|
||||
fn new() -> Self {
|
||||
let mut result: [u64; 4] = [0; 4];
|
||||
// SAFETY: rust/llvm only support s390x version with the `stfle` instruction.
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
// equivalently ".insn s, 0xb2b00000, 0({1})",
|
||||
"stfle 0({})",
|
||||
in(reg_addr) result.as_mut_ptr() ,
|
||||
inout("r0") result.len() as u64 - 1 => _,
|
||||
options(nostack)
|
||||
);
|
||||
}
|
||||
Self(result)
|
||||
}
|
||||
|
||||
const fn get_bit(&self, n: usize) -> bool {
|
||||
// NOTE: bits are numbered from the left.
|
||||
self.0[n / 64] & (1 << (63 - (n % 64))) != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the cache from the feature bits.
|
||||
///
|
||||
/// These values are part of the platform-specific [asm/elf.h][kernel], and are a selection of the
|
||||
/// fields found in the [Facility Indications].
|
||||
///
|
||||
/// [Facility Indications]: https://www.ibm.com/support/pages/sites/default/files/2021-05/SA22-7871-10.pdf#page=63
|
||||
/// [kernel]: https://github.com/torvalds/linux/blob/b62cef9a5c673f1b8083159f5dc03c1c5daced2f/arch/s390/include/asm/elf.h#L129
|
||||
fn cache(hwcap: Option<AtHwcap>, facilities: ExtendedFacilityList) -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
|
||||
{
|
||||
let mut enable_if_set = |bit_index, f| {
|
||||
if facilities.get_bit(bit_index) {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// We use HWCAP for `vector` because it requires both hardware and kernel support.
|
||||
if let Some(AtHwcap { vxrs: true, .. }) = hwcap {
|
||||
// vector and related
|
||||
|
||||
enable_if_set(129, Feature::vector);
|
||||
|
||||
enable_if_set(135, Feature::vector_enhancements_1);
|
||||
enable_if_set(148, Feature::vector_enhancements_2);
|
||||
enable_if_set(198, Feature::vector_enhancements_3);
|
||||
|
||||
enable_if_set(134, Feature::vector_packed_decimal);
|
||||
enable_if_set(152, Feature::vector_packed_decimal_enhancement);
|
||||
enable_if_set(192, Feature::vector_packed_decimal_enhancement_2);
|
||||
enable_if_set(199, Feature::vector_packed_decimal_enhancement_3);
|
||||
|
||||
enable_if_set(165, Feature::nnp_assist);
|
||||
}
|
||||
|
||||
// others
|
||||
|
||||
enable_if_set(76, Feature::message_security_assist_extension3);
|
||||
enable_if_set(77, Feature::message_security_assist_extension4);
|
||||
enable_if_set(57, Feature::message_security_assist_extension5);
|
||||
enable_if_set(146, Feature::message_security_assist_extension8);
|
||||
enable_if_set(155, Feature::message_security_assist_extension9);
|
||||
enable_if_set(86, Feature::message_security_assist_extension12);
|
||||
|
||||
enable_if_set(58, Feature::miscellaneous_extensions_2);
|
||||
enable_if_set(61, Feature::miscellaneous_extensions_3);
|
||||
enable_if_set(84, Feature::miscellaneous_extensions_4);
|
||||
|
||||
enable_if_set(45, Feature::high_word);
|
||||
enable_if_set(73, Feature::transactional_execution);
|
||||
enable_if_set(133, Feature::guarded_storage);
|
||||
enable_if_set(150, Feature::enhanced_sort);
|
||||
enable_if_set(151, Feature::deflate_conversion);
|
||||
enable_if_set(201, Feature::concurrent_functions);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//! Run-time feature detection for Aarch64 on OpenBSD.
|
||||
//!
|
||||
//! OpenBSD doesn't trap the mrs instruction, but exposes the system registers through sysctl.
|
||||
//! https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8
|
||||
//! https://github.com/golang/go/commit/cd54ef1f61945459486e9eea2f016d99ef1da925
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr;
|
||||
|
||||
use crate::detect::cache;
|
||||
|
||||
// Defined in machine/cpu.h.
|
||||
// https://github.com/openbsd/src/blob/72ccc03bd11da614f31f7ff76e3f6fce99bc1c79/sys/arch/arm64/include/cpu.h#L25-L40
|
||||
const CPU_ID_AA64ISAR0: libc::c_int = 2;
|
||||
const CPU_ID_AA64ISAR1: libc::c_int = 3;
|
||||
const CPU_ID_AA64MMFR2: libc::c_int = 7;
|
||||
const CPU_ID_AA64PFR0: libc::c_int = 8;
|
||||
|
||||
/// Try to read the features from the system registers.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
// ID_AA64ISAR0_EL1 and ID_AA64ISAR1_EL1 are supported on OpenBSD 7.1+.
|
||||
// https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8
|
||||
// Others are supported on OpenBSD 7.3+.
|
||||
// https://github.com/openbsd/src/commit/c7654cd65262d532212f65123ee3905ba200365c
|
||||
// sysctl returns an unsupported error if operation is not supported,
|
||||
// so we can safely use this function on older versions of OpenBSD.
|
||||
let aa64isar0 = sysctl64(&[libc::CTL_MACHDEP, CPU_ID_AA64ISAR0]).unwrap_or(0);
|
||||
let aa64isar1 = sysctl64(&[libc::CTL_MACHDEP, CPU_ID_AA64ISAR1]).unwrap_or(0);
|
||||
let aa64mmfr2 = sysctl64(&[libc::CTL_MACHDEP, CPU_ID_AA64MMFR2]).unwrap_or(0);
|
||||
// Do not use unwrap_or(0) because in fp and asimd fields, 0 indicates that
|
||||
// the feature is available.
|
||||
let aa64pfr0 = sysctl64(&[libc::CTL_MACHDEP, CPU_ID_AA64PFR0]);
|
||||
|
||||
crate::detect::aarch64::parse_system_registers(aa64isar0, aa64isar1, aa64mmfr2, aa64pfr0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sysctl64(mib: &[libc::c_int]) -> Option<u64> {
|
||||
const OUT_LEN: libc::size_t = core::mem::size_of::<u64>();
|
||||
let mut out = MaybeUninit::<u64>::uninit();
|
||||
let mut out_len = OUT_LEN;
|
||||
let res = unsafe {
|
||||
libc::sysctl(
|
||||
mib.as_ptr(),
|
||||
mib.len() as libc::c_uint,
|
||||
out.as_mut_ptr() as *mut libc::c_void,
|
||||
&mut out_len,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
)
|
||||
};
|
||||
if res == -1 || out_len != OUT_LEN {
|
||||
return None;
|
||||
}
|
||||
// SAFETY: we've checked that sysctl was successful and `out` was filled.
|
||||
Some(unsafe { out.assume_init() })
|
||||
}
|
||||
54
crates/std/crates/std_detect/src/detect/os/openbsd/auxvec.rs
Normal file
54
crates/std/crates/std_detect/src/detect/os/openbsd/auxvec.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
//! Parses ELF auxiliary vectors.
|
||||
#![cfg_attr(
|
||||
any(target_arch = "aarch64", target_arch = "powerpc64", target_arch = "riscv64"),
|
||||
allow(dead_code)
|
||||
)]
|
||||
|
||||
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
|
||||
///
|
||||
/// If an entry cannot be read all the bits in the bitfield are set to zero.
|
||||
/// This should be interpreted as all the features being disabled.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct AuxVec {
|
||||
pub hwcap: usize,
|
||||
pub hwcap2: usize,
|
||||
}
|
||||
|
||||
/// ELF Auxiliary Vector
|
||||
///
|
||||
/// The auxiliary vector is a memory region in a running ELF program's stack
|
||||
/// composed of (key: usize, value: usize) pairs.
|
||||
///
|
||||
/// The keys used in the aux vector are platform dependent. For OpenBSD, they are
|
||||
/// defined in [machine/elf.h][elfh]. The hardware capabilities of a given CPU
|
||||
/// can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
|
||||
///
|
||||
/// Note that run-time feature detection is not invoked for features that can
|
||||
/// be detected at compile-time.
|
||||
///
|
||||
/// [elf.h]: https://github.com/openbsd/src/blob/master/sys/arch/arm64/include/elf.h
|
||||
/// [elf.h]: https://github.com/openbsd/src/blob/master/sys/arch/powerpc64/include/elf.h
|
||||
pub(crate) fn auxv() -> Result<AuxVec, ()> {
|
||||
let hwcap = archauxv(libc::AT_HWCAP);
|
||||
let hwcap2 = archauxv(libc::AT_HWCAP2);
|
||||
// Zero could indicate that no features were detected, but it's also used to
|
||||
// indicate an error. In particular, on many platforms AT_HWCAP2 will be
|
||||
// legitimately zero, since it contains the most recent feature flags.
|
||||
if hwcap != 0 || hwcap2 != 0 {
|
||||
return Ok(AuxVec { hwcap, hwcap2 });
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
||||
/// Tries to read the `key` from the auxiliary vector.
|
||||
fn archauxv(key: libc::c_int) -> usize {
|
||||
const OUT_LEN: libc::c_int = core::mem::size_of::<libc::c_ulong>() as libc::c_int;
|
||||
let mut out: libc::c_ulong = 0;
|
||||
unsafe {
|
||||
let res =
|
||||
libc::elf_aux_info(key, &mut out as *mut libc::c_ulong as *mut libc::c_void, OUT_LEN);
|
||||
// If elf_aux_info fails, `out` will be left at zero (which is the proper default value).
|
||||
debug_assert!(res == 0 || out == 0);
|
||||
}
|
||||
out as usize
|
||||
}
|
||||
21
crates/std/crates/std_detect/src/detect/os/openbsd/mod.rs
Normal file
21
crates/std/crates/std_detect/src/detect/os/openbsd/mod.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
//! Run-time feature detection on OpenBSD
|
||||
|
||||
mod auxvec;
|
||||
|
||||
cfg_select! {
|
||||
target_arch = "aarch64" => {
|
||||
mod aarch64;
|
||||
pub(crate) use self::aarch64::detect_features;
|
||||
}
|
||||
target_arch = "powerpc64" => {
|
||||
mod powerpc;
|
||||
pub(crate) use self::powerpc::detect_features;
|
||||
}
|
||||
_ => {
|
||||
use crate::detect::cache;
|
||||
/// Performs run-time feature detection.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
cache::Initializer::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//! Run-time feature detection for PowerPC on OpenBSD.
|
||||
|
||||
use super::auxvec;
|
||||
use crate::detect::{Feature, cache};
|
||||
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
let enable_feature = |value: &mut cache::Initializer, f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(auxv) = auxvec::auxv() {
|
||||
enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0);
|
||||
enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0);
|
||||
enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0);
|
||||
return value;
|
||||
}
|
||||
value
|
||||
}
|
||||
8
crates/std/crates/std_detect/src/detect/os/other.rs
Normal file
8
crates/std/crates/std_detect/src/detect/os/other.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
//! Other operating systems
|
||||
|
||||
use crate::detect::cache;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
cache::Initializer::default()
|
||||
}
|
||||
159
crates/std/crates/std_detect/src/detect/os/riscv.rs
Normal file
159
crates/std/crates/std_detect/src/detect/os/riscv.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
//! Run-time feature detection utility for RISC-V.
|
||||
//!
|
||||
//! On RISC-V, full feature detection needs a help of one or more
|
||||
//! feature detection mechanisms (usually provided by the operating system).
|
||||
//!
|
||||
//! RISC-V architecture defines many extensions and some have dependency to others.
|
||||
//! More importantly, some of them cannot be enabled without resolving such
|
||||
//! dependencies due to limited set of features that such mechanisms provide.
|
||||
//!
|
||||
//! This module provides an OS-independent utility to process such relations
|
||||
//! between RISC-V extensions.
|
||||
|
||||
use crate::detect::{Feature, cache};
|
||||
|
||||
/// Imply features by the given set of enabled features.
|
||||
///
|
||||
/// Note that it does not perform any consistency checks including existence of
|
||||
/// conflicting extensions and/or complicated requirements. Eliminating such
|
||||
/// inconsistencies is the responsibility of the feature detection logic and
|
||||
/// its provider(s).
|
||||
pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initializer {
|
||||
loop {
|
||||
// Check convergence of the feature flags later.
|
||||
let prev = value;
|
||||
|
||||
// Expect that the optimizer turns repeated operations into
|
||||
// a fewer number of bit-manipulation operations.
|
||||
macro_rules! imply {
|
||||
// Regular implication:
|
||||
// A1 => (B1[, B2...]), A2 => (B1[, B2...]) and so on.
|
||||
($($from: ident)|+ => $($to: ident)&+) => {
|
||||
if [$(Feature::$from as u32),+].iter().any(|&x| value.test(x)) {
|
||||
$(
|
||||
value.set(Feature::$to as u32);
|
||||
)+
|
||||
}
|
||||
};
|
||||
// Implication with multiple requirements:
|
||||
// A1 && A2 ... => (B1[, B2...]).
|
||||
($($from: ident)&+ => $($to: ident)&+) => {
|
||||
if [$(Feature::$from as u32),+].iter().all(|&x| value.test(x)) {
|
||||
$(
|
||||
value.set(Feature::$to as u32);
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! group {
|
||||
($group: ident == $($member: ident)&+) => {
|
||||
// Forward implication as defined in the specifications.
|
||||
imply!($group => $($member)&+);
|
||||
// Reverse implication to "group extension" from its members.
|
||||
// This is not a part of specifications but convenient for
|
||||
// feature detection and implemented in e.g. LLVM.
|
||||
imply!($($member)&+ => $group);
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
If a dependency/implication is not explicitly stated in the
|
||||
specification, it is denoted as a comment as follows:
|
||||
"defined as subset":
|
||||
The latter extension is described as a subset of the former
|
||||
(but the evidence is weak).
|
||||
"functional":
|
||||
The former extension is functionally a superset of the latter
|
||||
(no direct references though).
|
||||
*/
|
||||
|
||||
imply!(zvbb => zvkb);
|
||||
|
||||
// Certain set of vector cryptography extensions form a group.
|
||||
group!(zvkn == zvkned & zvknhb & zvkb & zvkt);
|
||||
group!(zvknc == zvkn & zvbc);
|
||||
group!(zvkng == zvkn & zvkg);
|
||||
group!(zvks == zvksed & zvksh & zvkb & zvkt);
|
||||
group!(zvksc == zvks & zvbc);
|
||||
group!(zvksg == zvks & zvkg);
|
||||
|
||||
imply!(zvknhb => zvknha); // functional
|
||||
|
||||
// For vector cryptography, Zvknhb and Zvbc require integer arithmetic
|
||||
// with EEW=64 (Zve64x) while others not depending on them
|
||||
// require EEW=32 (Zve32x).
|
||||
imply!(zvknhb | zvbc => zve64x);
|
||||
imply!(zvbb | zvkb | zvkg | zvkned | zvknha | zvksed | zvksh => zve32x);
|
||||
|
||||
imply!(zbc => zbkc); // defined as subset
|
||||
group!(zkn == zbkb & zbkc & zbkx & zkne & zknd & zknh);
|
||||
group!(zks == zbkb & zbkc & zbkx & zksed & zksh);
|
||||
group!(zk == zkn & zkr & zkt);
|
||||
|
||||
imply!(zabha | zacas => zaamo);
|
||||
group!(a == zalrsc & zaamo);
|
||||
|
||||
group!(b == zba & zbb & zbs);
|
||||
|
||||
imply!(zcf => zca & f);
|
||||
imply!(zcd => zca & d);
|
||||
imply!(zcmop | zcb => zca);
|
||||
|
||||
imply!(zhinx => zhinxmin);
|
||||
imply!(zdinx | zhinxmin => zfinx);
|
||||
|
||||
imply!(zvfh => zvfhmin); // functional
|
||||
imply!(zvfh => zve32f & zfhmin);
|
||||
imply!(zvfhmin => zve32f);
|
||||
imply!(zvfbfwma => zvfbfmin & zfbfmin);
|
||||
imply!(zvfbfmin => zve32f);
|
||||
|
||||
imply!(v => zve64d);
|
||||
imply!(zve64d => zve64f & d);
|
||||
imply!(zve64f => zve64x & zve32f);
|
||||
imply!(zve64x => zve32x);
|
||||
imply!(zve32f => zve32x & f);
|
||||
|
||||
imply!(zfh => zfhmin);
|
||||
imply!(q => d);
|
||||
imply!(d | zfhmin | zfa => f);
|
||||
imply!(zfbfmin => f); // and some of (not all) "Zfh" instructions.
|
||||
|
||||
// Relatively complex implication rules around the "C" extension.
|
||||
// (from "C" and some others)
|
||||
imply!(c => zca);
|
||||
imply!(c & d => zcd);
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
imply!(c & f => zcf);
|
||||
// (to "C"; defined as superset)
|
||||
cfg_select! {
|
||||
target_arch = "riscv32" => {
|
||||
if value.test(Feature::d as u32) {
|
||||
imply!(zcf & zcd => c);
|
||||
} else if value.test(Feature::f as u32) {
|
||||
imply!(zcf => c);
|
||||
} else {
|
||||
imply!(zca => c);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if value.test(Feature::d as u32) {
|
||||
imply!(zcd => c);
|
||||
} else {
|
||||
imply!(zca => c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imply!(zicntr | zihpm | f | zfinx | zve32x => zicsr);
|
||||
|
||||
// Loop until the feature flags converge.
|
||||
if prev == value {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "riscv/tests.rs"]
|
||||
mod tests;
|
||||
64
crates/std/crates/std_detect/src/detect/os/riscv/tests.rs
Normal file
64
crates/std/crates/std_detect/src/detect/os/riscv/tests.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn simple_direct() {
|
||||
let mut value = cache::Initializer::default();
|
||||
value.set(Feature::f as u32);
|
||||
// F (and other extensions with CSRs) -> Zicsr
|
||||
assert!(imply_features(value).test(Feature::zicsr as u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_indirect() {
|
||||
let mut value = cache::Initializer::default();
|
||||
value.set(Feature::q as u32);
|
||||
// Q -> D, D -> F, F -> Zicsr
|
||||
assert!(imply_features(value).test(Feature::zicsr as u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complex_zcd() {
|
||||
let mut value = cache::Initializer::default();
|
||||
// C & D -> Zcd
|
||||
value.set(Feature::c as u32);
|
||||
assert!(!imply_features(value).test(Feature::zcd as u32));
|
||||
value.set(Feature::d as u32);
|
||||
assert!(imply_features(value).test(Feature::zcd as u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_simple_forward() {
|
||||
let mut value = cache::Initializer::default();
|
||||
// A -> Zalrsc & Zaamo (forward implication)
|
||||
value.set(Feature::a as u32);
|
||||
let value = imply_features(value);
|
||||
assert!(value.test(Feature::zalrsc as u32));
|
||||
assert!(value.test(Feature::zaamo as u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_simple_backward() {
|
||||
let mut value = cache::Initializer::default();
|
||||
// Zalrsc & Zaamo -> A (reverse implication)
|
||||
value.set(Feature::zalrsc as u32);
|
||||
value.set(Feature::zaamo as u32);
|
||||
assert!(imply_features(value).test(Feature::a as u32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_complex_convergence() {
|
||||
let mut value = cache::Initializer::default();
|
||||
// Needs 3 iterations to converge
|
||||
// (and 4th iteration for convergence checking):
|
||||
// 1. [Zvksc] -> Zvks & Zvbc
|
||||
// 2. Zvks -> Zvksed & Zvksh & Zvkb & Zvkt
|
||||
// 3a. [Zvkned] & [Zvknhb] & [Zvkb] & Zvkt -> {Zvkn}
|
||||
// 3b. Zvkn & Zvbc -> {Zvknc}
|
||||
value.set(Feature::zvksc as u32);
|
||||
value.set(Feature::zvkned as u32);
|
||||
value.set(Feature::zvknhb as u32);
|
||||
value.set(Feature::zvkb as u32);
|
||||
let value = imply_features(value);
|
||||
assert!(value.test(Feature::zvkn as u32));
|
||||
assert!(value.test(Feature::zvknc as u32));
|
||||
}
|
||||
125
crates/std/crates/std_detect/src/detect/os/windows/aarch64.rs
Normal file
125
crates/std/crates/std_detect/src/detect/os/windows/aarch64.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
//! Run-time feature detection for Aarch64 on Windows.
|
||||
|
||||
use crate::detect::{Feature, cache};
|
||||
|
||||
/// Try to read the features using IsProcessorFeaturePresent.
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
type DWORD = u32;
|
||||
type BOOL = i32;
|
||||
|
||||
const FALSE: BOOL = 0;
|
||||
// The following Microsoft documents isn't updated for aarch64.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
|
||||
// These are defined in winnt.h of Windows SDK
|
||||
const PF_ARM_VFP_32_REGISTERS_AVAILABLE: u32 = 18;
|
||||
const PF_ARM_NEON_INSTRUCTIONS_AVAILABLE: u32 = 19;
|
||||
const PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE: u32 = 30;
|
||||
const PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE: u32 = 31;
|
||||
const PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE: u32 = 34;
|
||||
const PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE: u32 = 43;
|
||||
const PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE: u32 = 44;
|
||||
const PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE: u32 = 45;
|
||||
const PF_ARM_SVE_INSTRUCTIONS_AVAILABLE: u32 = 46;
|
||||
const PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE: u32 = 47;
|
||||
const PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE: u32 = 48;
|
||||
const PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE: u32 = 49;
|
||||
const PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE: u32 = 50;
|
||||
const PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE: u32 = 51;
|
||||
// const PF_ARM_SVE_BF16_INSTRUCTIONS_AVAILABLE: u32 = 52;
|
||||
// const PF_ARM_SVE_EBF16_INSTRUCTIONS_AVAILABLE: u32 = 53;
|
||||
const PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE: u32 = 54;
|
||||
const PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE: u32 = 55;
|
||||
const PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE: u32 = 56;
|
||||
// const PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE: u32 = 57;
|
||||
// const PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE: u32 = 58;
|
||||
// const PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE: u32 = 59;
|
||||
|
||||
unsafe extern "system" {
|
||||
fn IsProcessorFeaturePresent(ProcessorFeature: DWORD) -> BOOL;
|
||||
}
|
||||
|
||||
let mut value = cache::Initializer::default();
|
||||
{
|
||||
let mut enable_feature = |f, enable| {
|
||||
if enable {
|
||||
value.set(f as u32);
|
||||
}
|
||||
};
|
||||
|
||||
// Some features may be supported on current CPU,
|
||||
// but no way to detect it by OS API.
|
||||
// Also, we require unsafe block for the extern "system" calls.
|
||||
unsafe {
|
||||
enable_feature(
|
||||
Feature::fp,
|
||||
IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::asimd,
|
||||
IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::crc,
|
||||
IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::lse,
|
||||
IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::dotprod,
|
||||
IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::jsconv,
|
||||
IsProcessorFeaturePresent(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::rcpc,
|
||||
IsProcessorFeaturePresent(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::sve,
|
||||
IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::sve2,
|
||||
IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::sve2p1,
|
||||
IsProcessorFeaturePresent(PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::sve2_aes,
|
||||
IsProcessorFeaturePresent(PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE) != FALSE
|
||||
&& IsProcessorFeaturePresent(PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE)
|
||||
!= FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::sve2_bitperm,
|
||||
IsProcessorFeaturePresent(PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::sve_b16b16,
|
||||
IsProcessorFeaturePresent(PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::sve2_sha3,
|
||||
IsProcessorFeaturePresent(PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
enable_feature(
|
||||
Feature::sve2_sm4,
|
||||
IsProcessorFeaturePresent(PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE) != FALSE,
|
||||
);
|
||||
// PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE means aes, sha1, sha2 and
|
||||
// pmull support
|
||||
let crypto =
|
||||
IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) != FALSE;
|
||||
enable_feature(Feature::aes, crypto);
|
||||
enable_feature(Feature::pmull, crypto);
|
||||
enable_feature(Feature::sha2, crypto);
|
||||
}
|
||||
}
|
||||
value
|
||||
}
|
||||
330
crates/std/crates/std_detect/src/detect/os/x86.rs
Normal file
330
crates/std/crates/std_detect/src/detect/os/x86.rs
Normal file
@@ -0,0 +1,330 @@
|
||||
//! x86 run-time feature detection is OS independent.
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
use core::mem;
|
||||
|
||||
use crate::detect::{Feature, bit, cache};
|
||||
|
||||
/// Run-time feature detection on x86 works by using the CPUID instruction.
|
||||
///
|
||||
/// The [CPUID Wikipedia page][wiki_cpuid] contains
|
||||
/// all the information about which flags to set to query which values, and in
|
||||
/// which registers these are reported.
|
||||
///
|
||||
/// The definitive references are:
|
||||
/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
|
||||
/// Instruction Set Reference, A-Z][intel64_ref].
|
||||
/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
|
||||
/// System Instructions][amd64_ref].
|
||||
///
|
||||
/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
|
||||
/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
|
||||
/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
|
||||
#[allow(clippy::similar_names)]
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
|
||||
if cfg!(target_env = "sgx") {
|
||||
// doesn't support this because it is untrusted data
|
||||
return value;
|
||||
}
|
||||
|
||||
// Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU
|
||||
// has `cpuid` support.
|
||||
|
||||
// 0. EAX = 0: Basic Information:
|
||||
// - EAX returns the "Highest Function Parameter", that is, the maximum
|
||||
// leaf value for subsequent calls of `cpuinfo` in range [0,
|
||||
// 0x8000_0000]. - The vendor ID is stored in 12 u8 ascii chars,
|
||||
// returned in EBX, EDX, and ECX (in that order):
|
||||
let (max_basic_leaf, vendor_id) = {
|
||||
let CpuidResult { eax: max_basic_leaf, ebx, ecx, edx } = __cpuid(0);
|
||||
let vendor_id: [[u8; 4]; 3] = [ebx.to_ne_bytes(), edx.to_ne_bytes(), ecx.to_ne_bytes()];
|
||||
let vendor_id: [u8; 12] = unsafe { mem::transmute(vendor_id) };
|
||||
(max_basic_leaf, vendor_id)
|
||||
};
|
||||
|
||||
if max_basic_leaf < 1 {
|
||||
// Earlier Intel 486, CPUID not implemented
|
||||
return value;
|
||||
}
|
||||
|
||||
// EAX = 1, ECX = 0: Queries "Processor Info and Feature Bits";
|
||||
// Contains information about most x86 features.
|
||||
let CpuidResult { ecx: proc_info_ecx, edx: proc_info_edx, .. } = __cpuid(0x0000_0001_u32);
|
||||
|
||||
// EAX = 7: Queries "Extended Features";
|
||||
// Contains information about bmi,bmi2, and avx2 support.
|
||||
let (
|
||||
extended_features_ebx,
|
||||
extended_features_ecx,
|
||||
extended_features_edx,
|
||||
extended_features_eax_leaf_1,
|
||||
extended_features_edx_leaf_1,
|
||||
) = if max_basic_leaf >= 7 {
|
||||
let CpuidResult { ebx, ecx, edx, .. } = __cpuid(0x0000_0007_u32);
|
||||
let CpuidResult { eax: eax_1, edx: edx_1, .. } =
|
||||
__cpuid_count(0x0000_0007_u32, 0x0000_0001_u32);
|
||||
(ebx, ecx, edx, eax_1, edx_1)
|
||||
} else {
|
||||
(0, 0, 0, 0, 0) // CPUID does not support "Extended Features"
|
||||
};
|
||||
|
||||
// EAX = 0x8000_0000, ECX = 0: Get Highest Extended Function Supported
|
||||
// - EAX returns the max leaf value for extended information, that is,
|
||||
// `cpuid` calls in range [0x8000_0000; u32::MAX]:
|
||||
let CpuidResult { eax: extended_max_basic_leaf, .. } = __cpuid(0x8000_0000_u32);
|
||||
|
||||
// EAX = 0x8000_0001, ECX=0: Queries "Extended Processor Info and Feature
|
||||
// Bits"
|
||||
let extended_proc_info_ecx = if extended_max_basic_leaf >= 1 {
|
||||
let CpuidResult { ecx, .. } = __cpuid(0x8000_0001_u32);
|
||||
ecx
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
{
|
||||
// borrows value till the end of this scope:
|
||||
let mut enable = |r, rb, f| {
|
||||
let present = bit::test(r as usize, rb);
|
||||
if present {
|
||||
value.set(f as u32);
|
||||
}
|
||||
present
|
||||
};
|
||||
|
||||
enable(proc_info_ecx, 0, Feature::sse3);
|
||||
enable(proc_info_ecx, 1, Feature::pclmulqdq);
|
||||
enable(proc_info_ecx, 9, Feature::ssse3);
|
||||
enable(proc_info_ecx, 13, Feature::cmpxchg16b);
|
||||
enable(proc_info_ecx, 19, Feature::sse4_1);
|
||||
enable(proc_info_ecx, 20, Feature::sse4_2);
|
||||
enable(proc_info_ecx, 22, Feature::movbe);
|
||||
enable(proc_info_ecx, 23, Feature::popcnt);
|
||||
enable(proc_info_ecx, 25, Feature::aes);
|
||||
let f16c = enable(proc_info_ecx, 29, Feature::f16c);
|
||||
enable(proc_info_ecx, 30, Feature::rdrand);
|
||||
enable(extended_features_ebx, 18, Feature::rdseed);
|
||||
enable(extended_features_ebx, 19, Feature::adx);
|
||||
enable(extended_features_ebx, 11, Feature::rtm);
|
||||
enable(proc_info_edx, 4, Feature::tsc);
|
||||
enable(proc_info_edx, 23, Feature::mmx);
|
||||
enable(proc_info_edx, 24, Feature::fxsr);
|
||||
enable(proc_info_edx, 25, Feature::sse);
|
||||
enable(proc_info_edx, 26, Feature::sse2);
|
||||
enable(extended_features_ebx, 29, Feature::sha);
|
||||
|
||||
enable(extended_features_ecx, 8, Feature::gfni);
|
||||
enable(extended_features_ecx, 9, Feature::vaes);
|
||||
enable(extended_features_ecx, 10, Feature::vpclmulqdq);
|
||||
|
||||
enable(extended_features_ebx, 3, Feature::bmi1);
|
||||
enable(extended_features_ebx, 8, Feature::bmi2);
|
||||
|
||||
enable(extended_features_ebx, 9, Feature::ermsb);
|
||||
|
||||
enable(extended_features_eax_leaf_1, 31, Feature::movrs);
|
||||
|
||||
// Detect if CPUID.19h available
|
||||
if bit::test(extended_features_ecx as usize, 23) {
|
||||
let CpuidResult { ebx, .. } = __cpuid(0x19);
|
||||
enable(ebx, 0, Feature::kl);
|
||||
enable(ebx, 2, Feature::widekl);
|
||||
}
|
||||
|
||||
// This detects ABM on AMD CPUs and LZCNT on Intel CPUs.
|
||||
// On intel CPUs with popcnt, lzcnt implements the
|
||||
// "missing part" of ABM, so we map both to the same
|
||||
// internal feature.
|
||||
//
|
||||
// The `is_x86_feature_detected!("lzcnt")` macro then
|
||||
// internally maps to Feature::abm.
|
||||
enable(extended_proc_info_ecx, 5, Feature::lzcnt);
|
||||
|
||||
// As Hygon Dhyana originates from AMD technology and shares most of the architecture with
|
||||
// AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series
|
||||
// number(Family 18h).
|
||||
//
|
||||
// For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD
|
||||
// family 17h.
|
||||
//
|
||||
// Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf.
|
||||
// Related Hygon kernel patch can be found on
|
||||
// http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn
|
||||
if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" {
|
||||
// These features are available on AMD arch CPUs:
|
||||
enable(extended_proc_info_ecx, 6, Feature::sse4a);
|
||||
enable(extended_proc_info_ecx, 21, Feature::tbm);
|
||||
enable(extended_proc_info_ecx, 11, Feature::xop);
|
||||
}
|
||||
|
||||
// `XSAVE` and `AVX` support:
|
||||
let cpu_xsave = bit::test(proc_info_ecx as usize, 26);
|
||||
if cpu_xsave {
|
||||
// 0. Here the CPU supports `XSAVE`.
|
||||
|
||||
// 1. Detect `OSXSAVE`, that is, whether the OS is AVX enabled and
|
||||
// supports saving the state of the AVX/AVX2 vector registers on
|
||||
// context-switches, see:
|
||||
//
|
||||
// - [intel: is avx enabled?][is_avx_enabled],
|
||||
// - [mozilla: sse.cpp][mozilla_sse_cpp].
|
||||
//
|
||||
// [is_avx_enabled]: https://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
|
||||
// [mozilla_sse_cpp]: https://hg.mozilla.org/mozilla-central/file/64bab5cbb9b6/mozglue/build/SSE.cpp#l190
|
||||
let cpu_osxsave = bit::test(proc_info_ecx as usize, 27);
|
||||
|
||||
if cpu_osxsave {
|
||||
// 2. The OS must have signaled the CPU that it supports saving and
|
||||
// restoring the:
|
||||
//
|
||||
// * SSE -> `XCR0.SSE[1]`
|
||||
// * AVX -> `XCR0.AVX[2]`
|
||||
// * AVX-512 -> `XCR0.AVX-512[7:5]`.
|
||||
// * AMX -> `XCR0.AMX[18:17]`
|
||||
// * APX -> `XCR0.APX[19]`
|
||||
//
|
||||
// by setting the corresponding bits of `XCR0` to `1`.
|
||||
//
|
||||
// This is safe because the CPU supports `xsave`
|
||||
// and the OS has set `osxsave`.
|
||||
let xcr0 = unsafe { _xgetbv(0) };
|
||||
// Test `XCR0.SSE[1]` and `XCR0.AVX[2]` with the mask `0b110 == 6`:
|
||||
let os_avx_support = xcr0 & 6 == 6;
|
||||
// Test `XCR0.AVX-512[7:5]` with the mask `0b1110_0000 == 0xe0`:
|
||||
let os_avx512_support = xcr0 & 0xe0 == 0xe0;
|
||||
// Test `XCR0.AMX[18:17]` with the mask `0b110_0000_0000_0000_0000 == 0x60000`
|
||||
let os_amx_support = xcr0 & 0x60000 == 0x60000;
|
||||
// Test `XCR0.APX[19]` with the mask `0b1000_0000_0000_0000_0000 == 0x80000`
|
||||
let os_apx_support = xcr0 & 0x80000 == 0x80000;
|
||||
|
||||
// Only if the OS and the CPU support saving/restoring the AVX
|
||||
// registers we enable `xsave` support:
|
||||
if os_avx_support {
|
||||
// See "13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED
|
||||
// FEATURES" in the "Intel® 64 and IA-32 Architectures Software
|
||||
// Developer’s Manual, Volume 1: Basic Architecture":
|
||||
//
|
||||
// "Software enables the XSAVE feature set by setting
|
||||
// CR4.OSXSAVE[bit 18] to 1 (e.g., with the MOV to CR4
|
||||
// instruction). If this bit is 0, execution of any of XGETBV,
|
||||
// XRSTOR, XRSTORS, XSAVE, XSAVEC, XSAVEOPT, XSAVES, and XSETBV
|
||||
// causes an invalid-opcode exception (#UD)"
|
||||
//
|
||||
enable(proc_info_ecx, 26, Feature::xsave);
|
||||
|
||||
// For `xsaveopt`, `xsavec`, and `xsaves` we need to query:
|
||||
// Processor Extended State Enumeration Sub-leaf (EAX = 0DH,
|
||||
// ECX = 1):
|
||||
if max_basic_leaf >= 0xd {
|
||||
let CpuidResult { eax: proc_extended_state1_eax, .. } =
|
||||
__cpuid_count(0xd_u32, 1);
|
||||
enable(proc_extended_state1_eax, 0, Feature::xsaveopt);
|
||||
enable(proc_extended_state1_eax, 1, Feature::xsavec);
|
||||
enable(proc_extended_state1_eax, 3, Feature::xsaves);
|
||||
}
|
||||
|
||||
// FMA (uses 256-bit wide registers):
|
||||
let fma = enable(proc_info_ecx, 12, Feature::fma);
|
||||
|
||||
// And AVX/AVX2:
|
||||
enable(proc_info_ecx, 28, Feature::avx);
|
||||
enable(extended_features_ebx, 5, Feature::avx2);
|
||||
|
||||
// "Short" versions of AVX512 instructions
|
||||
enable(extended_features_eax_leaf_1, 4, Feature::avxvnni);
|
||||
enable(extended_features_eax_leaf_1, 23, Feature::avxifma);
|
||||
enable(extended_features_edx_leaf_1, 4, Feature::avxvnniint8);
|
||||
enable(extended_features_edx_leaf_1, 5, Feature::avxneconvert);
|
||||
enable(extended_features_edx_leaf_1, 10, Feature::avxvnniint16);
|
||||
|
||||
enable(extended_features_eax_leaf_1, 0, Feature::sha512);
|
||||
enable(extended_features_eax_leaf_1, 1, Feature::sm3);
|
||||
enable(extended_features_eax_leaf_1, 2, Feature::sm4);
|
||||
|
||||
// For AVX-512 the OS also needs to support saving/restoring
|
||||
// the extended state, only then we enable AVX-512 support:
|
||||
// Also, Rust makes `avx512f` imply `fma` and `f16c`, because
|
||||
// otherwise the assembler is broken. But Intel doesn't guarantee
|
||||
// that `fma` and `f16c` are available with `avx512f`, so we
|
||||
// need to check for them separately.
|
||||
if os_avx512_support && f16c && fma {
|
||||
enable(extended_features_ebx, 16, Feature::avx512f);
|
||||
enable(extended_features_ebx, 17, Feature::avx512dq);
|
||||
enable(extended_features_ebx, 21, Feature::avx512ifma);
|
||||
enable(extended_features_ebx, 26, Feature::avx512pf);
|
||||
enable(extended_features_ebx, 27, Feature::avx512er);
|
||||
enable(extended_features_ebx, 28, Feature::avx512cd);
|
||||
enable(extended_features_ebx, 30, Feature::avx512bw);
|
||||
enable(extended_features_ebx, 31, Feature::avx512vl);
|
||||
enable(extended_features_ecx, 1, Feature::avx512vbmi);
|
||||
enable(extended_features_ecx, 6, Feature::avx512vbmi2);
|
||||
enable(extended_features_ecx, 11, Feature::avx512vnni);
|
||||
enable(extended_features_ecx, 12, Feature::avx512bitalg);
|
||||
enable(extended_features_ecx, 14, Feature::avx512vpopcntdq);
|
||||
enable(extended_features_edx, 8, Feature::avx512vp2intersect);
|
||||
enable(extended_features_edx, 23, Feature::avx512fp16);
|
||||
enable(extended_features_eax_leaf_1, 5, Feature::avx512bf16);
|
||||
}
|
||||
}
|
||||
|
||||
if os_amx_support {
|
||||
enable(extended_features_edx, 24, Feature::amx_tile);
|
||||
enable(extended_features_edx, 25, Feature::amx_int8);
|
||||
enable(extended_features_edx, 22, Feature::amx_bf16);
|
||||
enable(extended_features_eax_leaf_1, 21, Feature::amx_fp16);
|
||||
enable(extended_features_edx_leaf_1, 8, Feature::amx_complex);
|
||||
|
||||
if max_basic_leaf >= 0x1e {
|
||||
let CpuidResult { eax: amx_feature_flags_eax, .. } =
|
||||
__cpuid_count(0x1e_u32, 1);
|
||||
|
||||
enable(amx_feature_flags_eax, 4, Feature::amx_fp8);
|
||||
enable(amx_feature_flags_eax, 6, Feature::amx_tf32);
|
||||
enable(amx_feature_flags_eax, 7, Feature::amx_avx512);
|
||||
enable(amx_feature_flags_eax, 8, Feature::amx_movrs);
|
||||
}
|
||||
}
|
||||
|
||||
if os_apx_support {
|
||||
enable(extended_features_edx_leaf_1, 21, Feature::apxf);
|
||||
}
|
||||
|
||||
let avx10_1 = enable(extended_features_edx_leaf_1, 19, Feature::avx10_1);
|
||||
if avx10_1 {
|
||||
let CpuidResult { ebx, .. } = __cpuid(0x24);
|
||||
let avx10_version = ebx & 0xff;
|
||||
if avx10_version >= 2 {
|
||||
value.set(Feature::avx10_2 as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately, some Skylake chips erroneously report support for BMI1 and
|
||||
// BMI2 without actual support. These chips don't support AVX, and it seems
|
||||
// that all Intel chips with non-erroneous support BMI do (I didn't check
|
||||
// other vendors), so we can disable these flags for chips that don't also
|
||||
// report support for AVX.
|
||||
//
|
||||
// It's possible this will pessimize future chips that do support BMI and
|
||||
// not AVX, but this seems minor compared to a hard crash you get when
|
||||
// executing an unsupported instruction (to put it another way, it's safe
|
||||
// for us to under-report CPU features, but not to over-report them). Still,
|
||||
// to limit any impact this may have in the future, we only do this for
|
||||
// Intel chips, as it's a bug only present in their chips.
|
||||
//
|
||||
// This bug is documented as `SKL052` in the errata section of this document:
|
||||
// http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/desktop-6th-gen-core-family-spec-update.pdf
|
||||
if vendor_id == *b"GenuineIntel" && !value.test(Feature::avx as u32) {
|
||||
value.unset(Feature::bmi1 as u32);
|
||||
value.unset(Feature::bmi2 as u32);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
35
crates/std/crates/std_detect/src/lib.rs
Normal file
35
crates/std/crates/std_detect/src/lib.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
//! Run-time feature detection for the Rust standard library.
|
||||
//!
|
||||
//! To detect whether a feature is enabled in the system running the binary
|
||||
//! use one of the appropriate macro for the target:
|
||||
//!
|
||||
//! * `x86` and `x86_64`: [`is_x86_feature_detected`]
|
||||
//! * `arm`: [`is_arm_feature_detected`]
|
||||
//! * `aarch64`: [`is_aarch64_feature_detected`]
|
||||
//! * `riscv`: [`is_riscv_feature_detected`]
|
||||
//! * `mips`: [`is_mips_feature_detected`]
|
||||
//! * `mips64`: [`is_mips64_feature_detected`]
|
||||
//! * `powerpc`: [`is_powerpc_feature_detected`]
|
||||
//! * `powerpc64`: [`is_powerpc64_feature_detected`]
|
||||
//! * `loongarch`: [`is_loongarch_feature_detected`]
|
||||
//! * `s390x`: [`is_s390x_feature_detected`]
|
||||
|
||||
#![unstable(feature = "stdarch_internal", issue = "none")]
|
||||
#![feature(staged_api, doc_cfg, allow_internal_unstable)]
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![allow(clippy::shadow_reuse)]
|
||||
#![cfg_attr(test, allow(unused_imports))]
|
||||
#![no_std]
|
||||
#![allow(internal_features)]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
// rust-lang/rust#83888: removing `extern crate` gives an error that `vec_spare>
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate alloc;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "stdarch_internal", issue = "none")]
|
||||
pub mod detect;
|
||||
335
crates/std/crates/std_detect/tests/cpu-detection.rs
Normal file
335
crates/std/crates/std_detect/tests/cpu-detection.rs
Normal file
@@ -0,0 +1,335 @@
|
||||
#![allow(internal_features, unused_features)]
|
||||
#![feature(stdarch_internal)]
|
||||
#![cfg_attr(target_arch = "arm", feature(stdarch_arm_feature_detection))]
|
||||
#![cfg_attr(
|
||||
any(target_arch = "aarch64", target_arch = "arm64ec"),
|
||||
feature(stdarch_aarch64_feature_detection)
|
||||
)]
|
||||
#![cfg_attr(
|
||||
any(target_arch = "riscv32", target_arch = "riscv64"),
|
||||
feature(stdarch_riscv_feature_detection)
|
||||
)]
|
||||
#![cfg_attr(target_arch = "powerpc", feature(stdarch_powerpc_feature_detection))]
|
||||
#![cfg_attr(target_arch = "powerpc64", feature(stdarch_powerpc_feature_detection))]
|
||||
#![allow(clippy::unwrap_used, clippy::use_debug, clippy::print_stdout)]
|
||||
|
||||
#[cfg_attr(
|
||||
any(
|
||||
target_arch = "arm",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm64ec",
|
||||
target_arch = "riscv32",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
),
|
||||
macro_use
|
||||
)]
|
||||
#[cfg(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm64ec",
|
||||
target_arch = "riscv32",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
))]
|
||||
extern crate std_detect;
|
||||
|
||||
#[test]
|
||||
fn all() {
|
||||
for (f, e) in std_detect::detect::features() {
|
||||
println!("{f}: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(target_arch = "arm", target_os = "freebsd"))]
|
||||
fn arm_freebsd() {
|
||||
println!("neon: {}", is_arm_feature_detected!("neon"));
|
||||
println!("pmull: {}", is_arm_feature_detected!("pmull"));
|
||||
println!("crc: {}", is_arm_feature_detected!("crc"));
|
||||
println!("aes: {}", is_arm_feature_detected!("aes"));
|
||||
println!("sha2: {}", is_arm_feature_detected!("sha2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(target_arch = "arm", any(target_os = "linux", target_os = "android")))]
|
||||
fn arm_linux() {
|
||||
println!("neon: {}", is_arm_feature_detected!("neon"));
|
||||
println!("pmull: {}", is_arm_feature_detected!("pmull"));
|
||||
println!("crc: {}", is_arm_feature_detected!("crc"));
|
||||
println!("aes: {}", is_arm_feature_detected!("aes"));
|
||||
println!("sha2: {}", is_arm_feature_detected!("sha2"));
|
||||
println!("dotprod: {}", is_arm_feature_detected!("dotprod"));
|
||||
println!("i8mm: {}", is_arm_feature_detected!("i8mm"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")))]
|
||||
fn aarch64_linux() {
|
||||
println!("asimd: {}", is_aarch64_feature_detected!("asimd"));
|
||||
println!("neon: {}", is_aarch64_feature_detected!("neon"));
|
||||
println!("pmull: {}", is_aarch64_feature_detected!("pmull"));
|
||||
println!("fp: {}", is_aarch64_feature_detected!("fp"));
|
||||
println!("fp16: {}", is_aarch64_feature_detected!("fp16"));
|
||||
println!("sve: {}", is_aarch64_feature_detected!("sve"));
|
||||
println!("crc: {}", is_aarch64_feature_detected!("crc"));
|
||||
println!("lse: {}", is_aarch64_feature_detected!("lse"));
|
||||
println!("lse2: {}", is_aarch64_feature_detected!("lse2"));
|
||||
println!("lse128: {}", is_aarch64_feature_detected!("lse128"));
|
||||
println!("rdm: {}", is_aarch64_feature_detected!("rdm"));
|
||||
println!("rcpc: {}", is_aarch64_feature_detected!("rcpc"));
|
||||
println!("rcpc2: {}", is_aarch64_feature_detected!("rcpc2"));
|
||||
println!("rcpc3: {}", is_aarch64_feature_detected!("rcpc3"));
|
||||
println!("dotprod: {}", is_aarch64_feature_detected!("dotprod"));
|
||||
println!("fhm: {}", is_aarch64_feature_detected!("fhm"));
|
||||
println!("dit: {}", is_aarch64_feature_detected!("dit"));
|
||||
println!("flagm: {}", is_aarch64_feature_detected!("flagm"));
|
||||
println!("flagm2: {}", is_aarch64_feature_detected!("flagm2"));
|
||||
println!("ssbs: {}", is_aarch64_feature_detected!("ssbs"));
|
||||
println!("sb: {}", is_aarch64_feature_detected!("sb"));
|
||||
println!("paca: {}", is_aarch64_feature_detected!("paca"));
|
||||
println!("pacg: {}", is_aarch64_feature_detected!("pacg"));
|
||||
// println!("pauth-lr: {}", is_aarch64_feature_detected!("pauth-lr"));
|
||||
println!("dpb: {}", is_aarch64_feature_detected!("dpb"));
|
||||
println!("dpb2: {}", is_aarch64_feature_detected!("dpb2"));
|
||||
println!("sve-b16b16: {}", is_aarch64_feature_detected!("sve-b16b16"));
|
||||
println!("sve2: {}", is_aarch64_feature_detected!("sve2"));
|
||||
println!("sve2p1: {}", is_aarch64_feature_detected!("sve2p1"));
|
||||
println!("sve2-aes: {}", is_aarch64_feature_detected!("sve2-aes"));
|
||||
println!("sve2-sm4: {}", is_aarch64_feature_detected!("sve2-sm4"));
|
||||
println!("sve2-sha3: {}", is_aarch64_feature_detected!("sve2-sha3"));
|
||||
println!("sve2-bitperm: {}", is_aarch64_feature_detected!("sve2-bitperm"));
|
||||
println!("frintts: {}", is_aarch64_feature_detected!("frintts"));
|
||||
println!("i8mm: {}", is_aarch64_feature_detected!("i8mm"));
|
||||
println!("f32mm: {}", is_aarch64_feature_detected!("f32mm"));
|
||||
println!("f64mm: {}", is_aarch64_feature_detected!("f64mm"));
|
||||
println!("bf16: {}", is_aarch64_feature_detected!("bf16"));
|
||||
println!("rand: {}", is_aarch64_feature_detected!("rand"));
|
||||
println!("bti: {}", is_aarch64_feature_detected!("bti"));
|
||||
println!("mte: {}", is_aarch64_feature_detected!("mte"));
|
||||
println!("jsconv: {}", is_aarch64_feature_detected!("jsconv"));
|
||||
println!("fcma: {}", is_aarch64_feature_detected!("fcma"));
|
||||
println!("aes: {}", is_aarch64_feature_detected!("aes"));
|
||||
println!("sha2: {}", is_aarch64_feature_detected!("sha2"));
|
||||
println!("sha3: {}", is_aarch64_feature_detected!("sha3"));
|
||||
println!("sm4: {}", is_aarch64_feature_detected!("sm4"));
|
||||
println!("hbc: {}", is_aarch64_feature_detected!("hbc"));
|
||||
println!("mops: {}", is_aarch64_feature_detected!("mops"));
|
||||
println!("ecv: {}", is_aarch64_feature_detected!("ecv"));
|
||||
println!("cssc: {}", is_aarch64_feature_detected!("cssc"));
|
||||
println!("fpmr: {}", is_aarch64_feature_detected!("fpmr"));
|
||||
println!("lut: {}", is_aarch64_feature_detected!("lut"));
|
||||
println!("faminmax: {}", is_aarch64_feature_detected!("faminmax"));
|
||||
println!("fp8: {}", is_aarch64_feature_detected!("fp8"));
|
||||
println!("fp8fma: {}", is_aarch64_feature_detected!("fp8fma"));
|
||||
println!("fp8dot4: {}", is_aarch64_feature_detected!("fp8dot4"));
|
||||
println!("fp8dot2: {}", is_aarch64_feature_detected!("fp8dot2"));
|
||||
println!("wfxt: {}", is_aarch64_feature_detected!("wfxt"));
|
||||
println!("sme: {}", is_aarch64_feature_detected!("sme"));
|
||||
println!("sme-b16b16: {}", is_aarch64_feature_detected!("sme-b16b16"));
|
||||
println!("sme-i16i64: {}", is_aarch64_feature_detected!("sme-i16i64"));
|
||||
println!("sme-f64f64: {}", is_aarch64_feature_detected!("sme-f64f64"));
|
||||
println!("sme-fa64: {}", is_aarch64_feature_detected!("sme-fa64"));
|
||||
println!("sme2: {}", is_aarch64_feature_detected!("sme2"));
|
||||
println!("sme2p1: {}", is_aarch64_feature_detected!("sme2p1"));
|
||||
println!("sme-f16f16: {}", is_aarch64_feature_detected!("sme-f16f16"));
|
||||
println!("sme-lutv2: {}", is_aarch64_feature_detected!("sme-lutv2"));
|
||||
println!("sme-f8f16: {}", is_aarch64_feature_detected!("sme-f8f16"));
|
||||
println!("sme-f8f32: {}", is_aarch64_feature_detected!("sme-f8f32"));
|
||||
println!("ssve-fp8fma: {}", is_aarch64_feature_detected!("ssve-fp8fma"));
|
||||
println!("ssve-fp8dot4: {}", is_aarch64_feature_detected!("ssve-fp8dot4"));
|
||||
println!("ssve-fp8dot2: {}", is_aarch64_feature_detected!("ssve-fp8dot2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_os = "windows"))]
|
||||
fn aarch64_windows() {
|
||||
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
|
||||
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
|
||||
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
|
||||
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
|
||||
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
|
||||
println!("jsconv: {:?}", is_aarch64_feature_detected!("jsconv"));
|
||||
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
|
||||
println!("aes: {:?}", is_aarch64_feature_detected!("aes"));
|
||||
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
|
||||
println!("sha2: {:?}", is_aarch64_feature_detected!("sha2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(target_arch = "aarch64", any(target_os = "freebsd", target_os = "openbsd")))]
|
||||
fn aarch64_bsd() {
|
||||
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
|
||||
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
|
||||
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
|
||||
println!("fp16: {:?}", is_aarch64_feature_detected!("fp16"));
|
||||
println!("sve: {:?}", is_aarch64_feature_detected!("sve"));
|
||||
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
|
||||
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
|
||||
println!("lse2: {:?}", is_aarch64_feature_detected!("lse2"));
|
||||
println!("rdm: {:?}", is_aarch64_feature_detected!("rdm"));
|
||||
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
|
||||
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
|
||||
println!("paca: {:?}", is_aarch64_feature_detected!("paca"));
|
||||
println!("pacg: {:?}", is_aarch64_feature_detected!("pacg"));
|
||||
println!("aes: {:?}", is_aarch64_feature_detected!("aes"));
|
||||
println!("sha2: {:?}", is_aarch64_feature_detected!("sha2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
|
||||
fn aarch64_darwin() {
|
||||
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
|
||||
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
|
||||
println!("fp16: {:?}", is_aarch64_feature_detected!("fp16"));
|
||||
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
|
||||
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
|
||||
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
|
||||
println!("lse2: {:?}", is_aarch64_feature_detected!("lse2"));
|
||||
println!("rdm: {:?}", is_aarch64_feature_detected!("rdm"));
|
||||
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
|
||||
println!("rcpc2: {:?}", is_aarch64_feature_detected!("rcpc2"));
|
||||
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
|
||||
println!("fhm: {:?}", is_aarch64_feature_detected!("fhm"));
|
||||
println!("flagm: {:?}", is_aarch64_feature_detected!("flagm"));
|
||||
println!("ssbs: {:?}", is_aarch64_feature_detected!("ssbs"));
|
||||
println!("sb: {:?}", is_aarch64_feature_detected!("sb"));
|
||||
println!("paca: {:?}", is_aarch64_feature_detected!("paca"));
|
||||
println!("dpb: {:?}", is_aarch64_feature_detected!("dpb"));
|
||||
println!("dpb2: {:?}", is_aarch64_feature_detected!("dpb2"));
|
||||
println!("frintts: {:?}", is_aarch64_feature_detected!("frintts"));
|
||||
println!("i8mm: {:?}", is_aarch64_feature_detected!("i8mm"));
|
||||
println!("bf16: {:?}", is_aarch64_feature_detected!("bf16"));
|
||||
println!("bti: {:?}", is_aarch64_feature_detected!("bti"));
|
||||
println!("fcma: {:?}", is_aarch64_feature_detected!("fcma"));
|
||||
println!("jsconv: {:?}", is_aarch64_feature_detected!("jsconv"));
|
||||
println!("aes: {:?}", is_aarch64_feature_detected!("aes"));
|
||||
println!("sha2: {:?}", is_aarch64_feature_detected!("sha2"));
|
||||
println!("sha3: {:?}", is_aarch64_feature_detected!("sha3"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(
|
||||
any(target_arch = "riscv32", target_arch = "riscv64"),
|
||||
any(target_os = "linux", target_os = "android")
|
||||
))]
|
||||
fn riscv_linux() {
|
||||
println!("rv32i: {}", is_riscv_feature_detected!("rv32i"));
|
||||
println!("rv32e: {}", is_riscv_feature_detected!("rv32e"));
|
||||
println!("rv64i: {}", is_riscv_feature_detected!("rv64i"));
|
||||
println!("rv128i: {}", is_riscv_feature_detected!("rv128i"));
|
||||
println!("unaligned-scalar-mem: {}", is_riscv_feature_detected!("unaligned-scalar-mem"));
|
||||
println!("unaligned-vector-mem: {}", is_riscv_feature_detected!("unaligned-vector-mem"));
|
||||
println!("zicsr: {}", is_riscv_feature_detected!("zicsr"));
|
||||
println!("zicntr: {}", is_riscv_feature_detected!("zicntr"));
|
||||
println!("zihpm: {}", is_riscv_feature_detected!("zihpm"));
|
||||
println!("zifencei: {}", is_riscv_feature_detected!("zifencei"));
|
||||
println!("zihintntl: {}", is_riscv_feature_detected!("zihintntl"));
|
||||
println!("zihintpause: {}", is_riscv_feature_detected!("zihintpause"));
|
||||
println!("zimop: {}", is_riscv_feature_detected!("zimop"));
|
||||
println!("zicbom: {}", is_riscv_feature_detected!("zicbom"));
|
||||
println!("zicboz: {}", is_riscv_feature_detected!("zicboz"));
|
||||
println!("zicond: {}", is_riscv_feature_detected!("zicond"));
|
||||
println!("m: {}", is_riscv_feature_detected!("m"));
|
||||
println!("a: {}", is_riscv_feature_detected!("a"));
|
||||
println!("zalrsc: {}", is_riscv_feature_detected!("zalrsc"));
|
||||
println!("zaamo: {}", is_riscv_feature_detected!("zaamo"));
|
||||
println!("zawrs: {}", is_riscv_feature_detected!("zawrs"));
|
||||
println!("zabha: {}", is_riscv_feature_detected!("zabha"));
|
||||
println!("zacas: {}", is_riscv_feature_detected!("zacas"));
|
||||
println!("zam: {}", is_riscv_feature_detected!("zam"));
|
||||
println!("ztso: {}", is_riscv_feature_detected!("ztso"));
|
||||
println!("f: {}", is_riscv_feature_detected!("f"));
|
||||
println!("d: {}", is_riscv_feature_detected!("d"));
|
||||
println!("q: {}", is_riscv_feature_detected!("q"));
|
||||
println!("zfh: {}", is_riscv_feature_detected!("zfh"));
|
||||
println!("zfhmin: {}", is_riscv_feature_detected!("zfhmin"));
|
||||
println!("zfa: {}", is_riscv_feature_detected!("zfa"));
|
||||
println!("zfbfmin: {}", is_riscv_feature_detected!("zfbfmin"));
|
||||
println!("zfinx: {}", is_riscv_feature_detected!("zfinx"));
|
||||
println!("zdinx: {}", is_riscv_feature_detected!("zdinx"));
|
||||
println!("zhinx: {}", is_riscv_feature_detected!("zhinx"));
|
||||
println!("zhinxmin: {}", is_riscv_feature_detected!("zhinxmin"));
|
||||
println!("c: {}", is_riscv_feature_detected!("c"));
|
||||
println!("zca: {}", is_riscv_feature_detected!("zca"));
|
||||
println!("zcf: {}", is_riscv_feature_detected!("zcf"));
|
||||
println!("zcd: {}", is_riscv_feature_detected!("zcd"));
|
||||
println!("zcb: {}", is_riscv_feature_detected!("zcb"));
|
||||
println!("zcmop: {}", is_riscv_feature_detected!("zcmop"));
|
||||
println!("b: {}", is_riscv_feature_detected!("b"));
|
||||
println!("zba: {}", is_riscv_feature_detected!("zba"));
|
||||
println!("zbb: {}", is_riscv_feature_detected!("zbb"));
|
||||
println!("zbc: {}", is_riscv_feature_detected!("zbc"));
|
||||
println!("zbs: {}", is_riscv_feature_detected!("zbs"));
|
||||
println!("zbkb: {}", is_riscv_feature_detected!("zbkb"));
|
||||
println!("zbkc: {}", is_riscv_feature_detected!("zbkc"));
|
||||
println!("zbkx: {}", is_riscv_feature_detected!("zbkx"));
|
||||
println!("zknd: {}", is_riscv_feature_detected!("zknd"));
|
||||
println!("zkne: {}", is_riscv_feature_detected!("zkne"));
|
||||
println!("zknh: {}", is_riscv_feature_detected!("zknh"));
|
||||
println!("zksed: {}", is_riscv_feature_detected!("zksed"));
|
||||
println!("zksh: {}", is_riscv_feature_detected!("zksh"));
|
||||
println!("zkr: {}", is_riscv_feature_detected!("zkr"));
|
||||
println!("zkn: {}", is_riscv_feature_detected!("zkn"));
|
||||
println!("zks: {}", is_riscv_feature_detected!("zks"));
|
||||
println!("zk: {}", is_riscv_feature_detected!("zk"));
|
||||
println!("zkt: {}", is_riscv_feature_detected!("zkt"));
|
||||
println!("v: {}", is_riscv_feature_detected!("v"));
|
||||
println!("zve32x: {}", is_riscv_feature_detected!("zve32x"));
|
||||
println!("zve32f: {}", is_riscv_feature_detected!("zve32f"));
|
||||
println!("zve64x: {}", is_riscv_feature_detected!("zve64x"));
|
||||
println!("zve64f: {}", is_riscv_feature_detected!("zve64f"));
|
||||
println!("zve64d: {}", is_riscv_feature_detected!("zve64d"));
|
||||
println!("zvfh: {}", is_riscv_feature_detected!("zvfh"));
|
||||
println!("zvfhmin: {}", is_riscv_feature_detected!("zvfhmin"));
|
||||
println!("zvfbfmin: {}", is_riscv_feature_detected!("zvfbfmin"));
|
||||
println!("zvfbfwma: {}", is_riscv_feature_detected!("zvfbfwma"));
|
||||
println!("zvbb: {}", is_riscv_feature_detected!("zvbb"));
|
||||
println!("zvbc: {}", is_riscv_feature_detected!("zvbc"));
|
||||
println!("zvkb: {}", is_riscv_feature_detected!("zvkb"));
|
||||
println!("zvkg: {}", is_riscv_feature_detected!("zvkg"));
|
||||
println!("zvkned: {}", is_riscv_feature_detected!("zvkned"));
|
||||
println!("zvknha: {}", is_riscv_feature_detected!("zvknha"));
|
||||
println!("zvknhb: {}", is_riscv_feature_detected!("zvknhb"));
|
||||
println!("zvksed: {}", is_riscv_feature_detected!("zvksed"));
|
||||
println!("zvksh: {}", is_riscv_feature_detected!("zvksh"));
|
||||
println!("zvkn: {}", is_riscv_feature_detected!("zvkn"));
|
||||
println!("zvknc: {}", is_riscv_feature_detected!("zvknc"));
|
||||
println!("zvkng: {}", is_riscv_feature_detected!("zvkng"));
|
||||
println!("zvks: {}", is_riscv_feature_detected!("zvks"));
|
||||
println!("zvksc: {}", is_riscv_feature_detected!("zvksc"));
|
||||
println!("zvksg: {}", is_riscv_feature_detected!("zvksg"));
|
||||
println!("zvkt: {}", is_riscv_feature_detected!("zvkt"));
|
||||
println!("j: {}", is_riscv_feature_detected!("j"));
|
||||
println!("p: {}", is_riscv_feature_detected!("p"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(target_arch = "powerpc", target_os = "linux"))]
|
||||
fn powerpc_linux() {
|
||||
println!("altivec: {}", is_powerpc_feature_detected!("altivec"));
|
||||
println!("vsx: {}", is_powerpc_feature_detected!("vsx"));
|
||||
println!("power8: {}", is_powerpc_feature_detected!("power8"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(
|
||||
target_arch = "powerpc64",
|
||||
any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"),
|
||||
))]
|
||||
fn powerpc64_linux_or_bsd() {
|
||||
println!("altivec: {}", is_powerpc64_feature_detected!("altivec"));
|
||||
println!("vsx: {}", is_powerpc64_feature_detected!("vsx"));
|
||||
println!("power8: {}", is_powerpc64_feature_detected!("power8"));
|
||||
println!("power9: {}", is_powerpc64_feature_detected!("power9"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(target_arch = "s390x", target_os = "linux",))]
|
||||
fn s390x_linux() {
|
||||
println!("vector: {}", is_s390x_feature_detected!("vector"));
|
||||
}
|
||||
110
crates/std/crates/std_detect/tests/macro_trailing_commas.rs
Normal file
110
crates/std/crates/std_detect/tests/macro_trailing_commas.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
#![allow(internal_features, unused_features)]
|
||||
#![cfg_attr(
|
||||
any(
|
||||
target_arch = "arm",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm64ec",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
target_arch = "riscv32",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "loongarch32",
|
||||
target_arch = "loongarch64"
|
||||
),
|
||||
feature(stdarch_internal)
|
||||
)]
|
||||
#![cfg_attr(target_arch = "arm", feature(stdarch_arm_feature_detection))]
|
||||
#![cfg_attr(
|
||||
any(target_arch = "aarch64", target_arch = "arm64ec"),
|
||||
feature(stdarch_aarch64_feature_detection)
|
||||
)]
|
||||
#![cfg_attr(
|
||||
any(target_arch = "powerpc", target_arch = "powerpc64"),
|
||||
feature(stdarch_powerpc_feature_detection)
|
||||
)]
|
||||
#![cfg_attr(
|
||||
any(target_arch = "riscv32", target_arch = "riscv64"),
|
||||
feature(stdarch_riscv_feature_detection)
|
||||
)]
|
||||
#![cfg_attr(
|
||||
any(target_arch = "loongarch32", target_arch = "loongarch64"),
|
||||
feature(stdarch_loongarch_feature_detection)
|
||||
)]
|
||||
|
||||
#[cfg(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm64ec",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "s390x",
|
||||
target_arch = "riscv32",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "loongarch32",
|
||||
target_arch = "loongarch64"
|
||||
))]
|
||||
#[macro_use]
|
||||
extern crate std_detect;
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "arm")]
|
||||
fn arm() {
|
||||
let _ = is_arm_feature_detected!("neon");
|
||||
let _ = is_arm_feature_detected!("neon",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))]
|
||||
fn aarch64() {
|
||||
let _ = is_aarch64_feature_detected!("fp");
|
||||
let _ = is_aarch64_feature_detected!("fp",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_arch = "loongarch32", target_arch = "loongarch64"))]
|
||||
fn loongarch() {
|
||||
let _ = is_loongarch_feature_detected!("32s");
|
||||
let _ = is_loongarch_feature_detected!("32s",);
|
||||
let _ = is_loongarch_feature_detected!("lsx");
|
||||
let _ = is_loongarch_feature_detected!("lsx",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "powerpc")]
|
||||
fn powerpc() {
|
||||
let _ = is_powerpc_feature_detected!("altivec");
|
||||
let _ = is_powerpc_feature_detected!("altivec",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "powerpc64")]
|
||||
fn powerpc64() {
|
||||
let _ = is_powerpc64_feature_detected!("altivec");
|
||||
let _ = is_powerpc64_feature_detected!("altivec",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
|
||||
fn riscv() {
|
||||
let _ = is_riscv_feature_detected!("zk");
|
||||
let _ = is_riscv_feature_detected!("zk",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_arch = "s390x")]
|
||||
fn s390x() {
|
||||
let _ = is_s390x_feature_detected!("vector");
|
||||
let _ = is_s390x_feature_detected!("vector",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn x86() {
|
||||
let _ = is_x86_feature_detected!("sse");
|
||||
let _ = is_x86_feature_detected!("sse",);
|
||||
}
|
||||
90
crates/std/crates/std_detect/tests/x86-specific.rs
Normal file
90
crates/std/crates/std_detect/tests/x86-specific.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
#![allow(internal_features)]
|
||||
#![feature(stdarch_internal, x86_amx_intrinsics, xop_target_feature, movrs_target_feature)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate std_detect;
|
||||
|
||||
#[test]
|
||||
fn dump() {
|
||||
println!("aes: {:?}", is_x86_feature_detected!("aes"));
|
||||
println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq"));
|
||||
println!("rdrand: {:?}", is_x86_feature_detected!("rdrand"));
|
||||
println!("rdseed: {:?}", is_x86_feature_detected!("rdseed"));
|
||||
println!("tsc: {:?}", is_x86_feature_detected!("tsc"));
|
||||
println!("sse: {:?}", is_x86_feature_detected!("sse"));
|
||||
println!("sse2: {:?}", is_x86_feature_detected!("sse2"));
|
||||
println!("sse3: {:?}", is_x86_feature_detected!("sse3"));
|
||||
println!("ssse3: {:?}", is_x86_feature_detected!("ssse3"));
|
||||
println!("sse4.1: {:?}", is_x86_feature_detected!("sse4.1"));
|
||||
println!("sse4.2: {:?}", is_x86_feature_detected!("sse4.2"));
|
||||
println!("sse4a: {:?}", is_x86_feature_detected!("sse4a"));
|
||||
println!("sha: {:?}", is_x86_feature_detected!("sha"));
|
||||
println!("f16c: {:?}", is_x86_feature_detected!("f16c"));
|
||||
println!("avx: {:?}", is_x86_feature_detected!("avx"));
|
||||
println!("avx2: {:?}", is_x86_feature_detected!("avx2"));
|
||||
println!("sha512: {:?}", is_x86_feature_detected!("sha512"));
|
||||
println!("sm3: {:?}", is_x86_feature_detected!("sm3"));
|
||||
println!("sm4: {:?}", is_x86_feature_detected!("sm4"));
|
||||
println!("avx512f: {:?}", is_x86_feature_detected!("avx512f"));
|
||||
println!("avx512cd: {:?}", is_x86_feature_detected!("avx512cd"));
|
||||
println!("avx512er: {:?}", is_x86_feature_detected!("avx512er"));
|
||||
println!("avx512pf: {:?}", is_x86_feature_detected!("avx512pf"));
|
||||
println!("avx512bw: {:?}", is_x86_feature_detected!("avx512bw"));
|
||||
println!("avx512dq: {:?}", is_x86_feature_detected!("avx512dq"));
|
||||
println!("avx512vl: {:?}", is_x86_feature_detected!("avx512vl"));
|
||||
println!("avx512_ifma: {:?}", is_x86_feature_detected!("avx512ifma"));
|
||||
println!("avx512vbmi {:?}", is_x86_feature_detected!("avx512vbmi"));
|
||||
println!("avx512_vpopcntdq: {:?}", is_x86_feature_detected!("avx512vpopcntdq"));
|
||||
println!("avx512vbmi2: {:?}", is_x86_feature_detected!("avx512vbmi2"));
|
||||
println!("gfni: {:?}", is_x86_feature_detected!("gfni"));
|
||||
println!("vaes: {:?}", is_x86_feature_detected!("vaes"));
|
||||
println!("vpclmulqdq: {:?}", is_x86_feature_detected!("vpclmulqdq"));
|
||||
println!("avx512vnni: {:?}", is_x86_feature_detected!("avx512vnni"));
|
||||
println!("avx512bitalg: {:?}", is_x86_feature_detected!("avx512bitalg"));
|
||||
println!("avx512bf16: {:?}", is_x86_feature_detected!("avx512bf16"));
|
||||
println!("avx512vp2intersect: {:?}", is_x86_feature_detected!("avx512vp2intersect"));
|
||||
println!("avx512fp16: {:?}", is_x86_feature_detected!("avx512fp16"));
|
||||
println!("fma: {:?}", is_x86_feature_detected!("fma"));
|
||||
println!("abm: {:?}", is_x86_feature_detected!("abm"));
|
||||
println!("bmi: {:?}", is_x86_feature_detected!("bmi1"));
|
||||
println!("bmi2: {:?}", is_x86_feature_detected!("bmi2"));
|
||||
println!("tbm: {:?}", is_x86_feature_detected!("tbm"));
|
||||
println!("popcnt: {:?}", is_x86_feature_detected!("popcnt"));
|
||||
println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt"));
|
||||
println!("fxsr: {:?}", is_x86_feature_detected!("fxsr"));
|
||||
println!("xsave: {:?}", is_x86_feature_detected!("xsave"));
|
||||
println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt"));
|
||||
println!("xsaves: {:?}", is_x86_feature_detected!("xsaves"));
|
||||
println!("xsavec: {:?}", is_x86_feature_detected!("xsavec"));
|
||||
println!("cmpxchg16b: {:?}", is_x86_feature_detected!("cmpxchg16b"));
|
||||
println!("adx: {:?}", is_x86_feature_detected!("adx"));
|
||||
println!("rtm: {:?}", is_x86_feature_detected!("rtm"));
|
||||
println!("movbe: {:?}", is_x86_feature_detected!("movbe"));
|
||||
println!("avxvnni: {:?}", is_x86_feature_detected!("avxvnni"));
|
||||
println!("avxvnniint8: {:?}", is_x86_feature_detected!("avxvnniint8"));
|
||||
println!("avxneconvert: {:?}", is_x86_feature_detected!("avxneconvert"));
|
||||
println!("avxifma: {:?}", is_x86_feature_detected!("avxifma"));
|
||||
println!("avxvnniint16: {:?}", is_x86_feature_detected!("avxvnniint16"));
|
||||
println!("amx-bf16: {:?}", is_x86_feature_detected!("amx-bf16"));
|
||||
println!("amx-tile: {:?}", is_x86_feature_detected!("amx-tile"));
|
||||
println!("amx-int8: {:?}", is_x86_feature_detected!("amx-int8"));
|
||||
println!("amx-fp16: {:?}", is_x86_feature_detected!("amx-fp16"));
|
||||
println!("amx-complex: {:?}", is_x86_feature_detected!("amx-complex"));
|
||||
println!("xop: {:?}", is_x86_feature_detected!("xop"));
|
||||
println!("kl: {:?}", is_x86_feature_detected!("kl"));
|
||||
println!("widekl: {:?}", is_x86_feature_detected!("widekl"));
|
||||
println!("movrs: {:?}", is_x86_feature_detected!("movrs"));
|
||||
println!("amx-fp8: {:?}", is_x86_feature_detected!("amx-fp8"));
|
||||
println!("amx-tf32: {:?}", is_x86_feature_detected!("amx-tf32"));
|
||||
println!("amx-avx512: {:?}", is_x86_feature_detected!("amx-avx512"));
|
||||
println!("amx-movrs: {:?}", is_x86_feature_detected!("amx-movrs"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn x86_deprecated() {
|
||||
println!("avx512gfni {:?}", is_x86_feature_detected!("avx512gfni"));
|
||||
println!("avx512vaes {:?}", is_x86_feature_detected!("avx512vaes"));
|
||||
println!("avx512vpclmulqdq {:?}", is_x86_feature_detected!("avx512vpclmulqdq"));
|
||||
}
|
||||
@@ -27,8 +27,18 @@ cp_std path:
|
||||
@sed -i -f patches.sed {{ "src" / path }}
|
||||
|
||||
setup-std:
|
||||
@# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs, sync.rs
|
||||
# @just cp_std "collections/mod.rs"
|
||||
@mkdir "crates/std_detect" -p
|
||||
@cp {{ RUST_SRC / "../../std_detect/*" }} {{ "crates/std_detect/" }} -r
|
||||
# Remove the depency to core and alloc
|
||||
@sed -i "19d" "crates/std_detect/Cargo.toml"
|
||||
@sed -i "19d" "crates/std_detect/Cargo.toml"
|
||||
|
||||
@mkdir "crates/panic_abort" -p
|
||||
@cp {{ RUST_SRC / "../../panic_abort/*" }} {{ "crates/panic_abort/" }} -r
|
||||
# Remove the depency to core and alloc
|
||||
@sed -i "21d" "crates/panic_abort/Cargo.toml"
|
||||
@sed -i "15d" "crates/panic_abort/Cargo.toml"
|
||||
|
||||
@just cp_std "alloc.rs"
|
||||
@just cp_std "ascii.rs"
|
||||
@just cp_std "backtrace.rs"
|
||||
@@ -48,91 +58,187 @@ setup-std:
|
||||
# @just cp_std "tests_helpers.rs"
|
||||
@just cp_std "time.rs"
|
||||
|
||||
@just cp_std "os/mod.rs"
|
||||
@just cp_std "os/raw/mod.rs"
|
||||
@just cp_std "os/raw/tests.rs"
|
||||
@# Complete
|
||||
@just cp_std "backtrace/tests.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "collections/hash/map/tests.rs"
|
||||
@just cp_std "collections/hash/set/tests.rs"
|
||||
@just cp_std "collections/hash/map.rs"
|
||||
@just cp_std "collections/hash/set.rs"
|
||||
@just cp_std "collections/hash/mod.rs"
|
||||
@just cp_std "io/error.rs"
|
||||
@just cp_std "io/error/repr_bitpacked.rs"
|
||||
@just cp_std "io/error/repr_unpacked.rs"
|
||||
@just cp_std "io/error/tests.rs"
|
||||
@just cp_std "io/cursor.rs"
|
||||
@just cp_std "io/cursor/tests.rs"
|
||||
@just cp_std "io/prelude.rs"
|
||||
@just cp_std "io/impls.rs"
|
||||
@just cp_std "io/impls/tests.rs"
|
||||
@just cp_std "io/tests.rs"
|
||||
@just cp_std "io/util.rs"
|
||||
@just cp_std "io/util/tests.rs"
|
||||
@just cp_std "io/copy.rs"
|
||||
@just cp_std "io/copy/tests.rs"
|
||||
@just cp_std "io/pipe.rs"
|
||||
@just cp_std "io/pipe/tests.rs"
|
||||
@just cp_std "io/stdio.rs"
|
||||
@just cp_std "io/buffered/mod.rs"
|
||||
@just cp_std "io/buffered/bufreader.rs"
|
||||
@just cp_std "collections/mod.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "ffi/os_str/tests.rs"
|
||||
@just cp_std "ffi/c_str.rs"
|
||||
@just cp_std "ffi/mod.rs"
|
||||
@just cp_std "ffi/os_str.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "fs/tests.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "hash/mod.rs"
|
||||
@just cp_std "hash/random.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "io/buffered/bufreader/buffer.rs"
|
||||
@just cp_std "io/buffered/bufreader.rs"
|
||||
@just cp_std "io/buffered/bufwriter.rs"
|
||||
@just cp_std "io/buffered/linewriter.rs"
|
||||
@just cp_std "io/buffered/linewritershim.rs"
|
||||
@just cp_std "sync/once.rs"
|
||||
@just cp_std "sync/once_lock.rs"
|
||||
@just cp_std "sync/lazy_lock.rs"
|
||||
@just cp_std "sync/nonpoison.rs"
|
||||
@just cp_std "sync/nonpoison/condvar.rs"
|
||||
@just cp_std "sync/nonpoison/mutex.rs"
|
||||
@just cp_std "sync/nonpoison/rwlock.rs"
|
||||
@just cp_std "sync/poison.rs"
|
||||
@just cp_std "sync/poison/condvar.rs"
|
||||
@just cp_std "sync/poison/mutex.rs"
|
||||
@just cp_std "sync/poison/rwlock.rs"
|
||||
@just cp_std "sync/barrier.rs"
|
||||
@just cp_std "sync/reentrant_lock.rs"
|
||||
@just cp_std "sync/mpsc.rs"
|
||||
@just cp_std "sync/mpmc/mod.rs"
|
||||
@just cp_std "io/buffered/mod.rs"
|
||||
@just cp_std "io/buffered/tests.rs"
|
||||
@just cp_std "io/copy/tests.rs"
|
||||
@just cp_std "io/cursor/tests.rs"
|
||||
@just cp_std "io/error/repr_bitpacked.rs"
|
||||
@just cp_std "io/error/repr_unpacked.rs"
|
||||
@just cp_std "io/error/tests.rs"
|
||||
@just cp_std "io/impls/tests.rs"
|
||||
@just cp_std "io/pipe/tests.rs"
|
||||
@just cp_std "io/stdio/tests.rs"
|
||||
@just cp_std "io/util/tests.rs"
|
||||
@just cp_std "io/copy.rs"
|
||||
@just cp_std "io/cursor.rs"
|
||||
@just cp_std "io/error.rs"
|
||||
@just cp_std "io/impls.rs"
|
||||
@just cp_std "io/mod.rs"
|
||||
@just cp_std "io/pipe.rs"
|
||||
@just cp_std "io/prelude.rs"
|
||||
@just cp_std "io/stdio.rs"
|
||||
@just cp_std "io/tests.rs"
|
||||
@just cp_std "io/util.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "net/ip_addr/tests.rs"
|
||||
@just cp_std "net/socket_addr/tests.rs"
|
||||
@just cp_std "net/tcp/tests.rs"
|
||||
@just cp_std "net/udp/tests.rs"
|
||||
@just cp_std "net/hostname.rs"
|
||||
@just cp_std "net/ip_addr.rs"
|
||||
@just cp_std "net/mod.rs"
|
||||
@just cp_std "net/socket_addr.rs"
|
||||
@just cp_std "net/tcp.rs"
|
||||
@just cp_std "net/test.rs"
|
||||
@just cp_std "net/udp.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "num/mod.rs"
|
||||
@just cp_std "num/f16.rs"
|
||||
@just cp_std "num/f32.rs"
|
||||
@just cp_std "num/f64.rs"
|
||||
@just cp_std "num/f128.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "os/raw/mod.rs"
|
||||
@just cp_std "os/raw/tests.rs"
|
||||
@just cp_std "os/mod.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "prelude/mod.rs"
|
||||
@just cp_std "prelude/v1.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "process/tests.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "sync/mpmc/array.rs"
|
||||
@just cp_std "sync/mpmc/context.rs"
|
||||
@just cp_std "sync/mpmc/counter.rs"
|
||||
@just cp_std "sync/mpmc/error.rs"
|
||||
@just cp_std "sync/mpmc/list.rs"
|
||||
@just cp_std "sync/mpmc/mod.rs"
|
||||
@just cp_std "sync/mpmc/select.rs"
|
||||
@just cp_std "sync/mpmc/tests.rs"
|
||||
@just cp_std "sync/mpmc/utils.rs"
|
||||
@just cp_std "sync/mpmc/waker.rs"
|
||||
@just cp_std "sync/mpmc/zero.rs"
|
||||
@just cp_std "hash/mod.rs"
|
||||
@just cp_std "hash/random.rs"
|
||||
@just cp_std "num/mod.rs"
|
||||
@just cp_std "ffi/c_str.rs"
|
||||
@just cp_std "ffi/mod.rs"
|
||||
@just cp_std "ffi/os_str.rs"
|
||||
@just cp_std "ffi/os_str/tests.rs"
|
||||
@just cp_std "thread/local.rs"
|
||||
@just cp_std "thread/thread.rs"
|
||||
@just cp_std "thread/id.rs"
|
||||
@just cp_std "thread/main_thread.rs"
|
||||
@just cp_std "thread/current.rs"
|
||||
@just cp_std "thread/join_handle.rs"
|
||||
@just cp_std "thread/functions.rs"
|
||||
@just cp_std "thread/lifecycle.rs"
|
||||
@just cp_std "thread/builder.rs"
|
||||
@just cp_std "thread/scoped.rs"
|
||||
@just cp_std "thread/spawnhook.rs"
|
||||
@just cp_std "sys/exit.rs"
|
||||
@just cp_std "sys/env_consts.rs"
|
||||
@just cp_std "sys/configure_builtins.rs"
|
||||
@just cp_std "sys/cmath.rs"
|
||||
@just cp_std "sys/process/mod.rs"
|
||||
@just cp_std "sys/process/env.rs"
|
||||
@just cp_std "sys/process/unsupported.rs"
|
||||
@just cp_std "sync/nonpoison/condvar.rs"
|
||||
@just cp_std "sync/nonpoison/mutex.rs"
|
||||
@just cp_std "sync/nonpoison/rwlock.rs"
|
||||
@just cp_std "sync/poison/condvar.rs"
|
||||
@just cp_std "sync/poison/mutex.rs"
|
||||
@just cp_std "sync/poison/rwlock.rs"
|
||||
@just cp_std "sync/nonpoison.rs"
|
||||
@just cp_std "sync/barrier.rs"
|
||||
@just cp_std "sync/lazy_lock.rs"
|
||||
@just cp_std "sync/mod.rs"
|
||||
@just cp_std "sync/mpsc.rs"
|
||||
@just cp_std "sync/nonpoison.rs"
|
||||
@just cp_std "sync/once.rs"
|
||||
@just cp_std "sync/once_lock.rs"
|
||||
@just cp_std "sync/oneshot.rs"
|
||||
@just cp_std "sync/poison.rs"
|
||||
@just cp_std "sync/reentrant_lock.rs"
|
||||
|
||||
|
||||
@# Complete
|
||||
@just cp_std "sys/alloc/mod.rs"
|
||||
|
||||
@just cp_std "sys/args/mod.rs"
|
||||
@just cp_std "sys/args/unsupported.rs"
|
||||
|
||||
@just cp_std "sys/env/mod.rs"
|
||||
@just cp_std "sys/env/common.rs"
|
||||
@just cp_std "sys/env/unsupported.rs"
|
||||
|
||||
@just cp_std "sys/fd/mod.rs"
|
||||
|
||||
@just cp_std "sys/fs/mod.rs"
|
||||
@just cp_std "sys/fs/common.rs"
|
||||
@just cp_std "sys/fs/unsupported.rs"
|
||||
|
||||
@just cp_std "sys/helpers/mod.rs"
|
||||
@just cp_std "sys/helpers/small_c_string.rs"
|
||||
@just cp_std "sys/helpers/tests.rs"
|
||||
@just cp_std "sys/helpers/wstr.rs"
|
||||
|
||||
@just cp_std "sys/io/error/generic.rs"
|
||||
@just cp_std "sys/io/error/mod.rs"
|
||||
@just cp_std "sys/io/io_slice/unsupported.rs"
|
||||
@just cp_std "sys/io/is_terminal/unsupported.rs"
|
||||
@just cp_std "sys/io/kernel_copy/mod.rs"
|
||||
@just cp_std "sys/io/mod.rs"
|
||||
|
||||
@just cp_std "sys/net/connection/mod.rs"
|
||||
@just cp_std "sys/net/connection/unsupported.rs"
|
||||
@just cp_std "sys/net/hostname/mod.rs"
|
||||
@just cp_std "sys/net/hostname/unsupported.rs"
|
||||
@just cp_std "sys/net/mod.rs"
|
||||
|
||||
@just cp_std "sys/os_str/bytes/tests.rs"
|
||||
@just cp_std "sys/os_str/bytes.rs"
|
||||
@just cp_std "sys/os_str/mod.rs"
|
||||
|
||||
@just cp_std "sys/pal/mod.rs"
|
||||
@just cp_std "sys/pal/unsupported/mod.rs"
|
||||
@just cp_std "sys/pal/unsupported/common.rs"
|
||||
@just cp_std "sys/pal/unsupported/os.rs"
|
||||
|
||||
@just cp_std "sys/path/mod.rs"
|
||||
@just cp_std "sys/path/unix.rs"
|
||||
|
||||
@just cp_std "sys/personality/dwarf/eh.rs"
|
||||
@just cp_std "sys/personality/dwarf/mod.rs"
|
||||
@just cp_std "sys/personality/dwarf/tests.rs"
|
||||
@just cp_std "sys/personality/mod.rs"
|
||||
|
||||
@just cp_std "sys/pipe/mod.rs"
|
||||
@just cp_std "sys/pipe/unsupported.rs"
|
||||
|
||||
@just cp_std "sys/platform_version/mod.rs"
|
||||
|
||||
@just cp_std "sys/process/mod.rs"
|
||||
@just cp_std "sys/process/env.rs"
|
||||
@just cp_std "sys/process/unsupported.rs"
|
||||
|
||||
@just cp_std "sys/random/mod.rs"
|
||||
@just cp_std "sys/random/unsupported.rs"
|
||||
|
||||
@just cp_std "sys/stdio/mod.rs"
|
||||
@just cp_std "sys/stdio/unsupported.rs"
|
||||
|
||||
@just cp_std "sys/sync/condvar/mod.rs"
|
||||
@just cp_std "sys/sync/condvar/no_threads.rs"
|
||||
@just cp_std "sys/sync/mutex/mod.rs"
|
||||
@@ -143,42 +249,38 @@ setup-std:
|
||||
@just cp_std "sys/sync/rwlock/no_threads.rs"
|
||||
@just cp_std "sys/sync/thread_parking/mod.rs"
|
||||
@just cp_std "sys/sync/thread_parking/unsupported.rs"
|
||||
@just cp_std "sys/sync/mod.rs"
|
||||
@just cp_std "sys/sync/once_box.rs"
|
||||
|
||||
@just cp_std "sys/thread/mod.rs"
|
||||
@just cp_std "sys/thread/unsupported.rs"
|
||||
|
||||
@just cp_std "sys/thread_local/mod.rs"
|
||||
@just cp_std "sys/thread_local/no_threads.rs"
|
||||
@just cp_std "sys/thread_local/os.rs"
|
||||
@just cp_std "sys/thread_local/mod.rs"
|
||||
|
||||
@just cp_std "sys/time/mod.rs"
|
||||
@just cp_std "sys/time/unsupported.rs"
|
||||
@just cp_std "sys/random/mod.rs"
|
||||
@just cp_std "sys/random/unsupported.rs"
|
||||
@just cp_std "sys/env/mod.rs"
|
||||
@just cp_std "sys/env/common.rs"
|
||||
@just cp_std "sys/env/unsupported.rs"
|
||||
@just cp_std "sys/os_str/mod.rs"
|
||||
@just cp_std "sys/os_str/bytes.rs"
|
||||
@just cp_std "sys/os_str/bytes/tests.rs"
|
||||
@just cp_std "sys/path/mod.rs"
|
||||
@just cp_std "sys/path/unix.rs"
|
||||
@just cp_std "sys/fs/mod.rs"
|
||||
@just cp_std "sys/fs/common.rs"
|
||||
@just cp_std "sys/fs/unsupported.rs"
|
||||
@just cp_std "sys/io/error/generic.rs"
|
||||
@just cp_std "sys/io/io_slice/unsupported.rs"
|
||||
@just cp_std "sys/io/is_terminal/unsupported.rs"
|
||||
@just cp_std "sys/io/kernel_copy/mod.rs"
|
||||
@just cp_std "sys/io/mod.rs"
|
||||
@just cp_std "sys/io/error/mod.rs"
|
||||
@just cp_std "sys/pipe/mod.rs"
|
||||
@just cp_std "sys/pipe/unsupported.rs"
|
||||
@just cp_std "sys/stdio/mod.rs"
|
||||
@just cp_std "sys/stdio/unsupported.rs"
|
||||
@just cp_std "sys/alloc/mod.rs"
|
||||
|
||||
@just cp_std "sys/backtrace.rs"
|
||||
@just cp_std "sys/fs/mod.rs"
|
||||
@just cp_std "sys/fs/unsupported.rs"
|
||||
@just cp_std "sys/helpers/mod.rs"
|
||||
@just cp_std "sys/helpers/small_c_string.rs"
|
||||
@just cp_std "sys/helpers/tests.rs"
|
||||
@just cp_std "sys/helpers/wstr.rs"
|
||||
@just cp_std "sys/cmath.rs"
|
||||
@just cp_std "sys/configure_builtins.rs"
|
||||
@just cp_std "sys/env_consts.rs"
|
||||
@just cp_std "sys/exit.rs"
|
||||
@just cp_std "sys/mod.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "thread/builder.rs"
|
||||
@just cp_std "thread/current.rs"
|
||||
@just cp_std "thread/functions.rs"
|
||||
@just cp_std "thread/id.rs"
|
||||
@just cp_std "thread/join_handle.rs"
|
||||
@just cp_std "thread/lifecycle.rs"
|
||||
@just cp_std "thread/local.rs"
|
||||
@just cp_std "thread/main_thread.rs"
|
||||
@just cp_std "thread/mod.rs"
|
||||
@just cp_std "thread/scoped.rs"
|
||||
@just cp_std "thread/spawnhook.rs"
|
||||
@just cp_std "thread/tests.rs"
|
||||
@just cp_std "thread/thread.rs"
|
||||
@# Copied but edited for the moment
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
s|crate::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
|
||||
s|alloc::ffi|alloc_crate::ffi|g
|
||||
s|alloc::slice::Join|alloc_crate::slice::Join|g
|
||||
s|alloc::bstr|alloc_crate::bstr|g
|
||||
s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
|
||||
s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g
|
||||
/crate::sys::os::getpid/c \ todo!()
|
||||
/\[doc = include_str!/c \ // todo retreive docs
|
||||
|
||||
2
crates/std/patches/collections/hash/map.sed
Normal file
2
crates/std/patches/collections/hash/map.sed
Normal file
@@ -0,0 +1,2 @@
|
||||
362d
|
||||
361a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")]
|
||||
2
crates/std/patches/collections/hash/set.sed
Normal file
2
crates/std/patches/collections/hash/set.sed
Normal file
@@ -0,0 +1,2 @@
|
||||
234d
|
||||
233a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")]
|
||||
98
crates/std/src/backtrace/tests.rs
Normal file
98
crates/std/src/backtrace/tests.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use super::*;
|
||||
use crate::panic::RefUnwindSafe;
|
||||
|
||||
fn generate_fake_frames() -> Vec<BacktraceFrame> {
|
||||
vec![
|
||||
BacktraceFrame {
|
||||
frame: RawFrame::Fake,
|
||||
symbols: vec![BacktraceSymbol {
|
||||
name: Some(b"std::backtrace::Backtrace::create".to_vec()),
|
||||
filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())),
|
||||
lineno: Some(100),
|
||||
colno: None,
|
||||
}],
|
||||
},
|
||||
BacktraceFrame {
|
||||
frame: RawFrame::Fake,
|
||||
symbols: vec![BacktraceSymbol {
|
||||
name: Some(b"__rust_maybe_catch_panic".to_vec()),
|
||||
filename: None,
|
||||
lineno: None,
|
||||
colno: None,
|
||||
}],
|
||||
},
|
||||
BacktraceFrame {
|
||||
frame: RawFrame::Fake,
|
||||
symbols: vec![
|
||||
BacktraceSymbol {
|
||||
name: Some(b"std::rt::lang_start_internal".to_vec()),
|
||||
filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
|
||||
lineno: Some(300),
|
||||
colno: Some(5),
|
||||
},
|
||||
BacktraceSymbol {
|
||||
name: Some(b"std::rt::lang_start".to_vec()),
|
||||
filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
|
||||
lineno: Some(400),
|
||||
colno: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let backtrace = Backtrace {
|
||||
inner: Inner::Captured(
|
||||
(Capture { actual_start: 1, frames: generate_fake_frames() }).into(),
|
||||
),
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
let expected = "Backtrace [\
|
||||
\n { fn: \"__rust_maybe_catch_panic\" },\
|
||||
\n { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\
|
||||
\n { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\
|
||||
\n]";
|
||||
|
||||
assert_eq!(format!("{backtrace:#?}"), expected);
|
||||
|
||||
// Format the backtrace a second time, just to make sure lazily resolved state is stable
|
||||
assert_eq!(format!("{backtrace:#?}"), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_frames() {
|
||||
let backtrace = Backtrace {
|
||||
inner: Inner::Captured(
|
||||
(Capture { actual_start: 1, frames: generate_fake_frames() }).into(),
|
||||
),
|
||||
};
|
||||
|
||||
let frames = backtrace.frames();
|
||||
|
||||
#[rustfmt::skip]
|
||||
let expected = vec![
|
||||
"[
|
||||
{ fn: \"std::backtrace::Backtrace::create\", file: \"rust/backtrace.rs\", line: 100 },
|
||||
]",
|
||||
"[
|
||||
{ fn: \"__rust_maybe_catch_panic\" },
|
||||
]",
|
||||
"[
|
||||
{ fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },
|
||||
{ fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },
|
||||
]"
|
||||
];
|
||||
|
||||
let mut iter = frames.iter().zip(expected.iter());
|
||||
|
||||
assert!(iter.all(|(f, e)| format!("{f:#?}") == *e));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backtrace_unwind_safe() {
|
||||
fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
|
||||
assert_unwind_safe::<Backtrace>();
|
||||
}
|
||||
@@ -359,7 +359,7 @@ impl<K, V, S> HashMap<K, V, S> {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
#[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")]
|
||||
#[rustc_const_unstable(feature = "custom_std", issue = "none")]
|
||||
pub const fn with_hasher(hash_builder: S) -> HashMap<K, V, S> {
|
||||
HashMap { base: base::HashMap::with_hasher(hash_builder) }
|
||||
}
|
||||
|
||||
1059
crates/std/src/collections/hash/map/tests.rs
Normal file
1059
crates/std/src/collections/hash/map/tests.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ use hashbrown::hash_set as base;
|
||||
use super::map::map_try_reserve_error;
|
||||
use crate::alloc::{Allocator, Global};
|
||||
use crate::borrow::Borrow;
|
||||
use alloc_crate::collections::TryReserveError;
|
||||
use crate::collections::TryReserveError;
|
||||
use crate::fmt;
|
||||
use crate::hash::{BuildHasher, Hash, RandomState};
|
||||
use crate::iter::{Chain, FusedIterator};
|
||||
@@ -231,7 +231,7 @@ impl<T, S> HashSet<T, S> {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
#[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")]
|
||||
#[rustc_const_unstable(feature = "custom_std", issue = "none")]
|
||||
pub const fn with_hasher(hasher: S) -> HashSet<T, S> {
|
||||
HashSet { base: base::HashSet::with_hasher(hasher) }
|
||||
}
|
||||
|
||||
529
crates/std/src/collections/hash/set/tests.rs
Normal file
529
crates/std/src/collections/hash/set/tests.rs
Normal file
@@ -0,0 +1,529 @@
|
||||
use super::HashSet;
|
||||
use crate::hash::RandomState;
|
||||
use crate::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use crate::sync::Arc;
|
||||
use crate::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
#[test]
|
||||
fn test_zero_capacities() {
|
||||
type HS = HashSet<i32>;
|
||||
|
||||
let s = HS::new();
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let s = HS::default();
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let s = HS::with_hasher(RandomState::new());
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let s = HS::with_capacity(0);
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let s = HS::with_capacity_and_hasher(0, RandomState::new());
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let mut s = HS::new();
|
||||
s.insert(1);
|
||||
s.insert(2);
|
||||
s.remove(&1);
|
||||
s.remove(&2);
|
||||
s.shrink_to_fit();
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let mut s = HS::new();
|
||||
s.reserve(0);
|
||||
assert_eq!(s.capacity(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_disjoint() {
|
||||
let mut xs = HashSet::new();
|
||||
let mut ys = HashSet::new();
|
||||
assert!(xs.is_disjoint(&ys));
|
||||
assert!(ys.is_disjoint(&xs));
|
||||
assert!(xs.insert(5));
|
||||
assert!(ys.insert(11));
|
||||
assert!(xs.is_disjoint(&ys));
|
||||
assert!(ys.is_disjoint(&xs));
|
||||
assert!(xs.insert(7));
|
||||
assert!(xs.insert(19));
|
||||
assert!(xs.insert(4));
|
||||
assert!(ys.insert(2));
|
||||
assert!(ys.insert(-11));
|
||||
assert!(xs.is_disjoint(&ys));
|
||||
assert!(ys.is_disjoint(&xs));
|
||||
assert!(ys.insert(7));
|
||||
assert!(!xs.is_disjoint(&ys));
|
||||
assert!(!ys.is_disjoint(&xs));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subset_and_superset() {
|
||||
let mut a = HashSet::new();
|
||||
assert!(a.insert(0));
|
||||
assert!(a.insert(5));
|
||||
assert!(a.insert(11));
|
||||
assert!(a.insert(7));
|
||||
|
||||
let mut b = HashSet::new();
|
||||
assert!(b.insert(0));
|
||||
assert!(b.insert(7));
|
||||
assert!(b.insert(19));
|
||||
assert!(b.insert(250));
|
||||
assert!(b.insert(11));
|
||||
assert!(b.insert(200));
|
||||
|
||||
assert!(!a.is_subset(&b));
|
||||
assert!(!a.is_superset(&b));
|
||||
assert!(!b.is_subset(&a));
|
||||
assert!(!b.is_superset(&a));
|
||||
|
||||
assert!(b.insert(5));
|
||||
|
||||
assert!(a.is_subset(&b));
|
||||
assert!(!a.is_superset(&b));
|
||||
assert!(!b.is_subset(&a));
|
||||
assert!(b.is_superset(&a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterate() {
|
||||
let mut a = HashSet::new();
|
||||
for i in 0..32 {
|
||||
assert!(a.insert(i));
|
||||
}
|
||||
let mut observed: u32 = 0;
|
||||
for k in &a {
|
||||
observed |= 1 << *k;
|
||||
}
|
||||
assert_eq!(observed, 0xFFFF_FFFF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersection() {
|
||||
let mut a = HashSet::new();
|
||||
let mut b = HashSet::new();
|
||||
assert!(a.intersection(&b).next().is_none());
|
||||
|
||||
assert!(a.insert(11));
|
||||
assert!(a.insert(1));
|
||||
assert!(a.insert(3));
|
||||
assert!(a.insert(77));
|
||||
assert!(a.insert(103));
|
||||
assert!(a.insert(5));
|
||||
assert!(a.insert(-5));
|
||||
|
||||
assert!(b.insert(2));
|
||||
assert!(b.insert(11));
|
||||
assert!(b.insert(77));
|
||||
assert!(b.insert(-9));
|
||||
assert!(b.insert(-42));
|
||||
assert!(b.insert(5));
|
||||
assert!(b.insert(3));
|
||||
|
||||
let mut i = 0;
|
||||
let expected = [3, 5, 11, 77];
|
||||
for x in a.intersection(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
|
||||
assert!(a.insert(9)); // make a bigger than b
|
||||
|
||||
i = 0;
|
||||
for x in a.intersection(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
|
||||
i = 0;
|
||||
for x in b.intersection(&a) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_difference() {
|
||||
let mut a = HashSet::new();
|
||||
let mut b = HashSet::new();
|
||||
|
||||
assert!(a.insert(1));
|
||||
assert!(a.insert(3));
|
||||
assert!(a.insert(5));
|
||||
assert!(a.insert(9));
|
||||
assert!(a.insert(11));
|
||||
|
||||
assert!(b.insert(3));
|
||||
assert!(b.insert(9));
|
||||
|
||||
let mut i = 0;
|
||||
let expected = [1, 5, 11];
|
||||
for x in a.difference(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_symmetric_difference() {
|
||||
let mut a = HashSet::new();
|
||||
let mut b = HashSet::new();
|
||||
|
||||
assert!(a.insert(1));
|
||||
assert!(a.insert(3));
|
||||
assert!(a.insert(5));
|
||||
assert!(a.insert(9));
|
||||
assert!(a.insert(11));
|
||||
|
||||
assert!(b.insert(-2));
|
||||
assert!(b.insert(3));
|
||||
assert!(b.insert(9));
|
||||
assert!(b.insert(14));
|
||||
assert!(b.insert(22));
|
||||
|
||||
let mut i = 0;
|
||||
let expected = [-2, 1, 5, 11, 14, 22];
|
||||
for x in a.symmetric_difference(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_union() {
|
||||
let mut a = HashSet::new();
|
||||
let mut b = HashSet::new();
|
||||
assert!(a.union(&b).next().is_none());
|
||||
assert!(b.union(&a).next().is_none());
|
||||
|
||||
assert!(a.insert(1));
|
||||
assert!(a.insert(3));
|
||||
assert!(a.insert(11));
|
||||
assert!(a.insert(16));
|
||||
assert!(a.insert(19));
|
||||
assert!(a.insert(24));
|
||||
|
||||
assert!(b.insert(-2));
|
||||
assert!(b.insert(1));
|
||||
assert!(b.insert(5));
|
||||
assert!(b.insert(9));
|
||||
assert!(b.insert(13));
|
||||
assert!(b.insert(19));
|
||||
|
||||
let mut i = 0;
|
||||
let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24];
|
||||
for x in a.union(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
|
||||
assert!(a.insert(9)); // make a bigger than b
|
||||
assert!(a.insert(5));
|
||||
|
||||
i = 0;
|
||||
for x in a.union(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
|
||||
i = 0;
|
||||
for x in b.union(&a) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_iter() {
|
||||
let xs = [1, 2, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
let set: HashSet<_> = xs.iter().cloned().collect();
|
||||
|
||||
for x in &xs {
|
||||
assert!(set.contains(x));
|
||||
}
|
||||
|
||||
assert_eq!(set.iter().len(), xs.len() - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_iter() {
|
||||
let hs = {
|
||||
let mut hs = HashSet::new();
|
||||
|
||||
hs.insert('a');
|
||||
hs.insert('b');
|
||||
|
||||
hs
|
||||
};
|
||||
|
||||
let v = hs.into_iter().collect::<Vec<char>>();
|
||||
assert!(v == ['a', 'b'] || v == ['b', 'a']);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
// These constants once happened to expose a bug in insert().
|
||||
// I'm keeping them around to prevent a regression.
|
||||
let mut s1 = HashSet::new();
|
||||
|
||||
s1.insert(1);
|
||||
s1.insert(2);
|
||||
s1.insert(3);
|
||||
|
||||
let mut s2 = HashSet::new();
|
||||
|
||||
s2.insert(1);
|
||||
s2.insert(2);
|
||||
|
||||
assert!(s1 != s2);
|
||||
|
||||
s2.insert(3);
|
||||
|
||||
assert_eq!(s1, s2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_show() {
|
||||
let mut set = HashSet::new();
|
||||
let empty = HashSet::<i32>::new();
|
||||
|
||||
set.insert(1);
|
||||
set.insert(2);
|
||||
|
||||
let set_str = format!("{set:?}");
|
||||
|
||||
assert!(set_str == "{1, 2}" || set_str == "{2, 1}");
|
||||
assert_eq!(format!("{empty:?}"), "{}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trivial_drain() {
|
||||
let mut s = HashSet::<i32>::new();
|
||||
for _ in s.drain() {}
|
||||
assert!(s.is_empty());
|
||||
drop(s);
|
||||
|
||||
let mut s = HashSet::<i32>::new();
|
||||
drop(s.drain());
|
||||
assert!(s.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drain() {
|
||||
let mut s: HashSet<_> = (1..100).collect();
|
||||
|
||||
// try this a bunch of times to make sure we don't screw up internal state.
|
||||
for _ in 0..20 {
|
||||
assert_eq!(s.len(), 99);
|
||||
|
||||
{
|
||||
let mut last_i = 0;
|
||||
let mut d = s.drain();
|
||||
for (i, x) in d.by_ref().take(50).enumerate() {
|
||||
last_i = i;
|
||||
assert!(x != 0);
|
||||
}
|
||||
assert_eq!(last_i, 49);
|
||||
}
|
||||
|
||||
for _ in &s {
|
||||
panic!("s should be empty!");
|
||||
}
|
||||
|
||||
// reset to try again.
|
||||
s.extend(1..100);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
use crate::hash;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Foo(&'static str, #[allow(dead_code)] i32);
|
||||
|
||||
impl PartialEq for Foo {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Foo {}
|
||||
|
||||
impl hash::Hash for Foo {
|
||||
fn hash<H: hash::Hasher>(&self, h: &mut H) {
|
||||
self.0.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
let mut s = HashSet::new();
|
||||
assert_eq!(s.replace(Foo("a", 1)), None);
|
||||
assert_eq!(s.len(), 1);
|
||||
assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1)));
|
||||
assert_eq!(s.len(), 1);
|
||||
|
||||
let mut it = s.iter();
|
||||
assert_eq!(it.next(), Some(&Foo("a", 2)));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extend_ref() {
|
||||
let mut a = HashSet::new();
|
||||
a.insert(1);
|
||||
|
||||
a.extend(&[2, 3, 4]);
|
||||
|
||||
assert_eq!(a.len(), 4);
|
||||
assert!(a.contains(&1));
|
||||
assert!(a.contains(&2));
|
||||
assert!(a.contains(&3));
|
||||
assert!(a.contains(&4));
|
||||
|
||||
let mut b = HashSet::new();
|
||||
b.insert(5);
|
||||
b.insert(6);
|
||||
|
||||
a.extend(&b);
|
||||
|
||||
assert_eq!(a.len(), 6);
|
||||
assert!(a.contains(&1));
|
||||
assert!(a.contains(&2));
|
||||
assert!(a.contains(&3));
|
||||
assert!(a.contains(&4));
|
||||
assert!(a.contains(&5));
|
||||
assert!(a.contains(&6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retain() {
|
||||
let xs = [1, 2, 3, 4, 5, 6];
|
||||
let mut set: HashSet<i32> = xs.iter().cloned().collect();
|
||||
set.retain(|&k| k % 2 == 0);
|
||||
assert_eq!(set.len(), 3);
|
||||
assert!(set.contains(&2));
|
||||
assert!(set.contains(&4));
|
||||
assert!(set.contains(&6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_if() {
|
||||
let mut x: HashSet<_> = [1].iter().copied().collect();
|
||||
let mut y: HashSet<_> = [1].iter().copied().collect();
|
||||
|
||||
x.extract_if(|_| true).for_each(drop);
|
||||
y.extract_if(|_| false).for_each(drop);
|
||||
assert_eq!(x.len(), 0);
|
||||
assert_eq!(y.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn test_extract_if_drop_panic_leak() {
|
||||
static PREDS: AtomicU32 = AtomicU32::new(0);
|
||||
static DROPS: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Hash)]
|
||||
struct D(i32);
|
||||
impl Drop for D {
|
||||
fn drop(&mut self) {
|
||||
if DROPS.fetch_add(1, Ordering::SeqCst) == 1 {
|
||||
panic!("panic in `drop`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut set = (0..3).map(|i| D(i)).collect::<HashSet<_>>();
|
||||
|
||||
catch_unwind(move || {
|
||||
set.extract_if(|_| {
|
||||
PREDS.fetch_add(1, Ordering::SeqCst);
|
||||
true
|
||||
})
|
||||
.for_each(drop)
|
||||
})
|
||||
.ok();
|
||||
|
||||
assert_eq!(PREDS.load(Ordering::SeqCst), 2);
|
||||
assert_eq!(DROPS.load(Ordering::SeqCst), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn test_extract_if_pred_panic_leak() {
|
||||
static PREDS: AtomicU32 = AtomicU32::new(0);
|
||||
static DROPS: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Hash)]
|
||||
struct D;
|
||||
impl Drop for D {
|
||||
fn drop(&mut self) {
|
||||
DROPS.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let mut set: HashSet<_> = (0..3).map(|_| D).collect();
|
||||
|
||||
catch_unwind(AssertUnwindSafe(|| {
|
||||
set.extract_if(|_| match PREDS.fetch_add(1, Ordering::SeqCst) {
|
||||
0 => true,
|
||||
_ => panic!(),
|
||||
})
|
||||
.for_each(drop)
|
||||
}))
|
||||
.ok();
|
||||
|
||||
assert_eq!(PREDS.load(Ordering::SeqCst), 1);
|
||||
assert_eq!(DROPS.load(Ordering::SeqCst), 3);
|
||||
assert_eq!(set.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_array() {
|
||||
let set = HashSet::from([1, 2, 3, 4]);
|
||||
let unordered_duplicates = HashSet::from([4, 1, 4, 3, 2]);
|
||||
assert_eq!(set, unordered_duplicates);
|
||||
|
||||
// This next line must infer the hasher type parameter.
|
||||
// If you make a change that causes this line to no longer infer,
|
||||
// that's a problem!
|
||||
let _must_not_require_type_annotation = HashSet::from([1, 2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_with_hasher() {
|
||||
const X: HashSet<(), ()> = HashSet::with_hasher(());
|
||||
const Y: HashSet<(), ()> = Default::default();
|
||||
assert_eq!(X.len(), 0);
|
||||
assert_eq!(Y.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_does_not_overwrite_the_value() {
|
||||
let first_value = Arc::new(17);
|
||||
let second_value = Arc::new(17);
|
||||
|
||||
let mut set = HashSet::new();
|
||||
let inserted = set.insert(first_value.clone());
|
||||
assert!(inserted);
|
||||
|
||||
let inserted = set.insert(second_value);
|
||||
assert!(!inserted);
|
||||
|
||||
assert!(
|
||||
Arc::ptr_eq(set.iter().next().unwrap(), &first_value),
|
||||
"Insert must not overwrite the value, so the contained value pointer \
|
||||
must be the same as first value pointer we inserted"
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,459 @@
|
||||
use core::marker::PhantomData;
|
||||
//! Collection types.
|
||||
//!
|
||||
//! Rust's standard collection library provides efficient implementations of the
|
||||
//! most common general purpose programming data structures. By using the
|
||||
//! standard implementations, it should be possible for two libraries to
|
||||
//! communicate without significant data conversion.
|
||||
//!
|
||||
//! To get this out of the way: you should probably just use [`Vec`] or [`HashMap`].
|
||||
//! These two collections cover most use cases for generic data storage and
|
||||
//! processing. They are exceptionally good at doing what they do. All the other
|
||||
//! collections in the standard library have specific use cases where they are
|
||||
//! the optimal choice, but these cases are borderline *niche* in comparison.
|
||||
//! Even when `Vec` and `HashMap` are technically suboptimal, they're probably a
|
||||
//! good enough choice to get started.
|
||||
//!
|
||||
//! Rust's collections can be grouped into four major categories:
|
||||
//!
|
||||
//! * Sequences: [`Vec`], [`VecDeque`], [`LinkedList`]
|
||||
//! * Maps: [`HashMap`], [`BTreeMap`]
|
||||
//! * Sets: [`HashSet`], [`BTreeSet`]
|
||||
//! * Misc: [`BinaryHeap`]
|
||||
//!
|
||||
//! # When Should You Use Which Collection?
|
||||
//!
|
||||
//! These are fairly high-level and quick break-downs of when each collection
|
||||
//! should be considered. Detailed discussions of strengths and weaknesses of
|
||||
//! individual collections can be found on their own documentation pages.
|
||||
//!
|
||||
//! ### Use a [`Vec`] when:
|
||||
//! * You want to collect items up to be processed or sent elsewhere later, and
|
||||
//! don't care about any properties of the actual values being stored.
|
||||
//! * You want a sequence of elements in a particular order, and will only be
|
||||
//! appending to (or near) the end.
|
||||
//! * You want a stack.
|
||||
//! * You want a resizable array.
|
||||
//! * You want a heap-allocated array.
|
||||
//!
|
||||
//! ### Use a [`VecDeque`] when:
|
||||
//! * You want a [`Vec`] that supports efficient insertion at both ends of the
|
||||
//! sequence.
|
||||
//! * You want a queue.
|
||||
//! * You want a double-ended queue (deque).
|
||||
//!
|
||||
//! ### Use a [`LinkedList`] when:
|
||||
//! * You want a [`Vec`] or [`VecDeque`] of unknown size, and can't tolerate
|
||||
//! amortization.
|
||||
//! * You want to efficiently split and append lists.
|
||||
//! * You are *absolutely* certain you *really*, *truly*, want a doubly linked
|
||||
//! list.
|
||||
//!
|
||||
//! ### Use a [`HashMap`] when:
|
||||
//! * You want to associate arbitrary keys with an arbitrary value.
|
||||
//! * You want a cache.
|
||||
//! * You want a map, with no extra functionality.
|
||||
//!
|
||||
//! ### Use a [`BTreeMap`] when:
|
||||
//! * You want a map sorted by its keys.
|
||||
//! * You want to be able to get a range of entries on-demand.
|
||||
//! * You're interested in what the smallest or largest key-value pair is.
|
||||
//! * You want to find the largest or smallest key that is smaller or larger
|
||||
//! than something.
|
||||
//!
|
||||
//! ### Use the `Set` variant of any of these `Map`s when:
|
||||
//! * You just want to remember which keys you've seen.
|
||||
//! * There is no meaningful value to associate with your keys.
|
||||
//! * You just want a set.
|
||||
//!
|
||||
//! ### Use a [`BinaryHeap`] when:
|
||||
//!
|
||||
//! * You want to store a bunch of elements, but only ever want to process the
|
||||
//! "biggest" or "most important" one at any given time.
|
||||
//! * You want a priority queue.
|
||||
//!
|
||||
//! # Performance
|
||||
//!
|
||||
//! Choosing the right collection for the job requires an understanding of what
|
||||
//! each collection is good at. Here we briefly summarize the performance of
|
||||
//! different collections for certain important operations. For further details,
|
||||
//! see each type's documentation, and note that the names of actual methods may
|
||||
//! differ from the tables below on certain collections.
|
||||
//!
|
||||
//! Throughout the documentation, we will adhere to the following conventions
|
||||
//! for operation notation:
|
||||
//!
|
||||
//! * The collection's size is denoted by `n`.
|
||||
//! * If a second collection is involved, its size is denoted by `m`.
|
||||
//! * Item indices are denoted by `i`.
|
||||
//! * Operations which have an *amortized* cost are suffixed with a `*`.
|
||||
//! * Operations with an *expected* cost are suffixed with a `~`.
|
||||
//!
|
||||
//! Calling operations that add to a collection will occasionally require a
|
||||
//! collection to be resized - an extra operation that takes *O*(*n*) time.
|
||||
//!
|
||||
//! *Amortized* costs are calculated to account for the time cost of such resize
|
||||
//! operations *over a sufficiently large series of operations*. An individual
|
||||
//! operation may be slower or faster due to the sporadic nature of collection
|
||||
//! resizing, however the average cost per operation will approach the amortized
|
||||
//! cost.
|
||||
//!
|
||||
//! Rust's collections never automatically shrink, so removal operations aren't
|
||||
//! amortized.
|
||||
//!
|
||||
//! [`HashMap`] uses *expected* costs. It is theoretically possible, though very
|
||||
//! unlikely, for [`HashMap`] to experience significantly worse performance than
|
||||
//! the expected cost. This is due to the probabilistic nature of hashing - i.e.
|
||||
//! it is possible to generate a duplicate hash given some input key that will
|
||||
//! require extra computation to correct.
|
||||
//!
|
||||
//! ## Cost of Collection Operations
|
||||
//!
|
||||
//!
|
||||
//! | | get(i) | insert(i) | remove(i) | append(Vec(m)) | split_off(i) | range | append |
|
||||
//! |----------------|------------------------|-------------------------|------------------------|-------------------|------------------------|-----------------|--------------|
|
||||
//! | [`Vec`] | *O*(1) | *O*(*n*-*i*)* | *O*(*n*-*i*) | *O*(*m*)* | *O*(*n*-*i*) | N/A | N/A |
|
||||
//! | [`VecDeque`] | *O*(1) | *O*(min(*i*, *n*-*i*))* | *O*(min(*i*, *n*-*i*)) | *O*(*m*)* | *O*(min(*i*, *n*-*i*)) | N/A | N/A |
|
||||
//! | [`LinkedList`] | *O*(min(*i*, *n*-*i*)) | *O*(min(*i*, *n*-*i*)) | *O*(min(*i*, *n*-*i*)) | *O*(1) | *O*(min(*i*, *n*-*i*)) | N/A | N/A |
|
||||
//! | [`HashMap`] | *O*(1)~ | *O*(1)~* | *O*(1)~ | N/A | N/A | N/A | N/A |
|
||||
//! | [`BTreeMap`] | *O*(log(*n*)) | *O*(log(*n*)) | *O*(log(*n*)) | N/A | N/A | *O*(log(*n*)) | *O*(*n*+*m*) |
|
||||
//!
|
||||
//! Note that where ties occur, [`Vec`] is generally going to be faster than
|
||||
//! [`VecDeque`], and [`VecDeque`] is generally going to be faster than
|
||||
//! [`LinkedList`].
|
||||
//!
|
||||
//! For Sets, all operations have the cost of the equivalent Map operation.
|
||||
//!
|
||||
//! # Correct and Efficient Usage of Collections
|
||||
//!
|
||||
//! Of course, knowing which collection is the right one for the job doesn't
|
||||
//! instantly permit you to use it correctly. Here are some quick tips for
|
||||
//! efficient and correct usage of the standard collections in general. If
|
||||
//! you're interested in how to use a specific collection in particular, consult
|
||||
//! its documentation for detailed discussion and code examples.
|
||||
//!
|
||||
//! ## Capacity Management
|
||||
//!
|
||||
//! Many collections provide several constructors and methods that refer to
|
||||
//! "capacity". These collections are generally built on top of an array.
|
||||
//! Optimally, this array would be exactly the right size to fit only the
|
||||
//! elements stored in the collection, but for the collection to do this would
|
||||
//! be very inefficient. If the backing array was exactly the right size at all
|
||||
//! times, then every time an element is inserted, the collection would have to
|
||||
//! grow the array to fit it. Due to the way memory is allocated and managed on
|
||||
//! most computers, this would almost surely require allocating an entirely new
|
||||
//! array and copying every single element from the old one into the new one.
|
||||
//! Hopefully you can see that this wouldn't be very efficient to do on every
|
||||
//! operation.
|
||||
//!
|
||||
//! Most collections therefore use an *amortized* allocation strategy. They
|
||||
//! generally let themselves have a fair amount of unoccupied space so that they
|
||||
//! only have to grow on occasion. When they do grow, they allocate a
|
||||
//! substantially larger array to move the elements into so that it will take a
|
||||
//! while for another grow to be required. While this strategy is great in
|
||||
//! general, it would be even better if the collection *never* had to resize its
|
||||
//! backing array. Unfortunately, the collection itself doesn't have enough
|
||||
//! information to do this itself. Therefore, it is up to us programmers to give
|
||||
//! it hints.
|
||||
//!
|
||||
//! Any `with_capacity` constructor will instruct the collection to allocate
|
||||
//! enough space for the specified number of elements. Ideally this will be for
|
||||
//! exactly that many elements, but some implementation details may prevent
|
||||
//! this. See collection-specific documentation for details. In general, use
|
||||
//! `with_capacity` when you know exactly how many elements will be inserted, or
|
||||
//! at least have a reasonable upper-bound on that number.
|
||||
//!
|
||||
//! When anticipating a large influx of elements, the `reserve` family of
|
||||
//! methods can be used to hint to the collection how much room it should make
|
||||
//! for the coming items. As with `with_capacity`, the precise behavior of
|
||||
//! these methods will be specific to the collection of interest.
|
||||
//!
|
||||
//! For optimal performance, collections will generally avoid shrinking
|
||||
//! themselves. If you believe that a collection will not soon contain any more
|
||||
//! elements, or just really need the memory, the `shrink_to_fit` method prompts
|
||||
//! the collection to shrink the backing array to the minimum size capable of
|
||||
//! holding its elements.
|
||||
//!
|
||||
//! Finally, if ever you're interested in what the actual capacity of the
|
||||
//! collection is, most collections provide a `capacity` method to query this
|
||||
//! information on demand. This can be useful for debugging purposes, or for
|
||||
//! use with the `reserve` methods.
|
||||
//!
|
||||
//! ## Iterators
|
||||
//!
|
||||
//! [Iterators][crate::iter]
|
||||
//! are a powerful and robust mechanism used throughout Rust's
|
||||
//! standard libraries. Iterators provide a sequence of values in a generic,
|
||||
//! safe, efficient and convenient way. The contents of an iterator are usually
|
||||
//! *lazily* evaluated, so that only the values that are actually needed are
|
||||
//! ever actually produced, and no allocation need be done to temporarily store
|
||||
//! them. Iterators are primarily consumed using a `for` loop, although many
|
||||
//! functions also take iterators where a collection or sequence of values is
|
||||
//! desired.
|
||||
//!
|
||||
//! All of the standard collections provide several iterators for performing
|
||||
//! bulk manipulation of their contents. The three primary iterators almost
|
||||
//! every collection should provide are `iter`, `iter_mut`, and `into_iter`.
|
||||
//! Some of these are not provided on collections where it would be unsound or
|
||||
//! unreasonable to provide them.
|
||||
//!
|
||||
//! `iter` provides an iterator of immutable references to all the contents of a
|
||||
//! collection in the most "natural" order. For sequence collections like [`Vec`],
|
||||
//! this means the items will be yielded in increasing order of index starting
|
||||
//! at 0. For ordered collections like [`BTreeMap`], this means that the items
|
||||
//! will be yielded in sorted order. For unordered collections like [`HashMap`],
|
||||
//! the items will be yielded in whatever order the internal representation made
|
||||
//! most convenient. This is great for reading through all the contents of the
|
||||
//! collection.
|
||||
//!
|
||||
//! ```
|
||||
//! let vec = vec![1, 2, 3, 4];
|
||||
//! for x in vec.iter() {
|
||||
//! println!("vec contained {x:?}");
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! `iter_mut` provides an iterator of *mutable* references in the same order as
|
||||
//! `iter`. This is great for mutating all the contents of the collection.
|
||||
//!
|
||||
//! ```
|
||||
//! let mut vec = vec![1, 2, 3, 4];
|
||||
//! for x in vec.iter_mut() {
|
||||
//! *x += 1;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! `into_iter` transforms the actual collection into an iterator over its
|
||||
//! contents by-value. This is great when the collection itself is no longer
|
||||
//! needed, and the values are needed elsewhere. Using `extend` with `into_iter`
|
||||
//! is the main way that contents of one collection are moved into another.
|
||||
//! `extend` automatically calls `into_iter`, and takes any <code>T: [IntoIterator]</code>.
|
||||
//! Calling `collect` on an iterator itself is also a great way to convert one
|
||||
//! collection into another. Both of these methods should internally use the
|
||||
//! capacity management tools discussed in the previous section to do this as
|
||||
//! efficiently as possible.
|
||||
//!
|
||||
//! ```
|
||||
//! let mut vec1 = vec![1, 2, 3, 4];
|
||||
//! let vec2 = vec![10, 20, 30, 40];
|
||||
//! vec1.extend(vec2);
|
||||
//! ```
|
||||
//!
|
||||
//! ```
|
||||
//! use std::collections::VecDeque;
|
||||
//!
|
||||
//! let vec = [1, 2, 3, 4];
|
||||
//! let buf: VecDeque<_> = vec.into_iter().collect();
|
||||
//! ```
|
||||
//!
|
||||
//! Iterators also provide a series of *adapter* methods for performing common
|
||||
//! threads to sequences. Among the adapters are functional favorites like `map`,
|
||||
//! `fold`, `skip` and `take`. Of particular interest to collections is the
|
||||
//! `rev` adapter, which reverses any iterator that supports this operation. Most
|
||||
//! collections provide reversible iterators as the way to iterate over them in
|
||||
//! reverse order.
|
||||
//!
|
||||
//! ```
|
||||
//! let vec = vec![1, 2, 3, 4];
|
||||
//! for x in vec.iter().rev() {
|
||||
//! println!("vec contained {x:?}");
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Several other collection methods also return iterators to yield a sequence
|
||||
//! of results but avoid allocating an entire collection to store the result in.
|
||||
//! This provides maximum flexibility as
|
||||
//! [`collect`][crate::iter::Iterator::collect] or
|
||||
//! [`extend`][crate::iter::Extend::extend] can be called to
|
||||
//! "pipe" the sequence into any collection if desired. Otherwise, the sequence
|
||||
//! can be looped over with a `for` loop. The iterator can also be discarded
|
||||
//! after partial use, preventing the computation of the unused items.
|
||||
//!
|
||||
//! ## Entries
|
||||
//!
|
||||
//! The `entry` API is intended to provide an efficient mechanism for
|
||||
//! manipulating the contents of a map conditionally on the presence of a key or
|
||||
//! not. The primary motivating use case for this is to provide efficient
|
||||
//! accumulator maps. For instance, if one wishes to maintain a count of the
|
||||
//! number of times each key has been seen, they will have to perform some
|
||||
//! conditional logic on whether this is the first time the key has been seen or
|
||||
//! not. Normally, this would require a `find` followed by an `insert`,
|
||||
//! effectively duplicating the search effort on each insertion.
|
||||
//!
|
||||
//! When a user calls `map.entry(key)`, the map will search for the key and
|
||||
//! then yield a variant of the `Entry` enum.
|
||||
//!
|
||||
//! If a `Vacant(entry)` is yielded, then the key *was not* found. In this case
|
||||
//! the only valid operation is to `insert` a value into the entry. When this is
|
||||
//! done, the vacant entry is consumed and converted into a mutable reference to
|
||||
//! the value that was inserted. This allows for further manipulation of the
|
||||
//! value beyond the lifetime of the search itself. This is useful if complex
|
||||
//! logic needs to be performed on the value regardless of whether the value was
|
||||
//! just inserted.
|
||||
//!
|
||||
//! If an `Occupied(entry)` is yielded, then the key *was* found. In this case,
|
||||
//! the user has several options: they can `get`, `insert` or `remove` the
|
||||
//! value of the occupied entry. Additionally, they can convert the occupied
|
||||
//! entry into a mutable reference to its value, providing symmetry to the
|
||||
//! vacant `insert` case.
|
||||
//!
|
||||
//! ### Examples
|
||||
//!
|
||||
//! Here are the two primary ways in which `entry` is used. First, a simple
|
||||
//! example where the logic performed on the values is trivial.
|
||||
//!
|
||||
//! #### Counting the number of times each character in a string occurs
|
||||
//!
|
||||
//! ```
|
||||
//! use std::collections::btree_map::BTreeMap;
|
||||
//!
|
||||
//! let mut count = BTreeMap::new();
|
||||
//! let message = "she sells sea shells by the sea shore";
|
||||
//!
|
||||
//! for c in message.chars() {
|
||||
//! *count.entry(c).or_insert(0) += 1;
|
||||
//! }
|
||||
//!
|
||||
//! assert_eq!(count.get(&'s'), Some(&8));
|
||||
//!
|
||||
//! println!("Number of occurrences of each character");
|
||||
//! for (char, count) in &count {
|
||||
//! println!("{char}: {count}");
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! When the logic to be performed on the value is more complex, we may simply
|
||||
//! use the `entry` API to ensure that the value is initialized and perform the
|
||||
//! logic afterwards.
|
||||
//!
|
||||
//! #### Tracking the inebriation of customers at a bar
|
||||
//!
|
||||
//! ```
|
||||
//! use std::collections::btree_map::BTreeMap;
|
||||
//!
|
||||
//! // A client of the bar. They have a blood alcohol level.
|
||||
//! struct Person { blood_alcohol: f32 }
|
||||
//!
|
||||
//! // All the orders made to the bar, by client ID.
|
||||
//! let orders = vec![1, 2, 1, 2, 3, 4, 1, 2, 2, 3, 4, 1, 1, 1];
|
||||
//!
|
||||
//! // Our clients.
|
||||
//! let mut blood_alcohol = BTreeMap::new();
|
||||
//!
|
||||
//! for id in orders {
|
||||
//! // If this is the first time we've seen this customer, initialize them
|
||||
//! // with no blood alcohol. Otherwise, just retrieve them.
|
||||
//! let person = blood_alcohol.entry(id).or_insert(Person { blood_alcohol: 0.0 });
|
||||
//!
|
||||
//! // Reduce their blood alcohol level. It takes time to order and drink a beer!
|
||||
//! person.blood_alcohol *= 0.9;
|
||||
//!
|
||||
//! // Check if they're sober enough to have another beer.
|
||||
//! if person.blood_alcohol > 0.3 {
|
||||
//! // Too drunk... for now.
|
||||
//! println!("Sorry {id}, I have to cut you off");
|
||||
//! } else {
|
||||
//! // Have another!
|
||||
//! person.blood_alcohol += 0.1;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Insert and complex keys
|
||||
//!
|
||||
//! If we have a more complex key, calls to `insert` will
|
||||
//! not update the value of the key. For example:
|
||||
//!
|
||||
//! ```
|
||||
//! use std::cmp::Ordering;
|
||||
//! use std::collections::BTreeMap;
|
||||
//! use std::hash::{Hash, Hasher};
|
||||
//!
|
||||
//! #[derive(Debug)]
|
||||
//! struct Foo {
|
||||
//! a: u32,
|
||||
//! b: &'static str,
|
||||
//! }
|
||||
//!
|
||||
//! // we will compare `Foo`s by their `a` value only.
|
||||
//! impl PartialEq for Foo {
|
||||
//! fn eq(&self, other: &Self) -> bool { self.a == other.a }
|
||||
//! }
|
||||
//!
|
||||
//! impl Eq for Foo {}
|
||||
//!
|
||||
//! // we will hash `Foo`s by their `a` value only.
|
||||
//! impl Hash for Foo {
|
||||
//! fn hash<H: Hasher>(&self, h: &mut H) { self.a.hash(h); }
|
||||
//! }
|
||||
//!
|
||||
//! impl PartialOrd for Foo {
|
||||
//! fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.a.partial_cmp(&other.a) }
|
||||
//! }
|
||||
//!
|
||||
//! impl Ord for Foo {
|
||||
//! fn cmp(&self, other: &Self) -> Ordering { self.a.cmp(&other.a) }
|
||||
//! }
|
||||
//!
|
||||
//! let mut map = BTreeMap::new();
|
||||
//! map.insert(Foo { a: 1, b: "baz" }, 99);
|
||||
//!
|
||||
//! // We already have a Foo with an a of 1, so this will be updating the value.
|
||||
//! map.insert(Foo { a: 1, b: "xyz" }, 100);
|
||||
//!
|
||||
//! // The value has been updated...
|
||||
//! assert_eq!(map.values().next().unwrap(), &100);
|
||||
//!
|
||||
//! // ...but the key hasn't changed. b is still "baz", not "xyz".
|
||||
//! assert_eq!(map.keys().next().unwrap().b, "baz");
|
||||
//! ```
|
||||
|
||||
pub struct HashMap<K, V, T> {
|
||||
_phantom: PhantomData<(K, V, T)>,
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[stable(feature = "try_reserve", since = "1.57.0")]
|
||||
pub use alloc_crate::collections::TryReserveError;
|
||||
#[unstable(
|
||||
feature = "try_reserve_kind",
|
||||
reason = "Uncertain how much info should be exposed",
|
||||
issue = "48043"
|
||||
)]
|
||||
pub use alloc_crate::collections::TryReserveErrorKind;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use alloc_crate::collections::{BTreeMap, BTreeSet, BinaryHeap};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use alloc_crate::collections::{LinkedList, VecDeque};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use alloc_crate::collections::{binary_heap, btree_map, btree_set};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use alloc_crate::collections::{linked_list, vec_deque};
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(inline)]
|
||||
pub use self::hash_map::HashMap;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(inline)]
|
||||
pub use self::hash_set::HashSet;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
// FIXME(#82080) The deprecation here is only theoretical, and does not actually produce a warning.
|
||||
#[deprecated(note = "moved to `std::ops::Bound`", since = "1.26.0")]
|
||||
#[doc(hidden)]
|
||||
pub use crate::ops::Bound;
|
||||
|
||||
mod hash;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub mod hash_map {
|
||||
//! A hash map implemented with quadratic probing and SIMD lookup.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use super::hash::map::*;
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
pub use crate::hash::random::DefaultHasher;
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
pub use crate::hash::random::RandomState;
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub mod hash_set {
|
||||
//! A hash set implemented as a `HashMap` where the value is `()`.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use super::hash::set::*;
|
||||
}
|
||||
pub use alloc_crate::collections::BTreeMap;
|
||||
pub use alloc_crate::collections::btree_map;
|
||||
|
||||
@@ -6,7 +6,7 @@ mod tests;
|
||||
use core::clone::CloneToUninit;
|
||||
|
||||
use crate::borrow::{Borrow, Cow};
|
||||
use alloc_crate::collections::TryReserveError;
|
||||
use crate::collections::TryReserveError;
|
||||
use crate::hash::{Hash, Hasher};
|
||||
use crate::ops::{self, Range};
|
||||
use crate::rc::Rc;
|
||||
|
||||
2548
crates/std/src/fs/tests.rs
Normal file
2548
crates/std/src/fs/tests.rs
Normal file
File diff suppressed because it is too large
Load Diff
1087
crates/std/src/io/buffered/tests.rs
Normal file
1087
crates/std/src/io/buffered/tests.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,62 +1,353 @@
|
||||
//! Traits, helpers, and type definitions for core I/O functionality.
|
||||
//!
|
||||
//! The `std::io` module contains a number of common things you'll need
|
||||
//! when doing input and output. The most core part of this module is
|
||||
//! the [`Read`] and [`Write`] traits, which provide the
|
||||
//! most general interface for reading and writing input and output.
|
||||
//!
|
||||
//! ## Read and Write
|
||||
//!
|
||||
//! Because they are traits, [`Read`] and [`Write`] are implemented by a number
|
||||
//! of other types, and you can implement them for your types too. As such,
|
||||
//! you'll see a few different types of I/O throughout the documentation in
|
||||
//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec<T>`]s. For
|
||||
//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on
|
||||
//! [`File`]s:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let mut f = File::open("foo.txt")?;
|
||||
//! let mut buffer = [0; 10];
|
||||
//!
|
||||
//! // read up to 10 bytes
|
||||
//! let n = f.read(&mut buffer)?;
|
||||
//!
|
||||
//! println!("The bytes: {:?}", &buffer[..n]);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`Read`] and [`Write`] are so important, implementors of the two traits have a
|
||||
//! nickname: readers and writers. So you'll sometimes see 'a reader' instead
|
||||
//! of 'a type that implements the [`Read`] trait'. Much easier!
|
||||
//!
|
||||
//! ## Seek and BufRead
|
||||
//!
|
||||
//! Beyond that, there are two important traits that are provided: [`Seek`]
|
||||
//! and [`BufRead`]. Both of these build on top of a reader to control
|
||||
//! how the reading happens. [`Seek`] lets you control where the next byte is
|
||||
//! coming from:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io::SeekFrom;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let mut f = File::open("foo.txt")?;
|
||||
//! let mut buffer = [0; 10];
|
||||
//!
|
||||
//! // skip to the last 10 bytes of the file
|
||||
//! f.seek(SeekFrom::End(-10))?;
|
||||
//!
|
||||
//! // read up to 10 bytes
|
||||
//! let n = f.read(&mut buffer)?;
|
||||
//!
|
||||
//! println!("The bytes: {:?}", &buffer[..n]);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but
|
||||
//! to show it off, we'll need to talk about buffers in general. Keep reading!
|
||||
//!
|
||||
//! ## BufReader and BufWriter
|
||||
//!
|
||||
//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be
|
||||
//! making near-constant calls to the operating system. To help with this,
|
||||
//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap
|
||||
//! readers and writers. The wrapper uses a buffer, reducing the number of
|
||||
//! calls and providing nicer methods for accessing exactly what you want.
|
||||
//!
|
||||
//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra
|
||||
//! methods to any reader:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io::BufReader;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let f = File::open("foo.txt")?;
|
||||
//! let mut reader = BufReader::new(f);
|
||||
//! let mut buffer = String::new();
|
||||
//!
|
||||
//! // read a line into buffer
|
||||
//! reader.read_line(&mut buffer)?;
|
||||
//!
|
||||
//! println!("{buffer}");
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call
|
||||
//! to [`write`][`Write::write`]:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io::BufWriter;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let f = File::create("foo.txt")?;
|
||||
//! {
|
||||
//! let mut writer = BufWriter::new(f);
|
||||
//!
|
||||
//! // write a byte to the buffer
|
||||
//! writer.write(&[42])?;
|
||||
//!
|
||||
//! } // the buffer is flushed once writer goes out of scope
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Standard input and output
|
||||
//!
|
||||
//! A very common source of input is standard input:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let mut input = String::new();
|
||||
//!
|
||||
//! io::stdin().read_line(&mut input)?;
|
||||
//!
|
||||
//! println!("You typed: {}", input.trim());
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Note that you cannot use the [`?` operator] in functions that do not return
|
||||
//! a [`Result<T, E>`][`Result`]. Instead, you can call [`.unwrap()`]
|
||||
//! or `match` on the return value to catch any possible errors:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//!
|
||||
//! let mut input = String::new();
|
||||
//!
|
||||
//! io::stdin().read_line(&mut input).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! And a very common source of output is standard output:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! io::stdout().write(&[42])?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Of course, using [`io::stdout`] directly is less common than something like
|
||||
//! [`println!`].
|
||||
//!
|
||||
//! ## Iterator types
|
||||
//!
|
||||
//! A large number of the structures provided by `std::io` are for various
|
||||
//! ways of iterating over I/O. For example, [`Lines`] is used to split over
|
||||
//! lines:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io::BufReader;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let f = File::open("foo.txt")?;
|
||||
//! let reader = BufReader::new(f);
|
||||
//!
|
||||
//! for line in reader.lines() {
|
||||
//! println!("{}", line?);
|
||||
//! }
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Functions
|
||||
//!
|
||||
//! There are a number of [functions][functions-list] that offer access to various
|
||||
//! features. For example, we can use three of these functions to copy everything
|
||||
//! from standard input to standard output:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! io::copy(&mut io::stdin(), &mut io::stdout())?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [functions-list]: #functions-1
|
||||
//!
|
||||
//! ## io::Result
|
||||
//!
|
||||
//! Last, but certainly not least, is [`io::Result`]. This type is used
|
||||
//! as the return type of many `std::io` functions that can cause an error, and
|
||||
//! can be returned from your own functions as well. Many of the examples in this
|
||||
//! module use the [`?` operator]:
|
||||
//!
|
||||
//! ```
|
||||
//! use std::io;
|
||||
//!
|
||||
//! fn read_input() -> io::Result<()> {
|
||||
//! let mut input = String::new();
|
||||
//!
|
||||
//! io::stdin().read_line(&mut input)?;
|
||||
//!
|
||||
//! println!("You typed: {}", input.trim());
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The return type of `read_input()`, [`io::Result<()>`][`io::Result`], is a very
|
||||
//! common type for functions which don't have a 'real' return value, but do want to
|
||||
//! return errors if they happen. In this case, the only purpose of this function is
|
||||
//! to read the line and print it, so we use `()`.
|
||||
//!
|
||||
//! ## Platform-specific behavior
|
||||
//!
|
||||
//! Many I/O functions throughout the standard library are documented to indicate
|
||||
//! what various library or syscalls they are delegated to. This is done to help
|
||||
//! applications both understand what's happening under the hood as well as investigate
|
||||
//! any possibly unclear semantics. Note, however, that this is informative, not a binding
|
||||
//! contract. The implementation of many of these functions are subject to change over
|
||||
//! time and may call fewer or more syscalls/library functions.
|
||||
//!
|
||||
//! ## I/O Safety
|
||||
//!
|
||||
//! Rust follows an I/O safety discipline that is comparable to its memory safety discipline. This
|
||||
//! means that file descriptors can be *exclusively owned*. (Here, "file descriptor" is meant to
|
||||
//! subsume similar concepts that exist across a wide range of operating systems even if they might
|
||||
//! use a different name, such as "handle".) An exclusively owned file descriptor is one that no
|
||||
//! other code is allowed to access in any way, but the owner is allowed to access and even close
|
||||
//! it any time. A type that owns its file descriptor should usually close it in its `drop`
|
||||
//! function. Types like [`File`] own their file descriptor. Similarly, file descriptors
|
||||
//! can be *borrowed*, granting the temporary right to perform operations on this file descriptor.
|
||||
//! This indicates that the file descriptor will not be closed for the lifetime of the borrow, but
|
||||
//! it does *not* imply any right to close this file descriptor, since it will likely be owned by
|
||||
//! someone else.
|
||||
//!
|
||||
//! The platform-specific parts of the Rust standard library expose types that reflect these
|
||||
//! concepts, see [`os::unix`] and [`os::windows`].
|
||||
//!
|
||||
//! To uphold I/O safety, it is crucial that no code acts on file descriptors it does not own or
|
||||
//! borrow, and no code closes file descriptors it does not own. In other words, a safe function
|
||||
//! that takes a regular integer, treats it as a file descriptor, and acts on it, is *unsound*.
|
||||
//!
|
||||
//! Not upholding I/O safety and acting on a file descriptor without proof of ownership can lead to
|
||||
//! misbehavior and even Undefined Behavior in code that relies on ownership of its file
|
||||
//! descriptors: a closed file descriptor could be re-allocated, so the original owner of that file
|
||||
//! descriptor is now working on the wrong file. Some code might even rely on fully encapsulating
|
||||
//! its file descriptors with no operations being performed by any other part of the program.
|
||||
//!
|
||||
//! Note that exclusive ownership of a file descriptor does *not* imply exclusive ownership of the
|
||||
//! underlying kernel object that the file descriptor references (also called "open file description" on
|
||||
//! some operating systems). File descriptors basically work like [`Arc`]: when you receive an owned
|
||||
//! file descriptor, you cannot know whether there are any other file descriptors that reference the
|
||||
//! same kernel object. However, when you create a new kernel object, you know that you are holding
|
||||
//! the only reference to it. Just be careful not to lend it to anyone, since they can obtain a
|
||||
//! clone and then you can no longer know what the reference count is! In that sense, [`OwnedFd`] is
|
||||
//! like `Arc` and [`BorrowedFd<'a>`] is like `&'a Arc` (and similar for the Windows types). In
|
||||
//! particular, given a `BorrowedFd<'a>`, you are not allowed to close the file descriptor -- just
|
||||
//! like how, given a `&'a Arc`, you are not allowed to decrement the reference count and
|
||||
//! potentially free the underlying object. There is no equivalent to `Box` for file descriptors in
|
||||
//! the standard library (that would be a type that guarantees that the reference count is `1`),
|
||||
//! however, it would be possible for a crate to define a type with those semantics.
|
||||
//!
|
||||
//! [`File`]: crate::fs::File
|
||||
//! [`TcpStream`]: crate::net::TcpStream
|
||||
//! [`io::stdout`]: stdout
|
||||
//! [`io::Result`]: self::Result
|
||||
//! [`?` operator]: ../../book/appendix-02-operators.html
|
||||
//! [`Result`]: crate::result::Result
|
||||
//! [`.unwrap()`]: crate::result::Result::unwrap
|
||||
//! [`os::unix`]: ../os/unix/io/index.html
|
||||
//! [`os::windows`]: ../os/windows/io/index.html
|
||||
//! [`OwnedFd`]: ../os/fd/struct.OwnedFd.html
|
||||
//! [`BorrowedFd<'a>`]: ../os/fd/struct.BorrowedFd.html
|
||||
//! [`Arc`]: crate::sync::Arc
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub mod error;
|
||||
pub use self::buffered::{BufReader, BufWriter, IntoInnerError, LineWriter};
|
||||
pub use self::error::{Error, ErrorKind, Result, SimpleMessage, const_error};
|
||||
pub mod buffered;
|
||||
pub mod copy;
|
||||
pub mod cursor;
|
||||
pub mod impls;
|
||||
pub mod pipe;
|
||||
pub mod prelude;
|
||||
pub mod stdio;
|
||||
pub mod util;
|
||||
|
||||
use crate::mem::{MaybeUninit, take};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::{cmp, fmt, slice, str, sys};
|
||||
#[unstable(feature = "read_buf", issue = "78485")]
|
||||
pub use core::io::{BorrowedBuf, BorrowedCursor};
|
||||
use core::slice::memchr;
|
||||
pub use cursor::Cursor;
|
||||
|
||||
pub use self::copy::copy;
|
||||
pub use self::pipe::{PipeReader, PipeWriter};
|
||||
pub use self::stdio::cleanup;
|
||||
pub use self::stdio::{
|
||||
_print, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout,
|
||||
#[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;
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "io_const_error_internals", issue = "none")]
|
||||
pub use self::error::SimpleMessage;
|
||||
#[unstable(feature = "io_const_error", issue = "133448")]
|
||||
pub use self::error::const_error;
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
pub use self::pipe::{PipeReader, PipeWriter, pipe};
|
||||
#[stable(feature = "is_terminal", since = "1.70.0")]
|
||||
pub use self::stdio::IsTerminal;
|
||||
pub(crate) use self::stdio::attempt_print_to_stderr;
|
||||
#[unstable(feature = "print_internals", issue = "none")]
|
||||
#[doc(hidden)]
|
||||
pub use self::stdio::{_eprint, _print};
|
||||
#[unstable(feature = "internal_output_capture", issue = "none")]
|
||||
#[doc(no_inline, hidden)]
|
||||
pub use self::stdio::{set_output_capture, try_set_output_capture};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::{
|
||||
buffered::{BufReader, BufWriter, IntoInnerError, LineWriter},
|
||||
copy::copy,
|
||||
cursor::Cursor,
|
||||
error::{Error, ErrorKind, Result},
|
||||
stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout},
|
||||
util::{Empty, Repeat, Sink, empty, repeat, sink},
|
||||
};
|
||||
pub(crate) use stdio::attempt_print_to_stderr;
|
||||
pub use stdio::try_set_output_capture;
|
||||
use crate::mem::{MaybeUninit, take};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::{cmp, fmt, slice, str, sys};
|
||||
|
||||
use crate::fs::File;
|
||||
use io::IoBase;
|
||||
// pub use io::Read;
|
||||
// pub use io::Seek;
|
||||
// pub use io::SeekFrom;
|
||||
// pub use io::Write;
|
||||
|
||||
// pub struct Stdin;
|
||||
|
||||
impl IoBase for Stdin {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl io::Read for Stdin {
|
||||
fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
|
||||
unsafe { crate::other_fs::File::from_raw_fd(0).read(buf) }
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn stdin() -> Stdin {
|
||||
// Stdin
|
||||
// }
|
||||
|
||||
// Part took from the real std
|
||||
mod buffered;
|
||||
pub(crate) mod copy;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod impls;
|
||||
mod pipe;
|
||||
pub mod prelude;
|
||||
mod stdio;
|
||||
mod util;
|
||||
|
||||
const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
|
||||
|
||||
pub(crate) use stdio::cleanup;
|
||||
|
||||
struct Guard<'a> {
|
||||
buf: &'a mut Vec<u8>,
|
||||
len: usize,
|
||||
@@ -70,14 +361,30 @@ impl Drop for Guard<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// Several `read_to_string` and `read_line` methods in the standard library will
|
||||
// append data into a `String` buffer, but we need to be pretty careful when
|
||||
// doing this. The implementation will just call `.as_mut_vec()` and then
|
||||
// delegate to a byte-oriented reading method, but we must ensure that when
|
||||
// returning we never leave `buf` in a state such that it contains invalid UTF-8
|
||||
// in its bounds.
|
||||
//
|
||||
// To this end, we use an RAII guard (to protect against panics) which updates
|
||||
// the length of the string when it is dropped. This guard initially truncates
|
||||
// the string to the prior length and only after we've validated that the
|
||||
// new contents are valid UTF-8 do we allow it to set a longer length.
|
||||
//
|
||||
// The unsafety in this function is twofold:
|
||||
//
|
||||
// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8
|
||||
// checks.
|
||||
// 2. We're passing a raw buffer to the function `f`, and it is expected that
|
||||
// the function only *appends* bytes to the buffer. We'll get undefined
|
||||
// behavior if existing bytes are overwritten to have non-UTF-8 data.
|
||||
pub(crate) unsafe fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize>
|
||||
where
|
||||
F: FnOnce(&mut Vec<u8>) -> Result<usize>,
|
||||
{
|
||||
let mut g = Guard {
|
||||
len: buf.len(),
|
||||
buf: unsafe { buf.as_mut_vec() },
|
||||
};
|
||||
let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } };
|
||||
let ret = f(g.buf);
|
||||
|
||||
// SAFETY: the caller promises to only append data to `buf`
|
||||
@@ -109,10 +416,7 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
|
||||
// Optionally limit the maximum bytes read on each iteration.
|
||||
// This adds an arbitrary fiddle factor to allow for more data than we expect.
|
||||
let mut max_read_size = size_hint
|
||||
.and_then(|s| {
|
||||
s.checked_add(1024)?
|
||||
.checked_next_multiple_of(DEFAULT_BUF_SIZE)
|
||||
})
|
||||
.and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE))
|
||||
.unwrap_or(DEFAULT_BUF_SIZE);
|
||||
|
||||
let mut initialized = 0; // Extra initialized bytes from previous loop iteration
|
||||
@@ -253,10 +557,7 @@ pub(crate) fn default_read_vectored<F>(read: F, bufs: &mut [IoSliceMut<'_>]) ->
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<usize>,
|
||||
{
|
||||
let buf = bufs
|
||||
.iter_mut()
|
||||
.find(|b| !b.is_empty())
|
||||
.map_or(&mut [][..], |b| &mut **b);
|
||||
let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b);
|
||||
read(buf)
|
||||
}
|
||||
|
||||
@@ -264,10 +565,7 @@ pub(crate) fn default_write_vectored<F>(write: F, bufs: &[IoSlice<'_>]) -> Resul
|
||||
where
|
||||
F: FnOnce(&[u8]) -> Result<usize>,
|
||||
{
|
||||
let buf = bufs
|
||||
.iter()
|
||||
.find(|b| !b.is_empty())
|
||||
.map_or(&[][..], |b| &**b);
|
||||
let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b);
|
||||
write(buf)
|
||||
}
|
||||
|
||||
@@ -282,11 +580,7 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
if !buf.is_empty() {
|
||||
Err(Error::READ_EXACT_EOF)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
pub(crate) fn default_read_buf<F>(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()>
|
||||
@@ -341,10 +635,7 @@ pub(crate) fn default_write_fmt<W: Write + ?Sized>(
|
||||
}
|
||||
}
|
||||
|
||||
let mut output = Adapter {
|
||||
inner: this,
|
||||
error: Ok(()),
|
||||
};
|
||||
let mut output = Adapter { inner: this, error: Ok(()) };
|
||||
match fmt::write(&mut output, args) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(..) => {
|
||||
@@ -362,6 +653,79 @@ pub(crate) fn default_write_fmt<W: Write + ?Sized>(
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Read` trait allows for reading bytes from a source.
|
||||
///
|
||||
/// Implementors of the `Read` trait are called 'readers'.
|
||||
///
|
||||
/// Readers are defined by one required method, [`read()`]. Each call to [`read()`]
|
||||
/// will attempt to pull bytes from this source into a provided buffer. A
|
||||
/// number of other methods are implemented in terms of [`read()`], giving
|
||||
/// implementors a number of ways to read bytes while only needing to implement
|
||||
/// a single method.
|
||||
///
|
||||
/// Readers are intended to be composable with one another. Many implementors
|
||||
/// throughout [`std::io`] take and provide types which implement the `Read`
|
||||
/// trait.
|
||||
///
|
||||
/// Please note that each call to [`read()`] may involve a system call, and
|
||||
/// therefore, using something that implements [`BufRead`], such as
|
||||
/// [`BufReader`], will be more efficient.
|
||||
///
|
||||
/// Repeated calls to the reader use the same cursor, so for example
|
||||
/// calling `read_to_end` twice on a [`File`] will only return the file's
|
||||
/// contents once. It's recommended to first call `rewind()` in that case.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`]s implement `Read`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut f = File::open("foo.txt")?;
|
||||
/// let mut buffer = [0; 10];
|
||||
///
|
||||
/// // read up to 10 bytes
|
||||
/// f.read(&mut buffer)?;
|
||||
///
|
||||
/// let mut buffer = Vec::new();
|
||||
/// // read the whole file
|
||||
/// f.read_to_end(&mut buffer)?;
|
||||
///
|
||||
/// // read into a String, so that you don't need to do the conversion.
|
||||
/// let mut buffer = String::new();
|
||||
/// f.read_to_string(&mut buffer)?;
|
||||
///
|
||||
/// // and more! See the other methods for more details.
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use std::io;
|
||||
/// use std::io::prelude::*;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut b = "This string will be read".as_bytes();
|
||||
/// let mut buffer = [0; 10];
|
||||
///
|
||||
/// // read up to 10 bytes
|
||||
/// b.read(&mut buffer)?;
|
||||
///
|
||||
/// // etc... it works exactly as a File does!
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`read()`]: Read::read
|
||||
/// [`&str`]: prim@str
|
||||
/// [`std::io`]: self
|
||||
/// [`File`]: crate::fs::File
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(notable_trait)]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")]
|
||||
@@ -459,8 +823,7 @@ pub trait Read {
|
||||
/// buffer provided, or an empty one if none exists.
|
||||
#[stable(feature = "iovec", since = "1.36.0")]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
|
||||
// default_read_vectored(|b| self.read(b), bufs)
|
||||
todo!()
|
||||
default_read_vectored(|b| self.read(b), bufs)
|
||||
}
|
||||
|
||||
/// Determines if this `Read`er has an efficient `read_vectored`
|
||||
@@ -570,8 +933,7 @@ pub trait Read {
|
||||
/// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
// default_read_to_end(self, buf, None)
|
||||
todo!()
|
||||
default_read_to_end(self, buf, None)
|
||||
}
|
||||
|
||||
/// Reads all bytes until EOF in this source, appending them to `buf`.
|
||||
@@ -627,8 +989,7 @@ pub trait Read {
|
||||
/// [`std::fs::read_to_string`]: crate::fs::read_to_string
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
|
||||
// default_read_to_string(self, buf, None)
|
||||
todo!()
|
||||
default_read_to_string(self, buf, None)
|
||||
}
|
||||
|
||||
/// Reads the exact number of bytes required to fill `buf`.
|
||||
@@ -681,8 +1042,7 @@ pub trait Read {
|
||||
/// ```
|
||||
#[stable(feature = "read_exact", since = "1.6.0")]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
|
||||
// default_read_exact(self, buf)
|
||||
todo!()
|
||||
default_read_exact(self, buf)
|
||||
}
|
||||
|
||||
/// Pull some bytes from this source into the specified buffer.
|
||||
@@ -695,8 +1055,7 @@ pub trait Read {
|
||||
/// This method makes it possible to return both data and an error but it is advised against.
|
||||
#[unstable(feature = "read_buf", issue = "78485")]
|
||||
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
|
||||
// default_read_buf(|b| self.read(b), buf)
|
||||
todo!()
|
||||
default_read_buf(|b| self.read(b), buf)
|
||||
}
|
||||
|
||||
/// Reads the exact number of bytes required to fill `cursor`.
|
||||
@@ -719,8 +1078,7 @@ pub trait Read {
|
||||
/// If this function returns an error, all bytes read will be appended to `cursor`.
|
||||
#[unstable(feature = "read_buf", issue = "78485")]
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
|
||||
// default_read_buf_exact(self, cursor)
|
||||
todo!()
|
||||
default_read_buf_exact(self, cursor)
|
||||
}
|
||||
|
||||
/// Creates a "by reference" adapter for this instance of `Read`.
|
||||
@@ -843,11 +1201,7 @@ pub trait Read {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Chain {
|
||||
first: self,
|
||||
second: next,
|
||||
done_first: false,
|
||||
}
|
||||
Chain { first: self, second: next, done_first: false }
|
||||
}
|
||||
|
||||
/// Creates an adapter which will read at most `limit` bytes from it.
|
||||
@@ -886,11 +1240,7 @@ pub trait Read {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Take {
|
||||
inner: self,
|
||||
len: limit,
|
||||
limit,
|
||||
}
|
||||
Take { inner: self, len: limit, limit }
|
||||
}
|
||||
|
||||
/// Read and return a fixed array of bytes from this source.
|
||||
@@ -1944,6 +2294,55 @@ fn skip_until<R: BufRead + ?Sized>(r: &mut R, delim: u8) -> Result<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it
|
||||
/// to perform extra ways of reading.
|
||||
///
|
||||
/// For example, reading line-by-line is inefficient without using a buffer, so
|
||||
/// if you want to read by line, you'll need `BufRead`, which includes a
|
||||
/// [`read_line`] method as well as a [`lines`] iterator.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// A locked standard input implements `BufRead`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// use std::io::prelude::*;
|
||||
///
|
||||
/// let stdin = io::stdin();
|
||||
/// for line in stdin.lock().lines() {
|
||||
/// println!("{}", line?);
|
||||
/// }
|
||||
/// # std::io::Result::Ok(())
|
||||
/// ```
|
||||
///
|
||||
/// If you have something that implements [`Read`], you can use the [`BufReader`
|
||||
/// type][`BufReader`] to turn it into a `BufRead`.
|
||||
///
|
||||
/// For example, [`File`] implements [`Read`], but not `BufRead`.
|
||||
/// [`BufReader`] to the rescue!
|
||||
///
|
||||
/// [`File`]: crate::fs::File
|
||||
/// [`read_line`]: BufRead::read_line
|
||||
/// [`lines`]: BufRead::lines
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::{self, BufReader};
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let f = File::open("foo.txt")?;
|
||||
/// let f = BufReader::new(f);
|
||||
///
|
||||
/// for line in f.lines() {
|
||||
/// let line = line?;
|
||||
/// println!("{line}");
|
||||
/// }
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")]
|
||||
pub trait BufRead: Read {
|
||||
@@ -2267,10 +2666,7 @@ pub trait BufRead: Read {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Split {
|
||||
buf: self,
|
||||
delim: byte,
|
||||
}
|
||||
Split { buf: self, delim: byte }
|
||||
}
|
||||
|
||||
/// Returns an iterator over the lines of this reader.
|
||||
@@ -2310,6 +2706,7 @@ pub trait BufRead: Read {
|
||||
Lines { buf: self }
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapter to chain together two readers.
|
||||
///
|
||||
/// This struct is generally created by calling [`chain`] on a reader.
|
||||
@@ -2475,11 +2872,7 @@ impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
if !self.done_first {
|
||||
self.first.consume(amt)
|
||||
} else {
|
||||
self.second.consume(amt)
|
||||
}
|
||||
if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) }
|
||||
}
|
||||
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
@@ -2509,15 +2902,13 @@ impl<T, U> SizeHint for Chain<T, U> {
|
||||
|
||||
#[inline]
|
||||
fn upper_bound(&self) -> Option<usize> {
|
||||
match (
|
||||
SizeHint::upper_bound(&self.first),
|
||||
SizeHint::upper_bound(&self.second),
|
||||
) {
|
||||
match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) {
|
||||
(Some(first), Some(second)) => first.checked_add(second),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reader adapter which limits the bytes read from an underlying reader.
|
||||
///
|
||||
/// This struct is generally created by calling [`take`] on a reader.
|
||||
@@ -2800,11 +3191,7 @@ impl<T: Seek> Seek for Take<T> {
|
||||
self.limit = self.limit.wrapping_sub(offset as u64);
|
||||
break;
|
||||
}
|
||||
let offset = if new_position > self.position() {
|
||||
i64::MAX
|
||||
} else {
|
||||
i64::MIN
|
||||
};
|
||||
let offset = if new_position > self.position() { i64::MAX } else { i64::MIN };
|
||||
self.inner.seek_relative(offset)?;
|
||||
self.limit = self.limit.wrapping_sub(offset as u64);
|
||||
}
|
||||
@@ -2820,11 +3207,7 @@ impl<T: Seek> Seek for Take<T> {
|
||||
}
|
||||
|
||||
fn seek_relative(&mut self, offset: i64) -> Result<()> {
|
||||
if !self
|
||||
.position()
|
||||
.checked_add_signed(offset)
|
||||
.is_some_and(|p| p <= self.len)
|
||||
{
|
||||
if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) {
|
||||
return Err(ErrorKind::InvalidInput.into());
|
||||
}
|
||||
self.inner.seek_relative(offset)?;
|
||||
166
crates/std/src/io/stdio/tests.rs
Normal file
166
crates/std/src/io/stdio/tests.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
use super::*;
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::mpsc::sync_channel;
|
||||
use crate::thread;
|
||||
|
||||
#[test]
|
||||
fn stdout_unwind_safe() {
|
||||
assert_unwind_safe::<Stdout>();
|
||||
}
|
||||
#[test]
|
||||
fn stdoutlock_unwind_safe() {
|
||||
assert_unwind_safe::<StdoutLock<'_>>();
|
||||
assert_unwind_safe::<StdoutLock<'static>>();
|
||||
}
|
||||
#[test]
|
||||
fn stderr_unwind_safe() {
|
||||
assert_unwind_safe::<Stderr>();
|
||||
}
|
||||
#[test]
|
||||
fn stderrlock_unwind_safe() {
|
||||
assert_unwind_safe::<StderrLock<'_>>();
|
||||
assert_unwind_safe::<StderrLock<'static>>();
|
||||
}
|
||||
|
||||
fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn panic_doesnt_poison() {
|
||||
thread::spawn(|| {
|
||||
let _a = stdin();
|
||||
let _a = _a.lock();
|
||||
let _a = stdout();
|
||||
let _a = _a.lock();
|
||||
let _a = stderr();
|
||||
let _a = _a.lock();
|
||||
panic!();
|
||||
})
|
||||
.join()
|
||||
.unwrap_err();
|
||||
|
||||
let _a = stdin();
|
||||
let _a = _a.lock();
|
||||
let _a = stdout();
|
||||
let _a = _a.lock();
|
||||
let _a = stderr();
|
||||
let _a = _a.lock();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn test_lock_stderr() {
|
||||
test_lock(stderr, || stderr().lock());
|
||||
}
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn test_lock_stdin() {
|
||||
test_lock(stdin, || stdin().lock());
|
||||
}
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn test_lock_stdout() {
|
||||
test_lock(stdout, || stdout().lock());
|
||||
}
|
||||
|
||||
// Helper trait to make lock testing function generic.
|
||||
trait Stdio<'a>: 'static
|
||||
where
|
||||
Self::Lock: 'a,
|
||||
{
|
||||
type Lock;
|
||||
fn lock(&'a self) -> Self::Lock;
|
||||
}
|
||||
impl<'a> Stdio<'a> for Stderr {
|
||||
type Lock = StderrLock<'a>;
|
||||
fn lock(&'a self) -> StderrLock<'a> {
|
||||
self.lock()
|
||||
}
|
||||
}
|
||||
impl<'a> Stdio<'a> for Stdin {
|
||||
type Lock = StdinLock<'a>;
|
||||
fn lock(&'a self) -> StdinLock<'a> {
|
||||
self.lock()
|
||||
}
|
||||
}
|
||||
impl<'a> Stdio<'a> for Stdout {
|
||||
type Lock = StdoutLock<'a>;
|
||||
fn lock(&'a self) -> StdoutLock<'a> {
|
||||
self.lock()
|
||||
}
|
||||
}
|
||||
|
||||
// Helper trait to make lock testing function generic.
|
||||
trait StdioOwnedLock: 'static {}
|
||||
impl StdioOwnedLock for StderrLock<'static> {}
|
||||
impl StdioOwnedLock for StdinLock<'static> {}
|
||||
impl StdioOwnedLock for StdoutLock<'static> {}
|
||||
|
||||
// Tests locking on stdio handles by starting two threads and checking that
|
||||
// they block each other appropriately.
|
||||
fn test_lock<T, U>(get_handle: fn() -> T, get_locked: fn() -> U)
|
||||
where
|
||||
T: for<'a> Stdio<'a>,
|
||||
U: StdioOwnedLock,
|
||||
{
|
||||
// State enum to track different phases of the test, primarily when
|
||||
// each lock is acquired and released.
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum State {
|
||||
Start1,
|
||||
Acquire1,
|
||||
Start2,
|
||||
Release1,
|
||||
Acquire2,
|
||||
Release2,
|
||||
}
|
||||
use State::*;
|
||||
// Logging vector to be checked to make sure lock acquisitions and
|
||||
// releases happened in the correct order.
|
||||
let log = Arc::new(Mutex::new(Vec::new()));
|
||||
let ((tx1, rx1), (tx2, rx2)) = (sync_channel(0), sync_channel(0));
|
||||
let th1 = {
|
||||
let (log, tx) = (Arc::clone(&log), tx1);
|
||||
thread::spawn(move || {
|
||||
log.lock().unwrap().push(Start1);
|
||||
let handle = get_handle();
|
||||
{
|
||||
let locked = handle.lock();
|
||||
log.lock().unwrap().push(Acquire1);
|
||||
tx.send(Acquire1).unwrap(); // notify of acquisition
|
||||
tx.send(Release1).unwrap(); // wait for release command
|
||||
log.lock().unwrap().push(Release1);
|
||||
}
|
||||
tx.send(Acquire1).unwrap(); // wait for th2 acquire
|
||||
{
|
||||
let locked = handle.lock();
|
||||
log.lock().unwrap().push(Acquire1);
|
||||
}
|
||||
log.lock().unwrap().push(Release1);
|
||||
})
|
||||
};
|
||||
let th2 = {
|
||||
let (log, tx) = (Arc::clone(&log), tx2);
|
||||
thread::spawn(move || {
|
||||
tx.send(Start2).unwrap(); // wait for start command
|
||||
let locked = get_locked();
|
||||
log.lock().unwrap().push(Acquire2);
|
||||
tx.send(Acquire2).unwrap(); // notify of acquisition
|
||||
tx.send(Release2).unwrap(); // wait for release command
|
||||
log.lock().unwrap().push(Release2);
|
||||
})
|
||||
};
|
||||
assert_eq!(rx1.recv().unwrap(), Acquire1); // wait for th1 acquire
|
||||
log.lock().unwrap().push(Start2);
|
||||
assert_eq!(rx2.recv().unwrap(), Start2); // block th2
|
||||
assert_eq!(rx1.recv().unwrap(), Release1); // release th1
|
||||
assert_eq!(rx2.recv().unwrap(), Acquire2); // wait for th2 acquire
|
||||
assert_eq!(rx1.recv().unwrap(), Acquire1); // block th1
|
||||
assert_eq!(rx2.recv().unwrap(), Release2); // release th2
|
||||
th2.join().unwrap();
|
||||
th1.join().unwrap();
|
||||
assert_eq!(
|
||||
*log.lock().unwrap(),
|
||||
[Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1]
|
||||
);
|
||||
}
|
||||
@@ -262,11 +262,14 @@ pub use alloc_crate::string;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use alloc_crate::vec;
|
||||
|
||||
use io_crate::IoBase;
|
||||
pub use std_detect::is_x86_feature_detected;
|
||||
|
||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||
pub use core::{
|
||||
assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args,
|
||||
format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env,
|
||||
stringify, trace_macros, unimplemented
|
||||
stringify, trace_macros, unimplemented,
|
||||
};
|
||||
|
||||
#[rustc_std_internal_symbol]
|
||||
@@ -274,10 +277,10 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn core::panic::PanicPayload) -
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub mod error;
|
||||
pub mod ffi;
|
||||
pub mod hash;
|
||||
pub mod io;
|
||||
pub mod error;
|
||||
pub mod num;
|
||||
pub mod path;
|
||||
pub mod prelude;
|
||||
@@ -293,6 +296,7 @@ pub mod env;
|
||||
pub mod fs;
|
||||
pub mod keyword_docs;
|
||||
pub mod macros;
|
||||
pub mod net;
|
||||
pub mod os;
|
||||
pub mod panic;
|
||||
pub mod panicking;
|
||||
@@ -321,6 +325,8 @@ mod sealed {
|
||||
pub use shared::fs as other_fs;
|
||||
pub use shared::syscall;
|
||||
|
||||
use crate::io::Stdin;
|
||||
|
||||
// #[macro_export]
|
||||
// macro_rules! print {
|
||||
// ($($args:expr),*) => {
|
||||
@@ -338,3 +344,27 @@ pub use shared::syscall;
|
||||
// // $crate::println!();
|
||||
// };
|
||||
// }
|
||||
|
||||
impl IoBase for Stdin {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl io_crate::Read for Stdin {
|
||||
fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
|
||||
unsafe { crate::other_fs::File::from_raw_fd(0).read(buf) }
|
||||
}
|
||||
}
|
||||
#[global_allocator]
|
||||
static GLOBAL_ALLOCATOR: crate::alloc::System = crate::alloc::System;
|
||||
|
||||
/// # Safety
|
||||
/// `argc` and `argv` are passed by the kernel
|
||||
#[unsafe(no_mangle)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize {
|
||||
unsafe extern "C" {
|
||||
fn main(argc: isize, argv: *const *const u8) -> isize;
|
||||
}
|
||||
|
||||
unsafe { main(argc, argv) }
|
||||
}
|
||||
|
||||
22
crates/std/src/net/hostname.rs
Normal file
22
crates/std/src/net/hostname.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use crate::ffi::OsString;
|
||||
|
||||
/// Returns the system hostname.
|
||||
///
|
||||
/// This can error out in platform-specific error cases;
|
||||
/// for example, uefi and wasm, where hostnames aren't
|
||||
/// supported.
|
||||
///
|
||||
/// # Underlying system calls
|
||||
///
|
||||
/// | Platform | System call |
|
||||
/// |--------------|---------------------------------------------------------------------------------------------------------|
|
||||
/// | UNIX | [`gethostname`](https://www.man7.org/linux/man-pages/man2/gethostname.2.html) |
|
||||
/// | Windows (8+) | [`GetHostNameW`](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew) |
|
||||
///
|
||||
/// Note that platform-specific behavior [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: crate::io#platform-specific-behavior
|
||||
#[unstable(feature = "gethostname", issue = "135142")]
|
||||
pub fn hostname() -> crate::io::Result<OsString> {
|
||||
crate::sys::net::hostname()
|
||||
}
|
||||
10
crates/std/src/net/ip_addr.rs
Normal file
10
crates/std/src/net/ip_addr.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
// Tests for this module
|
||||
#[cfg(all(test, not(any(target_os = "emscripten", all(target_os = "wasi", target_env = "p1")))))]
|
||||
mod tests;
|
||||
|
||||
#[stable(feature = "ip_addr", since = "1.7.0")]
|
||||
pub use core::net::IpAddr;
|
||||
#[unstable(feature = "ip", issue = "27709")]
|
||||
pub use core::net::Ipv6MulticastScope;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::net::{Ipv4Addr, Ipv6Addr};
|
||||
8
crates/std/src/net/ip_addr/tests.rs
Normal file
8
crates/std/src/net/ip_addr/tests.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use crate::net::Ipv4Addr;
|
||||
use crate::net::test::{sa4, tsa};
|
||||
|
||||
#[test]
|
||||
fn to_socket_addr_socketaddr() {
|
||||
let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 12345);
|
||||
assert_eq!(Ok(vec![a]), tsa(a));
|
||||
}
|
||||
72
crates/std/src/net/mod.rs
Normal file
72
crates/std/src/net/mod.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
//! Networking primitives for TCP/UDP communication.
|
||||
//!
|
||||
//! This module provides networking functionality for the Transmission Control and User
|
||||
//! Datagram Protocols, as well as types for IP and socket addresses and functions related
|
||||
//! to network properties.
|
||||
//!
|
||||
//! # Organization
|
||||
//!
|
||||
//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP
|
||||
//! * [`UdpSocket`] provides functionality for communication over UDP
|
||||
//! * [`IpAddr`] represents IP addresses of either IPv4 or IPv6; [`Ipv4Addr`] and
|
||||
//! [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses
|
||||
//! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`]
|
||||
//! and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses
|
||||
//! * [`ToSocketAddrs`] is a trait that is used for generic address resolution when interacting
|
||||
//! with networking objects like [`TcpListener`], [`TcpStream`] or [`UdpSocket`]
|
||||
//! * Other types are return or parameter types for various methods in this module
|
||||
//!
|
||||
//! Rust disables inheritance of socket objects to child processes by default when possible. For
|
||||
//! example, through the use of the `CLOEXEC` flag in UNIX systems or the `HANDLE_FLAG_INHERIT`
|
||||
//! flag on Windows.
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::net::AddrParseError;
|
||||
|
||||
#[unstable(feature = "gethostname", issue = "135142")]
|
||||
pub use self::hostname::hostname;
|
||||
#[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::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
||||
#[unstable(feature = "tcplistener_into_incoming", issue = "88373")]
|
||||
pub use self::tcp::IntoIncoming;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::tcp::{Incoming, TcpListener, TcpStream};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::udp::UdpSocket;
|
||||
|
||||
mod hostname;
|
||||
mod ip_addr;
|
||||
mod socket_addr;
|
||||
mod tcp;
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test;
|
||||
mod udp;
|
||||
|
||||
/// Possible values which can be passed to the [`TcpStream::shutdown`] method.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub enum Shutdown {
|
||||
/// The reading portion of the [`TcpStream`] should be shut down.
|
||||
///
|
||||
/// All currently blocked and future [reads] will return <code>[Ok]\(0)</code>.
|
||||
///
|
||||
/// [reads]: crate::io::Read "io::Read"
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
Read,
|
||||
/// The writing portion of the [`TcpStream`] should be shut down.
|
||||
///
|
||||
/// All currently blocked and future [writes] will return an error.
|
||||
///
|
||||
/// [writes]: crate::io::Write "io::Write"
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
Write,
|
||||
/// Both the reading and the writing portions of the [`TcpStream`] should be shut down.
|
||||
///
|
||||
/// See [`Shutdown::Read`] and [`Shutdown::Write`] for more information.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
Both,
|
||||
}
|
||||
268
crates/std/src/net/socket_addr.rs
Normal file
268
crates/std/src/net/socket_addr.rs
Normal file
@@ -0,0 +1,268 @@
|
||||
// Tests for this module
|
||||
#[cfg(all(test, not(any(target_os = "emscripten", all(target_os = "wasi", target_env = "p1")))))]
|
||||
mod tests;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
|
||||
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use crate::{io, iter, option, slice, vec};
|
||||
|
||||
/// A trait for objects which can be converted or resolved to one or more
|
||||
/// [`SocketAddr`] values.
|
||||
///
|
||||
/// This trait is used for generic address resolution when constructing network
|
||||
/// objects. By default it is implemented for the following types:
|
||||
///
|
||||
/// * [`SocketAddr`]: [`to_socket_addrs`] is the identity function.
|
||||
///
|
||||
/// * [`SocketAddrV4`], [`SocketAddrV6`], <code>([IpAddr], [u16])</code>,
|
||||
/// <code>([Ipv4Addr], [u16])</code>, <code>([Ipv6Addr], [u16])</code>:
|
||||
/// [`to_socket_addrs`] constructs a [`SocketAddr`] trivially.
|
||||
///
|
||||
/// * <code>(&[str], [u16])</code>: <code>&[str]</code> should be either a string representation
|
||||
/// of an [`IpAddr`] address as expected by [`FromStr`] implementation or a host
|
||||
/// name. [`u16`] is the port number.
|
||||
///
|
||||
/// * <code>&[str]</code>: the string should be either a string representation of a
|
||||
/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like
|
||||
/// `<host_name>:<port>` pair where `<port>` is a [`u16`] value.
|
||||
///
|
||||
/// * <code>&[[SocketAddr]]</code>: all [`SocketAddr`] values in the slice will be used.
|
||||
///
|
||||
/// This trait allows constructing network objects like [`TcpStream`] or
|
||||
/// [`UdpSocket`] easily with values of various types for the bind/connection
|
||||
/// address. It is needed because sometimes one type is more appropriate than
|
||||
/// the other: for simple uses a string like `"localhost:12345"` is much nicer
|
||||
/// than manual construction of the corresponding [`SocketAddr`], but sometimes
|
||||
/// [`SocketAddr`] value is *the* main source of the address, and converting it to
|
||||
/// some other type (e.g., a string) just for it to be converted back to
|
||||
/// [`SocketAddr`] in constructor methods is pointless.
|
||||
///
|
||||
/// Addresses returned by the operating system that are not IP addresses are
|
||||
/// silently ignored.
|
||||
///
|
||||
/// [`FromStr`]: crate::str::FromStr "std::str::FromStr"
|
||||
/// [`TcpStream`]: crate::net::TcpStream "net::TcpStream"
|
||||
/// [`to_socket_addrs`]: ToSocketAddrs::to_socket_addrs
|
||||
/// [`UdpSocket`]: crate::net::UdpSocket "net::UdpSocket"
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a [`SocketAddr`] iterator that yields one item:
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::{ToSocketAddrs, SocketAddr};
|
||||
///
|
||||
/// let addr = SocketAddr::from(([127, 0, 0, 1], 443));
|
||||
/// let mut addrs_iter = addr.to_socket_addrs().unwrap();
|
||||
///
|
||||
/// assert_eq!(Some(addr), addrs_iter.next());
|
||||
/// assert!(addrs_iter.next().is_none());
|
||||
/// ```
|
||||
///
|
||||
/// Creating a [`SocketAddr`] iterator from a hostname:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::{SocketAddr, ToSocketAddrs};
|
||||
///
|
||||
/// // assuming 'localhost' resolves to 127.0.0.1
|
||||
/// let mut addrs_iter = "localhost:443".to_socket_addrs().unwrap();
|
||||
/// assert_eq!(addrs_iter.next(), Some(SocketAddr::from(([127, 0, 0, 1], 443))));
|
||||
/// assert!(addrs_iter.next().is_none());
|
||||
///
|
||||
/// // assuming 'foo' does not resolve
|
||||
/// assert!("foo:443".to_socket_addrs().is_err());
|
||||
/// ```
|
||||
///
|
||||
/// Creating a [`SocketAddr`] iterator that yields multiple items:
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::{SocketAddr, ToSocketAddrs};
|
||||
///
|
||||
/// let addr1 = SocketAddr::from(([0, 0, 0, 0], 80));
|
||||
/// let addr2 = SocketAddr::from(([127, 0, 0, 1], 443));
|
||||
/// let addrs = vec![addr1, addr2];
|
||||
///
|
||||
/// let mut addrs_iter = (&addrs[..]).to_socket_addrs().unwrap();
|
||||
///
|
||||
/// assert_eq!(Some(addr1), addrs_iter.next());
|
||||
/// assert_eq!(Some(addr2), addrs_iter.next());
|
||||
/// assert!(addrs_iter.next().is_none());
|
||||
/// ```
|
||||
///
|
||||
/// Attempting to create a [`SocketAddr`] iterator from an improperly formatted
|
||||
/// socket address `&str` (missing the port):
|
||||
///
|
||||
/// ```
|
||||
/// use std::io;
|
||||
/// use std::net::ToSocketAddrs;
|
||||
///
|
||||
/// let err = "127.0.0.1".to_socket_addrs().unwrap_err();
|
||||
/// assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
|
||||
/// ```
|
||||
///
|
||||
/// [`TcpStream::connect`] is an example of a function that utilizes
|
||||
/// `ToSocketAddrs` as a trait bound on its parameter in order to accept
|
||||
/// different types:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::{TcpStream, Ipv4Addr};
|
||||
///
|
||||
/// let stream = TcpStream::connect(("127.0.0.1", 443));
|
||||
/// // or
|
||||
/// let stream = TcpStream::connect("127.0.0.1:443");
|
||||
/// // or
|
||||
/// let stream = TcpStream::connect((Ipv4Addr::new(127, 0, 0, 1), 443));
|
||||
/// ```
|
||||
///
|
||||
/// [`TcpStream::connect`]: crate::net::TcpStream::connect
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait ToSocketAddrs {
|
||||
/// Returned iterator over socket addresses which this type may correspond
|
||||
/// to.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
type Iter: Iterator<Item = SocketAddr>;
|
||||
|
||||
/// Converts this object to an iterator of resolved [`SocketAddr`]s.
|
||||
///
|
||||
/// The returned iterator might not actually yield any values depending on the
|
||||
/// outcome of any resolution performed.
|
||||
///
|
||||
/// Note that this function may block the current thread while resolution is
|
||||
/// performed.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn to_socket_addrs(&self) -> io::Result<Self::Iter>;
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ToSocketAddrs for SocketAddr {
|
||||
type Iter = option::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
|
||||
Ok(Some(*self).into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ToSocketAddrs for SocketAddrV4 {
|
||||
type Iter = option::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
|
||||
SocketAddr::V4(*self).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ToSocketAddrs for SocketAddrV6 {
|
||||
type Iter = option::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
|
||||
SocketAddr::V6(*self).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ToSocketAddrs for (IpAddr, u16) {
|
||||
type Iter = option::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
|
||||
let (ip, port) = *self;
|
||||
match ip {
|
||||
IpAddr::V4(ref a) => (*a, port).to_socket_addrs(),
|
||||
IpAddr::V6(ref a) => (*a, port).to_socket_addrs(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ToSocketAddrs for (Ipv4Addr, u16) {
|
||||
type Iter = option::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
|
||||
let (ip, port) = *self;
|
||||
SocketAddrV4::new(ip, port).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ToSocketAddrs for (Ipv6Addr, u16) {
|
||||
type Iter = option::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
|
||||
let (ip, port) = *self;
|
||||
SocketAddrV6::new(ip, port, 0, 0).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_host(host: &str, port: u16) -> io::Result<vec::IntoIter<SocketAddr>> {
|
||||
let addrs = crate::sys::net::lookup_host(host, port)?;
|
||||
Ok(Vec::from_iter(addrs).into_iter())
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ToSocketAddrs for (&str, u16) {
|
||||
type Iter = vec::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
|
||||
let (host, port) = *self;
|
||||
|
||||
// Try to parse the host as a regular IP address first
|
||||
if let Ok(addr) = host.parse::<IpAddr>() {
|
||||
let addr = SocketAddr::new(addr, port);
|
||||
return Ok(vec![addr].into_iter());
|
||||
}
|
||||
|
||||
// Otherwise, make the system look it up.
|
||||
lookup_host(host, port)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "string_u16_to_socket_addrs", since = "1.46.0")]
|
||||
impl ToSocketAddrs for (String, u16) {
|
||||
type Iter = vec::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
|
||||
(&*self.0, self.1).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
// accepts strings like 'localhost:12345'
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl ToSocketAddrs for str {
|
||||
type Iter = vec::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
|
||||
// Try to parse as a regular SocketAddr first
|
||||
if let Ok(addr) = self.parse() {
|
||||
return Ok(vec![addr].into_iter());
|
||||
}
|
||||
|
||||
// Otherwise, split the string by ':' and convert the second part to u16...
|
||||
let Some((host, port_str)) = self.rsplit_once(':') else {
|
||||
return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid socket address"));
|
||||
};
|
||||
let Ok(port) = port_str.parse::<u16>() else {
|
||||
return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid port value"));
|
||||
};
|
||||
|
||||
// ... and make the system look up the host.
|
||||
lookup_host(host, port)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "slice_to_socket_addrs", since = "1.8.0")]
|
||||
impl<'a> ToSocketAddrs for &'a [SocketAddr] {
|
||||
type Iter = iter::Cloned<slice::Iter<'a, SocketAddr>>;
|
||||
|
||||
fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
|
||||
Ok(self.iter().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T: ToSocketAddrs + ?Sized> ToSocketAddrs for &T {
|
||||
type Iter = T::Iter;
|
||||
fn to_socket_addrs(&self) -> io::Result<T::Iter> {
|
||||
(**self).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "string_to_socket_addrs", since = "1.16.0")]
|
||||
impl ToSocketAddrs for String {
|
||||
type Iter = vec::IntoIter<SocketAddr>;
|
||||
fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
|
||||
(&**self).to_socket_addrs()
|
||||
}
|
||||
}
|
||||
306
crates/std/src/net/socket_addr/tests.rs
Normal file
306
crates/std/src/net/socket_addr/tests.rs
Normal file
@@ -0,0 +1,306 @@
|
||||
use crate::net::test::{sa4, sa6, tsa};
|
||||
use crate::net::*;
|
||||
|
||||
#[test]
|
||||
fn to_socket_addr_ipaddr_u16() {
|
||||
let a = Ipv4Addr::new(77, 88, 21, 11);
|
||||
let p = 12345;
|
||||
let e = SocketAddr::V4(SocketAddrV4::new(a, p));
|
||||
assert_eq!(Ok(vec![e]), tsa((a, p)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_socket_addr_str_u16() {
|
||||
let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
|
||||
assert_eq!(Ok(vec![a]), tsa(("77.88.21.11", 24352)));
|
||||
|
||||
let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
|
||||
assert_eq!(Ok(vec![a]), tsa(("2a02:6b8:0:1::1", 53)));
|
||||
|
||||
let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924);
|
||||
#[cfg(not(target_env = "sgx"))]
|
||||
assert!(tsa(("localhost", 23924)).unwrap().contains(&a));
|
||||
#[cfg(target_env = "sgx")]
|
||||
let _ = a;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_socket_addr_str() {
|
||||
let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
|
||||
assert_eq!(Ok(vec![a]), tsa("77.88.21.11:24352"));
|
||||
|
||||
let a = sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53);
|
||||
assert_eq!(Ok(vec![a]), tsa("[2a02:6b8:0:1::1]:53"));
|
||||
|
||||
let a = sa4(Ipv4Addr::new(127, 0, 0, 1), 23924);
|
||||
#[cfg(not(target_env = "sgx"))]
|
||||
assert!(tsa("localhost:23924").unwrap().contains(&a));
|
||||
#[cfg(target_env = "sgx")]
|
||||
let _ = a;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_socket_addr_string() {
|
||||
let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 24352);
|
||||
assert_eq!(Ok(vec![a]), tsa(&*format!("{}:{}", "77.88.21.11", "24352")));
|
||||
assert_eq!(Ok(vec![a]), tsa(&format!("{}:{}", "77.88.21.11", "24352")));
|
||||
assert_eq!(Ok(vec![a]), tsa(format!("{}:{}", "77.88.21.11", "24352")));
|
||||
|
||||
let s = format!("{}:{}", "77.88.21.11", "24352");
|
||||
assert_eq!(Ok(vec![a]), tsa(s));
|
||||
// s has been moved into the tsa call
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv4_socket_addr_to_string() {
|
||||
// Shortest possible IPv4 length.
|
||||
assert_eq!(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0).to_string(), "0.0.0.0:0");
|
||||
|
||||
// Longest possible IPv4 length.
|
||||
assert_eq!(
|
||||
SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), u16::MAX).to_string(),
|
||||
"255.255.255.255:65535"
|
||||
);
|
||||
|
||||
// Test padding.
|
||||
assert_eq!(
|
||||
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)),
|
||||
" 1.1.1.1:53"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv6_socket_addr_to_string() {
|
||||
// IPv4-mapped address.
|
||||
assert_eq!(
|
||||
SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280), 8080, 0, 0)
|
||||
.to_string(),
|
||||
"[::ffff:192.0.2.128]:8080"
|
||||
);
|
||||
|
||||
// IPv4-compatible address.
|
||||
assert_eq!(
|
||||
SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(),
|
||||
"[::c000:280]:8080"
|
||||
);
|
||||
|
||||
// IPv6 address with no zero segments.
|
||||
assert_eq!(
|
||||
SocketAddrV6::new(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15), 80, 0, 0).to_string(),
|
||||
"[8:9:a:b:c:d:e:f]:80"
|
||||
);
|
||||
|
||||
// Shortest possible IPv6 length.
|
||||
assert_eq!(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0).to_string(), "[::]:0");
|
||||
|
||||
// Longest possible IPv6 length.
|
||||
assert_eq!(
|
||||
SocketAddrV6::new(
|
||||
Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888),
|
||||
u16::MAX,
|
||||
u32::MAX,
|
||||
u32::MAX,
|
||||
)
|
||||
.to_string(),
|
||||
"[1111:2222:3333:4444:5555:6666:7777:8888%4294967295]:65535"
|
||||
);
|
||||
|
||||
// Test padding.
|
||||
assert_eq!(
|
||||
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)),
|
||||
" [1:2:3:4:5:6:7:8]:9"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bind_udp_socket_bad() {
|
||||
// rust-lang/rust#53957: This is a regression test for a parsing problem
|
||||
// discovered as part of issue rust-lang/rust#23076, where we were
|
||||
// incorrectly parsing invalid input and then that would result in a
|
||||
// successful `UdpSocket` binding when we would expect failure.
|
||||
//
|
||||
// At one time, this test was written as a call to `tsa` with
|
||||
// INPUT_23076. However, that structure yields an unreliable test,
|
||||
// because it ends up passing junk input to the DNS server, and some DNS
|
||||
// servers will respond with `Ok` to such input, with the ip address of
|
||||
// the DNS server itself.
|
||||
//
|
||||
// This form of the test is more robust: even when the DNS server
|
||||
// returns its own address, it is still an error to bind a UDP socket to
|
||||
// a non-local address, and so we still get an error here in that case.
|
||||
|
||||
const INPUT_23076: &str = "1200::AB00:1234::2552:7777:1313:34300";
|
||||
|
||||
assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_ip() {
|
||||
fn ip4(low: u8) -> Ipv4Addr {
|
||||
Ipv4Addr::new(77, 88, 21, low)
|
||||
}
|
||||
fn ip6(low: u16) -> Ipv6Addr {
|
||||
Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, low)
|
||||
}
|
||||
|
||||
let mut v4 = SocketAddrV4::new(ip4(11), 80);
|
||||
assert_eq!(v4.ip(), &ip4(11));
|
||||
v4.set_ip(ip4(12));
|
||||
assert_eq!(v4.ip(), &ip4(12));
|
||||
|
||||
let mut addr = SocketAddr::V4(v4);
|
||||
assert_eq!(addr.ip(), IpAddr::V4(ip4(12)));
|
||||
addr.set_ip(IpAddr::V4(ip4(13)));
|
||||
assert_eq!(addr.ip(), IpAddr::V4(ip4(13)));
|
||||
addr.set_ip(IpAddr::V6(ip6(14)));
|
||||
assert_eq!(addr.ip(), IpAddr::V6(ip6(14)));
|
||||
|
||||
let mut v6 = SocketAddrV6::new(ip6(1), 80, 0, 0);
|
||||
assert_eq!(v6.ip(), &ip6(1));
|
||||
v6.set_ip(ip6(2));
|
||||
assert_eq!(v6.ip(), &ip6(2));
|
||||
|
||||
let mut addr = SocketAddr::V6(v6);
|
||||
assert_eq!(addr.ip(), IpAddr::V6(ip6(2)));
|
||||
addr.set_ip(IpAddr::V6(ip6(3)));
|
||||
assert_eq!(addr.ip(), IpAddr::V6(ip6(3)));
|
||||
addr.set_ip(IpAddr::V4(ip4(4)));
|
||||
assert_eq!(addr.ip(), IpAddr::V4(ip4(4)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_port() {
|
||||
let mut v4 = SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80);
|
||||
assert_eq!(v4.port(), 80);
|
||||
v4.set_port(443);
|
||||
assert_eq!(v4.port(), 443);
|
||||
|
||||
let mut addr = SocketAddr::V4(v4);
|
||||
assert_eq!(addr.port(), 443);
|
||||
addr.set_port(8080);
|
||||
assert_eq!(addr.port(), 8080);
|
||||
|
||||
let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 0);
|
||||
assert_eq!(v6.port(), 80);
|
||||
v6.set_port(443);
|
||||
assert_eq!(v6.port(), 443);
|
||||
|
||||
let mut addr = SocketAddr::V6(v6);
|
||||
assert_eq!(addr.port(), 443);
|
||||
addr.set_port(8080);
|
||||
assert_eq!(addr.port(), 8080);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_flowinfo() {
|
||||
let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0);
|
||||
assert_eq!(v6.flowinfo(), 10);
|
||||
v6.set_flowinfo(20);
|
||||
assert_eq!(v6.flowinfo(), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_scope_id() {
|
||||
let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 10);
|
||||
assert_eq!(v6.scope_id(), 10);
|
||||
v6.set_scope_id(20);
|
||||
assert_eq!(v6.scope_id(), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_v4() {
|
||||
let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80));
|
||||
assert!(v4.is_ipv4());
|
||||
assert!(!v4.is_ipv6());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_v6() {
|
||||
let v6 = SocketAddr::V6(SocketAddrV6::new(
|
||||
Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1),
|
||||
80,
|
||||
10,
|
||||
0,
|
||||
));
|
||||
assert!(!v6.is_ipv4());
|
||||
assert!(v6.is_ipv6());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn socket_v4_to_str() {
|
||||
let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080);
|
||||
|
||||
assert_eq!(format!("{socket}"), "192.168.0.1:8080");
|
||||
assert_eq!(format!("{socket:<20}"), "192.168.0.1:8080 ");
|
||||
assert_eq!(format!("{socket:>20}"), " 192.168.0.1:8080");
|
||||
assert_eq!(format!("{socket:^20}"), " 192.168.0.1:8080 ");
|
||||
assert_eq!(format!("{socket:.10}"), "192.168.0.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn socket_v6_to_str() {
|
||||
let mut socket = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0);
|
||||
|
||||
assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1]:53");
|
||||
assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1]:53 ");
|
||||
assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1]:53");
|
||||
assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1]:53 ");
|
||||
assert_eq!(format!("{socket:.15}"), "[2a02:6b8:0:1::");
|
||||
|
||||
socket.set_scope_id(5);
|
||||
|
||||
assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1%5]:53");
|
||||
assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1%5]:53 ");
|
||||
assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1%5]:53");
|
||||
assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1%5]:53 ");
|
||||
assert_eq!(format!("{socket:.18}"), "[2a02:6b8:0:1::1%5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compare() {
|
||||
let v4_1 = "224.120.45.1:23456".parse::<SocketAddrV4>().unwrap();
|
||||
let v4_2 = "224.210.103.5:12345".parse::<SocketAddrV4>().unwrap();
|
||||
let v4_3 = "224.210.103.5:23456".parse::<SocketAddrV4>().unwrap();
|
||||
let v6_1 = "[2001:db8:f00::1002]:23456".parse::<SocketAddrV6>().unwrap();
|
||||
let v6_2 = "[2001:db8:f00::2001]:12345".parse::<SocketAddrV6>().unwrap();
|
||||
let v6_3 = "[2001:db8:f00::2001]:23456".parse::<SocketAddrV6>().unwrap();
|
||||
|
||||
// equality
|
||||
assert_eq!(v4_1, v4_1);
|
||||
assert_eq!(v6_1, v6_1);
|
||||
assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1));
|
||||
assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1));
|
||||
assert!(v4_1 != v4_2);
|
||||
assert!(v6_1 != v6_2);
|
||||
|
||||
// compare different addresses
|
||||
assert!(v4_1 < v4_2);
|
||||
assert!(v6_1 < v6_2);
|
||||
assert!(v4_2 > v4_1);
|
||||
assert!(v6_2 > v6_1);
|
||||
|
||||
// compare the same address with different ports
|
||||
assert!(v4_2 < v4_3);
|
||||
assert!(v6_2 < v6_3);
|
||||
assert!(v4_3 > v4_2);
|
||||
assert!(v6_3 > v6_2);
|
||||
|
||||
// compare different addresses with the same port
|
||||
assert!(v4_1 < v4_3);
|
||||
assert!(v6_1 < v6_3);
|
||||
assert!(v4_3 > v4_1);
|
||||
assert!(v6_3 > v6_1);
|
||||
|
||||
// compare with an inferred right-hand side
|
||||
assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap());
|
||||
assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap());
|
||||
assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap());
|
||||
}
|
||||
1083
crates/std/src/net/tcp.rs
Normal file
1083
crates/std/src/net/tcp.rs
Normal file
File diff suppressed because it is too large
Load Diff
940
crates/std/src/net/tcp/tests.rs
Normal file
940
crates/std/src/net/tcp/tests.rs
Normal file
@@ -0,0 +1,940 @@
|
||||
use crate::io::prelude::*;
|
||||
use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut};
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::net::test::{next_test_ip4, next_test_ip6};
|
||||
use crate::net::*;
|
||||
use crate::sync::mpsc::channel;
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::{fmt, thread};
|
||||
|
||||
fn each_ip(f: &mut dyn FnMut(SocketAddr)) {
|
||||
f(next_test_ip4());
|
||||
f(next_test_ip6());
|
||||
}
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(t) => t,
|
||||
Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bind_error() {
|
||||
match TcpListener::bind("1.1.1.1:9999") {
|
||||
Ok(..) => panic!(),
|
||||
Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn connect_error() {
|
||||
match TcpStream::connect("0.0.0.0:1") {
|
||||
Ok(..) => panic!(),
|
||||
Err(e) => assert!(
|
||||
e.kind() == ErrorKind::ConnectionRefused
|
||||
|| e.kind() == ErrorKind::InvalidInput
|
||||
|| e.kind() == ErrorKind::AddrInUse
|
||||
|| e.kind() == ErrorKind::AddrNotAvailable
|
||||
|| e.kind() == ErrorKind::NetworkUnreachable,
|
||||
"bad error: {} {:?}",
|
||||
e,
|
||||
e.kind()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
|
||||
fn connect_timeout_error() {
|
||||
let socket_addr = next_test_ip4();
|
||||
let result = TcpStream::connect_timeout(&socket_addr, Duration::MAX);
|
||||
assert!(!matches!(result, Err(e) if e.kind() == ErrorKind::TimedOut));
|
||||
|
||||
let _listener = TcpListener::bind(&socket_addr).unwrap();
|
||||
assert!(TcpStream::connect_timeout(&socket_addr, Duration::MAX).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn listen_localhost() {
|
||||
let socket_addr = next_test_ip4();
|
||||
let listener = t!(TcpListener::bind(&socket_addr));
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port())));
|
||||
t!(stream.write(&[144]));
|
||||
});
|
||||
|
||||
let mut stream = t!(listener.accept()).0;
|
||||
let mut buf = [0];
|
||||
t!(stream.read(&mut buf));
|
||||
assert!(buf[0] == 144);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn connect_loopback() {
|
||||
each_ip(&mut |addr| {
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let host = match addr {
|
||||
SocketAddr::V4(..) => "127.0.0.1",
|
||||
SocketAddr::V6(..) => "::1",
|
||||
};
|
||||
let mut stream = t!(TcpStream::connect(&(host, addr.port())));
|
||||
t!(stream.write(&[66]));
|
||||
});
|
||||
|
||||
let mut stream = t!(acceptor.accept()).0;
|
||||
let mut buf = [0];
|
||||
t!(stream.read(&mut buf));
|
||||
assert!(buf[0] == 66);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn smoke_test() {
|
||||
each_ip(&mut |addr| {
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
let mut stream = t!(TcpStream::connect(&addr));
|
||||
t!(stream.write(&[99]));
|
||||
tx.send(t!(stream.local_addr())).unwrap();
|
||||
});
|
||||
|
||||
let (mut stream, addr) = t!(acceptor.accept());
|
||||
let mut buf = [0];
|
||||
t!(stream.read(&mut buf));
|
||||
assert!(buf[0] == 99);
|
||||
assert_eq!(addr, t!(rx.recv()));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn read_eof() {
|
||||
each_ip(&mut |addr| {
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let _stream = t!(TcpStream::connect(&addr));
|
||||
// Close
|
||||
});
|
||||
|
||||
let mut stream = t!(acceptor.accept()).0;
|
||||
let mut buf = [0];
|
||||
let nread = t!(stream.read(&mut buf));
|
||||
assert_eq!(nread, 0);
|
||||
let nread = t!(stream.read(&mut buf));
|
||||
assert_eq!(nread, 0);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn write_close() {
|
||||
each_ip(&mut |addr| {
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
drop(t!(TcpStream::connect(&addr)));
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
let mut stream = t!(acceptor.accept()).0;
|
||||
rx.recv().unwrap();
|
||||
let buf = [0];
|
||||
match stream.write(&buf) {
|
||||
Ok(..) => {}
|
||||
Err(e) => {
|
||||
assert!(
|
||||
e.kind() == ErrorKind::ConnectionReset
|
||||
|| e.kind() == ErrorKind::BrokenPipe
|
||||
|| e.kind() == ErrorKind::ConnectionAborted,
|
||||
"unknown error: {e}"
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn multiple_connect_serial() {
|
||||
each_ip(&mut |addr| {
|
||||
let max = 10;
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
for _ in 0..max {
|
||||
let mut stream = t!(TcpStream::connect(&addr));
|
||||
t!(stream.write(&[99]));
|
||||
}
|
||||
});
|
||||
|
||||
for stream in acceptor.incoming().take(max) {
|
||||
let mut stream = t!(stream);
|
||||
let mut buf = [0];
|
||||
t!(stream.read(&mut buf));
|
||||
assert_eq!(buf[0], 99);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn multiple_connect_interleaved_greedy_schedule() {
|
||||
const MAX: usize = 10;
|
||||
each_ip(&mut |addr| {
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let acceptor = acceptor;
|
||||
for (i, stream) in acceptor.incoming().enumerate().take(MAX) {
|
||||
// Start another thread to handle the connection
|
||||
let _t = thread::spawn(move || {
|
||||
let mut stream = t!(stream);
|
||||
let mut buf = [0];
|
||||
t!(stream.read(&mut buf));
|
||||
assert!(buf[0] == i as u8);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
connect(0, addr);
|
||||
});
|
||||
|
||||
fn connect(i: usize, addr: SocketAddr) {
|
||||
if i == MAX {
|
||||
return;
|
||||
}
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
let mut stream = t!(TcpStream::connect(&addr));
|
||||
// Connect again before writing
|
||||
connect(i + 1, addr);
|
||||
t!(stream.write(&[i as u8]));
|
||||
});
|
||||
t.join().ok().expect("thread panicked");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn multiple_connect_interleaved_lazy_schedule() {
|
||||
const MAX: usize = 10;
|
||||
each_ip(&mut |addr| {
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
for stream in acceptor.incoming().take(MAX) {
|
||||
// Start another thread to handle the connection
|
||||
let _t = thread::spawn(move || {
|
||||
let mut stream = t!(stream);
|
||||
let mut buf = [0];
|
||||
t!(stream.read(&mut buf));
|
||||
assert!(buf[0] == 99);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
connect(0, addr);
|
||||
});
|
||||
|
||||
fn connect(i: usize, addr: SocketAddr) {
|
||||
if i == MAX {
|
||||
return;
|
||||
}
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
let mut stream = t!(TcpStream::connect(&addr));
|
||||
connect(i + 1, addr);
|
||||
t!(stream.write(&[99]));
|
||||
});
|
||||
t.join().ok().expect("thread panicked");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn socket_and_peer_name() {
|
||||
each_ip(&mut |addr| {
|
||||
let listener = t!(TcpListener::bind(&addr));
|
||||
let so_name = t!(listener.local_addr());
|
||||
assert_eq!(addr, so_name);
|
||||
let _t = thread::spawn(move || {
|
||||
t!(listener.accept());
|
||||
});
|
||||
|
||||
let stream = t!(TcpStream::connect(&addr));
|
||||
assert_eq!(addr, t!(stream.peer_addr()));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn partial_read() {
|
||||
each_ip(&mut |addr| {
|
||||
let (tx, rx) = channel();
|
||||
let srv = t!(TcpListener::bind(&addr));
|
||||
let _t = thread::spawn(move || {
|
||||
let mut cl = t!(srv.accept()).0;
|
||||
cl.write(&[10]).unwrap();
|
||||
let mut b = [0];
|
||||
t!(cl.read(&mut b));
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
let mut c = t!(TcpStream::connect(&addr));
|
||||
let mut b = [0; 10];
|
||||
assert_eq!(c.read(&mut b).unwrap(), 1);
|
||||
t!(c.write(&[1]));
|
||||
rx.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn read_buf() {
|
||||
each_ip(&mut |addr| {
|
||||
let srv = t!(TcpListener::bind(&addr));
|
||||
let t = thread::spawn(move || {
|
||||
let mut s = t!(TcpStream::connect(&addr));
|
||||
s.write_all(&[1, 2, 3, 4]).unwrap();
|
||||
});
|
||||
|
||||
let mut s = t!(srv.accept()).0;
|
||||
let mut buf: [MaybeUninit<u8>; 128] = [MaybeUninit::uninit(); 128];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
t!(s.read_buf(buf.unfilled()));
|
||||
assert_eq!(buf.filled(), &[1, 2, 3, 4]);
|
||||
// TcpStream::read_buf should omit buffer initialization.
|
||||
assert_eq!(buf.init_len(), 4);
|
||||
|
||||
t.join().ok().expect("thread panicked");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_vectored() {
|
||||
each_ip(&mut |addr| {
|
||||
let srv = t!(TcpListener::bind(&addr));
|
||||
let mut s1 = t!(TcpStream::connect(&addr));
|
||||
let mut s2 = t!(srv.accept()).0;
|
||||
|
||||
let len = s1.write(&[10, 11, 12]).unwrap();
|
||||
assert_eq!(len, 3);
|
||||
|
||||
let mut a = [];
|
||||
let mut b = [0];
|
||||
let mut c = [0; 3];
|
||||
let len = t!(s2.read_vectored(&mut [
|
||||
IoSliceMut::new(&mut a),
|
||||
IoSliceMut::new(&mut b),
|
||||
IoSliceMut::new(&mut c)
|
||||
],));
|
||||
assert!(len > 0);
|
||||
assert_eq!(b, [10]);
|
||||
// some implementations don't support readv, so we may only fill the first buffer
|
||||
assert!(len == 1 || c == [11, 12, 0]);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_vectored() {
|
||||
each_ip(&mut |addr| {
|
||||
let srv = t!(TcpListener::bind(&addr));
|
||||
let mut s1 = t!(TcpStream::connect(&addr));
|
||||
let mut s2 = t!(srv.accept()).0;
|
||||
|
||||
let a = [];
|
||||
let b = [10];
|
||||
let c = [11, 12];
|
||||
t!(s1.write_vectored(&[IoSlice::new(&a), IoSlice::new(&b), IoSlice::new(&c)]));
|
||||
|
||||
let mut buf = [0; 4];
|
||||
let len = t!(s2.read(&mut buf));
|
||||
// some implementations don't support writev, so we may only write the first buffer
|
||||
if len == 1 {
|
||||
assert_eq!(buf, [10, 0, 0, 0]);
|
||||
} else {
|
||||
assert_eq!(len, 3);
|
||||
assert_eq!(buf, [10, 11, 12, 0]);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_bind() {
|
||||
each_ip(&mut |addr| {
|
||||
let listener1 = t!(TcpListener::bind(&addr));
|
||||
match TcpListener::bind(&addr) {
|
||||
Ok(listener2) => panic!(
|
||||
"This system (perhaps due to options set by TcpListener::bind) \
|
||||
permits double binding: {:?} and {:?}",
|
||||
listener1, listener2
|
||||
),
|
||||
Err(e) => {
|
||||
assert!(
|
||||
e.kind() == ErrorKind::ConnectionRefused
|
||||
|| e.kind() == ErrorKind::Uncategorized
|
||||
|| e.kind() == ErrorKind::AddrInUse,
|
||||
"unknown error: {} {:?}",
|
||||
e,
|
||||
e.kind()
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn tcp_clone_smoke() {
|
||||
each_ip(&mut |addr| {
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let mut s = t!(TcpStream::connect(&addr));
|
||||
let mut buf = [0, 0];
|
||||
assert_eq!(s.read(&mut buf).unwrap(), 1);
|
||||
assert_eq!(buf[0], 1);
|
||||
t!(s.write(&[2]));
|
||||
});
|
||||
|
||||
let mut s1 = t!(acceptor.accept()).0;
|
||||
let s2 = t!(s1.try_clone());
|
||||
|
||||
let (tx1, rx1) = channel();
|
||||
let (tx2, rx2) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
let mut s2 = s2;
|
||||
rx1.recv().unwrap();
|
||||
t!(s2.write(&[1]));
|
||||
tx2.send(()).unwrap();
|
||||
});
|
||||
tx1.send(()).unwrap();
|
||||
let mut buf = [0, 0];
|
||||
assert_eq!(s1.read(&mut buf).unwrap(), 1);
|
||||
rx2.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn tcp_clone_two_read() {
|
||||
each_ip(&mut |addr| {
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
let (tx1, rx) = channel();
|
||||
let tx2 = tx1.clone();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let mut s = t!(TcpStream::connect(&addr));
|
||||
t!(s.write(&[1]));
|
||||
rx.recv().unwrap();
|
||||
t!(s.write(&[2]));
|
||||
rx.recv().unwrap();
|
||||
});
|
||||
|
||||
let mut s1 = t!(acceptor.accept()).0;
|
||||
let s2 = t!(s1.try_clone());
|
||||
|
||||
let (done, rx) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
let mut s2 = s2;
|
||||
let mut buf = [0, 0];
|
||||
t!(s2.read(&mut buf));
|
||||
tx2.send(()).unwrap();
|
||||
done.send(()).unwrap();
|
||||
});
|
||||
let mut buf = [0, 0];
|
||||
t!(s1.read(&mut buf));
|
||||
tx1.send(()).unwrap();
|
||||
|
||||
rx.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn tcp_clone_two_write() {
|
||||
each_ip(&mut |addr| {
|
||||
let acceptor = t!(TcpListener::bind(&addr));
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let mut s = t!(TcpStream::connect(&addr));
|
||||
let mut buf = [0, 1];
|
||||
t!(s.read(&mut buf));
|
||||
t!(s.read(&mut buf));
|
||||
});
|
||||
|
||||
let mut s1 = t!(acceptor.accept()).0;
|
||||
let s2 = t!(s1.try_clone());
|
||||
|
||||
let (done, rx) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
let mut s2 = s2;
|
||||
t!(s2.write(&[1]));
|
||||
done.send(()).unwrap();
|
||||
});
|
||||
t!(s1.write(&[2]));
|
||||
|
||||
rx.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
// FIXME: https://github.com/fortanix/rust-sgx/issues/110
|
||||
#[cfg_attr(target_env = "sgx", ignore)]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn shutdown_smoke() {
|
||||
each_ip(&mut |addr| {
|
||||
let a = t!(TcpListener::bind(&addr));
|
||||
let _t = thread::spawn(move || {
|
||||
let mut c = t!(a.accept()).0;
|
||||
let mut b = [0];
|
||||
assert_eq!(c.read(&mut b).unwrap(), 0);
|
||||
t!(c.write(&[1]));
|
||||
});
|
||||
|
||||
let mut s = t!(TcpStream::connect(&addr));
|
||||
t!(s.shutdown(Shutdown::Write));
|
||||
assert!(s.write(&[1]).is_err());
|
||||
let mut b = [0, 0];
|
||||
assert_eq!(t!(s.read(&mut b)), 1);
|
||||
assert_eq!(b[0], 1);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
// FIXME: https://github.com/fortanix/rust-sgx/issues/110
|
||||
#[cfg_attr(target_env = "sgx", ignore)]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn close_readwrite_smoke() {
|
||||
each_ip(&mut |addr| {
|
||||
let a = t!(TcpListener::bind(&addr));
|
||||
let (tx, rx) = channel::<()>();
|
||||
let _t = thread::spawn(move || {
|
||||
let _s = t!(a.accept());
|
||||
let _ = rx.recv();
|
||||
});
|
||||
|
||||
let mut b = [0];
|
||||
let mut s = t!(TcpStream::connect(&addr));
|
||||
let mut s2 = t!(s.try_clone());
|
||||
|
||||
// closing should prevent reads/writes
|
||||
t!(s.shutdown(Shutdown::Write));
|
||||
assert!(s.write(&[0]).is_err());
|
||||
t!(s.shutdown(Shutdown::Read));
|
||||
assert_eq!(s.read(&mut b).unwrap(), 0);
|
||||
|
||||
// closing should affect previous handles
|
||||
assert!(s2.write(&[0]).is_err());
|
||||
assert_eq!(s2.read(&mut b).unwrap(), 0);
|
||||
|
||||
// closing should affect new handles
|
||||
let mut s3 = t!(s.try_clone());
|
||||
assert!(s3.write(&[0]).is_err());
|
||||
assert_eq!(s3.read(&mut b).unwrap(), 0);
|
||||
|
||||
// make sure these don't die
|
||||
let _ = s2.shutdown(Shutdown::Read);
|
||||
let _ = s2.shutdown(Shutdown::Write);
|
||||
let _ = s3.shutdown(Shutdown::Read);
|
||||
let _ = s3.shutdown(Shutdown::Write);
|
||||
drop(tx);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
// FIXME: https://github.com/fortanix/rust-sgx/issues/110
|
||||
#[cfg_attr(target_env = "sgx", ignore)]
|
||||
// On windows, shutdown will not wake up blocking I/O operations.
|
||||
#[cfg_attr(windows, ignore)]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn close_read_wakes_up() {
|
||||
each_ip(&mut |addr| {
|
||||
let listener = t!(TcpListener::bind(&addr));
|
||||
let _t = thread::spawn(move || {
|
||||
let (stream, _) = t!(listener.accept());
|
||||
stream
|
||||
});
|
||||
|
||||
let mut stream = t!(TcpStream::connect(&addr));
|
||||
let stream2 = t!(stream.try_clone());
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let stream2 = stream2;
|
||||
|
||||
// to make it more likely that `read` happens before `shutdown`
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
|
||||
// this should wake up the reader up
|
||||
t!(stream2.shutdown(Shutdown::Read));
|
||||
});
|
||||
|
||||
// this `read` should get interrupted by `shutdown`
|
||||
assert_eq!(t!(stream.read(&mut [0])), 0);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn clone_while_reading() {
|
||||
each_ip(&mut |addr| {
|
||||
let accept = t!(TcpListener::bind(&addr));
|
||||
|
||||
// Enqueue a thread to write to a socket
|
||||
let (tx, rx) = channel();
|
||||
let (txdone, rxdone) = channel();
|
||||
let txdone2 = txdone.clone();
|
||||
let _t = thread::spawn(move || {
|
||||
let mut tcp = t!(TcpStream::connect(&addr));
|
||||
rx.recv().unwrap();
|
||||
t!(tcp.write(&[0]));
|
||||
txdone2.send(()).unwrap();
|
||||
});
|
||||
|
||||
// Spawn off a reading clone
|
||||
let tcp = t!(accept.accept()).0;
|
||||
let tcp2 = t!(tcp.try_clone());
|
||||
let txdone3 = txdone.clone();
|
||||
let _t = thread::spawn(move || {
|
||||
let mut tcp2 = tcp2;
|
||||
t!(tcp2.read(&mut [0]));
|
||||
txdone3.send(()).unwrap();
|
||||
});
|
||||
|
||||
// Try to ensure that the reading clone is indeed reading
|
||||
for _ in 0..50 {
|
||||
thread::yield_now();
|
||||
}
|
||||
|
||||
// clone the handle again while it's reading, then let it finish the
|
||||
// read.
|
||||
let _ = t!(tcp.try_clone());
|
||||
tx.send(()).unwrap();
|
||||
rxdone.recv().unwrap();
|
||||
rxdone.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn clone_accept_smoke() {
|
||||
each_ip(&mut |addr| {
|
||||
let a = t!(TcpListener::bind(&addr));
|
||||
let a2 = t!(a.try_clone());
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let _ = TcpStream::connect(&addr);
|
||||
});
|
||||
let _t = thread::spawn(move || {
|
||||
let _ = TcpStream::connect(&addr);
|
||||
});
|
||||
|
||||
t!(a.accept());
|
||||
t!(a2.accept());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn clone_accept_concurrent() {
|
||||
each_ip(&mut |addr| {
|
||||
let a = t!(TcpListener::bind(&addr));
|
||||
let a2 = t!(a.try_clone());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let tx2 = tx.clone();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
tx.send(t!(a.accept())).unwrap();
|
||||
});
|
||||
let _t = thread::spawn(move || {
|
||||
tx2.send(t!(a2.accept())).unwrap();
|
||||
});
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let _ = TcpStream::connect(&addr);
|
||||
});
|
||||
let _t = thread::spawn(move || {
|
||||
let _ = TcpStream::connect(&addr);
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
rx.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug() {
|
||||
#[cfg(not(target_env = "sgx"))]
|
||||
fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a {
|
||||
addr
|
||||
}
|
||||
#[cfg(target_env = "sgx")]
|
||||
fn render_socket_addr<'a>(addr: &'a SocketAddr) -> impl fmt::Debug + 'a {
|
||||
addr.to_string()
|
||||
}
|
||||
|
||||
#[cfg(any(unix, target_os = "wasi"))]
|
||||
use crate::os::fd::AsRawFd;
|
||||
#[cfg(target_env = "sgx")]
|
||||
use crate::os::fortanix_sgx::io::AsRawFd;
|
||||
#[cfg(not(windows))]
|
||||
fn render_inner(addr: &dyn AsRawFd) -> impl fmt::Debug {
|
||||
addr.as_raw_fd()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
fn render_inner(addr: &dyn crate::os::windows::io::AsRawSocket) -> impl fmt::Debug {
|
||||
addr.as_raw_socket()
|
||||
}
|
||||
|
||||
let inner_name = if cfg!(windows) { "socket" } else { "fd" };
|
||||
let socket_addr = next_test_ip4();
|
||||
|
||||
let listener = t!(TcpListener::bind(&socket_addr));
|
||||
let compare = format!(
|
||||
"TcpListener {{ addr: {:?}, {}: {:?} }}",
|
||||
render_socket_addr(&socket_addr),
|
||||
inner_name,
|
||||
render_inner(&listener)
|
||||
);
|
||||
assert_eq!(format!("{listener:?}"), compare);
|
||||
|
||||
let stream = t!(TcpStream::connect(&("localhost", socket_addr.port())));
|
||||
let compare = format!(
|
||||
"TcpStream {{ addr: {:?}, peer: {:?}, {}: {:?} }}",
|
||||
render_socket_addr(&stream.local_addr().unwrap()),
|
||||
render_socket_addr(&stream.peer_addr().unwrap()),
|
||||
inner_name,
|
||||
render_inner(&stream)
|
||||
);
|
||||
assert_eq!(format!("{stream:?}"), compare);
|
||||
}
|
||||
|
||||
// FIXME: re-enabled openbsd tests once their socket timeout code
|
||||
// no longer has rounding errors.
|
||||
// VxWorks ignores SO_SNDTIMEO.
|
||||
#[cfg_attr(
|
||||
any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"),
|
||||
ignore
|
||||
)]
|
||||
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported
|
||||
#[test]
|
||||
fn timeouts() {
|
||||
let addr = next_test_ip4();
|
||||
let listener = t!(TcpListener::bind(&addr));
|
||||
|
||||
let stream = t!(TcpStream::connect(&("localhost", addr.port())));
|
||||
let dur = Duration::new(15410, 0);
|
||||
|
||||
assert_eq!(None, t!(stream.read_timeout()));
|
||||
|
||||
t!(stream.set_read_timeout(Some(dur)));
|
||||
assert_eq!(Some(dur), t!(stream.read_timeout()));
|
||||
|
||||
assert_eq!(None, t!(stream.write_timeout()));
|
||||
|
||||
t!(stream.set_write_timeout(Some(dur)));
|
||||
assert_eq!(Some(dur), t!(stream.write_timeout()));
|
||||
|
||||
t!(stream.set_read_timeout(None));
|
||||
assert_eq!(None, t!(stream.read_timeout()));
|
||||
|
||||
t!(stream.set_write_timeout(None));
|
||||
assert_eq!(None, t!(stream.write_timeout()));
|
||||
drop(listener);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported
|
||||
fn test_read_timeout() {
|
||||
let addr = next_test_ip4();
|
||||
let listener = t!(TcpListener::bind(&addr));
|
||||
|
||||
let mut stream = t!(TcpStream::connect(&("localhost", addr.port())));
|
||||
t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
|
||||
|
||||
let mut buf = [0; 10];
|
||||
let start = Instant::now();
|
||||
let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
|
||||
assert!(
|
||||
kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
|
||||
"unexpected_error: {:?}",
|
||||
kind
|
||||
);
|
||||
assert!(start.elapsed() > Duration::from_millis(400));
|
||||
drop(listener);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported
|
||||
fn test_read_with_timeout() {
|
||||
let addr = next_test_ip4();
|
||||
let listener = t!(TcpListener::bind(&addr));
|
||||
|
||||
let mut stream = t!(TcpStream::connect(&("localhost", addr.port())));
|
||||
t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
|
||||
|
||||
let mut other_end = t!(listener.accept()).0;
|
||||
t!(other_end.write_all(b"hello world"));
|
||||
|
||||
let mut buf = [0; 11];
|
||||
t!(stream.read(&mut buf));
|
||||
assert_eq!(b"hello world", &buf[..]);
|
||||
|
||||
let start = Instant::now();
|
||||
let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
|
||||
assert!(
|
||||
kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
|
||||
"unexpected_error: {:?}",
|
||||
kind
|
||||
);
|
||||
assert!(start.elapsed() > Duration::from_millis(400));
|
||||
drop(listener);
|
||||
}
|
||||
|
||||
// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
|
||||
// when passed zero Durations
|
||||
#[test]
|
||||
fn test_timeout_zero_duration() {
|
||||
let addr = next_test_ip4();
|
||||
|
||||
let listener = t!(TcpListener::bind(&addr));
|
||||
let stream = t!(TcpStream::connect(&addr));
|
||||
|
||||
let result = stream.set_write_timeout(Some(Duration::new(0, 0)));
|
||||
let err = result.unwrap_err();
|
||||
assert_eq!(err.kind(), ErrorKind::InvalidInput);
|
||||
|
||||
let result = stream.set_read_timeout(Some(Duration::new(0, 0)));
|
||||
let err = result.unwrap_err();
|
||||
assert_eq!(err.kind(), ErrorKind::InvalidInput);
|
||||
|
||||
drop(listener);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_env = "sgx", ignore)]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // linger not supported
|
||||
fn linger() {
|
||||
let addr = next_test_ip4();
|
||||
let _listener = t!(TcpListener::bind(&addr));
|
||||
|
||||
let stream = t!(TcpStream::connect(&("localhost", addr.port())));
|
||||
|
||||
assert_eq!(None, t!(stream.linger()));
|
||||
t!(stream.set_linger(Some(Duration::from_secs(1))));
|
||||
assert_eq!(Some(Duration::from_secs(1)), t!(stream.linger()));
|
||||
t!(stream.set_linger(None));
|
||||
assert_eq!(None, t!(stream.linger()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_env = "sgx", ignore)]
|
||||
fn nodelay() {
|
||||
let addr = next_test_ip4();
|
||||
let _listener = t!(TcpListener::bind(&addr));
|
||||
|
||||
let stream = t!(TcpStream::connect(&("localhost", addr.port())));
|
||||
|
||||
assert_eq!(false, t!(stream.nodelay()));
|
||||
t!(stream.set_nodelay(true));
|
||||
assert_eq!(true, t!(stream.nodelay()));
|
||||
t!(stream.set_nodelay(false));
|
||||
assert_eq!(false, t!(stream.nodelay()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_env = "sgx", ignore)]
|
||||
fn ttl() {
|
||||
let ttl = 100;
|
||||
|
||||
let addr = next_test_ip4();
|
||||
let listener = t!(TcpListener::bind(&addr));
|
||||
|
||||
t!(listener.set_ttl(ttl));
|
||||
assert_eq!(ttl, t!(listener.ttl()));
|
||||
|
||||
let stream = t!(TcpStream::connect(&("localhost", addr.port())));
|
||||
|
||||
t!(stream.set_ttl(ttl));
|
||||
assert_eq!(ttl, t!(stream.ttl()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_env = "sgx", ignore)]
|
||||
fn set_nonblocking() {
|
||||
let addr = next_test_ip4();
|
||||
let listener = t!(TcpListener::bind(&addr));
|
||||
|
||||
t!(listener.set_nonblocking(true));
|
||||
t!(listener.set_nonblocking(false));
|
||||
|
||||
let mut stream = t!(TcpStream::connect(&("localhost", addr.port())));
|
||||
|
||||
t!(stream.set_nonblocking(false));
|
||||
t!(stream.set_nonblocking(true));
|
||||
|
||||
let mut buf = [0];
|
||||
match stream.read(&mut buf) {
|
||||
Ok(_) => panic!("expected error"),
|
||||
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
|
||||
Err(e) => panic!("unexpected error {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn peek() {
|
||||
each_ip(&mut |addr| {
|
||||
let (txdone, rxdone) = channel();
|
||||
|
||||
let srv = t!(TcpListener::bind(&addr));
|
||||
let _t = thread::spawn(move || {
|
||||
let mut cl = t!(srv.accept()).0;
|
||||
cl.write(&[1, 3, 3, 7]).unwrap();
|
||||
t!(rxdone.recv());
|
||||
});
|
||||
|
||||
let mut c = t!(TcpStream::connect(&addr));
|
||||
let mut b = [0; 10];
|
||||
for _ in 1..3 {
|
||||
let len = c.peek(&mut b).unwrap();
|
||||
assert_eq!(len, 4);
|
||||
}
|
||||
let len = c.read(&mut b).unwrap();
|
||||
assert_eq!(len, 4);
|
||||
|
||||
t!(c.set_nonblocking(true));
|
||||
match c.peek(&mut b) {
|
||||
Ok(_) => panic!("expected error"),
|
||||
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
|
||||
Err(e) => panic!("unexpected error {e}"),
|
||||
}
|
||||
t!(txdone.send(()));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
|
||||
fn connect_timeout_valid() {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
TcpStream::connect_timeout(&addr, Duration::from_secs(2)).unwrap();
|
||||
}
|
||||
44
crates/std/src/net/test.rs
Normal file
44
crates/std/src/net/test.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
#![allow(warnings)] // not used on emscripten
|
||||
|
||||
use crate::env;
|
||||
use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
static PORT: AtomicUsize = AtomicUsize::new(0);
|
||||
const BASE_PORT: u16 = 19600;
|
||||
|
||||
pub fn next_test_ip4() -> SocketAddr {
|
||||
let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT;
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port))
|
||||
}
|
||||
|
||||
pub fn next_test_ip6() -> SocketAddr {
|
||||
let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT;
|
||||
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0))
|
||||
}
|
||||
|
||||
pub fn sa4(a: Ipv4Addr, p: u16) -> SocketAddr {
|
||||
SocketAddr::V4(SocketAddrV4::new(a, p))
|
||||
}
|
||||
|
||||
pub fn sa6(a: Ipv6Addr, p: u16) -> SocketAddr {
|
||||
SocketAddr::V6(SocketAddrV6::new(a, p, 0, 0))
|
||||
}
|
||||
|
||||
pub fn tsa<A: ToSocketAddrs>(a: A) -> Result<Vec<SocketAddr>, String> {
|
||||
match a.to_socket_addrs() {
|
||||
Ok(a) => Ok(a.collect()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compare_ignore_zoneid(a: &SocketAddr, b: &SocketAddr) -> bool {
|
||||
match (a, b) {
|
||||
(SocketAddr::V6(a), SocketAddr::V6(b)) => {
|
||||
a.ip().segments() == b.ip().segments()
|
||||
&& a.flowinfo() == b.flowinfo()
|
||||
&& a.port() == b.port()
|
||||
}
|
||||
_ => a == b,
|
||||
}
|
||||
}
|
||||
848
crates/std/src/net/udp.rs
Normal file
848
crates/std/src/net/udp.rs
Normal file
@@ -0,0 +1,848 @@
|
||||
#[cfg(all(
|
||||
test,
|
||||
not(any(
|
||||
target_os = "emscripten",
|
||||
all(target_os = "wasi", target_env = "p1"),
|
||||
target_env = "sgx",
|
||||
target_os = "xous",
|
||||
target_os = "trusty",
|
||||
))
|
||||
))]
|
||||
mod tests;
|
||||
|
||||
use crate::fmt;
|
||||
use crate::io::{self, ErrorKind};
|
||||
use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs};
|
||||
use crate::sys::{AsInner, FromInner, IntoInner, net as net_imp};
|
||||
use crate::time::Duration;
|
||||
|
||||
/// A UDP socket.
|
||||
///
|
||||
/// After creating a `UdpSocket` by [`bind`]ing it to a socket address, data can be
|
||||
/// [sent to] and [received from] any other socket address.
|
||||
///
|
||||
/// Although UDP is a connectionless protocol, this implementation provides an interface
|
||||
/// to set an address where data should be sent and received from. After setting a remote
|
||||
/// address with [`connect`], data can be sent to and received from that address with
|
||||
/// [`send`] and [`recv`].
|
||||
///
|
||||
/// As stated in the User Datagram Protocol's specification in [IETF RFC 768], UDP is
|
||||
/// an unordered, unreliable protocol; refer to [`TcpListener`] and [`TcpStream`] for TCP
|
||||
/// primitives.
|
||||
///
|
||||
/// [`bind`]: UdpSocket::bind
|
||||
/// [`connect`]: UdpSocket::connect
|
||||
/// [IETF RFC 768]: https://tools.ietf.org/html/rfc768
|
||||
/// [`recv`]: UdpSocket::recv
|
||||
/// [received from]: UdpSocket::recv_from
|
||||
/// [`send`]: UdpSocket::send
|
||||
/// [sent to]: UdpSocket::send_to
|
||||
/// [`TcpListener`]: crate::net::TcpListener
|
||||
/// [`TcpStream`]: crate::net::TcpStream
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// {
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254")?;
|
||||
///
|
||||
/// // Receives a single datagram message on the socket. If `buf` is too small to hold
|
||||
/// // the message, it will be cut off.
|
||||
/// let mut buf = [0; 10];
|
||||
/// let (amt, src) = socket.recv_from(&mut buf)?;
|
||||
///
|
||||
/// // Redeclare `buf` as slice of the received data and send reverse data back to origin.
|
||||
/// let buf = &mut buf[..amt];
|
||||
/// buf.reverse();
|
||||
/// socket.send_to(buf, &src)?;
|
||||
/// } // the socket is closed here
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct UdpSocket(net_imp::UdpSocket);
|
||||
|
||||
impl UdpSocket {
|
||||
/// Creates a UDP socket from the given address.
|
||||
///
|
||||
/// The address type can be any implementor of [`ToSocketAddrs`] trait. See
|
||||
/// its documentation for concrete examples.
|
||||
///
|
||||
/// If `addr` yields multiple addresses, `bind` will be attempted with
|
||||
/// each of the addresses until one succeeds and returns the socket. If none
|
||||
/// of the addresses succeed in creating a socket, the error returned from
|
||||
/// the last attempt (the last address) is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creates a UDP socket bound to `127.0.0.1:3400`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:3400").expect("couldn't bind to address");
|
||||
/// ```
|
||||
///
|
||||
/// Creates a UDP socket bound to `127.0.0.1:3400`. If the socket cannot be
|
||||
/// bound to that address, create a UDP socket bound to `127.0.0.1:3401`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::{SocketAddr, UdpSocket};
|
||||
///
|
||||
/// let addrs = [
|
||||
/// SocketAddr::from(([127, 0, 0, 1], 3400)),
|
||||
/// SocketAddr::from(([127, 0, 0, 1], 3401)),
|
||||
/// ];
|
||||
/// let socket = UdpSocket::bind(&addrs[..]).expect("couldn't bind to address");
|
||||
/// ```
|
||||
///
|
||||
/// Creates a UDP socket bound to a port assigned by the operating system
|
||||
/// at `127.0.0.1`.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// Note that `bind` declares the scope of your network connection.
|
||||
/// You can only receive datagrams from and send datagrams to
|
||||
/// participants in that view of the network.
|
||||
/// For instance, binding to a loopback address as in the example
|
||||
/// above will prevent you from sending datagrams to another device
|
||||
/// in your local network.
|
||||
///
|
||||
/// In order to limit your view of the network the least, `bind` to
|
||||
/// [`Ipv4Addr::UNSPECIFIED`] or [`Ipv6Addr::UNSPECIFIED`].
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
|
||||
net_imp::UdpSocket::bind(addr).map(UdpSocket)
|
||||
}
|
||||
|
||||
/// Receives a single datagram message on the socket. On success, returns the number
|
||||
/// of bytes read and the origin.
|
||||
///
|
||||
/// The function must be called with valid byte array `buf` of sufficient size to
|
||||
/// hold the message bytes. If a message is too long to fit in the supplied buffer,
|
||||
/// excess bytes may be discarded.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// let mut buf = [0; 10];
|
||||
/// let (number_of_bytes, src_addr) = socket.recv_from(&mut buf)
|
||||
/// .expect("Didn't receive data");
|
||||
/// let filled_buf = &mut buf[..number_of_bytes];
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.0.recv_from(buf)
|
||||
}
|
||||
|
||||
/// Receives a single datagram message on the socket, without removing it from the
|
||||
/// queue. On success, returns the number of bytes read and the origin.
|
||||
///
|
||||
/// The function must be called with valid byte array `buf` of sufficient size to
|
||||
/// hold the message bytes. If a message is too long to fit in the supplied buffer,
|
||||
/// excess bytes may be discarded.
|
||||
///
|
||||
/// Successive calls return the same data. This is accomplished by passing
|
||||
/// `MSG_PEEK` as a flag to the underlying `recvfrom` system call.
|
||||
///
|
||||
/// Do not use this function to implement busy waiting, instead use `libc::poll` to
|
||||
/// synchronize IO events on one or more sockets.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// let mut buf = [0; 10];
|
||||
/// let (number_of_bytes, src_addr) = socket.peek_from(&mut buf)
|
||||
/// .expect("Didn't receive data");
|
||||
/// let filled_buf = &mut buf[..number_of_bytes];
|
||||
/// ```
|
||||
#[stable(feature = "peek", since = "1.18.0")]
|
||||
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.0.peek_from(buf)
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the given address. On success, returns the
|
||||
/// number of bytes written. Note that the operating system may refuse
|
||||
/// buffers larger than 65507. However, partial writes are not possible
|
||||
/// until buffer sizes above `i32::MAX`.
|
||||
///
|
||||
/// Address type can be any implementor of [`ToSocketAddrs`] trait. See its
|
||||
/// documentation for concrete examples.
|
||||
///
|
||||
/// It is possible for `addr` to yield multiple addresses, but `send_to`
|
||||
/// will only send data to the first address yielded by `addr`.
|
||||
///
|
||||
/// This will return an error when the IP version of the local socket
|
||||
/// does not match that returned from [`ToSocketAddrs`].
|
||||
///
|
||||
/// See [Issue #34202] for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.send_to(&[0; 10], "127.0.0.1:4242").expect("couldn't send data");
|
||||
/// ```
|
||||
///
|
||||
/// [Issue #34202]: https://github.com/rust-lang/rust/issues/34202
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn send_to<A: ToSocketAddrs>(&self, buf: &[u8], addr: A) -> io::Result<usize> {
|
||||
match addr.to_socket_addrs()?.next() {
|
||||
Some(addr) => self.0.send_to(buf, &addr),
|
||||
None => Err(io::const_error!(ErrorKind::InvalidInput, "no addresses to send data to")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote peer this socket was connected to.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("192.168.0.1:41203").expect("couldn't connect to address");
|
||||
/// assert_eq!(socket.peer_addr().unwrap(),
|
||||
/// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 41203)));
|
||||
/// ```
|
||||
///
|
||||
/// If the socket isn't connected, it will return a [`NotConnected`] error.
|
||||
///
|
||||
/// [`NotConnected`]: io::ErrorKind::NotConnected
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// assert_eq!(socket.peer_addr().unwrap_err().kind(),
|
||||
/// std::io::ErrorKind::NotConnected);
|
||||
/// ```
|
||||
#[stable(feature = "udp_peer_addr", since = "1.40.0")]
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address that this socket was created from.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// assert_eq!(socket.local_addr().unwrap(),
|
||||
/// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 34254)));
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.socket_addr()
|
||||
}
|
||||
|
||||
/// Creates a new independently owned handle to the underlying socket.
|
||||
///
|
||||
/// The returned `UdpSocket` is a reference to the same socket that this
|
||||
/// object references. Both handles will read and write the same port, and
|
||||
/// options set on one socket will be propagated to the other.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// let socket_clone = socket.try_clone().expect("couldn't clone the socket");
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn try_clone(&self) -> io::Result<UdpSocket> {
|
||||
self.0.duplicate().map(UdpSocket)
|
||||
}
|
||||
|
||||
/// Sets the read timeout to the timeout specified.
|
||||
///
|
||||
/// If the value specified is [`None`], then [`read`] calls will block
|
||||
/// indefinitely. An [`Err`] is returned if the zero [`Duration`] is
|
||||
/// passed to this method.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// Platforms may return a different error code whenever a read times out as
|
||||
/// a result of setting this option. For example Unix typically returns an
|
||||
/// error of the kind [`WouldBlock`], but Windows may return [`TimedOut`].
|
||||
///
|
||||
/// [`read`]: io::Read::read
|
||||
/// [`WouldBlock`]: io::ErrorKind::WouldBlock
|
||||
/// [`TimedOut`]: io::ErrorKind::TimedOut
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_read_timeout(None).expect("set_read_timeout call failed");
|
||||
/// ```
|
||||
///
|
||||
/// An [`Err`] is returned if the zero [`Duration`] is passed to this
|
||||
/// method:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// use std::net::UdpSocket;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap();
|
||||
/// let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
|
||||
/// let err = result.unwrap_err();
|
||||
/// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
|
||||
/// ```
|
||||
#[stable(feature = "socket_timeout", since = "1.4.0")]
|
||||
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
|
||||
self.0.set_read_timeout(dur)
|
||||
}
|
||||
|
||||
/// Sets the write timeout to the timeout specified.
|
||||
///
|
||||
/// If the value specified is [`None`], then [`write`] calls will block
|
||||
/// indefinitely. An [`Err`] is returned if the zero [`Duration`] is
|
||||
/// passed to this method.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// Platforms may return a different error code whenever a write times out
|
||||
/// as a result of setting this option. For example Unix typically returns
|
||||
/// an error of the kind [`WouldBlock`], but Windows may return [`TimedOut`].
|
||||
///
|
||||
/// [`write`]: io::Write::write
|
||||
/// [`WouldBlock`]: io::ErrorKind::WouldBlock
|
||||
/// [`TimedOut`]: io::ErrorKind::TimedOut
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_write_timeout(None).expect("set_write_timeout call failed");
|
||||
/// ```
|
||||
///
|
||||
/// An [`Err`] is returned if the zero [`Duration`] is passed to this
|
||||
/// method:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// use std::net::UdpSocket;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap();
|
||||
/// let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
|
||||
/// let err = result.unwrap_err();
|
||||
/// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
|
||||
/// ```
|
||||
#[stable(feature = "socket_timeout", since = "1.4.0")]
|
||||
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
|
||||
self.0.set_write_timeout(dur)
|
||||
}
|
||||
|
||||
/// Returns the read timeout of this socket.
|
||||
///
|
||||
/// If the timeout is [`None`], then [`read`] calls will block indefinitely.
|
||||
///
|
||||
/// [`read`]: io::Read::read
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_read_timeout(None).expect("set_read_timeout call failed");
|
||||
/// assert_eq!(socket.read_timeout().unwrap(), None);
|
||||
/// ```
|
||||
#[stable(feature = "socket_timeout", since = "1.4.0")]
|
||||
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
self.0.read_timeout()
|
||||
}
|
||||
|
||||
/// Returns the write timeout of this socket.
|
||||
///
|
||||
/// If the timeout is [`None`], then [`write`] calls will block indefinitely.
|
||||
///
|
||||
/// [`write`]: io::Write::write
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_write_timeout(None).expect("set_write_timeout call failed");
|
||||
/// assert_eq!(socket.write_timeout().unwrap(), None);
|
||||
/// ```
|
||||
#[stable(feature = "socket_timeout", since = "1.4.0")]
|
||||
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
|
||||
self.0.write_timeout()
|
||||
}
|
||||
|
||||
/// Sets the value of the `SO_BROADCAST` option for this socket.
|
||||
///
|
||||
/// When enabled, this socket is allowed to send packets to a broadcast
|
||||
/// address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_broadcast(false).expect("set_broadcast call failed");
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> {
|
||||
self.0.set_broadcast(broadcast)
|
||||
}
|
||||
|
||||
/// Gets the value of the `SO_BROADCAST` option for this socket.
|
||||
///
|
||||
/// For more information about this option, see [`UdpSocket::set_broadcast`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_broadcast(false).expect("set_broadcast call failed");
|
||||
/// assert_eq!(socket.broadcast().unwrap(), false);
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn broadcast(&self) -> io::Result<bool> {
|
||||
self.0.broadcast()
|
||||
}
|
||||
|
||||
/// Sets the value of the `IP_MULTICAST_LOOP` option for this socket.
|
||||
///
|
||||
/// If enabled, multicast packets will be looped back to the local socket.
|
||||
/// Note that this might not have any effect on IPv6 sockets.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed");
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> {
|
||||
self.0.set_multicast_loop_v4(multicast_loop_v4)
|
||||
}
|
||||
|
||||
/// Gets the value of the `IP_MULTICAST_LOOP` option for this socket.
|
||||
///
|
||||
/// For more information about this option, see [`UdpSocket::set_multicast_loop_v4`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed");
|
||||
/// assert_eq!(socket.multicast_loop_v4().unwrap(), false);
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
|
||||
self.0.multicast_loop_v4()
|
||||
}
|
||||
|
||||
/// Sets the value of the `IP_MULTICAST_TTL` option for this socket.
|
||||
///
|
||||
/// Indicates the time-to-live value of outgoing multicast packets for
|
||||
/// this socket. The default value is 1 which means that multicast packets
|
||||
/// don't leave the local network unless explicitly requested.
|
||||
///
|
||||
/// Note that this might not have any effect on IPv6 sockets.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed");
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> {
|
||||
self.0.set_multicast_ttl_v4(multicast_ttl_v4)
|
||||
}
|
||||
|
||||
/// Gets the value of the `IP_MULTICAST_TTL` option for this socket.
|
||||
///
|
||||
/// For more information about this option, see [`UdpSocket::set_multicast_ttl_v4`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed");
|
||||
/// assert_eq!(socket.multicast_ttl_v4().unwrap(), 42);
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
|
||||
self.0.multicast_ttl_v4()
|
||||
}
|
||||
|
||||
/// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket.
|
||||
///
|
||||
/// Controls whether this socket sees the multicast packets it sends itself.
|
||||
/// Note that this might not have any affect on IPv4 sockets.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed");
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> {
|
||||
self.0.set_multicast_loop_v6(multicast_loop_v6)
|
||||
}
|
||||
|
||||
/// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket.
|
||||
///
|
||||
/// For more information about this option, see [`UdpSocket::set_multicast_loop_v6`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed");
|
||||
/// assert_eq!(socket.multicast_loop_v6().unwrap(), false);
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
|
||||
self.0.multicast_loop_v6()
|
||||
}
|
||||
|
||||
/// Sets the value for the `IP_TTL` option on this socket.
|
||||
///
|
||||
/// This value sets the time-to-live field that is used in every packet sent
|
||||
/// from this socket.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_ttl(42).expect("set_ttl call failed");
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
|
||||
self.0.set_ttl(ttl)
|
||||
}
|
||||
|
||||
/// Gets the value of the `IP_TTL` option for this socket.
|
||||
///
|
||||
/// For more information about this option, see [`UdpSocket::set_ttl`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.set_ttl(42).expect("set_ttl call failed");
|
||||
/// assert_eq!(socket.ttl().unwrap(), 42);
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn ttl(&self) -> io::Result<u32> {
|
||||
self.0.ttl()
|
||||
}
|
||||
|
||||
/// Executes an operation of the `IP_ADD_MEMBERSHIP` type.
|
||||
///
|
||||
/// This function specifies a new multicast group for this socket to join.
|
||||
/// The address must be a valid multicast address, and `interface` is the
|
||||
/// address of the local interface with which the system should join the
|
||||
/// multicast group. If it's equal to [`UNSPECIFIED`](Ipv4Addr::UNSPECIFIED)
|
||||
/// then an appropriate interface is chosen by the system.
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
|
||||
self.0.join_multicast_v4(multiaddr, interface)
|
||||
}
|
||||
|
||||
/// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type.
|
||||
///
|
||||
/// This function specifies a new multicast group for this socket to join.
|
||||
/// The address must be a valid multicast address, and `interface` is the
|
||||
/// index of the interface to join/leave (or 0 to indicate any interface).
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
|
||||
self.0.join_multicast_v6(multiaddr, interface)
|
||||
}
|
||||
|
||||
/// Executes an operation of the `IP_DROP_MEMBERSHIP` type.
|
||||
///
|
||||
/// For more information about this option, see [`UdpSocket::join_multicast_v4`].
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
|
||||
self.0.leave_multicast_v4(multiaddr, interface)
|
||||
}
|
||||
|
||||
/// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type.
|
||||
///
|
||||
/// For more information about this option, see [`UdpSocket::join_multicast_v6`].
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
|
||||
self.0.leave_multicast_v6(multiaddr, interface)
|
||||
}
|
||||
|
||||
/// Gets the value of the `SO_ERROR` option on this socket.
|
||||
///
|
||||
/// This will retrieve the stored error in the underlying socket, clearing
|
||||
/// the field in the process. This can be useful for checking errors between
|
||||
/// calls.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// match socket.take_error() {
|
||||
/// Ok(Some(error)) => println!("UdpSocket error: {error:?}"),
|
||||
/// Ok(None) => println!("No error"),
|
||||
/// Err(error) => println!("UdpSocket.take_error failed: {error:?}"),
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
self.0.take_error()
|
||||
}
|
||||
|
||||
/// Connects this UDP socket to a remote address, allowing the `send` and
|
||||
/// `recv` syscalls to be used to send data and also applies filters to only
|
||||
/// receive data from the specified address.
|
||||
///
|
||||
/// If `addr` yields multiple addresses, `connect` will be attempted with
|
||||
/// each of the addresses until the underlying OS function returns no
|
||||
/// error. Note that usually, a successful `connect` call does not specify
|
||||
/// that there is a remote server listening on the port, rather, such an
|
||||
/// error would only be detected after the first send. If the OS returns an
|
||||
/// error for each of the specified addresses, the error returned from the
|
||||
/// last connection attempt (the last address) is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creates a UDP socket bound to `127.0.0.1:3400` and connect the socket to
|
||||
/// `127.0.0.1:8080`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:3400").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// ```
|
||||
///
|
||||
/// Unlike in the TCP case, passing an array of addresses to the `connect`
|
||||
/// function of a UDP socket is not a useful thing to do: The OS will be
|
||||
/// unable to determine whether something is listening on the remote
|
||||
/// address without the application sending data.
|
||||
///
|
||||
/// If your first `connect` is to a loopback address, subsequent
|
||||
/// `connect`s to non-loopback addresses might fail, depending
|
||||
/// on the platform.
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> {
|
||||
self.0.connect(addr)
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the remote address to which it is connected.
|
||||
/// On success, returns the number of bytes written. Note that the operating
|
||||
/// system may refuse buffers larger than 65507. However, partial writes are
|
||||
/// not possible until buffer sizes above `i32::MAX`.
|
||||
///
|
||||
/// [`UdpSocket::connect`] will connect this socket to a remote address. This
|
||||
/// method will fail if the socket is not connected.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// socket.send(&[0, 1, 2]).expect("couldn't send message");
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.send(buf)
|
||||
}
|
||||
|
||||
/// Receives a single datagram message on the socket from the remote address to
|
||||
/// which it is connected. On success, returns the number of bytes read.
|
||||
///
|
||||
/// The function must be called with valid byte array `buf` of sufficient size to
|
||||
/// hold the message bytes. If a message is too long to fit in the supplied buffer,
|
||||
/// excess bytes may be discarded.
|
||||
///
|
||||
/// [`UdpSocket::connect`] will connect this socket to a remote address. This
|
||||
/// method will fail if the socket is not connected.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// let mut buf = [0; 10];
|
||||
/// match socket.recv(&mut buf) {
|
||||
/// Ok(received) => println!("received {received} bytes {:?}", &buf[..received]),
|
||||
/// Err(e) => println!("recv function failed: {e:?}"),
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.recv(buf)
|
||||
}
|
||||
|
||||
/// Receives single datagram on the socket from the remote address to which it is
|
||||
/// connected, without removing the message from input queue. On success, returns
|
||||
/// the number of bytes peeked.
|
||||
///
|
||||
/// The function must be called with valid byte array `buf` of sufficient size to
|
||||
/// hold the message bytes. If a message is too long to fit in the supplied buffer,
|
||||
/// excess bytes may be discarded.
|
||||
///
|
||||
/// Successive calls return the same data. This is accomplished by passing
|
||||
/// `MSG_PEEK` as a flag to the underlying `recv` system call.
|
||||
///
|
||||
/// Do not use this function to implement busy waiting, instead use `libc::poll` to
|
||||
/// synchronize IO events on one or more sockets.
|
||||
///
|
||||
/// [`UdpSocket::connect`] will connect this socket to a remote address. This
|
||||
/// method will fail if the socket is not connected.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method will fail if the socket is not connected. The `connect` method
|
||||
/// will connect this socket to a remote address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
|
||||
/// socket.connect("127.0.0.1:8080").expect("connect function failed");
|
||||
/// let mut buf = [0; 10];
|
||||
/// match socket.peek(&mut buf) {
|
||||
/// Ok(received) => println!("received {received} bytes"),
|
||||
/// Err(e) => println!("peek function failed: {e:?}"),
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "peek", since = "1.18.0")]
|
||||
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.peek(buf)
|
||||
}
|
||||
|
||||
/// Moves this UDP socket into or out of nonblocking mode.
|
||||
///
|
||||
/// This will result in `recv`, `recv_from`, `send`, and `send_to` system
|
||||
/// operations becoming nonblocking, i.e., immediately returning from their
|
||||
/// calls. If the IO operation is successful, `Ok` is returned and no
|
||||
/// further action is required. If the IO operation could not be completed
|
||||
/// and needs to be retried, an error with kind
|
||||
/// [`io::ErrorKind::WouldBlock`] is returned.
|
||||
///
|
||||
/// On Unix platforms, calling this method corresponds to calling `fcntl`
|
||||
/// `FIONBIO`. On Windows calling this method corresponds to calling
|
||||
/// `ioctlsocket` `FIONBIO`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creates a UDP socket bound to `127.0.0.1:7878` and read bytes in
|
||||
/// nonblocking mode:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// use std::net::UdpSocket;
|
||||
///
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:7878").unwrap();
|
||||
/// socket.set_nonblocking(true).unwrap();
|
||||
///
|
||||
/// # fn wait_for_fd() { unimplemented!() }
|
||||
/// let mut buf = [0; 10];
|
||||
/// let (num_bytes_read, _) = loop {
|
||||
/// match socket.recv_from(&mut buf) {
|
||||
/// Ok(n) => break n,
|
||||
/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
/// // wait until network socket is ready, typically implemented
|
||||
/// // via platform-specific APIs such as epoll or IOCP
|
||||
/// wait_for_fd();
|
||||
/// }
|
||||
/// Err(e) => panic!("encountered IO error: {e}"),
|
||||
/// }
|
||||
/// };
|
||||
/// println!("bytes: {:?}", &buf[..num_bytes_read]);
|
||||
/// ```
|
||||
#[stable(feature = "net2_mutators", since = "1.9.0")]
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
||||
self.0.set_nonblocking(nonblocking)
|
||||
}
|
||||
}
|
||||
|
||||
// In addition to the `impl`s here, `UdpSocket` also has `impl`s for
|
||||
// `AsFd`/`From<OwnedFd>`/`Into<OwnedFd>` and
|
||||
// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and
|
||||
// `AsSocket`/`From<OwnedSocket>`/`Into<OwnedSocket>` and
|
||||
// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows.
|
||||
|
||||
impl AsInner<net_imp::UdpSocket> for UdpSocket {
|
||||
#[inline]
|
||||
fn as_inner(&self) -> &net_imp::UdpSocket {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromInner<net_imp::UdpSocket> for UdpSocket {
|
||||
fn from_inner(inner: net_imp::UdpSocket) -> UdpSocket {
|
||||
UdpSocket(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoInner<net_imp::UdpSocket> for UdpSocket {
|
||||
fn into_inner(self) -> net_imp::UdpSocket {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl fmt::Debug for UdpSocket {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
381
crates/std/src/net/udp/tests.rs
Normal file
381
crates/std/src/net/udp/tests.rs
Normal file
@@ -0,0 +1,381 @@
|
||||
use crate::io::ErrorKind;
|
||||
use crate::net::test::{compare_ignore_zoneid, next_test_ip4, next_test_ip6};
|
||||
use crate::net::*;
|
||||
use crate::sync::mpsc::channel;
|
||||
use crate::thread;
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
fn each_ip(f: &mut dyn FnMut(SocketAddr, SocketAddr)) {
|
||||
f(next_test_ip4(), next_test_ip4());
|
||||
f(next_test_ip6(), next_test_ip6());
|
||||
}
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(t) => t,
|
||||
Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bind_error() {
|
||||
match UdpSocket::bind("1.1.1.1:9999") {
|
||||
Ok(..) => panic!(),
|
||||
Err(e) => assert_eq!(e.kind(), ErrorKind::AddrNotAvailable),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn socket_smoke_test_ip4() {
|
||||
each_ip(&mut |server_ip, client_ip| {
|
||||
let (tx1, rx1) = channel();
|
||||
let (tx2, rx2) = channel();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let client = t!(UdpSocket::bind(&client_ip));
|
||||
rx1.recv().unwrap();
|
||||
t!(client.send_to(&[99], &server_ip));
|
||||
tx2.send(()).unwrap();
|
||||
});
|
||||
|
||||
let server = t!(UdpSocket::bind(&server_ip));
|
||||
tx1.send(()).unwrap();
|
||||
let mut buf = [0];
|
||||
let (nread, src) = t!(server.recv_from(&mut buf));
|
||||
assert_eq!(nread, 1);
|
||||
assert_eq!(buf[0], 99);
|
||||
assert_eq!(compare_ignore_zoneid(&src, &client_ip), true);
|
||||
rx2.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn socket_name() {
|
||||
each_ip(&mut |addr, _| {
|
||||
let server = t!(UdpSocket::bind(&addr));
|
||||
assert_eq!(addr, t!(server.local_addr()));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn socket_peer() {
|
||||
each_ip(&mut |addr1, addr2| {
|
||||
let server = t!(UdpSocket::bind(&addr1));
|
||||
assert_eq!(server.peer_addr().unwrap_err().kind(), ErrorKind::NotConnected);
|
||||
t!(server.connect(&addr2));
|
||||
assert_eq!(addr2, t!(server.peer_addr()));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn udp_clone_smoke() {
|
||||
each_ip(&mut |addr1, addr2| {
|
||||
let sock1 = t!(UdpSocket::bind(&addr1));
|
||||
let sock2 = t!(UdpSocket::bind(&addr2));
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let mut buf = [0, 0];
|
||||
let res = sock2.recv_from(&mut buf).unwrap();
|
||||
assert_eq!(res.0, 1);
|
||||
assert_eq!(compare_ignore_zoneid(&res.1, &addr1), true);
|
||||
assert_eq!(buf[0], 1);
|
||||
t!(sock2.send_to(&[2], &addr1));
|
||||
});
|
||||
|
||||
let sock3 = t!(sock1.try_clone());
|
||||
|
||||
let (tx1, rx1) = channel();
|
||||
let (tx2, rx2) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
rx1.recv().unwrap();
|
||||
t!(sock3.send_to(&[1], &addr2));
|
||||
tx2.send(()).unwrap();
|
||||
});
|
||||
tx1.send(()).unwrap();
|
||||
let mut buf = [0, 0];
|
||||
let res = sock1.recv_from(&mut buf).unwrap();
|
||||
assert_eq!(res.0, 1);
|
||||
assert_eq!(compare_ignore_zoneid(&res.1, &addr2), true);
|
||||
rx2.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn udp_clone_two_read() {
|
||||
each_ip(&mut |addr1, addr2| {
|
||||
let sock1 = t!(UdpSocket::bind(&addr1));
|
||||
let sock2 = t!(UdpSocket::bind(&addr2));
|
||||
let (tx1, rx) = channel();
|
||||
let tx2 = tx1.clone();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
t!(sock2.send_to(&[1], &addr1));
|
||||
rx.recv().unwrap();
|
||||
t!(sock2.send_to(&[2], &addr1));
|
||||
rx.recv().unwrap();
|
||||
});
|
||||
|
||||
let sock3 = t!(sock1.try_clone());
|
||||
|
||||
let (done, rx) = channel();
|
||||
let _t = thread::spawn(move || {
|
||||
let mut buf = [0, 0];
|
||||
t!(sock3.recv_from(&mut buf));
|
||||
tx2.send(()).unwrap();
|
||||
done.send(()).unwrap();
|
||||
});
|
||||
let mut buf = [0, 0];
|
||||
t!(sock1.recv_from(&mut buf));
|
||||
tx1.send(()).unwrap();
|
||||
|
||||
rx.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // no threads
|
||||
fn udp_clone_two_write() {
|
||||
each_ip(&mut |addr1, addr2| {
|
||||
let sock1 = t!(UdpSocket::bind(&addr1));
|
||||
let sock2 = t!(UdpSocket::bind(&addr2));
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let (serv_tx, serv_rx) = channel();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
let mut buf = [0, 1];
|
||||
rx.recv().unwrap();
|
||||
t!(sock2.recv_from(&mut buf));
|
||||
serv_tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
let sock3 = t!(sock1.try_clone());
|
||||
|
||||
let (done, rx) = channel();
|
||||
let tx2 = tx.clone();
|
||||
let _t = thread::spawn(move || {
|
||||
if sock3.send_to(&[1], &addr2).is_ok() {
|
||||
let _ = tx2.send(());
|
||||
}
|
||||
done.send(()).unwrap();
|
||||
});
|
||||
if sock1.send_to(&[2], &addr2).is_ok() {
|
||||
let _ = tx.send(());
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
rx.recv().unwrap();
|
||||
serv_rx.recv().unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug() {
|
||||
let name = if cfg!(windows) { "socket" } else { "fd" };
|
||||
let socket_addr = next_test_ip4();
|
||||
|
||||
let udpsock = t!(UdpSocket::bind(&socket_addr));
|
||||
let udpsock_inner = udpsock.0.socket().as_raw();
|
||||
let compare = format!("UdpSocket {{ addr: {socket_addr:?}, {name}: {udpsock_inner:?} }}");
|
||||
assert_eq!(format!("{udpsock:?}"), compare);
|
||||
}
|
||||
|
||||
// FIXME: re-enabled openbsd/netbsd tests once their socket timeout code
|
||||
// no longer has rounding errors.
|
||||
// VxWorks ignores SO_SNDTIMEO.
|
||||
#[cfg_attr(
|
||||
any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"),
|
||||
ignore
|
||||
)]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported
|
||||
#[test]
|
||||
fn timeouts() {
|
||||
let addr = next_test_ip4();
|
||||
|
||||
let stream = t!(UdpSocket::bind(&addr));
|
||||
let dur = Duration::new(15410, 0);
|
||||
|
||||
assert_eq!(None, t!(stream.read_timeout()));
|
||||
|
||||
t!(stream.set_read_timeout(Some(dur)));
|
||||
assert_eq!(Some(dur), t!(stream.read_timeout()));
|
||||
|
||||
assert_eq!(None, t!(stream.write_timeout()));
|
||||
|
||||
t!(stream.set_write_timeout(Some(dur)));
|
||||
assert_eq!(Some(dur), t!(stream.write_timeout()));
|
||||
|
||||
t!(stream.set_read_timeout(None));
|
||||
assert_eq!(None, t!(stream.read_timeout()));
|
||||
|
||||
t!(stream.set_write_timeout(None));
|
||||
assert_eq!(None, t!(stream.write_timeout()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported
|
||||
fn test_read_timeout() {
|
||||
let addr = next_test_ip4();
|
||||
|
||||
let stream = t!(UdpSocket::bind(&addr));
|
||||
t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
|
||||
|
||||
let mut buf = [0; 10];
|
||||
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
|
||||
if kind != ErrorKind::Interrupted {
|
||||
assert!(
|
||||
kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
|
||||
"unexpected_error: {:?}",
|
||||
kind
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(start.elapsed() > Duration::from_millis(400));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // timeout not supported
|
||||
fn test_read_with_timeout() {
|
||||
let addr = next_test_ip4();
|
||||
|
||||
let stream = t!(UdpSocket::bind(&addr));
|
||||
t!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
|
||||
|
||||
t!(stream.send_to(b"hello world", &addr));
|
||||
|
||||
let mut buf = [0; 11];
|
||||
t!(stream.recv_from(&mut buf));
|
||||
assert_eq!(b"hello world", &buf[..]);
|
||||
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
let kind = stream.recv_from(&mut buf).err().expect("expected error").kind();
|
||||
if kind != ErrorKind::Interrupted {
|
||||
assert!(
|
||||
kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
|
||||
"unexpected_error: {:?}",
|
||||
kind
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(start.elapsed() > Duration::from_millis(400));
|
||||
}
|
||||
|
||||
// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
|
||||
// when passed zero Durations
|
||||
#[test]
|
||||
fn test_timeout_zero_duration() {
|
||||
let addr = next_test_ip4();
|
||||
|
||||
let socket = t!(UdpSocket::bind(&addr));
|
||||
|
||||
let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
|
||||
let err = result.unwrap_err();
|
||||
assert_eq!(err.kind(), ErrorKind::InvalidInput);
|
||||
|
||||
let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
|
||||
let err = result.unwrap_err();
|
||||
assert_eq!(err.kind(), ErrorKind::InvalidInput);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn connect_send_recv() {
|
||||
let addr = next_test_ip4();
|
||||
|
||||
let socket = t!(UdpSocket::bind(&addr));
|
||||
t!(socket.connect(addr));
|
||||
|
||||
t!(socket.send(b"hello world"));
|
||||
|
||||
let mut buf = [0; 11];
|
||||
t!(socket.recv(&mut buf));
|
||||
assert_eq!(b"hello world", &buf[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // peek not supported
|
||||
fn connect_send_peek_recv() {
|
||||
each_ip(&mut |addr, _| {
|
||||
let socket = t!(UdpSocket::bind(&addr));
|
||||
t!(socket.connect(addr));
|
||||
|
||||
t!(socket.send(b"hello world"));
|
||||
|
||||
for _ in 1..3 {
|
||||
let mut buf = [0; 11];
|
||||
let size = t!(socket.peek(&mut buf));
|
||||
assert_eq!(b"hello world", &buf[..]);
|
||||
assert_eq!(size, 11);
|
||||
}
|
||||
|
||||
let mut buf = [0; 11];
|
||||
let size = t!(socket.recv(&mut buf));
|
||||
assert_eq!(b"hello world", &buf[..]);
|
||||
assert_eq!(size, 11);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "wasi", ignore)] // peek_from not supported
|
||||
fn peek_from() {
|
||||
each_ip(&mut |addr, _| {
|
||||
let socket = t!(UdpSocket::bind(&addr));
|
||||
t!(socket.send_to(b"hello world", &addr));
|
||||
|
||||
for _ in 1..3 {
|
||||
let mut buf = [0; 11];
|
||||
let (size, _) = t!(socket.peek_from(&mut buf));
|
||||
assert_eq!(b"hello world", &buf[..]);
|
||||
assert_eq!(size, 11);
|
||||
}
|
||||
|
||||
let mut buf = [0; 11];
|
||||
let (size, _) = t!(socket.recv_from(&mut buf));
|
||||
assert_eq!(b"hello world", &buf[..]);
|
||||
assert_eq!(size, 11);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ttl() {
|
||||
let ttl = 100;
|
||||
|
||||
let addr = next_test_ip4();
|
||||
|
||||
let stream = t!(UdpSocket::bind(&addr));
|
||||
|
||||
t!(stream.set_ttl(ttl));
|
||||
assert_eq!(ttl, t!(stream.ttl()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_nonblocking() {
|
||||
each_ip(&mut |addr, _| {
|
||||
let socket = t!(UdpSocket::bind(&addr));
|
||||
|
||||
t!(socket.set_nonblocking(true));
|
||||
t!(socket.set_nonblocking(false));
|
||||
|
||||
t!(socket.connect(addr));
|
||||
|
||||
t!(socket.set_nonblocking(false));
|
||||
t!(socket.set_nonblocking(true));
|
||||
|
||||
let mut buf = [0];
|
||||
match socket.recv(&mut buf) {
|
||||
Ok(_) => panic!("expected error"),
|
||||
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
|
||||
Err(e) => panic!("unexpected error {e}"),
|
||||
}
|
||||
})
|
||||
}
|
||||
1086
crates/std/src/num/f128.rs
Normal file
1086
crates/std/src/num/f128.rs
Normal file
File diff suppressed because it is too large
Load Diff
1047
crates/std/src/num/f16.rs
Normal file
1047
crates/std/src/num/f16.rs
Normal file
File diff suppressed because it is too large
Load Diff
1276
crates/std/src/num/f32.rs
Normal file
1276
crates/std/src/num/f32.rs
Normal file
File diff suppressed because it is too large
Load Diff
1276
crates/std/src/num/f64.rs
Normal file
1276
crates/std/src/num/f64.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -84,7 +84,7 @@
|
||||
use core::clone::CloneToUninit;
|
||||
|
||||
use crate::borrow::{Borrow, Cow};
|
||||
use alloc_crate::collections::TryReserveError;
|
||||
use crate::collections::TryReserveError;
|
||||
use crate::error::Error;
|
||||
use crate::ffi::{OsStr, OsString, os_str};
|
||||
use crate::hash::{Hash, Hasher};
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
pub mod rust_2024 {
|
||||
pub use crate::print;
|
||||
pub use crate::println;
|
||||
pub use alloc::format;
|
||||
pub use alloc::vec;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::borrow::ToOwned;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::boxed::Box;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::string::{String, ToString};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::vec::Vec;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::option::Option::{self, None, Some};
|
||||
|
||||
// Re-exported built-in macros and traits
|
||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||
#[doc(no_inline)]
|
||||
#[expect(deprecated)]
|
||||
pub use core::prelude::v1::{
|
||||
Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, assert, assert_eq,
|
||||
assert_ne, cfg, column, compile_error, concat, debug_assert, debug_assert_eq,
|
||||
debug_assert_ne, env, file, format_args, include, include_bytes, include_str, line,
|
||||
matches, module_path, option_env, stringify, todo, r#try, unimplemented, unreachable,
|
||||
write, writeln,
|
||||
};
|
||||
|
||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::thread_local;
|
||||
|
||||
#[stable(feature = "cfg_select", since = "1.95.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::cfg_select;
|
||||
|
||||
#[unstable(
|
||||
feature = "concat_bytes",
|
||||
issue = "87555",
|
||||
reason = "`concat_bytes` is not stable enough for use and is subject to change"
|
||||
)]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::concat_bytes;
|
||||
|
||||
#[unstable(feature = "const_format_args", issue = "none")]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::const_format_args;
|
||||
|
||||
#[unstable(
|
||||
feature = "log_syntax",
|
||||
issue = "29598",
|
||||
reason = "`log_syntax!` is not stable enough for use and is subject to change"
|
||||
)]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::log_syntax;
|
||||
|
||||
#[unstable(
|
||||
feature = "trace_macros",
|
||||
issue = "29598",
|
||||
reason = "`trace_macros` is not stable enough for use and is subject to change"
|
||||
)]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::trace_macros;
|
||||
|
||||
// Do not `doc(no_inline)` so that they become doc items on their own
|
||||
// (no public module for them to be re-exported from).
|
||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||
pub use core::prelude::v1::{
|
||||
alloc_error_handler, bench, derive, global_allocator, test, test_case,
|
||||
};
|
||||
|
||||
#[unstable(feature = "derive_const", issue = "118304")]
|
||||
pub use core::prelude::v1::derive_const;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "cfg_accessible",
|
||||
issue = "64797",
|
||||
reason = "`cfg_accessible` is not fully implemented"
|
||||
)]
|
||||
pub use core::prelude::v1::cfg_accessible;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "cfg_eval",
|
||||
issue = "82679",
|
||||
reason = "`cfg_eval` is a recently implemented feature"
|
||||
)]
|
||||
pub use core::prelude::v1::cfg_eval;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "type_ascription",
|
||||
issue = "23416",
|
||||
reason = "placeholder syntax for type ascription"
|
||||
)]
|
||||
pub use core::prelude::v1::type_ascribe;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "deref_patterns",
|
||||
issue = "87121",
|
||||
reason = "placeholder syntax for deref patterns"
|
||||
)]
|
||||
pub use core::prelude::v1::deref;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "type_alias_impl_trait",
|
||||
issue = "63063",
|
||||
reason = "`type_alias_impl_trait` has open design concerns"
|
||||
)]
|
||||
pub use core::prelude::v1::define_opaque;
|
||||
|
||||
#[unstable(feature = "extern_item_impls", issue = "125418")]
|
||||
pub use core::prelude::v1::{eii, unsafe_eii};
|
||||
|
||||
#[unstable(feature = "eii_internals", issue = "none")]
|
||||
pub use core::prelude::v1::eii_declaration;
|
||||
|
||||
#[stable(feature = "prelude_2021", since = "1.55.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::rust_2021::*;
|
||||
|
||||
#[stable(feature = "prelude_2024", since = "1.85.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::rust_2024::*;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::convert::{AsMut, AsRef, From, Into};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
struct GlobalAllocator;
|
||||
|
||||
#[core::prelude::v1::global_allocator]
|
||||
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
|
||||
|
||||
unsafe impl core::alloc::GlobalAlloc for GlobalAllocator {
|
||||
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
|
||||
crate::syscall::alloc(layout)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
|
||||
crate::syscall::dealloc(ptr, layout)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// `argc` and `argv` are passed by the kernel
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize {
|
||||
unsafe extern "Rust" {
|
||||
fn main(argc: isize, argv: *const *const u8) -> isize;
|
||||
}
|
||||
|
||||
unsafe { main(argc, argv) }
|
||||
}
|
||||
}
|
||||
192
crates/std/src/prelude/mod.rs
Normal file
192
crates/std/src/prelude/mod.rs
Normal file
@@ -0,0 +1,192 @@
|
||||
//! # The Rust Prelude
|
||||
//!
|
||||
//! Rust comes with a variety of things in its standard library. However, if
|
||||
//! you had to manually import every single thing that you used, it would be
|
||||
//! very verbose. But importing a lot of things that a program never uses isn't
|
||||
//! good either. A balance needs to be struck.
|
||||
//!
|
||||
//! The *prelude* is the list of things that Rust automatically imports into
|
||||
//! every Rust program. It's kept as small as possible, and is focused on
|
||||
//! things, particularly traits, which are used in almost every single Rust
|
||||
//! program.
|
||||
//!
|
||||
//! # Other preludes
|
||||
//!
|
||||
//! Preludes can be seen as a pattern to make using multiple types more
|
||||
//! convenient. As such, you'll find other preludes in the standard library,
|
||||
//! such as [`std::io::prelude`]. Various libraries in the Rust ecosystem may
|
||||
//! also define their own preludes.
|
||||
//!
|
||||
//! [`std::io::prelude`]: crate::io::prelude
|
||||
//!
|
||||
//! The difference between 'the prelude' and these other preludes is that they
|
||||
//! are not automatically `use`'d, and must be imported manually. This is still
|
||||
//! easier than importing all of their constituent components.
|
||||
//!
|
||||
//! # Prelude contents
|
||||
//!
|
||||
//! The items included in the prelude depend on the edition of the crate.
|
||||
//! The first version of the prelude is used in Rust 2015 and Rust 2018,
|
||||
//! and lives in [`std::prelude::v1`].
|
||||
//! [`std::prelude::rust_2015`] and [`std::prelude::rust_2018`] re-export this prelude.
|
||||
//! It re-exports the following:
|
||||
//!
|
||||
//! * <code>[std::marker]::{[Copy], [Send], [Sized], [Sync], [Unpin]}</code>,
|
||||
//! marker traits that indicate fundamental properties of types.
|
||||
//! * <code>[std::ops]::{[Fn], [FnMut], [FnOnce]}</code>, and their analogous
|
||||
//! async traits, <code>[std::ops]::{[AsyncFn], [AsyncFnMut], [AsyncFnOnce]}</code>.
|
||||
//! * <code>[std::ops]::[Drop]</code>, for implementing destructors.
|
||||
//! * <code>[std::mem]::[drop]</code>, a convenience function for explicitly
|
||||
//! dropping a value.
|
||||
//! * <code>[std::mem]::{[size_of], [size_of_val]}</code>, to get the size of
|
||||
//! a type or value.
|
||||
//! * <code>[std::mem]::{[align_of], [align_of_val]}</code>, to get the
|
||||
//! alignment of a type or value.
|
||||
//! * <code>[std::boxed]::[Box]</code>, a way to allocate values on the heap.
|
||||
//! * <code>[std::borrow]::[ToOwned]</code>, the conversion trait that defines
|
||||
//! [`to_owned`], the generic method for creating an owned type from a
|
||||
//! borrowed type.
|
||||
//! * <code>[std::clone]::[Clone]</code>, the ubiquitous trait that defines
|
||||
//! [`clone`][Clone::clone], the method for producing a copy of a value.
|
||||
//! * <code>[std::cmp]::{[PartialEq], [PartialOrd], [Eq], [Ord]}</code>, the
|
||||
//! comparison traits, which implement the comparison operators and are often
|
||||
//! seen in trait bounds.
|
||||
//! * <code>[std::convert]::{[AsRef], [AsMut], [Into], [From]}</code>, generic
|
||||
//! conversions, used by savvy API authors to create overloaded methods.
|
||||
//! * <code>[std::default]::[Default]</code>, types that have default values.
|
||||
//! * <code>[std::iter]::{[Iterator], [Extend], [IntoIterator], [DoubleEndedIterator],
|
||||
//! [ExactSizeIterator]}</code>, iterators of various kinds.
|
||||
//! * Most of the standard macros.
|
||||
//! * <code>[std::option]::[Option]::{[self][Option], [Some], [None]}</code>, a
|
||||
//! type which expresses the presence or absence of a value. This type is so
|
||||
//! commonly used, its variants are also exported.
|
||||
//! * <code>[std::result]::[Result]::{[self][Result], [Ok], [Err]}</code>, a type
|
||||
//! for functions that may succeed or fail. Like [`Option`], its variants are
|
||||
//! exported as well.
|
||||
//! * <code>[std::string]::{[String], [ToString]}</code>, heap-allocated strings.
|
||||
//! * <code>[std::vec]::[Vec]</code>, a growable, heap-allocated vector.
|
||||
//!
|
||||
//! The prelude used in Rust 2021, [`std::prelude::rust_2021`], includes all of the above,
|
||||
//! and in addition re-exports:
|
||||
//!
|
||||
//! * <code>[std::convert]::{[TryFrom], [TryInto]}</code>.
|
||||
//! * <code>[std::iter]::[FromIterator]</code>.
|
||||
//!
|
||||
//! The prelude used in Rust 2024, [`std::prelude::rust_2024`], includes all of the above,
|
||||
//! and in addition re-exports:
|
||||
//!
|
||||
//! * <code>[std::future]::{[Future], [IntoFuture]}</code>.
|
||||
//!
|
||||
//! [std::borrow]: crate::borrow
|
||||
//! [std::boxed]: crate::boxed
|
||||
//! [std::clone]: crate::clone
|
||||
//! [std::cmp]: crate::cmp
|
||||
//! [std::convert]: crate::convert
|
||||
//! [std::default]: crate::default
|
||||
//! [std::future]: crate::future
|
||||
//! [std::iter]: crate::iter
|
||||
//! [std::marker]: crate::marker
|
||||
//! [std::mem]: crate::mem
|
||||
//! [std::ops]: crate::ops
|
||||
//! [std::option]: crate::option
|
||||
//! [`std::prelude::v1`]: v1
|
||||
//! [`std::prelude::rust_2015`]: rust_2015
|
||||
//! [`std::prelude::rust_2018`]: rust_2018
|
||||
//! [`std::prelude::rust_2021`]: rust_2021
|
||||
//! [`std::prelude::rust_2024`]: rust_2024
|
||||
//! [std::result]: crate::result
|
||||
//! [std::slice]: crate::slice
|
||||
//! [std::string]: crate::string
|
||||
//! [std::vec]: mod@crate::vec
|
||||
//! [`to_owned`]: crate::borrow::ToOwned::to_owned
|
||||
//! [book-closures]: ../../book/ch13-01-closures.html
|
||||
//! [book-dtor]: ../../book/ch15-03-drop.html
|
||||
//! [book-enums]: ../../book/ch06-01-defining-an-enum.html
|
||||
//! [book-iter]: ../../book/ch13-02-iterators.html
|
||||
//! [Future]: crate::future::Future
|
||||
//! [IntoFuture]: crate::future::IntoFuture
|
||||
|
||||
// No formatting: this file is nothing but re-exports, and their order is worth preserving.
|
||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
pub mod v1;
|
||||
|
||||
/// The 2015 version of the prelude of The Rust Standard Library.
|
||||
///
|
||||
/// See the [module-level documentation](self) for more.
|
||||
#[stable(feature = "prelude_2015", since = "1.55.0")]
|
||||
pub mod rust_2015 {
|
||||
#[stable(feature = "prelude_2015", since = "1.55.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use super::v1::*;
|
||||
}
|
||||
|
||||
/// The 2018 version of the prelude of The Rust Standard Library.
|
||||
///
|
||||
/// See the [module-level documentation](self) for more.
|
||||
#[stable(feature = "prelude_2018", since = "1.55.0")]
|
||||
pub mod rust_2018 {
|
||||
#[stable(feature = "prelude_2018", since = "1.55.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use super::v1::*;
|
||||
}
|
||||
|
||||
/// The 2021 version of the prelude of The Rust Standard Library.
|
||||
///
|
||||
/// See the [module-level documentation](self) for more.
|
||||
#[stable(feature = "prelude_2021", since = "1.55.0")]
|
||||
pub mod rust_2021 {
|
||||
#[stable(feature = "prelude_2021", since = "1.55.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use super::v1::*;
|
||||
|
||||
#[stable(feature = "prelude_2021", since = "1.55.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::rust_2021::*;
|
||||
|
||||
// There are two different panic macros, one in `core` and one in `std`. They are slightly
|
||||
// different. For `std` we explicitly want the one defined in `std`.
|
||||
#[stable(feature = "prelude_2021", since = "1.55.0")]
|
||||
pub use super::v1::panic;
|
||||
}
|
||||
|
||||
/// The 2024 version of the prelude of The Rust Standard Library.
|
||||
///
|
||||
/// See the [module-level documentation](self) for more.
|
||||
#[stable(feature = "prelude_2024", since = "1.85.0")]
|
||||
pub mod rust_2024 {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use super::v1::*;
|
||||
|
||||
#[stable(feature = "prelude_2024", since = "1.85.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::rust_2024::*;
|
||||
|
||||
// There are two different panic macros, one in `core` and one in `std`. They are slightly
|
||||
// different. For `std` we explicitly want the one defined in `std`.
|
||||
#[stable(feature = "prelude_2024", since = "1.85.0")]
|
||||
pub use super::v1::panic;
|
||||
}
|
||||
|
||||
/// The Future version of the prelude of The Rust Standard Library.
|
||||
///
|
||||
/// See the [module-level documentation](self) for more.
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "prelude_future", issue = "none")]
|
||||
pub mod rust_future {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use super::v1::*;
|
||||
|
||||
#[unstable(feature = "prelude_next", issue = "none")]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::rust_future::*;
|
||||
|
||||
// There are two different panic macros, one in `core` and one in `std`. They are slightly
|
||||
// different. For `std` we explicitly want the one defined in `std`.
|
||||
#[unstable(feature = "prelude_next", issue = "none")]
|
||||
pub use super::v1::panic;
|
||||
}
|
||||
186
crates/std/src/prelude/v1.rs
Normal file
186
crates/std/src/prelude/v1.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
//! The first version of the prelude of The Rust Standard Library.
|
||||
//!
|
||||
//! See the [module-level documentation](super) for more.
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
// No formatting: this file is nothing but re-exports, and their order is worth preserving.
|
||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
||||
|
||||
// Re-exported core operators
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::marker::{Send, Sized, Sync, Unpin};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::ops::{Drop, Fn, FnMut, FnOnce};
|
||||
#[stable(feature = "async_closure", since = "1.85.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce};
|
||||
|
||||
// Re-exported functions
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::mem::drop;
|
||||
#[stable(feature = "size_of_prelude", since = "1.80.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::mem::{align_of, align_of_val, size_of, size_of_val};
|
||||
|
||||
// Re-exported types and traits
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::convert::{AsMut, AsRef, From, Into};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::iter::{Extend, IntoIterator, Iterator};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::option::Option::{self, None, Some};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::result::Result::{self, Err, Ok};
|
||||
|
||||
// Re-exported built-in macros and traits
|
||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||
#[doc(no_inline)]
|
||||
#[expect(deprecated)]
|
||||
pub use core::prelude::v1::{
|
||||
assert, assert_eq, assert_ne, cfg, column, compile_error, concat, debug_assert, debug_assert_eq,
|
||||
debug_assert_ne, env, file, format_args, include, include_bytes, include_str, line, matches,
|
||||
module_path, option_env, stringify, todo, r#try, unimplemented, unreachable, write,
|
||||
writeln, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd,
|
||||
};
|
||||
|
||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
dbg, eprint, eprintln, format, is_x86_feature_detected, print, println, thread_local
|
||||
};
|
||||
|
||||
// These macros need special handling, so that we don't export them *and* the modules of the same
|
||||
// name. We only want the macros in the prelude so we shadow the original modules with private
|
||||
// modules with the same names.
|
||||
mod ambiguous_macros_only {
|
||||
#[expect(hidden_glob_reexports)]
|
||||
mod vec {}
|
||||
#[expect(hidden_glob_reexports)]
|
||||
mod panic {}
|
||||
// Building std without the expect exported_private_dependencies will create warnings, but then
|
||||
// clippy claims its a useless_attribute. So silence both.
|
||||
#[expect(clippy::useless_attribute)]
|
||||
#[expect(exported_private_dependencies)]
|
||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||
pub use crate::*;
|
||||
}
|
||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use self::ambiguous_macros_only::{vec, panic};
|
||||
|
||||
#[stable(feature = "cfg_select", since = "1.95.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::cfg_select;
|
||||
|
||||
#[unstable(
|
||||
feature = "concat_bytes",
|
||||
issue = "87555",
|
||||
reason = "`concat_bytes` is not stable enough for use and is subject to change"
|
||||
)]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::concat_bytes;
|
||||
|
||||
#[unstable(feature = "const_format_args", issue = "none")]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::const_format_args;
|
||||
|
||||
#[unstable(
|
||||
feature = "log_syntax",
|
||||
issue = "29598",
|
||||
reason = "`log_syntax!` is not stable enough for use and is subject to change"
|
||||
)]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::log_syntax;
|
||||
|
||||
#[unstable(
|
||||
feature = "trace_macros",
|
||||
issue = "29598",
|
||||
reason = "`trace_macros` is not stable enough for use and is subject to change"
|
||||
)]
|
||||
#[doc(no_inline)]
|
||||
pub use core::prelude::v1::trace_macros;
|
||||
|
||||
// Do not `doc(no_inline)` so that they become doc items on their own
|
||||
// (no public module for them to be re-exported from).
|
||||
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||
pub use core::prelude::v1::{
|
||||
alloc_error_handler, bench, derive, global_allocator, test, test_case,
|
||||
};
|
||||
|
||||
#[unstable(feature = "derive_const", issue = "118304")]
|
||||
pub use core::prelude::v1::derive_const;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "cfg_accessible",
|
||||
issue = "64797",
|
||||
reason = "`cfg_accessible` is not fully implemented"
|
||||
)]
|
||||
pub use core::prelude::v1::cfg_accessible;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "cfg_eval",
|
||||
issue = "82679",
|
||||
reason = "`cfg_eval` is a recently implemented feature"
|
||||
)]
|
||||
pub use core::prelude::v1::cfg_eval;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "type_ascription",
|
||||
issue = "23416",
|
||||
reason = "placeholder syntax for type ascription"
|
||||
)]
|
||||
pub use core::prelude::v1::type_ascribe;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "deref_patterns",
|
||||
issue = "87121",
|
||||
reason = "placeholder syntax for deref patterns"
|
||||
)]
|
||||
pub use core::prelude::v1::deref;
|
||||
|
||||
// Do not `doc(no_inline)` either.
|
||||
#[unstable(
|
||||
feature = "type_alias_impl_trait",
|
||||
issue = "63063",
|
||||
reason = "`type_alias_impl_trait` has open design concerns"
|
||||
)]
|
||||
pub use core::prelude::v1::define_opaque;
|
||||
|
||||
#[unstable(feature = "extern_item_impls", issue = "125418")]
|
||||
pub use core::prelude::v1::{eii, unsafe_eii};
|
||||
|
||||
#[unstable(feature = "eii_internals", issue = "none")]
|
||||
pub use core::prelude::v1::eii_declaration;
|
||||
|
||||
// The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated
|
||||
// rather than glob imported because we want docs to show these re-exports as
|
||||
// pointing to within `std`.
|
||||
// Below are the items from the alloc crate.
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::borrow::ToOwned;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::boxed::Box;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::string::{String, ToString};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::vec::Vec;
|
||||
669
crates/std/src/process/tests.rs
Normal file
669
crates/std/src/process/tests.rs
Normal file
@@ -0,0 +1,669 @@
|
||||
use super::{Command, Output, Stdio};
|
||||
use crate::io::prelude::*;
|
||||
use crate::io::{BorrowedBuf, ErrorKind};
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::str;
|
||||
|
||||
fn known_command() -> Command {
|
||||
if cfg!(windows) {
|
||||
Command::new("help")
|
||||
} else if cfg!(all(target_vendor = "apple", not(target_os = "macos"))) {
|
||||
// iOS/tvOS/watchOS/visionOS have a very limited set of commandline
|
||||
// binaries available.
|
||||
Command::new("log")
|
||||
} else {
|
||||
Command::new("echo")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
fn shell_cmd() -> Command {
|
||||
Command::new("/system/bin/sh")
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
fn shell_cmd() -> Command {
|
||||
Command::new("/bin/sh")
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn smoke() {
|
||||
let p = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", "exit 0"]).spawn()
|
||||
} else {
|
||||
shell_cmd().arg("-c").arg("true").spawn()
|
||||
};
|
||||
assert!(p.is_ok());
|
||||
let mut p = p.unwrap();
|
||||
assert!(p.wait().unwrap().success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "android", ignore)]
|
||||
fn smoke_failure() {
|
||||
match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
|
||||
Ok(..) => panic!(),
|
||||
Err(..) => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn exit_reported_right() {
|
||||
let p = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", "exit 1"]).spawn()
|
||||
} else {
|
||||
shell_cmd().arg("-c").arg("false").spawn()
|
||||
};
|
||||
assert!(p.is_ok());
|
||||
let mut p = p.unwrap();
|
||||
assert!(p.wait().unwrap().code() == Some(1));
|
||||
drop(p.wait());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn signal_reported_right() {
|
||||
use crate::os::unix::process::ExitStatusExt;
|
||||
|
||||
let mut p = shell_cmd().arg("-c").arg("read a").stdin(Stdio::piped()).spawn().unwrap();
|
||||
p.kill().unwrap();
|
||||
match p.wait().unwrap().signal() {
|
||||
Some(9) => {}
|
||||
result => panic!("not terminated by signal 9 (instead, {result:?})"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_output(mut cmd: Command) -> String {
|
||||
let p = cmd.spawn();
|
||||
assert!(p.is_ok());
|
||||
let mut p = p.unwrap();
|
||||
assert!(p.stdout.is_some());
|
||||
let mut ret = String::new();
|
||||
p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap();
|
||||
assert!(p.wait().unwrap().success());
|
||||
return ret;
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn stdout_works() {
|
||||
if cfg!(target_os = "windows") {
|
||||
let mut cmd = Command::new("cmd");
|
||||
cmd.args(&["/C", "echo foobar"]).stdout(Stdio::piped());
|
||||
assert_eq!(run_output(cmd), "foobar\r\n");
|
||||
} else {
|
||||
let mut cmd = shell_cmd();
|
||||
cmd.arg("-c").arg("echo foobar").stdout(Stdio::piped());
|
||||
assert_eq!(run_output(cmd), "foobar\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn set_current_dir_works() {
|
||||
// On many Unix platforms this will use the posix_spawn path.
|
||||
let mut cmd = shell_cmd();
|
||||
cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped());
|
||||
assert_eq!(run_output(cmd), "/\n");
|
||||
|
||||
// Also test the fork/exec path by setting a pre_exec function.
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use crate::os::unix::process::CommandExt;
|
||||
|
||||
let mut cmd = shell_cmd();
|
||||
cmd.arg("-c").arg("pwd").current_dir("/").stdout(Stdio::piped());
|
||||
unsafe {
|
||||
cmd.pre_exec(|| Ok(()));
|
||||
}
|
||||
assert_eq!(run_output(cmd), "/\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn stdin_works() {
|
||||
let mut p = shell_cmd()
|
||||
.arg("-c")
|
||||
.arg("read line; echo $line")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap();
|
||||
drop(p.stdin.take());
|
||||
let mut out = String::new();
|
||||
p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap();
|
||||
assert!(p.wait().unwrap().success());
|
||||
assert_eq!(out, "foobar\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn child_stdout_read_buf() {
|
||||
let mut cmd = if cfg!(target_os = "windows") {
|
||||
let mut cmd = Command::new("cmd");
|
||||
cmd.arg("/C").arg("echo abc");
|
||||
cmd
|
||||
} else {
|
||||
let mut cmd = shell_cmd();
|
||||
cmd.arg("-c").arg("echo abc");
|
||||
cmd
|
||||
};
|
||||
cmd.stdin(Stdio::null());
|
||||
cmd.stdout(Stdio::piped());
|
||||
let child = cmd.spawn().unwrap();
|
||||
|
||||
let mut stdout = child.stdout.unwrap();
|
||||
let mut buf: [MaybeUninit<u8>; 128] = [MaybeUninit::uninit(); 128];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
stdout.read_buf(buf.unfilled()).unwrap();
|
||||
|
||||
// ChildStdout::read_buf should omit buffer initialization.
|
||||
if cfg!(target_os = "windows") {
|
||||
assert_eq!(buf.filled(), b"abc\r\n");
|
||||
assert_eq!(buf.init_len(), 5);
|
||||
} else {
|
||||
assert_eq!(buf.filled(), b"abc\n");
|
||||
assert_eq!(buf.init_len(), 4);
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn test_process_status() {
|
||||
let mut status = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap()
|
||||
} else {
|
||||
shell_cmd().arg("-c").arg("false").status().unwrap()
|
||||
};
|
||||
assert!(status.code() == Some(1));
|
||||
|
||||
status = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", "exit 0"]).status().unwrap()
|
||||
} else {
|
||||
shell_cmd().arg("-c").arg("true").status().unwrap()
|
||||
};
|
||||
assert!(status.success());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_output_fail_to_start() {
|
||||
match Command::new("/no-binary-by-this-name-should-exist").output() {
|
||||
Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound),
|
||||
Ok(..) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn test_process_output_output() {
|
||||
let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap()
|
||||
} else {
|
||||
shell_cmd().arg("-c").arg("echo hello").output().unwrap()
|
||||
};
|
||||
let output_str = str::from_utf8(&stdout).unwrap();
|
||||
|
||||
assert!(status.success());
|
||||
assert_eq!(output_str.trim().to_string(), "hello");
|
||||
assert_eq!(stderr, Vec::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn test_process_output_error() {
|
||||
let Output { status, stdout, stderr } = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap()
|
||||
} else {
|
||||
Command::new("mkdir").arg("./").output().unwrap()
|
||||
};
|
||||
|
||||
assert!(status.code().is_some());
|
||||
assert!(status.code() != Some(0));
|
||||
assert_eq!(stdout, Vec::new());
|
||||
assert!(!stderr.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn test_finish_once() {
|
||||
let mut prog = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
|
||||
} else {
|
||||
shell_cmd().arg("-c").arg("false").spawn().unwrap()
|
||||
};
|
||||
assert!(prog.wait().unwrap().code() == Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn test_finish_twice() {
|
||||
let mut prog = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap()
|
||||
} else {
|
||||
shell_cmd().arg("-c").arg("false").spawn().unwrap()
|
||||
};
|
||||
assert!(prog.wait().unwrap().code() == Some(1));
|
||||
assert!(prog.wait().unwrap().code() == Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn test_wait_with_output_once() {
|
||||
let prog = if cfg!(target_os = "windows") {
|
||||
Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap()
|
||||
} else {
|
||||
shell_cmd().arg("-c").arg("echo hello").stdout(Stdio::piped()).spawn().unwrap()
|
||||
};
|
||||
|
||||
let Output { status, stdout, stderr } = prog.wait_with_output().unwrap();
|
||||
let output_str = str::from_utf8(&stdout).unwrap();
|
||||
|
||||
assert!(status.success());
|
||||
assert_eq!(output_str.trim().to_string(), "hello");
|
||||
assert_eq!(stderr, Vec::new());
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
pub fn env_cmd() -> Command {
|
||||
Command::new("env")
|
||||
}
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn env_cmd() -> Command {
|
||||
let mut cmd = Command::new("/system/bin/sh");
|
||||
cmd.arg("-c").arg("set");
|
||||
cmd
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn env_cmd() -> Command {
|
||||
let mut cmd = Command::new("cmd");
|
||||
cmd.arg("/c").arg("set");
|
||||
cmd
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn test_override_env() {
|
||||
use crate::env;
|
||||
|
||||
// In some build environments (such as chrooted Nix builds), `env` can
|
||||
// only be found in the explicitly-provided PATH env variable, not in
|
||||
// default places such as /bin or /usr/bin. So we need to pass through
|
||||
// PATH to our sub-process.
|
||||
let mut cmd = env_cmd();
|
||||
cmd.env_clear().env("RUN_TEST_NEW_ENV", "123");
|
||||
if let Some(p) = env::var_os("PATH") {
|
||||
cmd.env("PATH", &p);
|
||||
}
|
||||
let result = cmd.output().unwrap();
|
||||
let output = String::from_utf8_lossy(&result.stdout).to_string();
|
||||
|
||||
assert!(
|
||||
output.contains("RUN_TEST_NEW_ENV=123"),
|
||||
"didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn test_add_to_env() {
|
||||
let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap();
|
||||
let output = String::from_utf8_lossy(&result.stdout).to_string();
|
||||
|
||||
assert!(
|
||||
output.contains("RUN_TEST_NEW_ENV=123"),
|
||||
"didn't find RUN_TEST_NEW_ENV inside of:\n\n{output}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no shell available"
|
||||
)]
|
||||
fn test_capture_env_at_spawn() {
|
||||
use crate::env;
|
||||
|
||||
let mut cmd = env_cmd();
|
||||
cmd.env("RUN_TEST_NEW_ENV1", "123");
|
||||
|
||||
// This variable will not be present if the environment has already
|
||||
// been captured above.
|
||||
unsafe {
|
||||
env::set_var("RUN_TEST_NEW_ENV2", "456");
|
||||
}
|
||||
let result = cmd.output().unwrap();
|
||||
unsafe {
|
||||
env::remove_var("RUN_TEST_NEW_ENV2");
|
||||
}
|
||||
|
||||
let output = String::from_utf8_lossy(&result.stdout).to_string();
|
||||
|
||||
assert!(
|
||||
output.contains("RUN_TEST_NEW_ENV1=123"),
|
||||
"didn't find RUN_TEST_NEW_ENV1 inside of:\n\n{output}"
|
||||
);
|
||||
assert!(
|
||||
output.contains("RUN_TEST_NEW_ENV2=456"),
|
||||
"didn't find RUN_TEST_NEW_ENV2 inside of:\n\n{output}"
|
||||
);
|
||||
}
|
||||
|
||||
// Regression tests for #30858.
|
||||
#[test]
|
||||
fn test_interior_nul_in_progname_is_error() {
|
||||
match Command::new("has-some-\0\0s-inside").spawn() {
|
||||
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
||||
Ok(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interior_nul_in_arg_is_error() {
|
||||
match known_command().arg("has-some-\0\0s-inside").spawn() {
|
||||
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
||||
Ok(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interior_nul_in_args_is_error() {
|
||||
match known_command().args(&["has-some-\0\0s-inside"]).spawn() {
|
||||
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
||||
Ok(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interior_nul_in_current_dir_is_error() {
|
||||
match known_command().current_dir("has-some-\0\0s-inside").spawn() {
|
||||
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
||||
Ok(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Regression tests for #30862.
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no `env` cmd available"
|
||||
)]
|
||||
fn test_interior_nul_in_env_key_is_error() {
|
||||
match env_cmd().env("has-some-\0\0s-inside", "value").spawn() {
|
||||
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
||||
Ok(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))),
|
||||
ignore = "no `env` cmd available"
|
||||
)]
|
||||
fn test_interior_nul_in_env_value_is_error() {
|
||||
match env_cmd().env("key", "has-some-\0\0s-inside").spawn() {
|
||||
Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput),
|
||||
Ok(_) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_command_implements_send_sync() {
|
||||
fn take_send_sync_type<T: Send + Sync>(_: T) {}
|
||||
take_send_sync_type(Command::new(""))
|
||||
}
|
||||
|
||||
// Ensure that starting a process with no environment variables works on Windows.
|
||||
// This will fail if the environment block is ill-formed.
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn env_empty() {
|
||||
let p = Command::new("cmd").args(&["/C", "exit 0"]).env_clear().spawn();
|
||||
assert!(p.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)]
|
||||
fn debug_print() {
|
||||
const PIDFD: &'static str =
|
||||
if cfg!(target_os = "linux") { " create_pidfd: false,\n" } else { "" };
|
||||
|
||||
let mut command = Command::new("some-boring-name");
|
||||
|
||||
assert_eq!(format!("{command:?}"), format!(r#""some-boring-name""#));
|
||||
|
||||
assert_eq!(
|
||||
format!("{command:#?}"),
|
||||
format!(
|
||||
r#"Command {{
|
||||
program: "some-boring-name",
|
||||
args: [
|
||||
"some-boring-name",
|
||||
],
|
||||
{PIDFD}}}"#
|
||||
)
|
||||
);
|
||||
|
||||
command.args(&["1", "2", "3"]);
|
||||
|
||||
assert_eq!(format!("{command:?}"), format!(r#""some-boring-name" "1" "2" "3""#));
|
||||
|
||||
assert_eq!(
|
||||
format!("{command:#?}"),
|
||||
format!(
|
||||
r#"Command {{
|
||||
program: "some-boring-name",
|
||||
args: [
|
||||
"some-boring-name",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
],
|
||||
{PIDFD}}}"#
|
||||
)
|
||||
);
|
||||
|
||||
crate::os::unix::process::CommandExt::arg0(&mut command, "exciting-name");
|
||||
|
||||
assert_eq!(
|
||||
format!("{command:?}"),
|
||||
format!(r#"["some-boring-name"] "exciting-name" "1" "2" "3""#)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
format!("{command:#?}"),
|
||||
format!(
|
||||
r#"Command {{
|
||||
program: "some-boring-name",
|
||||
args: [
|
||||
"exciting-name",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
],
|
||||
{PIDFD}}}"#
|
||||
)
|
||||
);
|
||||
|
||||
let mut command_with_env_and_cwd = Command::new("boring-name");
|
||||
command_with_env_and_cwd.current_dir("/some/path").env("FOO", "bar");
|
||||
assert_eq!(
|
||||
format!("{command_with_env_and_cwd:?}"),
|
||||
r#"cd "/some/path" && FOO="bar" "boring-name""#
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{command_with_env_and_cwd:#?}"),
|
||||
format!(
|
||||
r#"Command {{
|
||||
program: "boring-name",
|
||||
args: [
|
||||
"boring-name",
|
||||
],
|
||||
env: CommandEnv {{
|
||||
clear: false,
|
||||
vars: {{
|
||||
"FOO": Some(
|
||||
"bar",
|
||||
),
|
||||
}},
|
||||
}},
|
||||
cwd: Some(
|
||||
"/some/path",
|
||||
),
|
||||
{PIDFD}}}"#
|
||||
)
|
||||
);
|
||||
|
||||
let mut command_with_removed_env = Command::new("boring-name");
|
||||
command_with_removed_env.env_remove("FOO").env_remove("BAR");
|
||||
assert_eq!(format!("{command_with_removed_env:?}"), r#"env -u BAR -u FOO "boring-name""#);
|
||||
assert_eq!(
|
||||
format!("{command_with_removed_env:#?}"),
|
||||
format!(
|
||||
r#"Command {{
|
||||
program: "boring-name",
|
||||
args: [
|
||||
"boring-name",
|
||||
],
|
||||
env: CommandEnv {{
|
||||
clear: false,
|
||||
vars: {{
|
||||
"BAR": None,
|
||||
"FOO": None,
|
||||
}},
|
||||
}},
|
||||
{PIDFD}}}"#
|
||||
)
|
||||
);
|
||||
|
||||
let mut command_with_cleared_env = Command::new("boring-name");
|
||||
command_with_cleared_env.env_clear().env("BAR", "val").env_remove("FOO");
|
||||
assert_eq!(format!("{command_with_cleared_env:?}"), r#"env -i BAR="val" "boring-name""#);
|
||||
assert_eq!(
|
||||
format!("{command_with_cleared_env:#?}"),
|
||||
format!(
|
||||
r#"Command {{
|
||||
program: "boring-name",
|
||||
args: [
|
||||
"boring-name",
|
||||
],
|
||||
env: CommandEnv {{
|
||||
clear: true,
|
||||
vars: {{
|
||||
"BAR": Some(
|
||||
"val",
|
||||
),
|
||||
}},
|
||||
}},
|
||||
{PIDFD}}}"#
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// See issue #91991
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn run_bat_script() {
|
||||
let tempdir = crate::test_helpers::tmpdir();
|
||||
let script_path = tempdir.join("hello.cmd");
|
||||
|
||||
crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
|
||||
let output = Command::new(&script_path)
|
||||
.arg("fellow Rustaceans")
|
||||
.stdout(crate::process::Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait_with_output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
|
||||
}
|
||||
|
||||
// See issue #95178
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn run_canonical_bat_script() {
|
||||
let tempdir = crate::test_helpers::tmpdir();
|
||||
let script_path = tempdir.join("hello.cmd");
|
||||
|
||||
crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
|
||||
|
||||
// Try using a canonical path
|
||||
let output = Command::new(&script_path.canonicalize().unwrap())
|
||||
.arg("fellow Rustaceans")
|
||||
.stdout(crate::process::Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait_with_output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminate_exited_process() {
|
||||
let mut cmd = if cfg!(target_os = "android") {
|
||||
let mut p = shell_cmd();
|
||||
p.args(&["-c", "true"]);
|
||||
p
|
||||
} else {
|
||||
known_command()
|
||||
};
|
||||
let mut p = cmd.stdout(Stdio::null()).spawn().unwrap();
|
||||
p.wait().unwrap();
|
||||
assert!(p.kill().is_ok());
|
||||
assert!(p.kill().is_ok());
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
pub mod barrier;
|
||||
pub mod lazy_lock;
|
||||
pub mod mpmc;
|
||||
pub mod mpsc;
|
||||
pub mod nonpoison;
|
||||
pub mod once;
|
||||
pub mod once_lock;
|
||||
pub mod poison;
|
||||
pub mod reentrant_lock;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::sync::atomic;
|
||||
|
||||
pub use once::Once;
|
||||
pub use once::OnceState;
|
||||
|
||||
pub use alloc_crate::sync::Arc;
|
||||
pub use lazy_lock::LazyLock;
|
||||
pub use once_lock::OnceLock;
|
||||
pub use poison::Condvar;
|
||||
pub use poison::LockResult;
|
||||
pub use poison::Mutex;
|
||||
pub use poison::MutexGuard;
|
||||
pub use poison::PoisonError;
|
||||
pub use poison::RwLock;
|
||||
pub use poison::TryLockError;
|
||||
pub use poison::TryLockResult;
|
||||
pub use reentrant_lock::ReentrantLock;
|
||||
pub use reentrant_lock::ReentrantLockGuard;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
pub struct WaitTimeoutResult(bool);
|
||||
|
||||
impl WaitTimeoutResult {
|
||||
/// Returns `true` if the wait was known to have timed out.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This example spawns a thread which will sleep 20 milliseconds before
|
||||
/// updating a boolean value and then notifying the condvar.
|
||||
///
|
||||
/// The main thread will wait with a 10 millisecond timeout on the condvar
|
||||
/// and will leave the loop upon timeout.
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Condvar, Mutex};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// # let handle =
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
///
|
||||
/// // Let's wait 20 milliseconds before notifying the condvar.
|
||||
/// thread::sleep(Duration::from_millis(20));
|
||||
///
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // We update the boolean value.
|
||||
/// *started = true;
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// loop {
|
||||
/// // Let's put a timeout on the condvar's wait.
|
||||
/// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap();
|
||||
/// // 10 milliseconds have passed.
|
||||
/// if result.1.timed_out() {
|
||||
/// // timed out now and we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// # // Prevent leaks for Miri.
|
||||
/// # let _ = handle.join();
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
pub fn timed_out(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
307
crates/std/src/sync/mod.rs
Normal file
307
crates/std/src/sync/mod.rs
Normal file
@@ -0,0 +1,307 @@
|
||||
//! Useful synchronization primitives.
|
||||
//!
|
||||
//! ## The need for synchronization
|
||||
//!
|
||||
//! Conceptually, a Rust program is a series of operations which will
|
||||
//! be executed on a computer. The timeline of events happening in the
|
||||
//! program is consistent with the order of the operations in the code.
|
||||
//!
|
||||
//! Consider the following code, operating on some global static variables:
|
||||
//!
|
||||
//! ```rust
|
||||
//! // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
|
||||
//! #![allow(static_mut_refs)]
|
||||
//!
|
||||
//! static mut A: u32 = 0;
|
||||
//! static mut B: u32 = 0;
|
||||
//! static mut C: u32 = 0;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! unsafe {
|
||||
//! A = 3;
|
||||
//! B = 4;
|
||||
//! A = A + B;
|
||||
//! C = B;
|
||||
//! println!("{A} {B} {C}");
|
||||
//! C = A;
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! It appears as if some variables stored in memory are changed, an addition
|
||||
//! is performed, result is stored in `A` and the variable `C` is
|
||||
//! modified twice.
|
||||
//!
|
||||
//! When only a single thread is involved, the results are as expected:
|
||||
//! the line `7 4 4` gets printed.
|
||||
//!
|
||||
//! As for what happens behind the scenes, when optimizations are enabled the
|
||||
//! final generated machine code might look very different from the code:
|
||||
//!
|
||||
//! - The first store to `C` might be moved before the store to `A` or `B`,
|
||||
//! _as if_ we had written `C = 4; A = 3; B = 4`.
|
||||
//!
|
||||
//! - Assignment of `A + B` to `A` might be removed, since the sum can be stored
|
||||
//! in a temporary location until it gets printed, with the global variable
|
||||
//! never getting updated.
|
||||
//!
|
||||
//! - The final result could be determined just by looking at the code
|
||||
//! at compile time, so [constant folding] might turn the whole
|
||||
//! block into a simple `println!("7 4 4")`.
|
||||
//!
|
||||
//! The compiler is allowed to perform any combination of these
|
||||
//! optimizations, as long as the final optimized code, when executed,
|
||||
//! produces the same results as the one without optimizations.
|
||||
//!
|
||||
//! Due to the [concurrency] involved in modern computers, assumptions
|
||||
//! about the program's execution order are often wrong. Access to
|
||||
//! global variables can lead to nondeterministic results, **even if**
|
||||
//! compiler optimizations are disabled, and it is **still possible**
|
||||
//! to introduce synchronization bugs.
|
||||
//!
|
||||
//! Note that thanks to Rust's safety guarantees, accessing global (static)
|
||||
//! variables requires `unsafe` code, assuming we don't use any of the
|
||||
//! synchronization primitives in this module.
|
||||
//!
|
||||
//! [constant folding]: https://en.wikipedia.org/wiki/Constant_folding
|
||||
//! [concurrency]: https://en.wikipedia.org/wiki/Concurrency_(computer_science)
|
||||
//!
|
||||
//! ## Out-of-order execution
|
||||
//!
|
||||
//! Instructions can execute in a different order from the one we define, due to
|
||||
//! various reasons:
|
||||
//!
|
||||
//! - The **compiler** reordering instructions: If the compiler can issue an
|
||||
//! instruction at an earlier point, it will try to do so. For example, it
|
||||
//! might hoist memory loads at the top of a code block, so that the CPU can
|
||||
//! start [prefetching] the values from memory.
|
||||
//!
|
||||
//! In single-threaded scenarios, this can cause issues when writing
|
||||
//! signal handlers or certain kinds of low-level code.
|
||||
//! Use [compiler fences] to prevent this reordering.
|
||||
//!
|
||||
//! - A **single processor** executing instructions [out-of-order]:
|
||||
//! Modern CPUs are capable of [superscalar] execution,
|
||||
//! i.e., multiple instructions might be executing at the same time,
|
||||
//! even though the machine code describes a sequential process.
|
||||
//!
|
||||
//! This kind of reordering is handled transparently by the CPU.
|
||||
//!
|
||||
//! - A **multiprocessor** system executing multiple hardware threads
|
||||
//! at the same time: In multi-threaded scenarios, you can use two
|
||||
//! kinds of primitives to deal with synchronization:
|
||||
//! - [memory fences] to ensure memory accesses are made visible to
|
||||
//! other CPUs in the right order.
|
||||
//! - [atomic operations] to ensure simultaneous access to the same
|
||||
//! memory location doesn't lead to undefined behavior.
|
||||
//!
|
||||
//! [prefetching]: https://en.wikipedia.org/wiki/Cache_prefetching
|
||||
//! [compiler fences]: crate::sync::atomic::compiler_fence
|
||||
//! [out-of-order]: https://en.wikipedia.org/wiki/Out-of-order_execution
|
||||
//! [superscalar]: https://en.wikipedia.org/wiki/Superscalar_processor
|
||||
//! [memory fences]: crate::sync::atomic::fence
|
||||
//! [atomic operations]: crate::sync::atomic
|
||||
//!
|
||||
//! ## Higher-level synchronization objects
|
||||
//!
|
||||
//! Most of the low-level synchronization primitives are quite error-prone and
|
||||
//! inconvenient to use, which is why the standard library also exposes some
|
||||
//! higher-level synchronization objects.
|
||||
//!
|
||||
//! These abstractions can be built out of lower-level primitives.
|
||||
//! For efficiency, the sync objects in the standard library are usually
|
||||
//! implemented with help from the operating system's kernel, which is
|
||||
//! able to reschedule the threads while they are blocked on acquiring
|
||||
//! a lock.
|
||||
//!
|
||||
//! The following is an overview of the available synchronization
|
||||
//! objects:
|
||||
//!
|
||||
//! - [`Arc`]: Atomically Reference-Counted pointer, which can be used
|
||||
//! in multithreaded environments to prolong the lifetime of some
|
||||
//! data until all the threads have finished using it.
|
||||
//!
|
||||
//! - [`Barrier`]: Ensures multiple threads will wait for each other
|
||||
//! to reach a point in the program, before continuing execution all
|
||||
//! together.
|
||||
//!
|
||||
//! - [`Condvar`]: Condition Variable, providing the ability to block
|
||||
//! a thread while waiting for an event to occur.
|
||||
//!
|
||||
//! - [`mpsc`]: Multi-producer, single-consumer queues, used for
|
||||
//! message-based communication. Can provide a lightweight
|
||||
//! inter-thread synchronisation mechanism, at the cost of some
|
||||
//! extra memory.
|
||||
//!
|
||||
//! - [`mpmc`]: Multi-producer, multi-consumer queues, used for
|
||||
//! message-based communication. Can provide a lightweight
|
||||
//! inter-thread synchronisation mechanism, at the cost of some
|
||||
//! extra memory.
|
||||
//!
|
||||
//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at
|
||||
//! most one thread at a time is able to access some data.
|
||||
//!
|
||||
//! - [`Once`]: Used for a thread-safe, one-time global initialization routine.
|
||||
//! Mostly useful for implementing other types like [`OnceLock`].
|
||||
//!
|
||||
//! - [`OnceLock`]: Used for thread-safe, one-time initialization of a
|
||||
//! variable, with potentially different initializers based on the caller.
|
||||
//!
|
||||
//! - [`LazyLock`]: Used for thread-safe, one-time initialization of a
|
||||
//! variable, using one nullary initializer function provided at creation.
|
||||
//!
|
||||
//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows
|
||||
//! multiple readers at the same time, while allowing only one
|
||||
//! writer at a time. In some cases, this can be more efficient than
|
||||
//! a mutex.
|
||||
//!
|
||||
//! [`Arc`]: crate::sync::Arc
|
||||
//! [`Barrier`]: crate::sync::Barrier
|
||||
//! [`Condvar`]: crate::sync::Condvar
|
||||
//! [`mpmc`]: crate::sync::mpmc
|
||||
//! [`mpsc`]: crate::sync::mpsc
|
||||
//! [`Mutex`]: crate::sync::Mutex
|
||||
//! [`Once`]: crate::sync::Once
|
||||
//! [`OnceLock`]: crate::sync::OnceLock
|
||||
//! [`RwLock`]: crate::sync::RwLock
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
// No formatting: this file is just re-exports, and their order is worth preserving.
|
||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
||||
|
||||
// These come from `core` & `alloc` and only in one flavor: no poisoning.
|
||||
#[unstable(feature = "exclusive_wrapper", issue = "98407")]
|
||||
pub use core::sync::Exclusive;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::sync::atomic;
|
||||
|
||||
#[unstable(feature = "unique_rc_arc", issue = "112566")]
|
||||
pub use alloc_crate::sync::UniqueArc;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use alloc_crate::sync::{Arc, Weak};
|
||||
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
pub mod mpmc;
|
||||
pub mod mpsc;
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub mod oneshot;
|
||||
|
||||
pub(crate) mod once; // `pub(crate)` for the `sys::sync::once` implementations and `LazyLock`.
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::once::{Once, OnceState};
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(inline)]
|
||||
#[expect(deprecated)]
|
||||
pub use self::once::ONCE_INIT;
|
||||
|
||||
mod barrier;
|
||||
mod lazy_lock;
|
||||
mod once_lock;
|
||||
mod reentrant_lock;
|
||||
|
||||
// These exist only in one flavor: no poisoning.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::barrier::{Barrier, BarrierWaitResult};
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
pub use self::lazy_lock::LazyLock;
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub use self::once_lock::OnceLock;
|
||||
#[unstable(feature = "reentrant_lock", issue = "121440")]
|
||||
pub use self::reentrant_lock::{ReentrantLock, ReentrantLockGuard};
|
||||
|
||||
// Note: in the future we will change the default version in `std::sync` to the non-poisoning
|
||||
// version over an edition.
|
||||
// See https://github.com/rust-lang/rust/issues/134645#issuecomment-3324577500 for more details.
|
||||
|
||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
||||
pub mod nonpoison;
|
||||
#[unstable(feature = "sync_poison_mod", issue = "134646")]
|
||||
pub mod poison;
|
||||
|
||||
// FIXME(sync_poison_mod): remove all `#[doc(inline)]` once the modules are stabilized.
|
||||
|
||||
// These exist only with poisoning.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(inline)]
|
||||
pub use self::poison::{LockResult, PoisonError};
|
||||
|
||||
// These exist in both flavors: with and without poisoning.
|
||||
// The historical default is the version with poisoning.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(inline)]
|
||||
pub use self::poison::{
|
||||
TryLockError, TryLockResult,
|
||||
Mutex, MutexGuard,
|
||||
RwLock, RwLockReadGuard, RwLockWriteGuard,
|
||||
Condvar,
|
||||
};
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
#[doc(inline)]
|
||||
pub use self::poison::{MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWriteGuard};
|
||||
|
||||
/// A type indicating whether a timed wait on a condition variable returned
|
||||
/// due to a time out or not.
|
||||
///
|
||||
/// It is returned by the [`wait_timeout`] method.
|
||||
///
|
||||
/// [`wait_timeout`]: Condvar::wait_timeout
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
pub struct WaitTimeoutResult(bool);
|
||||
|
||||
impl WaitTimeoutResult {
|
||||
/// Returns `true` if the wait was known to have timed out.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// This example spawns a thread which will sleep 20 milliseconds before
|
||||
/// updating a boolean value and then notifying the condvar.
|
||||
///
|
||||
/// The main thread will wait with a 10 millisecond timeout on the condvar
|
||||
/// and will leave the loop upon timeout.
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Condvar, Mutex};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// # let handle =
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
///
|
||||
/// // Let's wait 20 milliseconds before notifying the condvar.
|
||||
/// thread::sleep(Duration::from_millis(20));
|
||||
///
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // We update the boolean value.
|
||||
/// *started = true;
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// loop {
|
||||
/// // Let's put a timeout on the condvar's wait.
|
||||
/// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap();
|
||||
/// // 10 milliseconds have passed.
|
||||
/// if result.1.timed_out() {
|
||||
/// // timed out now and we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// # // Prevent leaks for Miri.
|
||||
/// # let _ = handle.join();
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
pub fn timed_out(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
466
crates/std/src/sync/oneshot.rs
Normal file
466
crates/std/src/sync/oneshot.rs
Normal file
@@ -0,0 +1,466 @@
|
||||
//! A single-producer, single-consumer (oneshot) channel.
|
||||
//!
|
||||
//! This is an experimental module, so the API will likely change.
|
||||
|
||||
use crate::sync::mpmc;
|
||||
use crate::sync::mpsc::{RecvError, SendError};
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::{error, fmt};
|
||||
|
||||
/// Creates a new oneshot channel, returning the sender/receiver halves.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let (sender, receiver) = oneshot::channel();
|
||||
///
|
||||
/// // Spawn off an expensive computation.
|
||||
/// thread::spawn(move || {
|
||||
/// # fn expensive_computation() -> i32 { 42 }
|
||||
/// sender.send(expensive_computation()).unwrap();
|
||||
/// // `sender` is consumed by `send`, so we cannot use it anymore.
|
||||
/// });
|
||||
///
|
||||
/// # fn do_other_work() -> i32 { 42 }
|
||||
/// do_other_work();
|
||||
///
|
||||
/// // Let's see what that answer was...
|
||||
/// println!("{:?}", receiver.recv().unwrap());
|
||||
/// // `receiver` is consumed by `recv`, so we cannot use it anymore.
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
// Using a `sync_channel` with capacity 1 means that the internal implementation will use the
|
||||
// `Array`-flavored channel implementation.
|
||||
let (sender, receiver) = mpmc::sync_channel(1);
|
||||
(Sender { inner: sender }, Receiver { inner: receiver })
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Sender
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// The sending half of a oneshot channel.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let (sender, receiver) = oneshot::channel();
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// sender.send("Hello from thread!").unwrap();
|
||||
/// });
|
||||
///
|
||||
/// assert_eq!(receiver.recv().unwrap(), "Hello from thread!");
|
||||
/// ```
|
||||
///
|
||||
/// `Sender` cannot be sent between threads if it is sending non-`Send` types.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot;
|
||||
/// use std::thread;
|
||||
/// use std::ptr;
|
||||
///
|
||||
/// let (sender, receiver) = oneshot::channel();
|
||||
///
|
||||
/// struct NotSend(*mut ());
|
||||
/// thread::spawn(move || {
|
||||
/// sender.send(NotSend(ptr::null_mut()));
|
||||
/// });
|
||||
///
|
||||
/// let reply = receiver.try_recv().unwrap();
|
||||
/// ```
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub struct Sender<T> {
|
||||
/// The `oneshot` channel is simply a wrapper around a `mpmc` channel.
|
||||
inner: mpmc::Sender<T>,
|
||||
}
|
||||
|
||||
// SAFETY: Since the only methods in which synchronization must occur take full ownership of the
|
||||
// [`Sender`], it is perfectly safe to share a `&Sender` between threads (as it is effectively
|
||||
// useless without ownership).
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
unsafe impl<T> Sync for Sender<T> {}
|
||||
|
||||
impl<T> Sender<T> {
|
||||
/// Attempts to send a value through this channel. This can only fail if the corresponding
|
||||
/// [`Receiver<T>`] has been dropped.
|
||||
///
|
||||
/// This method is non-blocking (wait-free).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let (tx, rx) = oneshot::channel();
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// // Perform some computation.
|
||||
/// let result = 2 + 2;
|
||||
/// tx.send(result).unwrap();
|
||||
/// });
|
||||
///
|
||||
/// assert_eq!(rx.recv().unwrap(), 4);
|
||||
/// ```
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub fn send(self, t: T) -> Result<(), SendError<T>> {
|
||||
self.inner.send(t)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> fmt::Debug for Sender<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Sender").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Receiver
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// The receiving half of a oneshot channel.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let (sender, receiver) = oneshot::channel();
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// thread::sleep(Duration::from_millis(100));
|
||||
/// sender.send("Hello after delay!").unwrap();
|
||||
/// });
|
||||
///
|
||||
/// println!("Waiting for message...");
|
||||
/// println!("{}", receiver.recv().unwrap());
|
||||
/// ```
|
||||
///
|
||||
/// `Receiver` cannot be sent between threads if it is receiving non-`Send` types.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # #![feature(oneshot_channel)]
|
||||
/// # use std::sync::oneshot;
|
||||
/// # use std::thread;
|
||||
/// # use std::ptr;
|
||||
/// #
|
||||
/// let (sender, receiver) = oneshot::channel();
|
||||
///
|
||||
/// struct NotSend(*mut ());
|
||||
/// sender.send(NotSend(ptr::null_mut()));
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let reply = receiver.try_recv().unwrap();
|
||||
/// });
|
||||
/// ```
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub struct Receiver<T> {
|
||||
/// The `oneshot` channel is simply a wrapper around a `mpmc` channel.
|
||||
inner: mpmc::Receiver<T>,
|
||||
}
|
||||
|
||||
// SAFETY: Since the only methods in which synchronization must occur take full ownership of the
|
||||
// [`Receiver`], it is perfectly safe to share a `&Receiver` between threads (as it is unable to
|
||||
// receive any values without ownership).
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
unsafe impl<T> Sync for Receiver<T> {}
|
||||
|
||||
impl<T> Receiver<T> {
|
||||
/// Receives the value from the sending end, blocking the calling thread until it gets it.
|
||||
///
|
||||
/// Can only fail if the corresponding [`Sender<T>`] has been dropped.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let (tx, rx) = oneshot::channel();
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// thread::sleep(Duration::from_millis(500));
|
||||
/// tx.send("Done!").unwrap();
|
||||
/// });
|
||||
///
|
||||
/// // This will block until the message arrives.
|
||||
/// println!("{}", rx.recv().unwrap());
|
||||
/// ```
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub fn recv(self) -> Result<T, RecvError> {
|
||||
self.inner.recv()
|
||||
}
|
||||
|
||||
// Fallible methods.
|
||||
|
||||
/// Attempts to return a pending value on this receiver without blocking.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let (sender, mut receiver) = oneshot::channel();
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// thread::sleep(Duration::from_millis(100));
|
||||
/// sender.send(42).unwrap();
|
||||
/// });
|
||||
///
|
||||
/// // Keep trying until we get the message, doing other work in the process.
|
||||
/// loop {
|
||||
/// match receiver.try_recv() {
|
||||
/// Ok(value) => {
|
||||
/// assert_eq!(value, 42);
|
||||
/// break;
|
||||
/// }
|
||||
/// Err(oneshot::TryRecvError::Empty(rx)) => {
|
||||
/// // Retake ownership of the receiver.
|
||||
/// receiver = rx;
|
||||
/// # fn do_other_work() { thread::sleep(Duration::from_millis(25)); }
|
||||
/// do_other_work();
|
||||
/// }
|
||||
/// Err(oneshot::TryRecvError::Disconnected) => panic!("Sender disconnected"),
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub fn try_recv(self) -> Result<T, TryRecvError<T>> {
|
||||
self.inner.try_recv().map_err(|err| match err {
|
||||
mpmc::TryRecvError::Empty => TryRecvError::Empty(self),
|
||||
mpmc::TryRecvError::Disconnected => TryRecvError::Disconnected,
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to wait for a value on this receiver, returning an error if the corresponding
|
||||
/// [`Sender`] half of this channel has been dropped, or if it waits more than `timeout`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let (sender, receiver) = oneshot::channel();
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// thread::sleep(Duration::from_millis(500));
|
||||
/// sender.send("Success!").unwrap();
|
||||
/// });
|
||||
///
|
||||
/// // Wait up to 1 second for the message
|
||||
/// match receiver.recv_timeout(Duration::from_secs(1)) {
|
||||
/// Ok(msg) => println!("Received: {}", msg),
|
||||
/// Err(oneshot::RecvTimeoutError::Timeout(_)) => println!("Timed out!"),
|
||||
/// Err(oneshot::RecvTimeoutError::Disconnected) => println!("Sender dropped!"),
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub fn recv_timeout(self, timeout: Duration) -> Result<T, RecvTimeoutError<T>> {
|
||||
self.inner.recv_timeout(timeout).map_err(|err| match err {
|
||||
mpmc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout(self),
|
||||
mpmc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected,
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to wait for a value on this receiver, returning an error if the corresponding
|
||||
/// [`Sender`] half of this channel has been dropped, or if `deadline` is reached.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot;
|
||||
/// use std::thread;
|
||||
/// use std::time::{Duration, Instant};
|
||||
///
|
||||
/// let (sender, receiver) = oneshot::channel();
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// thread::sleep(Duration::from_millis(100));
|
||||
/// sender.send("Just in time!").unwrap();
|
||||
/// });
|
||||
///
|
||||
/// let deadline = Instant::now() + Duration::from_millis(500);
|
||||
/// match receiver.recv_deadline(deadline) {
|
||||
/// Ok(msg) => println!("Received: {}", msg),
|
||||
/// Err(oneshot::RecvTimeoutError::Timeout(_)) => println!("Missed deadline!"),
|
||||
/// Err(oneshot::RecvTimeoutError::Disconnected) => println!("Sender dropped!"),
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub fn recv_deadline(self, deadline: Instant) -> Result<T, RecvTimeoutError<T>> {
|
||||
self.inner.recv_deadline(deadline).map_err(|err| match err {
|
||||
mpmc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout(self),
|
||||
mpmc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> fmt::Debug for Receiver<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Receiver").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Receiver Errors
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// An error returned from the [`try_recv`](Receiver::try_recv) method.
|
||||
///
|
||||
/// See the documentation for [`try_recv`] for more information on how to use this error.
|
||||
///
|
||||
/// [`try_recv`]: Receiver::try_recv
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub enum TryRecvError<T> {
|
||||
/// The [`Sender`] has not sent a message yet, but it might in the future (as it has not yet
|
||||
/// disconnected). This variant contains the [`Receiver`] that [`try_recv`](Receiver::try_recv)
|
||||
/// took ownership over.
|
||||
Empty(Receiver<T>),
|
||||
/// The corresponding [`Sender`] half of this channel has become disconnected, and there will
|
||||
/// never be any more data sent over the channel.
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
/// An error returned from the [`recv_timeout`](Receiver::recv_timeout) or
|
||||
/// [`recv_deadline`](Receiver::recv_deadline) methods.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Usage of this error is similar to [`TryRecvError`].
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(oneshot_channel)]
|
||||
/// use std::sync::oneshot::{self, RecvTimeoutError};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let (sender, receiver) = oneshot::channel();
|
||||
///
|
||||
/// let send_failure = thread::spawn(move || {
|
||||
/// // Simulate a long computation that takes longer than our timeout.
|
||||
/// thread::sleep(Duration::from_millis(250));
|
||||
///
|
||||
/// // This will likely fail to send because we drop the receiver in the main thread.
|
||||
/// sender.send("Goodbye!".to_string()).unwrap();
|
||||
/// });
|
||||
///
|
||||
/// // Try to receive the message with a short timeout.
|
||||
/// match receiver.recv_timeout(Duration::from_millis(10)) {
|
||||
/// Ok(msg) => println!("Received: {}", msg),
|
||||
/// Err(RecvTimeoutError::Timeout(rx)) => {
|
||||
/// println!("Timed out waiting for message!");
|
||||
///
|
||||
/// // Note that you can reuse the receiver without dropping it.
|
||||
/// drop(rx);
|
||||
/// },
|
||||
/// Err(RecvTimeoutError::Disconnected) => println!("Sender dropped!"),
|
||||
/// }
|
||||
///
|
||||
/// send_failure.join().unwrap_err();
|
||||
/// ```
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
pub enum RecvTimeoutError<T> {
|
||||
/// The [`Sender`] has not sent a message yet, but it might in the future (as it has not yet
|
||||
/// disconnected). This variant contains the [`Receiver`] that either
|
||||
/// [`recv_timeout`](Receiver::recv_timeout) or [`recv_deadline`](Receiver::recv_deadline) took
|
||||
/// ownership over.
|
||||
Timeout(Receiver<T>),
|
||||
/// The corresponding [`Sender`] half of this channel has become disconnected, and there will
|
||||
/// never be any more data sent over the channel.
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> fmt::Debug for TryRecvError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("TryRecvError").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> fmt::Display for TryRecvError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
TryRecvError::Empty(..) => "receiving on an empty oneshot channel".fmt(f),
|
||||
TryRecvError::Disconnected => "receiving on a closed oneshot channel".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> error::Error for TryRecvError<T> {}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> From<RecvError> for TryRecvError<T> {
|
||||
/// Converts a `RecvError` into a `TryRecvError`.
|
||||
///
|
||||
/// This conversion always returns `TryRecvError::Disconnected`.
|
||||
///
|
||||
/// No data is allocated on the heap.
|
||||
fn from(err: RecvError) -> TryRecvError<T> {
|
||||
match err {
|
||||
RecvError => TryRecvError::Disconnected,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> fmt::Debug for RecvTimeoutError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("RecvTimeoutError").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> fmt::Display for RecvTimeoutError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
RecvTimeoutError::Timeout(..) => "timed out waiting on oneshot channel".fmt(f),
|
||||
RecvTimeoutError::Disconnected => "receiving on a closed oneshot channel".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> error::Error for RecvTimeoutError<T> {}
|
||||
|
||||
#[unstable(feature = "oneshot_channel", issue = "143674")]
|
||||
impl<T> From<RecvError> for RecvTimeoutError<T> {
|
||||
/// Converts a `RecvError` into a `RecvTimeoutError`.
|
||||
///
|
||||
/// This conversion always returns `RecvTimeoutError::Disconnected`.
|
||||
///
|
||||
/// No data is allocated on the heap.
|
||||
fn from(err: RecvError) -> RecvTimeoutError<T> {
|
||||
match err {
|
||||
RecvError => RecvTimeoutError::Disconnected,
|
||||
}
|
||||
}
|
||||
}
|
||||
23
crates/std/src/sys/fd/mod.rs
Normal file
23
crates/std/src/sys/fd/mod.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
//! Platform-dependent file descriptor abstraction.
|
||||
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
cfg_select! {
|
||||
any(target_family = "unix", target_os = "wasi") => {
|
||||
mod unix;
|
||||
pub use unix::*;
|
||||
}
|
||||
target_os = "hermit" => {
|
||||
mod hermit;
|
||||
pub use hermit::*;
|
||||
}
|
||||
target_os = "motor" => {
|
||||
mod motor;
|
||||
pub use motor::*;
|
||||
}
|
||||
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||
mod sgx;
|
||||
pub use sgx::*;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1,16 +1,25 @@
|
||||
pub mod alloc;
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
mod alloc;
|
||||
mod configure_builtins;
|
||||
mod helpers;
|
||||
mod pal;
|
||||
mod personality;
|
||||
|
||||
pub mod args;
|
||||
pub mod backtrace;
|
||||
pub mod cmath;
|
||||
pub mod configure_builtins;
|
||||
pub mod env;
|
||||
pub mod env_consts;
|
||||
pub mod exit;
|
||||
pub mod fd;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod net;
|
||||
pub mod os_str;
|
||||
pub mod pal;
|
||||
pub mod path;
|
||||
pub mod pipe;
|
||||
pub mod platform_version;
|
||||
pub mod process;
|
||||
pub mod random;
|
||||
pub mod stdio;
|
||||
@@ -18,9 +27,10 @@ pub mod sync;
|
||||
pub mod thread;
|
||||
pub mod thread_local;
|
||||
pub mod time;
|
||||
|
||||
// FIXME(117276): remove this, move feature implementations into individual
|
||||
// submodules.
|
||||
pub use pal::*;
|
||||
pub mod fs;
|
||||
pub mod helpers;
|
||||
|
||||
/// A trait for viewing representations from std types.
|
||||
#[cfg_attr(not(target_os = "linux"), allow(unused))]
|
||||
@@ -43,25 +53,3 @@ pub(crate) trait IntoInner<Inner> {
|
||||
pub(crate) trait FromInner<Inner> {
|
||||
fn from_inner(inner: Inner) -> Self;
|
||||
}
|
||||
|
||||
use crate::io as std_io;
|
||||
|
||||
// SAFETY: must be called only once during runtime initialization.
|
||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
|
||||
|
||||
// SAFETY: must be called only once during runtime cleanup.
|
||||
// NOTE: this is not guaranteed to run, for example when the program aborts.
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub fn unsupported<T>() -> std_io::Result<T> {
|
||||
Err(unsupported_err())
|
||||
}
|
||||
|
||||
pub fn unsupported_err() -> std_io::Error {
|
||||
std_io::Error::UNSUPPORTED_PLATFORM
|
||||
}
|
||||
|
||||
pub fn abort_internal() -> ! {
|
||||
core::intrinsics::abort();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user