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

View File

@@ -6,7 +6,9 @@ edition = "2024"
[dependencies]
cfg-if = { version = "1.0" }
rustc-demangle = { version = "0.1.27" }
hashbrown = "0.16"
std_detect = { path = "crates/std_detect" }
panic_abort = { path = "crates/panic_abort" }
hashbrown = { version = "0.16.1", default-features = false, features = ["nightly", "rustc-internal-api"] }
os-std-macros = { path = "../os-std-macros" }
shared = { path = "../shared", features = ["user"] }
io = { path = "../io", features = ["alloc"] }
io_crate = { package = "io", path = "../io", features = ["alloc"] }

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]

View File

@@ -0,0 +1,51 @@
use alloc::string::String;
use core::mem::transmute;
use core::panic::PanicPayload;
use core::ptr::copy_nonoverlapping;
const ANDROID_SET_ABORT_MESSAGE: &[u8] = b"android_set_abort_message\0";
type SetAbortMessageType = unsafe extern "C" fn(*const libc::c_char) -> ();
// Forward the abort message to libc's android_set_abort_message. We try our best to populate the
// message but as this function may already be called as part of a failed allocation, it might not be
// possible to do so.
//
// Some methods of core are on purpose avoided (such as try_reserve) as these rely on the correct
// resolution of rust_eh_personality which is loosely defined in panic_abort.
//
// Weakly resolve the symbol for android_set_abort_message. This function is only available
// for API >= 21.
pub(crate) unsafe fn android_set_abort_message(payload: &mut dyn PanicPayload) {
let func_addr = unsafe {
libc::dlsym(libc::RTLD_DEFAULT, ANDROID_SET_ABORT_MESSAGE.as_ptr() as *const libc::c_char)
as usize
};
if func_addr == 0 {
return;
}
let payload = payload.get();
let msg = match payload.downcast_ref::<&'static str>() {
Some(msg) => msg.as_bytes(),
None => match payload.downcast_ref::<String>() {
Some(msg) => msg.as_bytes(),
None => &[],
},
};
if msg.is_empty() {
return;
}
// Allocate a new buffer to append the null byte.
let size = msg.len() + 1usize;
let buf = unsafe { libc::malloc(size) as *mut libc::c_char };
if buf.is_null() {
return; // allocation failure
}
unsafe {
copy_nonoverlapping(msg.as_ptr(), buf as *mut u8, msg.len());
buf.add(msg.len()).write(0);
let func = transmute::<usize, SetAbortMessageType>(func_addr);
func(buf);
}
}

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];
}

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());
}
}

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 = []

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.

View File

@@ -0,0 +1,264 @@
//! Aarch64 run-time features.
features! {
@TARGET: aarch64;
@CFG: any(target_arch = "aarch64", target_arch = "arm64ec");
@MACRO_NAME: is_aarch64_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
///
/// This macro takes one argument which is a string literal of the feature being tested for.
/// The feature names are mostly taken from their FEAT_* definitions in the [ARM Architecture
/// Reference Manual][docs].
///
/// Currently most features are only supported on linux-based platforms: on other platforms the
/// runtime check will always return `false`.
///
/// ## Supported arguments
///
/// * `"aes"` - FEAT_AES & FEAT_PMULL
/// * `"asimd"` or "neon" - FEAT_AdvSIMD
/// * `"bf16"` - FEAT_BF16
/// * `"bti"` - FEAT_BTI
/// * `"crc"` - FEAT_CRC
/// * `"cssc"` - FEAT_CSSC
/// * `"dit"` - FEAT_DIT
/// * `"dotprod"` - FEAT_DotProd
/// * `"dpb"` - FEAT_DPB
/// * `"dpb2"` - FEAT_DPB2
/// * `"ecv"` - FEAT_ECV
/// * `"f32mm"` - FEAT_F32MM
/// * `"f64mm"` - FEAT_F64MM
/// * `"faminmax"` - FEAT_FAMINMAX
/// * `"fcma"` - FEAT_FCMA
/// * `"fhm"` - FEAT_FHM
/// * `"flagm"` - FEAT_FLAGM
/// * `"flagm2"` - FEAT_FLAGM2
/// * `"fp"` - FEAT_FP
/// * `"fp16"` - FEAT_FP16
/// * `"fp8"` - FEAT_FP8
/// * `"fp8dot2"` - FEAT_FP8DOT2
/// * `"fp8dot4"` - FEAT_FP8DOT4
/// * `"fp8fma"` - FEAT_FP8FMA
/// * `"fpmr"` - FEAT_FPMR
/// * `"frintts"` - FEAT_FRINTTS
/// * `"hbc"` - FEAT_HBC
/// * `"i8mm"` - FEAT_I8MM
/// * `"jsconv"` - FEAT_JSCVT
/// * `"lse"` - FEAT_LSE
/// * `"lse128"` - FEAT_LSE128
/// * `"lse2"` - FEAT_LSE2
/// * `"lut"` - FEAT_LUT
/// * `"mops"` - FEAT_MOPS
/// * `"mte"` - FEAT_MTE & FEAT_MTE2
/// * `"paca"` - FEAT_PAuth (address authentication)
/// * `"pacg"` - FEAT_Pauth (generic authentication)
/// * `"pauth-lr"` - FEAT_PAuth_LR
/// * `"pmull"` - FEAT_PMULL
/// * `"rand"` - FEAT_RNG
/// * `"rcpc"` - FEAT_LRCPC
/// * `"rcpc2"` - FEAT_LRCPC2
/// * `"rcpc3"` - FEAT_LRCPC3
/// * `"rdm"` - FEAT_RDM
/// * `"sb"` - FEAT_SB
/// * `"sha2"` - FEAT_SHA1 & FEAT_SHA256
/// * `"sha3"` - FEAT_SHA512 & FEAT_SHA3
/// * `"sm4"` - FEAT_SM3 & FEAT_SM4
/// * `"sme"` - FEAT_SME
/// * `"sme-b16b16"` - FEAT_SME_B16B16
/// * `"sme-f16f16"` - FEAT_SME_F16F16
/// * `"sme-f64f64"` - FEAT_SME_F64F64
/// * `"sme-f8f16"` - FEAT_SME_F8F16
/// * `"sme-f8f32"` - FEAT_SME_F8F32
/// * `"sme-fa64"` - FEAT_SME_FA64
/// * `"sme-i16i64"` - FEAT_SME_I16I64
/// * `"sme-lutv2"` - FEAT_SME_LUTv2
/// * `"sme2"` - FEAT_SME2
/// * `"sme2p1"` - FEAT_SME2p1
/// * `"ssbs"` - FEAT_SSBS & FEAT_SSBS2
/// * `"ssve-fp8dot2"` - FEAT_SSVE_FP8DOT2
/// * `"ssve-fp8dot4"` - FEAT_SSVE_FP8DOT4
/// * `"ssve-fp8fma"` - FEAT_SSVE_FP8FMA
/// * `"sve"` - FEAT_SVE
/// * `"sve-b16b16"` - FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions)
/// * `"sve2"` - FEAT_SVE2
/// * `"sve2-aes"` - FEAT_SVE_AES & FEAT_SVE_PMULL128 (SVE2 AES crypto)
/// * `"sve2-bitperm"` - FEAT_SVE2_BitPerm
/// * `"sve2-sha3"` - FEAT_SVE2_SHA3
/// * `"sve2-sm4"` - FEAT_SVE2_SM4
/// * `"sve2p1"` - FEAT_SVE2p1
/// * `"tme"` - FEAT_TME
/// * `"wfxt"` - FEAT_WFxT
///
/// [docs]: https://developer.arm.com/documentation/ddi0487/latest
#[stable(feature = "simd_aarch64", since = "1.60.0")]
@BIND_FEATURE_NAME: "asimd"; "neon";
@NO_RUNTIME_DETECTION: "ras";
@NO_RUNTIME_DETECTION: "v8.1a";
@NO_RUNTIME_DETECTION: "v8.2a";
@NO_RUNTIME_DETECTION: "v8.3a";
@NO_RUNTIME_DETECTION: "v8.4a";
@NO_RUNTIME_DETECTION: "v8.5a";
@NO_RUNTIME_DETECTION: "v8.6a";
@NO_RUNTIME_DETECTION: "v8.7a";
@NO_RUNTIME_DETECTION: "v8.8a";
@NO_RUNTIME_DETECTION: "v8.9a";
@NO_RUNTIME_DETECTION: "v9.1a";
@NO_RUNTIME_DETECTION: "v9.2a";
@NO_RUNTIME_DETECTION: "v9.3a";
@NO_RUNTIME_DETECTION: "v9.4a";
@NO_RUNTIME_DETECTION: "v9.5a";
@NO_RUNTIME_DETECTION: "v9a";
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] asimd: "neon";
/// FEAT_AdvSIMD (Advanced SIMD/NEON)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] pmull: "pmull";
implied by target_features: ["aes"];
/// FEAT_PMULL (Polynomial Multiply) - Implied by `aes` target_feature
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp: "fp";
implied by target_features: ["neon"];
/// FEAT_FP (Floating point support) - Implied by `neon` target_feature
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] aes: "aes";
/// FEAT_AES (AES SIMD instructions) & FEAT_PMULL (PMULL{2}, 64-bit operand variants)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bf16: "bf16";
/// FEAT_BF16 (BFloat16 type, plus MM instructions, plus ASIMD support)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bti: "bti";
/// FEAT_BTI (Branch Target Identification)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] crc: "crc";
/// FEAT_CRC32 (Cyclic Redundancy Check)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] cssc: "cssc";
/// FEAT_CSSC (Common Short Sequence Compression instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dit: "dit";
/// FEAT_DIT (Data Independent Timing instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb: "dpb";
/// FEAT_DPB (aka dcpop - data cache clean to point of persistence)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb2: "dpb2";
/// FEAT_DPB2 (aka dcpodp - data cache clean to point of deep persistence)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dotprod: "dotprod";
/// FEAT_DotProd (Vector Dot-Product - ASIMDDP)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ecv: "ecv";
/// FEAT_ECV (Enhanced Counter Virtualization)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f32mm: "f32mm";
/// FEAT_F32MM (single-precision matrix multiplication)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f64mm: "f64mm";
/// FEAT_F64MM (double-precision matrix multiplication)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] faminmax: "faminmax";
/// FEAT_FAMINMAX (FAMIN and FAMAX SIMD/SVE/SME instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fcma: "fcma";
/// FEAT_FCMA (float complex number operations)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fhm: "fhm";
/// FEAT_FHM (fp16 multiplication instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] flagm: "flagm";
/// FEAT_FLAGM (flag manipulation instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] flagm2: "flagm2";
/// FEAT_FLAGM2 (flag manipulation instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp16: "fp16";
/// FEAT_FP16 (Half-float support)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8: "fp8";
/// FEAT_FP8 (F8CVT Instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8dot2: "fp8dot2";
/// FEAT_FP8DOT2 (F8DP2 Instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8dot4: "fp8dot4";
/// FEAT_FP8DOT4 (F8DP4 Instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fp8fma: "fp8fma";
/// FEAT_FP8FMA (F8FMA Instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] fpmr: "fpmr";
without cfg check: true;
/// FEAT_FPMR (Special-purpose AArch64-FPMR register)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] frintts: "frintts";
/// FEAT_FRINTTS (float to integer rounding instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] hbc: "hbc";
/// FEAT_HBC (Hinted conditional branches)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] i8mm: "i8mm";
/// FEAT_I8MM (integer matrix multiplication, plus ASIMD support)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] jsconv: "jsconv";
/// FEAT_JSCVT (JavaScript float conversion instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse: "lse";
/// FEAT_LSE (Large System Extension - atomics)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] lse128: "lse128";
/// FEAT_LSE128 (128-bit atomics)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse2: "lse2";
/// FEAT_LSE2 (unaligned and register-pair atomics)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] lut: "lut";
/// FEAT_LUT (Lookup Table Instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] mops: "mops";
/// FEAT_MOPS (Standardization of memory operations)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] mte: "mte";
/// FEAT_MTE & FEAT_MTE2 (Memory Tagging Extension)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] paca: "paca";
/// FEAT_PAuth (address authentication)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] pacg: "pacg";
/// FEAT_PAuth (generic authentication)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] pauth_lr: "pauth-lr";
/// FEAT_PAuth_LR
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rand: "rand";
/// FEAT_RNG (Random Number Generator)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc: "rcpc";
/// FEAT_LRCPC (Release consistent Processor consistent)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc2: "rcpc2";
/// FEAT_LRCPC2 (RCPC with immediate offsets)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] rcpc3: "rcpc3";
/// FEAT_LRCPC3 (RCPC Instructions v3)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rdm: "rdm";
/// FEAT_RDM (Rounding Doubling Multiply - ASIMDRDM)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sb: "sb";
/// FEAT_SB (speculation barrier)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha2: "sha2";
/// FEAT_SHA1 & FEAT_SHA256 (SHA1 & SHA2-256 instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha3: "sha3";
/// FEAT_SHA512 & FEAT_SHA3 (SHA2-512 & SHA3 instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sm4: "sm4";
/// FEAT_SM3 & FEAT_SM4 (SM3 & SM4 instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme: "sme";
/// FEAT_SME (Scalable Matrix Extension)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme2: "sme2";
/// FEAT_SME2 (SME Version 2)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme2p1: "sme2p1";
/// FEAT_SME2p1 (SME Version 2.1)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_b16b16: "sme-b16b16";
/// FEAT_SME_B16B16
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f16f16: "sme-f16f16";
/// FEAT_SME_F16F16 (Non-widening half-precision FP16 to FP16 arithmetic for SME2)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f64f64: "sme-f64f64";
/// FEAT_SME_F64F64 (Double-precision floating-point outer product instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f8f16: "sme-f8f16";
/// FEAT_SME_F8F16
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_f8f32: "sme-f8f32";
/// FEAT_SME_F8F32
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_fa64: "sme-fa64";
/// FEAT_SME_FA64 (Full A64 instruction set support in Streaming SVE mode)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_i16i64: "sme-i16i64";
/// FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sme_lutv2: "sme-lutv2";
/// FEAT_SME_LUTv2 (LUTI4 Instruction)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] ssbs: "ssbs";
/// FEAT_SSBS & FEAT_SSBS2 (speculative store bypass safe)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ssve_fp8dot2: "ssve-fp8dot2";
/// FEAT_SSVE_FP8DOT2
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ssve_fp8dot4: "ssve-fp8dot4";
/// FEAT_SSVE_FP8DOT4
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] ssve_fp8fma: "ssve-fp8fma";
/// FEAT_SSVE_FP8FMA
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve: "sve";
/// FEAT_SVE (Scalable Vector Extension)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2: "sve2";
/// FEAT_SVE2 (Scalable Vector Extension 2)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sve2p1: "sve2p1";
/// FEAT_SVE2p1 (Scalable Vector Extension 2.1)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_aes: "sve2-aes";
/// FEAT_SVE_AES & FEAT_SVE_PMULL128 (SVE2 AES crypto)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] sve_b16b16: "sve-b16b16";
/// FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_bitperm: "sve2-bitperm";
/// FEAT_SVE_BitPerm (SVE2 bit permutation instructions)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sha3: "sve2-sha3";
/// FEAT_SVE_SHA3 (SVE2 SHA3 crypto)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sm4: "sve2-sm4";
/// FEAT_SVE_SM4 (SVE2 SM4 crypto)
@FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] tme: "tme";
/// FEAT_TME (Transactional Memory Extensions)
@FEATURE: #[unstable(feature = "stdarch_aarch64_feature_detection", issue = "127764")] wfxt: "wfxt";
/// FEAT_WFxT (WFET and WFIT Instructions)
}

View File

@@ -0,0 +1,32 @@
//! Run-time feature detection on ARM Aarch32.
features! {
@TARGET: arm;
@CFG: target_arch = "arm";
@MACRO_NAME: is_arm_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
#[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")]
@NO_RUNTIME_DETECTION: "v7";
@NO_RUNTIME_DETECTION: "vfp2";
@NO_RUNTIME_DETECTION: "vfp3";
@NO_RUNTIME_DETECTION: "vfp4";
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] neon: "neon";
/// ARM Advanced SIMD (NEON) - Aarch32
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] pmull: "pmull";
without cfg check: true;
/// Polynomial Multiply
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] crc: "crc";
/// CRC32 (Cyclic Redundancy Check)
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] aes: "aes";
/// FEAT_AES (AES instructions)
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] sha2: "sha2";
/// FEAT_SHA1 & FEAT_SHA256 (SHA1 & SHA2-256 instructions)
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] i8mm: "i8mm";
/// FEAT_I8MM (integer matrix multiplication, plus ASIMD support)
@FEATURE: #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")] dotprod: "dotprod";
/// FEAT_DotProd (Vector Dot-Product - ASIMDDP)
}

View File

@@ -0,0 +1,58 @@
//! Run-time feature detection on LoongArch.
features! {
@TARGET: loongarch;
@CFG: any(target_arch = "loongarch32", target_arch = "loongarch64");
@MACRO_NAME: is_loongarch_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
///
/// Supported arguments are:
///
/// * `"32s"`
/// * `"f"`
/// * `"d"`
/// * `"frecipe"`
/// * `"div32"`
/// * `"lsx"`
/// * `"lasx"`
/// * `"lam-bh"`
/// * `"lamcas"`
/// * `"ld-seq-sa"`
/// * `"scq"`
/// * `"lbt"`
/// * `"lvz"`
/// * `"ual"`
#[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")]
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] _32s: "32s";
/// 32S
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] f: "f";
/// F
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] d: "d";
/// D
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] frecipe: "frecipe";
/// Frecipe
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] div32: "div32";
/// Div32
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lsx: "lsx";
/// LSX
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lasx: "lasx";
/// LASX
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] lam_bh: "lam-bh";
/// LAM-BH
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] lamcas: "lamcas";
/// LAM-CAS
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] ld_seq_sa: "ld-seq-sa";
/// LD-SEQ-SA
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] scq: "scq";
/// SCQ
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lbt: "lbt";
/// LBT
@FEATURE: #[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")] lvz: "lvz";
/// LVZ
@FEATURE: #[unstable(feature = "stdarch_loongarch_feature_detection", issue = "117425")] ual: "ual";
/// UAL
}

View File

@@ -0,0 +1,15 @@
//! Run-time feature detection on MIPS.
features! {
@TARGET: mips;
@CFG: target_arch = "mips";
@MACRO_NAME: is_mips_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
#[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")]
@FEATURE: #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] msa: "msa";
/// MIPS SIMD Architecture (MSA)
}

View File

@@ -0,0 +1,15 @@
//! Run-time feature detection on MIPS64.
features! {
@TARGET: mips64;
@CFG: target_arch = "mips64";
@MACRO_NAME: is_mips64_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
#[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")]
@FEATURE: #[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")] msa: "msa";
/// MIPS SIMD Architecture (MSA)
}

View File

@@ -0,0 +1,83 @@
#![allow(dead_code)]
// Export the macros for all supported architectures.
#[macro_use]
mod x86;
#[macro_use]
mod arm;
#[macro_use]
mod aarch64;
#[macro_use]
mod riscv;
#[macro_use]
mod powerpc;
#[macro_use]
mod powerpc64;
#[macro_use]
mod mips;
#[macro_use]
mod mips64;
#[macro_use]
mod loongarch;
#[macro_use]
mod s390x;
cfg_select! {
any(target_arch = "x86", target_arch = "x86_64") => {
#[stable(feature = "simd_x86", since = "1.27.0")]
pub use x86::*;
}
target_arch = "arm" => {
#[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")]
pub use arm::*;
}
any(target_arch = "aarch64", target_arch = "arm64ec") => {
#[stable(feature = "simd_aarch64", since = "1.60.0")]
pub use aarch64::*;
}
any(target_arch = "riscv32", target_arch = "riscv64") => {
#[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")]
pub use riscv::*;
}
target_arch = "powerpc" => {
#[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")]
pub use powerpc::*;
}
target_arch = "powerpc64" => {
#[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")]
pub use powerpc64::*;
}
target_arch = "mips" => {
#[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")]
pub use mips::*;
}
target_arch = "mips64" => {
#[unstable(feature = "stdarch_mips_feature_detection", issue = "111188")]
pub use mips64::*;
}
any(target_arch = "loongarch32", target_arch = "loongarch64") => {
#[stable(feature = "stdarch_loongarch_feature", since = "1.89.0")]
pub use loongarch::*;
}
target_arch = "s390x" => {
#[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")]
pub use s390x::*;
}
_ => {
// Unimplemented architecture:
#[doc(hidden)]
pub(crate) enum Feature {
Null
}
#[doc(hidden)]
#[unstable(feature = "stdarch_internal", issue = "none")]
pub mod __is_feature_detected {}
impl Feature {
#[doc(hidden)]
pub(crate) fn from_str(_s: &str) -> Result<Feature, ()> { Err(()) }
#[doc(hidden)]
pub(crate) fn to_str(self) -> &'static str { "" }
}
}
}

View File

@@ -0,0 +1,33 @@
//! Run-time feature detection on PowerPC.
features! {
@TARGET: powerpc;
@CFG: target_arch = "powerpc";
@MACRO_NAME: is_powerpc_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
#[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")]
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] altivec: "altivec";
/// Altivec
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] vsx: "vsx";
/// VSX
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8: "power8";
without cfg check: true;
/// Power8
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_altivec: "power8-altivec";
/// Power8 altivec
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_vector: "power8-vector";
/// Power8 vector
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_crypto: "power8-crypto";
/// Power8 crypto
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9: "power9";
without cfg check: true;
/// Power9
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_altivec: "power9-altivec";
/// Power9 altivec
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_vector: "power9-vector";
/// Power9 vector
}

View File

@@ -0,0 +1,33 @@
//! Run-time feature detection on PowerPC64.
features! {
@TARGET: powerpc64;
@CFG: target_arch = "powerpc64";
@MACRO_NAME: is_powerpc64_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
#[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")]
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] altivec: "altivec";
/// Altivec
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] vsx: "vsx";
/// VSX
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8: "power8";
without cfg check: true;
/// Power8
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_altivec: "power8-altivec";
/// Power8 altivec
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_vector: "power8-vector";
/// Power8 vector
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power8_crypto: "power8-crypto";
/// Power8 crypto
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9: "power9";
without cfg check: true;
/// Power9
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_altivec: "power9-altivec";
/// Power9 altivec
@FEATURE: #[unstable(feature = "stdarch_powerpc_feature_detection", issue = "111191")] power9_vector: "power9-vector";
/// Power9 vector
}

View File

