Add more from the std

This commit is contained in:
2026-03-19 11:52:43 +01:00
parent fc3a04a20e
commit 45d23efe77
116 changed files with 24256 additions and 551 deletions
+4 -2
View File
@@ -6,7 +6,9 @@ edition = "2024"
[dependencies] [dependencies]
cfg-if = { version = "1.0" } cfg-if = { version = "1.0" }
rustc-demangle = { version = "0.1.27" } 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" } os-std-macros = { path = "../os-std-macros" }
shared = { path = "../shared", features = ["user"] } shared = { path = "../shared", features = ["user"] }
io = { path = "../io", features = ["alloc"] } io_crate = { package = "io", path = "../io", features = ["alloc"] }
+19
View 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]
@@ -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
View 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
View 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
View 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
View 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.
@@ -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)
}
@@ -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)
}
@@ -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
}
@@ -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)
}
@@ -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)
}
@@ -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 { "" }
}
}
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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)
}
@@ -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
}
@@ -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))
}
@@ -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)
}
)*
}
};
}
@@ -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(),
}
}
@@ -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)
}
@@ -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;
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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()
}
);
}
@@ -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
}
@@ -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;
@@ -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
}
@@ -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
}
@@ -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()
}
}
}
@@ -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
}
@@ -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)
}
@@ -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() })
}
@@ -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
}
@@ -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
}
@@ -0,0 +1,8 @@
//! Other operating systems
use crate::detect::cache;
#[allow(dead_code)]
pub(crate) fn detect_features() -> cache::Initializer {
cache::Initializer::default()
}
@@ -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;
@@ -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));
}
@@ -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
}
@@ -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
}
+35
View 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;
@@ -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"));
}
@@ -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",);
}
@@ -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"));
}
+197 -95
View File
@@ -27,8 +27,18 @@ cp_std path:
@sed -i -f patches.sed {{ "src" / path }} @sed -i -f patches.sed {{ "src" / path }}
setup-std: setup-std:
@# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs, sync.rs @mkdir "crates/std_detect" -p
# @just cp_std "collections/mod.rs" @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 "alloc.rs"
@just cp_std "ascii.rs" @just cp_std "ascii.rs"
@just cp_std "backtrace.rs" @just cp_std "backtrace.rs"
@@ -48,91 +58,187 @@ setup-std:
# @just cp_std "tests_helpers.rs" # @just cp_std "tests_helpers.rs"
@just cp_std "time.rs" @just cp_std "time.rs"
@just cp_std "os/mod.rs" @# Complete
@just cp_std "os/raw/mod.rs" @just cp_std "backtrace/tests.rs"
@just cp_std "os/raw/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/map.rs"
@just cp_std "collections/hash/set.rs" @just cp_std "collections/hash/set.rs"
@just cp_std "collections/hash/mod.rs" @just cp_std "collections/hash/mod.rs"
@just cp_std "io/error.rs" @just cp_std "collections/mod.rs"
@just cp_std "io/error/repr_bitpacked.rs"
@just cp_std "io/error/repr_unpacked.rs" @# Complete
@just cp_std "io/error/tests.rs" @just cp_std "ffi/os_str/tests.rs"
@just cp_std "io/cursor.rs" @just cp_std "ffi/c_str.rs"
@just cp_std "io/cursor/tests.rs" @just cp_std "ffi/mod.rs"
@just cp_std "io/prelude.rs" @just cp_std "ffi/os_str.rs"
@just cp_std "io/impls.rs"
@just cp_std "io/impls/tests.rs" @# Complete
@just cp_std "io/tests.rs" @just cp_std "fs/tests.rs"
@just cp_std "io/util.rs"
@just cp_std "io/util/tests.rs" @# Complete
@just cp_std "io/copy.rs" @just cp_std "hash/mod.rs"
@just cp_std "io/copy/tests.rs" @just cp_std "hash/random.rs"
@just cp_std "io/pipe.rs"
@just cp_std "io/pipe/tests.rs" @# Complete
@just cp_std "io/stdio.rs"
@just cp_std "io/buffered/mod.rs"
@just cp_std "io/buffered/bufreader.rs"
@just cp_std "io/buffered/bufreader/buffer.rs" @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/bufwriter.rs"
@just cp_std "io/buffered/linewriter.rs" @just cp_std "io/buffered/linewriter.rs"
@just cp_std "io/buffered/linewritershim.rs" @just cp_std "io/buffered/linewritershim.rs"
@just cp_std "sync/once.rs" @just cp_std "io/buffered/mod.rs"
@just cp_std "sync/once_lock.rs" @just cp_std "io/buffered/tests.rs"
@just cp_std "sync/lazy_lock.rs" @just cp_std "io/copy/tests.rs"
@just cp_std "sync/nonpoison.rs" @just cp_std "io/cursor/tests.rs"
@just cp_std "sync/nonpoison/condvar.rs" @just cp_std "io/error/repr_bitpacked.rs"
@just cp_std "sync/nonpoison/mutex.rs" @just cp_std "io/error/repr_unpacked.rs"
@just cp_std "sync/nonpoison/rwlock.rs" @just cp_std "io/error/tests.rs"
@just cp_std "sync/poison.rs" @just cp_std "io/impls/tests.rs"
@just cp_std "sync/poison/condvar.rs" @just cp_std "io/pipe/tests.rs"
@just cp_std "sync/poison/mutex.rs" @just cp_std "io/stdio/tests.rs"
@just cp_std "sync/poison/rwlock.rs" @just cp_std "io/util/tests.rs"
@just cp_std "sync/barrier.rs" @just cp_std "io/copy.rs"
@just cp_std "sync/reentrant_lock.rs" @just cp_std "io/cursor.rs"
@just cp_std "sync/mpsc.rs" @just cp_std "io/error.rs"
@just cp_std "sync/mpmc/mod.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/array.rs"
@just cp_std "sync/mpmc/context.rs" @just cp_std "sync/mpmc/context.rs"
@just cp_std "sync/mpmc/counter.rs" @just cp_std "sync/mpmc/counter.rs"
@just cp_std "sync/mpmc/error.rs" @just cp_std "sync/mpmc/error.rs"
@just cp_std "sync/mpmc/list.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/select.rs"
@just cp_std "sync/mpmc/tests.rs" @just cp_std "sync/mpmc/tests.rs"
@just cp_std "sync/mpmc/utils.rs" @just cp_std "sync/mpmc/utils.rs"
@just cp_std "sync/mpmc/waker.rs" @just cp_std "sync/mpmc/waker.rs"
@just cp_std "sync/mpmc/zero.rs" @just cp_std "sync/mpmc/zero.rs"
@just cp_std "hash/mod.rs" @just cp_std "sync/nonpoison/condvar.rs"
@just cp_std "hash/random.rs" @just cp_std "sync/nonpoison/mutex.rs"
@just cp_std "num/mod.rs" @just cp_std "sync/nonpoison/rwlock.rs"
@just cp_std "ffi/c_str.rs" @just cp_std "sync/poison/condvar.rs"
@just cp_std "ffi/mod.rs" @just cp_std "sync/poison/mutex.rs"
@just cp_std "ffi/os_str.rs" @just cp_std "sync/poison/rwlock.rs"
@just cp_std "ffi/os_str/tests.rs" @just cp_std "sync/nonpoison.rs"
@just cp_std "thread/local.rs" @just cp_std "sync/barrier.rs"
@just cp_std "thread/thread.rs" @just cp_std "sync/lazy_lock.rs"
@just cp_std "thread/id.rs" @just cp_std "sync/mod.rs"
@just cp_std "thread/main_thread.rs" @just cp_std "sync/mpsc.rs"
@just cp_std "thread/current.rs" @just cp_std "sync/nonpoison.rs"
@just cp_std "thread/join_handle.rs" @just cp_std "sync/once.rs"
@just cp_std "thread/functions.rs" @just cp_std "sync/once_lock.rs"
@just cp_std "thread/lifecycle.rs" @just cp_std "sync/oneshot.rs"
@just cp_std "thread/builder.rs" @just cp_std "sync/poison.rs"
@just cp_std "thread/scoped.rs" @just cp_std "sync/reentrant_lock.rs"
@just cp_std "thread/spawnhook.rs"
@just cp_std "sys/exit.rs"
@just cp_std "sys/env_consts.rs" @# Complete
@just cp_std "sys/configure_builtins.rs" @just cp_std "sys/alloc/mod.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 "sys/args/mod.rs" @just cp_std "sys/args/mod.rs"
@just cp_std "sys/args/unsupported.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/mod.rs"
@just cp_std "sys/pal/unsupported/mod.rs" @just cp_std "sys/pal/unsupported/mod.rs"
@just cp_std "sys/pal/unsupported/common.rs" @just cp_std "sys/pal/unsupported/common.rs"
@just cp_std "sys/pal/unsupported/os.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/mod.rs"
@just cp_std "sys/sync/condvar/no_threads.rs" @just cp_std "sys/sync/condvar/no_threads.rs"
@just cp_std "sys/sync/mutex/mod.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/rwlock/no_threads.rs"
@just cp_std "sys/sync/thread_parking/mod.rs" @just cp_std "sys/sync/thread_parking/mod.rs"
@just cp_std "sys/sync/thread_parking/unsupported.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/mod.rs"
@just cp_std "sys/thread/unsupported.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/no_threads.rs"
@just cp_std "sys/thread_local/os.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/mod.rs"
@just cp_std "sys/time/unsupported.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/backtrace.rs"
@just cp_std "sys/fs/mod.rs" @just cp_std "sys/cmath.rs"
@just cp_std "sys/fs/unsupported.rs" @just cp_std "sys/configure_builtins.rs"
@just cp_std "sys/helpers/mod.rs" @just cp_std "sys/env_consts.rs"
@just cp_std "sys/helpers/small_c_string.rs" @just cp_std "sys/exit.rs"
@just cp_std "sys/helpers/tests.rs" @just cp_std "sys/mod.rs"
@just cp_std "sys/helpers/wstr.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 @# Copied but edited for the moment
-2
View File
@@ -1,8 +1,6 @@
s|crate::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
s|alloc::ffi|alloc_crate::ffi|g s|alloc::ffi|alloc_crate::ffi|g
s|alloc::slice::Join|alloc_crate::slice::Join|g s|alloc::slice::Join|alloc_crate::slice::Join|g
s|alloc::bstr|alloc_crate::bstr|g s|alloc::bstr|alloc_crate::bstr|g
s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g
/crate::sys::os::getpid/c \ todo!()
/\[doc = include_str!/c \ // todo retreive docs /\[doc = include_str!/c \ // todo retreive docs
@@ -0,0 +1,2 @@
362d
361a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")]
@@ -0,0 +1,2 @@
234d
233a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")]
+98
View 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>();
}
+1 -1
View File
@@ -359,7 +359,7 @@ impl<K, V, S> HashMap<K, V, S> {
#[inline] #[inline]
#[must_use] #[must_use]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] #[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> { pub const fn with_hasher(hash_builder: S) -> HashMap<K, V, S> {
HashMap { base: base::HashMap::with_hasher(hash_builder) } HashMap { base: base::HashMap::with_hasher(hash_builder) }
} }
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -6,7 +6,7 @@ use hashbrown::hash_set as base;
use super::map::map_try_reserve_error; use super::map::map_try_reserve_error;
use crate::alloc::{Allocator, Global}; use crate::alloc::{Allocator, Global};
use crate::borrow::Borrow; use crate::borrow::Borrow;
use alloc_crate::collections::TryReserveError; use crate::collections::TryReserveError;
use crate::fmt; use crate::fmt;
use crate::hash::{BuildHasher, Hash, RandomState}; use crate::hash::{BuildHasher, Hash, RandomState};
use crate::iter::{Chain, FusedIterator}; use crate::iter::{Chain, FusedIterator};
@@ -231,7 +231,7 @@ impl<T, S> HashSet<T, S> {
#[inline] #[inline]
#[must_use] #[must_use]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")] #[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> { pub const fn with_hasher(hasher: S) -> HashSet<T, S> {
HashSet { base: base::HashSet::with_hasher(hasher) } HashSet { base: base::HashSet::with_hasher(hasher) }
} }
@@ -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"
);
}
+457 -5
View File
@@ -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> { #![stable(feature = "rust1", since = "1.0.0")]
_phantom: PhantomData<(K, V, T)>,
#[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;
+1 -1
View File
@@ -6,7 +6,7 @@ mod tests;
use core::clone::CloneToUninit; use core::clone::CloneToUninit;
use crate::borrow::{Borrow, Cow}; use crate::borrow::{Borrow, Cow};
use alloc_crate::collections::TryReserveError; use crate::collections::TryReserveError;
use crate::hash::{Hash, Hasher}; use crate::hash::{Hash, Hasher};
use crate::ops::{self, Range}; use crate::ops::{self, Range};
use crate::rc::Rc; use crate::rc::Rc;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+500 -117
View File
@@ -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)] #[cfg(test)]
mod tests; 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")] #[unstable(feature = "read_buf", issue = "78485")]
pub use core::io::{BorrowedBuf, BorrowedCursor}; pub use core::io::{BorrowedBuf, BorrowedCursor};
use core::slice::memchr; use core::slice::memchr;
pub use cursor::Cursor;
pub use self::copy::copy; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
pub use self::pipe::{PipeReader, PipeWriter}; pub use self::buffered::WriterPanicked;
pub use self::stdio::cleanup; #[unstable(feature = "raw_os_error_ty", issue = "107792")]
pub use self::stdio::{ pub use self::error::RawOsError;
_print, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout, #[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; use crate::mem::{MaybeUninit, take};
pub use stdio::try_set_output_capture; use crate::ops::{Deref, DerefMut};
use crate::{cmp, fmt, slice, str, sys};
use crate::fs::File; mod buffered;
use io::IoBase; pub(crate) mod copy;
// pub use io::Read; mod cursor;
// pub use io::Seek; mod error;
// pub use io::SeekFrom; mod impls;
// pub use io::Write; mod pipe;
pub mod prelude;
// pub struct Stdin; mod stdio;
mod util;
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
const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
pub(crate) use stdio::cleanup;
struct Guard<'a> { struct Guard<'a> {
buf: &'a mut Vec<u8>, buf: &'a mut Vec<u8>,
len: usize, 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> pub(crate) unsafe fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize>
where where
F: FnOnce(&mut Vec<u8>) -> Result<usize>, F: FnOnce(&mut Vec<u8>) -> Result<usize>,
{ {
let mut g = Guard { let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } };
len: buf.len(),
buf: unsafe { buf.as_mut_vec() },
};
let ret = f(g.buf); let ret = f(g.buf);
// SAFETY: the caller promises to only append data to `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. // Optionally limit the maximum bytes read on each iteration.
// This adds an arbitrary fiddle factor to allow for more data than we expect. // This adds an arbitrary fiddle factor to allow for more data than we expect.
let mut max_read_size = size_hint let mut max_read_size = size_hint
.and_then(|s| { .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE))
s.checked_add(1024)?
.checked_next_multiple_of(DEFAULT_BUF_SIZE)
})
.unwrap_or(DEFAULT_BUF_SIZE); .unwrap_or(DEFAULT_BUF_SIZE);
let mut initialized = 0; // Extra initialized bytes from previous loop iteration 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 where
F: FnOnce(&mut [u8]) -> Result<usize>, F: FnOnce(&mut [u8]) -> Result<usize>,
{ {
let buf = bufs let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b);
.iter_mut()
.find(|b| !b.is_empty())
.map_or(&mut [][..], |b| &mut **b);
read(buf) read(buf)
} }
@@ -264,10 +565,7 @@ pub(crate) fn default_write_vectored<F>(write: F, bufs: &[IoSlice<'_>]) -> Resul
where where
F: FnOnce(&[u8]) -> Result<usize>, F: FnOnce(&[u8]) -> Result<usize>,
{ {
let buf = bufs let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b);
.iter()
.find(|b| !b.is_empty())
.map_or(&[][..], |b| &**b);
write(buf) 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), Err(e) => return Err(e),
} }
} }
if !buf.is_empty() { if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) }
Err(Error::READ_EXACT_EOF)
} else {
Ok(())
}
} }
pub(crate) fn default_read_buf<F>(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()> 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 { let mut output = Adapter { inner: this, error: Ok(()) };
inner: this,
error: Ok(()),
};
match fmt::write(&mut output, args) { match fmt::write(&mut output, args) {
Ok(()) => Ok(()), Ok(()) => Ok(()),
Err(..) => { 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")] #[stable(feature = "rust1", since = "1.0.0")]
#[doc(notable_trait)] #[doc(notable_trait)]
#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")] #[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")]
@@ -459,8 +823,7 @@ pub trait Read {
/// buffer provided, or an empty one if none exists. /// buffer provided, or an empty one if none exists.
#[stable(feature = "iovec", since = "1.36.0")] #[stable(feature = "iovec", since = "1.36.0")]
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> { fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
// default_read_vectored(|b| self.read(b), bufs) default_read_vectored(|b| self.read(b), bufs)
todo!()
} }
/// Determines if this `Read`er has an efficient `read_vectored` /// 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 /// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
// default_read_to_end(self, buf, None) default_read_to_end(self, buf, None)
todo!()
} }
/// Reads all bytes until EOF in this source, appending them to `buf`. /// 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 /// [`std::fs::read_to_string`]: crate::fs::read_to_string
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> { fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
// default_read_to_string(self, buf, None) default_read_to_string(self, buf, None)
todo!()
} }
/// Reads the exact number of bytes required to fill `buf`. /// 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")] #[stable(feature = "read_exact", since = "1.6.0")]
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
// default_read_exact(self, buf) default_read_exact(self, buf)
todo!()
} }
/// Pull some bytes from this source into the specified buffer. /// 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. /// This method makes it possible to return both data and an error but it is advised against.
#[unstable(feature = "read_buf", issue = "78485")] #[unstable(feature = "read_buf", issue = "78485")]
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
// default_read_buf(|b| self.read(b), buf) default_read_buf(|b| self.read(b), buf)
todo!()
} }
/// Reads the exact number of bytes required to fill `cursor`. /// 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`. /// If this function returns an error, all bytes read will be appended to `cursor`.
#[unstable(feature = "read_buf", issue = "78485")] #[unstable(feature = "read_buf", issue = "78485")]
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
// default_read_buf_exact(self, cursor) default_read_buf_exact(self, cursor)
todo!()
} }
/// Creates a "by reference" adapter for this instance of `Read`. /// Creates a "by reference" adapter for this instance of `Read`.
@@ -843,11 +1201,7 @@ pub trait Read {
where where
Self: Sized, Self: Sized,
{ {
Chain { Chain { first: self, second: next, done_first: false }
first: self,
second: next,
done_first: false,
}
} }
/// Creates an adapter which will read at most `limit` bytes from it. /// Creates an adapter which will read at most `limit` bytes from it.
@@ -886,11 +1240,7 @@ pub trait Read {
where where
Self: Sized, Self: Sized,
{ {
Take { Take { inner: self, len: limit, limit }
inner: self,
len: limit,
limit,
}
} }
/// Read and return a fixed array of bytes from this source. /// 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")] #[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")] #[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")]
pub trait BufRead: Read { pub trait BufRead: Read {
@@ -2267,10 +2666,7 @@ pub trait BufRead: Read {
where where
Self: Sized, Self: Sized,
{ {
Split { Split { buf: self, delim: byte }
buf: self,
delim: byte,
}
} }
/// Returns an iterator over the lines of this reader. /// Returns an iterator over the lines of this reader.
@@ -2310,6 +2706,7 @@ pub trait BufRead: Read {
Lines { buf: self } Lines { buf: self }
} }
} }
/// Adapter to chain together two readers. /// Adapter to chain together two readers.
/// ///
/// This struct is generally created by calling [`chain`] on a reader. /// 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) { fn consume(&mut self, amt: usize) {
if !self.done_first { if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) }
self.first.consume(amt)
} else {
self.second.consume(amt)
}
} }
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> { 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] #[inline]
fn upper_bound(&self) -> Option<usize> { fn upper_bound(&self) -> Option<usize> {
match ( match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) {
SizeHint::upper_bound(&self.first),
SizeHint::upper_bound(&self.second),
) {
(Some(first), Some(second)) => first.checked_add(second), (Some(first), Some(second)) => first.checked_add(second),
_ => None, _ => None,
} }
} }
} }
/// Reader adapter which limits the bytes read from an underlying reader. /// Reader adapter which limits the bytes read from an underlying reader.
/// ///
/// This struct is generally created by calling [`take`] on a 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); self.limit = self.limit.wrapping_sub(offset as u64);
break; break;
} }
let offset = if new_position > self.position() { let offset = if new_position > self.position() { i64::MAX } else { i64::MIN };
i64::MAX
} else {
i64::MIN
};
self.inner.seek_relative(offset)?; self.inner.seek_relative(offset)?;
self.limit = self.limit.wrapping_sub(offset as u64); 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<()> { fn seek_relative(&mut self, offset: i64) -> Result<()> {
if !self if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) {
.position()
.checked_add_signed(offset)
.is_some_and(|p| p <= self.len)
{
return Err(ErrorKind::InvalidInput.into()); return Err(ErrorKind::InvalidInput.into());
} }
self.inner.seek_relative(offset)?; self.inner.seek_relative(offset)?;
+166
View 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]
);
}
+32 -2
View File
@@ -262,11 +262,14 @@ pub use alloc_crate::string;
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::vec; 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")] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
pub use core::{ pub use core::{
assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args, 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, 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] #[rustc_std_internal_symbol]
@@ -274,10 +277,10 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn core::panic::PanicPayload) -
todo!() todo!()
} }
pub mod error;
pub mod ffi; pub mod ffi;
pub mod hash; pub mod hash;
pub mod io; pub mod io;
pub mod error;
pub mod num; pub mod num;
pub mod path; pub mod path;
pub mod prelude; pub mod prelude;
@@ -293,6 +296,7 @@ pub mod env;
pub mod fs; pub mod fs;
pub mod keyword_docs; pub mod keyword_docs;
pub mod macros; pub mod macros;
pub mod net;
pub mod os; pub mod os;
pub mod panic; pub mod panic;
pub mod panicking; pub mod panicking;
@@ -321,6 +325,8 @@ mod sealed {
pub use shared::fs as other_fs; pub use shared::fs as other_fs;
pub use shared::syscall; pub use shared::syscall;
use crate::io::Stdin;
// #[macro_export] // #[macro_export]
// macro_rules! print { // macro_rules! print {
// ($($args:expr),*) => { // ($($args:expr),*) => {
@@ -338,3 +344,27 @@ pub use shared::syscall;
// // $crate::println!(); // // $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
View 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
View 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
View 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
View 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
View 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
View 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());
}
File diff suppressed because it is too large Load Diff
+940
View 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
View 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
View 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
View 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}"),
}
})
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -84,7 +84,7 @@
use core::clone::CloneToUninit; use core::clone::CloneToUninit;
use crate::borrow::{Borrow, Cow}; use crate::borrow::{Borrow, Cow};
use alloc_crate::collections::TryReserveError; use crate::collections::TryReserveError;
use crate::error::Error; use crate::error::Error;
use crate::ffi::{OsStr, OsString, os_str}; use crate::ffi::{OsStr, OsString, os_str};
use crate::hash::{Hash, Hasher}; use crate::hash::{Hash, Hasher};
-167
View File
@@ -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
View 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
View 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
View 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());
}
-86
View File
@@ -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
View 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
View 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
View 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::*;
}
_ => {}
}
+15 -27
View File
@@ -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 args;
pub mod backtrace; pub mod backtrace;
pub mod cmath; pub mod cmath;
pub mod configure_builtins;
pub mod env; pub mod env;
pub mod env_consts; pub mod env_consts;
pub mod exit; pub mod exit;
pub mod fd;
pub mod fs;
pub mod io; pub mod io;
pub mod net;
pub mod os_str; pub mod os_str;
pub mod pal;
pub mod path; pub mod path;
pub mod pipe; pub mod pipe;
pub mod platform_version;
pub mod process; pub mod process;
pub mod random; pub mod random;
pub mod stdio; pub mod stdio;
@@ -18,9 +27,10 @@ pub mod sync;
pub mod thread; pub mod thread;
pub mod thread_local; pub mod thread_local;
pub mod time; pub mod time;
// FIXME(117276): remove this, move feature implementations into individual
// submodules.
pub use pal::*; pub use pal::*;
pub mod fs;
pub mod helpers;
/// A trait for viewing representations from std types. /// A trait for viewing representations from std types.
#[cfg_attr(not(target_os = "linux"), allow(unused))] #[cfg_attr(not(target_os = "linux"), allow(unused))]
@@ -43,25 +53,3 @@ pub(crate) trait IntoInner<Inner> {
pub(crate) trait FromInner<Inner> { pub(crate) trait FromInner<Inner> {
fn from_inner(inner: Inner) -> Self; 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