Add more from the std
This commit is contained in:
@@ -1,12 +0,0 @@
|
||||
[package]
|
||||
name = "os-std-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2", features = ["full"] }
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "std"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cfg-if = { version = "1.0" }
|
||||
rustc-demangle = { version = "0.1.27" }
|
||||
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_crate = { package = "io", path = "../io", features = ["alloc"] }
|
||||
@@ -1,8 +0,0 @@
|
||||
use std::env;
|
||||
|
||||
pub fn main() {
|
||||
println!(
|
||||
"cargo:rustc-env=STD_ENV_ARCH={}",
|
||||
env::var("CARGO_CFG_TARGET_ARCH").unwrap()
|
||||
);
|
||||
}
|
||||
Submodule crates/std/crates/backtrace-rs deleted from 28ec93b503
@@ -1,19 +0,0 @@
|
||||
[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]
|
||||
@@ -1,51 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
//! 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];
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
[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 = []
|
||||
@@ -1,69 +0,0 @@
|
||||
`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.
|
||||
@@ -1,264 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
#![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 { "" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,380 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
//! 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))
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
#[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)
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
//! 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(),
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
//! Run-time feature detection for Aarch64 on FreeBSD.
|
||||
|
||||
pub(crate) use super::super::aarch64::detect_features;
|
||||
@@ -1,36 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
//! 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,409 +0,0 @@
|
||||
//! 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;
|
||||
@@ -1,70 +0,0 @@
|
||||
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()
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
//! 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;
|
||||
@@ -1,108 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
//! 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
//! 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() })
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
//! 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
//! Other operating systems
|
||||
|
||||
use crate::detect::cache;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
cache::Initializer::default()
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
//! 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;
|
||||
@@ -1,64 +0,0 @@
|
||||
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));
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
//! 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
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
//! x86 run-time feature detection is OS independent.
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
use core::arch::x86::*;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use core::arch::x86_64::*;
|
||||
use core::mem;
|
||||
|
||||
use crate::detect::{Feature, bit, cache};
|
||||
|
||||
/// Run-time feature detection on x86 works by using the CPUID instruction.
|
||||
///
|
||||
/// The [CPUID Wikipedia page][wiki_cpuid] contains
|
||||
/// all the information about which flags to set to query which values, and in
|
||||
/// which registers these are reported.
|
||||
///
|
||||
/// The definitive references are:
|
||||
/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
|
||||
/// Instruction Set Reference, A-Z][intel64_ref].
|
||||
/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
|
||||
/// System Instructions][amd64_ref].
|
||||
///
|
||||
/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
|
||||
/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
|
||||
/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
|
||||
#[allow(clippy::similar_names)]
|
||||
pub(crate) fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
|
||||
if cfg!(target_env = "sgx") {
|
||||
// doesn't support this because it is untrusted data
|
||||
return value;
|
||||
}
|
||||
|
||||
// Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU
|
||||
// has `cpuid` support.
|
||||
|
||||
// 0. EAX = 0: Basic Information:
|
||||
// - EAX returns the "Highest Function Parameter", that is, the maximum
|
||||
// leaf value for subsequent calls of `cpuinfo` in range [0,
|
||||
// 0x8000_0000]. - The vendor ID is stored in 12 u8 ascii chars,
|
||||
// returned in EBX, EDX, and ECX (in that order):
|
||||
let (max_basic_leaf, vendor_id) = {
|
||||
let CpuidResult { eax: max_basic_leaf, ebx, ecx, edx } = __cpuid(0);
|
||||
let vendor_id: [[u8; 4]; 3] = [ebx.to_ne_bytes(), edx.to_ne_bytes(), ecx.to_ne_bytes()];
|
||||
let vendor_id: [u8; 12] = unsafe { mem::transmute(vendor_id) };
|
||||
(max_basic_leaf, vendor_id)
|
||||
};
|
||||
|
||||
if max_basic_leaf < 1 {
|
||||
// Earlier Intel 486, CPUID not implemented
|
||||
return value;
|
||||
}
|
||||
|
||||
// EAX = 1, ECX = 0: Queries "Processor Info and Feature Bits";
|
||||
// Contains information about most x86 features.
|
||||
let CpuidResult { ecx: proc_info_ecx, edx: proc_info_edx, .. } = __cpuid(0x0000_0001_u32);
|
||||
|
||||
// EAX = 7: Queries "Extended Features";
|
||||
// Contains information about bmi,bmi2, and avx2 support.
|
||||
let (
|
||||
extended_features_ebx,
|
||||
extended_features_ecx,
|
||||
extended_features_edx,
|
||||
extended_features_eax_leaf_1,
|
||||
extended_features_edx_leaf_1,
|
||||
) = if max_basic_leaf >= 7 {
|
||||
let CpuidResult { ebx, ecx, edx, .. } = __cpuid(0x0000_0007_u32);
|
||||
let CpuidResult { eax: eax_1, edx: edx_1, .. } =
|
||||
__cpuid_count(0x0000_0007_u32, 0x0000_0001_u32);
|
||||
(ebx, ecx, edx, eax_1, edx_1)
|
||||
} else {
|
||||
(0, 0, 0, 0, 0) // CPUID does not support "Extended Features"
|
||||
};
|
||||
|
||||
// EAX = 0x8000_0000, ECX = 0: Get Highest Extended Function Supported
|
||||
// - EAX returns the max leaf value for extended information, that is,
|
||||
// `cpuid` calls in range [0x8000_0000; u32::MAX]:
|
||||
let CpuidResult { eax: extended_max_basic_leaf, .. } = __cpuid(0x8000_0000_u32);
|
||||
|
||||
// EAX = 0x8000_0001, ECX=0: Queries "Extended Processor Info and Feature
|
||||
// Bits"
|
||||
let extended_proc_info_ecx = if extended_max_basic_leaf >= 1 {
|
||||
let CpuidResult { ecx, .. } = __cpuid(0x8000_0001_u32);
|
||||
ecx
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
{
|
||||
// borrows value till the end of this scope:
|
||||
let mut enable = |r, rb, f| {
|
||||
let present = bit::test(r as usize, rb);
|
||||
if present {
|
||||
value.set(f as u32);
|
||||
}
|
||||
present
|
||||
};
|
||||
|
||||
enable(proc_info_ecx, 0, Feature::sse3);
|
||||
enable(proc_info_ecx, 1, Feature::pclmulqdq);
|
||||
enable(proc_info_ecx, 9, Feature::ssse3);
|
||||
enable(proc_info_ecx, 13, Feature::cmpxchg16b);
|
||||
enable(proc_info_ecx, 19, Feature::sse4_1);
|
||||
enable(proc_info_ecx, 20, Feature::sse4_2);
|
||||
enable(proc_info_ecx, 22, Feature::movbe);
|
||||
enable(proc_info_ecx, 23, Feature::popcnt);
|
||||
enable(proc_info_ecx, 25, Feature::aes);
|
||||
let f16c = enable(proc_info_ecx, 29, Feature::f16c);
|
||||
enable(proc_info_ecx, 30, Feature::rdrand);
|
||||
enable(extended_features_ebx, 18, Feature::rdseed);
|
||||
enable(extended_features_ebx, 19, Feature::adx);
|
||||
enable(extended_features_ebx, 11, Feature::rtm);
|
||||
enable(proc_info_edx, 4, Feature::tsc);
|
||||
enable(proc_info_edx, 23, Feature::mmx);
|
||||
enable(proc_info_edx, 24, Feature::fxsr);
|
||||
enable(proc_info_edx, 25, Feature::sse);
|
||||
enable(proc_info_edx, 26, Feature::sse2);
|
||||
enable(extended_features_ebx, 29, Feature::sha);
|
||||
|
||||
enable(extended_features_ecx, 8, Feature::gfni);
|
||||
enable(extended_features_ecx, 9, Feature::vaes);
|
||||
enable(extended_features_ecx, 10, Feature::vpclmulqdq);
|
||||
|
||||
enable(extended_features_ebx, 3, Feature::bmi1);
|
||||
enable(extended_features_ebx, 8, Feature::bmi2);
|
||||
|
||||
enable(extended_features_ebx, 9, Feature::ermsb);
|
||||
|
||||
enable(extended_features_eax_leaf_1, 31, Feature::movrs);
|
||||
|
||||
// Detect if CPUID.19h available
|
||||
if bit::test(extended_features_ecx as usize, 23) {
|
||||
let CpuidResult { ebx, .. } = __cpuid(0x19);
|
||||
enable(ebx, 0, Feature::kl);
|
||||
enable(ebx, 2, Feature::widekl);
|
||||
}
|
||||
|
||||
// This detects ABM on AMD CPUs and LZCNT on Intel CPUs.
|
||||
// On intel CPUs with popcnt, lzcnt implements the
|
||||
// "missing part" of ABM, so we map both to the same
|
||||
// internal feature.
|
||||
//
|
||||
// The `is_x86_feature_detected!("lzcnt")` macro then
|
||||
// internally maps to Feature::abm.
|
||||
enable(extended_proc_info_ecx, 5, Feature::lzcnt);
|
||||
|
||||
// As Hygon Dhyana originates from AMD technology and shares most of the architecture with
|
||||
// AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series
|
||||
// number(Family 18h).
|
||||
//
|
||||
// For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD
|
||||
// family 17h.
|
||||
//
|
||||
// Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf.
|
||||
// Related Hygon kernel patch can be found on
|
||||
// http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn
|
||||
if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" {
|
||||
// These features are available on AMD arch CPUs:
|
||||
enable(extended_proc_info_ecx, 6, Feature::sse4a);
|
||||
enable(extended_proc_info_ecx, 21, Feature::tbm);
|
||||
enable(extended_proc_info_ecx, 11, Feature::xop);
|
||||
}
|
||||
|
||||
// `XSAVE` and `AVX` support:
|
||||
let cpu_xsave = bit::test(proc_info_ecx as usize, 26);
|
||||
if cpu_xsave {
|
||||
// 0. Here the CPU supports `XSAVE`.
|
||||
|
||||
// 1. Detect `OSXSAVE`, that is, whether the OS is AVX enabled and
|
||||
// supports saving the state of the AVX/AVX2 vector registers on
|
||||
// context-switches, see:
|
||||
//
|
||||
// - [intel: is avx enabled?][is_avx_enabled],
|
||||
// - [mozilla: sse.cpp][mozilla_sse_cpp].
|
||||
//
|
||||
// [is_avx_enabled]: https://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
|
||||
// [mozilla_sse_cpp]: https://hg.mozilla.org/mozilla-central/file/64bab5cbb9b6/mozglue/build/SSE.cpp#l190
|
||||
let cpu_osxsave = bit::test(proc_info_ecx as usize, 27);
|
||||
|
||||
if cpu_osxsave {
|
||||
// 2. The OS must have signaled the CPU that it supports saving and
|
||||
// restoring the:
|
||||
//
|
||||
// * SSE -> `XCR0.SSE[1]`
|
||||
// * AVX -> `XCR0.AVX[2]`
|
||||
// * AVX-512 -> `XCR0.AVX-512[7:5]`.
|
||||
// * AMX -> `XCR0.AMX[18:17]`
|
||||
// * APX -> `XCR0.APX[19]`
|
||||
//
|
||||
// by setting the corresponding bits of `XCR0` to `1`.
|
||||
//
|
||||
// This is safe because the CPU supports `xsave`
|
||||
// and the OS has set `osxsave`.
|
||||
let xcr0 = unsafe { _xgetbv(0) };
|
||||
// Test `XCR0.SSE[1]` and `XCR0.AVX[2]` with the mask `0b110 == 6`:
|
||||
let os_avx_support = xcr0 & 6 == 6;
|
||||
// Test `XCR0.AVX-512[7:5]` with the mask `0b1110_0000 == 0xe0`:
|
||||
let os_avx512_support = xcr0 & 0xe0 == 0xe0;
|
||||
// Test `XCR0.AMX[18:17]` with the mask `0b110_0000_0000_0000_0000 == 0x60000`
|
||||
let os_amx_support = xcr0 & 0x60000 == 0x60000;
|
||||
// Test `XCR0.APX[19]` with the mask `0b1000_0000_0000_0000_0000 == 0x80000`
|
||||
let os_apx_support = xcr0 & 0x80000 == 0x80000;
|
||||
|
||||
// Only if the OS and the CPU support saving/restoring the AVX
|
||||
// registers we enable `xsave` support:
|
||||
if os_avx_support {
|
||||
// See "13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED
|
||||
// FEATURES" in the "Intel® 64 and IA-32 Architectures Software
|
||||
// Developer’s Manual, Volume 1: Basic Architecture":
|
||||
//
|
||||
// "Software enables the XSAVE feature set by setting
|
||||
// CR4.OSXSAVE[bit 18] to 1 (e.g., with the MOV to CR4
|
||||
// instruction). If this bit is 0, execution of any of XGETBV,
|
||||
// XRSTOR, XRSTORS, XSAVE, XSAVEC, XSAVEOPT, XSAVES, and XSETBV
|
||||
// causes an invalid-opcode exception (#UD)"
|
||||
//
|
||||
enable(proc_info_ecx, 26, Feature::xsave);
|
||||
|
||||
// For `xsaveopt`, `xsavec`, and `xsaves` we need to query:
|
||||
// Processor Extended State Enumeration Sub-leaf (EAX = 0DH,
|
||||
// ECX = 1):
|
||||
if max_basic_leaf >= 0xd {
|
||||
let CpuidResult { eax: proc_extended_state1_eax, .. } =
|
||||
__cpuid_count(0xd_u32, 1);
|
||||
enable(proc_extended_state1_eax, 0, Feature::xsaveopt);
|
||||
enable(proc_extended_state1_eax, 1, Feature::xsavec);
|
||||
enable(proc_extended_state1_eax, 3, Feature::xsaves);
|
||||
}
|
||||
|
||||
// FMA (uses 256-bit wide registers):
|
||||
let fma = enable(proc_info_ecx, 12, Feature::fma);
|
||||
|
||||
// And AVX/AVX2:
|
||||
enable(proc_info_ecx, 28, Feature::avx);
|
||||
enable(extended_features_ebx, 5, Feature::avx2);
|
||||
|
||||
// "Short" versions of AVX512 instructions
|
||||
enable(extended_features_eax_leaf_1, 4, Feature::avxvnni);
|
||||
enable(extended_features_eax_leaf_1, 23, Feature::avxifma);
|
||||
enable(extended_features_edx_leaf_1, 4, Feature::avxvnniint8);
|
||||
enable(extended_features_edx_leaf_1, 5, Feature::avxneconvert);
|
||||
enable(extended_features_edx_leaf_1, 10, Feature::avxvnniint16);
|
||||
|
||||
enable(extended_features_eax_leaf_1, 0, Feature::sha512);
|
||||
enable(extended_features_eax_leaf_1, 1, Feature::sm3);
|
||||
enable(extended_features_eax_leaf_1, 2, Feature::sm4);
|
||||
|
||||
// For AVX-512 the OS also needs to support saving/restoring
|
||||
// the extended state, only then we enable AVX-512 support:
|
||||
// Also, Rust makes `avx512f` imply `fma` and `f16c`, because
|
||||
// otherwise the assembler is broken. But Intel doesn't guarantee
|
||||
// that `fma` and `f16c` are available with `avx512f`, so we
|
||||
// need to check for them separately.
|
||||
if os_avx512_support && f16c && fma {
|
||||
enable(extended_features_ebx, 16, Feature::avx512f);
|
||||
enable(extended_features_ebx, 17, Feature::avx512dq);
|
||||
enable(extended_features_ebx, 21, Feature::avx512ifma);
|
||||
enable(extended_features_ebx, 26, Feature::avx512pf);
|
||||
enable(extended_features_ebx, 27, Feature::avx512er);
|
||||
enable(extended_features_ebx, 28, Feature::avx512cd);
|
||||
enable(extended_features_ebx, 30, Feature::avx512bw);
|
||||
enable(extended_features_ebx, 31, Feature::avx512vl);
|
||||
enable(extended_features_ecx, 1, Feature::avx512vbmi);
|
||||
enable(extended_features_ecx, 6, Feature::avx512vbmi2);
|
||||
enable(extended_features_ecx, 11, Feature::avx512vnni);
|
||||
enable(extended_features_ecx, 12, Feature::avx512bitalg);
|
||||
enable(extended_features_ecx, 14, Feature::avx512vpopcntdq);
|
||||
enable(extended_features_edx, 8, Feature::avx512vp2intersect);
|
||||
enable(extended_features_edx, 23, Feature::avx512fp16);
|
||||
enable(extended_features_eax_leaf_1, 5, Feature::avx512bf16);
|
||||
}
|
||||
}
|
||||
|
||||
if os_amx_support {
|
||||
enable(extended_features_edx, 24, Feature::amx_tile);
|
||||
enable(extended_features_edx, 25, Feature::amx_int8);
|
||||
enable(extended_features_edx, 22, Feature::amx_bf16);
|
||||
enable(extended_features_eax_leaf_1, 21, Feature::amx_fp16);
|
||||
enable(extended_features_edx_leaf_1, 8, Feature::amx_complex);
|
||||
|
||||
if max_basic_leaf >= 0x1e {
|
||||
let CpuidResult { eax: amx_feature_flags_eax, .. } =
|
||||
__cpuid_count(0x1e_u32, 1);
|
||||
|
||||
enable(amx_feature_flags_eax, 4, Feature::amx_fp8);
|
||||
enable(amx_feature_flags_eax, 6, Feature::amx_tf32);
|
||||
enable(amx_feature_flags_eax, 7, Feature::amx_avx512);
|
||||
enable(amx_feature_flags_eax, 8, Feature::amx_movrs);
|
||||
}
|
||||
}
|
||||
|
||||
if os_apx_support {
|
||||
enable(extended_features_edx_leaf_1, 21, Feature::apxf);
|
||||
}
|
||||
|
||||
let avx10_1 = enable(extended_features_edx_leaf_1, 19, Feature::avx10_1);
|
||||
if avx10_1 {
|
||||
let CpuidResult { ebx, .. } = __cpuid(0x24);
|
||||
let avx10_version = ebx & 0xff;
|
||||
if avx10_version >= 2 {
|
||||
value.set(Feature::avx10_2 as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately, some Skylake chips erroneously report support for BMI1 and
|
||||
// BMI2 without actual support. These chips don't support AVX, and it seems
|
||||
// that all Intel chips with non-erroneous support BMI do (I didn't check
|
||||
// other vendors), so we can disable these flags for chips that don't also
|
||||
// report support for AVX.
|
||||
//
|
||||
// It's possible this will pessimize future chips that do support BMI and
|
||||
// not AVX, but this seems minor compared to a hard crash you get when
|
||||
// executing an unsupported instruction (to put it another way, it's safe
|
||||
// for us to under-report CPU features, but not to over-report them). Still,
|
||||
// to limit any impact this may have in the future, we only do this for
|
||||
// Intel chips, as it's a bug only present in their chips.
|
||||
//
|
||||
// This bug is documented as `SKL052` in the errata section of this document:
|
||||
// http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/desktop-6th-gen-core-family-spec-update.pdf
|
||||
if vendor_id == *b"GenuineIntel" && !value.test(Feature::avx as u32) {
|
||||
value.unset(Feature::bmi1 as u32);
|
||||
value.unset(Feature::bmi2 as u32);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,35 +0,0 @@
|
||||
//! 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;
|
||||
@@ -1,335 +0,0 @@
|
||||
#![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"));
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
#![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",);
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
#![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"));
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
SRC_DIR := "src"
|
||||
PATCH_DIR := "patches"
|
||||
RUST_SRC := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src"
|
||||
|
||||
patch-std:
|
||||
@echo "Start patching the std..."
|
||||
@find {{ PATCH_DIR }} -type f | while read -r patch_file; do \
|
||||
relative_path="${patch_file#{{ PATCH_DIR }}/}"; \
|
||||
target_file="${relative_path%.sed}.rs"; \
|
||||
if [ -f "{{ SRC_DIR }}/$target_file" ]; then \
|
||||
echo " [SED] $target_file"; \
|
||||
sed -i -f "$patch_file" "{{ SRC_DIR }}/$target_file"; \
|
||||
else \
|
||||
echo "⚠ [WARN] target doesn't exist: $target_file"; \
|
||||
fi; \
|
||||
done
|
||||
@echo "✅ Patching done."
|
||||
|
||||
update-std:
|
||||
@just setup-std
|
||||
@just patch-std
|
||||
|
||||
cp_std path:
|
||||
@echo "Copying {{ path }}"
|
||||
@mkdir {{ "src" / parent_directory(path) }} -p
|
||||
@cp {{ RUST_SRC / path }} {{ "src" / path }}
|
||||
@sed -i -f patches.sed {{ "src" / path }}
|
||||
|
||||
setup-std:
|
||||
@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"
|
||||
@just cp_std "bstr.rs"
|
||||
@just cp_std "env.rs"
|
||||
@just cp_std "error.rs"
|
||||
@just cp_std "fs.rs"
|
||||
@just cp_std "keyword_docs.rs"
|
||||
@just cp_std "macros.rs"
|
||||
@just cp_std "panic.rs"
|
||||
@just cp_std "panicking.rs"
|
||||
@just cp_std "pat.rs"
|
||||
@just cp_std "path.rs"
|
||||
@just cp_std "process.rs"
|
||||
@just cp_std "random.rs"
|
||||
@just cp_std "rt.rs"
|
||||
# @just cp_std "tests_helpers.rs"
|
||||
@just cp_std "time.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 "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 "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 "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"
|
||||
@just cp_std "sys/sync/mutex/no_threads.rs"
|
||||
@just cp_std "sys/sync/once/mod.rs"
|
||||
@just cp_std "sys/sync/once/no_threads.rs"
|
||||
@just cp_std "sys/sync/rwlock/mod.rs"
|
||||
@just cp_std "sys/sync/rwlock/no_threads.rs"
|
||||
@just cp_std "sys/sync/thread_parking/mod.rs"
|
||||
@just cp_std "sys/sync/thread_parking/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/time/mod.rs"
|
||||
@just cp_std "sys/time/unsupported.rs"
|
||||
|
||||
@just cp_std "sys/backtrace.rs"
|
||||
@just cp_std "sys/cmath.rs"
|
||||
@just cp_std "sys/configure_builtins.rs"
|
||||
@just cp_std "sys/env_consts.rs"
|
||||
@just cp_std "sys/exit.rs"
|
||||
@just cp_std "sys/mod.rs"
|
||||
|
||||
@# Complete
|
||||
@just cp_std "thread/builder.rs"
|
||||
@just cp_std "thread/current.rs"
|
||||
@just cp_std "thread/functions.rs"
|
||||
@just cp_std "thread/id.rs"
|
||||
@just cp_std "thread/join_handle.rs"
|
||||
@just cp_std "thread/lifecycle.rs"
|
||||
@just cp_std "thread/local.rs"
|
||||
@just cp_std "thread/main_thread.rs"
|
||||
@just cp_std "thread/mod.rs"
|
||||
@just cp_std "thread/scoped.rs"
|
||||
@just cp_std "thread/spawnhook.rs"
|
||||
@just cp_std "thread/tests.rs"
|
||||
@just cp_std "thread/thread.rs"
|
||||
@# Copied but edited for the moment
|
||||
@@ -1,6 +0,0 @@
|
||||
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
|
||||
/\[doc = include_str!/c \ // todo retreive docs
|
||||
@@ -1,2 +0,0 @@
|
||||
362d
|
||||
361a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")]
|
||||
@@ -1,2 +0,0 @@
|
||||
234d
|
||||
233a \ #[rustc_const_unstable(feature = "custom_std", issue = "none")]
|
||||
@@ -1,3 +0,0 @@
|
||||
109a \ target_os = "survos" => { \
|
||||
mod survos; \
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
# 55a \ target_os = "survos" => { \
|
||||
# mod survos; \
|
||||
# pub use survos::*; \
|
||||
# }
|
||||
@@ -1 +0,0 @@
|
||||
45a \ target_os = "survos",
|
||||
@@ -1,2 +0,0 @@
|
||||
108a \target_os = "survos",
|
||||
124a \target_os = "survos",
|
||||
@@ -1,6 +0,0 @@
|
||||
32a \ target_os = "survos",
|
||||
|
||||
129a \ target_os = "survos" => { \
|
||||
// todo \
|
||||
pub(crate) fn enable() {} \
|
||||
}
|
||||
@@ -1,490 +0,0 @@
|
||||
//! Memory allocation APIs.
|
||||
//!
|
||||
//! In a given program, the standard library has one “global” memory allocator
|
||||
//! that is used for example by `Box<T>` and `Vec<T>`.
|
||||
//!
|
||||
//! Currently the default global allocator is unspecified. Libraries, however,
|
||||
//! like `cdylib`s and `staticlib`s are guaranteed to use the [`System`] by
|
||||
//! default.
|
||||
//!
|
||||
//! # The `#[global_allocator]` attribute
|
||||
//!
|
||||
//! This attribute allows configuring the choice of global allocator.
|
||||
//! You can use this to implement a completely custom global allocator
|
||||
//! to route all[^system-alloc] default allocation requests to a custom object.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::alloc::{GlobalAlloc, System, Layout};
|
||||
//!
|
||||
//! struct MyAllocator;
|
||||
//!
|
||||
//! unsafe impl GlobalAlloc for MyAllocator {
|
||||
//! unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
//! unsafe { System.alloc(layout) }
|
||||
//! }
|
||||
//!
|
||||
//! unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
//! unsafe { System.dealloc(ptr, layout) }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! #[global_allocator]
|
||||
//! static GLOBAL: MyAllocator = MyAllocator;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // This `Vec` will allocate memory through `GLOBAL` above
|
||||
//! let mut v = Vec::new();
|
||||
//! v.push(1);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The attribute is used on a `static` item whose type implements the
|
||||
//! [`GlobalAlloc`] trait. This type can be provided by an external library:
|
||||
//!
|
||||
//! ```rust,ignore (demonstrates crates.io usage)
|
||||
//! use jemallocator::Jemalloc;
|
||||
//!
|
||||
//! #[global_allocator]
|
||||
//! static GLOBAL: Jemalloc = Jemalloc;
|
||||
//!
|
||||
//! fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! The `#[global_allocator]` can only be used once in a crate
|
||||
//! or its recursive dependencies.
|
||||
//!
|
||||
//! [^system-alloc]: Note that the Rust standard library internals may still
|
||||
//! directly call [`System`] when necessary (for example for the runtime
|
||||
//! support typically required to implement a global allocator, see [re-entrance] on [`GlobalAlloc`]
|
||||
//! for more details).
|
||||
//!
|
||||
//! [re-entrance]: trait.GlobalAlloc.html#re-entrance
|
||||
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![stable(feature = "alloc_module", since = "1.28.0")]
|
||||
|
||||
use core::ptr::NonNull;
|
||||
use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
|
||||
use core::{hint, mem, ptr};
|
||||
|
||||
#[stable(feature = "alloc_module", since = "1.28.0")]
|
||||
#[doc(inline)]
|
||||
pub use alloc_crate::alloc::*;
|
||||
|
||||
/// The default memory allocator provided by the operating system.
|
||||
///
|
||||
/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
|
||||
/// plus related functions. However, it is not valid to mix use of the backing
|
||||
/// system allocator with `System`, as this implementation may include extra
|
||||
/// work, such as to serve alignment requests greater than the alignment
|
||||
/// provided directly by the backing system allocator.
|
||||
///
|
||||
/// This type implements the [`GlobalAlloc`] trait. Currently the default
|
||||
/// global allocator is unspecified. Libraries, however, like `cdylib`s and
|
||||
/// `staticlib`s are guaranteed to use the [`System`] by default and as such
|
||||
/// work as if they had this definition:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::alloc::System;
|
||||
///
|
||||
/// #[global_allocator]
|
||||
/// static A: System = System;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let a = Box::new(4); // Allocates from the system allocator.
|
||||
/// println!("{a}");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You can also define your own wrapper around `System` if you'd like, such as
|
||||
/// keeping track of the number of all bytes allocated:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::alloc::{System, GlobalAlloc, Layout};
|
||||
/// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
|
||||
///
|
||||
/// struct Counter;
|
||||
///
|
||||
/// static ALLOCATED: AtomicUsize = AtomicUsize::new(0);
|
||||
///
|
||||
/// unsafe impl GlobalAlloc for Counter {
|
||||
/// unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
/// let ret = unsafe { System.alloc(layout) };
|
||||
/// if !ret.is_null() {
|
||||
/// ALLOCATED.fetch_add(layout.size(), Relaxed);
|
||||
/// }
|
||||
/// ret
|
||||
/// }
|
||||
///
|
||||
/// unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
/// unsafe { System.dealloc(ptr, layout); }
|
||||
/// ALLOCATED.fetch_sub(layout.size(), Relaxed);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[global_allocator]
|
||||
/// static A: Counter = Counter;
|
||||
///
|
||||
/// fn main() {
|
||||
/// println!("allocated bytes before main: {}", ALLOCATED.load(Relaxed));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// It can also be used directly to allocate memory independently of whatever
|
||||
/// global allocator has been selected for a Rust program. For example if a Rust
|
||||
/// program opts in to using jemalloc as the global allocator, `System` will
|
||||
/// still allocate memory using `malloc` and `HeapAlloc`.
|
||||
#[stable(feature = "alloc_system_type", since = "1.28.0")]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct System;
|
||||
|
||||
impl System {
|
||||
#[inline]
|
||||
fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocError> {
|
||||
match layout.size() {
|
||||
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling_ptr(), 0)),
|
||||
// SAFETY: `layout` is non-zero in size,
|
||||
size => unsafe {
|
||||
let raw_ptr = if zeroed {
|
||||
GlobalAlloc::alloc_zeroed(self, layout)
|
||||
} else {
|
||||
GlobalAlloc::alloc(self, layout)
|
||||
};
|
||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||
Ok(NonNull::slice_from_raw_parts(ptr, size))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Same as `Allocator::grow`
|
||||
#[inline]
|
||||
unsafe fn grow_impl(
|
||||
&self,
|
||||
ptr: NonNull<u8>,
|
||||
old_layout: Layout,
|
||||
new_layout: Layout,
|
||||
zeroed: bool,
|
||||
) -> Result<NonNull<[u8]>, AllocError> {
|
||||
debug_assert!(
|
||||
new_layout.size() >= old_layout.size(),
|
||||
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
||||
);
|
||||
|
||||
match old_layout.size() {
|
||||
0 => self.alloc_impl(new_layout, zeroed),
|
||||
|
||||
// SAFETY: `new_size` is non-zero as `new_size` is greater than or equal to `old_size`
|
||||
// as required by safety conditions and the `old_size == 0` case was handled in the
|
||||
// previous match arm. Other conditions must be upheld by the caller
|
||||
old_size if old_layout.align() == new_layout.align() => unsafe {
|
||||
let new_size = new_layout.size();
|
||||
|
||||
// `realloc` probably checks for `new_size >= old_layout.size()` or something similar.
|
||||
hint::assert_unchecked(new_size >= old_layout.size());
|
||||
|
||||
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
|
||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||
if zeroed {
|
||||
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
|
||||
}
|
||||
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
|
||||
},
|
||||
|
||||
// SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
|
||||
// both the old and new memory allocation are valid for reads and writes for `old_size`
|
||||
// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
|
||||
// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
|
||||
// for `dealloc` must be upheld by the caller.
|
||||
old_size => unsafe {
|
||||
let new_ptr = self.alloc_impl(new_layout, zeroed)?;
|
||||
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
|
||||
Allocator::deallocate(self, ptr, old_layout);
|
||||
Ok(new_ptr)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The Allocator impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl,
|
||||
// which is in `std::sys::*::alloc`.
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
unsafe impl Allocator for System {
|
||||
#[inline]
|
||||
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||
self.alloc_impl(layout, false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||
self.alloc_impl(layout, true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
||||
if layout.size() != 0 {
|
||||
// SAFETY: `layout` is non-zero in size,
|
||||
// other conditions must be upheld by the caller
|
||||
unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn grow(
|
||||
&self,
|
||||
ptr: NonNull<u8>,
|
||||
old_layout: Layout,
|
||||
new_layout: Layout,
|
||||
) -> Result<NonNull<[u8]>, AllocError> {
|
||||
// SAFETY: all conditions must be upheld by the caller
|
||||
unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn grow_zeroed(
|
||||
&self,
|
||||
ptr: NonNull<u8>,
|
||||
old_layout: Layout,
|
||||
new_layout: Layout,
|
||||
) -> Result<NonNull<[u8]>, AllocError> {
|
||||
// SAFETY: all conditions must be upheld by the caller
|
||||
unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn shrink(
|
||||
&self,
|
||||
ptr: NonNull<u8>,
|
||||
old_layout: Layout,
|
||||
new_layout: Layout,
|
||||
) -> Result<NonNull<[u8]>, AllocError> {
|
||||
debug_assert!(
|
||||
new_layout.size() <= old_layout.size(),
|
||||
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
|
||||
);
|
||||
|
||||
match new_layout.size() {
|
||||
// SAFETY: conditions must be upheld by the caller
|
||||
0 => unsafe {
|
||||
Allocator::deallocate(self, ptr, old_layout);
|
||||
Ok(NonNull::slice_from_raw_parts(new_layout.dangling_ptr(), 0))
|
||||
},
|
||||
|
||||
// SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
|
||||
new_size if old_layout.align() == new_layout.align() => unsafe {
|
||||
// `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
|
||||
hint::assert_unchecked(new_size <= old_layout.size());
|
||||
|
||||
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
|
||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
|
||||
},
|
||||
|
||||
// SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
|
||||
// both the old and new memory allocation are valid for reads and writes for `new_size`
|
||||
// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
|
||||
// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
|
||||
// for `dealloc` must be upheld by the caller.
|
||||
new_size => unsafe {
|
||||
let new_ptr = Allocator::allocate(self, new_layout)?;
|
||||
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
|
||||
Allocator::deallocate(self, ptr, old_layout);
|
||||
Ok(new_ptr)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
|
||||
|
||||
/// Registers a custom allocation error hook, replacing any that was previously registered.
|
||||
///
|
||||
/// The allocation error hook is invoked when an infallible memory allocation fails — that is,
|
||||
/// as a consequence of calling [`handle_alloc_error`] — before the runtime aborts.
|
||||
///
|
||||
/// The allocation error hook is a global resource. [`take_alloc_error_hook`] may be used to
|
||||
/// retrieve a previously registered hook and wrap or discard it.
|
||||
///
|
||||
/// # What the provided `hook` function should expect
|
||||
///
|
||||
/// The hook function is provided with a [`Layout`] struct which contains information
|
||||
/// about the allocation that failed.
|
||||
///
|
||||
/// The hook function may choose to panic or abort; in the event that it returns normally, this
|
||||
/// will cause an immediate abort.
|
||||
///
|
||||
/// Since [`take_alloc_error_hook`] is a safe function that allows retrieving the hook, the hook
|
||||
/// function must be _sound_ to call even if no memory allocations were attempted.
|
||||
///
|
||||
/// # The default hook
|
||||
///
|
||||
/// The default hook, used if [`set_alloc_error_hook`] is never called, prints a message to
|
||||
/// standard error (and then returns, causing the runtime to abort the process).
|
||||
/// Compiler options may cause it to panic instead, and the default behavior may be changed
|
||||
/// to panicking in future versions of Rust.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(alloc_error_hook)]
|
||||
///
|
||||
/// use std::alloc::{Layout, set_alloc_error_hook};
|
||||
///
|
||||
/// fn custom_alloc_error_hook(layout: Layout) {
|
||||
/// panic!("memory allocation of {} bytes failed", layout.size());
|
||||
/// }
|
||||
///
|
||||
/// set_alloc_error_hook(custom_alloc_error_hook);
|
||||
/// ```
|
||||
#[unstable(feature = "alloc_error_hook", issue = "51245")]
|
||||
pub fn set_alloc_error_hook(hook: fn(Layout)) {
|
||||
HOOK.store(hook as *mut (), Ordering::Release);
|
||||
}
|
||||
|
||||
/// Unregisters the current allocation error hook, returning it.
|
||||
///
|
||||
/// *See also the function [`set_alloc_error_hook`].*
|
||||
///
|
||||
/// If no custom hook is registered, the default hook will be returned.
|
||||
#[unstable(feature = "alloc_error_hook", issue = "51245")]
|
||||
pub fn take_alloc_error_hook() -> fn(Layout) {
|
||||
let hook = HOOK.swap(ptr::null_mut(), Ordering::Acquire);
|
||||
if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }
|
||||
}
|
||||
|
||||
#[optimize(size)]
|
||||
fn default_alloc_error_hook(layout: Layout) {
|
||||
if cfg!(panic = "immediate-abort") {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is the default path taken on OOM, and the only path taken on stable with std.
|
||||
// Crucially, it does *not* call any user-defined code, and therefore users do not have to
|
||||
// worry about allocation failure causing reentrancy issues. That makes it different from
|
||||
// the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error
|
||||
// handler that is called when there is no `#[alloc_error_handler]`), which triggers a
|
||||
// regular panic and thus can invoke a user-defined panic hook, executing arbitrary
|
||||
// user-defined code.
|
||||
|
||||
static PREV_ALLOC_FAILURE: AtomicBool = AtomicBool::new(false);
|
||||
if PREV_ALLOC_FAILURE.swap(true, Ordering::Relaxed) {
|
||||
// Don't try to print a backtrace if a previous alloc error happened. This likely means
|
||||
// there is not enough memory to print a backtrace, although it could also mean that two
|
||||
// threads concurrently run out of memory.
|
||||
rtprintpanic!(
|
||||
"memory allocation of {} bytes failed\nskipping backtrace printing to avoid potential recursion\n",
|
||||
layout.size()
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
rtprintpanic!("memory allocation of {} bytes failed\n", layout.size());
|
||||
}
|
||||
|
||||
let Some(mut out) = crate::sys::stdio::panic_output() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Use a lock to prevent mixed output in multithreading context.
|
||||
// Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
|
||||
// Make sure to not take this lock until after checking PREV_ALLOC_FAILURE to avoid deadlocks
|
||||
// when there is too little memory to print a backtrace.
|
||||
let mut lock = crate::sys::backtrace::lock();
|
||||
|
||||
match crate::panic::get_backtrace_style() {
|
||||
Some(crate::panic::BacktraceStyle::Short) => {
|
||||
drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Short))
|
||||
}
|
||||
Some(crate::panic::BacktraceStyle::Full) => {
|
||||
drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Full))
|
||||
}
|
||||
Some(crate::panic::BacktraceStyle::Off) => {
|
||||
use crate::io::Write;
|
||||
let _ = writeln!(
|
||||
out,
|
||||
"note: run with `RUST_BACKTRACE=1` environment variable to display a \
|
||||
backtrace"
|
||||
);
|
||||
if cfg!(miri) {
|
||||
let _ = writeln!(
|
||||
out,
|
||||
"note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \
|
||||
for the environment variable to have an effect"
|
||||
);
|
||||
}
|
||||
}
|
||||
// If backtraces aren't supported or are forced-off, do nothing.
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[doc(hidden)]
|
||||
#[alloc_error_handler]
|
||||
#[unstable(feature = "alloc_internals", issue = "none")]
|
||||
pub fn rust_oom(layout: Layout) -> ! {
|
||||
crate::sys::backtrace::__rust_end_short_backtrace(|| {
|
||||
let hook = HOOK.load(Ordering::Acquire);
|
||||
let hook: fn(Layout) =
|
||||
if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } };
|
||||
hook(layout);
|
||||
crate::process::abort()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[doc(hidden)]
|
||||
#[allow(unused_attributes)]
|
||||
#[unstable(feature = "alloc_internals", issue = "none")]
|
||||
pub mod __default_lib_allocator {
|
||||
use super::{GlobalAlloc, Layout, System};
|
||||
// These magic symbol names are used as a fallback for implementing the
|
||||
// `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is
|
||||
// no `#[global_allocator]` attribute.
|
||||
|
||||
// for symbol names src/librustc_ast/expand/allocator.rs
|
||||
// for signatures src/librustc_allocator/lib.rs
|
||||
|
||||
// linkage directives are provided as part of the current compiler allocator
|
||||
// ABI
|
||||
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe extern "C" fn __rdl_alloc(size: usize, align: usize) -> *mut u8 {
|
||||
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
|
||||
// `GlobalAlloc::alloc`.
|
||||
unsafe {
|
||||
let layout = Layout::from_size_align_unchecked(size, align);
|
||||
System.alloc(layout)
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe extern "C" fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) {
|
||||
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
|
||||
// `GlobalAlloc::dealloc`.
|
||||
unsafe { System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) }
|
||||
}
|
||||
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe extern "C" fn __rdl_realloc(
|
||||
ptr: *mut u8,
|
||||
old_size: usize,
|
||||
align: usize,
|
||||
new_size: usize,
|
||||
) -> *mut u8 {
|
||||
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
|
||||
// `GlobalAlloc::realloc`.
|
||||
unsafe {
|
||||
let old_layout = Layout::from_size_align_unchecked(old_size, align);
|
||||
System.realloc(ptr, old_layout, new_size)
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe extern "C" fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
|
||||
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
|
||||
// `GlobalAlloc::alloc_zeroed`.
|
||||
unsafe {
|
||||
let layout = Layout::from_size_align_unchecked(size, align);
|
||||
System.alloc_zeroed(layout)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
//! Operations on ASCII strings and characters.
|
||||
//!
|
||||
//! Most string operations in Rust act on UTF-8 strings. However, at times it
|
||||
//! makes more sense to only consider the ASCII character set for a specific
|
||||
//! operation.
|
||||
//!
|
||||
//! The [`AsciiExt`] trait provides methods that allow for character
|
||||
//! operations that only act on the ASCII subset and leave non-ASCII characters
|
||||
//! alone.
|
||||
//!
|
||||
//! The [`escape_default`] function provides an iterator over the bytes of an
|
||||
//! escaped version of the character given.
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[unstable(feature = "ascii_char", issue = "110998")]
|
||||
pub use core::ascii::Char;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::ascii::{EscapeDefault, escape_default};
|
||||
|
||||
/// Extension methods for ASCII-subset only operations.
|
||||
///
|
||||
/// Be aware that operations on seemingly non-ASCII characters can sometimes
|
||||
/// have unexpected results. Consider this example:
|
||||
///
|
||||
/// ```
|
||||
/// use std::ascii::AsciiExt;
|
||||
///
|
||||
/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFÉ");
|
||||
/// assert_eq!(AsciiExt::to_ascii_uppercase("café"), "CAFé");
|
||||
/// ```
|
||||
///
|
||||
/// In the first example, the lowercased string is represented `"cafe\u{301}"`
|
||||
/// (the last character is an acute accent [combining character]). Unlike the
|
||||
/// other characters in the string, the combining character will not get mapped
|
||||
/// to an uppercase variant, resulting in `"CAFE\u{301}"`. In the second
|
||||
/// example, the lowercased string is represented `"caf\u{e9}"` (the last
|
||||
/// character is a single Unicode character representing an 'e' with an acute
|
||||
/// accent). Since the last character is defined outside the scope of ASCII,
|
||||
/// it will not get mapped to an uppercase variant, resulting in `"CAF\u{e9}"`.
|
||||
///
|
||||
/// [combining character]: https://en.wikipedia.org/wiki/Combining_character
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[deprecated(since = "1.26.0", note = "use inherent methods instead")]
|
||||
pub trait AsciiExt {
|
||||
/// Container type for copied ASCII characters.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
type Owned;
|
||||
|
||||
/// Checks if the value is within the ASCII range.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This method is deprecated in favor of the identically-named
|
||||
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn is_ascii(&self) -> bool;
|
||||
|
||||
/// Makes a copy of the value in its ASCII upper case equivalent.
|
||||
///
|
||||
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z',
|
||||
/// but non-ASCII letters are unchanged.
|
||||
///
|
||||
/// To uppercase the value in-place, use [`make_ascii_uppercase`].
|
||||
///
|
||||
/// To uppercase ASCII characters in addition to non-ASCII characters, use
|
||||
/// [`str::to_uppercase`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This method is deprecated in favor of the identically-named
|
||||
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
|
||||
///
|
||||
/// [`make_ascii_uppercase`]: AsciiExt::make_ascii_uppercase
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
fn to_ascii_uppercase(&self) -> Self::Owned;
|
||||
|
||||
/// Makes a copy of the value in its ASCII lower case equivalent.
|
||||
///
|
||||
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z',
|
||||
/// but non-ASCII letters are unchanged.
|
||||
///
|
||||
/// To lowercase the value in-place, use [`make_ascii_lowercase`].
|
||||
///
|
||||
/// To lowercase ASCII characters in addition to non-ASCII characters, use
|
||||
/// [`str::to_lowercase`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This method is deprecated in favor of the identically-named
|
||||
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
|
||||
///
|
||||
/// [`make_ascii_lowercase`]: AsciiExt::make_ascii_lowercase
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
fn to_ascii_lowercase(&self) -> Self::Owned;
|
||||
|
||||
/// Checks that two values are an ASCII case-insensitive match.
|
||||
///
|
||||
/// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`,
|
||||
/// but without allocating and copying temporaries.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This method is deprecated in favor of the identically-named
|
||||
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn eq_ignore_ascii_case(&self, other: &Self) -> bool;
|
||||
|
||||
/// Converts this type to its ASCII upper case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z',
|
||||
/// but non-ASCII letters are unchanged.
|
||||
///
|
||||
/// To return a new uppercased value without modifying the existing one, use
|
||||
/// [`to_ascii_uppercase`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This method is deprecated in favor of the identically-named
|
||||
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
|
||||
///
|
||||
/// [`to_ascii_uppercase`]: AsciiExt::to_ascii_uppercase
|
||||
#[stable(feature = "ascii", since = "1.9.0")]
|
||||
fn make_ascii_uppercase(&mut self);
|
||||
|
||||
/// Converts this type to its ASCII lower case equivalent in-place.
|
||||
///
|
||||
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z',
|
||||
/// but non-ASCII letters are unchanged.
|
||||
///
|
||||
/// To return a new lowercased value without modifying the existing one, use
|
||||
/// [`to_ascii_lowercase`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This method is deprecated in favor of the identically-named
|
||||
/// inherent methods on `u8`, `char`, `[u8]` and `str`.
|
||||
///
|
||||
/// [`to_ascii_lowercase`]: AsciiExt::to_ascii_lowercase
|
||||
#[stable(feature = "ascii", since = "1.9.0")]
|
||||
fn make_ascii_lowercase(&mut self);
|
||||
}
|
||||
|
||||
macro_rules! delegating_ascii_methods {
|
||||
() => {
|
||||
#[inline]
|
||||
fn is_ascii(&self) -> bool {
|
||||
self.is_ascii()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_ascii_uppercase(&self) -> Self::Owned {
|
||||
self.to_ascii_uppercase()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_ascii_lowercase(&self) -> Self::Owned {
|
||||
self.to_ascii_lowercase()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn eq_ignore_ascii_case(&self, o: &Self) -> bool {
|
||||
self.eq_ignore_ascii_case(o)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn make_ascii_uppercase(&mut self) {
|
||||
self.make_ascii_uppercase();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn make_ascii_lowercase(&mut self) {
|
||||
self.make_ascii_lowercase();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
impl AsciiExt for u8 {
|
||||
type Owned = u8;
|
||||
|
||||
delegating_ascii_methods!();
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
impl AsciiExt for char {
|
||||
type Owned = char;
|
||||
|
||||
delegating_ascii_methods!();
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
impl AsciiExt for [u8] {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
delegating_ascii_methods!();
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated)]
|
||||
impl AsciiExt for str {
|
||||
type Owned = String;
|
||||
|
||||
delegating_ascii_methods!();
|
||||
}
|
||||
@@ -1,478 +0,0 @@
|
||||
//! Support for capturing a stack backtrace of an OS thread
|
||||
//!
|
||||
//! This module contains the support necessary to capture a stack backtrace of a
|
||||
//! running OS thread from the OS thread itself. The `Backtrace` type supports
|
||||
//! capturing a stack trace via the `Backtrace::capture` and
|
||||
//! `Backtrace::force_capture` functions.
|
||||
//!
|
||||
//! A backtrace is typically quite handy to attach to errors (e.g. types
|
||||
//! implementing `std::error::Error`) to get a causal chain of where an error
|
||||
//! was generated.
|
||||
//!
|
||||
//! ## Accuracy
|
||||
//!
|
||||
//! Backtraces are attempted to be as accurate as possible, but no guarantees
|
||||
//! are provided about the exact accuracy of a backtrace. Instruction pointers,
|
||||
//! symbol names, filenames, line numbers, etc, may all be incorrect when
|
||||
//! reported. Accuracy is attempted on a best-effort basis, however, any bug
|
||||
//! reports are always welcome to indicate areas of improvement!
|
||||
//!
|
||||
//! For most platforms a backtrace with a filename/line number requires that
|
||||
//! programs be compiled with debug information. Without debug information
|
||||
//! filenames/line numbers will not be reported.
|
||||
//!
|
||||
//! ## Platform support
|
||||
//!
|
||||
//! Not all platforms that std compiles for support capturing backtraces. Some
|
||||
//! platforms simply do nothing when capturing a backtrace. To check whether the
|
||||
//! platform supports capturing backtraces you can consult the `BacktraceStatus`
|
||||
//! enum as a result of `Backtrace::status`.
|
||||
//!
|
||||
//! Like above with accuracy platform support is done on a best effort basis.
|
||||
//! Sometimes libraries might not be available at runtime or something may go
|
||||
//! wrong which would cause a backtrace to not be captured. Please feel free to
|
||||
//! report issues with platforms where a backtrace cannot be captured though!
|
||||
//!
|
||||
//! ## Environment Variables
|
||||
//!
|
||||
//! The `Backtrace::capture` function might not actually capture a backtrace by
|
||||
//! default. Its behavior is governed by two environment variables:
|
||||
//!
|
||||
//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture`
|
||||
//! will never capture a backtrace. Any other value set will enable
|
||||
//! `Backtrace::capture`.
|
||||
//!
|
||||
//! * `RUST_BACKTRACE` - if `RUST_LIB_BACKTRACE` is not set, then this variable
|
||||
//! is consulted with the same rules of `RUST_LIB_BACKTRACE`.
|
||||
//!
|
||||
//! * If neither of the above env vars are set, then `Backtrace::capture` will
|
||||
//! be disabled.
|
||||
//!
|
||||
//! Capturing a backtrace can be a quite expensive runtime operation, so the
|
||||
//! environment variables allow either forcibly disabling this runtime
|
||||
//! performance hit or allow selectively enabling it in some programs.
|
||||
//!
|
||||
//! Note that the `Backtrace::force_capture` function can be used to ignore
|
||||
//! these environment variables. Also note that the state of environment
|
||||
//! variables is cached once the first backtrace is created, so altering
|
||||
//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime might not actually change
|
||||
//! how backtraces are captured.
|
||||
|
||||
#![stable(feature = "backtrace", since = "1.65.0")]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// NB: A note on resolution of a backtrace:
|
||||
//
|
||||
// Backtraces primarily happen in two steps, one is where we actually capture
|
||||
// the stack backtrace, giving us a list of instruction pointers corresponding
|
||||
// to stack frames. Next we take these instruction pointers and, one-by-one,
|
||||
// turn them into a human readable name (like `main`).
|
||||
//
|
||||
// The first phase can be somewhat expensive (walking the stack), especially
|
||||
// on MSVC where debug information is consulted to return inline frames each as
|
||||
// their own frame. The second phase, however, is almost always extremely
|
||||
// expensive (on the order of milliseconds sometimes) when it's consulting debug
|
||||
// information.
|
||||
//
|
||||
// We attempt to amortize this cost as much as possible by delaying resolution
|
||||
// of an address to a human readable name for as long as possible. When
|
||||
// `Backtrace::create` is called to capture a backtrace it doesn't actually
|
||||
// perform any symbol resolution, but rather we lazily resolve symbols only just
|
||||
// before they're needed for printing. This way we can make capturing a
|
||||
// backtrace and throwing it away much cheaper, but actually printing a
|
||||
// backtrace is still basically the same cost.
|
||||
//
|
||||
// This strategy comes at the cost of some synchronization required inside of a
|
||||
// `Backtrace`, but that's a relatively small price to pay relative to capturing
|
||||
// a backtrace or actually symbolizing it.
|
||||
|
||||
use crate::backtrace_rs::{self, BytesOrWideString};
|
||||
use crate::ffi::c_void;
|
||||
use crate::panic::UnwindSafe;
|
||||
use crate::sync::LazyLock;
|
||||
use crate::sync::atomic::Ordering::Relaxed;
|
||||
use crate::sync::atomic::{Atomic, AtomicU8};
|
||||
use crate::sys::backtrace::{lock, output_filename, set_image_base};
|
||||
use crate::{env, fmt};
|
||||
|
||||
/// A captured OS thread stack backtrace.
|
||||
///
|
||||
/// This type represents a stack backtrace for an OS thread captured at a
|
||||
/// previous point in time. In some instances the `Backtrace` type may
|
||||
/// internally be empty due to configuration. For more information see
|
||||
/// `Backtrace::capture`.
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
#[must_use]
|
||||
pub struct Backtrace {
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
/// The current status of a backtrace, indicating whether it was captured or
|
||||
/// whether it is empty for some other reason.
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum BacktraceStatus {
|
||||
/// Capturing a backtrace is not supported, likely because it's not
|
||||
/// implemented for the current platform.
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
Unsupported,
|
||||
/// Capturing a backtrace has been disabled through either the
|
||||
/// `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` environment variables.
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
Disabled,
|
||||
/// A backtrace has been captured and the `Backtrace` should print
|
||||
/// reasonable information when rendered.
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
Captured,
|
||||
}
|
||||
|
||||
enum Inner {
|
||||
Unsupported,
|
||||
Disabled,
|
||||
Captured(LazyLock<Capture, LazyResolve>),
|
||||
}
|
||||
|
||||
struct Capture {
|
||||
actual_start: usize,
|
||||
frames: Vec<BacktraceFrame>,
|
||||
}
|
||||
|
||||
fn _assert_send_sync() {
|
||||
fn _assert<T: Send + Sync>() {}
|
||||
_assert::<Backtrace>();
|
||||
}
|
||||
|
||||
/// A single frame of a backtrace.
|
||||
#[unstable(feature = "backtrace_frames", issue = "79676")]
|
||||
pub struct BacktraceFrame {
|
||||
frame: RawFrame,
|
||||
symbols: Vec<BacktraceSymbol>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum RawFrame {
|
||||
Actual(backtrace_rs::Frame),
|
||||
#[cfg(test)]
|
||||
Fake,
|
||||
}
|
||||
|
||||
struct BacktraceSymbol {
|
||||
name: Option<Vec<u8>>,
|
||||
filename: Option<BytesOrWide>,
|
||||
lineno: Option<u32>,
|
||||
colno: Option<u32>,
|
||||
}
|
||||
|
||||
enum BytesOrWide {
|
||||
Bytes(Vec<u8>),
|
||||
Wide(Vec<u16>),
|
||||
}
|
||||
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
impl fmt::Debug for Backtrace {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let capture = match &self.inner {
|
||||
Inner::Unsupported => return fmt.write_str("<unsupported>"),
|
||||
Inner::Disabled => return fmt.write_str("<disabled>"),
|
||||
Inner::Captured(c) => &**c,
|
||||
};
|
||||
|
||||
let frames = &capture.frames[capture.actual_start..];
|
||||
|
||||
write!(fmt, "Backtrace ")?;
|
||||
|
||||
let mut dbg = fmt.debug_list();
|
||||
|
||||
for frame in frames {
|
||||
if frame.frame.ip().is_null() {
|
||||
continue;
|
||||
}
|
||||
|
||||
dbg.entries(&frame.symbols);
|
||||
}
|
||||
|
||||
dbg.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "backtrace_frames", issue = "79676")]
|
||||
impl fmt::Debug for BacktraceFrame {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut dbg = fmt.debug_list();
|
||||
dbg.entries(&self.symbols);
|
||||
dbg.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BacktraceSymbol {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// FIXME: improve formatting: https://github.com/rust-lang/rust/issues/65280
|
||||
// FIXME: Also, include column numbers into the debug format as Display already has them.
|
||||
// Until there are stable per-frame accessors, the format shouldn't be changed:
|
||||
// https://github.com/rust-lang/rust/issues/65280#issuecomment-638966585
|
||||
write!(fmt, "{{ ")?;
|
||||
|
||||
if let Some(fn_name) = self.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)) {
|
||||
write!(fmt, "fn: \"{:#}\"", fn_name)?;
|
||||
} else {
|
||||
write!(fmt, "fn: <unknown>")?;
|
||||
}
|
||||
|
||||
if let Some(fname) = self.filename.as_ref() {
|
||||
write!(fmt, ", file: \"{:?}\"", fname)?;
|
||||
}
|
||||
|
||||
if let Some(line) = self.lineno {
|
||||
write!(fmt, ", line: {:?}", line)?;
|
||||
}
|
||||
|
||||
write!(fmt, " }}")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BytesOrWide {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
output_filename(
|
||||
fmt,
|
||||
match self {
|
||||
BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
|
||||
BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
|
||||
},
|
||||
backtrace_rs::PrintFmt::Short,
|
||||
crate::env::current_dir().as_ref().ok(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Backtrace {
|
||||
/// Returns whether backtrace captures are enabled through environment
|
||||
/// variables.
|
||||
fn enabled() -> bool {
|
||||
// Cache the result of reading the environment variables to make
|
||||
// backtrace captures speedy, because otherwise reading environment
|
||||
// variables every time can be somewhat slow.
|
||||
static ENABLED: Atomic<u8> = AtomicU8::new(0);
|
||||
match ENABLED.load(Relaxed) {
|
||||
0 => {}
|
||||
1 => return false,
|
||||
_ => return true,
|
||||
}
|
||||
let enabled = match env::var("RUST_LIB_BACKTRACE") {
|
||||
Ok(s) => s != "0",
|
||||
Err(_) => match env::var("RUST_BACKTRACE") {
|
||||
Ok(s) => s != "0",
|
||||
Err(_) => false,
|
||||
},
|
||||
};
|
||||
ENABLED.store(enabled as u8 + 1, Relaxed);
|
||||
enabled
|
||||
}
|
||||
|
||||
/// Captures a stack backtrace of the current thread.
|
||||
///
|
||||
/// This function will capture a stack backtrace of the current OS thread of
|
||||
/// execution, returning a `Backtrace` type which can be later used to print
|
||||
/// the entire stack trace or render it to a string.
|
||||
///
|
||||
/// This function will be a noop if the `RUST_BACKTRACE` or
|
||||
/// `RUST_LIB_BACKTRACE` backtrace variables are both not set. If either
|
||||
/// environment variable is set and enabled then this function will actually
|
||||
/// capture a backtrace. Capturing a backtrace can be both memory intensive
|
||||
/// and slow, so these environment variables allow liberally using
|
||||
/// `Backtrace::capture` and only incurring a slowdown when the environment
|
||||
/// variables are set.
|
||||
///
|
||||
/// To forcibly capture a backtrace regardless of environment variables, use
|
||||
/// the `Backtrace::force_capture` function.
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
#[inline(never)] // want to make sure there's a frame here to remove
|
||||
pub fn capture() -> Backtrace {
|
||||
if !Backtrace::enabled() {
|
||||
return Backtrace { inner: Inner::Disabled };
|
||||
}
|
||||
Backtrace::create(Backtrace::capture as fn() -> Backtrace as usize)
|
||||
}
|
||||
|
||||
/// Forcibly captures a full backtrace, regardless of environment variable
|
||||
/// configuration.
|
||||
///
|
||||
/// This function behaves the same as `capture` except that it ignores the
|
||||
/// values of the `RUST_BACKTRACE` and `RUST_LIB_BACKTRACE` environment
|
||||
/// variables, always capturing a backtrace.
|
||||
///
|
||||
/// Note that capturing a backtrace can be an expensive operation on some
|
||||
/// platforms, so this should be used with caution in performance-sensitive
|
||||
/// parts of code.
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
#[inline(never)] // want to make sure there's a frame here to remove
|
||||
pub fn force_capture() -> Backtrace {
|
||||
Backtrace::create(Backtrace::force_capture as fn() -> Backtrace as usize)
|
||||
}
|
||||
|
||||
/// Forcibly captures a disabled backtrace, regardless of environment
|
||||
/// variable configuration.
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
#[rustc_const_stable(feature = "backtrace", since = "1.65.0")]
|
||||
pub const fn disabled() -> Backtrace {
|
||||
Backtrace { inner: Inner::Disabled }
|
||||
}
|
||||
|
||||
// Capture a backtrace which start just before the function addressed by
|
||||
// `ip`
|
||||
fn create(ip: usize) -> Backtrace {
|
||||
let _lock = lock();
|
||||
let mut frames = Vec::new();
|
||||
let mut actual_start = None;
|
||||
set_image_base();
|
||||
unsafe {
|
||||
backtrace_rs::trace_unsynchronized(|frame| {
|
||||
frames.push(BacktraceFrame {
|
||||
frame: RawFrame::Actual(frame.clone()),
|
||||
symbols: Vec::new(),
|
||||
});
|
||||
if frame.symbol_address().addr() == ip && actual_start.is_none() {
|
||||
actual_start = Some(frames.len());
|
||||
}
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
// If no frames came out assume that this is an unsupported platform
|
||||
// since `backtrace` doesn't provide a way of learning this right now,
|
||||
// and this should be a good enough approximation.
|
||||
let inner = if frames.is_empty() {
|
||||
Inner::Unsupported
|
||||
} else {
|
||||
Inner::Captured(LazyLock::new(lazy_resolve(Capture {
|
||||
actual_start: actual_start.unwrap_or(0),
|
||||
frames,
|
||||
})))
|
||||
};
|
||||
|
||||
Backtrace { inner }
|
||||
}
|
||||
|
||||
/// Returns the status of this backtrace, indicating whether this backtrace
|
||||
/// request was unsupported, disabled, or a stack trace was actually
|
||||
/// captured.
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
#[must_use]
|
||||
pub fn status(&self) -> BacktraceStatus {
|
||||
match self.inner {
|
||||
Inner::Unsupported => BacktraceStatus::Unsupported,
|
||||
Inner::Disabled => BacktraceStatus::Disabled,
|
||||
Inner::Captured(_) => BacktraceStatus::Captured,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Backtrace {
|
||||
/// Returns an iterator over the backtrace frames.
|
||||
#[must_use]
|
||||
#[unstable(feature = "backtrace_frames", issue = "79676")]
|
||||
pub fn frames(&'a self) -> &'a [BacktraceFrame] {
|
||||
if let Inner::Captured(c) = &self.inner { &c.frames } else { &[] }
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "backtrace", since = "1.65.0")]
|
||||
impl fmt::Display for Backtrace {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let capture = match &self.inner {
|
||||
Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
|
||||
Inner::Disabled => return fmt.write_str("disabled backtrace"),
|
||||
Inner::Captured(c) => &**c,
|
||||
};
|
||||
|
||||
let full = fmt.alternate();
|
||||
let (frames, style) = if full {
|
||||
(&capture.frames[..], backtrace_rs::PrintFmt::Full)
|
||||
} else {
|
||||
(&capture.frames[capture.actual_start..], backtrace_rs::PrintFmt::Short)
|
||||
};
|
||||
|
||||
// When printing paths we try to strip the cwd if it exists, otherwise
|
||||
// we just print the path as-is. Note that we also only do this for the
|
||||
// short format, because if it's full we presumably want to print
|
||||
// everything.
|
||||
let cwd = crate::env::current_dir();
|
||||
let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
|
||||
output_filename(fmt, path, style, cwd.as_ref().ok())
|
||||
};
|
||||
|
||||
let mut f = backtrace_rs::BacktraceFmt::new(fmt, style, &mut print_path);
|
||||
f.add_context()?;
|
||||
for frame in frames {
|
||||
if frame.symbols.is_empty() {
|
||||
f.frame().print_raw(frame.frame.ip(), None, None, None)?;
|
||||
} else {
|
||||
for symbol in frame.symbols.iter() {
|
||||
f.frame().print_raw_with_column(
|
||||
frame.frame.ip(),
|
||||
symbol.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)),
|
||||
symbol.filename.as_ref().map(|b| match b {
|
||||
BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w),
|
||||
BytesOrWide::Wide(w) => BytesOrWideString::Wide(w),
|
||||
}),
|
||||
symbol.lineno,
|
||||
symbol.colno,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
f.finish()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod helper {
|
||||
use super::*;
|
||||
pub(super) type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe;
|
||||
|
||||
#[define_opaque(LazyResolve)]
|
||||
pub(super) fn lazy_resolve(mut capture: Capture) -> LazyResolve {
|
||||
move || {
|
||||
// Use the global backtrace lock to synchronize this as it's a
|
||||
// requirement of the `backtrace` crate, and then actually resolve
|
||||
// everything.
|
||||
let _lock = lock();
|
||||
for frame in capture.frames.iter_mut() {
|
||||
let symbols = &mut frame.symbols;
|
||||
let frame = match &frame.frame {
|
||||
RawFrame::Actual(frame) => frame,
|
||||
#[cfg(test)]
|
||||
RawFrame::Fake => unimplemented!(),
|
||||
};
|
||||
unsafe {
|
||||
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
|
||||
symbols.push(BacktraceSymbol {
|
||||
name: symbol.name().map(|m| m.as_bytes().to_vec()),
|
||||
filename: symbol.filename_raw().map(|b| match b {
|
||||
BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()),
|
||||
BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()),
|
||||
}),
|
||||
lineno: symbol.lineno(),
|
||||
colno: symbol.colno(),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
capture
|
||||
}
|
||||
}
|
||||
}
|
||||
use helper::*;
|
||||
|
||||
impl RawFrame {
|
||||
fn ip(&self) -> *mut c_void {
|
||||
match self {
|
||||
RawFrame::Actual(frame) => frame.ip(),
|
||||
#[cfg(test)]
|
||||
RawFrame::Fake => crate::ptr::without_provenance_mut(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
use super::*;
|
||||
use crate::panic::RefUnwindSafe;
|
||||
|
||||
fn generate_fake_frames() -> Vec<BacktraceFrame> {
|
||||
vec![
|
||||
BacktraceFrame {
|
||||
frame: RawFrame::Fake,
|
||||
symbols: vec![BacktraceSymbol {
|
||||
name: Some(b"std::backtrace::Backtrace::create".to_vec()),
|
||||
filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())),
|
||||
lineno: Some(100),
|
||||
colno: None,
|
||||
}],
|
||||
},
|
||||
BacktraceFrame {
|
||||
frame: RawFrame::Fake,
|
||||
symbols: vec![BacktraceSymbol {
|
||||
name: Some(b"__rust_maybe_catch_panic".to_vec()),
|
||||
filename: None,
|
||||
lineno: None,
|
||||
colno: None,
|
||||
}],
|
||||
},
|
||||
BacktraceFrame {
|
||||
frame: RawFrame::Fake,
|
||||
symbols: vec![
|
||||
BacktraceSymbol {
|
||||
name: Some(b"std::rt::lang_start_internal".to_vec()),
|
||||
filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
|
||||
lineno: Some(300),
|
||||
colno: Some(5),
|
||||
},
|
||||
BacktraceSymbol {
|
||||
name: Some(b"std::rt::lang_start".to_vec()),
|
||||
filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
|
||||
lineno: Some(400),
|
||||
colno: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let backtrace = Backtrace {
|
||||
inner: Inner::Captured(
|
||||
(Capture { actual_start: 1, frames: generate_fake_frames() }).into(),
|
||||
),
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
let expected = "Backtrace [\
|
||||
\n { fn: \"__rust_maybe_catch_panic\" },\
|
||||
\n { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\
|
||||
\n { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\
|
||||
\n]";
|
||||
|
||||
assert_eq!(format!("{backtrace:#?}"), expected);
|
||||
|
||||
// Format the backtrace a second time, just to make sure lazily resolved state is stable
|
||||
assert_eq!(format!("{backtrace:#?}"), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_frames() {
|
||||
let backtrace = Backtrace {
|
||||
inner: Inner::Captured(
|
||||
(Capture { actual_start: 1, frames: generate_fake_frames() }).into(),
|
||||
),
|
||||
};
|
||||
|
||||
let frames = backtrace.frames();
|
||||
|
||||
#[rustfmt::skip]
|
||||
let expected = vec![
|
||||
"[
|
||||
{ fn: \"std::backtrace::Backtrace::create\", file: \"rust/backtrace.rs\", line: 100 },
|
||||
]",
|
||||
"[
|
||||
{ fn: \"__rust_maybe_catch_panic\" },
|
||||
]",
|
||||
"[
|
||||
{ fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },
|
||||
{ fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },
|
||||
]"
|
||||
];
|
||||
|
||||
let mut iter = frames.iter().zip(expected.iter());
|
||||
|
||||
assert!(iter.all(|(f, e)| format!("{f:#?}") == *e));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backtrace_unwind_safe() {
|
||||
fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
|
||||
assert_unwind_safe::<Backtrace>();
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
//! The `ByteStr` and `ByteString` types and trait implementations.
|
||||
|
||||
#[unstable(feature = "bstr", issue = "134915")]
|
||||
pub use alloc_crate::bstr::{ByteStr, ByteString};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
//! Unordered containers, implemented as hash-tables
|
||||
|
||||
pub mod map;
|
||||
pub mod set;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,529 +0,0 @@
|
||||
use super::HashSet;
|
||||
use crate::hash::RandomState;
|
||||
use crate::panic::{AssertUnwindSafe, catch_unwind};
|
||||
use crate::sync::Arc;
|
||||
use crate::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
#[test]
|
||||
fn test_zero_capacities() {
|
||||
type HS = HashSet<i32>;
|
||||
|
||||
let s = HS::new();
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let s = HS::default();
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let s = HS::with_hasher(RandomState::new());
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let s = HS::with_capacity(0);
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let s = HS::with_capacity_and_hasher(0, RandomState::new());
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let mut s = HS::new();
|
||||
s.insert(1);
|
||||
s.insert(2);
|
||||
s.remove(&1);
|
||||
s.remove(&2);
|
||||
s.shrink_to_fit();
|
||||
assert_eq!(s.capacity(), 0);
|
||||
|
||||
let mut s = HS::new();
|
||||
s.reserve(0);
|
||||
assert_eq!(s.capacity(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_disjoint() {
|
||||
let mut xs = HashSet::new();
|
||||
let mut ys = HashSet::new();
|
||||
assert!(xs.is_disjoint(&ys));
|
||||
assert!(ys.is_disjoint(&xs));
|
||||
assert!(xs.insert(5));
|
||||
assert!(ys.insert(11));
|
||||
assert!(xs.is_disjoint(&ys));
|
||||
assert!(ys.is_disjoint(&xs));
|
||||
assert!(xs.insert(7));
|
||||
assert!(xs.insert(19));
|
||||
assert!(xs.insert(4));
|
||||
assert!(ys.insert(2));
|
||||
assert!(ys.insert(-11));
|
||||
assert!(xs.is_disjoint(&ys));
|
||||
assert!(ys.is_disjoint(&xs));
|
||||
assert!(ys.insert(7));
|
||||
assert!(!xs.is_disjoint(&ys));
|
||||
assert!(!ys.is_disjoint(&xs));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subset_and_superset() {
|
||||
let mut a = HashSet::new();
|
||||
assert!(a.insert(0));
|
||||
assert!(a.insert(5));
|
||||
assert!(a.insert(11));
|
||||
assert!(a.insert(7));
|
||||
|
||||
let mut b = HashSet::new();
|
||||
assert!(b.insert(0));
|
||||
assert!(b.insert(7));
|
||||
assert!(b.insert(19));
|
||||
assert!(b.insert(250));
|
||||
assert!(b.insert(11));
|
||||
assert!(b.insert(200));
|
||||
|
||||
assert!(!a.is_subset(&b));
|
||||
assert!(!a.is_superset(&b));
|
||||
assert!(!b.is_subset(&a));
|
||||
assert!(!b.is_superset(&a));
|
||||
|
||||
assert!(b.insert(5));
|
||||
|
||||
assert!(a.is_subset(&b));
|
||||
assert!(!a.is_superset(&b));
|
||||
assert!(!b.is_subset(&a));
|
||||
assert!(b.is_superset(&a));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterate() {
|
||||
let mut a = HashSet::new();
|
||||
for i in 0..32 {
|
||||
assert!(a.insert(i));
|
||||
}
|
||||
let mut observed: u32 = 0;
|
||||
for k in &a {
|
||||
observed |= 1 << *k;
|
||||
}
|
||||
assert_eq!(observed, 0xFFFF_FFFF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersection() {
|
||||
let mut a = HashSet::new();
|
||||
let mut b = HashSet::new();
|
||||
assert!(a.intersection(&b).next().is_none());
|
||||
|
||||
assert!(a.insert(11));
|
||||
assert!(a.insert(1));
|
||||
assert!(a.insert(3));
|
||||
assert!(a.insert(77));
|
||||
assert!(a.insert(103));
|
||||
assert!(a.insert(5));
|
||||
assert!(a.insert(-5));
|
||||
|
||||
assert!(b.insert(2));
|
||||
assert!(b.insert(11));
|
||||
assert!(b.insert(77));
|
||||
assert!(b.insert(-9));
|
||||
assert!(b.insert(-42));
|
||||
assert!(b.insert(5));
|
||||
assert!(b.insert(3));
|
||||
|
||||
let mut i = 0;
|
||||
let expected = [3, 5, 11, 77];
|
||||
for x in a.intersection(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
|
||||
assert!(a.insert(9)); // make a bigger than b
|
||||
|
||||
i = 0;
|
||||
for x in a.intersection(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
|
||||
i = 0;
|
||||
for x in b.intersection(&a) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_difference() {
|
||||
let mut a = HashSet::new();
|
||||
let mut b = HashSet::new();
|
||||
|
||||
assert!(a.insert(1));
|
||||
assert!(a.insert(3));
|
||||
assert!(a.insert(5));
|
||||
assert!(a.insert(9));
|
||||
assert!(a.insert(11));
|
||||
|
||||
assert!(b.insert(3));
|
||||
assert!(b.insert(9));
|
||||
|
||||
let mut i = 0;
|
||||
let expected = [1, 5, 11];
|
||||
for x in a.difference(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_symmetric_difference() {
|
||||
let mut a = HashSet::new();
|
||||
let mut b = HashSet::new();
|
||||
|
||||
assert!(a.insert(1));
|
||||
assert!(a.insert(3));
|
||||
assert!(a.insert(5));
|
||||
assert!(a.insert(9));
|
||||
assert!(a.insert(11));
|
||||
|
||||
assert!(b.insert(-2));
|
||||
assert!(b.insert(3));
|
||||
assert!(b.insert(9));
|
||||
assert!(b.insert(14));
|
||||
assert!(b.insert(22));
|
||||
|
||||
let mut i = 0;
|
||||
let expected = [-2, 1, 5, 11, 14, 22];
|
||||
for x in a.symmetric_difference(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_union() {
|
||||
let mut a = HashSet::new();
|
||||
let mut b = HashSet::new();
|
||||
assert!(a.union(&b).next().is_none());
|
||||
assert!(b.union(&a).next().is_none());
|
||||
|
||||
assert!(a.insert(1));
|
||||
assert!(a.insert(3));
|
||||
assert!(a.insert(11));
|
||||
assert!(a.insert(16));
|
||||
assert!(a.insert(19));
|
||||
assert!(a.insert(24));
|
||||
|
||||
assert!(b.insert(-2));
|
||||
assert!(b.insert(1));
|
||||
assert!(b.insert(5));
|
||||
assert!(b.insert(9));
|
||||
assert!(b.insert(13));
|
||||
assert!(b.insert(19));
|
||||
|
||||
let mut i = 0;
|
||||
let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24];
|
||||
for x in a.union(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
|
||||
assert!(a.insert(9)); // make a bigger than b
|
||||
assert!(a.insert(5));
|
||||
|
||||
i = 0;
|
||||
for x in a.union(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
|
||||
i = 0;
|
||||
for x in b.union(&a) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_iter() {
|
||||
let xs = [1, 2, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
let set: HashSet<_> = xs.iter().cloned().collect();
|
||||
|
||||
for x in &xs {
|
||||
assert!(set.contains(x));
|
||||
}
|
||||
|
||||
assert_eq!(set.iter().len(), xs.len() - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_iter() {
|
||||
let hs = {
|
||||
let mut hs = HashSet::new();
|
||||
|
||||
hs.insert('a');
|
||||
hs.insert('b');
|
||||
|
||||
hs
|
||||
};
|
||||
|
||||
let v = hs.into_iter().collect::<Vec<char>>();
|
||||
assert!(v == ['a', 'b'] || v == ['b', 'a']);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
// These constants once happened to expose a bug in insert().
|
||||
// I'm keeping them around to prevent a regression.
|
||||
let mut s1 = HashSet::new();
|
||||
|
||||
s1.insert(1);
|
||||
s1.insert(2);
|
||||
s1.insert(3);
|
||||
|
||||
let mut s2 = HashSet::new();
|
||||
|
||||
s2.insert(1);
|
||||
s2.insert(2);
|
||||
|
||||
assert!(s1 != s2);
|
||||
|
||||
s2.insert(3);
|
||||
|
||||
assert_eq!(s1, s2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_show() {
|
||||
let mut set = HashSet::new();
|
||||
let empty = HashSet::<i32>::new();
|
||||
|
||||
set.insert(1);
|
||||
set.insert(2);
|
||||
|
||||
let set_str = format!("{set:?}");
|
||||
|
||||
assert!(set_str == "{1, 2}" || set_str == "{2, 1}");
|
||||
assert_eq!(format!("{empty:?}"), "{}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trivial_drain() {
|
||||
let mut s = HashSet::<i32>::new();
|
||||
for _ in s.drain() {}
|
||||
assert!(s.is_empty());
|
||||
drop(s);
|
||||
|
||||
let mut s = HashSet::<i32>::new();
|
||||
drop(s.drain());
|
||||
assert!(s.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drain() {
|
||||
let mut s: HashSet<_> = (1..100).collect();
|
||||
|
||||
// try this a bunch of times to make sure we don't screw up internal state.
|
||||
for _ in 0..20 {
|
||||
assert_eq!(s.len(), 99);
|
||||
|
||||
{
|
||||
let mut last_i = 0;
|
||||
let mut d = s.drain();
|
||||
for (i, x) in d.by_ref().take(50).enumerate() {
|
||||
last_i = i;
|
||||
assert!(x != 0);
|
||||
}
|
||||
assert_eq!(last_i, 49);
|
||||
}
|
||||
|
||||
for _ in &s {
|
||||
panic!("s should be empty!");
|
||||
}
|
||||
|
||||
// reset to try again.
|
||||
s.extend(1..100);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
use crate::hash;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Foo(&'static str, #[allow(dead_code)] i32);
|
||||
|
||||
impl PartialEq for Foo {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Foo {}
|
||||
|
||||
impl hash::Hash for Foo {
|
||||
fn hash<H: hash::Hasher>(&self, h: &mut H) {
|
||||
self.0.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
let mut s = HashSet::new();
|
||||
assert_eq!(s.replace(Foo("a", 1)), None);
|
||||
assert_eq!(s.len(), 1);
|
||||
assert_eq!(s.replace(Foo("a", 2)), Some(Foo("a", 1)));
|
||||
assert_eq!(s.len(), 1);
|
||||
|
||||
let mut it = s.iter();
|
||||
assert_eq!(it.next(), Some(&Foo("a", 2)));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extend_ref() {
|
||||
let mut a = HashSet::new();
|
||||
a.insert(1);
|
||||
|
||||
a.extend(&[2, 3, 4]);
|
||||
|
||||
assert_eq!(a.len(), 4);
|
||||
assert!(a.contains(&1));
|
||||
assert!(a.contains(&2));
|
||||
assert!(a.contains(&3));
|
||||
assert!(a.contains(&4));
|
||||
|
||||
let mut b = HashSet::new();
|
||||
b.insert(5);
|
||||
b.insert(6);
|
||||
|
||||
a.extend(&b);
|
||||
|
||||
assert_eq!(a.len(), 6);
|
||||
assert!(a.contains(&1));
|
||||
assert!(a.contains(&2));
|
||||
assert!(a.contains(&3));
|
||||
assert!(a.contains(&4));
|
||||
assert!(a.contains(&5));
|
||||
assert!(a.contains(&6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retain() {
|
||||
let xs = [1, 2, 3, 4, 5, 6];
|
||||
let mut set: HashSet<i32> = xs.iter().cloned().collect();
|
||||
set.retain(|&k| k % 2 == 0);
|
||||
assert_eq!(set.len(), 3);
|
||||
assert!(set.contains(&2));
|
||||
assert!(set.contains(&4));
|
||||
assert!(set.contains(&6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_if() {
|
||||
let mut x: HashSet<_> = [1].iter().copied().collect();
|
||||
let mut y: HashSet<_> = [1].iter().copied().collect();
|
||||
|
||||
x.extract_if(|_| true).for_each(drop);
|
||||
y.extract_if(|_| false).for_each(drop);
|
||||
assert_eq!(x.len(), 0);
|
||||
assert_eq!(y.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn test_extract_if_drop_panic_leak() {
|
||||
static PREDS: AtomicU32 = AtomicU32::new(0);
|
||||
static DROPS: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Hash)]
|
||||
struct D(i32);
|
||||
impl Drop for D {
|
||||
fn drop(&mut self) {
|
||||
if DROPS.fetch_add(1, Ordering::SeqCst) == 1 {
|
||||
panic!("panic in `drop`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut set = (0..3).map(|i| D(i)).collect::<HashSet<_>>();
|
||||
|
||||
catch_unwind(move || {
|
||||
set.extract_if(|_| {
|
||||
PREDS.fetch_add(1, Ordering::SeqCst);
|
||||
true
|
||||
})
|
||||
.for_each(drop)
|
||||
})
|
||||
.ok();
|
||||
|
||||
assert_eq!(PREDS.load(Ordering::SeqCst), 2);
|
||||
assert_eq!(DROPS.load(Ordering::SeqCst), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn test_extract_if_pred_panic_leak() {
|
||||
static PREDS: AtomicU32 = AtomicU32::new(0);
|
||||
static DROPS: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Hash)]
|
||||
struct D;
|
||||
impl Drop for D {
|
||||
fn drop(&mut self) {
|
||||
DROPS.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let mut set: HashSet<_> = (0..3).map(|_| D).collect();
|
||||
|
||||
catch_unwind(AssertUnwindSafe(|| {
|
||||
set.extract_if(|_| match PREDS.fetch_add(1, Ordering::SeqCst) {
|
||||
0 => true,
|
||||
_ => panic!(),
|
||||
})
|
||||
.for_each(drop)
|
||||
}))
|
||||
.ok();
|
||||
|
||||
assert_eq!(PREDS.load(Ordering::SeqCst), 1);
|
||||
assert_eq!(DROPS.load(Ordering::SeqCst), 3);
|
||||
assert_eq!(set.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_array() {
|
||||
let set = HashSet::from([1, 2, 3, 4]);
|
||||
let unordered_duplicates = HashSet::from([4, 1, 4, 3, 2]);
|
||||
assert_eq!(set, unordered_duplicates);
|
||||
|
||||
// This next line must infer the hasher type parameter.
|
||||
// If you make a change that causes this line to no longer infer,
|
||||
// that's a problem!
|
||||
let _must_not_require_type_annotation = HashSet::from([1, 2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_with_hasher() {
|
||||
const X: HashSet<(), ()> = HashSet::with_hasher(());
|
||||
const Y: HashSet<(), ()> = Default::default();
|
||||
assert_eq!(X.len(), 0);
|
||||
assert_eq!(Y.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_does_not_overwrite_the_value() {
|
||||
let first_value = Arc::new(17);
|
||||
let second_value = Arc::new(17);
|
||||
|
||||
let mut set = HashSet::new();
|
||||
let inserted = set.insert(first_value.clone());
|
||||
assert!(inserted);
|
||||
|
||||
let inserted = set.insert(second_value);
|
||||
assert!(!inserted);
|
||||
|
||||
assert!(
|
||||
Arc::ptr_eq(set.iter().next().unwrap(), &first_value),
|
||||
"Insert must not overwrite the value, so the contained value pointer \
|
||||
must be the same as first value pointer we inserted"
|
||||
);
|
||||
}
|
||||
@@ -1,459 +0,0 @@
|
||||
//! 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");
|
||||
//! ```
|
||||
|
||||
#![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::*;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,562 +0,0 @@
|
||||
// todo retreive docs
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::error::Error;
|
||||
#[unstable(feature = "error_generic_member_access", issue = "99301")]
|
||||
pub use core::error::{Request, request_ref, request_value};
|
||||
|
||||
use crate::backtrace::Backtrace;
|
||||
use crate::fmt::{self, Write};
|
||||
|
||||
/// An error reporter that prints an error and its sources.
|
||||
///
|
||||
/// Report also exposes configuration options for formatting the error sources, either entirely on a
|
||||
/// single line, or in multi-line format with each source on a new line.
|
||||
///
|
||||
/// `Report` only requires that the wrapped error implement `Error`. It doesn't require that the
|
||||
/// wrapped error be `Send`, `Sync`, or `'static`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(error_reporter)]
|
||||
/// use std::error::{Error, Report};
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// struct SuperError {
|
||||
/// source: SuperErrorSideKick,
|
||||
/// }
|
||||
///
|
||||
/// impl fmt::Display for SuperError {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// write!(f, "SuperError is here!")
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Error for SuperError {
|
||||
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
/// Some(&self.source)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// struct SuperErrorSideKick;
|
||||
///
|
||||
/// impl fmt::Display for SuperErrorSideKick {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// write!(f, "SuperErrorSideKick is here!")
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Error for SuperErrorSideKick {}
|
||||
///
|
||||
/// fn get_super_error() -> Result<(), SuperError> {
|
||||
/// Err(SuperError { source: SuperErrorSideKick })
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// match get_super_error() {
|
||||
/// Err(e) => println!("Error: {}", Report::new(e)),
|
||||
/// _ => println!("No error"),
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This example produces the following output:
|
||||
///
|
||||
/// ```console
|
||||
/// Error: SuperError is here!: SuperErrorSideKick is here!
|
||||
/// ```
|
||||
///
|
||||
/// ## Output consistency
|
||||
///
|
||||
/// Report prints the same output via `Display` and `Debug`, so it works well with
|
||||
/// [`Result::unwrap`]/[`Result::expect`] which print their `Err` variant via `Debug`:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// #![feature(error_reporter)]
|
||||
/// use std::error::Report;
|
||||
/// # use std::error::Error;
|
||||
/// # use std::fmt;
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperError {
|
||||
/// # source: SuperErrorSideKick,
|
||||
/// # }
|
||||
/// # impl fmt::Display for SuperError {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperError is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperError {
|
||||
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
/// # Some(&self.source)
|
||||
/// # }
|
||||
/// # }
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperErrorSideKick;
|
||||
/// # impl fmt::Display for SuperErrorSideKick {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperErrorSideKick is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperErrorSideKick {}
|
||||
/// # fn get_super_error() -> Result<(), SuperError> {
|
||||
/// # Err(SuperError { source: SuperErrorSideKick })
|
||||
/// # }
|
||||
///
|
||||
/// get_super_error().map_err(Report::new).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// This example produces the following output:
|
||||
///
|
||||
/// ```console
|
||||
/// thread 'main' panicked at src/error.rs:34:40:
|
||||
/// called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!
|
||||
/// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
/// ```
|
||||
///
|
||||
/// ## Return from `main`
|
||||
///
|
||||
/// `Report` also implements `From` for all types that implement [`Error`]; this when combined with
|
||||
/// the `Debug` output means `Report` is an ideal starting place for formatting errors returned
|
||||
/// from `main`.
|
||||
///
|
||||
/// ```should_panic
|
||||
/// #![feature(error_reporter)]
|
||||
/// use std::error::Report;
|
||||
/// # use std::error::Error;
|
||||
/// # use std::fmt;
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperError {
|
||||
/// # source: SuperErrorSideKick,
|
||||
/// # }
|
||||
/// # impl fmt::Display for SuperError {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperError is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperError {
|
||||
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
/// # Some(&self.source)
|
||||
/// # }
|
||||
/// # }
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperErrorSideKick;
|
||||
/// # impl fmt::Display for SuperErrorSideKick {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperErrorSideKick is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperErrorSideKick {}
|
||||
/// # fn get_super_error() -> Result<(), SuperError> {
|
||||
/// # Err(SuperError { source: SuperErrorSideKick })
|
||||
/// # }
|
||||
///
|
||||
/// fn main() -> Result<(), Report<SuperError>> {
|
||||
/// get_super_error()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This example produces the following output:
|
||||
///
|
||||
/// ```console
|
||||
/// Error: SuperError is here!: SuperErrorSideKick is here!
|
||||
/// ```
|
||||
///
|
||||
/// **Note**: `Report`s constructed via `?` and `From` will be configured to use the single line
|
||||
/// output format. If you want to make sure your `Report`s are pretty printed and include backtrace
|
||||
/// you will need to manually convert and enable those flags.
|
||||
///
|
||||
/// ```should_panic
|
||||
/// #![feature(error_reporter)]
|
||||
/// use std::error::Report;
|
||||
/// # use std::error::Error;
|
||||
/// # use std::fmt;
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperError {
|
||||
/// # source: SuperErrorSideKick,
|
||||
/// # }
|
||||
/// # impl fmt::Display for SuperError {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperError is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperError {
|
||||
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
/// # Some(&self.source)
|
||||
/// # }
|
||||
/// # }
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperErrorSideKick;
|
||||
/// # impl fmt::Display for SuperErrorSideKick {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperErrorSideKick is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperErrorSideKick {}
|
||||
/// # fn get_super_error() -> Result<(), SuperError> {
|
||||
/// # Err(SuperError { source: SuperErrorSideKick })
|
||||
/// # }
|
||||
///
|
||||
/// fn main() -> Result<(), Report<SuperError>> {
|
||||
/// get_super_error()
|
||||
/// .map_err(Report::from)
|
||||
/// .map_err(|r| r.pretty(true).show_backtrace(true))?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This example produces the following output:
|
||||
///
|
||||
/// ```console
|
||||
/// Error: SuperError is here!
|
||||
///
|
||||
/// Caused by:
|
||||
/// SuperErrorSideKick is here!
|
||||
/// ```
|
||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||
pub struct Report<E = Box<dyn Error>> {
|
||||
/// The error being reported.
|
||||
error: E,
|
||||
/// Whether a backtrace should be included as part of the report.
|
||||
show_backtrace: bool,
|
||||
/// Whether the report should be pretty-printed.
|
||||
pretty: bool,
|
||||
}
|
||||
|
||||
impl<E> Report<E>
|
||||
where
|
||||
Report<E>: From<E>,
|
||||
{
|
||||
/// Creates a new `Report` from an input error.
|
||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||
pub fn new(error: E) -> Report<E> {
|
||||
Self::from(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Report<E> {
|
||||
/// Enable pretty-printing the report across multiple lines.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(error_reporter)]
|
||||
/// use std::error::Report;
|
||||
/// # use std::error::Error;
|
||||
/// # use std::fmt;
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperError {
|
||||
/// # source: SuperErrorSideKick,
|
||||
/// # }
|
||||
/// # impl fmt::Display for SuperError {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperError is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperError {
|
||||
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
/// # Some(&self.source)
|
||||
/// # }
|
||||
/// # }
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperErrorSideKick;
|
||||
/// # impl fmt::Display for SuperErrorSideKick {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperErrorSideKick is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperErrorSideKick {}
|
||||
///
|
||||
/// let error = SuperError { source: SuperErrorSideKick };
|
||||
/// let report = Report::new(error).pretty(true);
|
||||
/// eprintln!("Error: {report:?}");
|
||||
/// ```
|
||||
///
|
||||
/// This example produces the following output:
|
||||
///
|
||||
/// ```console
|
||||
/// Error: SuperError is here!
|
||||
///
|
||||
/// Caused by:
|
||||
/// SuperErrorSideKick is here!
|
||||
/// ```
|
||||
///
|
||||
/// When there are multiple source errors the causes will be numbered in order of iteration
|
||||
/// starting from the outermost error.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(error_reporter)]
|
||||
/// use std::error::Report;
|
||||
/// # use std::error::Error;
|
||||
/// # use std::fmt;
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperError {
|
||||
/// # source: SuperErrorSideKick,
|
||||
/// # }
|
||||
/// # impl fmt::Display for SuperError {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperError is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperError {
|
||||
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
/// # Some(&self.source)
|
||||
/// # }
|
||||
/// # }
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperErrorSideKick {
|
||||
/// # source: SuperErrorSideKickSideKick,
|
||||
/// # }
|
||||
/// # impl fmt::Display for SuperErrorSideKick {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperErrorSideKick is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperErrorSideKick {
|
||||
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
/// # Some(&self.source)
|
||||
/// # }
|
||||
/// # }
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperErrorSideKickSideKick;
|
||||
/// # impl fmt::Display for SuperErrorSideKickSideKick {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperErrorSideKickSideKick is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperErrorSideKickSideKick { }
|
||||
///
|
||||
/// let source = SuperErrorSideKickSideKick;
|
||||
/// let source = SuperErrorSideKick { source };
|
||||
/// let error = SuperError { source };
|
||||
/// let report = Report::new(error).pretty(true);
|
||||
/// eprintln!("Error: {report:?}");
|
||||
/// ```
|
||||
///
|
||||
/// This example produces the following output:
|
||||
///
|
||||
/// ```console
|
||||
/// Error: SuperError is here!
|
||||
///
|
||||
/// Caused by:
|
||||
/// 0: SuperErrorSideKick is here!
|
||||
/// 1: SuperErrorSideKickSideKick is here!
|
||||
/// ```
|
||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||
pub fn pretty(mut self, pretty: bool) -> Self {
|
||||
self.pretty = pretty;
|
||||
self
|
||||
}
|
||||
|
||||
/// Display backtrace if available when using pretty output format.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// **Note**: Report will search for the first `Backtrace` it can find starting from the
|
||||
/// outermost error. In this example it will display the backtrace from the second error in the
|
||||
/// sources, `SuperErrorSideKick`.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(error_reporter)]
|
||||
/// #![feature(error_generic_member_access)]
|
||||
/// # use std::error::Error;
|
||||
/// # use std::fmt;
|
||||
/// use std::error::Request;
|
||||
/// use std::error::Report;
|
||||
/// use std::backtrace::Backtrace;
|
||||
///
|
||||
/// # #[derive(Debug)]
|
||||
/// # struct SuperError {
|
||||
/// # source: SuperErrorSideKick,
|
||||
/// # }
|
||||
/// # impl fmt::Display for SuperError {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperError is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
/// # impl Error for SuperError {
|
||||
/// # fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
/// # Some(&self.source)
|
||||
/// # }
|
||||
/// # }
|
||||
/// #[derive(Debug)]
|
||||
/// struct SuperErrorSideKick {
|
||||
/// backtrace: Backtrace,
|
||||
/// }
|
||||
///
|
||||
/// impl SuperErrorSideKick {
|
||||
/// fn new() -> SuperErrorSideKick {
|
||||
/// SuperErrorSideKick { backtrace: Backtrace::force_capture() }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Error for SuperErrorSideKick {
|
||||
/// fn provide<'a>(&'a self, request: &mut Request<'a>) {
|
||||
/// request.provide_ref::<Backtrace>(&self.backtrace);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // The rest of the example is unchanged ...
|
||||
/// # impl fmt::Display for SuperErrorSideKick {
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # write!(f, "SuperErrorSideKick is here!")
|
||||
/// # }
|
||||
/// # }
|
||||
///
|
||||
/// let source = SuperErrorSideKick::new();
|
||||
/// let error = SuperError { source };
|
||||
/// let report = Report::new(error).pretty(true).show_backtrace(true);
|
||||
/// eprintln!("Error: {report:?}");
|
||||
/// ```
|
||||
///
|
||||
/// This example produces something similar to the following output:
|
||||
///
|
||||
/// ```console
|
||||
/// Error: SuperError is here!
|
||||
///
|
||||
/// Caused by:
|
||||
/// SuperErrorSideKick is here!
|
||||
///
|
||||
/// Stack backtrace:
|
||||
/// 0: rust_out::main::_doctest_main_src_error_rs_1158_0::SuperErrorSideKick::new
|
||||
/// 1: rust_out::main::_doctest_main_src_error_rs_1158_0
|
||||
/// 2: rust_out::main
|
||||
/// 3: core::ops::function::FnOnce::call_once
|
||||
/// 4: std::sys::backtrace::__rust_begin_short_backtrace
|
||||
/// 5: std::rt::lang_start::{{closure}}
|
||||
/// 6: std::panicking::try
|
||||
/// 7: std::rt::lang_start_internal
|
||||
/// 8: std::rt::lang_start
|
||||
/// 9: main
|
||||
/// 10: __libc_start_main
|
||||
/// 11: _start
|
||||
/// ```
|
||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||
pub fn show_backtrace(mut self, show_backtrace: bool) -> Self {
|
||||
self.show_backtrace = show_backtrace;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Report<E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
fn backtrace(&self) -> Option<&Backtrace> {
|
||||
// have to grab the backtrace on the first error directly since that error may not be
|
||||
// 'static
|
||||
let backtrace = request_ref(&self.error);
|
||||
let backtrace = backtrace.or_else(|| {
|
||||
self.error
|
||||
.source()
|
||||
.map(|source| source.sources().find_map(|source| request_ref(source)))
|
||||
.flatten()
|
||||
});
|
||||
backtrace
|
||||
}
|
||||
|
||||
/// Format the report as a single line.
|
||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||
fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.error)?;
|
||||
|
||||
let sources = self.error.source().into_iter().flat_map(<dyn Error>::sources);
|
||||
|
||||
for cause in sources {
|
||||
write!(f, ": {cause}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Format the report as multiple lines, with each error cause on its own line.
|
||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||
fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let error = &self.error;
|
||||
|
||||
write!(f, "{error}")?;
|
||||
|
||||
if let Some(cause) = error.source() {
|
||||
write!(f, "\n\nCaused by:")?;
|
||||
|
||||
let multiple = cause.source().is_some();
|
||||
|
||||
for (ind, error) in cause.sources().enumerate() {
|
||||
writeln!(f)?;
|
||||
let mut indented = Indented { inner: f };
|
||||
if multiple {
|
||||
write!(indented, "{ind: >4}: {error}")?;
|
||||
} else {
|
||||
write!(indented, " {error}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.show_backtrace {
|
||||
if let Some(backtrace) = self.backtrace() {
|
||||
write!(f, "\n\nStack backtrace:\n{}", backtrace.to_string().trim_end())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||
impl<E> From<E> for Report<E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
fn from(error: E) -> Self {
|
||||
Report { error, show_backtrace: false, pretty: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||
impl<E> fmt::Display for Report<E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) }
|
||||
}
|
||||
}
|
||||
|
||||
// This type intentionally outputs the same format for `Display` and `Debug`for
|
||||
// situations where you unwrap a `Report` or return it from main.
|
||||
#[unstable(feature = "error_reporter", issue = "90172")]
|
||||
impl<E> fmt::Debug for Report<E>
|
||||
where
|
||||
Report<E>: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper type for indenting the inner source.
|
||||
struct Indented<'a, D> {
|
||||
inner: &'a mut D,
|
||||
}
|
||||
|
||||
impl<T> Write for Indented<'_, T>
|
||||
where
|
||||
T: Write,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for (i, line) in s.split('\n').enumerate() {
|
||||
if i > 0 {
|
||||
self.inner.write_char('\n')?;
|
||||
self.inner.write_str(" ")?;
|
||||
}
|
||||
|
||||
self.inner.write_str(line)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
//! [`CStr`], [`CString`], and related types.
|
||||
|
||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
||||
pub use alloc_crate::ffi::c_str::FromVecWithNulError;
|
||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
||||
pub use alloc_crate::ffi::c_str::IntoStringError;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use alloc_crate::ffi::c_str::{CString, NulError};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::ffi::c_str::CStr;
|
||||
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
|
||||
pub use core::ffi::c_str::FromBytesUntilNulError;
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
pub use core::ffi::c_str::FromBytesWithNulError;
|
||||
@@ -1,207 +0,0 @@
|
||||
//! Utilities related to FFI bindings.
|
||||
//!
|
||||
//! This module provides utilities to handle data across non-Rust
|
||||
//! interfaces, like other programming languages and the underlying
|
||||
//! operating system. It is mainly of use for FFI (Foreign Function
|
||||
//! Interface) bindings and code that needs to exchange C-like strings
|
||||
//! with other languages.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! Rust represents owned strings with the [`String`] type, and
|
||||
//! borrowed slices of strings with the [`str`] primitive. Both are
|
||||
//! always in UTF-8 encoding, and may contain nul bytes in the middle,
|
||||
//! i.e., if you look at the bytes that make up the string, there may
|
||||
//! be a `\0` among them. Both `String` and `str` store their length
|
||||
//! explicitly; there are no nul terminators at the end of strings
|
||||
//! like in C.
|
||||
//!
|
||||
//! C strings are different from Rust strings:
|
||||
//!
|
||||
//! * **Encodings** - Rust strings are UTF-8, but C strings may use
|
||||
//! other encodings. If you are using a string from C, you should
|
||||
//! check its encoding explicitly, rather than just assuming that it
|
||||
//! is UTF-8 like you can do in Rust.
|
||||
//!
|
||||
//! * **Character size** - C strings may use `char` or `wchar_t`-sized
|
||||
//! characters; please **note** that C's `char` is different from Rust's.
|
||||
//! The C standard leaves the actual sizes of those types open to
|
||||
//! interpretation, but defines different APIs for strings made up of
|
||||
//! each character type. Rust strings are always UTF-8, so different
|
||||
//! Unicode characters will be encoded in a variable number of bytes
|
||||
//! each. The Rust type [`char`] represents a '[Unicode scalar
|
||||
//! value]', which is similar to, but not the same as, a '[Unicode
|
||||
//! code point]'.
|
||||
//!
|
||||
//! * **Nul terminators and implicit string lengths** - Often, C
|
||||
//! strings are nul-terminated, i.e., they have a `\0` character at the
|
||||
//! end. The length of a string buffer is not stored, but has to be
|
||||
//! calculated; to compute the length of a string, C code must
|
||||
//! manually call a function like `strlen()` for `char`-based strings,
|
||||
//! or `wcslen()` for `wchar_t`-based ones. Those functions return
|
||||
//! the number of characters in the string excluding the nul
|
||||
//! terminator, so the buffer length is really `len+1` characters.
|
||||
//! Rust strings don't have a nul terminator; their length is always
|
||||
//! stored and does not need to be calculated. While in Rust
|
||||
//! accessing a string's length is an *O*(1) operation (because the
|
||||
//! length is stored); in C it is an *O*(*n*) operation because the
|
||||
//! length needs to be computed by scanning the string for the nul
|
||||
//! terminator.
|
||||
//!
|
||||
//! * **Internal nul characters** - When C strings have a nul
|
||||
//! terminator character, this usually means that they cannot have nul
|
||||
//! characters in the middle — a nul character would essentially
|
||||
//! truncate the string. Rust strings *can* have nul characters in
|
||||
//! the middle, because nul does not have to mark the end of the
|
||||
//! string in Rust.
|
||||
//!
|
||||
//! # Representations of non-Rust strings
|
||||
//!
|
||||
//! [`CString`] and [`CStr`] are useful when you need to transfer
|
||||
//! UTF-8 strings to and from languages with a C ABI, like Python.
|
||||
//!
|
||||
//! * **From Rust to C:** [`CString`] represents an owned, C-friendly
|
||||
//! string: it is nul-terminated, and has no internal nul characters.
|
||||
//! Rust code can create a [`CString`] out of a normal string (provided
|
||||
//! that the string doesn't have nul characters in the middle), and
|
||||
//! then use a variety of methods to obtain a raw <code>\*mut [u8]</code> that can
|
||||
//! then be passed as an argument to functions which use the C
|
||||
//! conventions for strings.
|
||||
//!
|
||||
//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it
|
||||
//! is what you would use to wrap a raw <code>\*const [u8]</code> that you got from
|
||||
//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array
|
||||
//! of bytes. Once you have a [`CStr`], you can convert it to a Rust
|
||||
//! <code>&[str]</code> if it's valid UTF-8, or lossily convert it by adding
|
||||
//! replacement characters.
|
||||
//!
|
||||
//! [`OsString`] and [`OsStr`] are useful when you need to transfer
|
||||
//! strings to and from the operating system itself, or when capturing
|
||||
//! the output of external commands. Conversions between [`OsString`],
|
||||
//! [`OsStr`] and Rust strings work similarly to those for [`CString`]
|
||||
//! and [`CStr`].
|
||||
//!
|
||||
//! * [`OsString`] losslessly represents an owned platform string. However, this
|
||||
//! representation is not necessarily in a form native to the platform.
|
||||
//! In the Rust standard library, various APIs that transfer strings to/from the operating
|
||||
//! system use [`OsString`] instead of plain strings. For example,
|
||||
//! [`env::var_os()`] is used to query environment variables; it
|
||||
//! returns an <code>[Option]<[OsString]></code>. If the environment variable
|
||||
//! exists you will get a <code>[Some]\(os_string)</code>, which you can
|
||||
//! *then* try to convert to a Rust string. This yields a [`Result`], so that
|
||||
//! your code can detect errors in case the environment variable did
|
||||
//! not in fact contain valid Unicode data.
|
||||
//!
|
||||
//! * [`OsStr`] losslessly represents a borrowed reference to a platform string.
|
||||
//! However, this representation is not necessarily in a form native to the platform.
|
||||
//! It can be converted into a UTF-8 Rust string slice in a similar way to
|
||||
//! [`OsString`].
|
||||
//!
|
||||
//! # Conversions
|
||||
//!
|
||||
//! ## On Unix
|
||||
//!
|
||||
//! On Unix, [`OsStr`] implements the
|
||||
//! <code>std::os::unix::ffi::[OsStrExt][unix.OsStrExt]</code> trait, which
|
||||
//! augments it with two methods, [`from_bytes`] and [`as_bytes`].
|
||||
//! These do inexpensive conversions from and to byte slices.
|
||||
//!
|
||||
//! Additionally, on Unix [`OsString`] implements the
|
||||
//! <code>std::os::unix::ffi::[OsStringExt][unix.OsStringExt]</code> trait,
|
||||
//! which provides [`from_vec`] and [`into_vec`] methods that consume
|
||||
//! their arguments, and take or produce vectors of [`u8`].
|
||||
//!
|
||||
//! ## On Windows
|
||||
//!
|
||||
//! An [`OsStr`] can be losslessly converted to a native Windows string. And
|
||||
//! a native Windows string can be losslessly converted to an [`OsString`].
|
||||
//!
|
||||
//! On Windows, [`OsStr`] implements the
|
||||
//! <code>std::os::windows::ffi::[OsStrExt][windows.OsStrExt]</code> trait,
|
||||
//! which provides an [`encode_wide`] method. This provides an
|
||||
//! iterator that can be [`collect`]ed into a vector of [`u16`]. After a nul
|
||||
//! characters is appended, this is the same as a native Windows string.
|
||||
//!
|
||||
//! Additionally, on Windows [`OsString`] implements the
|
||||
//! <code>std::os::windows:ffi::[OsStringExt][windows.OsStringExt]</code>
|
||||
//! trait, which provides a [`from_wide`] method to convert a native Windows
|
||||
//! string (without the terminating nul character) to an [`OsString`].
|
||||
//!
|
||||
//! ## Other platforms
|
||||
//!
|
||||
//! Many other platforms provide their own extension traits in a
|
||||
//! `std::os::*::ffi` module.
|
||||
//!
|
||||
//! ## On all platforms
|
||||
//!
|
||||
//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of
|
||||
//! UTF-8; see [`OsString`] for more details on its encoding on different platforms.
|
||||
//!
|
||||
//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_encoded_bytes`] and
|
||||
//! [`OsStr::from_encoded_bytes_unchecked`].
|
||||
//!
|
||||
//! For basic string processing, see [`OsStr::slice_encoded_bytes`].
|
||||
//!
|
||||
//! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
|
||||
//! [Unicode code point]: https://www.unicode.org/glossary/#code_point
|
||||
//! [`env::set_var()`]: crate::env::set_var "env::set_var"
|
||||
//! [`env::var_os()`]: crate::env::var_os "env::var_os"
|
||||
//! [unix.OsStringExt]: crate::os::unix::ffi::OsStringExt "os::unix::ffi::OsStringExt"
|
||||
//! [`from_vec`]: crate::os::unix::ffi::OsStringExt::from_vec "os::unix::ffi::OsStringExt::from_vec"
|
||||
//! [`into_vec`]: crate::os::unix::ffi::OsStringExt::into_vec "os::unix::ffi::OsStringExt::into_vec"
|
||||
//! [unix.OsStrExt]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt"
|
||||
//! [`from_bytes`]: crate::os::unix::ffi::OsStrExt::from_bytes "os::unix::ffi::OsStrExt::from_bytes"
|
||||
//! [`as_bytes`]: crate::os::unix::ffi::OsStrExt::as_bytes "os::unix::ffi::OsStrExt::as_bytes"
|
||||
//! [`OsStrExt`]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt"
|
||||
//! [windows.OsStrExt]: crate::os::windows::ffi::OsStrExt "os::windows::ffi::OsStrExt"
|
||||
//! [`encode_wide`]: crate::os::windows::ffi::OsStrExt::encode_wide "os::windows::ffi::OsStrExt::encode_wide"
|
||||
//! [`collect`]: crate::iter::Iterator::collect "iter::Iterator::collect"
|
||||
//! [windows.OsStringExt]: crate::os::windows::ffi::OsStringExt "os::windows::ffi::OsStringExt"
|
||||
//! [`from_wide`]: crate::os::windows::ffi::OsStringExt::from_wide "os::windows::ffi::OsStringExt::from_wide"
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[stable(feature = "c_str_module", since = "1.88.0")]
|
||||
pub mod c_str;
|
||||
|
||||
#[stable(feature = "core_c_void", since = "1.30.0")]
|
||||
pub use core::ffi::c_void;
|
||||
#[unstable(
|
||||
feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "44930"
|
||||
)]
|
||||
pub use core::ffi::{VaArgSafe, VaList};
|
||||
#[stable(feature = "core_ffi_c", since = "1.64.0")]
|
||||
pub use core::ffi::{
|
||||
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
|
||||
c_ulong, c_ulonglong, c_ushort,
|
||||
};
|
||||
#[unstable(feature = "c_size_t", issue = "88345")]
|
||||
pub use core::ffi::{c_ptrdiff_t, c_size_t, c_ssize_t};
|
||||
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
|
||||
pub use self::c_str::FromBytesUntilNulError;
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
pub use self::c_str::FromBytesWithNulError;
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
||||
pub use self::c_str::FromVecWithNulError;
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
||||
pub use self::c_str::IntoStringError;
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::c_str::NulError;
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::c_str::{CStr, CString};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(inline)]
|
||||
pub use self::os_str::{OsStr, OsString};
|
||||
|
||||
#[stable(feature = "os_str_display", since = "1.87.0")]
|
||||
pub mod os_str;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,311 +0,0 @@
|
||||
use super::*;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ptr;
|
||||
|
||||
#[test]
|
||||
fn test_os_string_with_capacity() {
|
||||
let os_string = OsString::with_capacity(0);
|
||||
assert_eq!(0, os_string.inner.into_inner().capacity());
|
||||
|
||||
let os_string = OsString::with_capacity(10);
|
||||
assert_eq!(10, os_string.inner.into_inner().capacity());
|
||||
|
||||
let mut os_string = OsString::with_capacity(0);
|
||||
os_string.push("abc");
|
||||
assert!(os_string.inner.into_inner().capacity() >= 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_string_clear() {
|
||||
let mut os_string = OsString::from("abc");
|
||||
assert_eq!(3, os_string.inner.as_inner().len());
|
||||
|
||||
os_string.clear();
|
||||
assert_eq!(&os_string, "");
|
||||
assert_eq!(0, os_string.inner.as_inner().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_string_leak() {
|
||||
let os_string = OsString::from("have a cake");
|
||||
let (len, cap) = (os_string.len(), os_string.capacity());
|
||||
let leaked = os_string.leak();
|
||||
assert_eq!(leaked.as_encoded_bytes(), b"have a cake");
|
||||
unsafe { drop(String::from_raw_parts(leaked as *mut OsStr as _, len, cap)) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_string_capacity() {
|
||||
let os_string = OsString::with_capacity(0);
|
||||
assert_eq!(0, os_string.capacity());
|
||||
|
||||
let os_string = OsString::with_capacity(10);
|
||||
assert_eq!(10, os_string.capacity());
|
||||
|
||||
let mut os_string = OsString::with_capacity(0);
|
||||
os_string.push("abc");
|
||||
assert!(os_string.capacity() >= 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_string_reserve() {
|
||||
let mut os_string = OsString::new();
|
||||
assert_eq!(os_string.capacity(), 0);
|
||||
|
||||
os_string.reserve(2);
|
||||
assert!(os_string.capacity() >= 2);
|
||||
|
||||
for _ in 0..16 {
|
||||
os_string.push("a");
|
||||
}
|
||||
|
||||
assert!(os_string.capacity() >= 16);
|
||||
os_string.reserve(16);
|
||||
assert!(os_string.capacity() >= 32);
|
||||
|
||||
os_string.push("a");
|
||||
|
||||
os_string.reserve(16);
|
||||
assert!(os_string.capacity() >= 33)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_string_reserve_exact() {
|
||||
let mut os_string = OsString::new();
|
||||
assert_eq!(os_string.capacity(), 0);
|
||||
|
||||
os_string.reserve_exact(2);
|
||||
assert!(os_string.capacity() >= 2);
|
||||
|
||||
for _ in 0..16 {
|
||||
os_string.push("a");
|
||||
}
|
||||
|
||||
assert!(os_string.capacity() >= 16);
|
||||
os_string.reserve_exact(16);
|
||||
assert!(os_string.capacity() >= 32);
|
||||
|
||||
os_string.push("a");
|
||||
|
||||
os_string.reserve_exact(16);
|
||||
assert!(os_string.capacity() >= 33)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_string_join() {
|
||||
let strings = [OsStr::new("hello"), OsStr::new("dear"), OsStr::new("world")];
|
||||
assert_eq!("hello", strings[..1].join(OsStr::new(" ")));
|
||||
assert_eq!("hello dear world", strings.join(OsStr::new(" ")));
|
||||
assert_eq!("hellodearworld", strings.join(OsStr::new("")));
|
||||
assert_eq!("hello.\n dear.\n world", strings.join(OsStr::new(".\n ")));
|
||||
|
||||
assert_eq!("dear world", strings[1..].join(&OsString::from(" ")));
|
||||
|
||||
let strings_abc = [OsString::from("a"), OsString::from("b"), OsString::from("c")];
|
||||
assert_eq!("a b c", strings_abc.join(OsStr::new(" ")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_string_default() {
|
||||
let os_string: OsString = Default::default();
|
||||
assert_eq!("", &os_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_str_is_empty() {
|
||||
let mut os_string = OsString::new();
|
||||
assert!(os_string.is_empty());
|
||||
|
||||
os_string.push("abc");
|
||||
assert!(!os_string.is_empty());
|
||||
|
||||
os_string.clear();
|
||||
assert!(os_string.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_str_len() {
|
||||
let mut os_string = OsString::new();
|
||||
assert_eq!(0, os_string.len());
|
||||
|
||||
os_string.push("abc");
|
||||
assert_eq!(3, os_string.len());
|
||||
|
||||
os_string.clear();
|
||||
assert_eq!(0, os_string.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_str_default() {
|
||||
let os_str: &OsStr = Default::default();
|
||||
assert_eq!("", os_str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_boxed() {
|
||||
let orig = "Hello, world!";
|
||||
let os_str = OsStr::new(orig);
|
||||
let boxed: Box<OsStr> = Box::from(os_str);
|
||||
let os_string = os_str.to_owned().into_boxed_os_str().into_os_string();
|
||||
assert_eq!(os_str, &*boxed);
|
||||
assert_eq!(&*boxed, &*os_string);
|
||||
assert_eq!(&*os_string, os_str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boxed_default() {
|
||||
let boxed = <Box<OsStr>>::default();
|
||||
assert!(boxed.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_str_clone_into() {
|
||||
let mut os_string = OsString::with_capacity(123);
|
||||
os_string.push("hello");
|
||||
let os_str = OsStr::new("bonjour");
|
||||
os_str.clone_into(&mut os_string);
|
||||
assert_eq!(os_str, os_string);
|
||||
assert!(os_string.capacity() >= 123);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_rc() {
|
||||
let orig = "Hello, world!";
|
||||
let os_str = OsStr::new(orig);
|
||||
let rc: Rc<OsStr> = Rc::from(os_str);
|
||||
let arc: Arc<OsStr> = Arc::from(os_str);
|
||||
|
||||
assert_eq!(&*rc, os_str);
|
||||
assert_eq!(&*arc, os_str);
|
||||
|
||||
let rc2: Rc<OsStr> = Rc::from(os_str.to_owned());
|
||||
let arc2: Arc<OsStr> = Arc::from(os_str.to_owned());
|
||||
|
||||
assert_eq!(&*rc2, os_str);
|
||||
assert_eq!(&*arc2, os_str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slice_encoded_bytes() {
|
||||
let os_str = OsStr::new("123θგ🦀");
|
||||
// ASCII
|
||||
let digits = os_str.slice_encoded_bytes(..3);
|
||||
assert_eq!(digits, "123");
|
||||
let three = os_str.slice_encoded_bytes(2..3);
|
||||
assert_eq!(three, "3");
|
||||
// 2-byte UTF-8
|
||||
let theta = os_str.slice_encoded_bytes(3..5);
|
||||
assert_eq!(theta, "θ");
|
||||
// 3-byte UTF-8
|
||||
let gani = os_str.slice_encoded_bytes(5..8);
|
||||
assert_eq!(gani, "გ");
|
||||
// 4-byte UTF-8
|
||||
let crab = os_str.slice_encoded_bytes(8..);
|
||||
assert_eq!(crab, "🦀");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn slice_out_of_bounds() {
|
||||
let crab = OsStr::new("🦀");
|
||||
let _ = crab.slice_encoded_bytes(..5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn slice_mid_char() {
|
||||
let crab = OsStr::new("🦀");
|
||||
let _ = crab.slice_encoded_bytes(..2);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
#[should_panic(expected = "byte index 1 is not an OsStr boundary")]
|
||||
fn slice_invalid_data() {
|
||||
use crate::os::unix::ffi::OsStrExt;
|
||||
|
||||
let os_string = OsStr::from_bytes(b"\xFF\xFF");
|
||||
let _ = os_string.slice_encoded_bytes(1..);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
#[should_panic(expected = "byte index 1 is not an OsStr boundary")]
|
||||
fn slice_partial_utf8() {
|
||||
use crate::os::unix::ffi::{OsStrExt, OsStringExt};
|
||||
|
||||
let part_crab = OsStr::from_bytes(&"🦀".as_bytes()[..3]);
|
||||
let mut os_string = OsString::from_vec(vec![0xFF]);
|
||||
os_string.push(part_crab);
|
||||
let _ = os_string.slice_encoded_bytes(1..);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn slice_invalid_edge() {
|
||||
use crate::os::unix::ffi::{OsStrExt, OsStringExt};
|
||||
|
||||
let os_string = OsStr::from_bytes(b"a\xFFa");
|
||||
assert_eq!(os_string.slice_encoded_bytes(..1), "a");
|
||||
assert_eq!(os_string.slice_encoded_bytes(1..), OsStr::from_bytes(b"\xFFa"));
|
||||
assert_eq!(os_string.slice_encoded_bytes(..2), OsStr::from_bytes(b"a\xFF"));
|
||||
assert_eq!(os_string.slice_encoded_bytes(2..), "a");
|
||||
|
||||
let os_string = OsStr::from_bytes(&"abc🦀".as_bytes()[..6]);
|
||||
assert_eq!(os_string.slice_encoded_bytes(..3), "abc");
|
||||
assert_eq!(os_string.slice_encoded_bytes(3..), OsStr::from_bytes(b"\xF0\x9F\xA6"));
|
||||
|
||||
let mut os_string = OsString::from_vec(vec![0xFF]);
|
||||
os_string.push("🦀");
|
||||
assert_eq!(os_string.slice_encoded_bytes(..1), OsStr::from_bytes(b"\xFF"));
|
||||
assert_eq!(os_string.slice_encoded_bytes(1..), "🦀");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
#[should_panic(expected = "byte index 3 lies between surrogate codepoints")]
|
||||
fn slice_between_surrogates() {
|
||||
use crate::os::windows::ffi::OsStringExt;
|
||||
|
||||
let os_string = OsString::from_wide(&[0xD800, 0xD800]);
|
||||
assert_eq!(os_string.as_encoded_bytes(), &[0xED, 0xA0, 0x80, 0xED, 0xA0, 0x80]);
|
||||
let _ = os_string.slice_encoded_bytes(..3);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn slice_surrogate_edge() {
|
||||
use crate::os::windows::ffi::OsStringExt;
|
||||
|
||||
let surrogate = OsString::from_wide(&[0xD800]);
|
||||
let mut pre_crab = surrogate.clone();
|
||||
pre_crab.push("🦀");
|
||||
assert_eq!(pre_crab.slice_encoded_bytes(..3), surrogate);
|
||||
assert_eq!(pre_crab.slice_encoded_bytes(3..), "🦀");
|
||||
|
||||
let mut post_crab = OsString::from("🦀");
|
||||
post_crab.push(&surrogate);
|
||||
assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀");
|
||||
assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone_to_uninit() {
|
||||
let a = OsStr::new("hello.txt");
|
||||
|
||||
let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<OsStr>(a)];
|
||||
unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) };
|
||||
assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() });
|
||||
|
||||
let mut b: Box<OsStr> = OsStr::new("world.exe").into();
|
||||
assert_eq!(size_of_val::<OsStr>(a), size_of_val::<OsStr>(&b));
|
||||
assert_ne!(a, &*b);
|
||||
unsafe { a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b).cast()) };
|
||||
assert_eq!(a, &*b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug() {
|
||||
let s = "'single quotes'";
|
||||
assert_eq!(format!("{:?}", OsStr::new(s)), format!("{:?}", s));
|
||||
}
|
||||
3531
crates/std/src/fs.rs
3531
crates/std/src/fs.rs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,91 +0,0 @@
|
||||
//! Generic hashing support.
|
||||
//!
|
||||
//! This module provides a generic way to compute the [hash] of a value.
|
||||
//! Hashes are most commonly used with [`HashMap`] and [`HashSet`].
|
||||
//!
|
||||
//! [hash]: https://en.wikipedia.org/wiki/Hash_function
|
||||
//! [`HashMap`]: ../../std/collections/struct.HashMap.html
|
||||
//! [`HashSet`]: ../../std/collections/struct.HashSet.html
|
||||
//!
|
||||
//! The simplest way to make a type hashable is to use `#[derive(Hash)]`:
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
//!
|
||||
//! #[derive(Hash)]
|
||||
//! struct Person {
|
||||
//! id: u32,
|
||||
//! name: String,
|
||||
//! phone: u64,
|
||||
//! }
|
||||
//!
|
||||
//! let person1 = Person {
|
||||
//! id: 5,
|
||||
//! name: "Janet".to_string(),
|
||||
//! phone: 555_666_7777,
|
||||
//! };
|
||||
//! let person2 = Person {
|
||||
//! id: 5,
|
||||
//! name: "Bob".to_string(),
|
||||
//! phone: 555_666_7777,
|
||||
//! };
|
||||
//!
|
||||
//! assert!(calculate_hash(&person1) != calculate_hash(&person2));
|
||||
//!
|
||||
//! fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
||||
//! let mut s = DefaultHasher::new();
|
||||
//! t.hash(&mut s);
|
||||
//! s.finish()
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! If you need more control over how a value is hashed, you need to implement
|
||||
//! the [`Hash`] trait:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
//!
|
||||
//! struct Person {
|
||||
//! id: u32,
|
||||
//! # #[allow(dead_code)]
|
||||
//! name: String,
|
||||
//! phone: u64,
|
||||
//! }
|
||||
//!
|
||||
//! impl Hash for Person {
|
||||
//! fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
//! self.id.hash(state);
|
||||
//! self.phone.hash(state);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! let person1 = Person {
|
||||
//! id: 5,
|
||||
//! name: "Janet".to_string(),
|
||||
//! phone: 555_666_7777,
|
||||
//! };
|
||||
//! let person2 = Person {
|
||||
//! id: 5,
|
||||
//! name: "Bob".to_string(),
|
||||
//! phone: 555_666_7777,
|
||||
//! };
|
||||
//!
|
||||
//! assert_eq!(calculate_hash(&person1), calculate_hash(&person2));
|
||||
//!
|
||||
//! fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
||||
//! let mut s = DefaultHasher::new();
|
||||
//! t.hash(&mut s);
|
||||
//! s.finish()
|
||||
//! }
|
||||
//! ```
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
pub(crate) mod random;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::hash::*;
|
||||
|
||||
#[stable(feature = "std_hash_exports", since = "1.76.0")]
|
||||
pub use self::random::{DefaultHasher, RandomState};
|
||||
@@ -1,159 +0,0 @@
|
||||
//! This module exists to isolate [`RandomState`] and [`DefaultHasher`] outside of the
|
||||
//! [`collections`] module without actually publicly exporting them, so that parts of that
|
||||
//! implementation can more easily be moved to the [`alloc`] crate.
|
||||
//!
|
||||
//! Although its items are public and contain stability attributes, they can't actually be accessed
|
||||
//! outside this crate.
|
||||
//!
|
||||
//! [`collections`]: crate::collections
|
||||
|
||||
use super::{BuildHasher, Hasher, SipHasher13};
|
||||
use crate::cell::Cell;
|
||||
use crate::fmt;
|
||||
use crate::sys::random::hashmap_random_keys;
|
||||
|
||||
/// `RandomState` is the default state for [`HashMap`] types.
|
||||
///
|
||||
/// A particular instance `RandomState` will create the same instances of
|
||||
/// [`Hasher`], but the hashers created by two different `RandomState`
|
||||
/// instances are unlikely to produce the same result for the same values.
|
||||
///
|
||||
/// [`HashMap`]: crate::collections::HashMap
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashMap;
|
||||
/// use std::hash::RandomState;
|
||||
///
|
||||
/// let s = RandomState::new();
|
||||
/// let mut map = HashMap::with_hasher(s);
|
||||
/// map.insert(1, 2);
|
||||
/// ```
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
#[derive(Clone)]
|
||||
pub struct RandomState {
|
||||
k0: u64,
|
||||
k1: u64,
|
||||
}
|
||||
|
||||
impl RandomState {
|
||||
/// Constructs a new `RandomState` that is initialized with random keys.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::hash::RandomState;
|
||||
///
|
||||
/// let s = RandomState::new();
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(deprecated)]
|
||||
// rand
|
||||
#[must_use]
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
pub fn new() -> RandomState {
|
||||
// Historically this function did not cache keys from the OS and instead
|
||||
// simply always called `rand::thread_rng().gen()` twice. In #31356 it
|
||||
// was discovered, however, that because we re-seed the thread-local RNG
|
||||
// from the OS periodically that this can cause excessive slowdown when
|
||||
// many hash maps are created on a thread. To solve this performance
|
||||
// trap we cache the first set of randomly generated keys per-thread.
|
||||
//
|
||||
// Later in #36481 it was discovered that exposing a deterministic
|
||||
// iteration order allows a form of DOS attack. To counter that we
|
||||
// increment one of the seeds on every RandomState creation, giving
|
||||
// every corresponding HashMap a different iteration order.
|
||||
thread_local!(static KEYS: Cell<(u64, u64)> = {
|
||||
Cell::new(hashmap_random_keys())
|
||||
});
|
||||
|
||||
KEYS.with(|keys| {
|
||||
let (k0, k1) = keys.get();
|
||||
keys.set((k0.wrapping_add(1), k1));
|
||||
RandomState { k0, k1 }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
impl BuildHasher for RandomState {
|
||||
type Hasher = DefaultHasher;
|
||||
#[inline]
|
||||
fn build_hasher(&self) -> DefaultHasher {
|
||||
DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1))
|
||||
}
|
||||
}
|
||||
|
||||
/// The default [`Hasher`] used by [`RandomState`].
|
||||
///
|
||||
/// The internal algorithm is not specified, and so it and its hashes should
|
||||
/// not be relied upon over releases.
|
||||
#[derive(Clone, Debug)]
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
pub struct DefaultHasher(SipHasher13);
|
||||
|
||||
impl DefaultHasher {
|
||||
/// Creates a new `DefaultHasher`.
|
||||
///
|
||||
/// This hasher is not guaranteed to be the same as all other
|
||||
/// `DefaultHasher` instances, but is the same as all other `DefaultHasher`
|
||||
/// instances created through `new` or `default`.
|
||||
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "const_default", issue = "143894")]
|
||||
#[must_use]
|
||||
pub const fn new() -> DefaultHasher {
|
||||
DefaultHasher(SipHasher13::new_with_keys(0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
|
||||
#[rustc_const_unstable(feature = "const_default", issue = "143894")]
|
||||
impl const Default for DefaultHasher {
|
||||
/// Creates a new `DefaultHasher` using [`new`].
|
||||
/// See its documentation for more.
|
||||
///
|
||||
/// [`new`]: DefaultHasher::new
|
||||
#[inline]
|
||||
fn default() -> DefaultHasher {
|
||||
DefaultHasher::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
|
||||
impl Hasher for DefaultHasher {
|
||||
// The underlying `SipHasher13` doesn't override the other
|
||||
// `write_*` methods, so it's ok not to forward them here.
|
||||
|
||||
#[inline]
|
||||
fn write(&mut self, msg: &[u8]) {
|
||||
self.0.write(msg)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_str(&mut self, s: &str) {
|
||||
self.0.write_str(s);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn finish(&self) -> u64 {
|
||||
self.0.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
impl Default for RandomState {
|
||||
/// Constructs a new `RandomState`.
|
||||
#[inline]
|
||||
fn default() -> RandomState {
|
||||
RandomState::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for RandomState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("RandomState").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
@@ -1,601 +0,0 @@
|
||||
mod buffer;
|
||||
|
||||
use buffer::Buffer;
|
||||
|
||||
use crate::fmt;
|
||||
use crate::io::{
|
||||
self, BorrowedCursor, BufRead, DEFAULT_BUF_SIZE, IoSliceMut, Read, Seek, SeekFrom, SizeHint,
|
||||
SpecReadByte, uninlined_slow_read_byte,
|
||||
};
|
||||
|
||||
/// The `BufReader<R>` struct adds buffering to any reader.
|
||||
///
|
||||
/// It can be excessively inefficient to work directly with a [`Read`] instance.
|
||||
/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`]
|
||||
/// results in a system call. A `BufReader<R>` performs large, infrequent reads on
|
||||
/// the underlying [`Read`] and maintains an in-memory buffer of the results.
|
||||
///
|
||||
/// `BufReader<R>` can improve the speed of programs that make *small* and
|
||||
/// *repeated* read calls to the same file or network socket. It does not
|
||||
/// help when reading very large amounts at once, or reading just one or a few
|
||||
/// times. It also provides no advantage when reading from a source that is
|
||||
/// already in memory, like a <code>[Vec]\<u8></code>.
|
||||
///
|
||||
/// When the `BufReader<R>` is dropped, the contents of its buffer will be
|
||||
/// discarded. Creating multiple instances of a `BufReader<R>` on the same
|
||||
/// stream can cause data loss. Reading from the underlying reader after
|
||||
/// unwrapping the `BufReader<R>` with [`BufReader::into_inner`] can also cause
|
||||
/// data loss.
|
||||
///
|
||||
/// [`TcpStream::read`]: crate::net::TcpStream::read
|
||||
/// [`TcpStream`]: crate::net::TcpStream
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f);
|
||||
///
|
||||
/// let mut line = String::new();
|
||||
/// let len = reader.read_line(&mut line)?;
|
||||
/// println!("First line is {len} bytes long");
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct BufReader<R: ?Sized> {
|
||||
buf: Buffer,
|
||||
inner: R,
|
||||
}
|
||||
|
||||
impl<R: Read> BufReader<R> {
|
||||
/// Creates a new `BufReader<R>` with a default buffer capacity. The default is currently 8 KiB,
|
||||
/// but may change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let reader = BufReader::new(f);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(inner: R) -> BufReader<R> {
|
||||
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||
}
|
||||
|
||||
pub(crate) fn try_new_buffer() -> io::Result<Buffer> {
|
||||
Buffer::try_with_capacity(DEFAULT_BUF_SIZE)
|
||||
}
|
||||
|
||||
pub(crate) fn with_buffer(inner: R, buf: Buffer) -> Self {
|
||||
Self { inner, buf }
|
||||
}
|
||||
|
||||
/// Creates a new `BufReader<R>` with the specified buffer capacity.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a buffer with ten bytes of capacity:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let reader = BufReader::with_capacity(10, f);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> {
|
||||
BufReader {
|
||||
inner,
|
||||
buf: Buffer::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + ?Sized> BufReader<R> {
|
||||
/// Attempt to look ahead `n` bytes.
|
||||
///
|
||||
/// `n` must be less than or equal to `capacity`.
|
||||
///
|
||||
/// The returned slice may be less than `n` bytes long if
|
||||
/// end of file is reached.
|
||||
///
|
||||
/// After calling this method, you may call [`consume`](BufRead::consume)
|
||||
/// with a value less than or equal to `n` to advance over some or all of
|
||||
/// the returned bytes.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(bufreader_peek)]
|
||||
/// use std::io::{Read, BufReader};
|
||||
///
|
||||
/// let mut bytes = &b"oh, hello there"[..];
|
||||
/// let mut rdr = BufReader::with_capacity(6, &mut bytes);
|
||||
/// assert_eq!(rdr.peek(2).unwrap(), b"oh");
|
||||
/// let mut buf = [0; 4];
|
||||
/// rdr.read(&mut buf[..]).unwrap();
|
||||
/// assert_eq!(&buf, b"oh, ");
|
||||
/// assert_eq!(rdr.peek(5).unwrap(), b"hello");
|
||||
/// let mut s = String::new();
|
||||
/// rdr.read_to_string(&mut s).unwrap();
|
||||
/// assert_eq!(&s, "hello there");
|
||||
/// assert_eq!(rdr.peek(1).unwrap().len(), 0);
|
||||
/// ```
|
||||
#[unstable(feature = "bufreader_peek", issue = "128405")]
|
||||
pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> {
|
||||
assert!(n <= self.capacity());
|
||||
while n > self.buf.buffer().len() {
|
||||
if self.buf.pos() > 0 {
|
||||
self.buf.backshift();
|
||||
}
|
||||
let new = self.buf.read_more(&mut self.inner)?;
|
||||
if new == 0 {
|
||||
// end of file, no more bytes to read
|
||||
return Ok(&self.buf.buffer()[..]);
|
||||
}
|
||||
debug_assert_eq!(self.buf.pos(), 0);
|
||||
}
|
||||
Ok(&self.buf.buffer()[..n])
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ?Sized> BufReader<R> {
|
||||
/// Gets a reference to the underlying reader.
|
||||
///
|
||||
/// It is inadvisable to directly read from the underlying reader.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f1 = File::open("log.txt")?;
|
||||
/// let reader = BufReader::new(f1);
|
||||
///
|
||||
/// let f2 = reader.get_ref();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_ref(&self) -> &R {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying reader.
|
||||
///
|
||||
/// It is inadvisable to directly read from the underlying reader.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f1 = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f1);
|
||||
///
|
||||
/// let f2 = reader.get_mut();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_mut(&mut self) -> &mut R {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns a reference to the internally buffered data.
|
||||
///
|
||||
/// Unlike [`fill_buf`], this will not attempt to fill the buffer if it is empty.
|
||||
///
|
||||
/// [`fill_buf`]: BufRead::fill_buf
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::{BufReader, BufRead};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f);
|
||||
/// assert!(reader.buffer().is_empty());
|
||||
///
|
||||
/// if reader.fill_buf()?.len() > 0 {
|
||||
/// assert!(!reader.buffer().is_empty());
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "bufreader_buffer", since = "1.37.0")]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
self.buf.buffer()
|
||||
}
|
||||
|
||||
/// Returns the number of bytes the internal buffer can hold at once.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::{BufReader, BufRead};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f);
|
||||
///
|
||||
/// let capacity = reader.capacity();
|
||||
/// let buffer = reader.fill_buf()?;
|
||||
/// assert!(buffer.len() <= capacity);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "buffered_io_capacity", since = "1.46.0")]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.capacity()
|
||||
}
|
||||
|
||||
/// Unwraps this `BufReader<R>`, returning the underlying reader.
|
||||
///
|
||||
/// Note that any leftover data in the internal buffer is lost. Therefore,
|
||||
/// a following read from the underlying reader may lead to data loss.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f1 = File::open("log.txt")?;
|
||||
/// let reader = BufReader::new(f1);
|
||||
///
|
||||
/// let f2 = reader.into_inner();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> R
|
||||
where
|
||||
R: Sized,
|
||||
{
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Invalidates all data in the internal buffer.
|
||||
#[inline]
|
||||
pub(in crate::io) fn discard_buffer(&mut self) {
|
||||
self.buf.discard_buffer()
|
||||
}
|
||||
}
|
||||
|
||||
// This is only used by a test which asserts that the initialization-tracking is correct.
|
||||
#[cfg(test)]
|
||||
impl<R: ?Sized> BufReader<R> {
|
||||
#[allow(missing_docs)]
|
||||
pub fn initialized(&self) -> usize {
|
||||
self.buf.initialized()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Seek> BufReader<R> {
|
||||
/// Seeks relative to the current position. If the new position lies within the buffer,
|
||||
/// the buffer will not be flushed, allowing for more efficient seeks.
|
||||
/// This method does not return the location of the underlying reader, so the caller
|
||||
/// must track this information themselves if it is required.
|
||||
#[stable(feature = "bufreader_seek_relative", since = "1.53.0")]
|
||||
pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
let pos = self.buf.pos() as u64;
|
||||
if offset < 0 {
|
||||
if let Some(_) = pos.checked_sub((-offset) as u64) {
|
||||
self.buf.unconsume((-offset) as usize);
|
||||
return Ok(());
|
||||
}
|
||||
} else if let Some(new_pos) = pos.checked_add(offset as u64) {
|
||||
if new_pos <= self.buf.filled() as u64 {
|
||||
self.buf.consume(offset as usize);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
self.seek(SeekFrom::Current(offset)).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> SpecReadByte for BufReader<R>
|
||||
where
|
||||
Self: Read,
|
||||
{
|
||||
#[inline]
|
||||
fn spec_read_byte(&mut self) -> Option<io::Result<u8>> {
|
||||
let mut byte = 0;
|
||||
if self.buf.consume_with(1, |claimed| byte = claimed[0]) {
|
||||
return Some(Ok(byte));
|
||||
}
|
||||
|
||||
// Fallback case, only reached once per buffer refill.
|
||||
uninlined_slow_read_byte(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: ?Sized + Read> Read for BufReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// If we don't have any buffered data and we're doing a massive read
|
||||
// (larger than our internal buffer), bypass our internal buffer
|
||||
// entirely.
|
||||
if self.buf.pos() == self.buf.filled() && buf.len() >= self.capacity() {
|
||||
self.discard_buffer();
|
||||
return self.inner.read(buf);
|
||||
}
|
||||
let mut rem = self.fill_buf()?;
|
||||
let nread = rem.read(buf)?;
|
||||
self.consume(nread);
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
// If we don't have any buffered data and we're doing a massive read
|
||||
// (larger than our internal buffer), bypass our internal buffer
|
||||
// entirely.
|
||||
if self.buf.pos() == self.buf.filled() && cursor.capacity() >= self.capacity() {
|
||||
self.discard_buffer();
|
||||
return self.inner.read_buf(cursor);
|
||||
}
|
||||
|
||||
let prev = cursor.written();
|
||||
|
||||
let mut rem = self.fill_buf()?;
|
||||
rem.read_buf(cursor.reborrow())?; // actually never fails
|
||||
|
||||
self.consume(cursor.written() - prev); //slice impl of read_buf known to never unfill buf
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Small read_exacts from a BufReader are extremely common when used with a deserializer.
|
||||
// The default implementation calls read in a loop, which results in surprisingly poor code
|
||||
// generation for the common path where the buffer has enough bytes to fill the passed-in
|
||||
// buffer.
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
if self
|
||||
.buf
|
||||
.consume_with(buf.len(), |claimed| buf.copy_from_slice(claimed))
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
crate::io::default_read_exact(self, buf)
|
||||
}
|
||||
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
if self
|
||||
.buf
|
||||
.consume_with(cursor.capacity(), |claimed| cursor.append(claimed))
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
crate::io::default_read_buf_exact(self, cursor)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
|
||||
if self.buf.pos() == self.buf.filled() && total_len >= self.capacity() {
|
||||
self.discard_buffer();
|
||||
return self.inner.read_vectored(bufs);
|
||||
}
|
||||
let mut rem = self.fill_buf()?;
|
||||
let nread = rem.read_vectored(bufs)?;
|
||||
|
||||
self.consume(nread);
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
self.inner.is_read_vectored()
|
||||
}
|
||||
|
||||
// The inner reader might have an optimized `read_to_end`. Drain our buffer and then
|
||||
// delegate to the inner implementation.
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let inner_buf = self.buffer();
|
||||
buf.try_reserve(inner_buf.len())?;
|
||||
buf.extend_from_slice(inner_buf);
|
||||
let nread = inner_buf.len();
|
||||
self.discard_buffer();
|
||||
Ok(nread + self.inner.read_to_end(buf)?)
|
||||
}
|
||||
|
||||
// The inner reader might have an optimized `read_to_end`. Drain our buffer and then
|
||||
// delegate to the inner implementation.
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
// In the general `else` case below we must read bytes into a side buffer, check
|
||||
// that they are valid UTF-8, and then append them to `buf`. This requires a
|
||||
// potentially large memcpy.
|
||||
//
|
||||
// If `buf` is empty--the most common case--we can leverage `append_to_string`
|
||||
// to read directly into `buf`'s internal byte buffer, saving an allocation and
|
||||
// a memcpy.
|
||||
if buf.is_empty() {
|
||||
// `append_to_string`'s safety relies on the buffer only being appended to since
|
||||
// it only checks the UTF-8 validity of new data. If there were existing content in
|
||||
// `buf` then an untrustworthy reader (i.e. `self.inner`) could not only append
|
||||
// bytes but also modify existing bytes and render them invalid. On the other hand,
|
||||
// if `buf` is empty then by definition any writes must be appends and
|
||||
// `append_to_string` will validate all of the new bytes.
|
||||
unsafe { crate::io::append_to_string(buf, |b| self.read_to_end(b)) }
|
||||
} else {
|
||||
// We cannot append our byte buffer directly onto the `buf` String as there could
|
||||
// be an incomplete UTF-8 sequence that has only been partially read. We must read
|
||||
// everything into a side buffer first and then call `from_utf8` on the complete
|
||||
// buffer.
|
||||
let mut bytes = Vec::new();
|
||||
self.read_to_end(&mut bytes)?;
|
||||
let string = crate::str::from_utf8(&bytes).map_err(|_| io::Error::INVALID_UTF8)?;
|
||||
*buf += string;
|
||||
Ok(string.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: ?Sized + Read> BufRead for BufReader<R> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
self.buf.fill_buf(&mut self.inner)
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.buf.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R> fmt::Debug for BufReader<R>
|
||||
where
|
||||
R: ?Sized + fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("BufReader")
|
||||
.field("reader", &&self.inner)
|
||||
.field(
|
||||
"buffer",
|
||||
&format_args!("{}/{}", self.buf.filled() - self.buf.pos(), self.capacity()),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: ?Sized + Seek> Seek for BufReader<R> {
|
||||
/// Seek to an offset, in bytes, in the underlying reader.
|
||||
///
|
||||
/// The position used for seeking with <code>[SeekFrom::Current]\(_)</code> is the
|
||||
/// position the underlying reader would be at if the `BufReader<R>` had no
|
||||
/// internal buffer.
|
||||
///
|
||||
/// Seeking always discards the internal buffer, even if the seek position
|
||||
/// would otherwise fall within it. This guarantees that calling
|
||||
/// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader
|
||||
/// at the same position.
|
||||
///
|
||||
/// To seek without discarding the internal buffer, use [`BufReader::seek_relative`].
|
||||
///
|
||||
/// See [`std::io::Seek`] for more details.
|
||||
///
|
||||
/// Note: In the edge case where you're seeking with <code>[SeekFrom::Current]\(n)</code>
|
||||
/// where `n` minus the internal buffer length overflows an `i64`, two
|
||||
/// seeks will be performed instead of one. If the second seek returns
|
||||
/// [`Err`], the underlying reader will be left at the same position it would
|
||||
/// have if you called `seek` with <code>[SeekFrom::Current]\(0)</code>.
|
||||
///
|
||||
/// [`std::io::Seek`]: Seek
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
let result: u64;
|
||||
if let SeekFrom::Current(n) = pos {
|
||||
let remainder = (self.buf.filled() - self.buf.pos()) as i64;
|
||||
// it should be safe to assume that remainder fits within an i64 as the alternative
|
||||
// means we managed to allocate 8 exbibytes and that's absurd.
|
||||
// But it's not out of the realm of possibility for some weird underlying reader to
|
||||
// support seeking by i64::MIN so we need to handle underflow when subtracting
|
||||
// remainder.
|
||||
if let Some(offset) = n.checked_sub(remainder) {
|
||||
result = self.inner.seek(SeekFrom::Current(offset))?;
|
||||
} else {
|
||||
// seek backwards by our remainder, and then by the offset
|
||||
self.inner.seek(SeekFrom::Current(-remainder))?;
|
||||
self.discard_buffer();
|
||||
result = self.inner.seek(SeekFrom::Current(n))?;
|
||||
}
|
||||
} else {
|
||||
// Seeking with Start/End doesn't care about our buffer length.
|
||||
result = self.inner.seek(pos)?;
|
||||
}
|
||||
self.discard_buffer();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Returns the current seek position from the start of the stream.
|
||||
///
|
||||
/// The value returned is equivalent to `self.seek(SeekFrom::Current(0))`
|
||||
/// but does not flush the internal buffer. Due to this optimization the
|
||||
/// function does not guarantee that calling `.into_inner()` immediately
|
||||
/// afterwards will yield the underlying reader at the same position. Use
|
||||
/// [`BufReader::seek`] instead if you require that guarantee.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the position of the inner reader is smaller
|
||||
/// than the amount of buffered data. That can happen if the inner reader
|
||||
/// has an incorrect implementation of [`Seek::stream_position`], or if the
|
||||
/// position has gone out of sync due to calling [`Seek::seek`] directly on
|
||||
/// the underlying reader.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::{
|
||||
/// io::{self, BufRead, BufReader, Seek},
|
||||
/// fs::File,
|
||||
/// };
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut f = BufReader::new(File::open("foo.txt")?);
|
||||
///
|
||||
/// let before = f.stream_position()?;
|
||||
/// f.read_line(&mut String::new())?;
|
||||
/// let after = f.stream_position()?;
|
||||
///
|
||||
/// println!("The first line was {} bytes long", after - before);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
let remainder = (self.buf.filled() - self.buf.pos()) as u64;
|
||||
self.inner.stream_position().map(|pos| {
|
||||
pos.checked_sub(remainder).expect(
|
||||
"overflow when subtracting remaining buffer size from inner stream position",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Seeks relative to the current position.
|
||||
///
|
||||
/// If the new position lies within the buffer, the buffer will not be
|
||||
/// flushed, allowing for more efficient seeks. This method does not return
|
||||
/// the location of the underlying reader, so the caller must track this
|
||||
/// information themselves if it is required.
|
||||
fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
self.seek_relative(offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> SizeHint for BufReader<T> {
|
||||
#[inline]
|
||||
fn lower_bound(&self) -> usize {
|
||||
SizeHint::lower_bound(self.get_ref()) + self.buffer().len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn upper_bound(&self) -> Option<usize> {
|
||||
SizeHint::upper_bound(self.get_ref()).and_then(|up| self.buffer().len().checked_add(up))
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
//! An encapsulation of `BufReader`'s buffer management logic.
|
||||
//!
|
||||
//! This module factors out the basic functionality of `BufReader` in order to protect two core
|
||||
//! invariants:
|
||||
//! * `filled` bytes of `buf` are always initialized
|
||||
//! * `pos` is always <= `filled`
|
||||
//! Since this module encapsulates the buffer management logic, we can ensure that the range
|
||||
//! `pos..filled` is always a valid index into the initialized region of the buffer. This means
|
||||
//! that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so
|
||||
//! without encountering any runtime bounds checks.
|
||||
|
||||
use crate::cmp;
|
||||
use crate::io::{self, BorrowedBuf, ErrorKind, Read};
|
||||
use crate::mem::MaybeUninit;
|
||||
|
||||
pub struct Buffer {
|
||||
// The buffer.
|
||||
buf: Box<[MaybeUninit<u8>]>,
|
||||
// The current seek offset into `buf`, must always be <= `filled`.
|
||||
pos: usize,
|
||||
// Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are
|
||||
// initialized with bytes from a read.
|
||||
filled: usize,
|
||||
// This is the max number of bytes returned across all `fill_buf` calls. We track this so that we
|
||||
// can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much of its
|
||||
// defensive initialization as possible. Note that while this often the same as `filled`, it
|
||||
// doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and
|
||||
// omitting this is a huge perf regression for `Read` impls that do not.
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let buf = Box::new_uninit_slice(capacity);
|
||||
Self { buf, pos: 0, filled: 0, initialized: 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_with_capacity(capacity: usize) -> io::Result<Self> {
|
||||
match Box::try_new_uninit_slice(capacity) {
|
||||
Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }),
|
||||
Err(_) => {
|
||||
Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
// SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and
|
||||
// that region is initialized because those are all invariants of this type.
|
||||
unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn filled(&self) -> usize {
|
||||
self.filled
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pos(&self) -> usize {
|
||||
self.pos
|
||||
}
|
||||
|
||||
// This is only used by a test which asserts that the initialization-tracking is correct.
|
||||
#[cfg(test)]
|
||||
pub fn initialized(&self) -> usize {
|
||||
self.initialized
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn discard_buffer(&mut self) {
|
||||
self.pos = 0;
|
||||
self.filled = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn consume(&mut self, amt: usize) {
|
||||
self.pos = cmp::min(self.pos + amt, self.filled);
|
||||
}
|
||||
|
||||
/// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to
|
||||
/// `visitor` and return true. If there are not enough bytes available, return false.
|
||||
#[inline]
|
||||
pub fn consume_with<V>(&mut self, amt: usize, mut visitor: V) -> bool
|
||||
where
|
||||
V: FnMut(&[u8]),
|
||||
{
|
||||
if let Some(claimed) = self.buffer().get(..amt) {
|
||||
visitor(claimed);
|
||||
// If the indexing into self.buffer() succeeds, amt must be a valid increment.
|
||||
self.pos += amt;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unconsume(&mut self, amt: usize) {
|
||||
self.pos = self.pos.saturating_sub(amt);
|
||||
}
|
||||
|
||||
/// Read more bytes into the buffer without discarding any of its contents
|
||||
pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<usize> {
|
||||
let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]);
|
||||
let old_init = self.initialized - self.filled;
|
||||
unsafe {
|
||||
buf.set_init(old_init);
|
||||
}
|
||||
reader.read_buf(buf.unfilled())?;
|
||||
self.filled += buf.len();
|
||||
self.initialized += buf.init_len() - old_init;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
/// Remove bytes that have already been read from the buffer.
|
||||
pub fn backshift(&mut self) {
|
||||
self.buf.copy_within(self.pos..self.filled, 0);
|
||||
self.filled -= self.pos;
|
||||
self.pos = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
|
||||
// If we've reached the end of our internal buffer then we need to fetch
|
||||
// some more data from the reader.
|
||||
// Branch using `>=` instead of the more correct `==`
|
||||
// to tell the compiler that the pos..cap slice is always valid.
|
||||
if self.pos >= self.filled {
|
||||
debug_assert!(self.pos == self.filled);
|
||||
|
||||
let mut buf = BorrowedBuf::from(&mut *self.buf);
|
||||
// SAFETY: `self.filled` bytes will always have been initialized.
|
||||
unsafe {
|
||||
buf.set_init(self.initialized);
|
||||
}
|
||||
|
||||
let result = reader.read_buf(buf.unfilled());
|
||||
|
||||
self.pos = 0;
|
||||
self.filled = buf.len();
|
||||
self.initialized = buf.init_len();
|
||||
|
||||
result?;
|
||||
}
|
||||
Ok(self.buffer())
|
||||
}
|
||||
}
|
||||
@@ -1,680 +0,0 @@
|
||||
use crate::io::{
|
||||
self, DEFAULT_BUF_SIZE, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write,
|
||||
};
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::{error, fmt, ptr};
|
||||
|
||||
/// Wraps a writer and buffers its output.
|
||||
///
|
||||
/// It can be excessively inefficient to work directly with something that
|
||||
/// implements [`Write`]. For example, every call to
|
||||
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
|
||||
/// `BufWriter<W>` keeps an in-memory buffer of data and writes it to an underlying
|
||||
/// writer in large, infrequent batches.
|
||||
///
|
||||
/// `BufWriter<W>` can improve the speed of programs that make *small* and
|
||||
/// *repeated* write calls to the same file or network socket. It does not
|
||||
/// help when writing very large amounts at once, or writing just one or a few
|
||||
/// times. It also provides no advantage when writing to a destination that is
|
||||
/// in memory, like a <code>[Vec]\<u8></code>.
|
||||
///
|
||||
/// It is critical to call [`flush`] before `BufWriter<W>` is dropped. Though
|
||||
/// dropping will attempt to flush the contents of the buffer, any errors
|
||||
/// that happen in the process of dropping will be ignored. Calling [`flush`]
|
||||
/// ensures that the buffer is empty and thus dropping will not even attempt
|
||||
/// file operations.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Let's write the numbers one through ten to a [`TcpStream`]:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// stream.write(&[i+1]).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Because we're not buffering, we write each one in turn, incurring the
|
||||
/// overhead of a system call per byte written. We can fix this with a
|
||||
/// `BufWriter<W>`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// stream.write(&[i+1]).unwrap();
|
||||
/// }
|
||||
/// stream.flush().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// By wrapping the stream with a `BufWriter<W>`, these ten writes are all grouped
|
||||
/// together by the buffer and will all be written out in one system call when
|
||||
/// the `stream` is flushed.
|
||||
///
|
||||
/// [`TcpStream::write`]: crate::net::TcpStream::write
|
||||
/// [`TcpStream`]: crate::net::TcpStream
|
||||
/// [`flush`]: BufWriter::flush
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct BufWriter<W: ?Sized + Write> {
|
||||
// The buffer. Avoid using this like a normal `Vec` in common code paths.
|
||||
// That is, don't use `buf.push`, `buf.extend_from_slice`, or any other
|
||||
// methods that require bounds checking or the like. This makes an enormous
|
||||
// difference to performance (we may want to stop using a `Vec` entirely).
|
||||
buf: Vec<u8>,
|
||||
// #30888: If the inner writer panics in a call to write, we don't want to
|
||||
// write the buffered data a second time in BufWriter's destructor. This
|
||||
// flag tells the Drop impl if it should skip the flush.
|
||||
panicked: bool,
|
||||
inner: W,
|
||||
}
|
||||
|
||||
impl<W: Write> BufWriter<W> {
|
||||
/// Creates a new `BufWriter<W>` with a default buffer capacity. The default is currently 8 KiB,
|
||||
/// but may change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(inner: W) -> BufWriter<W> {
|
||||
BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||
}
|
||||
|
||||
pub(crate) fn try_new_buffer() -> io::Result<Vec<u8>> {
|
||||
Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| {
|
||||
io::const_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer")
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn with_buffer(inner: W, buf: Vec<u8>) -> Self {
|
||||
Self { inner, buf, panicked: false }
|
||||
}
|
||||
|
||||
/// Creates a new `BufWriter<W>` with at least the specified buffer capacity.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a buffer with a buffer of at least a hundred bytes.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let stream = TcpStream::connect("127.0.0.1:34254").unwrap();
|
||||
/// let mut buffer = BufWriter::with_capacity(100, stream);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize, inner: W) -> BufWriter<W> {
|
||||
BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false }
|
||||
}
|
||||
|
||||
/// Unwraps this `BufWriter<W>`, returning the underlying writer.
|
||||
///
|
||||
/// The buffer is written out before returning the writer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An [`Err`] will be returned if an error occurs while flushing the buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // unwrap the TcpStream and flush the buffer
|
||||
/// let stream = buffer.into_inner().unwrap();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
|
||||
match self.flush_buf() {
|
||||
Err(e) => Err(IntoInnerError::new(self, e)),
|
||||
Ok(()) => Ok(self.into_parts().0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Disassembles this `BufWriter<W>`, returning the underlying writer, and any buffered but
|
||||
/// unwritten data.
|
||||
///
|
||||
/// If the underlying writer panicked, it is not known what portion of the data was written.
|
||||
/// In this case, we return `WriterPanicked` for the buffered data (from which the buffer
|
||||
/// contents can still be recovered).
|
||||
///
|
||||
/// `into_parts` makes no attempt to flush data and cannot fail.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::{BufWriter, Write};
|
||||
///
|
||||
/// let mut buffer = [0u8; 10];
|
||||
/// let mut stream = BufWriter::new(buffer.as_mut());
|
||||
/// write!(stream, "too much data").unwrap();
|
||||
/// stream.flush().expect_err("it doesn't fit");
|
||||
/// let (recovered_writer, buffered_data) = stream.into_parts();
|
||||
/// assert_eq!(recovered_writer.len(), 0);
|
||||
/// assert_eq!(&buffered_data.unwrap(), b"ata");
|
||||
/// ```
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub fn into_parts(self) -> (W, Result<Vec<u8>, WriterPanicked>) {
|
||||
let mut this = ManuallyDrop::new(self);
|
||||
let buf = mem::take(&mut this.buf);
|
||||
let buf = if !this.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
|
||||
|
||||
// SAFETY: double-drops are prevented by putting `this` in a ManuallyDrop that is never dropped
|
||||
let inner = unsafe { ptr::read(&this.inner) };
|
||||
|
||||
(inner, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Write> BufWriter<W> {
|
||||
/// Send data in our local buffer into the inner writer, looping as
|
||||
/// necessary until either it's all been sent or an error occurs.
|
||||
///
|
||||
/// Because all the data in the buffer has been reported to our owner as
|
||||
/// "successfully written" (by returning nonzero success values from
|
||||
/// `write`), any 0-length writes from `inner` must be reported as i/o
|
||||
/// errors from this method.
|
||||
pub(in crate::io) fn flush_buf(&mut self) -> io::Result<()> {
|
||||
/// Helper struct to ensure the buffer is updated after all the writes
|
||||
/// are complete. It tracks the number of written bytes and drains them
|
||||
/// all from the front of the buffer when dropped.
|
||||
struct BufGuard<'a> {
|
||||
buffer: &'a mut Vec<u8>,
|
||||
written: usize,
|
||||
}
|
||||
|
||||
impl<'a> BufGuard<'a> {
|
||||
fn new(buffer: &'a mut Vec<u8>) -> Self {
|
||||
Self { buffer, written: 0 }
|
||||
}
|
||||
|
||||
/// The unwritten part of the buffer
|
||||
fn remaining(&self) -> &[u8] {
|
||||
&self.buffer[self.written..]
|
||||
}
|
||||
|
||||
/// Flag some bytes as removed from the front of the buffer
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.written += amt;
|
||||
}
|
||||
|
||||
/// true if all of the bytes have been written
|
||||
fn done(&self) -> bool {
|
||||
self.written >= self.buffer.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BufGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
if self.written > 0 {
|
||||
self.buffer.drain(..self.written);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut guard = BufGuard::new(&mut self.buf);
|
||||
while !guard.done() {
|
||||
self.panicked = true;
|
||||
let r = self.inner.write(guard.remaining());
|
||||
self.panicked = false;
|
||||
|
||||
match r {
|
||||
Ok(0) => {
|
||||
return Err(io::const_error!(
|
||||
ErrorKind::WriteZero,
|
||||
"failed to write the buffered data",
|
||||
));
|
||||
}
|
||||
Ok(n) => guard.consume(n),
|
||||
Err(ref e) if e.is_interrupted() => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Buffer some data without flushing it, regardless of the size of the
|
||||
/// data. Writes as much as possible without exceeding capacity. Returns
|
||||
/// the number of bytes written.
|
||||
pub(super) fn write_to_buf(&mut self, buf: &[u8]) -> usize {
|
||||
let available = self.spare_capacity();
|
||||
let amt_to_buffer = available.min(buf.len());
|
||||
|
||||
// SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(&buf[..amt_to_buffer]);
|
||||
}
|
||||
|
||||
amt_to_buffer
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying writer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // we can use reference just like buffer
|
||||
/// let reference = buffer.get_ref();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_ref(&self) -> &W {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying writer.
|
||||
///
|
||||
/// It is inadvisable to directly write to the underlying writer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // we can use reference just like buffer
|
||||
/// let reference = buffer.get_mut();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_mut(&mut self) -> &mut W {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns a reference to the internally buffered data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // See how many bytes are currently buffered
|
||||
/// let bytes_buffered = buf_writer.buffer().len();
|
||||
/// ```
|
||||
#[stable(feature = "bufreader_buffer", since = "1.37.0")]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the internal buffer.
|
||||
///
|
||||
/// This can be used to write data directly into the buffer without triggering writers
|
||||
/// to the underlying writer.
|
||||
///
|
||||
/// That the buffer is a `Vec` is an implementation detail.
|
||||
/// Callers should not modify the capacity as there currently is no public API to do so
|
||||
/// and thus any capacity changes would be unexpected by the user.
|
||||
pub(in crate::io) fn buffer_mut(&mut self) -> &mut Vec<u8> {
|
||||
&mut self.buf
|
||||
}
|
||||
|
||||
/// Returns the number of bytes the internal buffer can hold without flushing.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // Check the capacity of the inner buffer
|
||||
/// let capacity = buf_writer.capacity();
|
||||
/// // Calculate how many bytes can be written without flushing
|
||||
/// let without_flush = capacity - buf_writer.buffer().len();
|
||||
/// ```
|
||||
#[stable(feature = "buffered_io_capacity", since = "1.46.0")]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.capacity()
|
||||
}
|
||||
|
||||
// Ensure this function does not get inlined into `write`, so that it
|
||||
// remains inlineable and its common path remains as short as possible.
|
||||
// If this function ends up being called frequently relative to `write`,
|
||||
// it's likely a sign that the client is using an improperly sized buffer
|
||||
// or their write patterns are somewhat pathological.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn write_cold(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if buf.len() > self.spare_capacity() {
|
||||
self.flush_buf()?;
|
||||
}
|
||||
|
||||
// Why not len > capacity? To avoid a needless trip through the buffer when the input
|
||||
// exactly fills it. We'd just need to flush it to the underlying writer anyway.
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write(buf);
|
||||
self.panicked = false;
|
||||
r
|
||||
} else {
|
||||
// Write to the buffer. In this case, we write to the buffer even if it fills it
|
||||
// exactly. Doing otherwise would mean flushing the buffer, then writing this
|
||||
// input to the inner writer, which in many cases would be a worse strategy.
|
||||
|
||||
// SAFETY: There was either enough spare capacity already, or there wasn't and we
|
||||
// flushed the buffer to ensure that there is. In the latter case, we know that there
|
||||
// is because flushing ensured that our entire buffer is spare capacity, and we entered
|
||||
// this block because the input buffer length is less than that capacity. In either
|
||||
// case, it's safe to write the input buffer to our buffer.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure this function does not get inlined into `write_all`, so that it
|
||||
// remains inlineable and its common path remains as short as possible.
|
||||
// If this function ends up being called frequently relative to `write_all`,
|
||||
// it's likely a sign that the client is using an improperly sized buffer
|
||||
// or their write patterns are somewhat pathological.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn write_all_cold(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
// Normally, `write_all` just calls `write` in a loop. We can do better
|
||||
// by calling `self.get_mut().write_all()` directly, which avoids
|
||||
// round trips through the buffer in the event of a series of partial
|
||||
// writes in some circumstances.
|
||||
|
||||
if buf.len() > self.spare_capacity() {
|
||||
self.flush_buf()?;
|
||||
}
|
||||
|
||||
// Why not len > capacity? To avoid a needless trip through the buffer when the input
|
||||
// exactly fills it. We'd just need to flush it to the underlying writer anyway.
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write_all(buf);
|
||||
self.panicked = false;
|
||||
r
|
||||
} else {
|
||||
// Write to the buffer. In this case, we write to the buffer even if it fills it
|
||||
// exactly. Doing otherwise would mean flushing the buffer, then writing this
|
||||
// input to the inner writer, which in many cases would be a worse strategy.
|
||||
|
||||
// SAFETY: There was either enough spare capacity already, or there wasn't and we
|
||||
// flushed the buffer to ensure that there is. In the latter case, we know that there
|
||||
// is because flushing ensured that our entire buffer is spare capacity, and we entered
|
||||
// this block because the input buffer length is less than that capacity. In either
|
||||
// case, it's safe to write the input buffer to our buffer.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Requires `buf.len() <= self.buf.capacity() - self.buf.len()`,
|
||||
// i.e., that input buffer length is less than or equal to spare capacity.
|
||||
#[inline]
|
||||
unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) {
|
||||
debug_assert!(buf.len() <= self.spare_capacity());
|
||||
let old_len = self.buf.len();
|
||||
let buf_len = buf.len();
|
||||
let src = buf.as_ptr();
|
||||
unsafe {
|
||||
let dst = self.buf.as_mut_ptr().add(old_len);
|
||||
ptr::copy_nonoverlapping(src, dst, buf_len);
|
||||
self.buf.set_len(old_len + buf_len);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn spare_capacity(&self) -> usize {
|
||||
self.buf.capacity() - self.buf.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
/// Error returned for the buffered data from `BufWriter::into_parts`, when the underlying
|
||||
/// writer has previously panicked. Contains the (possibly partly written) buffered data.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::{self, BufWriter, Write};
|
||||
/// use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
///
|
||||
/// struct PanickingWriter;
|
||||
/// impl Write for PanickingWriter {
|
||||
/// fn write(&mut self, buf: &[u8]) -> io::Result<usize> { panic!() }
|
||||
/// fn flush(&mut self) -> io::Result<()> { panic!() }
|
||||
/// }
|
||||
///
|
||||
/// let mut stream = BufWriter::new(PanickingWriter);
|
||||
/// write!(stream, "some data").unwrap();
|
||||
/// let result = catch_unwind(AssertUnwindSafe(|| {
|
||||
/// stream.flush().unwrap()
|
||||
/// }));
|
||||
/// assert!(result.is_err());
|
||||
/// let (recovered_writer, buffered_data) = stream.into_parts();
|
||||
/// assert!(matches!(recovered_writer, PanickingWriter));
|
||||
/// assert_eq!(buffered_data.unwrap_err().into_inner(), b"some data");
|
||||
/// ```
|
||||
pub struct WriterPanicked {
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl WriterPanicked {
|
||||
/// Returns the perhaps-unwritten data. Some of this data may have been written by the
|
||||
/// panicking call(s) to the underlying writer, so simply writing it again is not a good idea.
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub fn into_inner(self) -> Vec<u8> {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
impl error::Error for WriterPanicked {}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
impl fmt::Display for WriterPanicked {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"BufWriter inner writer panicked, what data remains unwritten is not known".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
impl fmt::Debug for WriterPanicked {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("WriterPanicked")
|
||||
.field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> Write for BufWriter<W> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// Use < instead of <= to avoid a needless trip through the buffer in some cases.
|
||||
// See `write_cold` for details.
|
||||
if buf.len() < self.spare_capacity() {
|
||||
// SAFETY: safe by above conditional.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
self.write_cold(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
// Use < instead of <= to avoid a needless trip through the buffer in some cases.
|
||||
// See `write_all_cold` for details.
|
||||
if buf.len() < self.spare_capacity() {
|
||||
// SAFETY: safe by above conditional.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
self.write_all_cold(buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
// FIXME: Consider applying `#[inline]` / `#[inline(never)]` optimizations already applied
|
||||
// to `write` and `write_all`. The performance benefits can be significant. See #79930.
|
||||
if self.get_ref().is_write_vectored() {
|
||||
// We have to handle the possibility that the total length of the buffers overflows
|
||||
// `usize` (even though this can only happen if multiple `IoSlice`s reference the
|
||||
// same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the
|
||||
// computation overflows, then surely the input cannot fit in our buffer, so we forward
|
||||
// to the inner writer's `write_vectored` method to let it handle it appropriately.
|
||||
let mut saturated_total_len: usize = 0;
|
||||
|
||||
for buf in bufs {
|
||||
saturated_total_len = saturated_total_len.saturating_add(buf.len());
|
||||
|
||||
if saturated_total_len > self.spare_capacity() && !self.buf.is_empty() {
|
||||
// Flush if the total length of the input exceeds our buffer's spare capacity.
|
||||
// If we would have overflowed, this condition also holds, and we need to flush.
|
||||
self.flush_buf()?;
|
||||
}
|
||||
|
||||
if saturated_total_len >= self.buf.capacity() {
|
||||
// Forward to our inner writer if the total length of the input is greater than or
|
||||
// equal to our buffer capacity. If we would have overflowed, this condition also
|
||||
// holds, and we punt to the inner writer.
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write_vectored(bufs);
|
||||
self.panicked = false;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// `saturated_total_len < self.buf.capacity()` implies that we did not saturate.
|
||||
|
||||
// SAFETY: We checked whether or not the spare capacity was large enough above. If
|
||||
// it was, then we're safe already. If it wasn't, we flushed, making sufficient
|
||||
// room for any input <= the buffer size, which includes this input.
|
||||
unsafe {
|
||||
bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b));
|
||||
};
|
||||
|
||||
Ok(saturated_total_len)
|
||||
} else {
|
||||
let mut iter = bufs.iter();
|
||||
let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) {
|
||||
// This is the first non-empty slice to write, so if it does
|
||||
// not fit in the buffer, we still get to flush and proceed.
|
||||
if buf.len() > self.spare_capacity() {
|
||||
self.flush_buf()?;
|
||||
}
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
// The slice is at least as large as the buffering capacity,
|
||||
// so it's better to write it directly, bypassing the buffer.
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write(buf);
|
||||
self.panicked = false;
|
||||
return r;
|
||||
} else {
|
||||
// SAFETY: We checked whether or not the spare capacity was large enough above.
|
||||
// If it was, then we're safe already. If it wasn't, we flushed, making
|
||||
// sufficient room for any input <= the buffer size, which includes this input.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
buf.len()
|
||||
}
|
||||
} else {
|
||||
return Ok(0);
|
||||
};
|
||||
debug_assert!(total_written != 0);
|
||||
for buf in iter {
|
||||
if buf.len() <= self.spare_capacity() {
|
||||
// SAFETY: safe by above conditional.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
// This cannot overflow `usize`. If we are here, we've written all of the bytes
|
||||
// so far to our buffer, and we've ensured that we never exceed the buffer's
|
||||
// capacity. Therefore, `total_written` <= `self.buf.capacity()` <= `usize::MAX`.
|
||||
total_written += buf.len();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(total_written)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.flush_buf().and_then(|()| self.get_mut().flush())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> fmt::Debug for BufWriter<W>
|
||||
where
|
||||
W: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("BufWriter")
|
||||
.field("writer", &&self.inner)
|
||||
.field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write + Seek> Seek for BufWriter<W> {
|
||||
/// Seek to the offset, in bytes, in the underlying writer.
|
||||
///
|
||||
/// Seeking always writes out the internal buffer before seeking.
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
self.flush_buf()?;
|
||||
self.get_mut().seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> Drop for BufWriter<W> {
|
||||
fn drop(&mut self) {
|
||||
if !self.panicked {
|
||||
// dtors should not panic, so we ignore a failed flush
|
||||
let _r = self.flush_buf();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
use crate::fmt;
|
||||
use crate::io::buffered::LineWriterShim;
|
||||
use crate::io::{self, BufWriter, IntoInnerError, IoSlice, Write};
|
||||
|
||||
/// Wraps a writer and buffers output to it, flushing whenever a newline
|
||||
/// (`0x0a`, `'\n'`) is detected.
|
||||
///
|
||||
/// The [`BufWriter`] struct wraps a writer and buffers its output.
|
||||
/// But it only does this batched write when it goes out of scope, or when the
|
||||
/// internal buffer is full. Sometimes, you'd prefer to write each line as it's
|
||||
/// completed, rather than the entire buffer at once. Enter `LineWriter`. It
|
||||
/// does exactly that.
|
||||
///
|
||||
/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the
|
||||
/// `LineWriter` goes out of scope or when its internal buffer is full.
|
||||
///
|
||||
/// If there's still a partial line in the buffer when the `LineWriter` is
|
||||
/// dropped, it will flush those contents.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// We can use `LineWriter` to write one line at a time, significantly
|
||||
/// reducing the number of actual writes to the file.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::{self, File};
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let road_not_taken = b"I shall be telling this with a sigh
|
||||
/// Somewhere ages and ages hence:
|
||||
/// Two roads diverged in a wood, and I -
|
||||
/// I took the one less traveled by,
|
||||
/// And that has made all the difference.";
|
||||
///
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let mut file = LineWriter::new(file);
|
||||
///
|
||||
/// file.write_all(b"I shall be telling this with a sigh")?;
|
||||
///
|
||||
/// // No bytes are written until a newline is encountered (or
|
||||
/// // the internal buffer is filled).
|
||||
/// assert_eq!(fs::read_to_string("poem.txt")?, "");
|
||||
/// file.write_all(b"\n")?;
|
||||
/// assert_eq!(
|
||||
/// fs::read_to_string("poem.txt")?,
|
||||
/// "I shall be telling this with a sigh\n",
|
||||
/// );
|
||||
///
|
||||
/// // Write the rest of the poem.
|
||||
/// file.write_all(b"Somewhere ages and ages hence:
|
||||
/// Two roads diverged in a wood, and I -
|
||||
/// I took the one less traveled by,
|
||||
/// And that has made all the difference.")?;
|
||||
///
|
||||
/// // The last line of the poem doesn't end in a newline, so
|
||||
/// // we have to flush or drop the `LineWriter` to finish
|
||||
/// // writing.
|
||||
/// file.flush()?;
|
||||
///
|
||||
/// // Confirm the whole poem was written.
|
||||
/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct LineWriter<W: ?Sized + Write> {
|
||||
inner: BufWriter<W>,
|
||||
}
|
||||
|
||||
impl<W: Write> LineWriter<W> {
|
||||
/// Creates a new `LineWriter`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let file = LineWriter::new(file);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(inner: W) -> LineWriter<W> {
|
||||
// Lines typically aren't that long, don't use a giant buffer
|
||||
LineWriter::with_capacity(1024, inner)
|
||||
}
|
||||
|
||||
/// Creates a new `LineWriter` with at least the specified capacity for the
|
||||
/// internal buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let file = LineWriter::with_capacity(100, file);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize, inner: W) -> LineWriter<W> {
|
||||
LineWriter { inner: BufWriter::with_capacity(capacity, inner) }
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying writer.
|
||||
///
|
||||
/// Caution must be taken when calling methods on the mutable reference
|
||||
/// returned as extra writes could corrupt the output stream.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let mut file = LineWriter::new(file);
|
||||
///
|
||||
/// // we can use reference just like file
|
||||
/// let reference = file.get_mut();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_mut(&mut self) -> &mut W {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Unwraps this `LineWriter`, returning the underlying writer.
|
||||
///
|
||||
/// The internal buffer is written out before returning the writer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An [`Err`] will be returned if an error occurs while flushing the buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
///
|
||||
/// let writer: LineWriter<File> = LineWriter::new(file);
|
||||
///
|
||||
/// let file: File = writer.into_inner()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
|
||||
self.inner.into_inner().map_err(|err| err.new_wrapped(|inner| LineWriter { inner }))
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Write> LineWriter<W> {
|
||||
/// Gets a reference to the underlying writer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let file = LineWriter::new(file);
|
||||
///
|
||||
/// let reference = file.get_ref();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_ref(&self) -> &W {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> Write for LineWriter<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
LineWriterShim::new(&mut self.inner).write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
LineWriterShim::new(&mut self.inner).write_vectored(bufs)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.inner.is_write_vectored()
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
LineWriterShim::new(&mut self.inner).write_all(buf)
|
||||
}
|
||||
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
LineWriterShim::new(&mut self.inner).write_all_vectored(bufs)
|
||||
}
|
||||
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
LineWriterShim::new(&mut self.inner).write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> fmt::Debug for LineWriter<W>
|
||||
where
|
||||
W: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("LineWriter")
|
||||
.field("writer", &self.get_ref())
|
||||
.field(
|
||||
"buffer",
|
||||
&format_args!("{}/{}", self.inner.buffer().len(), self.inner.capacity()),
|
||||
)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
use core::slice::memchr;
|
||||
|
||||
use crate::io::{self, BufWriter, IoSlice, Write};
|
||||
|
||||
/// Private helper struct for implementing the line-buffered writing logic.
|
||||
///
|
||||
/// This shim temporarily wraps a BufWriter, and uses its internals to
|
||||
/// implement a line-buffered writer (specifically by using the internal
|
||||
/// methods like write_to_buf and flush_buf). In this way, a more
|
||||
/// efficient abstraction can be created than one that only had access to
|
||||
/// `write` and `flush`, without needlessly duplicating a lot of the
|
||||
/// implementation details of BufWriter. This also allows existing
|
||||
/// `BufWriters` to be temporarily given line-buffering logic; this is what
|
||||
/// enables Stdout to be alternately in line-buffered or block-buffered mode.
|
||||
#[derive(Debug)]
|
||||
pub struct LineWriterShim<'a, W: ?Sized + Write> {
|
||||
buffer: &'a mut BufWriter<W>,
|
||||
}
|
||||
|
||||
impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> {
|
||||
pub fn new(buffer: &'a mut BufWriter<W>) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
|
||||
/// Gets a reference to the inner writer (that is, the writer
|
||||
/// wrapped by the BufWriter).
|
||||
fn inner(&self) -> &W {
|
||||
self.buffer.get_ref()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the inner writer (that is, the writer
|
||||
/// wrapped by the BufWriter). Be careful with this writer, as writes to
|
||||
/// it will bypass the buffer.
|
||||
fn inner_mut(&mut self) -> &mut W {
|
||||
self.buffer.get_mut()
|
||||
}
|
||||
|
||||
/// Gets the content currently buffered in self.buffer
|
||||
fn buffered(&self) -> &[u8] {
|
||||
self.buffer.buffer()
|
||||
}
|
||||
|
||||
/// Flushes the buffer iff the last byte is a newline (indicating that an
|
||||
/// earlier write only succeeded partially, and we want to retry flushing
|
||||
/// the buffered line before continuing with a subsequent write).
|
||||
fn flush_if_completed_line(&mut self) -> io::Result<()> {
|
||||
match self.buffered().last().copied() {
|
||||
Some(b'\n') => self.buffer.flush_buf(),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> {
|
||||
/// Writes some data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// the last newline is sent directly to the underlying writer, and data
|
||||
/// after it is buffered. Returns the number of bytes written.
|
||||
///
|
||||
/// This function operates on a "best effort basis"; in keeping with the
|
||||
/// convention of `Write::write`, it makes at most one attempt to write
|
||||
/// new data to the underlying writer. If that write only reports a partial
|
||||
/// success, the remaining data will be buffered.
|
||||
///
|
||||
/// Because this function attempts to send completed lines to the underlying
|
||||
/// writer, it will also flush the existing buffer if it ends with a
|
||||
/// newline, even if the incoming data does not contain any newlines.
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let newline_idx = match memchr::memrchr(b'\n', buf) {
|
||||
// If there are no new newlines (that is, if this write is less than
|
||||
// one line), just do a regular buffered write (which may flush if
|
||||
// we exceed the inner buffer's size)
|
||||
None => {
|
||||
self.flush_if_completed_line()?;
|
||||
return self.buffer.write(buf);
|
||||
}
|
||||
// Otherwise, arrange for the lines to be written directly to the
|
||||
// inner writer.
|
||||
Some(newline_idx) => newline_idx + 1,
|
||||
};
|
||||
|
||||
// Flush existing content to prepare for our write. We have to do this
|
||||
// before attempting to write `buf` in order to maintain consistency;
|
||||
// if we add `buf` to the buffer then try to flush it all at once,
|
||||
// we're obligated to return Ok(), which would mean suppressing any
|
||||
// errors that occur during flush.
|
||||
self.buffer.flush_buf()?;
|
||||
|
||||
// This is what we're going to try to write directly to the inner
|
||||
// writer. The rest will be buffered, if nothing goes wrong.
|
||||
let lines = &buf[..newline_idx];
|
||||
|
||||
// Write `lines` directly to the inner writer. In keeping with the
|
||||
// `write` convention, make at most one attempt to add new (unbuffered)
|
||||
// data. Because this write doesn't touch the BufWriter state directly,
|
||||
// and the buffer is known to be empty, we don't need to worry about
|
||||
// self.buffer.panicked here.
|
||||
let flushed = self.inner_mut().write(lines)?;
|
||||
|
||||
// If buffer returns Ok(0), propagate that to the caller without
|
||||
// doing additional buffering; otherwise we're just guaranteeing
|
||||
// an "ErrorKind::WriteZero" later.
|
||||
if flushed == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Now that the write has succeeded, buffer the rest (or as much of
|
||||
// the rest as possible). If there were any unwritten newlines, we
|
||||
// only buffer out to the last unwritten newline that fits in the
|
||||
// buffer; this helps prevent flushing partial lines on subsequent
|
||||
// calls to LineWriterShim::write.
|
||||
|
||||
// Handle the cases in order of most-common to least-common, under
|
||||
// the presumption that most writes succeed in totality, and that most
|
||||
// writes are smaller than the buffer.
|
||||
// - Is this a partial line (ie, no newlines left in the unwritten tail)
|
||||
// - If not, does the data out to the last unwritten newline fit in
|
||||
// the buffer?
|
||||
// - If not, scan for the last newline that *does* fit in the buffer
|
||||
let tail = if flushed >= newline_idx {
|
||||
let tail = &buf[flushed..];
|
||||
// Avoid unnecessary short writes by not splitting the remaining
|
||||
// bytes if they're larger than the buffer.
|
||||
// They can be written in full by the next call to write.
|
||||
if tail.len() >= self.buffer.capacity() {
|
||||
return Ok(flushed);
|
||||
}
|
||||
tail
|
||||
} else if newline_idx - flushed <= self.buffer.capacity() {
|
||||
&buf[flushed..newline_idx]
|
||||
} else {
|
||||
let scan_area = &buf[flushed..];
|
||||
let scan_area = &scan_area[..self.buffer.capacity()];
|
||||
match memchr::memrchr(b'\n', scan_area) {
|
||||
Some(newline_idx) => &scan_area[..newline_idx + 1],
|
||||
None => scan_area,
|
||||
}
|
||||
};
|
||||
|
||||
let buffered = self.buffer.write_to_buf(tail);
|
||||
Ok(flushed + buffered)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.buffer.flush()
|
||||
}
|
||||
|
||||
/// Writes some vectored data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// and including the buffer containing the last newline is sent directly to
|
||||
/// the inner writer, and the data after it is buffered. Returns the number
|
||||
/// of bytes written.
|
||||
///
|
||||
/// This function operates on a "best effort basis"; in keeping with the
|
||||
/// convention of `Write::write`, it makes at most one attempt to write
|
||||
/// new data to the underlying writer.
|
||||
///
|
||||
/// Because this function attempts to send completed lines to the underlying
|
||||
/// writer, it will also flush the existing buffer if it contains any
|
||||
/// newlines.
|
||||
///
|
||||
/// Because sorting through an array of `IoSlice` can be a bit convoluted,
|
||||
/// This method differs from write in the following ways:
|
||||
///
|
||||
/// - It attempts to write the full content of all the buffers up to and
|
||||
/// including the one containing the last newline. This means that it
|
||||
/// may attempt to write a partial line, that buffer has data past the
|
||||
/// newline.
|
||||
/// - If the write only reports partial success, it does not attempt to
|
||||
/// find the precise location of the written bytes and buffer the rest.
|
||||
///
|
||||
/// If the underlying vector doesn't support vectored writing, we instead
|
||||
/// simply write the first non-empty buffer with `write`. This way, we
|
||||
/// get the benefits of more granular partial-line handling without losing
|
||||
/// anything in efficiency
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
// If there's no specialized behavior for write_vectored, just use
|
||||
// write. This has the benefit of more granular partial-line handling.
|
||||
if !self.is_write_vectored() {
|
||||
return match bufs.iter().find(|buf| !buf.is_empty()) {
|
||||
Some(buf) => self.write(buf),
|
||||
None => Ok(0),
|
||||
};
|
||||
}
|
||||
|
||||
// Find the buffer containing the last newline
|
||||
// FIXME: This is overly slow if there are very many bufs and none contain
|
||||
// newlines. e.g. writev() on Linux only writes up to 1024 slices, so
|
||||
// scanning the rest is wasted effort. This makes write_all_vectored()
|
||||
// quadratic.
|
||||
let last_newline_buf_idx = bufs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i));
|
||||
|
||||
// If there are no new newlines (that is, if this write is less than
|
||||
// one line), just do a regular buffered write
|
||||
let last_newline_buf_idx = match last_newline_buf_idx {
|
||||
// No newlines; just do a normal buffered write
|
||||
None => {
|
||||
self.flush_if_completed_line()?;
|
||||
return self.buffer.write_vectored(bufs);
|
||||
}
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
// Flush existing content to prepare for our write
|
||||
self.buffer.flush_buf()?;
|
||||
|
||||
// This is what we're going to try to write directly to the inner
|
||||
// writer. The rest will be buffered, if nothing goes wrong.
|
||||
let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1);
|
||||
|
||||
// Write `lines` directly to the inner writer. In keeping with the
|
||||
// `write` convention, make at most one attempt to add new (unbuffered)
|
||||
// data. Because this write doesn't touch the BufWriter state directly,
|
||||
// and the buffer is known to be empty, we don't need to worry about
|
||||
// self.panicked here.
|
||||
let flushed = self.inner_mut().write_vectored(lines)?;
|
||||
|
||||
// If inner returns Ok(0), propagate that to the caller without
|
||||
// doing additional buffering; otherwise we're just guaranteeing
|
||||
// an "ErrorKind::WriteZero" later.
|
||||
if flushed == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Don't try to reconstruct the exact amount written; just bail
|
||||
// in the event of a partial write
|
||||
let mut lines_len: usize = 0;
|
||||
for buf in lines {
|
||||
// With overlapping/duplicate slices the total length may in theory
|
||||
// exceed usize::MAX
|
||||
lines_len = lines_len.saturating_add(buf.len());
|
||||
if flushed < lines_len {
|
||||
return Ok(flushed);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the write has succeeded, buffer the rest (or as much of the
|
||||
// rest as possible)
|
||||
let buffered: usize = tail
|
||||
.iter()
|
||||
.filter(|buf| !buf.is_empty())
|
||||
.map(|buf| self.buffer.write_to_buf(buf))
|
||||
.take_while(|&n| n > 0)
|
||||
.sum();
|
||||
|
||||
Ok(flushed + buffered)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.inner().is_write_vectored()
|
||||
}
|
||||
|
||||
/// Writes some data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// the last newline is sent directly to the underlying writer, and data
|
||||
/// after it is buffered.
|
||||
///
|
||||
/// Because this function attempts to send completed lines to the underlying
|
||||
/// writer, it will also flush the existing buffer if it contains any
|
||||
/// newlines, even if the incoming data does not contain any newlines.
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
match memchr::memrchr(b'\n', buf) {
|
||||
// If there are no new newlines (that is, if this write is less than
|
||||
// one line), just do a regular buffered write (which may flush if
|
||||
// we exceed the inner buffer's size)
|
||||
None => {
|
||||
self.flush_if_completed_line()?;
|
||||
self.buffer.write_all(buf)
|
||||
}
|
||||
Some(newline_idx) => {
|
||||
let (lines, tail) = buf.split_at(newline_idx + 1);
|
||||
|
||||
if self.buffered().is_empty() {
|
||||
self.inner_mut().write_all(lines)?;
|
||||
} else {
|
||||
// If there is any buffered data, we add the incoming lines
|
||||
// to that buffer before flushing, which saves us at least
|
||||
// one write call. We can't really do this with `write`,
|
||||
// since we can't do this *and* not suppress errors *and*
|
||||
// report a consistent state to the caller in a return
|
||||
// value, but here in write_all it's fine.
|
||||
self.buffer.write_all(lines)?;
|
||||
self.buffer.flush_buf()?;
|
||||
}
|
||||
|
||||
self.buffer.write_all(tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
//! Buffering wrappers for I/O traits
|
||||
|
||||
mod bufreader;
|
||||
mod bufwriter;
|
||||
mod linewriter;
|
||||
mod linewritershim;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub use bufwriter::WriterPanicked;
|
||||
use linewritershim::LineWriterShim;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter};
|
||||
use crate::io::Error;
|
||||
use crate::{error, fmt};
|
||||
|
||||
/// An error returned by [`BufWriter::into_inner`] which combines an error that
|
||||
/// happened while writing out the buffer, and the buffered writer object
|
||||
/// which may be used to recover from the condition.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // do stuff with the stream
|
||||
///
|
||||
/// // we want to get our `TcpStream` back, so let's try:
|
||||
///
|
||||
/// let stream = match stream.into_inner() {
|
||||
/// Ok(s) => s,
|
||||
/// Err(e) => {
|
||||
/// // Here, e is an IntoInnerError
|
||||
/// panic!("An error occurred");
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct IntoInnerError<W>(W, Error);
|
||||
|
||||
impl<W> IntoInnerError<W> {
|
||||
/// Constructs a new IntoInnerError
|
||||
fn new(writer: W, error: Error) -> Self {
|
||||
Self(writer, error)
|
||||
}
|
||||
|
||||
/// Helper to construct a new IntoInnerError; intended to help with
|
||||
/// adapters that wrap other adapters
|
||||
fn new_wrapped<W2>(self, f: impl FnOnce(W) -> W2) -> IntoInnerError<W2> {
|
||||
let Self(writer, error) = self;
|
||||
IntoInnerError::new(f(writer), error)
|
||||
}
|
||||
|
||||
/// Returns the error which caused the call to [`BufWriter::into_inner()`]
|
||||
/// to fail.
|
||||
///
|
||||
/// This error was returned when attempting to write the internal buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // do stuff with the stream
|
||||
///
|
||||
/// // we want to get our `TcpStream` back, so let's try:
|
||||
///
|
||||
/// let stream = match stream.into_inner() {
|
||||
/// Ok(s) => s,
|
||||
/// Err(e) => {
|
||||
/// // Here, e is an IntoInnerError, let's log the inner error.
|
||||
/// //
|
||||
/// // We'll just 'log' to stdout for this example.
|
||||
/// println!("{}", e.error());
|
||||
///
|
||||
/// panic!("An unexpected error occurred.");
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn error(&self) -> &Error {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Returns the buffered writer instance which generated the error.
|
||||
///
|
||||
/// The returned object can be used for error recovery, such as
|
||||
/// re-inspecting the buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // do stuff with the stream
|
||||
///
|
||||
/// // we want to get our `TcpStream` back, so let's try:
|
||||
///
|
||||
/// let stream = match stream.into_inner() {
|
||||
/// Ok(s) => s,
|
||||
/// Err(e) => {
|
||||
/// // Here, e is an IntoInnerError, let's re-examine the buffer:
|
||||
/// let buffer = e.into_inner();
|
||||
///
|
||||
/// // do stuff to try to recover
|
||||
///
|
||||
/// // afterwards, let's just return the stream
|
||||
/// buffer.into_inner().unwrap()
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> W {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Consumes the [`IntoInnerError`] and returns the error which caused the call to
|
||||
/// [`BufWriter::into_inner()`] to fail. Unlike `error`, this can be used to
|
||||
/// obtain ownership of the underlying error.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use std::io::{BufWriter, ErrorKind, Write};
|
||||
///
|
||||
/// let mut not_enough_space = [0u8; 10];
|
||||
/// let mut stream = BufWriter::new(not_enough_space.as_mut());
|
||||
/// write!(stream, "this cannot be actually written").unwrap();
|
||||
/// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small");
|
||||
/// let err = into_inner_err.into_error();
|
||||
/// assert_eq!(err.kind(), ErrorKind::WriteZero);
|
||||
/// ```
|
||||
#[stable(feature = "io_into_inner_error_parts", since = "1.55.0")]
|
||||
pub fn into_error(self) -> Error {
|
||||
self.1
|
||||
}
|
||||
|
||||
/// Consumes the [`IntoInnerError`] and returns the error which caused the call to
|
||||
/// [`BufWriter::into_inner()`] to fail, and the underlying writer.
|
||||
///
|
||||
/// This can be used to simply obtain ownership of the underlying error; it can also be used for
|
||||
/// advanced error recovery.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use std::io::{BufWriter, ErrorKind, Write};
|
||||
///
|
||||
/// let mut not_enough_space = [0u8; 10];
|
||||
/// let mut stream = BufWriter::new(not_enough_space.as_mut());
|
||||
/// write!(stream, "this cannot be actually written").unwrap();
|
||||
/// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small");
|
||||
/// let (err, recovered_writer) = into_inner_err.into_parts();
|
||||
/// assert_eq!(err.kind(), ErrorKind::WriteZero);
|
||||
/// assert_eq!(recovered_writer.buffer(), b"t be actually written");
|
||||
/// ```
|
||||
#[stable(feature = "io_into_inner_error_parts", since = "1.55.0")]
|
||||
pub fn into_parts(self) -> (Error, W) {
|
||||
(self.1, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W> From<IntoInnerError<W>> for Error {
|
||||
fn from(iie: IntoInnerError<W>) -> Error {
|
||||
iie.1
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: Send + fmt::Debug> error::Error for IntoInnerError<W> {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W> fmt::Display for IntoInnerError<W> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.error().fmt(f)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user