@@ -0,0 +1,380 @@
//! Run-time feature detection on RISC-V.
features! {
@TARGET: riscv;
@CFG: any(target_arch = "riscv32", target_arch = "riscv64");
@MACRO_NAME: is_riscv_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
///
/// RISC-V standard defined the base sets and the extension sets.
/// The base sets are RV32I, RV64I, RV32E or RV128I. Any RISC-V platform
/// must support one base set and/or multiple extension sets.
///
/// Any RISC-V standard instruction sets can be in state of either ratified,
/// frozen or draft. The version and status of current standard instruction
/// sets can be checked out from preface section of the [ISA manual].
///
/// Platform may define and support their own custom instruction sets with
/// ISA prefix X. These sets are highly platform specific and should be
/// detected with their own platform support crates.
///
/// [ISA manual]: https://riscv.org/specifications/ratified/
///
/// # Platform-specific/agnostic Behavior and Availability
///
/// Runtime detection depends on the platform-specific feature detection
/// facility and its availability per feature is
/// highly platform/version-specific.
///
/// Still, a best-effort attempt is performed to enable subset/dependent
/// features if a superset feature is enabled regardless of the platform.
/// For instance, if the A extension (`"a"`) is enabled, its subsets (the
/// Zalrsc and Zaamo extensions; `"zalrsc"` and `"zaamo"`) are also enabled.
/// Likewise, if the F extension (`"f"`) is enabled, one of its dependencies
/// (the Zicsr extension `"zicsr"`) is also enabled.
///
/// # Unprivileged Specification
///
/// The supported ratified RISC-V instruction sets are as follows (OS
/// columns denote runtime feature detection support with or without the
/// minimum supported version):
///
/// | Literal | Base | Linux |
/// |:---------- |:------- |:---------- |
/// | `"rv32e"` | RV32E | No |
/// | `"rv32i"` | RV32I | Yes [^ima] |
/// | `"rv64i"` | RV64I | Yes [^ima] |
///
/// | Literal | Extension | Linux |
/// |:--------------- |:----------- |:------------------- |
/// | `"a"` | A | Yes [^ima] |
/// | `"b"` | B | 6.5 |
/// | `"c"` | C | Yes |
/// | `"d"` | D | Yes |
/// | `"f"` | F | Yes |
/// | `"m"` | M | Yes [^ima] |
/// | `"q"` | Q | No |
/// | `"v"` | V | 6.5 |
/// | `"zaamo"` | Zaamo | 6.15 [^ima] [^dep] |
/// | `"zabha"` | Zabha | 6.16 |
/// | `"zacas"` | Zacas | 6.8 |
/// | `"zalrsc"` | Zalrsc | 6.15 [^ima] [^dep] |
/// | `"zawrs"` | Zawrs | 6.11 |
/// | `"zba"` | Zba | 6.5 |
/// | `"zbb"` | Zbb | 6.5 |
/// | `"zbc"` | Zbc | 6.8 |
/// | `"zbkb"` | Zbkb | 6.8 |
/// | `"zbkc"` | Zbkc | 6.8 |
/// | `"zbkx"` | Zbkx | 6.8 |
/// | `"zbs"` | Zbs | 6.5 |
/// | `"zca"` | Zca | 6.11 [^dep] |
/// | `"zcb"` | Zcb | 6.11 |
/// | `"zcd"` | Zcd | 6.11 [^dep] |
/// | `"zcf"` | Zcf | 6.11 [^dep] |
/// | `"zcmop"` | Zcmop | 6.11 |
/// | `"zdinx"` | Zdinx | No |
/// | `"zfa"` | Zfa | 6.8 |
/// | `"zfbfmin"` | Zfbfmin | 6.15 |
/// | `"zfh"` | Zfh | 6.8 |
/// | `"zfhmin"` | Zfhmin | 6.8 |
/// | `"zfinx"` | Zfinx | No |
/// | `"zhinx"` | Zhinx | No |
/// | `"zhinxmin"` | Zhinxmin | No |
/// | `"zicbom"` | Zicbom | 6.15 |
/// | `"zicboz"` | Zicboz | 6.7 |
/// | `"zicntr"` | Zicntr | 6.15 [^ima] [^cntr] |
/// | `"zicond"` | Zicond | 6.8 |
/// | `"zicsr"` | Zicsr | No [^ima] [^dep] |
/// | `"zifencei"` | Zifencei | No [^ima] |
/// | `"zihintntl"` | Zihintntl | 6.8 |
/// | `"zihintpause"` | Zihintpause | 6.10 |
/// | `"zihpm"` | Zihpm | 6.15 [^cntr] |
/// | `"zimop"` | Zimop | 6.11 |
/// | `"zk"` | Zk | No [^zkr] |
/// | `"zkn"` | Zkn | 6.8 |
/// | `"zknd"` | Zknd | 6.8 |
/// | `"zkne"` | Zkne | 6.8 |
/// | `"zknh"` | Zknh | 6.8 |
/// | `"zkr"` | Zkr | No [^zkr] |
/// | `"zks"` | Zks | 6.8 |
/// | `"zksed"` | Zksed | 6.8 |
/// | `"zksh"` | Zksh | 6.8 |
/// | `"zkt"` | Zkt | 6.8 |
/// | `"ztso"` | Ztso | 6.8 |
/// | `"zvbb"` | Zvbb | 6.8 |
/// | `"zvbc"` | Zvbc | 6.8 |
/// | `"zve32f"` | Zve32f | 6.11 [^dep] |
/// | `"zve32x"` | Zve32x | 6.11 [^dep] |
/// | `"zve64d"` | Zve64d | 6.11 [^dep] |
/// | `"zve64f"` | Zve64f | 6.11 [^dep] |
/// | `"zve64x"` | Zve64x | 6.11 [^dep] |
/// | `"zvfbfmin"` | Zvfbfmin | 6.15 |
/// | `"zvfbfwma"` | Zvfbfwma | 6.15 |
/// | `"zvfh"` | Zvfh | 6.8 |
/// | `"zvfhmin"` | Zvfhmin | 6.8 |
/// | `"zvkb"` | Zvkb | 6.8 |
/// | `"zvkg"` | Zvkg | 6.8 |
/// | `"zvkn"` | Zvkn | 6.8 |
/// | `"zvknc"` | Zvknc | 6.8 |
/// | `"zvkned"` | Zvkned | 6.8 |
/// | `"zvkng"` | Zvkng | 6.8 |
/// | `"zvknha"` | Zvknha | 6.8 |
/// | `"zvknhb"` | Zvknhb | 6.8 |
/// | `"zvks"` | Zvks | 6.8 |
/// | `"zvksc"` | Zvksc | 6.8 |
/// | `"zvksed"` | Zvksed | 6.8 |
/// | `"zvksg"` | Zvksg | 6.8 |
/// | `"zvksh"` | Zvksh | 6.8 |
/// | `"zvkt"` | Zvkt | 6.8 |
///
/// [^ima]: Or enabled when the IMA base behavior is detected on the Linux
/// kernel version 6.4 or later (for bases, the only matching one -- either
/// `"rv32i"` or `"rv64i"` -- is enabled).
///
/// [^cntr]: Even if this extension is available, it does not necessarily
/// mean all performance counters are accessible.
/// For example, accesses to all performance counters except `time`
/// (wall-clock) are blocked by default on the Linux kernel
/// version 6.6 or later.
/// Also beware that, even if performance counters like `cycle` and
/// `instret` are accessible, their value can be unreliable (e.g. returning
/// the constant value) under certain circumstances.
///
/// [^dep]: Or enabled as a dependency of another extension (a superset)
/// even if runtime detection of this feature itself is not supported (as
/// long as the runtime detection of the superset is supported).
///
/// [^zkr]: Linux does not report existence of this extension even if
/// supported by the hardware mainly because the `seed` CSR on the Zkr
/// extension (which provides hardware-based randomness) is normally
/// inaccessible from the user mode.
/// For the Zk extension features except this CSR, check existence of both
/// `"zkn"` and `"zkt"` features instead.
///
/// There's also bases and extensions marked as standard instruction set,
/// but they are in frozen or draft state. These instruction sets are also
/// reserved by this macro and can be detected in the future platforms.
///
/// Draft RISC-V instruction sets:
///
/// * RV128I: `"rv128i"`
/// * J: `"j"`
/// * P: `"p"`
/// * Zam: `"zam"`
///
/// # Performance Hints
///
/// The two features below define performance hints for unaligned
/// scalar/vector memory accesses, respectively. If enabled, it denotes that
/// corresponding unaligned memory access is reasonably fast.
///
/// * `"unaligned-scalar-mem"`
/// * Runtime detection requires Linux kernel version 6.4 or later.
/// * `"unaligned-vector-mem"`
/// * Runtime detection requires Linux kernel version 6.13 or later.
#[stable(feature = "riscv_ratified", since = "1.78.0")]
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv32i: "rv32i";
without cfg check: true;
/// RV32I Base Integer Instruction Set
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv32e: "rv32e";
without cfg check: true;
/// RV32E Base Integer Instruction Set
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv64i: "rv64i";
without cfg check: true;
/// RV64I Base Integer Instruction Set
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] rv128i: "rv128i";
without cfg check: true;
/// RV128I Base Integer Instruction Set
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_scalar_mem: "unaligned-scalar-mem";
/// Has reasonably performant unaligned scalar
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] unaligned_vector_mem: "unaligned-vector-mem";
/// Has reasonably performant unaligned vector
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicsr: "zicsr";
/// "Zicsr" Extension for Control and Status Register (CSR) Instructions
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicntr: "zicntr";
/// "Zicntr" Extension for Base Counters and Timers
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zihpm: "zihpm";
/// "Zihpm" Extension for Hardware Performance Counters
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zifencei: "zifencei";
/// "Zifencei" Extension for Instruction-Fetch Fence
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zihintntl: "zihintntl";
/// "Zihintntl" Extension for Non-Temporal Locality Hints
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zihintpause: "zihintpause";
/// "Zihintpause" Extension for Pause Hint
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zimop: "zimop";
/// "Zimop" Extension for May-Be-Operations
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicbom: "zicbom";
/// "Zicbom" Extension for Cache-Block Management Instructions
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicboz: "zicboz";
/// "Zicboz" Extension for Cache-Block Zero Instruction
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zicond: "zicond";
/// "Zicond" Extension for Integer Conditional Operations
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] m: "m";
/// "M" Extension for Integer Multiplication and Division
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] a: "a";
/// "A" Extension for Atomic Instructions
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zalrsc: "zalrsc";
/// "Zalrsc" Extension for Load-Reserved/Store-Conditional Instructions
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zaamo: "zaamo";
/// "Zaamo" Extension for Atomic Memory Operations
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zawrs: "zawrs";
/// "Zawrs" Extension for Wait-on-Reservation-Set Instructions
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zabha: "zabha";
/// "Zabha" Extension for Byte and Halfword Atomic Memory Operations
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zacas: "zacas";
/// "Zacas" Extension for Atomic Compare-and-Swap (CAS) Instructions
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zam: "zam";
without cfg check: true;
/// "Zam" Extension for Misaligned Atomics
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] ztso: "ztso";
/// "Ztso" Extension for Total Store Ordering
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] f: "f";
/// "F" Extension for Single-Precision Floating-Point
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] d: "d";
/// "D" Extension for Double-Precision Floating-Point
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] q: "q";
without cfg check: true;
/// "Q" Extension for Quad-Precision Floating-Point
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfh: "zfh";
/// "Zfh" Extension for Half-Precision Floating-Point
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfhmin: "zfhmin";
/// "Zfhmin" Extension for Minimal Half-Precision Floating-Point
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfa: "zfa";
/// "Zfa" Extension for Additional Floating-Point Instructions
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfbfmin: "zfbfmin";
/// "Zfbfmin" Extension for Scalar BF16 Converts
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zfinx: "zfinx";
/// "Zfinx" Extension for Single-Precision Floating-Point in Integer Registers
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zdinx: "zdinx";
/// "Zdinx" Extension for Double-Precision Floating-Point in Integer Registers
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zhinx: "zhinx";
/// "Zhinx" Extension for Half-Precision Floating-Point in Integer Registers
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zhinxmin: "zhinxmin";
/// "Zhinxmin" Extension for Minimal Half-Precision Floating-Point in Integer Registers
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] c: "c";
/// "C" Extension for Compressed Instructions
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zca: "zca";
/// "Zca" Compressed Instructions excluding Floating-Point Loads/Stores
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcf: "zcf";
without cfg check: true;
/// "Zcf" Compressed Instructions for Single-Precision Floating-Point Loads/Stores on RV32
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zcd: "zcd";
without cfg check: true;
/// "Zcd" Compressed Instructions for Double-Precision Floating-Point Loads/Stores
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zcb: "zcb";
/// "Zcb" Simple Code-size Saving Compressed Instructions
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] zcmop: "zcmop";
/// "Zcmop" Extension for Compressed May-Be-Operations
@FEATURE: #[stable(feature = "riscv_ratified_v2", since = "1.94.0")] b: "b";
/// "B" Extension for Bit Manipulation
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zba: "zba";
/// "Zba" Extension for Address Generation
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbb: "zbb";
/// "Zbb" Extension for Basic Bit-Manipulation
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbc: "zbc";
/// "Zbc" Extension for Carry-less Multiplication
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbs: "zbs";
/// "Zbs" Extension for Single-Bit Instructions
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbkb: "zbkb";
/// "Zbkb" Extension for Bit-Manipulation for Cryptography
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbkc: "zbkc";
/// "Zbkc" Extension for Carry-less Multiplication for Cryptography
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zbkx: "zbkx";
/// "Zbkx" Extension for Crossbar Permutations
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zknd: "zknd";
/// "Zknd" Cryptography Extension for NIST Suite: AES Decryption
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkne: "zkne";
/// "Zkne" Cryptography Extension for NIST Suite: AES Encryption
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zknh: "zknh";
/// "Zknh" Cryptography Extension for NIST Suite: Hash Function Instructions
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zksed: "zksed";
/// "Zksed" Cryptography Extension for ShangMi Suite: SM4 Block Cipher Instructions
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zksh: "zksh";
/// "Zksh" Cryptography Extension for ShangMi Suite: SM3 Hash Function Instructions
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkr: "zkr";
/// "Zkr" Entropy Source Extension
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkn: "zkn";
/// "Zkn" Cryptography Extension for NIST Algorithm Suite
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zks: "zks";
/// "Zks" Cryptography Extension for ShangMi Algorithm Suite
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zk: "zk";
/// "Zk" Cryptography Extension for Standard Scalar Cryptography
@FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] zkt: "zkt";
/// "Zkt" Cryptography Extension for Data Independent Execution Latency
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] v: "v";
/// "V" Extension for Vector Operations
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32x: "zve32x";
/// "Zve32x" Vector Extension for Embedded Processors (32-bit+; Integer)
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve32f: "zve32f";
/// "Zve32f" Vector Extension for Embedded Processors (32-bit+; with Single-Precision Floating-Point)
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64x: "zve64x";
/// "Zve64x" Vector Extension for Embedded Processors (64-bit+; Integer)
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64f: "zve64f";
/// "Zve64f" Vector Extension for Embedded Processors (64-bit+; with Single-Precision Floating-Point)
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zve64d: "zve64d";
/// "Zve64d" Vector Extension for Embedded Processors (64-bit+; with Double-Precision Floating-Point)
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfh: "zvfh";
/// "Zvfh" Vector Extension for Half-Precision Floating-Point
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfhmin: "zvfhmin";
/// "Zvfhmin" Vector Extension for Minimal Half-Precision Floating-Point
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfbfmin: "zvfbfmin";
/// "Zvfbfmin" Vector Extension for BF16 Converts
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvfbfwma: "zvfbfwma";
/// "Zvfbfwma" Vector Extension for BF16 Widening Multiply-Add
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbb: "zvbb";
/// "Zvbb" Extension for Vector Basic Bit-Manipulation
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvbc: "zvbc";
/// "Zvbc" Extension for Vector Carryless Multiplication
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkb: "zvkb";
/// "Zvkb" Extension for Vector Cryptography Bit-Manipulation
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkg: "zvkg";
/// "Zvkg" Cryptography Extension for Vector GCM/GMAC
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkned: "zvkned";
/// "Zvkned" Cryptography Extension for NIST Suite: Vector AES Block Cipher
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknha: "zvknha";
/// "Zvknha" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256)
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknhb: "zvknhb";
/// "Zvknhb" Cryptography Extension for Vector SHA-2 Secure Hash (SHA-256/512)
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksed: "zvksed";
/// "Zvksed" Cryptography Extension for ShangMi Suite: Vector SM4 Block Cipher
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksh: "zvksh";
/// "Zvksh" Cryptography Extension for ShangMi Suite: Vector SM3 Secure Hash
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkn: "zvkn";
/// "Zvkn" Cryptography Extension for NIST Algorithm Suite
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvknc: "zvknc";
/// "Zvknc" Cryptography Extension for NIST Algorithm Suite with Carryless Multiply
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkng: "zvkng";
/// "Zvkng" Cryptography Extension for NIST Algorithm Suite with GCM
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvks: "zvks";
/// "Zvks" Cryptography Extension for ShangMi Algorithm Suite
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksc: "zvksc";
/// "Zvksc" Cryptography Extension for ShangMi Algorithm Suite with Carryless Multiply
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvksg: "zvksg";
/// "Zvksg" Cryptography Extension for ShangMi Algorithm Suite with GCM
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zvkt: "zvkt";
/// "Zvkt" Extension for Vector Data-Independent Execution Latency
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] j: "j";
without cfg check: true;
/// "J" Extension for Dynamically Translated Languages
@FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] p: "p";
without cfg check: true;
/// "P" Extension for Packed-SIMD Instructions
}

View File

@@ -0,0 +1,61 @@
//! Run-time feature detection on s390x.
features! {
@TARGET: s390x;
@CFG: target_arch = "s390x";
@MACRO_NAME: is_s390x_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
#[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")]
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] concurrent_functions: "concurrent-functions";
/// s390x concurrent-functions facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] deflate_conversion: "deflate-conversion";
/// s390x deflate-conversion facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] enhanced_sort: "enhanced-sort";
/// s390x enhanced-sort facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] guarded_storage: "guarded-storage";
/// s390x guarded-storage facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] high_word: "high-word";
/// s390x high-word facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension3: "message-security-assist-extension3";
/// s390x message-security-assist-extension3 facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension4: "message-security-assist-extension4";
/// s390x message-security-assist-extension4 facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension5: "message-security-assist-extension5";
/// s390x message-security-assist-extension5 facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension8: "message-security-assist-extension8";
/// s390x message-security-assist-extension8 facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension9: "message-security-assist-extension9";
/// s390x message-security-assist-extension9 facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] message_security_assist_extension12: "message-security-assist-extension12";
/// s390x message-security-assist-extension12 facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_2: "miscellaneous-extensions-2";
/// s390x miscellaneous-extensions-2 facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_3: "miscellaneous-extensions-3";
/// s390x miscellaneous-extensions-3 facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_4: "miscellaneous-extensions-4";
/// s390x miscellaneous-extensions-4 facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] nnp_assist: "nnp-assist";
/// s390x nnp-assist facility
@FEATURE: #[unstable(feature = "s390x_target_feature", issue = "150259")] transactional_execution: "transactional-execution";
/// s390x transactional-execution facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector: "vector";
/// s390x vector facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_1: "vector-enhancements-1";
/// s390x vector-enhancements-1 facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_2: "vector-enhancements-2";
/// s390x vector-enhancements-2 facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_3: "vector-enhancements-3";
/// s390x vector-enhancements-3 facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal: "vector-packed-decimal";
/// s390x vector-packed-decimal facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement: "vector-packed-decimal-enhancement";
/// s390x vector-packed-decimal-enhancement facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement_2: "vector-packed-decimal-enhancement-2";
/// s390x vector-packed-decimal-enhancement-2 facility
@FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement_3: "vector-packed-decimal-enhancement-3";
/// s390x vector-packed-decimal-enhancement-3 facility
}

View File

@@ -0,0 +1,280 @@
//! This module implements minimal run-time feature detection for x86.
//!
//! The features are detected using the `detect_features` function below.
//! This function uses the CPUID instruction to read the feature flags from the
//! CPU and encodes them in a `usize` where each bit position represents
//! whether a feature is available (bit is set) or unavailable (bit is cleared).
//!
//! The enum `Feature` is used to map bit positions to feature names, and the
//! the `__crate::detect::check_for!` macro is used to map string literals (e.g.,
//! "avx") to these bit positions (e.g., `Feature::avx`).
//!
//! The run-time feature detection is performed by the
//! `__crate::detect::check_for(Feature) -> bool` function. On its first call,
//! this functions queries the CPU for the available features and stores them
//! in a global `AtomicUsize` variable. The query is performed by just checking
//! whether the feature bit in this global variable is set or cleared.
features! {
@TARGET: x86;
@CFG: any(target_arch = "x86", target_arch = "x86_64");
@MACRO_NAME: is_x86_feature_detected;
@MACRO_ATTRS:
/// Check for the presence of a CPU feature at runtime.
///
/// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`)
/// the macro expands to `true`.
///
/// Runtime detection currently relies mostly on the `cpuid` instruction.
///
/// This macro only takes one argument which is a string literal of the feature
/// being tested for. The feature names supported are the lowercase versions of
/// the ones defined by Intel in [their documentation][docs].
///
/// ## Supported arguments
///
/// This macro supports the same names that `#[target_feature]` supports. Unlike
/// `#[target_feature]`, however, this macro does not support names separated
/// with a comma. Instead testing for multiple features must be done through
/// separate macro invocations for now.
///
/// Supported arguments are:
///
/// * `"aes"`
/// * `"pclmulqdq"`
/// * `"rdrand"`
/// * `"rdseed"`
/// * `"tsc"`
/// * `"mmx"`
/// * `"sse"`
/// * `"sse2"`
/// * `"sse3"`
/// * `"ssse3"`
/// * `"sse4.1"`
/// * `"sse4.2"`
/// * `"sse4a"`
/// * `"sha"`
/// * `"avx"`
/// * `"avx2"`
/// * `"sha512"`
/// * `"sm3"`
/// * `"sm4"`
/// * `"avx512f"`
/// * `"avx512cd"`
/// * `"avx512er"`
/// * `"avx512pf"`
/// * `"avx512bw"`
/// * `"avx512dq"`
/// * `"avx512vl"`
/// * `"avx512ifma"`
/// * `"avx512vbmi"`
/// * `"avx512vpopcntdq"`
/// * `"avx512vbmi2"`
/// * `"gfni"`
/// * `"vaes"`
/// * `"vpclmulqdq"`
/// * `"avx512vnni"`
/// * `"avx512bitalg"`
/// * `"avx512bf16"`
/// * `"avx512vp2intersect"`
/// * `"avx512fp16"`
/// * `"avxvnni"`
/// * `"avxifma"`
/// * `"avxneconvert"`
/// * `"avxvnniint8"`
/// * `"avxvnniint16"`
/// * `"amx-tile"`
/// * `"amx-int8"`
/// * `"amx-bf16"`
/// * `"amx-fp16"`
/// * `"amx-complex"`
/// * `"amx-avx512"`
/// * `"amx-fp8"`
/// * `"amx-movrs"`
/// * `"amx-tf32"`
/// * `"f16c"`
/// * `"fma"`
/// * `"bmi1"`
/// * `"bmi2"`
/// * `"abm"`
/// * `"lzcnt"`
/// * `"tbm"`
/// * `"popcnt"`
/// * `"fxsr"`
/// * `"xsave"`
/// * `"xsaveopt"`
/// * `"xsaves"`
/// * `"xsavec"`
/// * `"cmpxchg16b"`
/// * `"kl"`
/// * `"widekl"`
/// * `"adx"`
/// * `"rtm"`
/// * `"movbe"`
/// * `"ermsb"`
/// * `"movrs"`
/// * `"xop"`
///
/// [docs]: https://software.intel.com/sites/landingpage/IntrinsicsGuide
#[stable(feature = "simd_x86", since = "1.27.0")]
@BIND_FEATURE_NAME: "abm"; "lzcnt"; // abm is a synonym for lzcnt
@BIND_FEATURE_NAME: "avx512gfni"; "gfni"; #[deprecated(since = "1.67.0", note = "the `avx512gfni` feature has been renamed to `gfni`")];
@BIND_FEATURE_NAME: "avx512vaes"; "vaes"; #[deprecated(since = "1.67.0", note = "the `avx512vaes` feature has been renamed to `vaes`")];
@BIND_FEATURE_NAME: "avx512vpclmulqdq"; "vpclmulqdq"; #[deprecated(since = "1.67.0", note = "the `avx512vpclmulqdq` feature has been renamed to `vpclmulqdq`")];
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] aes: "aes";
/// AES (Advanced Encryption Standard New Instructions AES-NI)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] pclmulqdq: "pclmulqdq";
/// CLMUL (Carry-less Multiplication)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rdrand: "rdrand";
/// RDRAND
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rdseed: "rdseed";
/// RDSEED
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] tsc: "tsc";
without cfg check: true;
/// TSC (Time Stamp Counter)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] mmx: "mmx";
without cfg check: true;
/// MMX (MultiMedia eXtensions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse: "sse";
/// SSE (Streaming SIMD Extensions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse2: "sse2";
/// SSE2 (Streaming SIMD Extensions 2)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse3: "sse3";
/// SSE3 (Streaming SIMD Extensions 3)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] ssse3: "ssse3";
/// SSSE3 (Supplemental Streaming SIMD Extensions 3)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_1: "sse4.1";
/// SSE4.1 (Streaming SIMD Extensions 4.1)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4_2: "sse4.2";
/// SSE4.2 (Streaming SIMD Extensions 4.2)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sse4a: "sse4a";
/// SSE4a (Streaming SIMD Extensions 4a)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] sha: "sha";
/// SHA
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx: "avx";
/// AVX (Advanced Vector Extensions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx2: "avx2";
/// AVX2 (Advanced Vector Extensions 2)
@FEATURE: #[stable(feature = "sha512_sm_x86", since = "1.89.0")] sha512: "sha512";
/// SHA512
@FEATURE: #[stable(feature = "sha512_sm_x86", since = "1.89.0")] sm3: "sm3";
/// SM3
@FEATURE: #[stable(feature = "sha512_sm_x86", since = "1.89.0")] sm4: "sm4";
/// SM4
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512f: "avx512f" ;
/// AVX-512 F (Foundation)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512cd: "avx512cd" ;
/// AVX-512 CD (Conflict Detection Instructions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512er: "avx512er";
without cfg check: true;
/// AVX-512 ER (Expo nential and Reciprocal Instructions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512pf: "avx512pf";
without cfg check: true;
/// AVX-512 PF (Prefetch Instructions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bw: "avx512bw";
/// AVX-512 BW (Byte and Word Instructions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512dq: "avx512dq";
/// AVX-512 DQ (Doubleword and Quadword)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vl: "avx512vl";
/// AVX-512 VL (Vector Length Extensions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512ifma: "avx512ifma";
/// AVX-512 IFMA (Integer Fused Multiply Add)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vbmi: "avx512vbmi";
/// AVX-512 VBMI (Vector Byte Manipulation Instructions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vpopcntdq: "avx512vpopcntdq";
/// AVX-512 VPOPCNTDQ (Vector Population Count Doubleword and Quadword)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vbmi2: "avx512vbmi2";
/// AVX-512 VBMI2 (Additional byte, word, dword and qword capabilities)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] gfni: "gfni";
/// AVX-512 GFNI (Galois Field New Instruction)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] vaes: "vaes";
/// AVX-512 VAES (Vector AES instruction)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] vpclmulqdq: "vpclmulqdq";
/// AVX-512 VPCLMULQDQ (Vector PCLMULQDQ instructions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vnni: "avx512vnni";
/// AVX-512 VNNI (Vector Neural Network Instructions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bitalg: "avx512bitalg";
/// AVX-512 BITALG (Support for VPOPCNT\[B,W\] and VPSHUFBITQMB)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512bf16: "avx512bf16";
/// AVX-512 BF16 (BFLOAT16 instructions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512vp2intersect: "avx512vp2intersect";
/// AVX-512 P2INTERSECT
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] avx512fp16: "avx512fp16";
/// AVX-512 FP16 (FLOAT16 instructions)
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxifma: "avxifma";
/// AVX-IFMA (Integer Fused Multiply Add)
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxneconvert: "avxneconvert";
/// AVX-NE-CONVERT (Exceptionless Convert)
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxvnni: "avxvnni";
/// AVX-VNNI (Vector Neural Network Instructions)
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxvnniint16: "avxvnniint16";
/// AVX-VNNI_INT8 (VNNI with 16-bit Integers)
@FEATURE: #[stable(feature = "avx512_target_feature", since = "1.89.0")] avxvnniint8: "avxvnniint8";
/// AVX-VNNI_INT16 (VNNI with 8-bit integers)
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tile: "amx-tile";
/// AMX (Advanced Matrix Extensions) - Tile load/store
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_int8: "amx-int8";
/// AMX-INT8 (Operations on 8-bit integers)
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_bf16: "amx-bf16";
/// AMX-BF16 (BFloat16 Operations)
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_fp16: "amx-fp16";
/// AMX-FP16 (Float16 Operations)
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_complex: "amx-complex";
/// AMX-COMPLEX (Complex number Operations)
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_avx512: "amx-avx512";
/// AMX-AVX512 (AVX512 operations extended to matrices)
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_fp8: "amx-fp8";
/// AMX-FP8 (Float8 Operations)
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_movrs: "amx-movrs";
/// AMX-MOVRS (Matrix MOVERS operations)
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tf32: "amx-tf32";
/// AMX-TF32 (TensorFloat32 Operations)
@FEATURE: #[unstable(feature = "apx_target_feature", issue = "139284")] apxf: "apxf";
/// APX-F (Advanced Performance Extensions - Foundation)
@FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_1: "avx10.1";
/// AVX10.1
@FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_2: "avx10.2";
/// AVX10.2
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] f16c: "f16c";
/// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fma: "fma";
/// FMA (Fused Multiply Add)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi1: "bmi1" ;
/// BMI1 (Bit Manipulation Instructions 1)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] bmi2: "bmi2" ;
/// BMI2 (Bit Manipulation Instructions 2)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] lzcnt: "lzcnt";
/// ABM (Advanced Bit Manipulation) / LZCNT (Leading Zero Count)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] tbm: "tbm";
/// TBM (Trailing Bit Manipulation)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] popcnt: "popcnt";
/// POPCNT (Population Count)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] fxsr: "fxsr";
/// FXSR (Floating-point context fast save and restore)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsave: "xsave";
/// XSAVE (Save Processor Extended States)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaveopt: "xsaveopt";
/// XSAVEOPT (Save Processor Extended States Optimized)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsaves: "xsaves";
/// XSAVES (Save Processor Extended States Supervisor)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] xsavec: "xsavec";
/// XSAVEC (Save Processor Extended States Compacted)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] cmpxchg16b: "cmpxchg16b";
/// CMPXCH16B (16-byte compare-and-swap instruction)
@FEATURE: #[stable(feature = "keylocker_x86", since = "1.89.0")] kl: "kl";
/// Intel Key Locker
@FEATURE: #[stable(feature = "keylocker_x86", since = "1.89.0")] widekl: "widekl";
/// Intel Key Locker Wide
@FEATURE: #[stable(feature = "simd_x86_adx", since = "1.33.0")] adx: "adx";
/// ADX, Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] rtm: "rtm";
/// RTM, Intel (Restricted Transactional Memory)
@FEATURE: #[stable(feature = "movbe_target_feature", since = "1.67.0")] movbe: "movbe";
/// MOVBE (Move Data After Swapping Bytes)
@FEATURE: #[unstable(feature = "movrs_target_feature", issue = "137976")] movrs: "movrs";
/// MOVRS (Move data with the read-shared hint)
@FEATURE: #[stable(feature = "simd_x86", since = "1.27.0")] ermsb: "ermsb";
/// ERMSB, Enhanced REP MOVSB and STOSB
@FEATURE: #[unstable(feature = "xop_target_feature", issue = "127208")] xop: "xop";
/// XOP: eXtended Operations (AMD)
}

View File

@@ -0,0 +1,9 @@
//! Bit manipulation utilities.
/// Tests the `bit` of `x`.
#[allow(dead_code)]
#[inline]
pub(crate) fn test(x: usize, bit: u32) -> bool {
debug_assert!(bit < usize::BITS, "bit index out-of-bounds");
x & (1 << bit) != 0
}

View File

@@ -0,0 +1,203 @@
//! Caches run-time feature detection so that it only needs to be computed
//! once.
#![allow(dead_code)] // not used on all platforms
use core::sync::atomic::{AtomicUsize, Ordering};
/// Sets the `bit` of `x`.
#[inline]
const fn set_bit(x: u128, bit: u32) -> u128 {
x | 1 << bit
}
/// Tests the `bit` of `x`.
#[inline]
const fn test_bit(x: u128, bit: u32) -> bool {
x & (1 << bit) != 0
}
/// Unset the `bit of `x`.
#[inline]
const fn unset_bit(x: u128, bit: u32) -> u128 {
x & !(1 << bit)
}
/// Maximum number of features that can be cached.
const CACHE_CAPACITY: u32 = 93;
/// This type is used to initialize the cache
// The derived `Default` implementation will initialize the field to zero,
// which is what we want.
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub(crate) struct Initializer(u128);
// NOTE: the `debug_assert!` would catch that we do not add more Features than
// the one fitting our cache.
impl Initializer {
/// Tests the `bit` of the cache.
#[inline]
pub(crate) fn test(self, bit: u32) -> bool {
debug_assert!(bit < CACHE_CAPACITY, "too many features, time to increase the cache size!");
test_bit(self.0, bit)
}
/// Sets the `bit` of the cache.
#[inline]
pub(crate) fn set(&mut self, bit: u32) {
debug_assert!(bit < CACHE_CAPACITY, "too many features, time to increase the cache size!");
let v = self.0;
self.0 = set_bit(v, bit);
}
/// Unsets the `bit` of the cache.
#[inline]
pub(crate) fn unset(&mut self, bit: u32) {
debug_assert!(bit < CACHE_CAPACITY, "too many features, time to increase the cache size!");
let v = self.0;
self.0 = unset_bit(v, bit);
}
}
/// This global variable is a cache of the features supported by the CPU.
// Note: the third slot is only used in x86
// Another Slot can be added if needed without any change to `Initializer`
static CACHE: [Cache; 3] = [Cache::uninitialized(), Cache::uninitialized(), Cache::uninitialized()];
/// Feature cache with capacity for `size_of::<usize>() * 8 - 1` features.
///
/// Note: 0 is used to represent an uninitialized cache, and (at least) the most
/// significant bit is set on any cache which has been initialized.
///
/// Note: we use `Relaxed` atomic operations, because we are only interested in
/// the effects of operations on a single memory location. That is, we only need
/// "modification order", and not the full-blown "happens before".
struct Cache(AtomicUsize);
impl Cache {
const CAPACITY: u32 = (core::mem::size_of::<usize>() * 8 - 1) as u32;
const MASK: usize = (1 << Cache::CAPACITY) - 1;
const INITIALIZED_BIT: usize = 1usize << Cache::CAPACITY;
/// Creates an uninitialized cache.
#[allow(clippy::declare_interior_mutable_const)]
const fn uninitialized() -> Self {
Cache(AtomicUsize::new(0))
}
/// Is the `bit` in the cache set? Returns `None` if the cache has not been initialized.
#[inline]
pub(crate) fn test(&self, bit: u32) -> Option<bool> {
let cached = self.0.load(Ordering::Relaxed);
if cached == 0 { None } else { Some(test_bit(cached as u128, bit)) }
}
/// Initializes the cache.
#[inline]
fn initialize(&self, value: usize) -> usize {
debug_assert_eq!((value & !Cache::MASK), 0);
self.0.store(value | Cache::INITIALIZED_BIT, Ordering::Relaxed);
value
}
}
cfg_select! {
feature = "std_detect_env_override" => {
#[inline]
fn disable_features(disable: &[u8], value: &mut Initializer) {
if let Ok(disable) = core::str::from_utf8(disable) {
for v in disable.split(" ") {
let _ = super::Feature::from_str(v).map(|v| value.unset(v as u32));
}
}
}
#[inline]
fn initialize(mut value: Initializer) -> Initializer {
use core::ffi::CStr;
const RUST_STD_DETECT_UNSTABLE: &CStr = c"RUST_STD_DETECT_UNSTABLE";
cfg_select! {
windows => {
use alloc::vec;
#[link(name = "kernel32")]
unsafe extern "system" {
fn GetEnvironmentVariableA(name: *const u8, buffer: *mut u8, size: u32) -> u32;
}
let len = unsafe { GetEnvironmentVariableA(RUST_STD_DETECT_UNSTABLE.as_ptr().cast::<u8>(), core::ptr::null_mut(), 0) };
if len > 0 {
// +1 to include the null terminator.
let mut env = vec![0; len as usize + 1];
let len = unsafe { GetEnvironmentVariableA(RUST_STD_DETECT_UNSTABLE.as_ptr().cast::<u8>(), env.as_mut_ptr(), len + 1) };
if len > 0 {
disable_features(&env[..len as usize], &mut value);
}
}
}
_ => {
let env = unsafe {
libc::getenv(RUST_STD_DETECT_UNSTABLE.as_ptr())
};
if !env.is_null() {
let len = unsafe { libc::strlen(env) };
let env = unsafe { core::slice::from_raw_parts(env as *const u8, len) };
disable_features(env, &mut value);
}
}
}
do_initialize(value);
value
}
}
_ => {
#[inline]
fn initialize(value: Initializer) -> Initializer {
do_initialize(value);
value
}
}
}
#[inline]
fn do_initialize(value: Initializer) {
CACHE[0].initialize((value.0) as usize & Cache::MASK);
CACHE[1].initialize((value.0 >> Cache::CAPACITY) as usize & Cache::MASK);
CACHE[2].initialize((value.0 >> (2 * Cache::CAPACITY)) as usize & Cache::MASK);
}
// We only have to detect features once, and it's fairly costly, so hint to LLVM
// that it should assume that cache hits are more common than misses (which is
// the point of caching). It's possibly unfortunate that this function needs to
// reach across modules like this to call `os::detect_features`, but it produces
// the best code out of several attempted variants.
//
// The `Initializer` that the cache was initialized with is returned, so that
// the caller can call `test()` on it without having to load the value from the
// cache again.
#[cold]
fn detect_and_initialize() -> Initializer {
initialize(super::os::detect_features())
}
/// Tests the `bit` of the storage. If the storage has not been initialized,
/// initializes it with the result of `os::detect_features()`.
///
/// On its first invocation, it detects the CPU features and caches them in the
/// `CACHE` global variable as an `AtomicU64`.
///
/// It uses the `Feature` variant to index into this variable as a bitset. If
/// the bit is set, the feature is enabled, and otherwise it is disabled.
///
/// If the feature `std_detect_env_override` is enabled looks for the env
/// variable `RUST_STD_DETECT_UNSTABLE` and uses its content to disable
/// Features that would had been otherwise detected.
#[inline]
pub(crate) fn test(bit: u32) -> bool {
let (relative_bit, idx) = if bit < Cache::CAPACITY {
(bit, 0)
} else if bit < 2 * Cache::CAPACITY {
(bit - Cache::CAPACITY, 1)
} else {
(bit - 2 * Cache::CAPACITY, 2)
};
CACHE[idx].test(relative_bit).unwrap_or_else(|| detect_and_initialize().test(bit))
}

View File

@@ -0,0 +1,203 @@
#[macro_export]
#[allow_internal_unstable(stdarch_internal)]
#[unstable(feature = "stdarch_internal", issue = "none")]
macro_rules! detect_feature {
($feature:tt, $feature_lit:tt) => {
$crate::detect_feature!($feature, $feature_lit : $feature_lit)
};
($feature:tt, $feature_lit:tt : $($target_feature_lit:tt),*) => {
$(cfg!(target_feature = $target_feature_lit) ||)*
$crate::detect::__is_feature_detected::$feature()
};
($feature:tt, $feature_lit:tt, without cfg check: true) => {
$crate::detect::__is_feature_detected::$feature()
};
}
#[allow(unused_macros, reason = "it's used in the features! macro below")]
macro_rules! check_cfg_feature {
($feature:tt, $feature_lit:tt) => {
check_cfg_feature!($feature, $feature_lit : $feature_lit)
};
($feature:tt, $feature_lit:tt : $($target_feature_lit:tt),*) => {
$(cfg!(target_feature = $target_feature_lit);)*
};
($feature:tt, $feature_lit:tt, without cfg check: $feature_cfg_check:literal) => {
#[allow(unexpected_cfgs, reason = $feature_lit)]
{ cfg!(target_feature = $feature_lit) }
};
}
#[allow(unused)]
macro_rules! features {
(
@TARGET: $target:ident;
@CFG: $cfg:meta;
@MACRO_NAME: $macro_name:ident;
@MACRO_ATTRS: $(#[$macro_attrs:meta])*
$(@BIND_FEATURE_NAME: $bind_feature:tt; $feature_impl:tt; $(#[$deprecate_attr:meta];)?)*
$(@NO_RUNTIME_DETECTION: $nort_feature:tt; )*
$(@FEATURE: #[$stability_attr:meta] $feature:ident: $feature_lit:tt;
$(without cfg check: $feature_cfg_check:tt;)?
$(implied by target_features: [$($target_feature_lit:tt),*];)?
$(#[$feature_comment:meta])*)*
) => {
#[macro_export]
$(#[$macro_attrs])*
#[allow_internal_unstable(stdarch_internal)]
#[cfg($cfg)]
#[doc(cfg($cfg))]
macro_rules! $macro_name {
$(
($feature_lit) => {
$crate::detect_feature!($feature, $feature_lit $(, without cfg check: $feature_cfg_check)? $(: $($target_feature_lit),*)?)
};
)*
$(
($bind_feature) => {
{
$(
#[$deprecate_attr] macro_rules! deprecated_feature { {} => {}; }
deprecated_feature! {};
)?
$crate::$macro_name!($feature_impl)
}
};
)*
$(
($nort_feature) => {
compile_error!(
concat!(
stringify!($nort_feature),
" feature cannot be detected at run-time"
)
)
};
)*
($t:tt,) => {
$crate::$macro_name!($t);
};
($t:tt) => {
compile_error!(
concat!(
concat!("unknown ", stringify!($target)),
concat!(" target feature: ", $t)
)
)
};
}
$(#[$macro_attrs])*
#[macro_export]
#[cfg(not($cfg))]
#[doc(cfg($cfg))]
macro_rules! $macro_name {
$(
($feature_lit) => {
compile_error!(
concat!(
r#"This macro cannot be used on the current target.
You can prevent it from being used in other architectures by
guarding it behind a cfg("#,
stringify!($cfg),
")."
)
)
};
)*
$(
($bind_feature) => { $crate::$macro_name!($feature_impl) };
)*
$(
($nort_feature) => {
compile_error!(
concat!(
stringify!($nort_feature),
" feature cannot be detected at run-time"
)
)
};
)*
($t:tt,) => {
$crate::$macro_name!($t);
};
($t:tt) => {
compile_error!(
concat!(
concat!("unknown ", stringify!($target)),
concat!(" target feature: ", $t)
)
)
};
}
#[deny(unexpected_cfgs)]
#[deny(unfulfilled_lint_expectations)]
const _: () = {
$(
check_cfg_feature!($feature, $feature_lit $(, without cfg check: $feature_cfg_check)? $(: $($target_feature_lit),*)?);
)*
};
/// Each variant denotes a position in a bitset for a particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject
/// to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
#[repr(u8)]
#[unstable(feature = "stdarch_internal", issue = "none")]
#[cfg($cfg)]
pub(crate) enum Feature {
$(
$(#[$feature_comment])*
$feature,
)*
// Do not add variants after last:
_last
}
#[cfg($cfg)]
impl Feature {
pub(crate) fn to_str(self) -> &'static str {
match self {
$(Feature::$feature => $feature_lit,)*
Feature::_last => unreachable!(),
}
}
#[cfg(feature = "std_detect_env_override")]
pub(crate) fn from_str(s: &str) -> Result<Feature, ()> {
match s {
$($feature_lit => Ok(Feature::$feature),)*
_ => Err(())
}
}
}
/// Each function performs run-time feature detection for a single
/// feature. This allow us to use stability attributes on a per feature
/// basis.
///
/// PLEASE: do not use this, it is an implementation detail subject
/// to change.
#[doc(hidden)]
#[cfg($cfg)]
#[unstable(feature = "stdarch_internal", issue = "none")]
pub mod __is_feature_detected {
$(
/// PLEASE: do not use this, it is an implementation detail
/// subject to change.
#[inline]
#[doc(hidden)]
#[$stability_attr]
pub fn $feature() -> bool {
$crate::detect::check_for($crate::detect::Feature::$feature)
}
)*
}
};
}

View File

@@ -0,0 +1,125 @@
//! This module implements run-time feature detection.
//!
//! The `is_{arch}_feature_detected!("feature-name")` macros take the name of a
//! feature as a string-literal, and return a boolean indicating whether the
//! feature is enabled at run-time or not.
//!
//! These macros do two things:
//! * map the string-literal into an integer stored as a `Feature` enum,
//! * call a `os::check_for(x: Feature)` function that returns `true` if the
//! feature is enabled.
//!
//! The `Feature` enums are also implemented in the `arch/{target_arch}.rs`
//! modules.
//!
//! The `check_for` functions are, in general, Operating System dependent. Most
//! architectures do not allow user-space programs to query the feature bits
//! due to security concerns (x86 is the big exception). These functions are
//! implemented in the `os/{target_os}.rs` modules.
#[macro_use]
mod macros;
mod arch;
// This module needs to be public because the `is_{arch}_feature_detected!`
// macros expand calls to items within it in user crates.
#[doc(hidden)]
#[unstable(feature = "stdarch_internal", issue = "none")]
pub use self::arch::__is_feature_detected;
pub(crate) use self::arch::Feature;
mod bit;
mod cache;
cfg_select! {
miri => {
// When running under miri all target-features that are not enabled at
// compile-time are reported as disabled at run-time.
//
// For features for which `cfg(target_feature)` returns true,
// this run-time detection logic is never called.
#[path = "os/other.rs"]
mod os;
}
any(target_arch = "x86", target_arch = "x86_64") => {
// On x86/x86_64 no OS specific functionality is required.
#[path = "os/x86.rs"]
mod os;
}
any(target_os = "linux", target_os = "android") => {
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
#[path = "os/riscv.rs"]
mod riscv;
#[path = "os/linux/mod.rs"]
mod os;
}
target_os = "freebsd" => {
#[cfg(target_arch = "aarch64")]
#[path = "os/aarch64.rs"]
mod aarch64;
#[path = "os/freebsd/mod.rs"]
mod os;
}
target_os = "openbsd" => {
#[allow(dead_code)] // we don't use code that calls the mrs instruction.
#[cfg(target_arch = "aarch64")]
#[path = "os/aarch64.rs"]
mod aarch64;
#[path = "os/openbsd/mod.rs"]
mod os;
}
all(target_os = "windows", any(target_arch = "aarch64", target_arch = "arm64ec")) => {
#[path = "os/windows/aarch64.rs"]
mod os;
}
all(target_vendor = "apple", target_arch = "aarch64") => {
#[path = "os/darwin/aarch64.rs"]
mod os;
}
_ => {
#[path = "os/other.rs"]
mod os;
}
}
/// Performs run-time feature detection.
#[inline]
#[allow(dead_code)]
fn check_for(x: Feature) -> bool {
cache::test(x as u32)
}
/// Returns an `Iterator<Item=(&'static str, bool)>` where
/// `Item.0` is the feature name, and `Item.1` is a `bool` which
/// is `true` if the feature is supported by the host and `false` otherwise.
#[unstable(feature = "stdarch_internal", issue = "none")]
pub fn features() -> impl Iterator<Item = (&'static str, bool)> {
cfg_select! {
any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "mips",
target_arch = "mips64",
target_arch = "loongarch32",
target_arch = "loongarch64",
target_arch = "s390x",
) => {
(0_u8..Feature::_last as u8).map(|discriminant: u8| {
#[allow(bindings_with_variant_name)] // RISC-V has Feature::f
let f: Feature = unsafe { core::mem::transmute(discriminant) };
let name: &'static str = f.to_str();
let enabled: bool = check_for(f);
(name, enabled)
})
}
_ => None.into_iter(),
}
}

View File

@@ -0,0 +1,127 @@
//! Run-time feature detection for Aarch64 on any OS that emulates the mrs instruction.
//!
//! On FreeBSD >= 12.0, Linux >= 4.11 and other operating systems, it is possible to use
//! privileged system registers from userspace to check CPU feature support.
//!
//! AArch64 system registers ID_AA64ISAR0_EL1, ID_AA64PFR0_EL1, ID_AA64ISAR1_EL1
//! have bits dedicated to features like AdvSIMD, CRC32, AES, atomics (LSE), etc.
//! Each part of the register indicates the level of support for a certain feature, e.g.
//! when ID_AA64ISAR0_EL1\[7:4\] is >= 1, AES is supported; when it's >= 2, PMULL is supported.
//!
//! For proper support of [SoCs where different cores have different capabilities](https://medium.com/@jadr2ddude/a-big-little-problem-a-tale-of-big-little-gone-wrong-e7778ce744bb),
//! the OS has to always report only the features supported by all cores, like [FreeBSD does](https://reviews.freebsd.org/D17137#393947).
//!
//! References:
//!
//! - [Zircon implementation](https://fuchsia.googlesource.com/zircon/+/master/kernel/arch/arm64/feature.cpp)
//! - [Linux documentation](https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt)
//! - [ARM documentation](https://developer.arm.com/documentation/ddi0601/2022-12/AArch64-Registers?lang=en)
use core::arch::asm;
use crate::detect::{Feature, cache};
/// Try to read the features from the system registers.
///
/// This will cause SIGILL if the current OS is not trapping the mrs instruction.
pub(crate) fn detect_features() -> cache::Initializer {
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
let aa64isar0: u64;
unsafe {
asm!(
"mrs {}, ID_AA64ISAR0_EL1",
out(reg) aa64isar0,
options(pure, nomem, preserves_flags, nostack)
);
}
// ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1
let aa64isar1: u64;
unsafe {
asm!(
"mrs {}, ID_AA64ISAR1_EL1",
out(reg) aa64isar1,
options(pure, nomem, preserves_flags, nostack)
);
}
// ID_AA64MMFR2_EL1 - AArch64 Memory Model Feature Register 2
let aa64mmfr2: u64;
unsafe {
asm!(
"mrs {}, ID_AA64MMFR2_EL1",
out(reg) aa64mmfr2,
options(pure, nomem, preserves_flags, nostack)
);
}
// ID_AA64PFR0_EL1 - Processor Feature Register 0
let aa64pfr0: u64;
unsafe {
asm!(
"mrs {}, ID_AA64PFR0_EL1",
out(reg) aa64pfr0,
options(pure, nomem, preserves_flags, nostack)
);
}
parse_system_registers(aa64isar0, aa64isar1, aa64mmfr2, Some(aa64pfr0))
}
pub(crate) fn parse_system_registers(
aa64isar0: u64,
aa64isar1: u64,
aa64mmfr2: u64,
aa64pfr0: Option<u64>,
) -> cache::Initializer {
let mut value = cache::Initializer::default();
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
enable_feature(Feature::pmull, bits_shift(aa64isar0, 7, 4) >= 2);
enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 2);
enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1);
// ID_AA64PFR0_EL1 - Processor Feature Register 0
if let Some(aa64pfr0) = aa64pfr0 {
let fp = bits_shift(aa64pfr0, 19, 16) < 0xF;
let fphp = bits_shift(aa64pfr0, 19, 16) >= 1;
let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF;
let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1;
enable_feature(Feature::fp, fp);
enable_feature(Feature::fp16, fphp);
// SIMD support requires float support - if half-floats are
// supported, it also requires half-float support:
enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp));
// SIMD extensions require SIMD support:
enable_feature(Feature::aes, asimd && bits_shift(aa64isar0, 7, 4) >= 2);
let sha1 = bits_shift(aa64isar0, 11, 8) >= 1;
let sha2 = bits_shift(aa64isar0, 15, 12) >= 1;
enable_feature(Feature::sha2, asimd && sha1 && sha2);
enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1);
enable_feature(Feature::dotprod, asimd && bits_shift(aa64isar0, 47, 44) >= 1);
enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1);
}
// ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1
// Check for either APA or API field
enable_feature(Feature::paca, bits_shift(aa64isar1, 11, 4) >= 1);
enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1);
// Check for either GPA or GPI field
enable_feature(Feature::pacg, bits_shift(aa64isar1, 31, 24) >= 1);
// ID_AA64MMFR2_EL1 - AArch64 Memory Model Feature Register 2
enable_feature(Feature::lse2, bits_shift(aa64mmfr2, 35, 32) >= 1);
value
}
#[inline]
fn bits_shift(x: u64, high: usize, low: usize) -> u64 {
(x >> low) & ((1 << (high - low + 1)) - 1)
}

View File

@@ -0,0 +1,166 @@
//! Run-time feature detection for aarch64 on Darwin (macOS/iOS/tvOS/watchOS/visionOS).
//!
//! <https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics>
use core::ffi::CStr;
use crate::detect::{Feature, cache};
#[inline]
fn _sysctlbyname(name: &CStr) -> bool {
use libc;
let mut enabled: i32 = 0;
let mut enabled_len: usize = 4;
let enabled_ptr = &mut enabled as *mut i32 as *mut libc::c_void;
let ret = unsafe {
libc::sysctlbyname(name.as_ptr(), enabled_ptr, &mut enabled_len, core::ptr::null_mut(), 0)
};
match ret {
0 => enabled != 0,
_ => false,
}
}
/// Try to read the features using sysctlbyname.
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};
// Armv8.0 features not using the standard identifiers
let fp = _sysctlbyname(c"hw.optional.floatingpoint");
let asimd = _sysctlbyname(c"hw.optional.AdvSIMD");
let crc_old = _sysctlbyname(c"hw.optional.armv8_crc32");
// Armv8 and Armv9 features using the standard identifiers
let aes = _sysctlbyname(c"hw.optional.arm.FEAT_AES");
let bf16 = _sysctlbyname(c"hw.optional.arm.FEAT_BF16");
let bti = _sysctlbyname(c"hw.optional.arm.FEAT_BTI");
let crc = _sysctlbyname(c"hw.optional.arm.FEAT_CRC32");
let cssc = _sysctlbyname(c"hw.optional.arm.FEAT_CSSC");
let dit = _sysctlbyname(c"hw.optional.arm.FEAT_DIT");
let dotprod = _sysctlbyname(c"hw.optional.arm.FEAT_DotProd");
let dpb = _sysctlbyname(c"hw.optional.arm.FEAT_DPB");
let dpb2 = _sysctlbyname(c"hw.optional.arm.FEAT_DPB2");
let ecv = _sysctlbyname(c"hw.optional.arm.FEAT_ECV");
let fcma = _sysctlbyname(c"hw.optional.arm.FEAT_FCMA");
let fhm = _sysctlbyname(c"hw.optional.arm.FEAT_FHM");
let flagm = _sysctlbyname(c"hw.optional.arm.FEAT_FlagM");
let flagm2 = _sysctlbyname(c"hw.optional.arm.FEAT_FlagM2");
let fp16 = _sysctlbyname(c"hw.optional.arm.FEAT_FP16");
let frintts = _sysctlbyname(c"hw.optional.arm.FEAT_FRINTTS");
let hbc = _sysctlbyname(c"hw.optional.arm.FEAT_HBC");
let i8mm = _sysctlbyname(c"hw.optional.arm.FEAT_I8MM");
let jsconv = _sysctlbyname(c"hw.optional.arm.FEAT_JSCVT");
let rcpc = _sysctlbyname(c"hw.optional.arm.FEAT_LRCPC");
let rcpc2 = _sysctlbyname(c"hw.optional.arm.FEAT_LRCPC2");
let lse = _sysctlbyname(c"hw.optional.arm.FEAT_LSE");
let lse2 = _sysctlbyname(c"hw.optional.arm.FEAT_LSE2");
let mte = _sysctlbyname(c"hw.optional.arm.FEAT_MTE");
let mte2 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE2");
let pauth = _sysctlbyname(c"hw.optional.arm.FEAT_PAuth");
let pmull = _sysctlbyname(c"hw.optional.arm.FEAT_PMULL");
let rdm = _sysctlbyname(c"hw.optional.arm.FEAT_RDM");
let sb = _sysctlbyname(c"hw.optional.arm.FEAT_SB");
let sha1 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA1");
let sha256 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA256");
let sha3 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA3");
let sha512 = _sysctlbyname(c"hw.optional.arm.FEAT_SHA512");
let sme = _sysctlbyname(c"hw.optional.arm.FEAT_SME");
let sme2 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2");
let sme2p1 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2p1");
let sme_b16b16 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_B16B16");
let sme_f16f16 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_F16F16");
let sme_f64f64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_F64F64");
let sme_i16i64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_I16I64");
let ssbs = _sysctlbyname(c"hw.optional.arm.FEAT_SSBS");
let wfxt = _sysctlbyname(c"hw.optional.arm.FEAT_WFxT");
// The following features are not exposed by `is_aarch64_feature_detected`,
// but *are* reported by `sysctl`. They are here as documentation that they
// exist, and may potentially be exposed later.
/*
let afp = _sysctlbyname(c"hw.optional.arm.FEAT_AFP");
let csv2 = _sysctlbyname(c"hw.optional.arm.FEAT_CSV2");
let csv3 = _sysctlbyname(c"hw.optional.arm.FEAT_CSV3");
let ebf16 = _sysctlbyname(c"hw.optional.arm.FEAT_EBF16");
let fpac = _sysctlbyname(c"hw.optional.arm.FEAT_FPAC");
let fpaccombine = _sysctlbyname(c"hw.optional.arm.FEAT_FPACCOMBINE");
let mte_async = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_ASYNC");
let mte_canonical_tags = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_CANONICAL_TAGS");
let mte_no_address_tags = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_NO_ADDRESS_TAGS");
let mte_store_only = _sysctlbyname(c"hw.optional.arm.FEAT_MTE_STORE_ONLY");
let mte3 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE3");
let mte4 = _sysctlbyname(c"hw.optional.arm.FEAT_MTE4");
let pacimp = _sysctlbyname(c"hw.optional.arm.FEAT_PACIMP");
let pauth2 = _sysctlbyname(c"hw.optional.arm.FEAT_PAuth2");
let rpres = _sysctlbyname(c"hw.optional.arm.FEAT_RPRES");
let specres = _sysctlbyname(c"hw.optional.arm.FEAT_SPECRES");
let specres2 = _sysctlbyname(c"hw.optional.arm.FEAT_SPECRES2");
*/
// The following "features" are reported by `sysctl` but are mandatory parts
// of SME or SME2, and so are not exposed separately by
// `is_aarch64_feature_detected`. They are here to document their
// existence, in case they're needed in the future.
/*
let sme_b16f32 = _sysctlbyname(c"hw.optional.arm.SME_B16F32");
let sme_bi32i32 = _sysctlbyname(c"hw.optional.arm.SME_BI32I32");
let sme_f16f32 = _sysctlbyname(c"hw.optional.arm.SME_F16F32");
let sme_f32f32 = _sysctlbyname(c"hw.optional.arm.SME_F32F32");
let sme_i16i32 = _sysctlbyname(c"hw.optional.arm.SME_I16I32");
let sme_i8i32 = _sysctlbyname(c"hw.optional.arm.SME_I8I32");
*/
enable_feature(Feature::aes, aes && pmull);
enable_feature(Feature::asimd, asimd);
enable_feature(Feature::bf16, bf16);
enable_feature(Feature::bti, bti);
enable_feature(Feature::crc, crc_old || crc);
enable_feature(Feature::cssc, cssc);
enable_feature(Feature::dit, dit);
enable_feature(Feature::dotprod, dotprod);
enable_feature(Feature::dpb, dpb);
enable_feature(Feature::dpb2, dpb2);
enable_feature(Feature::ecv, ecv);
enable_feature(Feature::fcma, fcma);
enable_feature(Feature::fhm, fhm);
enable_feature(Feature::flagm, flagm);
enable_feature(Feature::flagm2, flagm2);
enable_feature(Feature::fp, fp);
enable_feature(Feature::fp16, fp16);
enable_feature(Feature::frintts, frintts);
enable_feature(Feature::hbc, hbc);
enable_feature(Feature::i8mm, i8mm);
enable_feature(Feature::jsconv, jsconv);
enable_feature(Feature::lse, lse);
enable_feature(Feature::lse2, lse2);
enable_feature(Feature::mte, mte && mte2);
enable_feature(Feature::paca, pauth);
enable_feature(Feature::pacg, pauth);
enable_feature(Feature::pmull, aes && pmull);
enable_feature(Feature::rcpc, rcpc);
enable_feature(Feature::rcpc2, rcpc2);
enable_feature(Feature::rdm, rdm);
enable_feature(Feature::sb, sb);
enable_feature(Feature::sha2, sha1 && sha256 && asimd);
enable_feature(Feature::sha3, sha512 && sha3 && asimd);
enable_feature(Feature::sme, sme);
enable_feature(Feature::sme2, sme2);
enable_feature(Feature::sme2p1, sme2p1);
enable_feature(Feature::sme_b16b16, sme_b16b16);
enable_feature(Feature::sme_f16f16, sme_f16f16);
enable_feature(Feature::sme_f64f64, sme_f64f64);
enable_feature(Feature::sme_i16i64, sme_i16i64);
enable_feature(Feature::ssbs, ssbs);
enable_feature(Feature::wfxt, wfxt);
value
}

View File

@@ -0,0 +1,3 @@
//! Run-time feature detection for Aarch64 on FreeBSD.
pub(crate) use super::super::aarch64::detect_features;

View File

@@ -0,0 +1,36 @@
//! Run-time feature detection for ARM on FreeBSD
use super::auxvec;
use crate::detect::{Feature, cache};
// Defined in machine/elf.h.
// https://github.com/freebsd/freebsd-src/blob/deb63adf945d446ed91a9d84124c71f15ae571d1/sys/arm/include/elf.h
const HWCAP_NEON: usize = 0x00001000;
const HWCAP2_AES: usize = 0x00000001;
const HWCAP2_PMULL: usize = 0x00000002;
const HWCAP2_SHA1: usize = 0x00000004;
const HWCAP2_SHA2: usize = 0x00000008;
const HWCAP2_CRC32: usize = 0x00000010;
/// Try to read the features from the auxiliary vector
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::neon, auxv.hwcap & HWCAP_NEON != 0);
enable_feature(&mut value, Feature::pmull, auxv.hwcap2 & HWCAP2_PMULL != 0);
enable_feature(&mut value, Feature::crc, auxv.hwcap2 & HWCAP2_CRC32 != 0);
enable_feature(&mut value, Feature::aes, auxv.hwcap2 & HWCAP2_AES != 0);
// SHA2 requires SHA1 & SHA2 features
let sha1 = auxv.hwcap2 & HWCAP2_SHA1 != 0;
let sha2 = auxv.hwcap2 & HWCAP2_SHA2 != 0;
enable_feature(&mut value, Feature::sha2, sha1 && sha2);
return value;
}
value
}

View File

@@ -0,0 +1,63 @@
//! Parses ELF auxiliary vectors.
#![cfg_attr(
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "riscv64"
),
allow(dead_code)
)]
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
///
/// If an entry cannot be read all the bits in the bitfield are set to zero.
/// This should be interpreted as all the features being disabled.
#[derive(Debug, Copy, Clone)]
pub(crate) struct AuxVec {
pub hwcap: usize,
pub hwcap2: usize,
}
/// ELF Auxiliary Vector
///
/// The auxiliary vector is a memory region in a running ELF program's stack
/// composed of (key: usize, value: usize) pairs.
///
/// The keys used in the aux vector are platform dependent. For FreeBSD, they are
/// defined in [sys/elf_common.h][elf_common_h]. The hardware capabilities of a given
/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
///
/// Note that run-time feature detection is not invoked for features that can
/// be detected at compile-time.
///
/// [elf_common.h]: https://svnweb.freebsd.org/base/release/12.0.0/sys/sys/elf_common.h?revision=341707
pub(crate) fn auxv() -> Result<AuxVec, ()> {
let hwcap = archauxv(libc::AT_HWCAP);
let hwcap2 = archauxv(libc::AT_HWCAP2);
// Zero could indicate that no features were detected, but it's also used to
// indicate an error. In particular, on many platforms AT_HWCAP2 will be
// legitimately zero, since it contains the most recent feature flags.
if hwcap != 0 || hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
Err(())
}
/// Tries to read the `key` from the auxiliary vector.
fn archauxv(key: libc::c_int) -> usize {
const OUT_LEN: libc::c_int = core::mem::size_of::<libc::c_ulong>() as libc::c_int;
let mut out: libc::c_ulong = 0;
unsafe {
// elf_aux_info is available on FreeBSD 12.0+ and 11.4+:
// https://github.com/freebsd/freebsd-src/commit/0b08ae2120cdd08c20a2b806e2fcef4d0a36c470
// https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/sys/auxv.h
// FreeBSD 11 support in std has been removed in Rust 1.75 (https://github.com/rust-lang/rust/pull/114521),
// so we can safely use this function.
let res =
libc::elf_aux_info(key, &mut out as *mut libc::c_ulong as *mut libc::c_void, OUT_LEN);
// If elf_aux_info fails, `out` will be left at zero (which is the proper default value).
debug_assert!(res == 0 || out == 0);
}
out as usize
}

View File

@@ -0,0 +1,25 @@
//! Run-time feature detection on FreeBSD
mod auxvec;
cfg_select! {
target_arch = "aarch64" => {
mod aarch64;
pub(crate) use self::aarch64::detect_features;
}
target_arch = "arm" => {
mod arm;
pub(crate) use self::arm::detect_features;
}
target_arch = "powerpc64" => {
mod powerpc;
pub(crate) use self::powerpc::detect_features;
}
_ => {
use crate::detect::cache;
/// Performs run-time feature detection.
pub(crate) fn detect_features() -> cache::Initializer {
cache::Initializer::default()
}
}
}

View File

@@ -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
}

View File

@@ -0,0 +1,409 @@
//! Run-time feature detection for Aarch64 on Linux.
use super::auxvec;
use crate::detect::{Feature, bit, cache};
/// Try to read the features from the auxiliary vector.
pub(crate) fn detect_features() -> cache::Initializer {
#[cfg(target_os = "android")]
let is_exynos9810 = {
// Samsung Exynos 9810 has a bug that big and little cores have different
// ISAs. And on older Android (pre-9), the kernel incorrectly reports
// that features available only on some cores are available on all cores.
// https://reviews.llvm.org/D114523
let mut arch = [0_u8; libc::PROP_VALUE_MAX as usize];
let len = unsafe {
libc::__system_property_get(c"ro.arch".as_ptr(), arch.as_mut_ptr() as *mut libc::c_char)
};
// On Exynos, ro.arch is not available on Android 12+, but it is fine
// because Android 9+ includes the fix.
len > 0 && arch.starts_with(b"exynos9810")
};
#[cfg(not(target_os = "android"))]
let is_exynos9810 = false;
if let Ok(auxv) = auxvec::auxv() {
let hwcap: AtHwcap = auxv.into();
return hwcap.cache(is_exynos9810);
}
cache::Initializer::default()
}
/// These values are part of the platform-specific [asm/hwcap.h][hwcap] .
///
/// The names match those used for cpuinfo.
///
/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
#[derive(Debug, Default, PartialEq)]
struct AtHwcap {
// AT_HWCAP
fp: bool,
asimd: bool,
// evtstrm: No LLVM support.
aes: bool,
pmull: bool,
sha1: bool,
sha2: bool,
crc32: bool,
atomics: bool,
fphp: bool,
asimdhp: bool,
// cpuid: No LLVM support.
asimdrdm: bool,
jscvt: bool,
fcma: bool,
lrcpc: bool,
dcpop: bool,
sha3: bool,
sm3: bool,
sm4: bool,
asimddp: bool,
sha512: bool,
sve: bool,
fhm: bool,
dit: bool,
uscat: bool,
ilrcpc: bool,
flagm: bool,
ssbs: bool,
sb: bool,
paca: bool,
pacg: bool,
// AT_HWCAP2
dcpodp: bool,
sve2: bool,
sveaes: bool,
svepmull: bool,
svebitperm: bool,
svesha3: bool,
svesm4: bool,
flagm2: bool,
frint: bool,
// svei8mm: See i8mm feature.
svef32mm: bool,
svef64mm: bool,
// svebf16: See bf16 feature.
i8mm: bool,
bf16: bool,
// dgh: No LLVM support.
rng: bool,
bti: bool,
mte: bool,
ecv: bool,
// afp: bool,
// rpres: bool,
// mte3: bool,
sme: bool,
smei16i64: bool,
smef64f64: bool,
// smei8i32: bool,
// smef16f32: bool,
// smeb16f32: bool,
// smef32f32: bool,
smefa64: bool,
wfxt: bool,
// ebf16: bool,
// sveebf16: bool,
cssc: bool,
// rprfm: bool,
sve2p1: bool,
sme2: bool,
sme2p1: bool,
// smei16i32: bool,
// smebi32i32: bool,
smeb16b16: bool,
smef16f16: bool,
mops: bool,
hbc: bool,
sveb16b16: bool,
lrcpc3: bool,
lse128: bool,
fpmr: bool,
lut: bool,
faminmax: bool,
f8cvt: bool,
f8fma: bool,
f8dp4: bool,
f8dp2: bool,
f8e4m3: bool,
f8e5m2: bool,
smelutv2: bool,
smef8f16: bool,
smef8f32: bool,
smesf8fma: bool,
smesf8dp4: bool,
smesf8dp2: bool,
// pauthlr: bool,
}
impl From<auxvec::AuxVec> for AtHwcap {
/// Reads AtHwcap from the auxiliary vector.
fn from(auxv: auxvec::AuxVec) -> Self {
let mut cap = AtHwcap {
fp: bit::test(auxv.hwcap, 0),
asimd: bit::test(auxv.hwcap, 1),
// evtstrm: bit::test(auxv.hwcap, 2),
aes: bit::test(auxv.hwcap, 3),
pmull: bit::test(auxv.hwcap, 4),
sha1: bit::test(auxv.hwcap, 5),
sha2: bit::test(auxv.hwcap, 6),
crc32: bit::test(auxv.hwcap, 7),
atomics: bit::test(auxv.hwcap, 8),
fphp: bit::test(auxv.hwcap, 9),
asimdhp: bit::test(auxv.hwcap, 10),
// cpuid: bit::test(auxv.hwcap, 11),
asimdrdm: bit::test(auxv.hwcap, 12),
jscvt: bit::test(auxv.hwcap, 13),
fcma: bit::test(auxv.hwcap, 14),
lrcpc: bit::test(auxv.hwcap, 15),
dcpop: bit::test(auxv.hwcap, 16),
sha3: bit::test(auxv.hwcap, 17),
sm3: bit::test(auxv.hwcap, 18),
sm4: bit::test(auxv.hwcap, 19),
asimddp: bit::test(auxv.hwcap, 20),
sha512: bit::test(auxv.hwcap, 21),
sve: bit::test(auxv.hwcap, 22),
fhm: bit::test(auxv.hwcap, 23),
dit: bit::test(auxv.hwcap, 24),
uscat: bit::test(auxv.hwcap, 25),
ilrcpc: bit::test(auxv.hwcap, 26),
flagm: bit::test(auxv.hwcap, 27),
ssbs: bit::test(auxv.hwcap, 28),
sb: bit::test(auxv.hwcap, 29),
paca: bit::test(auxv.hwcap, 30),
pacg: bit::test(auxv.hwcap, 31),
// AT_HWCAP2
dcpodp: bit::test(auxv.hwcap2, 0),
sve2: bit::test(auxv.hwcap2, 1),
sveaes: bit::test(auxv.hwcap2, 2),
svepmull: bit::test(auxv.hwcap2, 3),
svebitperm: bit::test(auxv.hwcap2, 4),
svesha3: bit::test(auxv.hwcap2, 5),
svesm4: bit::test(auxv.hwcap2, 6),
flagm2: bit::test(auxv.hwcap2, 7),
frint: bit::test(auxv.hwcap2, 8),
// svei8mm: bit::test(auxv.hwcap2, 9),
svef32mm: bit::test(auxv.hwcap2, 10),
svef64mm: bit::test(auxv.hwcap2, 11),
// svebf16: bit::test(auxv.hwcap2, 12),
i8mm: bit::test(auxv.hwcap2, 13),
bf16: bit::test(auxv.hwcap2, 14),
// dgh: bit::test(auxv.hwcap2, 15),
rng: bit::test(auxv.hwcap2, 16),
bti: bit::test(auxv.hwcap2, 17),
mte: bit::test(auxv.hwcap2, 18),
ecv: bit::test(auxv.hwcap2, 19),
// afp: bit::test(auxv.hwcap2, 20),
// rpres: bit::test(auxv.hwcap2, 21),
// mte3: bit::test(auxv.hwcap2, 22),
sme: bit::test(auxv.hwcap2, 23),
smei16i64: bit::test(auxv.hwcap2, 24),
smef64f64: bit::test(auxv.hwcap2, 25),
// smei8i32: bit::test(auxv.hwcap2, 26),
// smef16f32: bit::test(auxv.hwcap2, 27),
// smeb16f32: bit::test(auxv.hwcap2, 28),
// smef32f32: bit::test(auxv.hwcap2, 29),
smefa64: bit::test(auxv.hwcap2, 30),
wfxt: bit::test(auxv.hwcap2, 31),
..Default::default()
};
// Hardware capabilities from bits 32 to 63 should only
// be tested on LP64 targets with 64 bits `usize`.
// On ILP32 targets like `aarch64-unknown-linux-gnu_ilp32`,
// these hardware capabilities will default to `false`.
// https://github.com/rust-lang/rust/issues/146230
#[cfg(target_pointer_width = "64")]
{
// cap.ebf16: bit::test(auxv.hwcap2, 32);
// cap.sveebf16: bit::test(auxv.hwcap2, 33);
cap.cssc = bit::test(auxv.hwcap2, 34);
// cap.rprfm: bit::test(auxv.hwcap2, 35);
cap.sve2p1 = bit::test(auxv.hwcap2, 36);
cap.sme2 = bit::test(auxv.hwcap2, 37);
cap.sme2p1 = bit::test(auxv.hwcap2, 38);
// cap.smei16i32 = bit::test(auxv.hwcap2, 39);
// cap.smebi32i32 = bit::test(auxv.hwcap2, 40);
cap.smeb16b16 = bit::test(auxv.hwcap2, 41);
cap.smef16f16 = bit::test(auxv.hwcap2, 42);
cap.mops = bit::test(auxv.hwcap2, 43);
cap.hbc = bit::test(auxv.hwcap2, 44);
cap.sveb16b16 = bit::test(auxv.hwcap2, 45);
cap.lrcpc3 = bit::test(auxv.hwcap2, 46);
cap.lse128 = bit::test(auxv.hwcap2, 47);
cap.fpmr = bit::test(auxv.hwcap2, 48);
cap.lut = bit::test(auxv.hwcap2, 49);
cap.faminmax = bit::test(auxv.hwcap2, 50);
cap.f8cvt = bit::test(auxv.hwcap2, 51);
cap.f8fma = bit::test(auxv.hwcap2, 52);
cap.f8dp4 = bit::test(auxv.hwcap2, 53);
cap.f8dp2 = bit::test(auxv.hwcap2, 54);
cap.f8e4m3 = bit::test(auxv.hwcap2, 55);
cap.f8e5m2 = bit::test(auxv.hwcap2, 56);
cap.smelutv2 = bit::test(auxv.hwcap2, 57);
cap.smef8f16 = bit::test(auxv.hwcap2, 58);
cap.smef8f32 = bit::test(auxv.hwcap2, 59);
cap.smesf8fma = bit::test(auxv.hwcap2, 60);
cap.smesf8dp4 = bit::test(auxv.hwcap2, 61);
cap.smesf8dp2 = bit::test(auxv.hwcap2, 62);
// cap.pauthlr = bit::test(auxv.hwcap2, ??);
}
cap
}
}
impl AtHwcap {
/// Initializes the cache from the feature -bits.
///
/// The feature dependencies here come directly from LLVM's feature definitions:
/// https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/AArch64/AArch64.td
fn cache(self, is_exynos9810: bool) -> cache::Initializer {
let mut value = cache::Initializer::default();
{
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};
// Samsung Exynos 9810 has a bug that big and little cores have different
// ISAs. And on older Android (pre-9), the kernel incorrectly reports
// that features available only on some cores are available on all cores.
// So, only check features that are known to be available on exynos-m3:
// $ rustc --print cfg --target aarch64-linux-android -C target-cpu=exynos-m3 | grep target_feature
// See also https://github.com/rust-lang/stdarch/pull/1378#discussion_r1103748342.
if is_exynos9810 {
enable_feature(Feature::fp, self.fp);
enable_feature(Feature::crc, self.crc32);
// ASIMD support requires float support - if half-floats are
// supported, it also requires half-float support:
let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp);
enable_feature(Feature::asimd, asimd);
// Cryptographic extensions require ASIMD
// AES also covers FEAT_PMULL
enable_feature(Feature::aes, self.aes && self.pmull && asimd);
enable_feature(Feature::sha2, self.sha1 && self.sha2 && asimd);
return value;
}
enable_feature(Feature::fp, self.fp);
// Half-float support requires float support
enable_feature(Feature::fp16, self.fp && self.fphp);
// FHM (fp16fml in LLVM) requires half float support
enable_feature(Feature::fhm, self.fphp && self.fhm);
enable_feature(Feature::pmull, self.pmull);
enable_feature(Feature::crc, self.crc32);
enable_feature(Feature::lse, self.atomics);
enable_feature(Feature::lse2, self.uscat);
enable_feature(Feature::lse128, self.lse128 && self.atomics);
enable_feature(Feature::rcpc, self.lrcpc);
// RCPC2 (rcpc-immo in LLVM) requires RCPC support
let rcpc2 = self.ilrcpc && self.lrcpc;
enable_feature(Feature::rcpc2, rcpc2);
enable_feature(Feature::rcpc3, self.lrcpc3 && rcpc2);
enable_feature(Feature::dit, self.dit);
enable_feature(Feature::flagm, self.flagm);
enable_feature(Feature::flagm2, self.flagm2);
enable_feature(Feature::ssbs, self.ssbs);
enable_feature(Feature::sb, self.sb);
enable_feature(Feature::paca, self.paca);
enable_feature(Feature::pacg, self.pacg);
// enable_feature(Feature::pauth_lr, self.pauthlr);
enable_feature(Feature::dpb, self.dcpop);
enable_feature(Feature::dpb2, self.dcpodp);
enable_feature(Feature::rand, self.rng);
enable_feature(Feature::bti, self.bti);
enable_feature(Feature::mte, self.mte);
// jsconv requires float support
enable_feature(Feature::jsconv, self.jscvt && self.fp);
enable_feature(Feature::rdm, self.asimdrdm);
enable_feature(Feature::dotprod, self.asimddp);
enable_feature(Feature::frintts, self.frint);
// FEAT_I8MM & FEAT_BF16 also include optional SVE components which linux exposes
// separately. We ignore that distinction here.
enable_feature(Feature::i8mm, self.i8mm);
enable_feature(Feature::bf16, self.bf16);
// ASIMD support requires float support - if half-floats are
// supported, it also requires half-float support:
let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp);
enable_feature(Feature::asimd, asimd);
// ASIMD extensions require ASIMD support:
enable_feature(Feature::fcma, self.fcma && asimd);
enable_feature(Feature::sve, self.sve && asimd);
// SVE extensions require SVE & ASIMD
enable_feature(Feature::f32mm, self.svef32mm && self.sve && asimd);
enable_feature(Feature::f64mm, self.svef64mm && self.sve && asimd);
// Cryptographic extensions require ASIMD
enable_feature(Feature::aes, self.aes && asimd);
enable_feature(Feature::sha2, self.sha1 && self.sha2 && asimd);
// SHA512/SHA3 require SHA1 & SHA256
enable_feature(
Feature::sha3,
self.sha512 && self.sha3 && self.sha1 && self.sha2 && asimd,
);
enable_feature(Feature::sm4, self.sm3 && self.sm4 && asimd);
// SVE2 requires SVE
let sve2 = self.sve2 && self.sve && asimd;
enable_feature(Feature::sve2, sve2);
enable_feature(Feature::sve2p1, self.sve2p1 && sve2);
// SVE2 extensions require SVE2 and crypto features
enable_feature(Feature::sve2_aes, self.sveaes && self.svepmull && sve2 && self.aes);
enable_feature(Feature::sve2_sm4, self.svesm4 && sve2 && self.sm3 && self.sm4);
enable_feature(
Feature::sve2_sha3,
self.svesha3 && sve2 && self.sha512 && self.sha3 && self.sha1 && self.sha2,
);
enable_feature(Feature::sve2_bitperm, self.svebitperm && self.sve2);
enable_feature(Feature::sve_b16b16, self.bf16 && self.sveb16b16);
enable_feature(Feature::hbc, self.hbc);
enable_feature(Feature::mops, self.mops);
enable_feature(Feature::ecv, self.ecv);
enable_feature(Feature::lut, self.lut);
enable_feature(Feature::cssc, self.cssc);
enable_feature(Feature::fpmr, self.fpmr);
enable_feature(Feature::faminmax, self.faminmax);
let fp8 = self.f8cvt && self.faminmax && self.lut && self.bf16;
enable_feature(Feature::fp8, fp8);
let fp8fma = self.f8fma && fp8;
enable_feature(Feature::fp8fma, fp8fma);
let fp8dot4 = self.f8dp4 && fp8fma;
enable_feature(Feature::fp8dot4, fp8dot4);
enable_feature(Feature::fp8dot2, self.f8dp2 && fp8dot4);
enable_feature(Feature::wfxt, self.wfxt);
let sme = self.sme && self.bf16;
enable_feature(Feature::sme, sme);
enable_feature(Feature::sme_i16i64, self.smei16i64 && sme);
enable_feature(Feature::sme_f64f64, self.smef64f64 && sme);
enable_feature(Feature::sme_fa64, self.smefa64 && sme && sve2);
let sme2 = self.sme2 && sme;
enable_feature(Feature::sme2, sme2);
enable_feature(Feature::sme2p1, self.sme2p1 && sme2);
enable_feature(
Feature::sme_b16b16,
sme2 && self.bf16 && self.sveb16b16 && self.smeb16b16,
);
enable_feature(Feature::sme_f16f16, self.smef16f16 && sme2);
enable_feature(Feature::sme_lutv2, self.smelutv2);
let sme_f8f32 = self.smef8f32 && sme2 && fp8;
enable_feature(Feature::sme_f8f32, sme_f8f32);
enable_feature(Feature::sme_f8f16, self.smef8f16 && sme_f8f32);
let ssve_fp8fma = self.smesf8fma && sme2 && fp8;
enable_feature(Feature::ssve_fp8fma, ssve_fp8fma);
let ssve_fp8dot4 = self.smesf8dp4 && ssve_fp8fma;
enable_feature(Feature::ssve_fp8dot4, ssve_fp8dot4);
enable_feature(Feature::ssve_fp8dot2, self.smesf8dp2 && ssve_fp8dot4);
}
value
}
}
#[cfg(target_endian = "little")]
#[cfg(test)]
mod tests;

View File

@@ -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()
}
);
}

View File

@@ -0,0 +1,34 @@
//! Run-time feature detection for ARM on Linux.
use super::auxvec;
use crate::detect::{Feature, bit, cache};
/// Try to read the features from the auxiliary vector.
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
//
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm/include/uapi/asm/hwcap.h
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::i8mm, bit::test(auxv.hwcap, 27));
enable_feature(&mut value, Feature::dotprod, bit::test(auxv.hwcap, 24));
enable_feature(&mut value, Feature::neon, bit::test(auxv.hwcap, 12));
enable_feature(&mut value, Feature::pmull, bit::test(auxv.hwcap2, 1));
enable_feature(&mut value, Feature::crc, bit::test(auxv.hwcap2, 4));
enable_feature(&mut value, Feature::aes, bit::test(auxv.hwcap2, 0));
// SHA2 requires SHA1 & SHA2 features
enable_feature(
&mut value,
Feature::sha2,
bit::test(auxv.hwcap2, 2) && bit::test(auxv.hwcap2, 3),
);
return value;
}
value
}

View File

@@ -0,0 +1,221 @@
//! Parses ELF auxiliary vectors.
#![allow(dead_code)]
pub(crate) const AT_NULL: usize = 0;
/// Key to access the CPU Hardware capabilities bitfield.
pub(crate) const AT_HWCAP: usize = 16;
/// Key to access the CPU Hardware capabilities 2 bitfield.
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
pub(crate) const AT_HWCAP2: usize = 26;
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
///
/// If an entry cannot be read all the bits in the bitfield are set to zero.
/// This should be interpreted as all the features being disabled.
#[derive(Debug, Copy, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub(crate) struct AuxVec {
pub hwcap: usize,
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
pub hwcap2: usize,
}
/// ELF Auxiliary Vector
///
/// The auxiliary vector is a memory region in a running ELF program's stack
/// composed of (key: usize, value: usize) pairs.
///
/// The keys used in the aux vector are platform dependent. For Linux, they are
/// defined in [linux/auxvec.h][auxvec_h]. The hardware capabilities of a given
/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
///
/// There is no perfect way of reading the auxiliary vector.
///
/// - If [`getauxval`] is linked to the binary we use it, and otherwise it will
/// try to read `/proc/self/auxv`.
/// - If that fails, this function returns an error.
///
/// Note that run-time feature detection is not invoked for features that can
/// be detected at compile-time.
///
/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos}*` and
/// `*-android*` targets rather than `dlsym` it because we can safely assume
/// `getauxval` is linked to the binary.
/// - `*-linux-gnu*` targets ([since Rust 1.64](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html))
/// have glibc requirements higher than [glibc 2.16 that added `getauxval`](https://sourceware.org/legacy-ml/libc-announce/2012/msg00000.html).
/// - `*-linux-musl*` targets ([at least since Rust 1.15](https://github.com/rust-lang/rust/blob/1.15.0/src/ci/docker/x86_64-musl/build-musl.sh#L15))
/// use musl newer than [musl 1.1.0 that added `getauxval`](https://git.musl-libc.org/cgit/musl/tree/WHATSNEW?h=v1.1.0#n1197)
/// - `*-linux-ohos*` targets use a [fork of musl 1.2](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/native-lib/musl.md)
/// - `*-android*` targets ([since Rust 1.68](https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html))
/// have the minimum supported API level higher than [Android 4.3 (API level 18) that added `getauxval`](https://github.com/aosp-mirror/platform_bionic/blob/d3ebc2f7c49a9893b114124d4a6b315f3a328764/libc/include/sys/auxv.h#L49).
///
/// For more information about when `getauxval` is available check the great
/// [`auxv` crate documentation][auxv_docs].
///
/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h
/// [auxv_docs]: https://docs.rs/auxv/0.3.3/auxv/
/// [`getauxval`]: https://man7.org/linux/man-pages/man3/getauxval.3.html
pub(crate) fn auxv() -> Result<AuxVec, ()> {
// Try to call a getauxval function.
if let Ok(hwcap) = getauxval(AT_HWCAP) {
// Targets with only AT_HWCAP:
#[cfg(any(
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "mips",
target_arch = "mips64",
target_arch = "loongarch32",
target_arch = "loongarch64",
))]
{
// Zero could indicate that no features were detected, but it's also used to indicate
// an error. In either case, try the fallback.
if hwcap != 0 {
return Ok(AuxVec { hwcap });
}
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
{
if let Ok(hwcap2) = getauxval(AT_HWCAP2) {
// Zero could indicate that no features were detected, but it's also used to indicate
// an error. In particular, on many platforms AT_HWCAP2 will be legitimately zero,
// since it contains the most recent feature flags. Use the fallback only if no
// features were detected at all.
if hwcap != 0 || hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
}
// Intentionnaly not used
let _ = hwcap;
}
// If calling getauxval fails, try to read the auxiliary vector from
// its file:
auxv_from_file("/proc/self/auxv").map_err(|_| ())
}
/// Tries to read the `key` from the auxiliary vector by calling the
/// `getauxval` function. If the function is not linked, this function return `Err`.
fn getauxval(key: usize) -> Result<usize, ()> {
type F = unsafe extern "C" fn(libc::c_ulong) -> libc::c_ulong;
cfg_select! {
any(
all(
target_os = "linux",
any(target_env = "gnu", target_env = "musl", target_env = "ohos"),
),
target_os = "android",
) => {
let ffi_getauxval: F = libc::getauxval;
}
_ => {
let ffi_getauxval: F = unsafe {
let ptr = libc::dlsym(libc::RTLD_DEFAULT, c"getauxval".as_ptr());
if ptr.is_null() {
return Err(());
}
core::mem::transmute(ptr)
};
}
}
Ok(unsafe { ffi_getauxval(key as libc::c_ulong) as usize })
}
/// Tries to read the auxiliary vector from the `file`. If this fails, this
/// function returns `Err`.
pub(super) fn auxv_from_file(file: &str) -> Result<AuxVec, alloc::string::String> {
let file = super::read_file(file)?;
auxv_from_file_bytes(&file)
}
/// Read auxiliary vector from a slice of bytes.
pub(super) fn auxv_from_file_bytes(bytes: &[u8]) -> Result<AuxVec, alloc::string::String> {
// See <https://github.com/torvalds/linux/blob/v5.15/include/uapi/linux/auxvec.h>.
//
// The auxiliary vector contains at most 34 (key,value) fields: from
// `AT_MINSIGSTKSZ` to `AT_NULL`, but its number may increase.
let len = bytes.len();
let mut buf = alloc::vec![0_usize; 1 + len / core::mem::size_of::<usize>()];
unsafe {
core::ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr() as *mut u8, len);
}
auxv_from_buf(&buf)
}
/// Tries to interpret the `buffer` as an auxiliary vector. If that fails, this
/// function returns `Err`.
fn auxv_from_buf(buf: &[usize]) -> Result<AuxVec, alloc::string::String> {
// Targets with only AT_HWCAP:
#[cfg(any(
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "mips",
target_arch = "mips64",
target_arch = "loongarch32",
target_arch = "loongarch64",
))]
{
for el in buf.chunks(2) {
match el[0] {
AT_NULL => break,
AT_HWCAP => return Ok(AuxVec { hwcap: el[1] }),
_ => (),
}
}
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
{
let mut hwcap = None;
// For some platforms, AT_HWCAP2 was added recently, so let it default to zero.
let mut hwcap2 = 0;
for el in buf.chunks(2) {
match el[0] {
AT_NULL => break,
AT_HWCAP => hwcap = Some(el[1]),
AT_HWCAP2 => hwcap2 = el[1],
_ => (),
}
}
if let Some(hwcap) = hwcap {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
// Suppress unused variable
let _ = buf;
Err(alloc::string::String::from("hwcap not found"))
}
#[cfg(test)]
mod tests;

View File

@@ -0,0 +1,108 @@
use super::*;
// FIXME: on mips/mips64 getauxval returns 0, and /proc/self/auxv
// does not always contain the AT_HWCAP key under qemu.
#[cfg(any(
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
#[test]
fn auxv_crate() {
let v = auxv();
if let Ok(hwcap) = getauxval(AT_HWCAP) {
let rt_hwcap = v.expect("failed to find hwcap key").hwcap;
assert_eq!(rt_hwcap, hwcap);
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
{
if let Ok(hwcap2) = getauxval(AT_HWCAP2) {
let rt_hwcap2 = v.expect("failed to find hwcap2 key").hwcap2;
assert_eq!(rt_hwcap2, hwcap2);
}
}
}
#[test]
fn auxv_dump() {
if let Ok(auxvec) = auxv() {
println!("{:?}", auxvec);
} else {
println!("both getauxval() and reading /proc/self/auxv failed!");
}
}
cfg_select! {
target_arch = "arm" => {
// The tests below can be executed under qemu, where we do not have access to the test
// files on disk, so we need to embed them with `include_bytes!`.
#[test]
fn linux_rpi3() {
let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-rpi3.auxv"));
let v = auxv_from_file_bytes(auxv).unwrap();
assert_eq!(v.hwcap, 4174038);
assert_eq!(v.hwcap2, 16);
}
#[test]
fn linux_macos_vb() {
let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv"));
// The file contains HWCAP but not HWCAP2. In that case, we treat HWCAP2 as zero.
let v = auxv_from_file_bytes(auxv).unwrap();
assert_eq!(v.hwcap, 126614527);
assert_eq!(v.hwcap2, 0);
}
}
target_arch = "aarch64" => {
#[cfg(target_endian = "little")]
#[test]
fn linux_artificial_aarch64() {
let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-artificial-aarch64.auxv"));
let v = auxv_from_file_bytes(auxv).unwrap();
assert_eq!(v.hwcap, 0x0123456789abcdef);
assert_eq!(v.hwcap2, 0x02468ace13579bdf);
}
#[cfg(target_endian = "little")]
#[test]
fn linux_no_hwcap2_aarch64() {
let auxv = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-no-hwcap2-aarch64.auxv"));
let v = auxv_from_file_bytes(auxv).unwrap();
// An absent HWCAP2 is treated as zero, and does not prevent acceptance of HWCAP.
assert_ne!(v.hwcap, 0);
assert_eq!(v.hwcap2, 0);
}
}
_ => {}
}
#[test]
fn auxv_dump_procfs() {
if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") {
println!("{:?}", auxvec);
} else {
println!("reading /proc/self/auxv failed!");
}
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
#[test]
fn auxv_crate_procfs() {
if let Ok(procfs_auxv) = auxv_from_file("/proc/self/auxv") {
assert_eq!(auxv().unwrap(), procfs_auxv);
}
}

View File

@@ -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
}

View File

@@ -0,0 +1,23 @@
//! Run-time feature detection for MIPS on Linux.
use super::auxvec;
use crate::detect::{Feature, bit, cache};
/// Try to read the features from the auxiliary vector.
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
//
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/mips/include/uapi/asm/hwcap.h
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::msa, bit::test(auxv.hwcap, 1));
return value;
}
value
}

View File

@@ -0,0 +1,74 @@
//! Run-time feature detection on Linux
use alloc::vec::Vec;
mod auxvec;
fn read_file(orig_path: &str) -> Result<Vec<u8>, alloc::string::String> {
use alloc::format;
let mut path = Vec::from(orig_path.as_bytes());
path.push(0);
unsafe {
let file = libc::open(path.as_ptr() as *const libc::c_char, libc::O_RDONLY);
if file == -1 {
return Err(format!("Cannot open file at {orig_path}"));
}
let mut data = Vec::new();
loop {
data.reserve(4096);
let spare = data.spare_capacity_mut();
match libc::read(file, spare.as_mut_ptr() as *mut _, spare.len()) {
-1 => {
libc::close(file);
return Err(format!("Error while reading from file at {orig_path}"));
}
0 => break,
n => data.set_len(data.len() + n as usize),
}
}
libc::close(file);
Ok(data)
}
}
cfg_select! {
target_arch = "aarch64" => {
mod aarch64;
pub(crate) use self::aarch64::detect_features;
}
target_arch = "arm" => {
mod arm;
pub(crate) use self::arm::detect_features;
}
any(target_arch = "riscv32", target_arch = "riscv64") => {
mod riscv;
pub(crate) use self::riscv::detect_features;
}
any(target_arch = "mips", target_arch = "mips64") => {
mod mips;
pub(crate) use self::mips::detect_features;
}
any(target_arch = "powerpc", target_arch = "powerpc64") => {
mod powerpc;
pub(crate) use self::powerpc::detect_features;
}
any(target_arch = "loongarch32", target_arch = "loongarch64") => {
mod loongarch;
pub(crate) use self::loongarch::detect_features;
}
target_arch = "s390x" => {
mod s390x;
pub(crate) use self::s390x::detect_features;
}
_ => {
use crate::detect::cache;
/// Performs run-time feature detection.
pub(crate) fn detect_features() -> cache::Initializer {
cache::Initializer::default()
}
}
}

View File

@@ -0,0 +1,35 @@
//! Run-time feature detection for PowerPC on Linux.
use super::auxvec;
use crate::detect::{Feature, cache};
/// Try to read the features from the auxiliary vector.
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
// The values are part of the platform-specific [asm/cputable.h][cputable]
//
// [cputable]: https://github.com/torvalds/linux/blob/master/arch/powerpc/include/uapi/asm/cputable.h
if let Ok(auxv) = auxvec::auxv() {
// note: the PowerPC values are the mask to do the test (instead of the
// index of the bit to test like in ARM and Aarch64)
enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0);
enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0);
let power8_features = auxv.hwcap2 & 0x80000000 != 0;
enable_feature(&mut value, Feature::power8, power8_features);
enable_feature(&mut value, Feature::power8_altivec, power8_features);
enable_feature(&mut value, Feature::power8_crypto, power8_features);
enable_feature(&mut value, Feature::power8_vector, power8_features);
let power9_features = auxv.hwcap2 & 0x00800000 != 0;
enable_feature(&mut value, Feature::power9, power9_features);
enable_feature(&mut value, Feature::power9_altivec, power9_features);
enable_feature(&mut value, Feature::power9_vector, power9_features);
return value;
}
value
}

View File

@@ -0,0 +1,323 @@
//! Run-time feature detection for RISC-V on Linux.
//!
//! On RISC-V, detection using auxv only supports single-letter extensions.
//! So, we use riscv_hwprobe that supports multi-letter extensions if available.
//! <https://www.kernel.org/doc/html/latest/arch/riscv/hwprobe.html>
use core::ptr;
use super::super::riscv::imply_features;
use super::auxvec;
use crate::detect::{Feature, bit, cache};
// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/prctl.h?h=v6.16>
// for runtime status query constants.
const PR_RISCV_V_GET_CONTROL: libc::c_int = 70;
const PR_RISCV_V_VSTATE_CTRL_ON: libc::c_int = 2;
const PR_RISCV_V_VSTATE_CTRL_CUR_MASK: libc::c_int = 3;
// See <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwprobe.h?h=v6.16>
// for riscv_hwprobe struct and hardware probing constants.
#[repr(C)]
struct riscv_hwprobe {
key: i64,
value: u64,
}
impl riscv_hwprobe {
// key is overwritten to -1 if not supported by riscv_hwprobe syscall.
pub fn get(&self) -> Option<u64> {
(self.key != -1).then_some(self.value)
}
}
#[allow(non_upper_case_globals)]
const __NR_riscv_hwprobe: libc::c_long = 258;
const RISCV_HWPROBE_KEY_BASE_BEHAVIOR: i64 = 3;
const RISCV_HWPROBE_BASE_BEHAVIOR_IMA: u64 = 1 << 0;
const RISCV_HWPROBE_KEY_IMA_EXT_0: i64 = 4;
const RISCV_HWPROBE_IMA_FD: u64 = 1 << 0;
const RISCV_HWPROBE_IMA_C: u64 = 1 << 1;
const RISCV_HWPROBE_IMA_V: u64 = 1 << 2;
const RISCV_HWPROBE_EXT_ZBA: u64 = 1 << 3;
const RISCV_HWPROBE_EXT_ZBB: u64 = 1 << 4;
const RISCV_HWPROBE_EXT_ZBS: u64 = 1 << 5;
const RISCV_HWPROBE_EXT_ZICBOZ: u64 = 1 << 6;
const RISCV_HWPROBE_EXT_ZBC: u64 = 1 << 7;
const RISCV_HWPROBE_EXT_ZBKB: u64 = 1 << 8;
const RISCV_HWPROBE_EXT_ZBKC: u64 = 1 << 9;
const RISCV_HWPROBE_EXT_ZBKX: u64 = 1 << 10;
const RISCV_HWPROBE_EXT_ZKND: u64 = 1 << 11;
const RISCV_HWPROBE_EXT_ZKNE: u64 = 1 << 12;
const RISCV_HWPROBE_EXT_ZKNH: u64 = 1 << 13;
const RISCV_HWPROBE_EXT_ZKSED: u64 = 1 << 14;
const RISCV_HWPROBE_EXT_ZKSH: u64 = 1 << 15;
const RISCV_HWPROBE_EXT_ZKT: u64 = 1 << 16;
const RISCV_HWPROBE_EXT_ZVBB: u64 = 1 << 17;
const RISCV_HWPROBE_EXT_ZVBC: u64 = 1 << 18;
const RISCV_HWPROBE_EXT_ZVKB: u64 = 1 << 19;
const RISCV_HWPROBE_EXT_ZVKG: u64 = 1 << 20;
const RISCV_HWPROBE_EXT_ZVKNED: u64 = 1 << 21;
const RISCV_HWPROBE_EXT_ZVKNHA: u64 = 1 << 22;
const RISCV_HWPROBE_EXT_ZVKNHB: u64 = 1 << 23;
const RISCV_HWPROBE_EXT_ZVKSED: u64 = 1 << 24;
const RISCV_HWPROBE_EXT_ZVKSH: u64 = 1 << 25;
const RISCV_HWPROBE_EXT_ZVKT: u64 = 1 << 26;
const RISCV_HWPROBE_EXT_ZFH: u64 = 1 << 27;
const RISCV_HWPROBE_EXT_ZFHMIN: u64 = 1 << 28;
const RISCV_HWPROBE_EXT_ZIHINTNTL: u64 = 1 << 29;
const RISCV_HWPROBE_EXT_ZVFH: u64 = 1 << 30;
const RISCV_HWPROBE_EXT_ZVFHMIN: u64 = 1 << 31;
const RISCV_HWPROBE_EXT_ZFA: u64 = 1 << 32;
const RISCV_HWPROBE_EXT_ZTSO: u64 = 1 << 33;
const RISCV_HWPROBE_EXT_ZACAS: u64 = 1 << 34;
const RISCV_HWPROBE_EXT_ZICOND: u64 = 1 << 35;
const RISCV_HWPROBE_EXT_ZIHINTPAUSE: u64 = 1 << 36;
const RISCV_HWPROBE_EXT_ZVE32X: u64 = 1 << 37;
const RISCV_HWPROBE_EXT_ZVE32F: u64 = 1 << 38;
const RISCV_HWPROBE_EXT_ZVE64X: u64 = 1 << 39;
const RISCV_HWPROBE_EXT_ZVE64F: u64 = 1 << 40;
const RISCV_HWPROBE_EXT_ZVE64D: u64 = 1 << 41;
const RISCV_HWPROBE_EXT_ZIMOP: u64 = 1 << 42;
const RISCV_HWPROBE_EXT_ZCA: u64 = 1 << 43;
const RISCV_HWPROBE_EXT_ZCB: u64 = 1 << 44;
const RISCV_HWPROBE_EXT_ZCD: u64 = 1 << 45;
const RISCV_HWPROBE_EXT_ZCF: u64 = 1 << 46;
const RISCV_HWPROBE_EXT_ZCMOP: u64 = 1 << 47;
const RISCV_HWPROBE_EXT_ZAWRS: u64 = 1 << 48;
// Excluded because it only reports the existence of `prctl`-based pointer masking control.
// const RISCV_HWPROBE_EXT_SUPM: u64 = 1 << 49;
const RISCV_HWPROBE_EXT_ZICNTR: u64 = 1 << 50;
const RISCV_HWPROBE_EXT_ZIHPM: u64 = 1 << 51;
const RISCV_HWPROBE_EXT_ZFBFMIN: u64 = 1 << 52;
const RISCV_HWPROBE_EXT_ZVFBFMIN: u64 = 1 << 53;
const RISCV_HWPROBE_EXT_ZVFBFWMA: u64 = 1 << 54;
const RISCV_HWPROBE_EXT_ZICBOM: u64 = 1 << 55;
const RISCV_HWPROBE_EXT_ZAAMO: u64 = 1 << 56;
const RISCV_HWPROBE_EXT_ZALRSC: u64 = 1 << 57;
const RISCV_HWPROBE_EXT_ZABHA: u64 = 1 << 58;
const RISCV_HWPROBE_KEY_CPUPERF_0: i64 = 5;
const RISCV_HWPROBE_MISALIGNED_FAST: u64 = 3;
const RISCV_HWPROBE_MISALIGNED_MASK: u64 = 7;
const RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF: i64 = 9;
const RISCV_HWPROBE_MISALIGNED_SCALAR_FAST: u64 = 3;
const RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF: i64 = 10;
const RISCV_HWPROBE_MISALIGNED_VECTOR_FAST: u64 = 3;
// syscall returns an unsupported error if riscv_hwprobe is not supported,
// so we can safely use this function on older versions of Linux.
fn _riscv_hwprobe(out: &mut [riscv_hwprobe]) -> bool {
unsafe fn __riscv_hwprobe(
pairs: *mut riscv_hwprobe,
pair_count: libc::size_t,
cpu_set_size: libc::size_t,
cpus: *mut libc::c_ulong,
flags: libc::c_uint,
) -> libc::c_long {
unsafe { libc::syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_set_size, cpus, flags) }
}
unsafe { __riscv_hwprobe(out.as_mut_ptr(), out.len(), 0, ptr::null_mut(), 0) == 0 }
}
/// Read list of supported features from (1) the auxiliary vector
/// and (2) the results of `riscv_hwprobe` and `prctl` system calls.
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let mut enable_feature = |feature, enable| {
if enable {
value.set(feature as u32);
}
};
// Use auxiliary vector to enable single-letter ISA extensions.
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
//
// [hwcap]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/riscv/include/uapi/asm/hwcap.h?h=v6.16
let auxv = auxvec::auxv().expect("read auxvec"); // should not fail on RISC-V platform
let mut has_i = bit::test(auxv.hwcap, (b'i' - b'a').into());
#[allow(clippy::eq_op)]
enable_feature(Feature::a, bit::test(auxv.hwcap, (b'a' - b'a').into()));
enable_feature(Feature::c, bit::test(auxv.hwcap, (b'c' - b'a').into()));
enable_feature(Feature::d, bit::test(auxv.hwcap, (b'd' - b'a').into()));
enable_feature(Feature::f, bit::test(auxv.hwcap, (b'f' - b'a').into()));
enable_feature(Feature::m, bit::test(auxv.hwcap, (b'm' - b'a').into()));
let has_v = bit::test(auxv.hwcap, (b'v' - b'a').into());
let mut is_v_set = false;
// Use riscv_hwprobe syscall to query more extensions and
// performance-related capabilities.
'hwprobe: {
macro_rules! init {
{ $($name: ident : $key: expr),* $(,)? } => {
#[repr(usize)]
enum Indices { $($name),* }
let mut t = [$(riscv_hwprobe { key: $key, value: 0 }),*];
macro_rules! data_mut { () => { &mut t } }
macro_rules! query { [$idx: ident] => { t[Indices::$idx as usize].get() } }
}
}
init! {
BaseBehavior: RISCV_HWPROBE_KEY_BASE_BEHAVIOR,
Extensions: RISCV_HWPROBE_KEY_IMA_EXT_0,
MisalignedScalarPerf: RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF,
MisalignedVectorPerf: RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF,
MisalignedScalarPerfFallback: RISCV_HWPROBE_KEY_CPUPERF_0,
};
if !_riscv_hwprobe(data_mut!()) {
break 'hwprobe;
}
// Query scalar misaligned behavior.
if let Some(value) = query![MisalignedScalarPerf] {
enable_feature(
Feature::unaligned_scalar_mem,
value == RISCV_HWPROBE_MISALIGNED_SCALAR_FAST,
);
} else if let Some(value) = query![MisalignedScalarPerfFallback] {
// Deprecated method for fallback
enable_feature(
Feature::unaligned_scalar_mem,
value & RISCV_HWPROBE_MISALIGNED_MASK == RISCV_HWPROBE_MISALIGNED_FAST,
);
}
// Query vector misaligned behavior.
if let Some(value) = query![MisalignedVectorPerf] {
enable_feature(
Feature::unaligned_vector_mem,
value == RISCV_HWPROBE_MISALIGNED_VECTOR_FAST,
);
}
// Query whether "I" base and extensions "M" and "A" (as in the ISA
// manual version 2.2) are enabled. "I" base at that time corresponds
// to "I", "Zicsr", "Zicntr" and "Zifencei" (as in the ISA manual version
// 20240411).
// This is a current requirement of
// `RISCV_HWPROBE_KEY_IMA_EXT_0`-based tests.
if query![BaseBehavior].is_none_or(|value| value & RISCV_HWPROBE_BASE_BEHAVIOR_IMA == 0) {
break 'hwprobe;
}
has_i = true;
enable_feature(Feature::zicsr, true);
enable_feature(Feature::zicntr, true);
enable_feature(Feature::zifencei, true);
enable_feature(Feature::m, true);
enable_feature(Feature::a, true);
// Enable features based on `RISCV_HWPROBE_KEY_IMA_EXT_0`.
let Some(ima_ext_0) = query![Extensions] else {
break 'hwprobe;
};
let test = |mask| (ima_ext_0 & mask) != 0;
enable_feature(Feature::d, test(RISCV_HWPROBE_IMA_FD)); // F is implied.
enable_feature(Feature::c, test(RISCV_HWPROBE_IMA_C));
enable_feature(Feature::zicntr, test(RISCV_HWPROBE_EXT_ZICNTR));
enable_feature(Feature::zihpm, test(RISCV_HWPROBE_EXT_ZIHPM));
enable_feature(Feature::zihintntl, test(RISCV_HWPROBE_EXT_ZIHINTNTL));
enable_feature(Feature::zihintpause, test(RISCV_HWPROBE_EXT_ZIHINTPAUSE));
enable_feature(Feature::zimop, test(RISCV_HWPROBE_EXT_ZIMOP));
enable_feature(Feature::zicbom, test(RISCV_HWPROBE_EXT_ZICBOM));
enable_feature(Feature::zicboz, test(RISCV_HWPROBE_EXT_ZICBOZ));
enable_feature(Feature::zicond, test(RISCV_HWPROBE_EXT_ZICOND));
enable_feature(Feature::zalrsc, test(RISCV_HWPROBE_EXT_ZALRSC));
enable_feature(Feature::zaamo, test(RISCV_HWPROBE_EXT_ZAAMO));
enable_feature(Feature::zawrs, test(RISCV_HWPROBE_EXT_ZAWRS));
enable_feature(Feature::zabha, test(RISCV_HWPROBE_EXT_ZABHA));
enable_feature(Feature::zacas, test(RISCV_HWPROBE_EXT_ZACAS));
enable_feature(Feature::ztso, test(RISCV_HWPROBE_EXT_ZTSO));
enable_feature(Feature::zba, test(RISCV_HWPROBE_EXT_ZBA));
enable_feature(Feature::zbb, test(RISCV_HWPROBE_EXT_ZBB));
enable_feature(Feature::zbs, test(RISCV_HWPROBE_EXT_ZBS));
enable_feature(Feature::zbc, test(RISCV_HWPROBE_EXT_ZBC));
enable_feature(Feature::zbkb, test(RISCV_HWPROBE_EXT_ZBKB));
enable_feature(Feature::zbkc, test(RISCV_HWPROBE_EXT_ZBKC));
enable_feature(Feature::zbkx, test(RISCV_HWPROBE_EXT_ZBKX));
enable_feature(Feature::zknd, test(RISCV_HWPROBE_EXT_ZKND));
enable_feature(Feature::zkne, test(RISCV_HWPROBE_EXT_ZKNE));
enable_feature(Feature::zknh, test(RISCV_HWPROBE_EXT_ZKNH));
enable_feature(Feature::zksed, test(RISCV_HWPROBE_EXT_ZKSED));
enable_feature(Feature::zksh, test(RISCV_HWPROBE_EXT_ZKSH));
enable_feature(Feature::zkt, test(RISCV_HWPROBE_EXT_ZKT));
enable_feature(Feature::zcmop, test(RISCV_HWPROBE_EXT_ZCMOP));
enable_feature(Feature::zca, test(RISCV_HWPROBE_EXT_ZCA));
enable_feature(Feature::zcf, test(RISCV_HWPROBE_EXT_ZCF));
enable_feature(Feature::zcd, test(RISCV_HWPROBE_EXT_ZCD));
enable_feature(Feature::zcb, test(RISCV_HWPROBE_EXT_ZCB));
enable_feature(Feature::zfh, test(RISCV_HWPROBE_EXT_ZFH));
enable_feature(Feature::zfhmin, test(RISCV_HWPROBE_EXT_ZFHMIN));
enable_feature(Feature::zfa, test(RISCV_HWPROBE_EXT_ZFA));
enable_feature(Feature::zfbfmin, test(RISCV_HWPROBE_EXT_ZFBFMIN));
// Use prctl (if any) to determine whether the vector extension
// is enabled on the current thread (assuming the entire process
// share the same status). If prctl fails (e.g. QEMU userland emulator
// as of version 9.2.3), use auxiliary vector to retrieve the default
// vector status on the process startup.
let has_vectors = {
let v_status = unsafe { libc::prctl(PR_RISCV_V_GET_CONTROL) };
if v_status >= 0 {
(v_status & PR_RISCV_V_VSTATE_CTRL_CUR_MASK) == PR_RISCV_V_VSTATE_CTRL_ON
} else {
has_v
}
};
if has_vectors {
enable_feature(Feature::v, test(RISCV_HWPROBE_IMA_V));
enable_feature(Feature::zve32x, test(RISCV_HWPROBE_EXT_ZVE32X));
enable_feature(Feature::zve32f, test(RISCV_HWPROBE_EXT_ZVE32F));
enable_feature(Feature::zve64x, test(RISCV_HWPROBE_EXT_ZVE64X));
enable_feature(Feature::zve64f, test(RISCV_HWPROBE_EXT_ZVE64F));
enable_feature(Feature::zve64d, test(RISCV_HWPROBE_EXT_ZVE64D));
enable_feature(Feature::zvbb, test(RISCV_HWPROBE_EXT_ZVBB));
enable_feature(Feature::zvbc, test(RISCV_HWPROBE_EXT_ZVBC));
enable_feature(Feature::zvkb, test(RISCV_HWPROBE_EXT_ZVKB));
enable_feature(Feature::zvkg, test(RISCV_HWPROBE_EXT_ZVKG));
enable_feature(Feature::zvkned, test(RISCV_HWPROBE_EXT_ZVKNED));
enable_feature(Feature::zvknha, test(RISCV_HWPROBE_EXT_ZVKNHA));
enable_feature(Feature::zvknhb, test(RISCV_HWPROBE_EXT_ZVKNHB));
enable_feature(Feature::zvksed, test(RISCV_HWPROBE_EXT_ZVKSED));
enable_feature(Feature::zvksh, test(RISCV_HWPROBE_EXT_ZVKSH));
enable_feature(Feature::zvkt, test(RISCV_HWPROBE_EXT_ZVKT));
enable_feature(Feature::zvfh, test(RISCV_HWPROBE_EXT_ZVFH));
enable_feature(Feature::zvfhmin, test(RISCV_HWPROBE_EXT_ZVFHMIN));
enable_feature(Feature::zvfbfmin, test(RISCV_HWPROBE_EXT_ZVFBFMIN));
enable_feature(Feature::zvfbfwma, test(RISCV_HWPROBE_EXT_ZVFBFWMA));
}
is_v_set = true;
};
// Set V purely depending on the auxiliary vector
// only if no fine-grained vector extension detection is available.
if !is_v_set {
enable_feature(Feature::v, has_v);
}
// Handle base ISA.
// If future RV128I is supported, implement with `enable_feature` here.
// Note that we should use `target_arch` instead of `target_pointer_width`
// to avoid misdetection caused by experimental ABIs such as RV64ILP32.
#[cfg(target_arch = "riscv64")]
enable_feature(Feature::rv64i, has_i);
#[cfg(target_arch = "riscv32")]
enable_feature(Feature::rv32i, has_i);
imply_features(value)
}

View File

@@ -0,0 +1,152 @@
//! Run-time feature detection for s390x on Linux.
use super::auxvec;
use crate::detect::{Feature, bit, cache};
/// Try to read the features from the auxiliary vector
pub(crate) fn detect_features() -> cache::Initializer {
let opt_hwcap: Option<AtHwcap> = auxvec::auxv().ok().map(Into::into);
let facilities = ExtendedFacilityList::new();
cache(opt_hwcap, facilities)
}
#[derive(Debug, Default, PartialEq)]
struct AtHwcap {
esan3: bool,
zarch: bool,
stfle: bool,
msa: bool,
ldisp: bool,
eimm: bool,
dfp: bool,
hpage: bool,
etf3eh: bool,
high_gprs: bool,
te: bool,
vxrs: bool,
vxrs_bcd: bool,
vxrs_ext: bool,
gs: bool,
vxrs_ext2: bool,
vxrs_pde: bool,
sort: bool,
dflt: bool,
vxrs_pde2: bool,
nnpa: bool,
pci_mio: bool,
sie: bool,
}
impl From<auxvec::AuxVec> for AtHwcap {
/// Reads AtHwcap from the auxiliary vector.
fn from(auxv: auxvec::AuxVec) -> Self {
AtHwcap {
esan3: bit::test(auxv.hwcap, 0),
zarch: bit::test(auxv.hwcap, 1),
stfle: bit::test(auxv.hwcap, 2),
msa: bit::test(auxv.hwcap, 3),
ldisp: bit::test(auxv.hwcap, 4),
eimm: bit::test(auxv.hwcap, 5),
dfp: bit::test(auxv.hwcap, 6),
hpage: bit::test(auxv.hwcap, 7),
etf3eh: bit::test(auxv.hwcap, 8),
high_gprs: bit::test(auxv.hwcap, 9),
te: bit::test(auxv.hwcap, 10),
vxrs: bit::test(auxv.hwcap, 11),
vxrs_bcd: bit::test(auxv.hwcap, 12),
vxrs_ext: bit::test(auxv.hwcap, 13),
gs: bit::test(auxv.hwcap, 14),
vxrs_ext2: bit::test(auxv.hwcap, 15),
vxrs_pde: bit::test(auxv.hwcap, 16),
sort: bit::test(auxv.hwcap, 17),
dflt: bit::test(auxv.hwcap, 18),
vxrs_pde2: bit::test(auxv.hwcap, 19),
nnpa: bit::test(auxv.hwcap, 20),
pci_mio: bit::test(auxv.hwcap, 21),
sie: bit::test(auxv.hwcap, 22),
}
}
}
struct ExtendedFacilityList([u64; 4]);
impl ExtendedFacilityList {
fn new() -> Self {
let mut result: [u64; 4] = [0; 4];
// SAFETY: rust/llvm only support s390x version with the `stfle` instruction.
unsafe {
core::arch::asm!(
// equivalently ".insn s, 0xb2b00000, 0({1})",
"stfle 0({})",
in(reg_addr) result.as_mut_ptr() ,
inout("r0") result.len() as u64 - 1 => _,
options(nostack)
);
}
Self(result)
}
const fn get_bit(&self, n: usize) -> bool {
// NOTE: bits are numbered from the left.
self.0[n / 64] & (1 << (63 - (n % 64))) != 0
}
}
/// Initializes the cache from the feature bits.
///
/// These values are part of the platform-specific [asm/elf.h][kernel], and are a selection of the
/// fields found in the [Facility Indications].
///
/// [Facility Indications]: https://www.ibm.com/support/pages/sites/default/files/2021-05/SA22-7871-10.pdf#page=63
/// [kernel]: https://github.com/torvalds/linux/blob/b62cef9a5c673f1b8083159f5dc03c1c5daced2f/arch/s390/include/asm/elf.h#L129
fn cache(hwcap: Option<AtHwcap>, facilities: ExtendedFacilityList) -> cache::Initializer {
let mut value = cache::Initializer::default();
{
let mut enable_if_set = |bit_index, f| {
if facilities.get_bit(bit_index) {
value.set(f as u32);
}
};
// We use HWCAP for `vector` because it requires both hardware and kernel support.
if let Some(AtHwcap { vxrs: true, .. }) = hwcap {
// vector and related
enable_if_set(129, Feature::vector);
enable_if_set(135, Feature::vector_enhancements_1);
enable_if_set(148, Feature::vector_enhancements_2);
enable_if_set(198, Feature::vector_enhancements_3);
enable_if_set(134, Feature::vector_packed_decimal);
enable_if_set(152, Feature::vector_packed_decimal_enhancement);
enable_if_set(192, Feature::vector_packed_decimal_enhancement_2);
enable_if_set(199, Feature::vector_packed_decimal_enhancement_3);
enable_if_set(165, Feature::nnp_assist);
}
// others
enable_if_set(76, Feature::message_security_assist_extension3);
enable_if_set(77, Feature::message_security_assist_extension4);
enable_if_set(57, Feature::message_security_assist_extension5);
enable_if_set(146, Feature::message_security_assist_extension8);
enable_if_set(155, Feature::message_security_assist_extension9);
enable_if_set(86, Feature::message_security_assist_extension12);
enable_if_set(58, Feature::miscellaneous_extensions_2);
enable_if_set(61, Feature::miscellaneous_extensions_3);
enable_if_set(84, Feature::miscellaneous_extensions_4);
enable_if_set(45, Feature::high_word);
enable_if_set(73, Feature::transactional_execution);
enable_if_set(133, Feature::guarded_storage);
enable_if_set(150, Feature::enhanced_sort);
enable_if_set(151, Feature::deflate_conversion);
enable_if_set(201, Feature::concurrent_functions);
}
value
}

View File

@@ -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() })
}

View File

@@ -0,0 +1,54 @@
//! Parses ELF auxiliary vectors.
#![cfg_attr(
any(target_arch = "aarch64", target_arch = "powerpc64", target_arch = "riscv64"),
allow(dead_code)
)]
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
///
/// If an entry cannot be read all the bits in the bitfield are set to zero.
/// This should be interpreted as all the features being disabled.
#[derive(Debug, Copy, Clone)]
pub(crate) struct AuxVec {
pub hwcap: usize,
pub hwcap2: usize,
}
/// ELF Auxiliary Vector
///
/// The auxiliary vector is a memory region in a running ELF program's stack
/// composed of (key: usize, value: usize) pairs.
///
/// The keys used in the aux vector are platform dependent. For OpenBSD, they are
/// defined in [machine/elf.h][elfh]. The hardware capabilities of a given CPU
/// can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
///
/// Note that run-time feature detection is not invoked for features that can
/// be detected at compile-time.
///
/// [elf.h]: https://github.com/openbsd/src/blob/master/sys/arch/arm64/include/elf.h
/// [elf.h]: https://github.com/openbsd/src/blob/master/sys/arch/powerpc64/include/elf.h
pub(crate) fn auxv() -> Result<AuxVec, ()> {
let hwcap = archauxv(libc::AT_HWCAP);
let hwcap2 = archauxv(libc::AT_HWCAP2);
// Zero could indicate that no features were detected, but it's also used to
// indicate an error. In particular, on many platforms AT_HWCAP2 will be
// legitimately zero, since it contains the most recent feature flags.
if hwcap != 0 || hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
Err(())
}
/// Tries to read the `key` from the auxiliary vector.
fn archauxv(key: libc::c_int) -> usize {
const OUT_LEN: libc::c_int = core::mem::size_of::<libc::c_ulong>() as libc::c_int;
let mut out: libc::c_ulong = 0;
unsafe {
let res =
libc::elf_aux_info(key, &mut out as *mut libc::c_ulong as *mut libc::c_void, OUT_LEN);
// If elf_aux_info fails, `out` will be left at zero (which is the proper default value).
debug_assert!(res == 0 || out == 0);
}
out as usize
}

View File

@@ -0,0 +1,21 @@
//! Run-time feature detection on OpenBSD
mod auxvec;
cfg_select! {
target_arch = "aarch64" => {
mod aarch64;
pub(crate) use self::aarch64::detect_features;
}
target_arch = "powerpc64" => {
mod powerpc;
pub(crate) use self::powerpc::detect_features;
}
_ => {
use crate::detect::cache;
/// Performs run-time feature detection.
pub(crate) fn detect_features() -> cache::Initializer {
cache::Initializer::default()
}
}
}

View File

@@ -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
}

View File

@@ -0,0 +1,8 @@
//! Other operating systems
use crate::detect::cache;
#[allow(dead_code)]
pub(crate) fn detect_features() -> cache::Initializer {
cache::Initializer::default()
}

View File

@@ -0,0 +1,159 @@
//! Run-time feature detection utility for RISC-V.
//!
//! On RISC-V, full feature detection needs a help of one or more
//! feature detection mechanisms (usually provided by the operating system).
//!
//! RISC-V architecture defines many extensions and some have dependency to others.
//! More importantly, some of them cannot be enabled without resolving such
//! dependencies due to limited set of features that such mechanisms provide.
//!
//! This module provides an OS-independent utility to process such relations
//! between RISC-V extensions.
use crate::detect::{Feature, cache};
/// Imply features by the given set of enabled features.
///
/// Note that it does not perform any consistency checks including existence of
/// conflicting extensions and/or complicated requirements. Eliminating such
/// inconsistencies is the responsibility of the feature detection logic and
/// its provider(s).
pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initializer {
loop {
// Check convergence of the feature flags later.
let prev = value;
// Expect that the optimizer turns repeated operations into
// a fewer number of bit-manipulation operations.
macro_rules! imply {
// Regular implication:
// A1 => (B1[, B2...]), A2 => (B1[, B2...]) and so on.
($($from: ident)|+ => $($to: ident)&+) => {
if [$(Feature::$from as u32),+].iter().any(|&x| value.test(x)) {
$(
value.set(Feature::$to as u32);
)+
}
};
// Implication with multiple requirements:
// A1 && A2 ... => (B1[, B2...]).
($($from: ident)&+ => $($to: ident)&+) => {
if [$(Feature::$from as u32),+].iter().all(|&x| value.test(x)) {
$(
value.set(Feature::$to as u32);
)+
}
};
}
macro_rules! group {
($group: ident == $($member: ident)&+) => {
// Forward implication as defined in the specifications.
imply!($group => $($member)&+);
// Reverse implication to "group extension" from its members.
// This is not a part of specifications but convenient for
// feature detection and implemented in e.g. LLVM.
imply!($($member)&+ => $group);
};
}
/*
If a dependency/implication is not explicitly stated in the
specification, it is denoted as a comment as follows:
"defined as subset":
The latter extension is described as a subset of the former
(but the evidence is weak).
"functional":
The former extension is functionally a superset of the latter
(no direct references though).
*/
imply!(zvbb => zvkb);
// Certain set of vector cryptography extensions form a group.
group!(zvkn == zvkned & zvknhb & zvkb & zvkt);
group!(zvknc == zvkn & zvbc);
group!(zvkng == zvkn & zvkg);
group!(zvks == zvksed & zvksh & zvkb & zvkt);
group!(zvksc == zvks & zvbc);
group!(zvksg == zvks & zvkg);
imply!(zvknhb => zvknha); // functional
// For vector cryptography, Zvknhb and Zvbc require integer arithmetic
// with EEW=64 (Zve64x) while others not depending on them
// require EEW=32 (Zve32x).
imply!(zvknhb | zvbc => zve64x);
imply!(zvbb | zvkb | zvkg | zvkned | zvknha | zvksed | zvksh => zve32x);
imply!(zbc => zbkc); // defined as subset
group!(zkn == zbkb & zbkc & zbkx & zkne & zknd & zknh);
group!(zks == zbkb & zbkc & zbkx & zksed & zksh);
group!(zk == zkn & zkr & zkt);
imply!(zabha | zacas => zaamo);
group!(a == zalrsc & zaamo);
group!(b == zba & zbb & zbs);
imply!(zcf => zca & f);
imply!(zcd => zca & d);
imply!(zcmop | zcb => zca);
imply!(zhinx => zhinxmin);
imply!(zdinx | zhinxmin => zfinx);
imply!(zvfh => zvfhmin); // functional
imply!(zvfh => zve32f & zfhmin);
imply!(zvfhmin => zve32f);
imply!(zvfbfwma => zvfbfmin & zfbfmin);
imply!(zvfbfmin => zve32f);
imply!(v => zve64d);
imply!(zve64d => zve64f & d);
imply!(zve64f => zve64x & zve32f);
imply!(zve64x => zve32x);
imply!(zve32f => zve32x & f);
imply!(zfh => zfhmin);
imply!(q => d);
imply!(d | zfhmin | zfa => f);
imply!(zfbfmin => f); // and some of (not all) "Zfh" instructions.
// Relatively complex implication rules around the "C" extension.
// (from "C" and some others)
imply!(c => zca);
imply!(c & d => zcd);
#[cfg(target_arch = "riscv32")]
imply!(c & f => zcf);
// (to "C"; defined as superset)
cfg_select! {
target_arch = "riscv32" => {
if value.test(Feature::d as u32) {
imply!(zcf & zcd => c);
} else if value.test(Feature::f as u32) {
imply!(zcf => c);
} else {
imply!(zca => c);
}
}
_ => {
if value.test(Feature::d as u32) {
imply!(zcd => c);
} else {
imply!(zca => c);
}
}
}
imply!(zicntr | zihpm | f | zfinx | zve32x => zicsr);
// Loop until the feature flags converge.
if prev == value {
return value;
}
}
}
#[cfg(test)]
#[path = "riscv/tests.rs"]
mod tests;

View File

@@ -0,0 +1,64 @@
use super::*;
#[test]
fn simple_direct() {
let mut value = cache::Initializer::default();
value.set(Feature::f as u32);
// F (and other extensions with CSRs) -> Zicsr
assert!(imply_features(value).test(Feature::zicsr as u32));
}
#[test]
fn simple_indirect() {
let mut value = cache::Initializer::default();
value.set(Feature::q as u32);
// Q -> D, D -> F, F -> Zicsr
assert!(imply_features(value).test(Feature::zicsr as u32));
}
#[test]
fn complex_zcd() {
let mut value = cache::Initializer::default();
// C & D -> Zcd
value.set(Feature::c as u32);
assert!(!imply_features(value).test(Feature::zcd as u32));
value.set(Feature::d as u32);
assert!(imply_features(value).test(Feature::zcd as u32));
}
#[test]
fn group_simple_forward() {
let mut value = cache::Initializer::default();
// A -> Zalrsc & Zaamo (forward implication)
value.set(Feature::a as u32);
let value = imply_features(value);
assert!(value.test(Feature::zalrsc as u32));
assert!(value.test(Feature::zaamo as u32));
}
#[test]
fn group_simple_backward() {
let mut value = cache::Initializer::default();
// Zalrsc & Zaamo -> A (reverse implication)
value.set(Feature::zalrsc as u32);
value.set(Feature::zaamo as u32);
assert!(imply_features(value).test(Feature::a as u32));
}
#[test]
fn group_complex_convergence() {
let mut value = cache::Initializer::default();
// Needs 3 iterations to converge
// (and 4th iteration for convergence checking):
// 1. [Zvksc] -> Zvks & Zvbc
// 2. Zvks -> Zvksed & Zvksh & Zvkb & Zvkt
// 3a. [Zvkned] & [Zvknhb] & [Zvkb] & Zvkt -> {Zvkn}
// 3b. Zvkn & Zvbc -> {Zvknc}
value.set(Feature::zvksc as u32);
value.set(Feature::zvkned as u32);
value.set(Feature::zvknhb as u32);
value.set(Feature::zvkb as u32);
let value = imply_features(value);
assert!(value.test(Feature::zvkn as u32));
assert!(value.test(Feature::zvknc as u32));
}

View File

@@ -0,0 +1,125 @@
//! Run-time feature detection for Aarch64 on Windows.
use crate::detect::{Feature, cache};
/// Try to read the features using IsProcessorFeaturePresent.
pub(crate) fn detect_features() -> cache::Initializer {
type DWORD = u32;
type BOOL = i32;
const FALSE: BOOL = 0;
// The following Microsoft documents isn't updated for aarch64.
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
// These are defined in winnt.h of Windows SDK
const PF_ARM_VFP_32_REGISTERS_AVAILABLE: u32 = 18;
const PF_ARM_NEON_INSTRUCTIONS_AVAILABLE: u32 = 19;
const PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE: u32 = 30;
const PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE: u32 = 31;
const PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE: u32 = 34;
const PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE: u32 = 43;
const PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE: u32 = 44;
const PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE: u32 = 45;
const PF_ARM_SVE_INSTRUCTIONS_AVAILABLE: u32 = 46;
const PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE: u32 = 47;
const PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE: u32 = 48;
const PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE: u32 = 49;
const PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE: u32 = 50;
const PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE: u32 = 51;
// const PF_ARM_SVE_BF16_INSTRUCTIONS_AVAILABLE: u32 = 52;
// const PF_ARM_SVE_EBF16_INSTRUCTIONS_AVAILABLE: u32 = 53;
const PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE: u32 = 54;
const PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE: u32 = 55;
const PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE: u32 = 56;
// const PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE: u32 = 57;
// const PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE: u32 = 58;
// const PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE: u32 = 59;
unsafe extern "system" {
fn IsProcessorFeaturePresent(ProcessorFeature: DWORD) -> BOOL;
}
let mut value = cache::Initializer::default();
{
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};
// Some features may be supported on current CPU,
// but no way to detect it by OS API.
// Also, we require unsafe block for the extern "system" calls.
unsafe {
enable_feature(
Feature::fp,
IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::asimd,
IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::crc,
IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::lse,
IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::dotprod,
IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::jsconv,
IsProcessorFeaturePresent(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::rcpc,
IsProcessorFeaturePresent(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::sve,
IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::sve2,
IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::sve2p1,
IsProcessorFeaturePresent(PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::sve2_aes,
IsProcessorFeaturePresent(PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE) != FALSE
&& IsProcessorFeaturePresent(PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE)
!= FALSE,
);
enable_feature(
Feature::sve2_bitperm,
IsProcessorFeaturePresent(PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::sve_b16b16,
IsProcessorFeaturePresent(PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::sve2_sha3,
IsProcessorFeaturePresent(PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE) != FALSE,
);
enable_feature(
Feature::sve2_sm4,
IsProcessorFeaturePresent(PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE) != FALSE,
);
// PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE means aes, sha1, sha2 and
// pmull support
let crypto =
IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) != FALSE;
enable_feature(Feature::aes, crypto);
enable_feature(Feature::pmull, crypto);
enable_feature(Feature::sha2, crypto);
}
}
value
}

View File

@@ -0,0 +1,330 @@
//! x86 run-time feature detection is OS independent.
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use core::mem;
use crate::detect::{Feature, bit, cache};
/// Run-time feature detection on x86 works by using the CPUID instruction.
///
/// The [CPUID Wikipedia page][wiki_cpuid] contains
/// all the information about which flags to set to query which values, and in
/// which registers these are reported.
///
/// The definitive references are:
/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
/// Instruction Set Reference, A-Z][intel64_ref].
/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
/// System Instructions][amd64_ref].
///
/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
#[allow(clippy::similar_names)]
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
if cfg!(target_env = "sgx") {
// doesn't support this because it is untrusted data
return value;
}
// Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU
// has `cpuid` support.
// 0. EAX = 0: Basic Information:
// - EAX returns the "Highest Function Parameter", that is, the maximum
// leaf value for subsequent calls of `cpuinfo` in range [0,
// 0x8000_0000]. - The vendor ID is stored in 12 u8 ascii chars,
// returned in EBX, EDX, and ECX (in that order):
let (max_basic_leaf, vendor_id) = {
let CpuidResult { eax: max_basic_leaf, ebx, ecx, edx } = __cpuid(0);
let vendor_id: [[u8; 4]; 3] = [ebx.to_ne_bytes(), edx.to_ne_bytes(), ecx.to_ne_bytes()];
let vendor_id: [u8; 12] = unsafe { mem::transmute(vendor_id) };
(max_basic_leaf, vendor_id)
};
if max_basic_leaf < 1 {
// Earlier Intel 486, CPUID not implemented
return value;
}
// EAX = 1, ECX = 0: Queries "Processor Info and Feature Bits";
// Contains information about most x86 features.
let CpuidResult { ecx: proc_info_ecx, edx: proc_info_edx, .. } = __cpuid(0x0000_0001_u32);
// EAX = 7: Queries "Extended Features";
// Contains information about bmi,bmi2, and avx2 support.
let (
extended_features_ebx,
extended_features_ecx,
extended_features_edx,
extended_features_eax_leaf_1,
extended_features_edx_leaf_1,
) = if max_basic_leaf >= 7 {
let CpuidResult { ebx, ecx, edx, .. } = __cpuid(0x0000_0007_u32);
let CpuidResult { eax: eax_1, edx: edx_1, .. } =
__cpuid_count(0x0000_0007_u32, 0x0000_0001_u32);
(ebx, ecx, edx, eax_1, edx_1)
} else {
(0, 0, 0, 0, 0) // CPUID does not support "Extended Features"
};
// EAX = 0x8000_0000, ECX = 0: Get Highest Extended Function Supported
// - EAX returns the max leaf value for extended information, that is,
// `cpuid` calls in range [0x8000_0000; u32::MAX]:
let CpuidResult { eax: extended_max_basic_leaf, .. } = __cpuid(0x8000_0000_u32);
// EAX = 0x8000_0001, ECX=0: Queries "Extended Processor Info and Feature
// Bits"
let extended_proc_info_ecx = if extended_max_basic_leaf >= 1 {
let CpuidResult { ecx, .. } = __cpuid(0x8000_0001_u32);
ecx
} else {
0
};
{
// borrows value till the end of this scope:
let mut enable = |r, rb, f| {
let present = bit::test(r as usize, rb);
if present {
value.set(f as u32);
}
present
};
enable(proc_info_ecx, 0, Feature::sse3);
enable(proc_info_ecx, 1, Feature::pclmulqdq);
enable(proc_info_ecx, 9, Feature::ssse3);
enable(proc_info_ecx, 13, Feature::cmpxchg16b);
enable(proc_info_ecx, 19, Feature::sse4_1);
enable(proc_info_ecx, 20, Feature::sse4_2);
enable(proc_info_ecx, 22, Feature::movbe);
enable(proc_info_ecx, 23, Feature::popcnt);
enable(proc_info_ecx, 25, Feature::aes);
let f16c = enable(proc_info_ecx, 29, Feature::f16c);
enable(proc_info_ecx, 30, Feature::rdrand);
enable(extended_features_ebx, 18, Feature::rdseed);
enable(extended_features_ebx, 19, Feature::adx);
enable(extended_features_ebx, 11, Feature::rtm);
enable(proc_info_edx, 4, Feature::tsc);
enable(proc_info_edx, 23, Feature::mmx);
enable(proc_info_edx, 24, Feature::fxsr);
enable(proc_info_edx, 25, Feature::sse);
enable(proc_info_edx, 26, Feature::sse2);
enable(extended_features_ebx, 29, Feature::sha);
enable(extended_features_ecx, 8, Feature::gfni);
enable(extended_features_ecx, 9, Feature::vaes);
enable(extended_features_ecx, 10, Feature::vpclmulqdq);
enable(extended_features_ebx, 3, Feature::bmi1);
enable(extended_features_ebx, 8, Feature::bmi2);
enable(extended_features_ebx, 9, Feature::ermsb);
enable(extended_features_eax_leaf_1, 31, Feature::movrs);
// Detect if CPUID.19h available
if bit::test(extended_features_ecx as usize, 23) {
let CpuidResult { ebx, .. } = __cpuid(0x19);
enable(ebx, 0, Feature::kl);
enable(ebx, 2, Feature::widekl);
}
// This detects ABM on AMD CPUs and LZCNT on Intel CPUs.
// On intel CPUs with popcnt, lzcnt implements the
// "missing part" of ABM, so we map both to the same
// internal feature.
//
// The `is_x86_feature_detected!("lzcnt")` macro then
// internally maps to Feature::abm.
enable(extended_proc_info_ecx, 5, Feature::lzcnt);
// As Hygon Dhyana originates from AMD technology and shares most of the architecture with
// AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series
// number(Family 18h).
//
// For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD
// family 17h.
//
// Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf.
// Related Hygon kernel patch can be found on
// http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn
if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" {
// These features are available on AMD arch CPUs:
enable(extended_proc_info_ecx, 6, Feature::sse4a);
enable(extended_proc_info_ecx, 21, Feature::tbm);
enable(extended_proc_info_ecx, 11, Feature::xop);
}
// `XSAVE` and `AVX` support:
let cpu_xsave = bit::test(proc_info_ecx as usize, 26);
if cpu_xsave {
// 0. Here the CPU supports `XSAVE`.
// 1. Detect `OSXSAVE`, that is, whether the OS is AVX enabled and
// supports saving the state of the AVX/AVX2 vector registers on
// context-switches, see:
//
// - [intel: is avx enabled?][is_avx_enabled],
// - [mozilla: sse.cpp][mozilla_sse_cpp].
//
// [is_avx_enabled]: https://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
// [mozilla_sse_cpp]: https://hg.mozilla.org/mozilla-central/file/64bab5cbb9b6/mozglue/build/SSE.cpp#l190
let cpu_osxsave = bit::test(proc_info_ecx as usize, 27);
if cpu_osxsave {
// 2. The OS must have signaled the CPU that it supports saving and
// restoring the:
//
// * SSE -> `XCR0.SSE[1]`
// * AVX -> `XCR0.AVX[2]`
// * AVX-512 -> `XCR0.AVX-512[7:5]`.
// * AMX -> `XCR0.AMX[18:17]`
// * APX -> `XCR0.APX[19]`
//
// by setting the corresponding bits of `XCR0` to `1`.
//
// This is safe because the CPU supports `xsave`
// and the OS has set `osxsave`.
let xcr0 = unsafe { _xgetbv(0) };
// Test `XCR0.SSE[1]` and `XCR0.AVX[2]` with the mask `0b110 == 6`:
let os_avx_support = xcr0 & 6 == 6;
// Test `XCR0.AVX-512[7:5]` with the mask `0b1110_0000 == 0xe0`:
let os_avx512_support = xcr0 & 0xe0 == 0xe0;
// Test `XCR0.AMX[18:17]` with the mask `0b110_0000_0000_0000_0000 == 0x60000`
let os_amx_support = xcr0 & 0x60000 == 0x60000;
// Test `XCR0.APX[19]` with the mask `0b1000_0000_0000_0000_0000 == 0x80000`
let os_apx_support = xcr0 & 0x80000 == 0x80000;
// Only if the OS and the CPU support saving/restoring the AVX
// registers we enable `xsave` support:
if os_avx_support {
// See "13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED
// FEATURES" in the "Intel® 64 and IA-32 Architectures Software
// Developers 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
}

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;

View File

@@ -0,0 +1,335 @@
#![allow(internal_features, unused_features)]
#![feature(stdarch_internal)]
#![cfg_attr(target_arch = "arm", feature(stdarch_arm_feature_detection))]
#![cfg_attr(
any(target_arch = "aarch64", target_arch = "arm64ec"),
feature(stdarch_aarch64_feature_detection)
)]
#![cfg_attr(
any(target_arch = "riscv32", target_arch = "riscv64"),
feature(stdarch_riscv_feature_detection)
)]
#![cfg_attr(target_arch = "powerpc", feature(stdarch_powerpc_feature_detection))]
#![cfg_attr(target_arch = "powerpc64", feature(stdarch_powerpc_feature_detection))]
#![allow(clippy::unwrap_used, clippy::use_debug, clippy::print_stdout)]
#[cfg_attr(
any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
),
macro_use
)]
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
extern crate std_detect;
#[test]
fn all() {
for (f, e) in std_detect::detect::features() {
println!("{f}: {e}");
}
}
#[test]
#[cfg(all(target_arch = "arm", target_os = "freebsd"))]
fn arm_freebsd() {
println!("neon: {}", is_arm_feature_detected!("neon"));
println!("pmull: {}", is_arm_feature_detected!("pmull"));
println!("crc: {}", is_arm_feature_detected!("crc"));
println!("aes: {}", is_arm_feature_detected!("aes"));
println!("sha2: {}", is_arm_feature_detected!("sha2"));
}
#[test]
#[cfg(all(target_arch = "arm", any(target_os = "linux", target_os = "android")))]
fn arm_linux() {
println!("neon: {}", is_arm_feature_detected!("neon"));
println!("pmull: {}", is_arm_feature_detected!("pmull"));
println!("crc: {}", is_arm_feature_detected!("crc"));
println!("aes: {}", is_arm_feature_detected!("aes"));
println!("sha2: {}", is_arm_feature_detected!("sha2"));
println!("dotprod: {}", is_arm_feature_detected!("dotprod"));
println!("i8mm: {}", is_arm_feature_detected!("i8mm"));
}
#[test]
#[cfg(all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")))]
fn aarch64_linux() {
println!("asimd: {}", is_aarch64_feature_detected!("asimd"));
println!("neon: {}", is_aarch64_feature_detected!("neon"));
println!("pmull: {}", is_aarch64_feature_detected!("pmull"));
println!("fp: {}", is_aarch64_feature_detected!("fp"));
println!("fp16: {}", is_aarch64_feature_detected!("fp16"));
println!("sve: {}", is_aarch64_feature_detected!("sve"));
println!("crc: {}", is_aarch64_feature_detected!("crc"));
println!("lse: {}", is_aarch64_feature_detected!("lse"));
println!("lse2: {}", is_aarch64_feature_detected!("lse2"));
println!("lse128: {}", is_aarch64_feature_detected!("lse128"));
println!("rdm: {}", is_aarch64_feature_detected!("rdm"));
println!("rcpc: {}", is_aarch64_feature_detected!("rcpc"));
println!("rcpc2: {}", is_aarch64_feature_detected!("rcpc2"));
println!("rcpc3: {}", is_aarch64_feature_detected!("rcpc3"));
println!("dotprod: {}", is_aarch64_feature_detected!("dotprod"));
println!("fhm: {}", is_aarch64_feature_detected!("fhm"));
println!("dit: {}", is_aarch64_feature_detected!("dit"));
println!("flagm: {}", is_aarch64_feature_detected!("flagm"));
println!("flagm2: {}", is_aarch64_feature_detected!("flagm2"));
println!("ssbs: {}", is_aarch64_feature_detected!("ssbs"));
println!("sb: {}", is_aarch64_feature_detected!("sb"));
println!("paca: {}", is_aarch64_feature_detected!("paca"));
println!("pacg: {}", is_aarch64_feature_detected!("pacg"));
// println!("pauth-lr: {}", is_aarch64_feature_detected!("pauth-lr"));
println!("dpb: {}", is_aarch64_feature_detected!("dpb"));
println!("dpb2: {}", is_aarch64_feature_detected!("dpb2"));
println!("sve-b16b16: {}", is_aarch64_feature_detected!("sve-b16b16"));
println!("sve2: {}", is_aarch64_feature_detected!("sve2"));
println!("sve2p1: {}", is_aarch64_feature_detected!("sve2p1"));
println!("sve2-aes: {}", is_aarch64_feature_detected!("sve2-aes"));
println!("sve2-sm4: {}", is_aarch64_feature_detected!("sve2-sm4"));
println!("sve2-sha3: {}", is_aarch64_feature_detected!("sve2-sha3"));
println!("sve2-bitperm: {}", is_aarch64_feature_detected!("sve2-bitperm"));
println!("frintts: {}", is_aarch64_feature_detected!("frintts"));
println!("i8mm: {}", is_aarch64_feature_detected!("i8mm"));
println!("f32mm: {}", is_aarch64_feature_detected!("f32mm"));
println!("f64mm: {}", is_aarch64_feature_detected!("f64mm"));
println!("bf16: {}", is_aarch64_feature_detected!("bf16"));
println!("rand: {}", is_aarch64_feature_detected!("rand"));
println!("bti: {}", is_aarch64_feature_detected!("bti"));
println!("mte: {}", is_aarch64_feature_detected!("mte"));
println!("jsconv: {}", is_aarch64_feature_detected!("jsconv"));
println!("fcma: {}", is_aarch64_feature_detected!("fcma"));
println!("aes: {}", is_aarch64_feature_detected!("aes"));
println!("sha2: {}", is_aarch64_feature_detected!("sha2"));
println!("sha3: {}", is_aarch64_feature_detected!("sha3"));
println!("sm4: {}", is_aarch64_feature_detected!("sm4"));
println!("hbc: {}", is_aarch64_feature_detected!("hbc"));
println!("mops: {}", is_aarch64_feature_detected!("mops"));
println!("ecv: {}", is_aarch64_feature_detected!("ecv"));
println!("cssc: {}", is_aarch64_feature_detected!("cssc"));
println!("fpmr: {}", is_aarch64_feature_detected!("fpmr"));
println!("lut: {}", is_aarch64_feature_detected!("lut"));
println!("faminmax: {}", is_aarch64_feature_detected!("faminmax"));
println!("fp8: {}", is_aarch64_feature_detected!("fp8"));
println!("fp8fma: {}", is_aarch64_feature_detected!("fp8fma"));
println!("fp8dot4: {}", is_aarch64_feature_detected!("fp8dot4"));
println!("fp8dot2: {}", is_aarch64_feature_detected!("fp8dot2"));
println!("wfxt: {}", is_aarch64_feature_detected!("wfxt"));
println!("sme: {}", is_aarch64_feature_detected!("sme"));
println!("sme-b16b16: {}", is_aarch64_feature_detected!("sme-b16b16"));
println!("sme-i16i64: {}", is_aarch64_feature_detected!("sme-i16i64"));
println!("sme-f64f64: {}", is_aarch64_feature_detected!("sme-f64f64"));
println!("sme-fa64: {}", is_aarch64_feature_detected!("sme-fa64"));
println!("sme2: {}", is_aarch64_feature_detected!("sme2"));
println!("sme2p1: {}", is_aarch64_feature_detected!("sme2p1"));
println!("sme-f16f16: {}", is_aarch64_feature_detected!("sme-f16f16"));
println!("sme-lutv2: {}", is_aarch64_feature_detected!("sme-lutv2"));
println!("sme-f8f16: {}", is_aarch64_feature_detected!("sme-f8f16"));
println!("sme-f8f32: {}", is_aarch64_feature_detected!("sme-f8f32"));
println!("ssve-fp8fma: {}", is_aarch64_feature_detected!("ssve-fp8fma"));
println!("ssve-fp8dot4: {}", is_aarch64_feature_detected!("ssve-fp8dot4"));
println!("ssve-fp8dot2: {}", is_aarch64_feature_detected!("ssve-fp8dot2"));
}
#[test]
#[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_os = "windows"))]
fn aarch64_windows() {
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
println!("jsconv: {:?}", is_aarch64_feature_detected!("jsconv"));
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
println!("aes: {:?}", is_aarch64_feature_detected!("aes"));
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
println!("sha2: {:?}", is_aarch64_feature_detected!("sha2"));
}
#[test]
#[cfg(all(target_arch = "aarch64", any(target_os = "freebsd", target_os = "openbsd")))]
fn aarch64_bsd() {
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
println!("fp16: {:?}", is_aarch64_feature_detected!("fp16"));
println!("sve: {:?}", is_aarch64_feature_detected!("sve"));
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
println!("lse2: {:?}", is_aarch64_feature_detected!("lse2"));
println!("rdm: {:?}", is_aarch64_feature_detected!("rdm"));
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
println!("paca: {:?}", is_aarch64_feature_detected!("paca"));
println!("pacg: {:?}", is_aarch64_feature_detected!("pacg"));
println!("aes: {:?}", is_aarch64_feature_detected!("aes"));
println!("sha2: {:?}", is_aarch64_feature_detected!("sha2"));
}
#[test]
#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
fn aarch64_darwin() {
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
println!("fp16: {:?}", is_aarch64_feature_detected!("fp16"));
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
println!("lse2: {:?}", is_aarch64_feature_detected!("lse2"));
println!("rdm: {:?}", is_aarch64_feature_detected!("rdm"));
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
println!("rcpc2: {:?}", is_aarch64_feature_detected!("rcpc2"));
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
println!("fhm: {:?}", is_aarch64_feature_detected!("fhm"));
println!("flagm: {:?}", is_aarch64_feature_detected!("flagm"));
println!("ssbs: {:?}", is_aarch64_feature_detected!("ssbs"));
println!("sb: {:?}", is_aarch64_feature_detected!("sb"));
println!("paca: {:?}", is_aarch64_feature_detected!("paca"));
println!("dpb: {:?}", is_aarch64_feature_detected!("dpb"));
println!("dpb2: {:?}", is_aarch64_feature_detected!("dpb2"));
println!("frintts: {:?}", is_aarch64_feature_detected!("frintts"));
println!("i8mm: {:?}", is_aarch64_feature_detected!("i8mm"));
println!("bf16: {:?}", is_aarch64_feature_detected!("bf16"));
println!("bti: {:?}", is_aarch64_feature_detected!("bti"));
println!("fcma: {:?}", is_aarch64_feature_detected!("fcma"));
println!("jsconv: {:?}", is_aarch64_feature_detected!("jsconv"));
println!("aes: {:?}", is_aarch64_feature_detected!("aes"));
println!("sha2: {:?}", is_aarch64_feature_detected!("sha2"));
println!("sha3: {:?}", is_aarch64_feature_detected!("sha3"));
}
#[test]
#[cfg(all(
any(target_arch = "riscv32", target_arch = "riscv64"),
any(target_os = "linux", target_os = "android")
))]
fn riscv_linux() {
println!("rv32i: {}", is_riscv_feature_detected!("rv32i"));
println!("rv32e: {}", is_riscv_feature_detected!("rv32e"));
println!("rv64i: {}", is_riscv_feature_detected!("rv64i"));
println!("rv128i: {}", is_riscv_feature_detected!("rv128i"));
println!("unaligned-scalar-mem: {}", is_riscv_feature_detected!("unaligned-scalar-mem"));
println!("unaligned-vector-mem: {}", is_riscv_feature_detected!("unaligned-vector-mem"));
println!("zicsr: {}", is_riscv_feature_detected!("zicsr"));
println!("zicntr: {}", is_riscv_feature_detected!("zicntr"));
println!("zihpm: {}", is_riscv_feature_detected!("zihpm"));
println!("zifencei: {}", is_riscv_feature_detected!("zifencei"));
println!("zihintntl: {}", is_riscv_feature_detected!("zihintntl"));
println!("zihintpause: {}", is_riscv_feature_detected!("zihintpause"));
println!("zimop: {}", is_riscv_feature_detected!("zimop"));
println!("zicbom: {}", is_riscv_feature_detected!("zicbom"));
println!("zicboz: {}", is_riscv_feature_detected!("zicboz"));
println!("zicond: {}", is_riscv_feature_detected!("zicond"));
println!("m: {}", is_riscv_feature_detected!("m"));
println!("a: {}", is_riscv_feature_detected!("a"));
println!("zalrsc: {}", is_riscv_feature_detected!("zalrsc"));
println!("zaamo: {}", is_riscv_feature_detected!("zaamo"));
println!("zawrs: {}", is_riscv_feature_detected!("zawrs"));
println!("zabha: {}", is_riscv_feature_detected!("zabha"));
println!("zacas: {}", is_riscv_feature_detected!("zacas"));
println!("zam: {}", is_riscv_feature_detected!("zam"));
println!("ztso: {}", is_riscv_feature_detected!("ztso"));
println!("f: {}", is_riscv_feature_detected!("f"));
println!("d: {}", is_riscv_feature_detected!("d"));
println!("q: {}", is_riscv_feature_detected!("q"));
println!("zfh: {}", is_riscv_feature_detected!("zfh"));
println!("zfhmin: {}", is_riscv_feature_detected!("zfhmin"));
println!("zfa: {}", is_riscv_feature_detected!("zfa"));
println!("zfbfmin: {}", is_riscv_feature_detected!("zfbfmin"));
println!("zfinx: {}", is_riscv_feature_detected!("zfinx"));
println!("zdinx: {}", is_riscv_feature_detected!("zdinx"));
println!("zhinx: {}", is_riscv_feature_detected!("zhinx"));
println!("zhinxmin: {}", is_riscv_feature_detected!("zhinxmin"));
println!("c: {}", is_riscv_feature_detected!("c"));
println!("zca: {}", is_riscv_feature_detected!("zca"));
println!("zcf: {}", is_riscv_feature_detected!("zcf"));
println!("zcd: {}", is_riscv_feature_detected!("zcd"));
println!("zcb: {}", is_riscv_feature_detected!("zcb"));
println!("zcmop: {}", is_riscv_feature_detected!("zcmop"));
println!("b: {}", is_riscv_feature_detected!("b"));
println!("zba: {}", is_riscv_feature_detected!("zba"));
println!("zbb: {}", is_riscv_feature_detected!("zbb"));
println!("zbc: {}", is_riscv_feature_detected!("zbc"));
println!("zbs: {}", is_riscv_feature_detected!("zbs"));
println!("zbkb: {}", is_riscv_feature_detected!("zbkb"));
println!("zbkc: {}", is_riscv_feature_detected!("zbkc"));
println!("zbkx: {}", is_riscv_feature_detected!("zbkx"));
println!("zknd: {}", is_riscv_feature_detected!("zknd"));
println!("zkne: {}", is_riscv_feature_detected!("zkne"));
println!("zknh: {}", is_riscv_feature_detected!("zknh"));
println!("zksed: {}", is_riscv_feature_detected!("zksed"));
println!("zksh: {}", is_riscv_feature_detected!("zksh"));
println!("zkr: {}", is_riscv_feature_detected!("zkr"));
println!("zkn: {}", is_riscv_feature_detected!("zkn"));
println!("zks: {}", is_riscv_feature_detected!("zks"));
println!("zk: {}", is_riscv_feature_detected!("zk"));
println!("zkt: {}", is_riscv_feature_detected!("zkt"));
println!("v: {}", is_riscv_feature_detected!("v"));
println!("zve32x: {}", is_riscv_feature_detected!("zve32x"));
println!("zve32f: {}", is_riscv_feature_detected!("zve32f"));
println!("zve64x: {}", is_riscv_feature_detected!("zve64x"));
println!("zve64f: {}", is_riscv_feature_detected!("zve64f"));
println!("zve64d: {}", is_riscv_feature_detected!("zve64d"));
println!("zvfh: {}", is_riscv_feature_detected!("zvfh"));
println!("zvfhmin: {}", is_riscv_feature_detected!("zvfhmin"));
println!("zvfbfmin: {}", is_riscv_feature_detected!("zvfbfmin"));
println!("zvfbfwma: {}", is_riscv_feature_detected!("zvfbfwma"));
println!("zvbb: {}", is_riscv_feature_detected!("zvbb"));
println!("zvbc: {}", is_riscv_feature_detected!("zvbc"));
println!("zvkb: {}", is_riscv_feature_detected!("zvkb"));
println!("zvkg: {}", is_riscv_feature_detected!("zvkg"));
println!("zvkned: {}", is_riscv_feature_detected!("zvkned"));
println!("zvknha: {}", is_riscv_feature_detected!("zvknha"));
println!("zvknhb: {}", is_riscv_feature_detected!("zvknhb"));
println!("zvksed: {}", is_riscv_feature_detected!("zvksed"));
println!("zvksh: {}", is_riscv_feature_detected!("zvksh"));
println!("zvkn: {}", is_riscv_feature_detected!("zvkn"));
println!("zvknc: {}", is_riscv_feature_detected!("zvknc"));
println!("zvkng: {}", is_riscv_feature_detected!("zvkng"));
println!("zvks: {}", is_riscv_feature_detected!("zvks"));
println!("zvksc: {}", is_riscv_feature_detected!("zvksc"));
println!("zvksg: {}", is_riscv_feature_detected!("zvksg"));
println!("zvkt: {}", is_riscv_feature_detected!("zvkt"));
println!("j: {}", is_riscv_feature_detected!("j"));
println!("p: {}", is_riscv_feature_detected!("p"));
}
#[test]
#[cfg(all(target_arch = "powerpc", target_os = "linux"))]
fn powerpc_linux() {
println!("altivec: {}", is_powerpc_feature_detected!("altivec"));
println!("vsx: {}", is_powerpc_feature_detected!("vsx"));
println!("power8: {}", is_powerpc_feature_detected!("power8"));
}
#[test]
#[cfg(all(
target_arch = "powerpc64",
any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"),
))]
fn powerpc64_linux_or_bsd() {
println!("altivec: {}", is_powerpc64_feature_detected!("altivec"));
println!("vsx: {}", is_powerpc64_feature_detected!("vsx"));
println!("power8: {}", is_powerpc64_feature_detected!("power8"));
println!("power9: {}", is_powerpc64_feature_detected!("power9"));
}
#[test]
#[cfg(all(target_arch = "s390x", target_os = "linux",))]
fn s390x_linux() {
println!("vector: {}", is_s390x_feature_detected!("vector"));
}

View File

@@ -0,0 +1,110 @@
#![allow(internal_features, unused_features)]
#![cfg_attr(
any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "x86",
target_arch = "x86_64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "loongarch32",
target_arch = "loongarch64"
),
feature(stdarch_internal)
)]
#![cfg_attr(target_arch = "arm", feature(stdarch_arm_feature_detection))]
#![cfg_attr(
any(target_arch = "aarch64", target_arch = "arm64ec"),
feature(stdarch_aarch64_feature_detection)
)]
#![cfg_attr(
any(target_arch = "powerpc", target_arch = "powerpc64"),
feature(stdarch_powerpc_feature_detection)
)]
#![cfg_attr(
any(target_arch = "riscv32", target_arch = "riscv64"),
feature(stdarch_riscv_feature_detection)
)]
#![cfg_attr(
any(target_arch = "loongarch32", target_arch = "loongarch64"),
feature(stdarch_loongarch_feature_detection)
)]
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "x86",
target_arch = "x86_64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "loongarch32",
target_arch = "loongarch64"
))]
#[macro_use]
extern crate std_detect;
#[test]
#[cfg(target_arch = "arm")]
fn arm() {
let _ = is_arm_feature_detected!("neon");
let _ = is_arm_feature_detected!("neon",);
}
#[test]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))]
fn aarch64() {
let _ = is_aarch64_feature_detected!("fp");
let _ = is_aarch64_feature_detected!("fp",);
}
#[test]
#[cfg(any(target_arch = "loongarch32", target_arch = "loongarch64"))]
fn loongarch() {
let _ = is_loongarch_feature_detected!("32s");
let _ = is_loongarch_feature_detected!("32s",);
let _ = is_loongarch_feature_detected!("lsx");
let _ = is_loongarch_feature_detected!("lsx",);
}
#[test]
#[cfg(target_arch = "powerpc")]
fn powerpc() {
let _ = is_powerpc_feature_detected!("altivec");
let _ = is_powerpc_feature_detected!("altivec",);
}
#[test]
#[cfg(target_arch = "powerpc64")]
fn powerpc64() {
let _ = is_powerpc64_feature_detected!("altivec");
let _ = is_powerpc64_feature_detected!("altivec",);
}
#[test]
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
fn riscv() {
let _ = is_riscv_feature_detected!("zk");
let _ = is_riscv_feature_detected!("zk",);
}
#[test]
#[cfg(target_arch = "s390x")]
fn s390x() {
let _ = is_s390x_feature_detected!("vector");
let _ = is_s390x_feature_detected!("vector",);
}
#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn x86() {
let _ = is_x86_feature_detected!("sse");
let _ = is_x86_feature_detected!("sse",);
}

View File

@@ -0,0 +1,90 @@
#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#![allow(internal_features)]
#![feature(stdarch_internal, x86_amx_intrinsics, xop_target_feature, movrs_target_feature)]
#[macro_use]
extern crate std_detect;
#[test]
fn dump() {
println!("aes: {:?}", is_x86_feature_detected!("aes"));
println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq"));
println!("rdrand: {:?}", is_x86_feature_detected!("rdrand"));
println!("rdseed: {:?}", is_x86_feature_detected!("rdseed"));
println!("tsc: {:?}", is_x86_feature_detected!("tsc"));
println!("sse: {:?}", is_x86_feature_detected!("sse"));
println!("sse2: {:?}", is_x86_feature_detected!("sse2"));
println!("sse3: {:?}", is_x86_feature_detected!("sse3"));
println!("ssse3: {:?}", is_x86_feature_detected!("ssse3"));
println!("sse4.1: {:?}", is_x86_feature_detected!("sse4.1"));
println!("sse4.2: {:?}", is_x86_feature_detected!("sse4.2"));
println!("sse4a: {:?}", is_x86_feature_detected!("sse4a"));
println!("sha: {:?}", is_x86_feature_detected!("sha"));
println!("f16c: {:?}", is_x86_feature_detected!("f16c"));
println!("avx: {:?}", is_x86_feature_detected!("avx"));
println!("avx2: {:?}", is_x86_feature_detected!("avx2"));
println!("sha512: {:?}", is_x86_feature_detected!("sha512"));
println!("sm3: {:?}", is_x86_feature_detected!("sm3"));
println!("sm4: {:?}", is_x86_feature_detected!("sm4"));
println!("avx512f: {:?}", is_x86_feature_detected!("avx512f"));
println!("avx512cd: {:?}", is_x86_feature_detected!("avx512cd"));
println!("avx512er: {:?}", is_x86_feature_detected!("avx512er"));
println!("avx512pf: {:?}", is_x86_feature_detected!("avx512pf"));
println!("avx512bw: {:?}", is_x86_feature_detected!("avx512bw"));
println!("avx512dq: {:?}", is_x86_feature_detected!("avx512dq"));
println!("avx512vl: {:?}", is_x86_feature_detected!("avx512vl"));
println!("avx512_ifma: {:?}", is_x86_feature_detected!("avx512ifma"));
println!("avx512vbmi {:?}", is_x86_feature_detected!("avx512vbmi"));
println!("avx512_vpopcntdq: {:?}", is_x86_feature_detected!("avx512vpopcntdq"));
println!("avx512vbmi2: {:?}", is_x86_feature_detected!("avx512vbmi2"));
println!("gfni: {:?}", is_x86_feature_detected!("gfni"));
println!("vaes: {:?}", is_x86_feature_detected!("vaes"));
println!("vpclmulqdq: {:?}", is_x86_feature_detected!("vpclmulqdq"));
println!("avx512vnni: {:?}", is_x86_feature_detected!("avx512vnni"));
println!("avx512bitalg: {:?}", is_x86_feature_detected!("avx512bitalg"));
println!("avx512bf16: {:?}", is_x86_feature_detected!("avx512bf16"));
println!("avx512vp2intersect: {:?}", is_x86_feature_detected!("avx512vp2intersect"));
println!("avx512fp16: {:?}", is_x86_feature_detected!("avx512fp16"));
println!("fma: {:?}", is_x86_feature_detected!("fma"));
println!("abm: {:?}", is_x86_feature_detected!("abm"));
println!("bmi: {:?}", is_x86_feature_detected!("bmi1"));
println!("bmi2: {:?}", is_x86_feature_detected!("bmi2"));
println!("tbm: {:?}", is_x86_feature_detected!("tbm"));
println!("popcnt: {:?}", is_x86_feature_detected!("popcnt"));
println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt"));
println!("fxsr: {:?}", is_x86_feature_detected!("fxsr"));
println!("xsave: {:?}", is_x86_feature_detected!("xsave"));
println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt"));
println!("xsaves: {:?}", is_x86_feature_detected!("xsaves"));
println!("xsavec: {:?}", is_x86_feature_detected!("xsavec"));
println!("cmpxchg16b: {:?}", is_x86_feature_detected!("cmpxchg16b"));
println!("adx: {:?}", is_x86_feature_detected!("adx"));
println!("rtm: {:?}", is_x86_feature_detected!("rtm"));
println!("movbe: {:?}", is_x86_feature_detected!("movbe"));
println!("avxvnni: {:?}", is_x86_feature_detected!("avxvnni"));
println!("avxvnniint8: {:?}", is_x86_feature_detected!("avxvnniint8"));
println!("avxneconvert: {:?}", is_x86_feature_detected!("avxneconvert"));
println!("avxifma: {:?}", is_x86_feature_detected!("avxifma"));
println!("avxvnniint16: {:?}", is_x86_feature_detected!("avxvnniint16"));
println!("amx-bf16: {:?}", is_x86_feature_detected!("amx-bf16"));
println!("amx-tile: {:?}", is_x86_feature_detected!("amx-tile"));
println!("amx-int8: {:?}", is_x86_feature_detected!("amx-int8"));
println!("amx-fp16: {:?}", is_x86_feature_detected!("amx-fp16"));
println!("amx-complex: {:?}", is_x86_feature_detected!("amx-complex"));
println!("xop: {:?}", is_x86_feature_detected!("xop"));
println!("kl: {:?}", is_x86_feature_detected!("kl"));
println!("widekl: {:?}", is_x86_feature_detected!("widekl"));
println!("movrs: {:?}", is_x86_feature_detected!("movrs"));
println!("amx-fp8: {:?}", is_x86_feature_detected!("amx-fp8"));
println!("amx-tf32: {:?}", is_x86_feature_detected!("amx-tf32"));
println!("amx-avx512: {:?}", is_x86_feature_detected!("amx-avx512"));
println!("amx-movrs: {:?}", is_x86_feature_detected!("amx-movrs"));
}
#[test]
#[allow(deprecated)]
fn x86_deprecated() {
println!("avx512gfni {:?}", is_x86_feature_detected!("avx512gfni"));
println!("avx512vaes {:?}", is_x86_feature_detected!("avx512vaes"));
println!("avx512vpclmulqdq {:?}", is_x86_feature_detected!("avx512vpclmulqdq"));
}

View File

@@ -27,8 +27,18 @@ cp_std path:
@sed -i -f patches.sed {{ "src" / path }}
setup-std:
@# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs, sync.rs
# @just cp_std "collections/mod.rs"
@mkdir "crates/std_detect" -p
@cp {{ RUST_SRC / "../../std_detect/*" }} {{ "crates/std_detect/" }} -r
# Remove the depency to core and alloc
@sed -i "19d" "crates/std_detect/Cargo.toml"
@sed -i "19d" "crates/std_detect/Cargo.toml"
@mkdir "crates/panic_abort" -p
@cp {{ RUST_SRC / "../../panic_abort/*" }} {{ "crates/panic_abort/" }} -r
# Remove the depency to core and alloc
@sed -i "21d" "crates/panic_abort/Cargo.toml"
@sed -i "15d" "crates/panic_abort/Cargo.toml"
@just cp_std "alloc.rs"
@just cp_std "ascii.rs"
@just cp_std "backtrace.rs"
@@ -48,91 +58,187 @@ setup-std:
# @just cp_std "tests_helpers.rs"
@just cp_std "time.rs"
@just cp_std "os/mod.rs"
@just cp_std "os/raw/mod.rs"
@just cp_std "os/raw/tests.rs"
@# Complete
@just cp_std "backtrace/tests.rs"
@# Complete
@just cp_std "collections/hash/map/tests.rs"
@just cp_std "collections/hash/set/tests.rs"
@just cp_std "collections/hash/map.rs"
@just cp_std "collections/hash/set.rs"
@just cp_std "collections/hash/mod.rs"
@just cp_std "io/error.rs"
@just cp_std "io/error/repr_bitpacked.rs"
@just cp_std "io/error/repr_unpacked.rs"
@just cp_std "io/error/tests.rs"
@just cp_std "io/cursor.rs"
@just cp_std "io/cursor/tests.rs"
@just cp_std "io/prelude.rs"
@just cp_std "io/impls.rs"
@just cp_std "io/impls/tests.rs"
@just cp_std "io/tests.rs"
@just cp_std "io/util.rs"
@just cp_std "io/util/tests.rs"
@just cp_std "io/copy.rs"
@just cp_std "io/copy/tests.rs"
@just cp_std "io/pipe.rs"
@just cp_std "io/pipe/tests.rs"
@just cp_std "io/stdio.rs"
@just cp_std "io/buffered/mod.rs"
@just cp_std "io/buffered/bufreader.rs"
@just cp_std "collections/mod.rs"
@# Complete
@just cp_std "ffi/os_str/tests.rs"
@just cp_std "ffi/c_str.rs"
@just cp_std "ffi/mod.rs"
@just cp_std "ffi/os_str.rs"
@# Complete
@just cp_std "fs/tests.rs"
@# Complete
@just cp_std "hash/mod.rs"
@just cp_std "hash/random.rs"
@# Complete
@just cp_std "io/buffered/bufreader/buffer.rs"
@just cp_std "io/buffered/bufreader.rs"
@just cp_std "io/buffered/bufwriter.rs"
@just cp_std "io/buffered/linewriter.rs"
@just cp_std "io/buffered/linewritershim.rs"
@just cp_std "sync/once.rs"
@just cp_std "sync/once_lock.rs"
@just cp_std "sync/lazy_lock.rs"
@just cp_std "sync/nonpoison.rs"
@just cp_std "sync/nonpoison/condvar.rs"
@just cp_std "sync/nonpoison/mutex.rs"
@just cp_std "sync/nonpoison/rwlock.rs"
@just cp_std "sync/poison.rs"
@just cp_std "sync/poison/condvar.rs"
@just cp_std "sync/poison/mutex.rs"
@just cp_std "sync/poison/rwlock.rs"
@just cp_std "sync/barrier.rs"
@just cp_std "sync/reentrant_lock.rs"
@just cp_std "sync/mpsc.rs"
@just cp_std "sync/mpmc/mod.rs"
@just cp_std "io/buffered/mod.rs"
@just cp_std "io/buffered/tests.rs"
@just cp_std "io/copy/tests.rs"
@just cp_std "io/cursor/tests.rs"
@just cp_std "io/error/repr_bitpacked.rs"
@just cp_std "io/error/repr_unpacked.rs"
@just cp_std "io/error/tests.rs"
@just cp_std "io/impls/tests.rs"
@just cp_std "io/pipe/tests.rs"
@just cp_std "io/stdio/tests.rs"
@just cp_std "io/util/tests.rs"
@just cp_std "io/copy.rs"
@just cp_std "io/cursor.rs"
@just cp_std "io/error.rs"
@just cp_std "io/impls.rs"
@just cp_std "io/mod.rs"
@just cp_std "io/pipe.rs"
@just cp_std "io/prelude.rs"
@just cp_std "io/stdio.rs"
@just cp_std "io/tests.rs"
@just cp_std "io/util.rs"
@# Complete
@just cp_std "net/ip_addr/tests.rs"
@just cp_std "net/socket_addr/tests.rs"
@just cp_std "net/tcp/tests.rs"
@just cp_std "net/udp/tests.rs"
@just cp_std "net/hostname.rs"
@just cp_std "net/ip_addr.rs"
@just cp_std "net/mod.rs"
@just cp_std "net/socket_addr.rs"
@just cp_std "net/tcp.rs"
@just cp_std "net/test.rs"
@just cp_std "net/udp.rs"
@# Complete
@just cp_std "num/mod.rs"
@just cp_std "num/f16.rs"
@just cp_std "num/f32.rs"
@just cp_std "num/f64.rs"
@just cp_std "num/f128.rs"
@# Complete
@just cp_std "os/raw/mod.rs"
@just cp_std "os/raw/tests.rs"
@just cp_std "os/mod.rs"
@# Complete
@just cp_std "prelude/mod.rs"
@just cp_std "prelude/v1.rs"
@# Complete
@just cp_std "process/tests.rs"
@# Complete
@just cp_std "sync/mpmc/array.rs"
@just cp_std "sync/mpmc/context.rs"
@just cp_std "sync/mpmc/counter.rs"
@just cp_std "sync/mpmc/error.rs"
@just cp_std "sync/mpmc/list.rs"
@just cp_std "sync/mpmc/mod.rs"
@just cp_std "sync/mpmc/select.rs"
@just cp_std "sync/mpmc/tests.rs"
@just cp_std "sync/mpmc/utils.rs"
@just cp_std "sync/mpmc/waker.rs"
@just cp_std "sync/mpmc/zero.rs"
@just cp_std "hash/mod.rs"
@just cp_std "hash/random.rs"
@just cp_std "num/mod.rs"
@just cp_std "ffi/c_str.rs"
@just cp_std "ffi/mod.rs"
@just cp_std "ffi/os_str.rs"
@just cp_std "ffi/os_str/tests.rs"
@just cp_std "thread/local.rs"
@just cp_std "thread/thread.rs"
@just cp_std "thread/id.rs"
@just cp_std "thread/main_thread.rs"
@just cp_std "thread/current.rs"
@just cp_std "thread/join_handle.rs"
@just cp_std "thread/functions.rs"
@just cp_std "thread/lifecycle.rs"
@just cp_std "thread/builder.rs"
@just cp_std "thread/scoped.rs"
@just cp_std "thread/spawnhook.rs"
@just cp_std "sys/exit.rs"
@just cp_std "sys/env_consts.rs"
@just cp_std "sys/configure_builtins.rs"
@just cp_std "sys/cmath.rs"
@just cp_std "sys/process/mod.rs"
@just cp_std "sys/process/env.rs"
@just cp_std "sys/process/unsupported.rs"
@just cp_std "sync/nonpoison/condvar.rs"
@just cp_std "sync/nonpoison/mutex.rs"
@just cp_std "sync/nonpoison/rwlock.rs"
@just cp_std "sync/poison/condvar.rs"
@just cp_std "sync/poison/mutex.rs"
@just cp_std "sync/poison/rwlock.rs"
@just cp_std "sync/nonpoison.rs"
@just cp_std "sync/barrier.rs"
@just cp_std "sync/lazy_lock.rs"
@just cp_std "sync/mod.rs"
@just cp_std "sync/mpsc.rs"
@just cp_std "sync/nonpoison.rs"
@just cp_std "sync/once.rs"
@just cp_std "sync/once_lock.rs"
@just cp_std "sync/oneshot.rs"
@just cp_std "sync/poison.rs"
@just cp_std "sync/reentrant_lock.rs"
@# Complete
@just cp_std "sys/alloc/mod.rs"
@just cp_std "sys/args/mod.rs"
@just cp_std "sys/args/unsupported.rs"
@just cp_std "sys/env/mod.rs"
@just cp_std "sys/env/common.rs"
@just cp_std "sys/env/unsupported.rs"
@just cp_std "sys/fd/mod.rs"
@just cp_std "sys/fs/mod.rs"
@just cp_std "sys/fs/common.rs"
@just cp_std "sys/fs/unsupported.rs"
@just cp_std "sys/helpers/mod.rs"
@just cp_std "sys/helpers/small_c_string.rs"
@just cp_std "sys/helpers/tests.rs"
@just cp_std "sys/helpers/wstr.rs"
@just cp_std "sys/io/error/generic.rs"
@just cp_std "sys/io/error/mod.rs"
@just cp_std "sys/io/io_slice/unsupported.rs"
@just cp_std "sys/io/is_terminal/unsupported.rs"
@just cp_std "sys/io/kernel_copy/mod.rs"
@just cp_std "sys/io/mod.rs"
@just cp_std "sys/net/connection/mod.rs"
@just cp_std "sys/net/connection/unsupported.rs"
@just cp_std "sys/net/hostname/mod.rs"
@just cp_std "sys/net/hostname/unsupported.rs"
@just cp_std "sys/net/mod.rs"
@just cp_std "sys/os_str/bytes/tests.rs"
@just cp_std "sys/os_str/bytes.rs"
@just cp_std "sys/os_str/mod.rs"
@just cp_std "sys/pal/mod.rs"
@just cp_std "sys/pal/unsupported/mod.rs"
@just cp_std "sys/pal/unsupported/common.rs"
@just cp_std "sys/pal/unsupported/os.rs"
@just cp_std "sys/path/mod.rs"
@just cp_std "sys/path/unix.rs"
@just cp_std "sys/personality/dwarf/eh.rs"
@just cp_std "sys/personality/dwarf/mod.rs"
@just cp_std "sys/personality/dwarf/tests.rs"
@just cp_std "sys/personality/mod.rs"
@just cp_std "sys/pipe/mod.rs"
@just cp_std "sys/pipe/unsupported.rs"
@just cp_std "sys/platform_version/mod.rs"
@just cp_std "sys/process/mod.rs"
@just cp_std "sys/process/env.rs"
@just cp_std "sys/process/unsupported.rs"
@just cp_std "sys/random/mod.rs"
@just cp_std "sys/random/unsupported.rs"
@just cp_std "sys/stdio/mod.rs"
@just cp_std "sys/stdio/unsupported.rs"
@just cp_std "sys/sync/condvar/mod.rs"
@just cp_std "sys/sync/condvar/no_threads.rs"
@just cp_std "sys/sync/mutex/mod.rs"
@@ -143,42 +249,38 @@ setup-std:
@just cp_std "sys/sync/rwlock/no_threads.rs"
@just cp_std "sys/sync/thread_parking/mod.rs"
@just cp_std "sys/sync/thread_parking/unsupported.rs"
@just cp_std "sys/sync/mod.rs"
@just cp_std "sys/sync/once_box.rs"
@just cp_std "sys/thread/mod.rs"
@just cp_std "sys/thread/unsupported.rs"
@just cp_std "sys/thread_local/mod.rs"
@just cp_std "sys/thread_local/no_threads.rs"
@just cp_std "sys/thread_local/os.rs"
@just cp_std "sys/thread_local/mod.rs"
@just cp_std "sys/time/mod.rs"
@just cp_std "sys/time/unsupported.rs"
@just cp_std "sys/random/mod.rs"
@just cp_std "sys/random/unsupported.rs"
@just cp_std "sys/env/mod.rs"
@just cp_std "sys/env/common.rs"
@just cp_std "sys/env/unsupported.rs"
@just cp_std "sys/os_str/mod.rs"
@just cp_std "sys/os_str/bytes.rs"
@just cp_std "sys/os_str/bytes/tests.rs"
@just cp_std "sys/path/mod.rs"
@just cp_std "sys/path/unix.rs"
@just cp_std "sys/fs/mod.rs"
@just cp_std "sys/fs/common.rs"
@just cp_std "sys/fs/unsupported.rs"
@just cp_std "sys/io/error/generic.rs"
@just cp_std "sys/io/io_slice/unsupported.rs"
@just cp_std "sys/io/is_terminal/unsupported.rs"
@just cp_std "sys/io/kernel_copy/mod.rs"
@just cp_std "sys/io/mod.rs"
@just cp_std "sys/io/error/mod.rs"
@just cp_std "sys/pipe/mod.rs"
@just cp_std "sys/pipe/unsupported.rs"
@just cp_std "sys/stdio/mod.rs"
@just cp_std "sys/stdio/unsupported.rs"
@just cp_std "sys/alloc/mod.rs"
@just cp_std "sys/backtrace.rs"
@just cp_std "sys/fs/mod.rs"
@just cp_std "sys/fs/unsupported.rs"
@just cp_std "sys/helpers/mod.rs"
@just cp_std "sys/helpers/small_c_string.rs"
@just cp_std "sys/helpers/tests.rs"
@just cp_std "sys/helpers/wstr.rs"
@just cp_std "sys/cmath.rs"
@just cp_std "sys/configure_builtins.rs"
@just cp_std "sys/env_consts.rs"
@just cp_std "sys/exit.rs"
@just cp_std "sys/mod.rs"
@# Complete
@just cp_std "thread/builder.rs"
@just cp_std "thread/current.rs"
@just cp_std "thread/functions.rs"
@just cp_std "thread/id.rs"
@just cp_std "thread/join_handle.rs"
@just cp_std "thread/lifecycle.rs"
@just cp_std "thread/local.rs"
@just cp_std "thread/main_thread.rs"
@just cp_std "thread/mod.rs"
@just cp_std "thread/scoped.rs"
@just cp_std "thread/spawnhook.rs"
@just cp_std "thread/tests.rs"
@just cp_std "thread/thread.rs"
@# Copied but edited for the moment

View File

@@ -1,8 +1,6 @@
s|crate::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
s|alloc::ffi|alloc_crate::ffi|g
s|alloc::slice::Join|alloc_crate::slice::Join|g
s|alloc::bstr|alloc_crate::bstr|g
s|alloc::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
s|crate::collections::VecDeque|alloc_crate::collections::VecDeque|g
/crate::sys::os::getpid/c \ todo!()
/\[doc = include_str!/c \ // todo retreive docs

View File

@@ -0,0 +1,2 @@
362d
361a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")]

View File

@@ -0,0 +1,2 @@
234d
233a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")]

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>();
}

View File

@@ -359,7 +359,7 @@ impl<K, V, S> HashMap<K, V, S> {
#[inline]
#[must_use]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
#[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")]
#[rustc_const_unstable(feature = "custom_std", issue = "none")]
pub const fn with_hasher(hash_builder: S) -> HashMap<K, V, S> {
HashMap { base: base::HashMap::with_hasher(hash_builder) }
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ use hashbrown::hash_set as base;
use super::map::map_try_reserve_error;
use crate::alloc::{Allocator, Global};
use crate::borrow::Borrow;
use alloc_crate::collections::TryReserveError;
use crate::collections::TryReserveError;
use crate::fmt;
use crate::hash::{BuildHasher, Hash, RandomState};
use crate::iter::{Chain, FusedIterator};
@@ -231,7 +231,7 @@ impl<T, S> HashSet<T, S> {
#[inline]
#[must_use]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
#[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")]
#[rustc_const_unstable(feature = "custom_std", issue = "none")]
pub const fn with_hasher(hasher: S) -> HashSet<T, S> {
HashSet { base: base::HashSet::with_hasher(hasher) }
}

View File

@@ -0,0 +1,529 @@
use super::HashSet;
use crate::hash::RandomState;
use crate::panic::{AssertUnwindSafe, catch_unwind};
use crate::sync::Arc;
use crate::sync::atomic::{AtomicU32, Ordering};
#[test]
fn test_zero_capacities() {
type HS = HashSet<i32>;
let s = HS::new();
assert_eq!(s.capacity(), 0);
let s = HS::default();
assert_eq!(s.capacity(), 0);
let s = HS::with_hasher(RandomState::new());
assert_eq!(s.capacity(), 0);
let s = HS::with_capacity(0);
assert_eq!(s.capacity(), 0);
let s = HS::with_capacity_and_hasher(0, RandomState::new());
assert_eq!(s.capacity(), 0);
let mut s = HS::new();
s.insert(1);
s.insert(2);
s.remove(&1);
s.remove(&2);
s.shrink_to_fit();
assert_eq!(s.capacity(), 0);
let mut s = HS::new();
s.reserve(0);
assert_eq!(s.capacity(), 0);
}
#[test]
fn test_disjoint() {
let mut xs = HashSet::new();
let mut ys = HashSet::new();
assert!(xs.is_disjoint(&ys));
assert!(ys.is_disjoint(&xs));
assert!(xs.insert(5));
assert!(ys.insert(11));
assert!(xs.is_disjoint(&ys));
assert!(ys.is_disjoint(&xs));
assert!(xs.insert(7));
assert!(xs.insert(19));
assert!(xs.insert(4));
assert!(ys.insert(2));
assert!(ys.insert(-11));
assert!(xs.is_disjoint(&ys));
assert!(ys.is_disjoint(&xs));
assert!(ys.insert(7));
assert!(!xs.is_disjoint(&ys));
assert!(!ys.is_disjoint(&xs));
}
#[test]
fn test_subset_and_superset() {
let mut a = HashSet::new();
assert!(a.insert(0));
assert!(a.insert(5));
assert!(a.insert(11));
assert!(a.insert(7));
let mut b = HashSet::new();
assert!(b.insert(0));
assert!(b.insert(7));
assert!(b.insert(19));
assert!(b.insert(250));
assert!(b.insert(11));
assert!(b.insert(200));
assert!(!a.is_subset(&b));
assert!(!a.is_superset(&b));
assert!(!b.is_subset(&a));
assert!(!b.is_superset(&a));
assert!(b.insert(5));
assert!(a.is_subset(&b));
assert!(!a.is_superset(&b));
assert!(!b.is_subset(&a));
assert!(b.is_superset(&a));
}
#[test]
fn test_iterate() {
let mut a = HashSet::new();
for i in 0..32 {
assert!(a.insert(i));
}
let mut observed: u32 = 0;
for k in &a {
observed |= 1 << *k;
}
assert_eq!(observed, 0xFFFF_FFFF);
}
#[test]
fn test_intersection() {
let mut a = HashSet::new();
let mut b = HashSet::new();
assert!(a.intersection(&b).next().is_none());
assert!(a.insert(11));
assert!(a.insert(1));
assert!(a.insert(3));
assert!(a.insert(77));
assert!(a.insert(103));
assert!(a.insert(5));
assert!(a.insert(-5));
assert!(b.insert(2));
assert!(b.insert(11));
assert!(b.insert(77));
assert!(b.insert(-9));
assert!(b.insert(-42));
assert!(b.insert(5));
assert!(b.insert(3));
let mut i = 0;
let expected = [3, 5, 11, 77];
for x in a.intersection(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
assert!(a.insert(9)); // make a bigger than b
i = 0;
for x in a.intersection(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
i = 0;
for x in b.intersection(&a) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
}
#[test]
fn test_difference() {
let mut a = HashSet::new();
let mut b = HashSet::new();
assert!(a.insert(1));
assert!(a.insert(3));
assert!(a.insert(5));
assert!(a.insert(9));
assert!(a.insert(11));
assert!(b.insert(3));
assert!(b.insert(9));
let mut i = 0;
let expected = [1, 5, 11];
for x in a.difference(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
}
#[test]
fn test_symmetric_difference() {
let mut a = HashSet::new();
let mut b = HashSet::new();
assert!(a.insert(1));
assert!(a.insert(3));
assert!(a.insert(5));
assert!(a.insert(9));
assert!(a.insert(11));
assert!(b.insert(-2));
assert!(b.insert(3));
assert!(b.insert(9));
assert!(b.insert(14));
assert!(b.insert(22));
let mut i = 0;
let expected = [-2, 1, 5, 11, 14, 22];
for x in a.symmetric_difference(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
}
#[test]
fn test_union() {
let mut a = HashSet::new();
let mut b = HashSet::new();
assert!(a.union(&b).next().is_none());
assert!(b.union(&a).next().is_none());
assert!(a.insert(1));
assert!(a.insert(3));
assert!(a.insert(11));
assert!(a.insert(16));
assert!(a.insert(19));
assert!(a.insert(24));
assert!(b.insert(-2));
assert!(b.insert(1));
assert!(b.insert(5));
assert!(b.insert(9));
assert!(b.insert(13));
assert!(b.insert(19));
let mut i = 0;
let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24];
for x in a.union(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
assert!(a.insert(9)); // make a bigger than b
assert!(a.insert(5));
i = 0;
for x in a.union(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
i = 0;
for x in b.union(&a) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
}
#[test]
fn test_from_iter() {
let xs = [1, 2, 2, 3, 4, 5, 6, 7, 8, 9];
let set: HashSet<_> = xs.iter().cloned().collect();
for x in &xs {
assert!(set.contains(x));
}
assert_eq!(set.iter().len(), xs.len() - 1);
}
#[test]
fn test_move_iter() {
let hs = {
let mut hs = HashSet::new();
hs.insert('a');
hs.insert('b');
hs
};
let v = hs.into_iter().collect::<Vec<char>>();
assert!(v == ['a', 'b'] || v == ['b', 'a']);
}
#[test]
fn test_eq() {
// These constants once happened to expose a bug in insert().
// I'm keeping them around to prevent a regression.
let mut s1 = HashSet::new();
s1.insert(1);
s1.insert(2);
s1.insert(3);
let mut s2 = HashSet::new();
s2.insert(1);
s2.insert(2);
assert!(s1 != s2);
s2.insert(3);
assert_eq!(s1, s2);
}
#[test]
fn test_show() {
let mut set = HashSet::new();
let empty = HashSet::<i32>::new();
set.insert(1);
set.insert(2);
let set_str = format!("{set:?}");
assert!(set_str == "{1, 2}" || set_str == "{2, 1}");
assert_eq!(format!("{empty:?}"), "{}");
}
#[test]
fn test_trivial_drain() {
let mut s = HashSet::<i32>::new();
for _ in s.drain() {}
assert!(s.is_empty());
drop(s);
let mut s = HashSet::<i32>::new();
drop(s.drain());
assert!(s.is_empty());
}
#[test]
fn test_drain() {
let mut s: HashSet<_> = (1..100).collect();
// try this a bunch of times to make sure we don't screw up internal state.
for _ in 0..20 {
assert_eq!(s.len(), 99);
{
let mut last_i = 0;
let mut d = s.drain();
for (i, x) in d.by_ref().take(50).enumerate() {
last_i = i;
assert!(x != 0);
}
assert_eq!(last_i, 49);
}
for _ in &s {
panic!("s should be empty!");
}
// reset to try again.
s.extend(1..100);
}
}
#[test]
fn test_replace() {
use crate::hash;
#[derive(Debug)]
struct Foo(&'static str, #[allow(dead_code)] i32);
impl PartialEq for Foo {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for Foo {}
impl hash::Hash for Foo {
fn hash<H: hash::Hasher>(&self, h: &mut H) {
self.0.hash(h);
}
}
let mut s = HashSet::new();
assert_eq!(s.replace(Foo("a", 1)), None);
assert_eq!(s.len(), 1);
assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1)));
assert_eq!(s.len(), 1);
let mut it = s.iter();
assert_eq!(it.next(), Some(&Foo("a", 2)));
assert_eq!(it.next(), None);
}
#[test]
fn test_extend_ref() {
let mut a = HashSet::new();
a.insert(1);
a.extend(&[2, 3, 4]);
assert_eq!(a.len(), 4);
assert!(a.contains(&1));
assert!(a.contains(&2));
assert!(a.contains(&3));
assert!(a.contains(&4));
let mut b = HashSet::new();
b.insert(5);
b.insert(6);
a.extend(&b);
assert_eq!(a.len(), 6);
assert!(a.contains(&1));
assert!(a.contains(&2));
assert!(a.contains(&3));
assert!(a.contains(&4));
assert!(a.contains(&5));
assert!(a.contains(&6));
}
#[test]
fn test_retain() {
let xs = [1, 2, 3, 4, 5, 6];
let mut set: HashSet<i32> = xs.iter().cloned().collect();
set.retain(|&k| k % 2 == 0);
assert_eq!(set.len(), 3);
assert!(set.contains(&2));
assert!(set.contains(&4));
assert!(set.contains(&6));
}
#[test]
fn test_extract_if() {
let mut x: HashSet<_> = [1].iter().copied().collect();
let mut y: HashSet<_> = [1].iter().copied().collect();
x.extract_if(|_| true).for_each(drop);
y.extract_if(|_| false).for_each(drop);
assert_eq!(x.len(), 0);
assert_eq!(y.len(), 1);
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_extract_if_drop_panic_leak() {
static PREDS: AtomicU32 = AtomicU32::new(0);
static DROPS: AtomicU32 = AtomicU32::new(0);
#[derive(PartialEq, Eq, PartialOrd, Hash)]
struct D(i32);
impl Drop for D {
fn drop(&mut self) {
if DROPS.fetch_add(1, Ordering::SeqCst) == 1 {
panic!("panic in `drop`");
}
}
}
let mut set = (0..3).map(|i| D(i)).collect::<HashSet<_>>();
catch_unwind(move || {
set.extract_if(|_| {
PREDS.fetch_add(1, Ordering::SeqCst);
true
})
.for_each(drop)
})
.ok();
assert_eq!(PREDS.load(Ordering::SeqCst), 2);
assert_eq!(DROPS.load(Ordering::SeqCst), 3);
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_extract_if_pred_panic_leak() {
static PREDS: AtomicU32 = AtomicU32::new(0);
static DROPS: AtomicU32 = AtomicU32::new(0);
#[derive(PartialEq, Eq, PartialOrd, Hash)]
struct D;
impl Drop for D {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::SeqCst);
}
}
let mut set: HashSet<_> = (0..3).map(|_| D).collect();
catch_unwind(AssertUnwindSafe(|| {
set.extract_if(|_| match PREDS.fetch_add(1, Ordering::SeqCst) {
0 => true,
_ => panic!(),
})
.for_each(drop)
}))
.ok();
assert_eq!(PREDS.load(Ordering::SeqCst), 1);
assert_eq!(DROPS.load(Ordering::SeqCst), 3);
assert_eq!(set.len(), 0);
}
#[test]
fn from_array() {
let set = HashSet::from([1, 2, 3, 4]);
let unordered_duplicates = HashSet::from([4, 1, 4, 3, 2]);
assert_eq!(set, unordered_duplicates);
// This next line must infer the hasher type parameter.
// If you make a change that causes this line to no longer infer,
// that's a problem!
let _must_not_require_type_annotation = HashSet::from([1, 2]);
}
#[test]
fn const_with_hasher() {
const X: HashSet<(), ()> = HashSet::with_hasher(());
const Y: HashSet<(), ()> = Default::default();
assert_eq!(X.len(), 0);
assert_eq!(Y.len(), 0);
}
#[test]
fn test_insert_does_not_overwrite_the_value() {
let first_value = Arc::new(17);
let second_value = Arc::new(17);
let mut set = HashSet::new();
let inserted = set.insert(first_value.clone());
assert!(inserted);
let inserted = set.insert(second_value);
assert!(!inserted);
assert!(
Arc::ptr_eq(set.iter().next().unwrap(), &first_value),
"Insert must not overwrite the value, so the contained value pointer \
must be the same as first value pointer we inserted"
);
}

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> {
_phantom: PhantomData<(K, V, T)>,
#![stable(feature = "rust1", since = "1.0.0")]
#[stable(feature = "try_reserve", since = "1.57.0")]
pub use alloc_crate::collections::TryReserveError;
#[unstable(
feature = "try_reserve_kind",
reason = "Uncertain how much info should be exposed",
issue = "48043"
)]
pub use alloc_crate::collections::TryReserveErrorKind;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::collections::{BTreeMap, BTreeSet, BinaryHeap};
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::collections::{LinkedList, VecDeque};
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::collections::{binary_heap, btree_map, btree_set};
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::collections::{linked_list, vec_deque};
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(inline)]
pub use self::hash_map::HashMap;
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(inline)]
pub use self::hash_set::HashSet;
#[stable(feature = "rust1", since = "1.0.0")]
// FIXME(#82080) The deprecation here is only theoretical, and does not actually produce a warning.
#[deprecated(note = "moved to `std::ops::Bound`", since = "1.26.0")]
#[doc(hidden)]
pub use crate::ops::Bound;
mod hash;
#[stable(feature = "rust1", since = "1.0.0")]
pub mod hash_map {
//! A hash map implemented with quadratic probing and SIMD lookup.
#[stable(feature = "rust1", since = "1.0.0")]
pub use super::hash::map::*;
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
pub use crate::hash::random::DefaultHasher;
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
pub use crate::hash::random::RandomState;
}
#[stable(feature = "rust1", since = "1.0.0")]
pub mod hash_set {
//! A hash set implemented as a `HashMap` where the value is `()`.
#[stable(feature = "rust1", since = "1.0.0")]
pub use super::hash::set::*;
}
pub use alloc_crate::collections::BTreeMap;
pub use alloc_crate::collections::btree_map;

View File

@@ -6,7 +6,7 @@ mod tests;
use core::clone::CloneToUninit;
use crate::borrow::{Borrow, Cow};
use alloc_crate::collections::TryReserveError;
use crate::collections::TryReserveError;
use crate::hash::{Hash, Hasher};
use crate::ops::{self, Range};
use crate::rc::Rc;

2548
crates/std/src/fs/tests.rs Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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)]
mod tests;
pub mod error;
pub use self::buffered::{BufReader, BufWriter, IntoInnerError, LineWriter};
pub use self::error::{Error, ErrorKind, Result, SimpleMessage, const_error};
pub mod buffered;
pub mod copy;
pub mod cursor;
pub mod impls;
pub mod pipe;
pub mod prelude;
pub mod stdio;
pub mod util;
use crate::mem::{MaybeUninit, take};
use crate::ops::{Deref, DerefMut};
use crate::{cmp, fmt, slice, str, sys};
#[unstable(feature = "read_buf", issue = "78485")]
pub use core::io::{BorrowedBuf, BorrowedCursor};
use core::slice::memchr;
pub use cursor::Cursor;
pub use self::copy::copy;
pub use self::pipe::{PipeReader, PipeWriter};
pub use self::stdio::cleanup;
pub use self::stdio::{
_print, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout,
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
pub use self::buffered::WriterPanicked;
#[unstable(feature = "raw_os_error_ty", issue = "107792")]
pub use self::error::RawOsError;
#[doc(hidden)]
#[unstable(feature = "io_const_error_internals", issue = "none")]
pub use self::error::SimpleMessage;
#[unstable(feature = "io_const_error", issue = "133448")]
pub use self::error::const_error;
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
pub use self::pipe::{PipeReader, PipeWriter, pipe};
#[stable(feature = "is_terminal", since = "1.70.0")]
pub use self::stdio::IsTerminal;
pub(crate) use self::stdio::attempt_print_to_stderr;
#[unstable(feature = "print_internals", issue = "none")]
#[doc(hidden)]
pub use self::stdio::{_eprint, _print};
#[unstable(feature = "internal_output_capture", issue = "none")]
#[doc(no_inline, hidden)]
pub use self::stdio::{set_output_capture, try_set_output_capture};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::{
buffered::{BufReader, BufWriter, IntoInnerError, LineWriter},
copy::copy,
cursor::Cursor,
error::{Error, ErrorKind, Result},
stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout},
util::{Empty, Repeat, Sink, empty, repeat, sink},
};
pub(crate) use stdio::attempt_print_to_stderr;
pub use stdio::try_set_output_capture;
use crate::mem::{MaybeUninit, take};
use crate::ops::{Deref, DerefMut};
use crate::{cmp, fmt, slice, str, sys};
use crate::fs::File;
use io::IoBase;
// pub use io::Read;
// pub use io::Seek;
// pub use io::SeekFrom;
// pub use io::Write;
// pub struct Stdin;
impl IoBase for Stdin {
type Error = ();
}
impl io::Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
unsafe { crate::other_fs::File::from_raw_fd(0).read(buf) }
}
}
// pub fn stdin() -> Stdin {
// Stdin
// }
// Part took from the real std
mod buffered;
pub(crate) mod copy;
mod cursor;
mod error;
mod impls;
mod pipe;
pub mod prelude;
mod stdio;
mod util;
const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
pub(crate) use stdio::cleanup;
struct Guard<'a> {
buf: &'a mut Vec<u8>,
len: usize,
@@ -70,14 +361,30 @@ impl Drop for Guard<'_> {
}
}
// Several `read_to_string` and `read_line` methods in the standard library will
// append data into a `String` buffer, but we need to be pretty careful when
// doing this. The implementation will just call `.as_mut_vec()` and then
// delegate to a byte-oriented reading method, but we must ensure that when
// returning we never leave `buf` in a state such that it contains invalid UTF-8
// in its bounds.
//
// To this end, we use an RAII guard (to protect against panics) which updates
// the length of the string when it is dropped. This guard initially truncates
// the string to the prior length and only after we've validated that the
// new contents are valid UTF-8 do we allow it to set a longer length.
//
// The unsafety in this function is twofold:
//
// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8
// checks.
// 2. We're passing a raw buffer to the function `f`, and it is expected that
// the function only *appends* bytes to the buffer. We'll get undefined
// behavior if existing bytes are overwritten to have non-UTF-8 data.
pub(crate) unsafe fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize>
where
F: FnOnce(&mut Vec<u8>) -> Result<usize>,
{
let mut g = Guard {
len: buf.len(),
buf: unsafe { buf.as_mut_vec() },
};
let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } };
let ret = f(g.buf);
// SAFETY: the caller promises to only append data to `buf`
@@ -109,10 +416,7 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
// Optionally limit the maximum bytes read on each iteration.
// This adds an arbitrary fiddle factor to allow for more data than we expect.
let mut max_read_size = size_hint
.and_then(|s| {
s.checked_add(1024)?
.checked_next_multiple_of(DEFAULT_BUF_SIZE)
})
.and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE))
.unwrap_or(DEFAULT_BUF_SIZE);
let mut initialized = 0; // Extra initialized bytes from previous loop iteration
@@ -253,10 +557,7 @@ pub(crate) fn default_read_vectored<F>(read: F, bufs: &mut [IoSliceMut<'_>]) ->
where
F: FnOnce(&mut [u8]) -> Result<usize>,
{
let buf = bufs
.iter_mut()
.find(|b| !b.is_empty())
.map_or(&mut [][..], |b| &mut **b);
let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b);
read(buf)
}
@@ -264,10 +565,7 @@ pub(crate) fn default_write_vectored<F>(write: F, bufs: &[IoSlice<'_>]) -> Resul
where
F: FnOnce(&[u8]) -> Result<usize>,
{
let buf = bufs
.iter()
.find(|b| !b.is_empty())
.map_or(&[][..], |b| &**b);
let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b);
write(buf)
}
@@ -282,11 +580,7 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [
Err(e) => return Err(e),
}
}
if !buf.is_empty() {
Err(Error::READ_EXACT_EOF)
} else {
Ok(())
}
if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) }
}
pub(crate) fn default_read_buf<F>(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()>
@@ -341,10 +635,7 @@ pub(crate) fn default_write_fmt<W: Write + ?Sized>(
}
}
let mut output = Adapter {
inner: this,
error: Ok(()),
};
let mut output = Adapter { inner: this, error: Ok(()) };
match fmt::write(&mut output, args) {
Ok(()) => Ok(()),
Err(..) => {
@@ -362,6 +653,79 @@ pub(crate) fn default_write_fmt<W: Write + ?Sized>(
}
}
/// The `Read` trait allows for reading bytes from a source.
///
/// Implementors of the `Read` trait are called 'readers'.
///
/// Readers are defined by one required method, [`read()`]. Each call to [`read()`]
/// will attempt to pull bytes from this source into a provided buffer. A
/// number of other methods are implemented in terms of [`read()`], giving
/// implementors a number of ways to read bytes while only needing to implement
/// a single method.
///
/// Readers are intended to be composable with one another. Many implementors
/// throughout [`std::io`] take and provide types which implement the `Read`
/// trait.
///
/// Please note that each call to [`read()`] may involve a system call, and
/// therefore, using something that implements [`BufRead`], such as
/// [`BufReader`], will be more efficient.
///
/// Repeated calls to the reader use the same cursor, so for example
/// calling `read_to_end` twice on a [`File`] will only return the file's
/// contents once. It's recommended to first call `rewind()` in that case.
///
/// # Examples
///
/// [`File`]s implement `Read`:
///
/// ```no_run
/// use std::io;
/// use std::io::prelude::*;
/// use std::fs::File;
///
/// fn main() -> io::Result<()> {
/// let mut f = File::open("foo.txt")?;
/// let mut buffer = [0; 10];
///
/// // read up to 10 bytes
/// f.read(&mut buffer)?;
///
/// let mut buffer = Vec::new();
/// // read the whole file
/// f.read_to_end(&mut buffer)?;
///
/// // read into a String, so that you don't need to do the conversion.
/// let mut buffer = String::new();
/// f.read_to_string(&mut buffer)?;
///
/// // and more! See the other methods for more details.
/// Ok(())
/// }
/// ```
///
/// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`:
///
/// ```no_run
/// # use std::io;
/// use std::io::prelude::*;
///
/// fn main() -> io::Result<()> {
/// let mut b = "This string will be read".as_bytes();
/// let mut buffer = [0; 10];
///
/// // read up to 10 bytes
/// b.read(&mut buffer)?;
///
/// // etc... it works exactly as a File does!
/// Ok(())
/// }
/// ```
///
/// [`read()`]: Read::read
/// [`&str`]: prim@str
/// [`std::io`]: self
/// [`File`]: crate::fs::File
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(notable_trait)]
#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")]
@@ -459,8 +823,7 @@ pub trait Read {
/// buffer provided, or an empty one if none exists.
#[stable(feature = "iovec", since = "1.36.0")]
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
// default_read_vectored(|b| self.read(b), bufs)
todo!()
default_read_vectored(|b| self.read(b), bufs)
}
/// Determines if this `Read`er has an efficient `read_vectored`
@@ -570,8 +933,7 @@ pub trait Read {
/// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve
#[stable(feature = "rust1", since = "1.0.0")]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
// default_read_to_end(self, buf, None)
todo!()
default_read_to_end(self, buf, None)
}
/// Reads all bytes until EOF in this source, appending them to `buf`.
@@ -627,8 +989,7 @@ pub trait Read {
/// [`std::fs::read_to_string`]: crate::fs::read_to_string
#[stable(feature = "rust1", since = "1.0.0")]
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
// default_read_to_string(self, buf, None)
todo!()
default_read_to_string(self, buf, None)
}
/// Reads the exact number of bytes required to fill `buf`.
@@ -681,8 +1042,7 @@ pub trait Read {
/// ```
#[stable(feature = "read_exact", since = "1.6.0")]
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
// default_read_exact(self, buf)
todo!()
default_read_exact(self, buf)
}
/// Pull some bytes from this source into the specified buffer.
@@ -695,8 +1055,7 @@ pub trait Read {
/// This method makes it possible to return both data and an error but it is advised against.
#[unstable(feature = "read_buf", issue = "78485")]
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
// default_read_buf(|b| self.read(b), buf)
todo!()
default_read_buf(|b| self.read(b), buf)
}
/// Reads the exact number of bytes required to fill `cursor`.
@@ -719,8 +1078,7 @@ pub trait Read {
/// If this function returns an error, all bytes read will be appended to `cursor`.
#[unstable(feature = "read_buf", issue = "78485")]
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
// default_read_buf_exact(self, cursor)
todo!()
default_read_buf_exact(self, cursor)
}
/// Creates a "by reference" adapter for this instance of `Read`.
@@ -843,11 +1201,7 @@ pub trait Read {
where
Self: Sized,
{
Chain {
first: self,
second: next,
done_first: false,
}
Chain { first: self, second: next, done_first: false }
}
/// Creates an adapter which will read at most `limit` bytes from it.
@@ -886,11 +1240,7 @@ pub trait Read {
where
Self: Sized,
{
Take {
inner: self,
len: limit,
limit,
}
Take { inner: self, len: limit, limit }
}
/// Read and return a fixed array of bytes from this source.
@@ -1944,6 +2294,55 @@ fn skip_until<R: BufRead + ?Sized>(r: &mut R, delim: u8) -> Result<usize> {
}
}
/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it
/// to perform extra ways of reading.
///
/// For example, reading line-by-line is inefficient without using a buffer, so
/// if you want to read by line, you'll need `BufRead`, which includes a
/// [`read_line`] method as well as a [`lines`] iterator.
///
/// # Examples
///
/// A locked standard input implements `BufRead`:
///
/// ```no_run
/// use std::io;
/// use std::io::prelude::*;
///
/// let stdin = io::stdin();
/// for line in stdin.lock().lines() {
/// println!("{}", line?);
/// }
/// # std::io::Result::Ok(())
/// ```
///
/// If you have something that implements [`Read`], you can use the [`BufReader`
/// type][`BufReader`] to turn it into a `BufRead`.
///
/// For example, [`File`] implements [`Read`], but not `BufRead`.
/// [`BufReader`] to the rescue!
///
/// [`File`]: crate::fs::File
/// [`read_line`]: BufRead::read_line
/// [`lines`]: BufRead::lines
///
/// ```no_run
/// use std::io::{self, BufReader};
/// use std::io::prelude::*;
/// use std::fs::File;
///
/// fn main() -> io::Result<()> {
/// let f = File::open("foo.txt")?;
/// let f = BufReader::new(f);
///
/// for line in f.lines() {
/// let line = line?;
/// println!("{line}");
/// }
///
/// Ok(())
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")]
pub trait BufRead: Read {
@@ -2267,10 +2666,7 @@ pub trait BufRead: Read {
where
Self: Sized,
{
Split {
buf: self,
delim: byte,
}
Split { buf: self, delim: byte }
}
/// Returns an iterator over the lines of this reader.
@@ -2310,6 +2706,7 @@ pub trait BufRead: Read {
Lines { buf: self }
}
}
/// Adapter to chain together two readers.
///
/// This struct is generally created by calling [`chain`] on a reader.
@@ -2475,11 +2872,7 @@ impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
}
fn consume(&mut self, amt: usize) {
if !self.done_first {
self.first.consume(amt)
} else {
self.second.consume(amt)
}
if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) }
}
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> {
@@ -2509,15 +2902,13 @@ impl<T, U> SizeHint for Chain<T, U> {
#[inline]
fn upper_bound(&self) -> Option<usize> {
match (
SizeHint::upper_bound(&self.first),
SizeHint::upper_bound(&self.second),
) {
match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) {
(Some(first), Some(second)) => first.checked_add(second),
_ => None,
}
}
}
/// Reader adapter which limits the bytes read from an underlying reader.
///
/// This struct is generally created by calling [`take`] on a reader.
@@ -2800,11 +3191,7 @@ impl<T: Seek> Seek for Take<T> {
self.limit = self.limit.wrapping_sub(offset as u64);
break;
}
let offset = if new_position > self.position() {
i64::MAX
} else {
i64::MIN
};
let offset = if new_position > self.position() { i64::MAX } else { i64::MIN };
self.inner.seek_relative(offset)?;
self.limit = self.limit.wrapping_sub(offset as u64);
}
@@ -2820,11 +3207,7 @@ impl<T: Seek> Seek for Take<T> {
}
fn seek_relative(&mut self, offset: i64) -> Result<()> {
if !self
.position()
.checked_add_signed(offset)
.is_some_and(|p| p <= self.len)
{
if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) {
return Err(ErrorKind::InvalidInput.into());
}
self.inner.seek_relative(offset)?;

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]
);
}

View File

@@ -262,11 +262,14 @@ pub use alloc_crate::string;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::vec;
use io_crate::IoBase;
pub use std_detect::is_x86_feature_detected;
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
pub use core::{
assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args,
format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env,
stringify, trace_macros, unimplemented
stringify, trace_macros, unimplemented,
};
#[rustc_std_internal_symbol]
@@ -274,10 +277,10 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn core::panic::PanicPayload) -
todo!()
}
pub mod error;
pub mod ffi;
pub mod hash;
pub mod io;
pub mod error;
pub mod num;
pub mod path;
pub mod prelude;
@@ -293,6 +296,7 @@ pub mod env;
pub mod fs;
pub mod keyword_docs;
pub mod macros;
pub mod net;
pub mod os;
pub mod panic;
pub mod panicking;
@@ -321,6 +325,8 @@ mod sealed {
pub use shared::fs as other_fs;
pub use shared::syscall;
use crate::io::Stdin;
// #[macro_export]
// macro_rules! print {
// ($($args:expr),*) => {
@@ -338,3 +344,27 @@ pub use shared::syscall;
// // $crate::println!();
// };
// }
impl IoBase for Stdin {
type Error = ();
}
impl io_crate::Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
unsafe { crate::other_fs::File::from_raw_fd(0).read(buf) }
}
}
#[global_allocator]
static GLOBAL_ALLOCATOR: crate::alloc::System = crate::alloc::System;
/// # Safety
/// `argc` and `argv` are passed by the kernel
#[unsafe(no_mangle)]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize {
unsafe extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize;
}
unsafe { main(argc, argv) }
}

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()
}

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};

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
crates/std/src/net/mod.rs Normal file
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,
}

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()
}
}

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());
}

1083
crates/std/src/net/tcp.rs Normal file

File diff suppressed because it is too large Load Diff

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();
}

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
crates/std/src/net/udp.rs Normal file
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)
}
}

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}"),
}
})
}

1086
crates/std/src/num/f128.rs Normal file

File diff suppressed because it is too large Load Diff

1047
crates/std/src/num/f16.rs Normal file

File diff suppressed because it is too large Load Diff

1276
crates/std/src/num/f32.rs Normal file

File diff suppressed because it is too large Load Diff

1276
crates/std/src/num/f64.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -84,7 +84,7 @@
use core::clone::CloneToUninit;
use crate::borrow::{Borrow, Cow};
use alloc_crate::collections::TryReserveError;
use crate::collections::TryReserveError;
use crate::error::Error;
use crate::ffi::{OsStr, OsString, os_str};
use crate::hash::{Hash, Hasher};

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) }
}
}

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;
}

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;

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());
}

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
crates/std/src/sync/mod.rs Normal file
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
}
}

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,
}
}
}

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::*;
}
_ => {}
}

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 backtrace;
pub mod cmath;
pub mod configure_builtins;
pub mod env;
pub mod env_consts;
pub mod exit;
pub mod fd;
pub mod fs;
pub mod io;
pub mod net;
pub mod os_str;
pub mod pal;
pub mod path;
pub mod pipe;
pub mod platform_version;
pub mod process;
pub mod random;
pub mod stdio;
@@ -18,9 +27,10 @@ pub mod sync;
pub mod thread;
pub mod thread_local;
pub mod time;
// FIXME(117276): remove this, move feature implementations into individual
// submodules.
pub use pal::*;
pub mod fs;
pub mod helpers;
/// A trait for viewing representations from std types.
#[cfg_attr(not(target_os = "linux"), allow(unused))]
@@ -43,25 +53,3 @@ pub(crate) trait IntoInner<Inner> {
pub(crate) trait FromInner<Inner> {
fn from_inner(inner: Inner) -> Self;
}
use crate::io as std_io;
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
// SAFETY: must be called only once during runtime cleanup.
// NOTE: this is not guaranteed to run, for example when the program aborts.
pub unsafe fn cleanup() {}
pub fn unsupported<T>() -> std_io::Result<T> {
Err(unsupported_err())
}
pub fn unsupported_err() -> std_io::Error {
std_io::Error::UNSUPPORTED_PLATFORM
}
pub fn abort_internal() -> ! {
core::intrinsics::abort();
}

Some files were not shown because too many files have changed in this diff Show More