Compare commits
4 Commits
main
...
72989d86a8
| Author | SHA1 | Date | |
|---|---|---|---|
| 72989d86a8 | |||
| 9958b23c89 | |||
| 56ad115e58 | |||
| 56a00d0403 |
@@ -1,6 +1,6 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/bytes-struct","crates/io","crates/os-std", "crates/shared", "user/*"]
|
members = ["crates/bytes-struct","crates/io","crates/std", "crates/shared", "user/*"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "kernel-rust"
|
name = "kernel-rust"
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
pub use io::SeekFrom;
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
pub mod io;
|
|
||||||
pub mod prelude;
|
|
||||||
|
|
||||||
pub use shared::fs;
|
|
||||||
pub use shared::syscall;
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! custom_std_setup {
|
|
||||||
() => {
|
|
||||||
use $crate::prelude::*;
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
struct GlobalAllocator;
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
|
|
||||||
|
|
||||||
unsafe impl core::alloc::GlobalAlloc for GlobalAllocator {
|
|
||||||
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
|
|
||||||
$crate::syscall::alloc(layout)
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
|
|
||||||
$crate::syscall::dealloc(ptr, layout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
|
|
||||||
// TODO print
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn _start() {
|
|
||||||
main()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! print {
|
|
||||||
($($args:expr),*) => {
|
|
||||||
$crate::syscall::write_string_temp(&format!($($args),*))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! println {
|
|
||||||
() => {
|
|
||||||
$crate::print!("");
|
|
||||||
// $crate::print!("\n\r");
|
|
||||||
};
|
|
||||||
($($args:expr),*) => {
|
|
||||||
$crate::print!($($args),*);
|
|
||||||
// $crate::println!();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
pub use crate::print;
|
|
||||||
pub use crate::println;
|
|
||||||
pub use alloc::format;
|
|
||||||
pub use alloc::string::String;
|
|
||||||
pub use alloc::vec;
|
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
use io::{IoBase, Read, Write};
|
||||||
|
|
||||||
|
use crate::syscall;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct File {
|
pub struct File {
|
||||||
fd: u64,
|
fd: u64,
|
||||||
@@ -6,7 +10,7 @@ pub struct File {
|
|||||||
impl File {
|
impl File {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// The file descriptor must be valid
|
/// The file descriptor must be valid
|
||||||
pub unsafe fn new(fd: u64) -> Self {
|
pub unsafe fn from_raw_fd(fd: u64) -> Self {
|
||||||
Self { fd }
|
Self { fd }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,3 +18,23 @@ impl File {
|
|||||||
self.fd
|
self.fd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IoBase for File {
|
||||||
|
type Error = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for File {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
|
Ok(syscall::read(self.as_fd(), buf) as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for File {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
|
Ok(syscall::write(self.as_fd(), buf) as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ pub enum SysCall {
|
|||||||
Read = 0,
|
Read = 0,
|
||||||
Write = 1,
|
Write = 1,
|
||||||
Open = 2,
|
Open = 2,
|
||||||
|
Close = 3,
|
||||||
Seek = 8,
|
Seek = 8,
|
||||||
Alloc = 40,
|
Alloc = 40,
|
||||||
Dealloc = 41,
|
Dealloc = 41,
|
||||||
|
Spawn = 58,
|
||||||
|
ExecVE = 59,
|
||||||
Exit = 60,
|
Exit = 60,
|
||||||
NanoSleep = 101,
|
NanoSleep = 101,
|
||||||
WriteIntTemp = 998,
|
WriteIntTemp = 998,
|
||||||
@@ -26,9 +29,12 @@ impl From<u64> for SysCall {
|
|||||||
0 => SysCall::Read,
|
0 => SysCall::Read,
|
||||||
1 => SysCall::Write,
|
1 => SysCall::Write,
|
||||||
2 => SysCall::Open,
|
2 => SysCall::Open,
|
||||||
|
3 => SysCall::Close,
|
||||||
8 => SysCall::Seek,
|
8 => SysCall::Seek,
|
||||||
40 => SysCall::Alloc,
|
40 => SysCall::Alloc,
|
||||||
41 => SysCall::Dealloc,
|
41 => SysCall::Dealloc,
|
||||||
|
58 => SysCall::Spawn,
|
||||||
|
59 => SysCall::ExecVE,
|
||||||
60 => SysCall::Exit,
|
60 => SysCall::Exit,
|
||||||
101 => SysCall::NanoSleep,
|
101 => SysCall::NanoSleep,
|
||||||
998 => SysCall::WriteIntTemp,
|
998 => SysCall::WriteIntTemp,
|
||||||
@@ -143,30 +149,53 @@ pub fn open<P: AsRef<Path>>(path: P) -> File {
|
|||||||
let ptr = path_str.as_ptr();
|
let ptr = path_str.as_ptr();
|
||||||
let size = path_str.len();
|
let size = path_str.len();
|
||||||
let (fd, ..) = syscall!(SysCall::Open, ptr as u64, size as u64);
|
let (fd, ..) = syscall!(SysCall::Open, ptr as u64, size as u64);
|
||||||
File::new(fd)
|
File::from_raw_fd(fd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn write(file: &mut File, buf: &[u8]) {
|
pub fn close(file_descriptor: u64) {
|
||||||
|
unsafe {
|
||||||
|
syscall!(SysCall::Close, file_descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn write(file_descriptor: u64, buf: &[u8]) -> u64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = buf.as_ptr();
|
let ptr = buf.as_ptr();
|
||||||
let size = buf.len();
|
let size = buf.len();
|
||||||
syscall!(SysCall::Write, file.as_fd(), ptr as u64, size as u64);
|
let (len, ..) = syscall!(SysCall::Write, file_descriptor, ptr as u64, size as u64);
|
||||||
|
len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn read(file: &mut File, buf: &mut [u8]) {
|
pub fn read(file_descriptor: u64, buf: &mut [u8]) -> u64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = buf.as_ptr();
|
let ptr = buf.as_ptr();
|
||||||
let size = buf.len();
|
let size = buf.len();
|
||||||
syscall!(SysCall::Read, file.as_fd(), ptr as u64, size as u64);
|
let (len, ..) = syscall!(SysCall::Read, file_descriptor, ptr as u64, size as u64);
|
||||||
|
len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn seek(file: &mut File, seek: SeekFrom) {
|
pub fn seek(file_descriptor: u64, seek: SeekFrom) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let (discriminant, value) = match seek {
|
let (discriminant, value) = match seek {
|
||||||
SeekFrom::Start(v) => (0, v),
|
SeekFrom::Start(v) => (0, v),
|
||||||
SeekFrom::End(v) => (1, v as u64),
|
SeekFrom::End(v) => (1, v as u64),
|
||||||
SeekFrom::Current(v) => (2, v as u64),
|
SeekFrom::Current(v) => (2, v as u64),
|
||||||
};
|
};
|
||||||
syscall!(SysCall::Seek, file.as_fd(), discriminant, value);
|
syscall!(SysCall::Seek, file_descriptor, discriminant, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn spawn<P: AsRef<Path>>(path: P) {
|
||||||
|
unsafe {
|
||||||
|
let path_str = path.as_ref().as_str();
|
||||||
|
let ptr = path_str.as_ptr();
|
||||||
|
let size = path_str.len();
|
||||||
|
syscall!(SysCall::Spawn, ptr as u64, size as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn execve<P: AsRef<Path>>(path: P) {
|
||||||
|
unsafe {
|
||||||
|
let path_str = path.as_ref().as_str();
|
||||||
|
let ptr = path_str.as_ptr();
|
||||||
|
let size = path_str.len();
|
||||||
|
syscall!(SysCall::ExecVE, ptr as u64, size as u64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "os-std"
|
name = "std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
490
crates/std/src/alloc.rs
Normal file
490
crates/std/src/alloc.rs
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
//! 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
crates/std/src/error.rs
Normal file
4
crates/std/src/error.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#[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};
|
||||||
14
crates/std/src/ffi/c_str.rs
Normal file
14
crates/std/src/ffi/c_str.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//! [`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;
|
||||||
207
crates/std/src/ffi/mod.rs
Normal file
207
crates/std/src/ffi/mod.rs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
//! 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;
|
||||||
1845
crates/std/src/ffi/os_str.rs
Normal file
1845
crates/std/src/ffi/os_str.rs
Normal file
File diff suppressed because it is too large
Load Diff
311
crates/std/src/ffi/os_str/tests.rs
Normal file
311
crates/std/src/ffi/os_str/tests.rs
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
3532
crates/std/src/fs.rs
Normal file
3532
crates/std/src/fs.rs
Normal file
File diff suppressed because it is too large
Load Diff
91
crates/std/src/hash/mod.rs
Normal file
91
crates/std/src/hash/mod.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
//! 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};
|
||||||
159
crates/std/src/hash/random.rs
Normal file
159
crates/std/src/hash/random.rs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
//! 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
71
crates/std/src/io.rs
Normal file
71
crates/std/src/io.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
use crate::fs::File;
|
||||||
|
use io::IoBase;
|
||||||
|
pub use io::Read;
|
||||||
|
pub use io::Seek;
|
||||||
|
pub use io::SeekFrom;
|
||||||
|
pub use io::Write;
|
||||||
|
|
||||||
|
pub struct Stdin;
|
||||||
|
|
||||||
|
impl IoBase for Stdin {
|
||||||
|
type Error = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Stdin {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
|
||||||
|
unsafe { File::from_raw_fd(0).read(buf) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stdin() -> Stdin {
|
||||||
|
Stdin
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
|
pub(super) struct Repr();
|
||||||
|
|
||||||
|
// Part took from the real std
|
||||||
|
#[rustc_macro_transparency = "semiopaque"]
|
||||||
|
#[unstable(feature = "io_const_error", issue = "133448")]
|
||||||
|
#[allow_internal_unstable(hint_must_use, io_const_error_internals)]
|
||||||
|
pub macro const_error($kind:expr, $message:expr $(,)?) {
|
||||||
|
crate::io::Error::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub struct Error {
|
||||||
|
repr: Repr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self { repr: Repr() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl Error {
|
||||||
|
pub(crate) const INVALID_UTF8: Self =
|
||||||
|
const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8");
|
||||||
|
|
||||||
|
pub(crate) const READ_EXACT_EOF: Self =
|
||||||
|
const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer");
|
||||||
|
|
||||||
|
pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!(
|
||||||
|
ErrorKind::NotFound,
|
||||||
|
"the number of hardware threads is not known for the target platform",
|
||||||
|
);
|
||||||
|
|
||||||
|
pub(crate) const UNSUPPORTED_PLATFORM: Self =
|
||||||
|
const_error!(ErrorKind::Unsupported, "operation not supported on this platform");
|
||||||
|
|
||||||
|
pub(crate) const WRITE_ALL_EOF: Self =
|
||||||
|
const_error!(ErrorKind::WriteZero, "failed to write whole buffer");
|
||||||
|
|
||||||
|
pub(crate) const ZERO_TIMEOUT: Self =
|
||||||
|
const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout");
|
||||||
|
|
||||||
|
pub(crate) const NO_ADDRESSES: Self =
|
||||||
|
const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses");
|
||||||
|
}
|
||||||
314
crates/std/src/lib.rs
Normal file
314
crates/std/src/lib.rs
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
#![unstable(feature = "custom_std", issue = "none")]
|
||||||
|
#![no_std]
|
||||||
|
//
|
||||||
|
// Lints:
|
||||||
|
#![warn(deprecated_in_future)]
|
||||||
|
// #![warn(missing_docs)]
|
||||||
|
// #![warn(missing_debug_implementations)]
|
||||||
|
#![allow(explicit_outlives_requirements)]
|
||||||
|
#![allow(unused_lifetimes)]
|
||||||
|
#![allow(internal_features)]
|
||||||
|
#![deny(fuzzy_provenance_casts)]
|
||||||
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
|
#![allow(rustdoc::redundant_explicit_links)]
|
||||||
|
#![warn(rustdoc::unescaped_backticks)]
|
||||||
|
// Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind`
|
||||||
|
#![deny(ffi_unwind_calls)]
|
||||||
|
// std may use features in a platform-specific way
|
||||||
|
#![allow(unused_features)]
|
||||||
|
//
|
||||||
|
// Features:
|
||||||
|
#![cfg_attr(
|
||||||
|
test,
|
||||||
|
feature(internal_output_capture, print_internals, update_panic_count, rt)
|
||||||
|
)]
|
||||||
|
#![cfg_attr(
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx"),
|
||||||
|
feature(slice_index_methods, coerce_unsized, sgx_platform)
|
||||||
|
)]
|
||||||
|
#![cfg_attr(all(test, target_os = "uefi"), feature(uefi_std))]
|
||||||
|
#![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))]
|
||||||
|
#![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))]
|
||||||
|
//
|
||||||
|
// Language features:
|
||||||
|
// tidy-alphabetical-start
|
||||||
|
#![feature(alloc_error_handler)]
|
||||||
|
#![feature(allocator_internals)]
|
||||||
|
#![feature(allow_internal_unsafe)]
|
||||||
|
#![feature(allow_internal_unstable)]
|
||||||
|
#![feature(asm_experimental_arch)]
|
||||||
|
#![feature(autodiff)]
|
||||||
|
#![feature(cfg_sanitizer_cfi)]
|
||||||
|
#![feature(cfg_target_thread_local)]
|
||||||
|
#![feature(cfi_encoding)]
|
||||||
|
#![feature(const_default)]
|
||||||
|
#![feature(const_trait_impl)]
|
||||||
|
#![feature(core_float_math)]
|
||||||
|
#![feature(decl_macro)]
|
||||||
|
#![feature(deprecated_suggestion)]
|
||||||
|
#![feature(doc_cfg)]
|
||||||
|
#![feature(doc_masked)]
|
||||||
|
#![feature(doc_notable_trait)]
|
||||||
|
#![feature(dropck_eyepatch)]
|
||||||
|
#![feature(f16)]
|
||||||
|
#![feature(f128)]
|
||||||
|
#![feature(ffi_const)]
|
||||||
|
#![feature(formatting_options)]
|
||||||
|
#![feature(funnel_shifts)]
|
||||||
|
#![feature(if_let_guard)]
|
||||||
|
#![feature(intra_doc_pointers)]
|
||||||
|
#![feature(iter_advance_by)]
|
||||||
|
#![feature(iter_next_chunk)]
|
||||||
|
#![feature(lang_items)]
|
||||||
|
#![feature(link_cfg)]
|
||||||
|
#![feature(linkage)]
|
||||||
|
#![feature(macro_metavar_expr_concat)]
|
||||||
|
#![feature(maybe_uninit_fill)]
|
||||||
|
#![feature(min_specialization)]
|
||||||
|
#![feature(must_not_suspend)]
|
||||||
|
#![feature(needs_panic_runtime)]
|
||||||
|
#![feature(negative_impls)]
|
||||||
|
#![feature(never_type)]
|
||||||
|
#![feature(optimize_attribute)]
|
||||||
|
#![feature(prelude_import)]
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
#![feature(rustdoc_internals)]
|
||||||
|
#![feature(staged_api)]
|
||||||
|
#![feature(stmt_expr_attributes)]
|
||||||
|
#![feature(strict_provenance_lints)]
|
||||||
|
#![feature(thread_local)]
|
||||||
|
#![feature(try_blocks)]
|
||||||
|
#![feature(try_trait_v2)]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
// tidy-alphabetical-end
|
||||||
|
//
|
||||||
|
// Library features (core):
|
||||||
|
// tidy-alphabetical-start
|
||||||
|
#![feature(bstr)]
|
||||||
|
#![feature(bstr_internals)]
|
||||||
|
#![feature(cast_maybe_uninit)]
|
||||||
|
#![feature(cfg_select)]
|
||||||
|
#![feature(char_internals)]
|
||||||
|
#![feature(clone_to_uninit)]
|
||||||
|
#![feature(const_convert)]
|
||||||
|
#![feature(core_intrinsics)]
|
||||||
|
#![feature(core_io_borrowed_buf)]
|
||||||
|
#![feature(drop_guard)]
|
||||||
|
#![feature(duration_constants)]
|
||||||
|
#![feature(error_generic_member_access)]
|
||||||
|
#![feature(error_iter)]
|
||||||
|
#![feature(exact_size_is_empty)]
|
||||||
|
#![feature(exclusive_wrapper)]
|
||||||
|
#![feature(extend_one)]
|
||||||
|
#![feature(float_algebraic)]
|
||||||
|
// #![feature(float_gamma)]
|
||||||
|
#![feature(float_minimum_maximum)]
|
||||||
|
#![feature(fmt_internals)]
|
||||||
|
#![feature(fn_ptr_trait)]
|
||||||
|
#![feature(generic_atomic)]
|
||||||
|
#![feature(hasher_prefixfree_extras)]
|
||||||
|
#![feature(hashmap_internals)]
|
||||||
|
#![feature(hint_must_use)]
|
||||||
|
#![feature(int_from_ascii)]
|
||||||
|
#![feature(ip)]
|
||||||
|
#![feature(maybe_uninit_array_assume_init)]
|
||||||
|
#![feature(panic_can_unwind)]
|
||||||
|
#![feature(panic_internals)]
|
||||||
|
#![feature(pin_coerce_unsized_trait)]
|
||||||
|
#![feature(pointer_is_aligned_to)]
|
||||||
|
#![feature(portable_simd)]
|
||||||
|
#![feature(ptr_as_uninit)]
|
||||||
|
#![feature(ptr_mask)]
|
||||||
|
#![feature(random)]
|
||||||
|
#![feature(slice_internals)]
|
||||||
|
#![feature(slice_ptr_get)]
|
||||||
|
#![feature(slice_range)]
|
||||||
|
#![feature(slice_split_once)]
|
||||||
|
#![feature(std_internals)]
|
||||||
|
#![feature(str_internals)]
|
||||||
|
#![feature(sync_unsafe_cell)]
|
||||||
|
#![feature(temporary_niche_types)]
|
||||||
|
#![feature(ub_checks)]
|
||||||
|
#![feature(used_with_arg)]
|
||||||
|
// tidy-alphabetical-end
|
||||||
|
//
|
||||||
|
// Library features (alloc):
|
||||||
|
// tidy-alphabetical-start
|
||||||
|
#![feature(alloc_layout_extra)]
|
||||||
|
#![feature(allocator_api)]
|
||||||
|
#![feature(clone_from_ref)]
|
||||||
|
#![feature(get_mut_unchecked)]
|
||||||
|
#![feature(map_try_insert)]
|
||||||
|
#![feature(slice_concat_trait)]
|
||||||
|
#![feature(thin_box)]
|
||||||
|
#![feature(try_reserve_kind)]
|
||||||
|
#![feature(try_with_capacity)]
|
||||||
|
#![feature(unique_rc_arc)]
|
||||||
|
#![feature(wtf8_internals)]
|
||||||
|
// tidy-alphabetical-end
|
||||||
|
//
|
||||||
|
// Library features (unwind):
|
||||||
|
// tidy-alphabetical-start
|
||||||
|
// #![feature(panic_unwind)]
|
||||||
|
// tidy-alphabetical-end
|
||||||
|
//
|
||||||
|
// Library features (std_detect):
|
||||||
|
// tidy-alphabetical-start
|
||||||
|
// #![feature(stdarch_internal)]
|
||||||
|
// tidy-alphabetical-end
|
||||||
|
//
|
||||||
|
// Only for re-exporting:
|
||||||
|
// tidy-alphabetical-start
|
||||||
|
#![feature(assert_matches)]
|
||||||
|
#![feature(async_iterator)]
|
||||||
|
#![feature(c_variadic)]
|
||||||
|
#![feature(cfg_accessible)]
|
||||||
|
#![feature(cfg_eval)]
|
||||||
|
#![feature(concat_bytes)]
|
||||||
|
#![feature(const_format_args)]
|
||||||
|
#![feature(custom_test_frameworks)]
|
||||||
|
#![feature(edition_panic)]
|
||||||
|
#![feature(format_args_nl)]
|
||||||
|
#![feature(log_syntax)]
|
||||||
|
#![feature(test)]
|
||||||
|
#![feature(trace_macros)]
|
||||||
|
// tidy-alphabetical-end
|
||||||
|
//
|
||||||
|
// Only used in tests/benchmarks:
|
||||||
|
//
|
||||||
|
// Only for const-ness:
|
||||||
|
// tidy-alphabetical-start
|
||||||
|
#![feature(io_const_error)]
|
||||||
|
// tidy-alphabetical-end
|
||||||
|
//
|
||||||
|
#![feature(c_size_t, unsafe_binders)]
|
||||||
|
#![allow(clippy::doc_lazy_continuation, clippy::all)]
|
||||||
|
#![allow(stable_features, incomplete_features, unexpected_cfgs)]
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate alloc as alloc_crate;
|
||||||
|
|
||||||
|
pub use core::any;
|
||||||
|
pub use core::array;
|
||||||
|
pub use core::async_iter;
|
||||||
|
pub use core::cell;
|
||||||
|
pub use core::char;
|
||||||
|
pub use core::clone;
|
||||||
|
pub use core::cmp;
|
||||||
|
pub use core::convert;
|
||||||
|
pub use core::default;
|
||||||
|
pub use core::future;
|
||||||
|
pub use core::hint;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::i8;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::i16;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::i32;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::i64;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::i128;
|
||||||
|
pub use core::intrinsics;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::isize;
|
||||||
|
pub use core::iter;
|
||||||
|
pub use core::marker;
|
||||||
|
pub use core::mem;
|
||||||
|
pub use core::ops;
|
||||||
|
pub use core::option;
|
||||||
|
pub use core::pin;
|
||||||
|
pub use core::ptr;
|
||||||
|
pub use core::range;
|
||||||
|
pub use core::result;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::u8;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::u16;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::u32;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::u64;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::u128;
|
||||||
|
pub use core::unsafe_binder;
|
||||||
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
|
pub use core::usize;
|
||||||
|
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use alloc_crate::borrow;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use alloc_crate::boxed;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use alloc_crate::fmt;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use alloc_crate::format;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use alloc_crate::rc;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use alloc_crate::slice;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use alloc_crate::str;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use alloc_crate::string;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use alloc_crate::vec;
|
||||||
|
|
||||||
|
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||||
|
pub use core::{
|
||||||
|
assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args,
|
||||||
|
format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env,
|
||||||
|
stringify, trace_macros,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod ffi;
|
||||||
|
pub mod hash;
|
||||||
|
pub mod io;
|
||||||
|
// pub mod fs;
|
||||||
|
pub mod error;
|
||||||
|
pub mod num;
|
||||||
|
pub mod path;
|
||||||
|
pub mod prelude;
|
||||||
|
pub mod process;
|
||||||
|
#[macro_use]
|
||||||
|
pub mod rt;
|
||||||
|
pub mod alloc;
|
||||||
|
pub mod sync;
|
||||||
|
pub mod sys;
|
||||||
|
pub mod thread;
|
||||||
|
pub mod time;
|
||||||
|
|
||||||
|
#[prelude_import]
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use prelude::rust_2024::*;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
mod sealed {
|
||||||
|
/// This trait being unreachable from outside the crate
|
||||||
|
/// prevents outside implementations of our extension traits.
|
||||||
|
/// This allows adding more trait methods in the future.
|
||||||
|
#[unstable(feature = "sealed", issue = "none")]
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use shared::fs;
|
||||||
|
pub use shared::syscall;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! print {
|
||||||
|
($($args:expr),*) => {
|
||||||
|
$crate::syscall::write_string_temp(&format!($($args),*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! println {
|
||||||
|
() => {
|
||||||
|
$crate::print!("");
|
||||||
|
// $crate::print!("\n\r");
|
||||||
|
};
|
||||||
|
($($args:expr),*) => {
|
||||||
|
$crate::print!($($args),*);
|
||||||
|
// $crate::println!();
|
||||||
|
};
|
||||||
|
}
|
||||||
28
crates/std/src/num/mod.rs
Normal file
28
crates/std/src/num/mod.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//! Additional functionality for numerics.
|
||||||
|
//!
|
||||||
|
//! This module provides some extra types that are useful when doing numerical
|
||||||
|
//! work. See the individual documentation for each piece for more information.
|
||||||
|
|
||||||
|
#![stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
#[stable(feature = "int_error_matching", since = "1.55.0")]
|
||||||
|
pub use core::num::IntErrorKind;
|
||||||
|
#[stable(feature = "generic_nonzero", since = "1.79.0")]
|
||||||
|
pub use core::num::NonZero;
|
||||||
|
#[stable(feature = "saturating_int_impl", since = "1.74.0")]
|
||||||
|
pub use core::num::Saturating;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use core::num::Wrapping;
|
||||||
|
#[unstable(
|
||||||
|
feature = "nonzero_internals",
|
||||||
|
reason = "implementation detail which may disappear or be replaced at any time",
|
||||||
|
issue = "none"
|
||||||
|
)]
|
||||||
|
pub use core::num::ZeroablePrimitive;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError};
|
||||||
|
#[stable(feature = "signed_nonzero", since = "1.34.0")]
|
||||||
|
pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize};
|
||||||
|
#[stable(feature = "nonzero", since = "1.28.0")]
|
||||||
|
pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize};
|
||||||
4047
crates/std/src/path.rs
Normal file
4047
crates/std/src/path.rs
Normal file
File diff suppressed because it is too large
Load Diff
181
crates/std/src/prelude.rs
Normal file
181
crates/std/src/prelude.rs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
pub mod rust_2024 {
|
||||||
|
pub use crate::print;
|
||||||
|
pub use crate::println;
|
||||||
|
pub use alloc::format;
|
||||||
|
pub use alloc::vec;
|
||||||
|
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use crate::borrow::ToOwned;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use crate::boxed::Box;
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use crate::string::{String, ToString};
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use crate::vec::Vec;
|
||||||
|
|
||||||
|
// Re-exported built-in macros and traits
|
||||||
|
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
#[expect(deprecated)]
|
||||||
|
pub use core::prelude::v1::{
|
||||||
|
Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, assert, assert_eq,
|
||||||
|
assert_ne, cfg, column, compile_error, concat, debug_assert, debug_assert_eq,
|
||||||
|
debug_assert_ne, env, file, format_args, include, include_bytes, include_str, line,
|
||||||
|
matches, module_path, option_env, stringify, todo, r#try, unimplemented, unreachable,
|
||||||
|
write, writeln,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use crate::thread_local;
|
||||||
|
|
||||||
|
#[stable(feature = "cfg_select", since = "1.95.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use core::prelude::v1::cfg_select;
|
||||||
|
|
||||||
|
#[unstable(
|
||||||
|
feature = "concat_bytes",
|
||||||
|
issue = "87555",
|
||||||
|
reason = "`concat_bytes` is not stable enough for use and is subject to change"
|
||||||
|
)]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use core::prelude::v1::concat_bytes;
|
||||||
|
|
||||||
|
#[unstable(feature = "const_format_args", issue = "none")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use core::prelude::v1::const_format_args;
|
||||||
|
|
||||||
|
#[unstable(
|
||||||
|
feature = "log_syntax",
|
||||||
|
issue = "29598",
|
||||||
|
reason = "`log_syntax!` is not stable enough for use and is subject to change"
|
||||||
|
)]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use core::prelude::v1::log_syntax;
|
||||||
|
|
||||||
|
#[unstable(
|
||||||
|
feature = "trace_macros",
|
||||||
|
issue = "29598",
|
||||||
|
reason = "`trace_macros` is not stable enough for use and is subject to change"
|
||||||
|
)]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use core::prelude::v1::trace_macros;
|
||||||
|
|
||||||
|
// Do not `doc(no_inline)` so that they become doc items on their own
|
||||||
|
// (no public module for them to be re-exported from).
|
||||||
|
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
|
||||||
|
pub use core::prelude::v1::{
|
||||||
|
alloc_error_handler, bench, derive, global_allocator, test, test_case,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[unstable(feature = "derive_const", issue = "118304")]
|
||||||
|
pub use core::prelude::v1::derive_const;
|
||||||
|
|
||||||
|
// Do not `doc(no_inline)` either.
|
||||||
|
#[unstable(
|
||||||
|
feature = "cfg_accessible",
|
||||||
|
issue = "64797",
|
||||||
|
reason = "`cfg_accessible` is not fully implemented"
|
||||||
|
)]
|
||||||
|
pub use core::prelude::v1::cfg_accessible;
|
||||||
|
|
||||||
|
// Do not `doc(no_inline)` either.
|
||||||
|
#[unstable(
|
||||||
|
feature = "cfg_eval",
|
||||||
|
issue = "82679",
|
||||||
|
reason = "`cfg_eval` is a recently implemented feature"
|
||||||
|
)]
|
||||||
|
pub use core::prelude::v1::cfg_eval;
|
||||||
|
|
||||||
|
// Do not `doc(no_inline)` either.
|
||||||
|
#[unstable(
|
||||||
|
feature = "type_ascription",
|
||||||
|
issue = "23416",
|
||||||
|
reason = "placeholder syntax for type ascription"
|
||||||
|
)]
|
||||||
|
pub use core::prelude::v1::type_ascribe;
|
||||||
|
|
||||||
|
// Do not `doc(no_inline)` either.
|
||||||
|
#[unstable(
|
||||||
|
feature = "deref_patterns",
|
||||||
|
issue = "87121",
|
||||||
|
reason = "placeholder syntax for deref patterns"
|
||||||
|
)]
|
||||||
|
pub use core::prelude::v1::deref;
|
||||||
|
|
||||||
|
// Do not `doc(no_inline)` either.
|
||||||
|
#[unstable(
|
||||||
|
feature = "type_alias_impl_trait",
|
||||||
|
issue = "63063",
|
||||||
|
reason = "`type_alias_impl_trait` has open design concerns"
|
||||||
|
)]
|
||||||
|
pub use core::prelude::v1::define_opaque;
|
||||||
|
|
||||||
|
#[unstable(feature = "extern_item_impls", issue = "125418")]
|
||||||
|
pub use core::prelude::v1::{eii, unsafe_eii};
|
||||||
|
|
||||||
|
#[unstable(feature = "eii_internals", issue = "none")]
|
||||||
|
pub use core::prelude::v1::eii_declaration;
|
||||||
|
|
||||||
|
#[stable(feature = "prelude_2021", since = "1.55.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use core::prelude::rust_2021::*;
|
||||||
|
|
||||||
|
#[stable(feature = "prelude_2024", since = "1.85.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use core::prelude::rust_2024::*;
|
||||||
|
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[doc(no_inline)]
|
||||||
|
pub use crate::convert::{AsMut, AsRef, From, Into};
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
struct GlobalAllocator;
|
||||||
|
|
||||||
|
#[core::prelude::v1::global_allocator]
|
||||||
|
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
|
||||||
|
|
||||||
|
unsafe impl core::alloc::GlobalAlloc for GlobalAllocator {
|
||||||
|
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
|
||||||
|
crate::syscall::alloc(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
|
||||||
|
crate::syscall::dealloc(ptr, layout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
|
||||||
|
// TODO print
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// `argc` and `argv` are passed by the kernel
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize {
|
||||||
|
unsafe extern "Rust" {
|
||||||
|
fn main(argc: isize, argv: *const *const u8) -> isize;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { main(argc, argv) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[lang = "start"]
|
||||||
|
pub fn lang_start<T: crate::process::Termination + 'static>(
|
||||||
|
main: fn() -> T,
|
||||||
|
argc: isize,
|
||||||
|
argv: *const *const u8,
|
||||||
|
_sigpipe: u8,
|
||||||
|
) -> isize {
|
||||||
|
println!("{}", argc);
|
||||||
|
println!("{:?}", argv);
|
||||||
|
main().report().to_isize()
|
||||||
|
}
|
||||||
|
}
|
||||||
29
crates/std/src/process.rs
Normal file
29
crates/std/src/process.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
pub struct ExitCode(isize);
|
||||||
|
|
||||||
|
impl ExitCode {
|
||||||
|
pub const SUCCESS: ExitCode = ExitCode(0);
|
||||||
|
|
||||||
|
pub fn to_isize(self) -> isize {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[lang = "termination"]
|
||||||
|
pub trait Termination {
|
||||||
|
/// Is called to get the representation of the value as status code.
|
||||||
|
/// This status code is returned to the operating system.
|
||||||
|
fn report(self) -> ExitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Termination for () {
|
||||||
|
#[inline]
|
||||||
|
fn report(self) -> ExitCode {
|
||||||
|
ExitCode::SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Termination for isize {
|
||||||
|
#[inline]
|
||||||
|
fn report(self) -> ExitCode {
|
||||||
|
ExitCode(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
6
crates/std/src/rt.rs
Normal file
6
crates/std/src/rt.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
macro_rules! rtabort {
|
||||||
|
($($t:tt)*) => {{}};
|
||||||
|
}
|
||||||
|
macro_rules! rtprintpanic {
|
||||||
|
($($t:tt)*) => {{}};
|
||||||
|
}
|
||||||
14
crates/std/src/sync.rs
Normal file
14
crates/std/src/sync.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
pub mod once {
|
||||||
|
use crate::sys::sync as sys;
|
||||||
|
pub struct OnceState {
|
||||||
|
pub(crate) inner: sys::OnceState,
|
||||||
|
}
|
||||||
|
/// Used for the internal implementation of `sys::sync::once` on different platforms and the
|
||||||
|
/// [`LazyLock`](crate::sync::LazyLock) implementation.
|
||||||
|
pub(crate) enum OnceExclusiveState {
|
||||||
|
Incomplete,
|
||||||
|
Poisoned,
|
||||||
|
Complete,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub use once::OnceState;
|
||||||
113
crates/std/src/sys/alloc/mod.rs
Normal file
113
crates/std/src/sys/alloc/mod.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
use crate::alloc::{GlobalAlloc, Layout, System};
|
||||||
|
use crate::ptr;
|
||||||
|
|
||||||
|
// The minimum alignment guaranteed by the architecture. This value is used to
|
||||||
|
// add fast paths for low alignment values.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const MIN_ALIGN: usize = if cfg!(any(
|
||||||
|
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
|
||||||
|
all(target_arch = "xtensa", target_os = "espidf"),
|
||||||
|
)) {
|
||||||
|
// The allocator on the esp-idf and zkvm platforms guarantees 4 byte alignment.
|
||||||
|
4
|
||||||
|
} else if cfg!(any(
|
||||||
|
target_arch = "x86",
|
||||||
|
target_arch = "arm",
|
||||||
|
target_arch = "m68k",
|
||||||
|
target_arch = "csky",
|
||||||
|
target_arch = "loongarch32",
|
||||||
|
target_arch = "mips",
|
||||||
|
target_arch = "mips32r6",
|
||||||
|
target_arch = "powerpc",
|
||||||
|
target_arch = "powerpc64",
|
||||||
|
target_arch = "sparc",
|
||||||
|
target_arch = "wasm32",
|
||||||
|
target_arch = "hexagon",
|
||||||
|
target_arch = "riscv32",
|
||||||
|
target_arch = "xtensa",
|
||||||
|
)) {
|
||||||
|
8
|
||||||
|
} else if cfg!(any(
|
||||||
|
target_arch = "x86_64",
|
||||||
|
target_arch = "aarch64",
|
||||||
|
target_arch = "arm64ec",
|
||||||
|
target_arch = "loongarch64",
|
||||||
|
target_arch = "mips64",
|
||||||
|
target_arch = "mips64r6",
|
||||||
|
target_arch = "s390x",
|
||||||
|
target_arch = "sparc64",
|
||||||
|
target_arch = "riscv64",
|
||||||
|
target_arch = "wasm64",
|
||||||
|
)) {
|
||||||
|
16
|
||||||
|
} else {
|
||||||
|
panic!("add a value for MIN_ALIGN")
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
unsafe fn realloc_fallback(
|
||||||
|
alloc: &System,
|
||||||
|
ptr: *mut u8,
|
||||||
|
old_layout: Layout,
|
||||||
|
new_size: usize,
|
||||||
|
) -> *mut u8 {
|
||||||
|
// SAFETY: Docs for GlobalAlloc::realloc require this to be valid
|
||||||
|
unsafe {
|
||||||
|
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
|
||||||
|
|
||||||
|
let new_ptr = GlobalAlloc::alloc(alloc, new_layout);
|
||||||
|
if !new_ptr.is_null() {
|
||||||
|
let size = usize::min(old_layout.size(), new_size);
|
||||||
|
ptr::copy_nonoverlapping(ptr, new_ptr, size);
|
||||||
|
GlobalAlloc::dealloc(alloc, ptr, old_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
any(
|
||||||
|
target_family = "unix",
|
||||||
|
target_os = "wasi",
|
||||||
|
target_os = "teeos",
|
||||||
|
target_os = "trusty",
|
||||||
|
) => {
|
||||||
|
mod unix;
|
||||||
|
}
|
||||||
|
target_os = "windows" => {
|
||||||
|
mod windows;
|
||||||
|
}
|
||||||
|
target_os = "hermit" => {
|
||||||
|
mod hermit;
|
||||||
|
}
|
||||||
|
target_os = "motor" => {
|
||||||
|
mod motor;
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
mod sgx;
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod solid;
|
||||||
|
}
|
||||||
|
target_os = "uefi" => {
|
||||||
|
mod uefi;
|
||||||
|
}
|
||||||
|
target_os = "vexos" => {
|
||||||
|
mod vexos;
|
||||||
|
}
|
||||||
|
target_family = "wasm" => {
|
||||||
|
mod wasm;
|
||||||
|
}
|
||||||
|
target_os = "xous" => {
|
||||||
|
mod xous;
|
||||||
|
}
|
||||||
|
target_os = "zkvm" => {
|
||||||
|
mod zkvm;
|
||||||
|
}
|
||||||
|
target_os = "survos" => {
|
||||||
|
mod survos;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
crates/std/src/sys/alloc/survos.rs
Normal file
14
crates/std/src/sys/alloc/survos.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use crate::alloc::{GlobalAlloc, Layout, System};
|
||||||
|
|
||||||
|
#[stable(feature = "alloc_system_type", since = "1.28.0")]
|
||||||
|
unsafe impl GlobalAlloc for System {
|
||||||
|
#[inline]
|
||||||
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
|
unsafe { crate::syscall::alloc(layout) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||||
|
unsafe { crate::syscall::dealloc(ptr, layout) }
|
||||||
|
}
|
||||||
|
}
|
||||||
114
crates/std/src/sys/cmath.rs
Normal file
114
crates/std/src/sys/cmath.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#![cfg(not(test))]
|
||||||
|
|
||||||
|
// These symbols are all defined by `libm`,
|
||||||
|
// or by `compiler-builtins` on unsupported platforms.
|
||||||
|
unsafe extern "C" {
|
||||||
|
pub safe fn acos(n: f64) -> f64;
|
||||||
|
pub safe fn asin(n: f64) -> f64;
|
||||||
|
pub safe fn atan(n: f64) -> f64;
|
||||||
|
pub safe fn atan2(a: f64, b: f64) -> f64;
|
||||||
|
pub safe fn cosh(n: f64) -> f64;
|
||||||
|
pub safe fn expm1(n: f64) -> f64;
|
||||||
|
pub safe fn expm1f(n: f32) -> f32;
|
||||||
|
#[cfg_attr(target_env = "msvc", link_name = "_hypot")]
|
||||||
|
pub safe fn hypot(x: f64, y: f64) -> f64;
|
||||||
|
#[cfg_attr(target_env = "msvc", link_name = "_hypotf")]
|
||||||
|
pub safe fn hypotf(x: f32, y: f32) -> f32;
|
||||||
|
pub safe fn log1p(n: f64) -> f64;
|
||||||
|
pub safe fn log1pf(n: f32) -> f32;
|
||||||
|
pub safe fn sinh(n: f64) -> f64;
|
||||||
|
pub safe fn tan(n: f64) -> f64;
|
||||||
|
pub safe fn tanh(n: f64) -> f64;
|
||||||
|
pub safe fn tgamma(n: f64) -> f64;
|
||||||
|
pub safe fn tgammaf(n: f32) -> f32;
|
||||||
|
pub safe fn lgamma_r(n: f64, s: &mut i32) -> f64;
|
||||||
|
#[cfg(not(target_os = "aix"))]
|
||||||
|
pub safe fn lgammaf_r(n: f32, s: &mut i32) -> f32;
|
||||||
|
pub safe fn erf(n: f64) -> f64;
|
||||||
|
pub safe fn erff(n: f32) -> f32;
|
||||||
|
pub safe fn erfc(n: f64) -> f64;
|
||||||
|
pub safe fn erfcf(n: f32) -> f32;
|
||||||
|
|
||||||
|
pub safe fn acosf128(n: f128) -> f128;
|
||||||
|
pub safe fn asinf128(n: f128) -> f128;
|
||||||
|
pub safe fn atanf128(n: f128) -> f128;
|
||||||
|
pub safe fn atan2f128(a: f128, b: f128) -> f128;
|
||||||
|
pub safe fn cbrtf128(n: f128) -> f128;
|
||||||
|
pub safe fn coshf128(n: f128) -> f128;
|
||||||
|
pub safe fn expm1f128(n: f128) -> f128;
|
||||||
|
pub safe fn hypotf128(x: f128, y: f128) -> f128;
|
||||||
|
pub safe fn log1pf128(n: f128) -> f128;
|
||||||
|
pub safe fn sinhf128(n: f128) -> f128;
|
||||||
|
pub safe fn tanf128(n: f128) -> f128;
|
||||||
|
pub safe fn tanhf128(n: f128) -> f128;
|
||||||
|
pub safe fn tgammaf128(n: f128) -> f128;
|
||||||
|
pub safe fn lgammaf128_r(n: f128, s: &mut i32) -> f128;
|
||||||
|
pub safe fn erff128(n: f128) -> f128;
|
||||||
|
pub safe fn erfcf128(n: f128) -> f128;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
all(target_os = "windows", target_env = "msvc", target_arch = "x86") => {
|
||||||
|
// On 32-bit x86 MSVC these functions aren't defined, so we just define shims
|
||||||
|
// which promote everything to f64, perform the calculation, and then demote
|
||||||
|
// back to f32. While not precisely correct should be "correct enough" for now.
|
||||||
|
#[inline]
|
||||||
|
pub fn acosf(n: f32) -> f32 {
|
||||||
|
f64::acos(n as f64) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn asinf(n: f32) -> f32 {
|
||||||
|
f64::asin(n as f64) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn atan2f(n: f32, b: f32) -> f32 {
|
||||||
|
f64::atan2(n as f64, b as f64) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn atanf(n: f32) -> f32 {
|
||||||
|
f64::atan(n as f64) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn coshf(n: f32) -> f32 {
|
||||||
|
f64::cosh(n as f64) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn sinhf(n: f32) -> f32 {
|
||||||
|
f64::sinh(n as f64) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn tanf(n: f32) -> f32 {
|
||||||
|
f64::tan(n as f64) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn tanhf(n: f32) -> f32 {
|
||||||
|
f64::tanh(n as f64) as f32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unsafe extern "C" {
|
||||||
|
pub safe fn acosf(n: f32) -> f32;
|
||||||
|
pub safe fn asinf(n: f32) -> f32;
|
||||||
|
pub safe fn atan2f(a: f32, b: f32) -> f32;
|
||||||
|
pub safe fn atanf(n: f32) -> f32;
|
||||||
|
pub safe fn coshf(n: f32) -> f32;
|
||||||
|
pub safe fn sinhf(n: f32) -> f32;
|
||||||
|
pub safe fn tanf(n: f32) -> f32;
|
||||||
|
pub safe fn tanhf(n: f32) -> f32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On AIX, we don't have lgammaf_r only the f64 version, so we can
|
||||||
|
// use the f64 version lgamma_r
|
||||||
|
#[cfg(target_os = "aix")]
|
||||||
|
pub fn lgammaf_r(n: f32, s: &mut i32) -> f32 {
|
||||||
|
lgamma_r(n.into(), s) as f32
|
||||||
|
}
|
||||||
62
crates/std/src/sys/configure_builtins.rs
Normal file
62
crates/std/src/sys/configure_builtins.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//! The configure builtins provides runtime support compiler-builtin features
|
||||||
|
//! which require dynamic initialization to work as expected, e.g. aarch64
|
||||||
|
//! outline-atomics.
|
||||||
|
|
||||||
|
/// Enable LSE atomic operations at startup, if supported.
|
||||||
|
///
|
||||||
|
/// Linker sections are based on what [`ctor`] does, with priorities to run slightly before user
|
||||||
|
/// code:
|
||||||
|
///
|
||||||
|
/// - Apple uses the section `__mod_init_func`, `mod_init_funcs` is needed to set
|
||||||
|
/// `S_MOD_INIT_FUNC_POINTERS`. There doesn't seem to be a way to indicate priorities.
|
||||||
|
/// - Windows uses `.CRT$XCT`, which is run before user constructors (these should use `.CRT$XCU`).
|
||||||
|
/// - ELF uses `.init_array` with a priority of 90, which runs before our `ARGV_INIT_ARRAY`
|
||||||
|
/// initializer (priority 99). Both are within the 0-100 implementation-reserved range, per docs
|
||||||
|
/// for the [`prio-ctor-dtor`] warning, and this matches compiler-rt's `CONSTRUCTOR_PRIORITY`.
|
||||||
|
///
|
||||||
|
/// To save startup time, the initializer is only run if outline atomic routines from
|
||||||
|
/// compiler-builtins may be used. If LSE is known to be available then the calls are never
|
||||||
|
/// emitted, and if we build the C intrinsics then it has its own initializer using the symbol
|
||||||
|
/// `__aarch64_have_lse_atomics`.
|
||||||
|
///
|
||||||
|
/// Initialization is done in a global constructor to so we get the same behavior regardless of
|
||||||
|
/// whether Rust's `init` is used, or if we are in a `dylib` or `no_main` situation (as opposed
|
||||||
|
/// to doing it as part of pre-main startup). This also matches C implementations.
|
||||||
|
///
|
||||||
|
/// Ideally `core` would have something similar, but detecting the CPU features requires the
|
||||||
|
/// auxiliary vector from the OS. We do the initialization in `std` rather than as part of
|
||||||
|
/// `compiler-builtins` because a builtins->std dependency isn't possible, and inlining parts of
|
||||||
|
/// `std-detect` would be much messier.
|
||||||
|
///
|
||||||
|
/// [`ctor`]: https://github.com/mmastrac/rust-ctor/blob/63382b833ddcbfb8b064f4e86bfa1ed4026ff356/shared/src/macros/mod.rs#L522-L534
|
||||||
|
/// [`prio-ctor-dtor`]: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
|
||||||
|
#[cfg(all(
|
||||||
|
target_arch = "aarch64",
|
||||||
|
target_feature = "outline-atomics",
|
||||||
|
not(target_feature = "lse"),
|
||||||
|
not(feature = "compiler-builtins-c"),
|
||||||
|
))]
|
||||||
|
#[used]
|
||||||
|
#[cfg_attr(target_vendor = "apple", unsafe(link_section = "__DATA,__mod_init_func,mod_init_funcs"))]
|
||||||
|
#[cfg_attr(target_os = "windows", unsafe(link_section = ".CRT$XCT"))]
|
||||||
|
#[cfg_attr(
|
||||||
|
not(any(target_vendor = "apple", target_os = "windows")),
|
||||||
|
unsafe(link_section = ".init_array.90")
|
||||||
|
)]
|
||||||
|
static RUST_LSE_INIT: extern "C" fn() = {
|
||||||
|
extern "C" fn init_lse() {
|
||||||
|
use crate::arch;
|
||||||
|
|
||||||
|
// This is provided by compiler-builtins::aarch64_outline_atomics.
|
||||||
|
unsafe extern "C" {
|
||||||
|
fn __rust_enable_lse();
|
||||||
|
}
|
||||||
|
|
||||||
|
if arch::is_aarch64_feature_detected!("lse") {
|
||||||
|
unsafe {
|
||||||
|
__rust_enable_lse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init_lse
|
||||||
|
};
|
||||||
31
crates/std/src/sys/env/common.rs
vendored
Normal file
31
crates/std/src/sys/env/common.rs
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use crate::ffi::OsString;
|
||||||
|
use crate::{fmt, vec};
|
||||||
|
|
||||||
|
pub struct Env {
|
||||||
|
iter: vec::IntoIter<(OsString, OsString)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Env {
|
||||||
|
pub(super) fn new(env: Vec<(OsString, OsString)>) -> Self {
|
||||||
|
Env { iter: env.into_iter() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Env {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_list().entries(self.iter.as_slice()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl !Send for Env {}
|
||||||
|
impl !Sync for Env {}
|
||||||
|
|
||||||
|
impl Iterator for Env {
|
||||||
|
type Item = (OsString, OsString);
|
||||||
|
fn next(&mut self) -> Option<(OsString, OsString)> {
|
||||||
|
self.iter.next()
|
||||||
|
}
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
self.iter.size_hint()
|
||||||
|
}
|
||||||
|
}
|
||||||
62
crates/std/src/sys/env/mod.rs
vendored
Normal file
62
crates/std/src/sys/env/mod.rs
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//! Platform-dependent environment variables abstraction.
|
||||||
|
|
||||||
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
target_family = "unix",
|
||||||
|
target_os = "hermit",
|
||||||
|
target_os = "motor",
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx"),
|
||||||
|
target_os = "solid_asp3",
|
||||||
|
target_os = "uefi",
|
||||||
|
target_os = "wasi",
|
||||||
|
target_os = "xous",
|
||||||
|
))]
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
target_family = "unix" => {
|
||||||
|
mod unix;
|
||||||
|
pub use unix::*;
|
||||||
|
}
|
||||||
|
target_family = "windows" => {
|
||||||
|
mod windows;
|
||||||
|
pub use windows::*;
|
||||||
|
}
|
||||||
|
target_os = "hermit" => {
|
||||||
|
mod hermit;
|
||||||
|
pub use hermit::*;
|
||||||
|
}
|
||||||
|
target_os = "motor" => {
|
||||||
|
mod motor;
|
||||||
|
pub use motor::*;
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
mod sgx;
|
||||||
|
pub use sgx::*;
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod solid;
|
||||||
|
pub use solid::*;
|
||||||
|
}
|
||||||
|
target_os = "uefi" => {
|
||||||
|
mod uefi;
|
||||||
|
pub use uefi::*;
|
||||||
|
}
|
||||||
|
target_os = "wasi" => {
|
||||||
|
mod wasi;
|
||||||
|
pub use wasi::*;
|
||||||
|
}
|
||||||
|
target_os = "xous" => {
|
||||||
|
mod xous;
|
||||||
|
pub use xous::*;
|
||||||
|
}
|
||||||
|
target_os = "zkvm" => {
|
||||||
|
mod zkvm;
|
||||||
|
pub use zkvm::*;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
crates/std/src/sys/env/unsupported.rs
vendored
Normal file
33
crates/std/src/sys/env/unsupported.rs
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
use crate::ffi::{OsStr, OsString};
|
||||||
|
use crate::{fmt, io};
|
||||||
|
|
||||||
|
pub struct Env(!);
|
||||||
|
|
||||||
|
impl fmt::Debug for Env {
|
||||||
|
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Env {
|
||||||
|
type Item = (OsString, OsString);
|
||||||
|
fn next(&mut self) -> Option<(OsString, OsString)> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn env() -> Env {
|
||||||
|
panic!("not supported on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getenv(_: &OsStr) -> Option<OsString> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
|
||||||
|
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
|
||||||
|
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
|
||||||
|
}
|
||||||
426
crates/std/src/sys/env_consts.rs
Normal file
426
crates/std/src/sys/env_consts.rs
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
//! Constants associated with each target.
|
||||||
|
|
||||||
|
// Replaces the #[else] gate with #[cfg(not(any(…)))] of all the other gates.
|
||||||
|
// This ensures that they must be mutually exclusive and do not have precedence
|
||||||
|
// like cfg_select!.
|
||||||
|
macro cfg_unordered(
|
||||||
|
$(#[cfg($cfg:meta)] $os:item)*
|
||||||
|
#[else] $fallback:item
|
||||||
|
) {
|
||||||
|
$(#[cfg($cfg)] $os)*
|
||||||
|
#[cfg(not(any($($cfg),*)))] $fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep entries sorted alphabetically and mutually exclusive.
|
||||||
|
|
||||||
|
cfg_unordered! {
|
||||||
|
|
||||||
|
#[cfg(target_os = "aix")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "aix";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".a";
|
||||||
|
pub const DLL_EXTENSION: &str = "a";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "android";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "cygwin")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "cygwin";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = ".dll";
|
||||||
|
pub const DLL_EXTENSION: &str = "dll";
|
||||||
|
pub const EXE_SUFFIX: &str = ".exe";
|
||||||
|
pub const EXE_EXTENSION: &str = "exe";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "dragonfly")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "dragonfly";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "emscripten")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "emscripten";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = ".js";
|
||||||
|
pub const EXE_EXTENSION: &str = "js";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "espidf")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "espidf";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "freebsd";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "fuchsia")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "fuchsia";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "haiku")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "haiku";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "hermit")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "";
|
||||||
|
pub const OS: &str = "hermit";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = "";
|
||||||
|
pub const DLL_EXTENSION: &str = "";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "horizon")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "horizon";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = ".elf";
|
||||||
|
pub const EXE_EXTENSION: &str = "elf";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "hurd")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "hurd";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "illumos")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "illumos";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "ios")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "ios";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".dylib";
|
||||||
|
pub const DLL_EXTENSION: &str = "dylib";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "l4re")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "l4re";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "linux";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "macos";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".dylib";
|
||||||
|
pub const DLL_EXTENSION: &str = "dylib";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "netbsd")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "netbsd";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "nto")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "nto";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "nuttx")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "nuttx";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "openbsd")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "openbsd";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "redox";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "rtems")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "rtems";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "";
|
||||||
|
pub const OS: &str = "";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = ".sgxs";
|
||||||
|
pub const DLL_EXTENSION: &str = "sgxs";
|
||||||
|
pub const EXE_SUFFIX: &str = ".sgxs";
|
||||||
|
pub const EXE_EXTENSION: &str = "sgxs";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "solaris")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "solaris";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "solid_asp3")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "itron";
|
||||||
|
pub const OS: &str = "solid";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "tvos")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "tvos";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".dylib";
|
||||||
|
pub const DLL_EXTENSION: &str = "dylib";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "uefi")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "";
|
||||||
|
pub const OS: &str = "uefi";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = "";
|
||||||
|
pub const DLL_EXTENSION: &str = "";
|
||||||
|
pub const EXE_SUFFIX: &str = ".efi";
|
||||||
|
pub const EXE_EXTENSION: &str = "efi";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "vexos")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "";
|
||||||
|
pub const OS: &str = "vexos";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = "";
|
||||||
|
pub const DLL_EXTENSION: &str = "";
|
||||||
|
pub const EXE_SUFFIX: &str = ".bin";
|
||||||
|
pub const EXE_EXTENSION: &str = "bin";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "visionos")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "visionos";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".dylib";
|
||||||
|
pub const DLL_EXTENSION: &str = "dylib";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "vita")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "vita";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = ".elf";
|
||||||
|
pub const EXE_EXTENSION: &str = "elf";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "vxworks")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "vxworks";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".so";
|
||||||
|
pub const DLL_EXTENSION: &str = "so";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_family = "wasm", not(any(target_os = "emscripten", target_os = "linux"))))]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "";
|
||||||
|
pub const OS: &str = "";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = ".wasm";
|
||||||
|
pub const DLL_EXTENSION: &str = "wasm";
|
||||||
|
pub const EXE_SUFFIX: &str = ".wasm";
|
||||||
|
pub const EXE_EXTENSION: &str = "wasm";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "watchos")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "unix";
|
||||||
|
pub const OS: &str = "watchos";
|
||||||
|
pub const DLL_PREFIX: &str = "lib";
|
||||||
|
pub const DLL_SUFFIX: &str = ".dylib";
|
||||||
|
pub const DLL_EXTENSION: &str = "dylib";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "windows";
|
||||||
|
pub const OS: &str = "windows";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = ".dll";
|
||||||
|
pub const DLL_EXTENSION: &str = "dll";
|
||||||
|
pub const EXE_SUFFIX: &str = ".exe";
|
||||||
|
pub const EXE_EXTENSION: &str = "exe";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "zkvm")]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "";
|
||||||
|
pub const OS: &str = "";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = ".elf";
|
||||||
|
pub const DLL_EXTENSION: &str = "elf";
|
||||||
|
pub const EXE_SUFFIX: &str = ".elf";
|
||||||
|
pub const EXE_EXTENSION: &str = "elf";
|
||||||
|
}
|
||||||
|
|
||||||
|
// The fallback when none of the other gates match.
|
||||||
|
#[else]
|
||||||
|
pub mod os {
|
||||||
|
pub const FAMILY: &str = "";
|
||||||
|
pub const OS: &str = "";
|
||||||
|
pub const DLL_PREFIX: &str = "";
|
||||||
|
pub const DLL_SUFFIX: &str = "";
|
||||||
|
pub const DLL_EXTENSION: &str = "";
|
||||||
|
pub const EXE_SUFFIX: &str = "";
|
||||||
|
pub const EXE_EXTENSION: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
143
crates/std/src/sys/exit.rs
Normal file
143
crates/std/src/sys/exit.rs
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
cfg_select! {
|
||||||
|
target_os = "linux" => {
|
||||||
|
/// Mitigation for <https://github.com/rust-lang/rust/issues/126600>
|
||||||
|
///
|
||||||
|
/// On glibc, `libc::exit` has been observed to not always be thread-safe.
|
||||||
|
/// It is currently unclear whether that is a glibc bug or allowed by the standard.
|
||||||
|
/// To mitigate this problem, we ensure that only one
|
||||||
|
/// Rust thread calls `libc::exit` (or returns from `main`) by calling this function before
|
||||||
|
/// calling `libc::exit` (or returning from `main`).
|
||||||
|
///
|
||||||
|
/// Technically, this is not enough to ensure soundness, since other code directly calling
|
||||||
|
/// `libc::exit` will still race with this.
|
||||||
|
///
|
||||||
|
/// *This function does not itself call `libc::exit`.* This is so it can also be used
|
||||||
|
/// to guard returning from `main`.
|
||||||
|
///
|
||||||
|
/// This function will return only the first time it is called in a process.
|
||||||
|
///
|
||||||
|
/// * If it is called again on the same thread as the first call, it will abort.
|
||||||
|
/// * If it is called again on a different thread, it will wait in a loop
|
||||||
|
/// (waiting for the process to exit).
|
||||||
|
pub fn unique_thread_exit() {
|
||||||
|
use crate::ffi::c_int;
|
||||||
|
use crate::ptr;
|
||||||
|
use crate::sync::atomic::AtomicPtr;
|
||||||
|
use crate::sync::atomic::Ordering::{Acquire, Relaxed};
|
||||||
|
|
||||||
|
static EXITING_THREAD_ID: AtomicPtr<c_int> = AtomicPtr::new(ptr::null_mut());
|
||||||
|
|
||||||
|
// We use the address of `errno` as a cheap and safe way to identify
|
||||||
|
// threads. As the C standard mandates that `errno` must have thread
|
||||||
|
// storage duration, we can rely on its address not changing over the
|
||||||
|
// lifetime of the thread. Additionally, accesses to `errno` are
|
||||||
|
// async-signal-safe, so this function is available in all imaginable
|
||||||
|
// circumstances.
|
||||||
|
let this_thread_id = crate::sys::io::errno_location();
|
||||||
|
match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) {
|
||||||
|
Ok(_) => {
|
||||||
|
// This is the first thread to call `unique_thread_exit`,
|
||||||
|
// and this is the first time it is called. Continue exiting.
|
||||||
|
}
|
||||||
|
Err(exiting_thread_id) if exiting_thread_id == this_thread_id => {
|
||||||
|
// This is the first thread to call `unique_thread_exit`,
|
||||||
|
// but this is the second time it is called.
|
||||||
|
// Abort the process.
|
||||||
|
core::panicking::panic_nounwind("std::process::exit called re-entrantly")
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// This is not the first thread to call `unique_thread_exit`.
|
||||||
|
// Pause until the process exits.
|
||||||
|
loop {
|
||||||
|
// Safety: libc::pause is safe to call.
|
||||||
|
unsafe { libc::pause(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
/// Mitigation for <https://github.com/rust-lang/rust/issues/126600>
|
||||||
|
///
|
||||||
|
/// Mitigation is ***NOT*** implemented on this platform, either because this platform
|
||||||
|
/// is not affected, or because mitigation is not yet implemented for this platform.
|
||||||
|
#[cfg_attr(any(test, doctest), expect(dead_code))]
|
||||||
|
pub fn unique_thread_exit() {
|
||||||
|
// Mitigation not required on platforms where `exit` is thread-safe.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit(code: i32) -> ! {
|
||||||
|
cfg_select! {
|
||||||
|
target_os = "hermit" => {
|
||||||
|
unsafe { hermit_abi::exit(code) }
|
||||||
|
}
|
||||||
|
target_os = "linux" => {
|
||||||
|
unsafe {
|
||||||
|
unique_thread_exit();
|
||||||
|
libc::exit(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target_os = "motor" => {
|
||||||
|
moto_rt::process::exit(code)
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
crate::sys::pal::abi::exit_with_code(code as _)
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
rtabort!("exit({}) called", code)
|
||||||
|
}
|
||||||
|
target_os = "teeos" => {
|
||||||
|
let _ = code;
|
||||||
|
panic!("TA should not call `exit`")
|
||||||
|
}
|
||||||
|
target_os = "uefi" => {
|
||||||
|
use r_efi::base::Status;
|
||||||
|
|
||||||
|
use crate::os::uefi::env;
|
||||||
|
|
||||||
|
if let (Some(boot_services), Some(handle)) =
|
||||||
|
(env::boot_services(), env::try_image_handle())
|
||||||
|
{
|
||||||
|
let boot_services = boot_services.cast::<r_efi::efi::BootServices>();
|
||||||
|
let _ = unsafe {
|
||||||
|
((*boot_services.as_ptr()).exit)(
|
||||||
|
handle.as_ptr(),
|
||||||
|
Status::from_usize(code as usize),
|
||||||
|
0,
|
||||||
|
crate::ptr::null_mut(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
crate::intrinsics::abort()
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
target_family = "unix",
|
||||||
|
target_os = "wasi",
|
||||||
|
) => {
|
||||||
|
unsafe { libc::exit(code as crate::ffi::c_int) }
|
||||||
|
}
|
||||||
|
target_os = "vexos" => {
|
||||||
|
let _ = code;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
vex_sdk::vexSystemExitRequest();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
vex_sdk::vexTasksRun();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target_os = "windows" => {
|
||||||
|
unsafe { crate::sys::pal::c::ExitProcess(code as u32) }
|
||||||
|
}
|
||||||
|
target_os = "xous" => {
|
||||||
|
crate::os::xous::ffi::exit(code as u32)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let _ = code;
|
||||||
|
crate::intrinsics::abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
crates/std/src/sys/fs/common.rs
Normal file
81
crates/std/src/sys/fs/common.rs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#![allow(dead_code)] // not used on all platforms
|
||||||
|
|
||||||
|
use crate::io::{self, Error, ErrorKind};
|
||||||
|
use crate::path::{Path, PathBuf};
|
||||||
|
use crate::sys::fs::{File, OpenOptions};
|
||||||
|
use crate::sys::helpers::ignore_notfound;
|
||||||
|
use crate::{fmt, fs};
|
||||||
|
|
||||||
|
pub(crate) const NOT_FILE_ERROR: Error = io::const_error!(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"the source path is neither a regular file nor a symlink to a regular file",
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||||
|
let mut reader = fs::File::open(from)?;
|
||||||
|
let metadata = reader.metadata()?;
|
||||||
|
|
||||||
|
if !metadata.is_file() {
|
||||||
|
return Err(NOT_FILE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut writer = fs::File::create(to)?;
|
||||||
|
let perm = metadata.permissions();
|
||||||
|
|
||||||
|
let ret = io::copy(&mut reader, &mut writer)?;
|
||||||
|
writer.set_permissions(perm)?;
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
|
||||||
|
let filetype = fs::symlink_metadata(path)?.file_type();
|
||||||
|
if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_recursive(path) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
|
||||||
|
for child in fs::read_dir(path)? {
|
||||||
|
let result: io::Result<()> = try {
|
||||||
|
let child = child?;
|
||||||
|
if child.file_type()?.is_dir() {
|
||||||
|
remove_dir_all_recursive(&child.path())?;
|
||||||
|
} else {
|
||||||
|
fs::remove_file(&child.path())?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// ignore internal NotFound errors to prevent race conditions
|
||||||
|
if let Err(err) = &result
|
||||||
|
&& err.kind() != io::ErrorKind::NotFound
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ignore_notfound(fs::remove_dir(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exists(path: &Path) -> io::Result<bool> {
|
||||||
|
match fs::metadata(path) {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
|
||||||
|
Err(error) => Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Dir {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dir {
|
||||||
|
pub fn open(path: &Path, _opts: &OpenOptions) -> io::Result<Self> {
|
||||||
|
path.canonicalize().map(|path| Self { path })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
||||||
|
File::open(&self.path.join(path), &opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Dir {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("Dir").field("path", &self.path).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
173
crates/std/src/sys/fs/mod.rs
Normal file
173
crates/std/src/sys/fs/mod.rs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
use crate::io;
|
||||||
|
use crate::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub mod common;
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
any(target_family = "unix", target_os = "wasi") => {
|
||||||
|
mod unix;
|
||||||
|
use unix as imp;
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
|
pub use unix::{chown, fchown, lchown, mkfifo};
|
||||||
|
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
|
||||||
|
pub use unix::chroot;
|
||||||
|
#[cfg(not(target_os = "wasi"))]
|
||||||
|
pub(crate) use unix::debug_assert_fd_is_open;
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub(super) use unix::CachedFileMetadata;
|
||||||
|
use crate::sys::helpers::run_path_with_cstr as with_native_path;
|
||||||
|
}
|
||||||
|
target_os = "windows" => {
|
||||||
|
mod windows;
|
||||||
|
use windows as imp;
|
||||||
|
pub use windows::{symlink_inner, junction_point};
|
||||||
|
use crate::sys::path::with_native_path;
|
||||||
|
}
|
||||||
|
target_os = "hermit" => {
|
||||||
|
mod hermit;
|
||||||
|
use hermit as imp;
|
||||||
|
}
|
||||||
|
target_os = "motor" => {
|
||||||
|
mod motor;
|
||||||
|
use motor as imp;
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod solid;
|
||||||
|
use solid as imp;
|
||||||
|
}
|
||||||
|
target_os = "uefi" => {
|
||||||
|
mod uefi;
|
||||||
|
use uefi as imp;
|
||||||
|
}
|
||||||
|
target_os = "vexos" => {
|
||||||
|
mod vexos;
|
||||||
|
use vexos as imp;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod unsupported;
|
||||||
|
use unsupported as imp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Replace this with platform-specific path conversion functions.
|
||||||
|
#[cfg(not(any(target_family = "unix", target_os = "windows", target_os = "wasi")))]
|
||||||
|
#[inline]
|
||||||
|
pub fn with_native_path<T>(path: &Path, f: &dyn Fn(&Path) -> io::Result<T>) -> io::Result<T> {
|
||||||
|
f(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use imp::{
|
||||||
|
Dir, DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
|
||||||
|
ReadDir,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn read_dir(path: &Path) -> io::Result<ReadDir> {
|
||||||
|
// FIXME: use with_native_path on all platforms
|
||||||
|
imp::readdir(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_file(path: &Path) -> io::Result<()> {
|
||||||
|
with_native_path(path, &imp::unlink)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
|
||||||
|
with_native_path(old, &|old| with_native_path(new, &|new| imp::rename(old, new)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_dir(path: &Path) -> io::Result<()> {
|
||||||
|
with_native_path(path, &imp::rmdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
|
||||||
|
// FIXME: use with_native_path on all platforms
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
return imp::remove_dir_all(path);
|
||||||
|
#[cfg(windows)]
|
||||||
|
with_native_path(path, &imp::remove_dir_all)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_link(path: &Path) -> io::Result<PathBuf> {
|
||||||
|
with_native_path(path, &imp::readlink)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
|
||||||
|
// FIXME: use with_native_path on all platforms
|
||||||
|
#[cfg(windows)]
|
||||||
|
return imp::symlink(original, link);
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
with_native_path(original, &|original| {
|
||||||
|
with_native_path(link, &|link| imp::symlink(original, link))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hard_link(original: &Path, link: &Path) -> io::Result<()> {
|
||||||
|
with_native_path(original, &|original| {
|
||||||
|
with_native_path(link, &|link| imp::link(original, link))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(path: &Path) -> io::Result<FileAttr> {
|
||||||
|
with_native_path(path, &imp::stat)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symlink_metadata(path: &Path) -> io::Result<FileAttr> {
|
||||||
|
with_native_path(path, &imp::lstat)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_permissions(path: &Path, perm: FilePermissions) -> io::Result<()> {
|
||||||
|
with_native_path(path, &|path| imp::set_perm(path, perm.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(unix, not(target_os = "vxworks")))]
|
||||||
|
pub fn set_permissions_nofollow(path: &Path, perm: crate::fs::Permissions) -> io::Result<()> {
|
||||||
|
use crate::fs::OpenOptions;
|
||||||
|
|
||||||
|
let mut options = OpenOptions::new();
|
||||||
|
|
||||||
|
// ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it.
|
||||||
|
// Their filesystems do not have symbolic links, so no special handling is required.
|
||||||
|
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
|
||||||
|
{
|
||||||
|
use crate::os::unix::fs::OpenOptionsExt;
|
||||||
|
options.custom_flags(libc::O_NOFOLLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.open(path)?.set_permissions(perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(not(unix), target_os = "vxworks"))]
|
||||||
|
pub fn set_permissions_nofollow(_path: &Path, _perm: crate::fs::Permissions) -> io::Result<()> {
|
||||||
|
crate::unimplemented!(
|
||||||
|
"`set_permissions_nofollow` is currently only implemented on Unix platforms"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn canonicalize(path: &Path) -> io::Result<PathBuf> {
|
||||||
|
with_native_path(path, &imp::canonicalize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||||
|
// FIXME: use with_native_path on all platforms
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
return imp::copy(from, to);
|
||||||
|
#[cfg(windows)]
|
||||||
|
with_native_path(from, &|from| with_native_path(to, &|to| imp::copy(from, to)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exists(path: &Path) -> io::Result<bool> {
|
||||||
|
// FIXME: use with_native_path on all platforms
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
return imp::exists(path);
|
||||||
|
#[cfg(windows)]
|
||||||
|
with_native_path(path, &imp::exists)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_times(path: &Path, times: FileTimes) -> io::Result<()> {
|
||||||
|
with_native_path(path, &|path| imp::set_times(path, times.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_times_nofollow(path: &Path, times: FileTimes) -> io::Result<()> {
|
||||||
|
with_native_path(path, &|path| imp::set_times_nofollow(path, times.clone()))
|
||||||
|
}
|
||||||
362
crates/std/src/sys/fs/unsupported.rs
Normal file
362
crates/std/src/sys/fs/unsupported.rs
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
use crate::ffi::OsString;
|
||||||
|
use crate::fmt;
|
||||||
|
use crate::fs::TryLockError;
|
||||||
|
use crate::hash::{Hash, Hasher};
|
||||||
|
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
|
||||||
|
use crate::path::{Path, PathBuf};
|
||||||
|
pub use crate::sys::fs::common::Dir;
|
||||||
|
use crate::sys::time::SystemTime;
|
||||||
|
use crate::sys::unsupported;
|
||||||
|
|
||||||
|
pub struct File(!);
|
||||||
|
|
||||||
|
pub struct FileAttr(!);
|
||||||
|
|
||||||
|
pub struct ReadDir(!);
|
||||||
|
|
||||||
|
pub struct DirEntry(!);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct OpenOptions {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub struct FileTimes {}
|
||||||
|
|
||||||
|
pub struct FilePermissions(!);
|
||||||
|
|
||||||
|
pub struct FileType(!);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DirBuilder {}
|
||||||
|
|
||||||
|
impl FileAttr {
|
||||||
|
pub fn size(&self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn perm(&self) -> FilePermissions {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_type(&self) -> FileType {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modified(&self) -> io::Result<SystemTime> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn accessed(&self) -> io::Result<SystemTime> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn created(&self) -> io::Result<SystemTime> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for FileAttr {
|
||||||
|
fn clone(&self) -> FileAttr {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilePermissions {
|
||||||
|
pub fn readonly(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_readonly(&mut self, _readonly: bool) {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for FilePermissions {
|
||||||
|
fn clone(&self) -> FilePermissions {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for FilePermissions {
|
||||||
|
fn eq(&self, _other: &FilePermissions) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for FilePermissions {}
|
||||||
|
|
||||||
|
impl fmt::Debug for FilePermissions {
|
||||||
|
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileTimes {
|
||||||
|
pub fn set_accessed(&mut self, _t: SystemTime) {}
|
||||||
|
pub fn set_modified(&mut self, _t: SystemTime) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileType {
|
||||||
|
pub fn is_dir(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_file(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_symlink(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for FileType {
|
||||||
|
fn clone(&self) -> FileType {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Copy for FileType {}
|
||||||
|
|
||||||
|
impl PartialEq for FileType {
|
||||||
|
fn eq(&self, _other: &FileType) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for FileType {}
|
||||||
|
|
||||||
|
impl Hash for FileType {
|
||||||
|
fn hash<H: Hasher>(&self, _h: &mut H) {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for FileType {
|
||||||
|
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ReadDir {
|
||||||
|
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for ReadDir {
|
||||||
|
type Item = io::Result<DirEntry>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<io::Result<DirEntry>> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirEntry {
|
||||||
|
pub fn path(&self) -> PathBuf {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_name(&self) -> OsString {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(&self) -> io::Result<FileAttr> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_type(&self) -> io::Result<FileType> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OpenOptions {
|
||||||
|
pub fn new() -> OpenOptions {
|
||||||
|
OpenOptions {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, _read: bool) {}
|
||||||
|
pub fn write(&mut self, _write: bool) {}
|
||||||
|
pub fn append(&mut self, _append: bool) {}
|
||||||
|
pub fn truncate(&mut self, _truncate: bool) {}
|
||||||
|
pub fn create(&mut self, _create: bool) {}
|
||||||
|
pub fn create_new(&mut self, _create_new: bool) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_attr(&self) -> io::Result<FileAttr> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fsync(&self) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn datasync(&self) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lock(&self) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lock_shared(&self) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_lock(&self) -> Result<(), TryLockError> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlock(&self) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn truncate(&self, _size: u64) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_read_vectored(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_write_vectored(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&self) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> Option<io::Result<u64>> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tell(&self) -> io::Result<u64> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn duplicate(&self) -> io::Result<File> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirBuilder {
|
||||||
|
pub fn new() -> DirBuilder {
|
||||||
|
DirBuilder {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for File {
|
||||||
|
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlink(_p: &Path) -> io::Result<()> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
|
||||||
|
match perm.0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rmdir(_p: &Path) -> io::Result<()> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exists(_path: &Path) -> io::Result<bool> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stat(_p: &Path) -> io::Result<FileAttr> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
|
||||||
|
unsupported()
|
||||||
|
}
|
||||||
36
crates/std/src/sys/mod.rs
Normal file
36
crates/std/src/sys/mod.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
pub mod cmath;
|
||||||
|
pub mod configure_builtins;
|
||||||
|
pub mod env;
|
||||||
|
pub mod env_consts;
|
||||||
|
pub mod exit;
|
||||||
|
pub mod os_str;
|
||||||
|
pub mod path;
|
||||||
|
pub mod sync;
|
||||||
|
pub mod thread;
|
||||||
|
pub mod time;
|
||||||
|
pub mod random;
|
||||||
|
pub mod thread_local;
|
||||||
|
pub mod alloc;
|
||||||
|
// pub mod fs;
|
||||||
|
|
||||||
|
/// A trait for viewing representations from std types.
|
||||||
|
#[cfg_attr(not(target_os = "linux"), allow(unused))]
|
||||||
|
pub(crate) trait AsInner<Inner: ?Sized> {
|
||||||
|
fn as_inner(&self) -> &Inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for viewing representations from std types.
|
||||||
|
#[cfg_attr(not(target_os = "linux"), allow(unused))]
|
||||||
|
pub(crate) trait AsInnerMut<Inner: ?Sized> {
|
||||||
|
fn as_inner_mut(&mut self) -> &mut Inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for extracting representations from std types.
|
||||||
|
pub(crate) trait IntoInner<Inner> {
|
||||||
|
fn into_inner(self) -> Inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for creating std types from internal representations.
|
||||||
|
pub(crate) trait FromInner<Inner> {
|
||||||
|
fn from_inner(inner: Inner) -> Self;
|
||||||
|
}
|
||||||
363
crates/std/src/sys/os_str/bytes.rs
Normal file
363
crates/std/src/sys/os_str/bytes.rs
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
//! The underlying OsString/OsStr implementation on Unix and many other
|
||||||
|
//! systems: just a `Vec<u8>`/`[u8]`.
|
||||||
|
|
||||||
|
use core::clone::CloneToUninit;
|
||||||
|
|
||||||
|
use crate::borrow::Cow;
|
||||||
|
use alloc_crate::bstr::ByteStr;
|
||||||
|
use alloc_crate::collections::TryReserveError;
|
||||||
|
use crate::rc::Rc;
|
||||||
|
use alloc_crate::sync::Arc;
|
||||||
|
use crate::sys::{AsInner, FromInner, IntoInner};
|
||||||
|
use crate::{fmt, mem, str};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
#[derive(Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Buf {
|
||||||
|
pub inner: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Slice {
|
||||||
|
pub inner: [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoInner<Vec<u8>> for Buf {
|
||||||
|
fn into_inner(self) -> Vec<u8> {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromInner<Vec<u8>> for Buf {
|
||||||
|
fn from_inner(inner: Vec<u8>) -> Self {
|
||||||
|
Buf { inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsInner<[u8]> for Buf {
|
||||||
|
#[inline]
|
||||||
|
fn as_inner(&self) -> &[u8] {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Buf {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(self.as_slice(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Buf {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(self.as_slice(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Slice {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.inner.utf8_chunks().debug(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Slice {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(ByteStr::new(&self.inner), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Buf {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Buf { inner: self.inner.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
self.inner.clone_from(&source.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buf {
|
||||||
|
#[inline]
|
||||||
|
pub fn into_encoded_bytes(self) -> Vec<u8> {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn from_encoded_bytes_unchecked(s: Vec<u8>) -> Self {
|
||||||
|
Self { inner: s }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_string(self) -> Result<String, Buf> {
|
||||||
|
String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn from_string(s: String) -> Buf {
|
||||||
|
Buf { inner: s.into_bytes() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn with_capacity(capacity: usize) -> Buf {
|
||||||
|
Buf { inner: Vec::with_capacity(capacity) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.inner.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn capacity(&self) -> usize {
|
||||||
|
self.inner.capacity()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn push_slice(&mut self, s: &Slice) {
|
||||||
|
self.inner.extend_from_slice(&s.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn push_str(&mut self, s: &str) {
|
||||||
|
self.inner.extend_from_slice(s.as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn reserve(&mut self, additional: usize) {
|
||||||
|
self.inner.reserve(additional)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
|
||||||
|
self.inner.try_reserve(additional)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn reserve_exact(&mut self, additional: usize) {
|
||||||
|
self.inner.reserve_exact(additional)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
|
||||||
|
self.inner.try_reserve_exact(additional)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn shrink_to_fit(&mut self) {
|
||||||
|
self.inner.shrink_to_fit()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn shrink_to(&mut self, min_capacity: usize) {
|
||||||
|
self.inner.shrink_to(min_capacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn as_slice(&self) -> &Slice {
|
||||||
|
// SAFETY: Slice is just a wrapper for [u8],
|
||||||
|
// and self.inner.as_slice() returns &[u8].
|
||||||
|
// Therefore, transmuting &[u8] to &Slice is safe.
|
||||||
|
unsafe { mem::transmute(self.inner.as_slice()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn as_mut_slice(&mut self) -> &mut Slice {
|
||||||
|
// SAFETY: Slice is just a wrapper for [u8],
|
||||||
|
// and self.inner.as_mut_slice() returns &mut [u8].
|
||||||
|
// Therefore, transmuting &mut [u8] to &mut Slice is safe.
|
||||||
|
unsafe { mem::transmute(self.inner.as_mut_slice()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn leak<'a>(self) -> &'a mut Slice {
|
||||||
|
unsafe { mem::transmute(self.inner.leak()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_box(self) -> Box<Slice> {
|
||||||
|
unsafe { mem::transmute(self.inner.into_boxed_slice()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_box(boxed: Box<Slice>) -> Buf {
|
||||||
|
let inner: Box<[u8]> = unsafe { mem::transmute(boxed) };
|
||||||
|
Buf { inner: inner.into_vec() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_arc(&self) -> Arc<Slice> {
|
||||||
|
self.as_slice().into_arc()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_rc(&self) -> Rc<Slice> {
|
||||||
|
self.as_slice().into_rc()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides plumbing to `Vec::truncate` without giving full mutable access
|
||||||
|
/// to the `Vec`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The length must be at an `OsStr` boundary, according to
|
||||||
|
/// `Slice::check_public_boundary`.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn truncate_unchecked(&mut self, len: usize) {
|
||||||
|
self.inner.truncate(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides plumbing to `Vec::extend_from_slice` without giving full
|
||||||
|
/// mutable access to the `Vec`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The slice must be valid for the platform encoding (as described in
|
||||||
|
/// `OsStr::from_encoded_bytes_unchecked`). This encoding has no safety
|
||||||
|
/// requirements.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) {
|
||||||
|
self.inner.extend_from_slice(other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Slice {
|
||||||
|
#[inline]
|
||||||
|
pub fn as_encoded_bytes(&self) -> &[u8] {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice {
|
||||||
|
unsafe { mem::transmute(s) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
#[inline]
|
||||||
|
pub fn check_public_boundary(&self, index: usize) {
|
||||||
|
if index == 0 || index == self.inner.len() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if index < self.inner.len()
|
||||||
|
&& (self.inner[index - 1].is_ascii() || self.inner[index].is_ascii())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
slow_path(&self.inner, index);
|
||||||
|
|
||||||
|
/// We're betting that typical splits will involve an ASCII character.
|
||||||
|
///
|
||||||
|
/// Putting the expensive checks in a separate function generates notably
|
||||||
|
/// better assembly.
|
||||||
|
#[track_caller]
|
||||||
|
#[inline(never)]
|
||||||
|
fn slow_path(bytes: &[u8], index: usize) {
|
||||||
|
let (before, after) = bytes.split_at(index);
|
||||||
|
|
||||||
|
// UTF-8 takes at most 4 bytes per codepoint, so we don't
|
||||||
|
// need to check more than that.
|
||||||
|
let after = after.get(..4).unwrap_or(after);
|
||||||
|
match str::from_utf8(after) {
|
||||||
|
Ok(_) => return,
|
||||||
|
Err(err) if err.valid_up_to() != 0 => return,
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
for len in 2..=4.min(index) {
|
||||||
|
let before = &before[index - len..];
|
||||||
|
if str::from_utf8(before).is_ok() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("byte index {index} is not an OsStr boundary");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_str(s: &str) -> &Slice {
|
||||||
|
unsafe { Slice::from_encoded_bytes_unchecked(s.as_bytes()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> {
|
||||||
|
str::from_utf8(&self.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||||
|
String::from_utf8_lossy(&self.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_owned(&self) -> Buf {
|
||||||
|
Buf { inner: self.inner.to_vec() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clone_into(&self, buf: &mut Buf) {
|
||||||
|
self.inner.clone_into(&mut buf.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn empty_box() -> Box<Slice> {
|
||||||
|
let boxed: Box<[u8]> = Default::default();
|
||||||
|
unsafe { mem::transmute(boxed) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_arc(&self) -> Arc<Slice> {
|
||||||
|
let arc: Arc<[u8]> = Arc::from(&self.inner);
|
||||||
|
unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_rc(&self) -> Rc<Slice> {
|
||||||
|
let rc: Rc<[u8]> = Rc::from(&self.inner);
|
||||||
|
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn make_ascii_lowercase(&mut self) {
|
||||||
|
self.inner.make_ascii_lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn make_ascii_uppercase(&mut self) {
|
||||||
|
self.inner.make_ascii_uppercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_ascii_lowercase(&self) -> Buf {
|
||||||
|
Buf { inner: self.inner.to_ascii_lowercase() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_ascii_uppercase(&self) -> Buf {
|
||||||
|
Buf { inner: self.inner.to_ascii_uppercase() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_ascii(&self) -> bool {
|
||||||
|
self.inner.is_ascii()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
|
||||||
|
self.inner.eq_ignore_ascii_case(&other.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "clone_to_uninit", issue = "126799")]
|
||||||
|
unsafe impl CloneToUninit for Slice {
|
||||||
|
#[inline]
|
||||||
|
#[cfg_attr(debug_assertions, track_caller)]
|
||||||
|
unsafe fn clone_to_uninit(&self, dst: *mut u8) {
|
||||||
|
// SAFETY: we're just a transparent wrapper around [u8]
|
||||||
|
unsafe { self.inner.clone_to_uninit(dst) }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
crates/std/src/sys/os_str/bytes/tests.rs
Normal file
17
crates/std/src/sys/os_str/bytes/tests.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn slice_debug_output() {
|
||||||
|
let input = unsafe { Slice::from_encoded_bytes_unchecked(b"\xF0hello,\tworld") };
|
||||||
|
let expected = r#""\xF0hello,\tworld""#;
|
||||||
|
let output = format!("{input:?}");
|
||||||
|
|
||||||
|
assert_eq!(output, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display() {
|
||||||
|
assert_eq!("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", unsafe {
|
||||||
|
Slice::from_encoded_bytes_unchecked(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string()
|
||||||
|
},);
|
||||||
|
}
|
||||||
16
crates/std/src/sys/os_str/mod.rs
Normal file
16
crates/std/src/sys/os_str/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
any(target_os = "windows", target_os = "uefi") => {
|
||||||
|
mod wtf8;
|
||||||
|
pub use wtf8::{Buf, Slice};
|
||||||
|
}
|
||||||
|
any(target_os = "motor") => {
|
||||||
|
mod utf8;
|
||||||
|
pub use utf8::{Buf, Slice};
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod bytes;
|
||||||
|
pub use bytes::{Buf, Slice};
|
||||||
|
}
|
||||||
|
}
|
||||||
28
crates/std/src/sys/path/mod.rs
Normal file
28
crates/std/src/sys/path/mod.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
cfg_select! {
|
||||||
|
target_os = "windows" => {
|
||||||
|
mod windows;
|
||||||
|
mod windows_prefix;
|
||||||
|
pub use windows::*;
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
mod sgx;
|
||||||
|
pub use sgx::*;
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod unsupported_backslash;
|
||||||
|
pub use unsupported_backslash::*;
|
||||||
|
}
|
||||||
|
target_os = "uefi" => {
|
||||||
|
mod uefi;
|
||||||
|
pub use uefi::*;
|
||||||
|
}
|
||||||
|
target_os = "cygwin" => {
|
||||||
|
mod cygwin;
|
||||||
|
mod windows_prefix;
|
||||||
|
pub use cygwin::*;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod unix;
|
||||||
|
pub use unix::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
72
crates/std/src/sys/path/unix.rs
Normal file
72
crates/std/src/sys/path/unix.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
use crate::ffi::OsStr;
|
||||||
|
use crate::path::{Path, PathBuf, Prefix};
|
||||||
|
use crate::{env, io};
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_sep_byte(b: u8) -> bool {
|
||||||
|
b == b'/'
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_verbatim_sep(b: u8) -> bool {
|
||||||
|
b == b'/'
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const HAS_PREFIXES: bool = false;
|
||||||
|
pub const MAIN_SEP_STR: &str = "/";
|
||||||
|
pub const MAIN_SEP: char = '/';
|
||||||
|
|
||||||
|
/// Make a POSIX path absolute without changing its semantics.
|
||||||
|
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
|
||||||
|
// This is mostly a wrapper around collecting `Path::components`, with
|
||||||
|
// exceptions made where this conflicts with the POSIX specification.
|
||||||
|
// See 4.13 Pathname Resolution, IEEE Std 1003.1-2017
|
||||||
|
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
|
||||||
|
|
||||||
|
// Get the components, skipping the redundant leading "." component if it exists.
|
||||||
|
let mut components = path.strip_prefix(".").unwrap_or(path).components();
|
||||||
|
let path_os = path.as_os_str().as_encoded_bytes();
|
||||||
|
|
||||||
|
let mut normalized = if path.is_absolute() {
|
||||||
|
// "If a pathname begins with two successive <slash> characters, the
|
||||||
|
// first component following the leading <slash> characters may be
|
||||||
|
// interpreted in an implementation-defined manner, although more than
|
||||||
|
// two leading <slash> characters shall be treated as a single <slash>
|
||||||
|
// character."
|
||||||
|
if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
|
||||||
|
components.next();
|
||||||
|
PathBuf::from("//")
|
||||||
|
} else {
|
||||||
|
PathBuf::new()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// env::current_dir()?
|
||||||
|
todo!()
|
||||||
|
};
|
||||||
|
normalized.extend(components);
|
||||||
|
|
||||||
|
// "Interfaces using pathname resolution may specify additional constraints
|
||||||
|
// when a pathname that does not name an existing directory contains at
|
||||||
|
// least one non- <slash> character and contains one or more trailing
|
||||||
|
// <slash> characters".
|
||||||
|
// A trailing <slash> is also meaningful if "a symbolic link is
|
||||||
|
// encountered during pathname resolution".
|
||||||
|
if path_os.ends_with(b"/") {
|
||||||
|
normalized.push("");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(normalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_absolute(path: &Path) -> bool {
|
||||||
|
if cfg!(any(unix, target_os = "hermit", target_os = "wasi", target_os = "motor")) {
|
||||||
|
path.has_root()
|
||||||
|
} else {
|
||||||
|
path.has_root() && path.prefix().is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
134
crates/std/src/sys/random/mod.rs
Normal file
134
crates/std/src/sys/random/mod.rs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
cfg_select! {
|
||||||
|
// Tier 1
|
||||||
|
any(target_os = "linux", target_os = "android") => {
|
||||||
|
mod linux;
|
||||||
|
pub use linux::{fill_bytes, hashmap_random_keys};
|
||||||
|
}
|
||||||
|
target_os = "windows" => {
|
||||||
|
mod windows;
|
||||||
|
pub use windows::fill_bytes;
|
||||||
|
}
|
||||||
|
target_vendor = "apple" => {
|
||||||
|
mod apple;
|
||||||
|
pub use apple::fill_bytes;
|
||||||
|
// Others, in alphabetical ordering.
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "haiku",
|
||||||
|
target_os = "illumos",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "rtems",
|
||||||
|
target_os = "solaris",
|
||||||
|
target_os = "vita",
|
||||||
|
target_os = "nuttx",
|
||||||
|
) => {
|
||||||
|
mod arc4random;
|
||||||
|
pub use arc4random::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "emscripten" => {
|
||||||
|
mod getentropy;
|
||||||
|
pub use getentropy::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "espidf" => {
|
||||||
|
mod espidf;
|
||||||
|
pub use espidf::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "fuchsia" => {
|
||||||
|
mod fuchsia;
|
||||||
|
pub use fuchsia::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "hermit" => {
|
||||||
|
mod hermit;
|
||||||
|
pub use hermit::fill_bytes;
|
||||||
|
}
|
||||||
|
any(target_os = "horizon", target_os = "cygwin") => {
|
||||||
|
// FIXME(horizon): add arc4random_buf to shim-3ds
|
||||||
|
mod getrandom;
|
||||||
|
pub use getrandom::fill_bytes;
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
target_os = "aix",
|
||||||
|
target_os = "hurd",
|
||||||
|
target_os = "l4re",
|
||||||
|
target_os = "nto",
|
||||||
|
) => {
|
||||||
|
mod unix_legacy;
|
||||||
|
pub use unix_legacy::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "redox" => {
|
||||||
|
mod redox;
|
||||||
|
pub use redox::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "motor" => {
|
||||||
|
mod motor;
|
||||||
|
pub use motor::fill_bytes;
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
mod sgx;
|
||||||
|
pub use sgx::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod solid;
|
||||||
|
pub use solid::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "teeos" => {
|
||||||
|
mod teeos;
|
||||||
|
pub use teeos::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "trusty" => {
|
||||||
|
mod trusty;
|
||||||
|
pub use trusty::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "uefi" => {
|
||||||
|
mod uefi;
|
||||||
|
pub use uefi::fill_bytes;
|
||||||
|
}
|
||||||
|
target_os = "vxworks" => {
|
||||||
|
mod vxworks;
|
||||||
|
pub use vxworks::fill_bytes;
|
||||||
|
}
|
||||||
|
all(target_os = "wasi", target_env = "p1") => {
|
||||||
|
mod wasip1;
|
||||||
|
pub use wasip1::fill_bytes;
|
||||||
|
}
|
||||||
|
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => {
|
||||||
|
mod wasip2;
|
||||||
|
pub use wasip2::{fill_bytes, hashmap_random_keys};
|
||||||
|
}
|
||||||
|
target_os = "zkvm" => {
|
||||||
|
mod zkvm;
|
||||||
|
pub use zkvm::fill_bytes;
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
all(target_family = "wasm", target_os = "unknown"),
|
||||||
|
target_os = "xous",
|
||||||
|
target_os = "vexos",
|
||||||
|
target_os = "survos",
|
||||||
|
) => {
|
||||||
|
// FIXME: finally remove std support for wasm32-unknown-unknown
|
||||||
|
// FIXME: add random data generation to xous
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{fill_bytes, hashmap_random_keys};
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "android",
|
||||||
|
all(target_family = "wasm", target_os = "unknown"),
|
||||||
|
all(target_os = "wasi", not(target_env = "p1")),
|
||||||
|
target_os = "xous",
|
||||||
|
target_os = "vexos",
|
||||||
|
target_os = "survos",
|
||||||
|
)))]
|
||||||
|
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||||
|
let mut buf = [0; 16];
|
||||||
|
fill_bytes(&mut buf);
|
||||||
|
let k1 = u64::from_ne_bytes(buf[..8].try_into().unwrap());
|
||||||
|
let k2 = u64::from_ne_bytes(buf[8..].try_into().unwrap());
|
||||||
|
(k1, k2)
|
||||||
|
}
|
||||||
15
crates/std/src/sys/random/unsupported.rs
Normal file
15
crates/std/src/sys/random/unsupported.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use crate::ptr;
|
||||||
|
|
||||||
|
pub fn fill_bytes(_: &mut [u8]) {
|
||||||
|
panic!("this target does not support random data generation");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||||
|
// Use allocation addresses for a bit of randomness. This isn't
|
||||||
|
// particularly secure, but there isn't really an alternative.
|
||||||
|
let stack = 0u8;
|
||||||
|
let heap = Box::new(0u8);
|
||||||
|
let k1 = ptr::from_ref(&stack).addr() as u64;
|
||||||
|
let k2 = ptr::from_ref(&*heap).addr() as u64;
|
||||||
|
(k1, k2)
|
||||||
|
}
|
||||||
44
crates/std/src/sys/sync/condvar/mod.rs
Normal file
44
crates/std/src/sys/sync/condvar/mod.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
cfg_select! {
|
||||||
|
any(
|
||||||
|
all(target_os = "windows", not(target_vendor="win7")),
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "motor",
|
||||||
|
target_os = "fuchsia",
|
||||||
|
all(target_family = "wasm", target_feature = "atomics"),
|
||||||
|
target_os = "hermit",
|
||||||
|
) => {
|
||||||
|
mod futex;
|
||||||
|
pub use futex::Condvar;
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
target_family = "unix",
|
||||||
|
target_os = "teeos",
|
||||||
|
) => {
|
||||||
|
mod pthread;
|
||||||
|
pub use pthread::Condvar;
|
||||||
|
}
|
||||||
|
all(target_os = "windows", target_vendor = "win7") => {
|
||||||
|
mod windows7;
|
||||||
|
pub use windows7::Condvar;
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
mod sgx;
|
||||||
|
pub use sgx::Condvar;
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod itron;
|
||||||
|
pub use itron::Condvar;
|
||||||
|
}
|
||||||
|
target_os = "xous" => {
|
||||||
|
mod xous;
|
||||||
|
pub use xous::Condvar;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod no_threads;
|
||||||
|
pub use no_threads::Condvar;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
crates/std/src/sys/sync/condvar/no_threads.rs
Normal file
27
crates/std/src/sys/sync/condvar/no_threads.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use crate::sys::sync::Mutex;
|
||||||
|
use crate::thread::sleep;
|
||||||
|
use crate::time::Duration;
|
||||||
|
|
||||||
|
pub struct Condvar {}
|
||||||
|
|
||||||
|
impl Condvar {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new() -> Condvar {
|
||||||
|
Condvar {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn notify_one(&self) {}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn notify_all(&self) {}
|
||||||
|
|
||||||
|
pub unsafe fn wait(&self, _mutex: &Mutex) {
|
||||||
|
panic!("condvar wait not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn wait_timeout(&self, _mutex: &Mutex, dur: Duration) -> bool {
|
||||||
|
sleep(dur);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
9
crates/std/src/sys/sync/mod.rs
Normal file
9
crates/std/src/sys/sync/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
mod condvar;
|
||||||
|
mod mutex;
|
||||||
|
mod once;
|
||||||
|
mod rwlock;
|
||||||
|
|
||||||
|
pub use condvar::Condvar;
|
||||||
|
pub use mutex::Mutex;
|
||||||
|
pub use once::{Once, OnceState};
|
||||||
|
pub use rwlock::RwLock;
|
||||||
47
crates/std/src/sys/sync/mutex/mod.rs
Normal file
47
crates/std/src/sys/sync/mutex/mod.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
cfg_select! {
|
||||||
|
any(
|
||||||
|
all(target_os = "windows", not(target_vendor = "win7")),
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "motor",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
all(target_family = "wasm", target_feature = "atomics"),
|
||||||
|
target_os = "hermit",
|
||||||
|
) => {
|
||||||
|
mod futex;
|
||||||
|
pub use futex::Mutex;
|
||||||
|
}
|
||||||
|
target_os = "fuchsia" => {
|
||||||
|
mod fuchsia;
|
||||||
|
pub use fuchsia::Mutex;
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
target_family = "unix",
|
||||||
|
target_os = "teeos",
|
||||||
|
) => {
|
||||||
|
mod pthread;
|
||||||
|
pub use pthread::Mutex;
|
||||||
|
}
|
||||||
|
all(target_os = "windows", target_vendor = "win7") => {
|
||||||
|
mod windows7;
|
||||||
|
pub use windows7::{Mutex, raw};
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
mod sgx;
|
||||||
|
pub use sgx::Mutex;
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod itron;
|
||||||
|
pub use itron::Mutex;
|
||||||
|
}
|
||||||
|
target_os = "xous" => {
|
||||||
|
mod xous;
|
||||||
|
pub use xous::Mutex;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod no_threads;
|
||||||
|
pub use no_threads::Mutex;
|
||||||
|
}
|
||||||
|
}
|
||||||
31
crates/std/src/sys/sync/mutex/no_threads.rs
Normal file
31
crates/std/src/sys/sync/mutex/no_threads.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use crate::cell::Cell;
|
||||||
|
|
||||||
|
pub struct Mutex {
|
||||||
|
// This platform has no threads, so we can use a Cell here.
|
||||||
|
locked: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Mutex {}
|
||||||
|
unsafe impl Sync for Mutex {} // no threads on this platform
|
||||||
|
|
||||||
|
impl Mutex {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new() -> Mutex {
|
||||||
|
Mutex { locked: Cell::new(false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn lock(&self) {
|
||||||
|
assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn unlock(&self) {
|
||||||
|
self.locked.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_lock(&self) -> bool {
|
||||||
|
self.locked.replace(true) == false
|
||||||
|
}
|
||||||
|
}
|
||||||
40
crates/std/src/sys/sync/once/mod.rs
Normal file
40
crates/std/src/sys/sync/once/mod.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// A "once" is a relatively simple primitive, and it's also typically provided
|
||||||
|
// by the OS as well (see `pthread_once` or `InitOnceExecuteOnce`). The OS
|
||||||
|
// primitives, however, tend to have surprising restrictions, such as the Unix
|
||||||
|
// one doesn't allow an argument to be passed to the function.
|
||||||
|
//
|
||||||
|
// As a result, we end up implementing it ourselves in the standard library.
|
||||||
|
// This also gives us the opportunity to optimize the implementation a bit which
|
||||||
|
// should help the fast path on call sites.
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
any(
|
||||||
|
all(target_os = "windows", not(target_vendor="win7")),
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "android",
|
||||||
|
all(target_arch = "wasm32", target_feature = "atomics"),
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "motor",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "fuchsia",
|
||||||
|
target_os = "hermit",
|
||||||
|
) => {
|
||||||
|
mod futex;
|
||||||
|
pub use futex::{Once, OnceState};
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
windows,
|
||||||
|
target_family = "unix",
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx"),
|
||||||
|
target_os = "solid_asp3",
|
||||||
|
target_os = "xous",
|
||||||
|
) => {
|
||||||
|
mod queue;
|
||||||
|
pub use queue::{Once, OnceState};
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod no_threads;
|
||||||
|
pub use no_threads::{Once, OnceState};
|
||||||
|
}
|
||||||
|
}
|
||||||
114
crates/std/src/sys/sync/once/no_threads.rs
Normal file
114
crates/std/src/sys/sync/once/no_threads.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
use crate::cell::Cell;
|
||||||
|
use crate::sync as public;
|
||||||
|
use crate::sync::once::OnceExclusiveState;
|
||||||
|
|
||||||
|
pub struct Once {
|
||||||
|
state: Cell<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OnceState {
|
||||||
|
poisoned: bool,
|
||||||
|
set_state_to: Cell<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum State {
|
||||||
|
Incomplete,
|
||||||
|
Poisoned,
|
||||||
|
Running,
|
||||||
|
Complete,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CompletionGuard<'a> {
|
||||||
|
state: &'a Cell<State>,
|
||||||
|
set_state_on_drop_to: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for CompletionGuard<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.state.set(self.set_state_on_drop_to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safety: threads are not supported on this platform.
|
||||||
|
unsafe impl Sync for Once {}
|
||||||
|
|
||||||
|
impl Once {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new() -> Once {
|
||||||
|
Once { state: Cell::new(State::Incomplete) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_completed(&self) -> bool {
|
||||||
|
self.state.get() == State::Complete
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn state(&mut self) -> OnceExclusiveState {
|
||||||
|
match self.state.get() {
|
||||||
|
State::Incomplete => OnceExclusiveState::Incomplete,
|
||||||
|
State::Poisoned => OnceExclusiveState::Poisoned,
|
||||||
|
State::Complete => OnceExclusiveState::Complete,
|
||||||
|
_ => unreachable!("invalid Once state"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn set_state(&mut self, new_state: OnceExclusiveState) {
|
||||||
|
self.state.set(match new_state {
|
||||||
|
OnceExclusiveState::Incomplete => State::Incomplete,
|
||||||
|
OnceExclusiveState::Poisoned => State::Poisoned,
|
||||||
|
OnceExclusiveState::Complete => State::Complete,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn wait(&self, _ignore_poisoning: bool) {
|
||||||
|
panic!("not implementable on this target");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
|
||||||
|
let state = self.state.get();
|
||||||
|
match state {
|
||||||
|
State::Poisoned if !ignore_poisoning => {
|
||||||
|
// Panic to propagate the poison.
|
||||||
|
panic!("Once instance has previously been poisoned");
|
||||||
|
}
|
||||||
|
State::Incomplete | State::Poisoned => {
|
||||||
|
self.state.set(State::Running);
|
||||||
|
// `guard` will set the new state on drop.
|
||||||
|
let mut guard =
|
||||||
|
CompletionGuard { state: &self.state, set_state_on_drop_to: State::Poisoned };
|
||||||
|
// Run the function, letting it know if we're poisoned or not.
|
||||||
|
let f_state = public::OnceState {
|
||||||
|
inner: OnceState {
|
||||||
|
poisoned: state == State::Poisoned,
|
||||||
|
set_state_to: Cell::new(State::Complete),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
f(&f_state);
|
||||||
|
guard.set_state_on_drop_to = f_state.inner.set_state_to.get();
|
||||||
|
}
|
||||||
|
State::Running => {
|
||||||
|
panic!("one-time initialization may not be performed recursively");
|
||||||
|
}
|
||||||
|
State::Complete => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnceState {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_poisoned(&self) -> bool {
|
||||||
|
self.poisoned
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn poison(&self) {
|
||||||
|
self.set_state_to.set(State::Poisoned)
|
||||||
|
}
|
||||||
|
}
|
||||||
35
crates/std/src/sys/sync/rwlock/mod.rs
Normal file
35
crates/std/src/sys/sync/rwlock/mod.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
cfg_select! {
|
||||||
|
any(
|
||||||
|
all(target_os = "windows", not(target_vendor = "win7")),
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "fuchsia",
|
||||||
|
all(target_family = "wasm", target_feature = "atomics"),
|
||||||
|
target_os = "hermit",
|
||||||
|
target_os = "motor",
|
||||||
|
) => {
|
||||||
|
mod futex;
|
||||||
|
pub use futex::RwLock;
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
target_family = "unix",
|
||||||
|
all(target_os = "windows", target_vendor = "win7"),
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx"),
|
||||||
|
target_os = "xous",
|
||||||
|
target_os = "teeos",
|
||||||
|
) => {
|
||||||
|
mod queue;
|
||||||
|
pub use queue::RwLock;
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod solid;
|
||||||
|
pub use solid::RwLock;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod no_threads;
|
||||||
|
pub use no_threads::RwLock;
|
||||||
|
}
|
||||||
|
}
|
||||||
69
crates/std/src/sys/sync/rwlock/no_threads.rs
Normal file
69
crates/std/src/sys/sync/rwlock/no_threads.rs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
use crate::cell::Cell;
|
||||||
|
|
||||||
|
pub struct RwLock {
|
||||||
|
// This platform has no threads, so we can use a Cell here.
|
||||||
|
mode: Cell<isize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for RwLock {}
|
||||||
|
unsafe impl Sync for RwLock {} // no threads on this platform
|
||||||
|
|
||||||
|
impl RwLock {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new() -> RwLock {
|
||||||
|
RwLock { mode: Cell::new(0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read(&self) {
|
||||||
|
let m = self.mode.get();
|
||||||
|
if m >= 0 {
|
||||||
|
self.mode.set(m + 1);
|
||||||
|
} else {
|
||||||
|
rtabort!("rwlock locked for writing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_read(&self) -> bool {
|
||||||
|
let m = self.mode.get();
|
||||||
|
if m >= 0 {
|
||||||
|
self.mode.set(m + 1);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn write(&self) {
|
||||||
|
if self.mode.replace(-1) != 0 {
|
||||||
|
rtabort!("rwlock locked for reading")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn try_write(&self) -> bool {
|
||||||
|
if self.mode.get() == 0 {
|
||||||
|
self.mode.set(-1);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn read_unlock(&self) {
|
||||||
|
self.mode.set(self.mode.get() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn write_unlock(&self) {
|
||||||
|
assert_eq!(self.mode.replace(0), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn downgrade(&self) {
|
||||||
|
assert_eq!(self.mode.replace(1), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
154
crates/std/src/sys/thread/mod.rs
Normal file
154
crates/std/src/sys/thread/mod.rs
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
cfg_select! {
|
||||||
|
target_os = "hermit" => {
|
||||||
|
mod hermit;
|
||||||
|
pub use hermit::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
#[expect(dead_code)]
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{current_os_id, set_name};
|
||||||
|
}
|
||||||
|
target_os = "motor" => {
|
||||||
|
mod motor;
|
||||||
|
pub use motor::*;
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
mod sgx;
|
||||||
|
pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
|
||||||
|
// SGX should protect in-enclave data from outside attackers, so there
|
||||||
|
// must not be any data leakage to the OS, particularly no 1-1 mapping
|
||||||
|
// between SGX thread names and OS thread names. Hence `set_name` is
|
||||||
|
// intentionally a no-op.
|
||||||
|
//
|
||||||
|
// Note that the internally visible SGX thread name is already provided
|
||||||
|
// by the platform-agnostic Rust thread code. This can be observed in
|
||||||
|
// the [`std::thread::tests::test_named_thread`] test, which succeeds
|
||||||
|
// as-is with the SGX target.
|
||||||
|
#[expect(dead_code)]
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{available_parallelism, set_name};
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod solid;
|
||||||
|
pub use solid::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
#[expect(dead_code)]
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{available_parallelism, current_os_id, set_name};
|
||||||
|
}
|
||||||
|
target_os = "teeos" => {
|
||||||
|
mod teeos;
|
||||||
|
pub use teeos::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
#[expect(dead_code)]
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{available_parallelism, current_os_id, set_name};
|
||||||
|
}
|
||||||
|
target_os = "uefi" => {
|
||||||
|
mod uefi;
|
||||||
|
pub use uefi::{available_parallelism, sleep};
|
||||||
|
#[expect(dead_code)]
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
}
|
||||||
|
any(target_family = "unix", target_os = "wasi") => {
|
||||||
|
mod unix;
|
||||||
|
pub use unix::{Thread, available_parallelism, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_env = "newlib",
|
||||||
|
target_os = "l4re",
|
||||||
|
target_os = "emscripten",
|
||||||
|
target_os = "redox",
|
||||||
|
target_os = "hurd",
|
||||||
|
target_os = "aix",
|
||||||
|
target_os = "wasi",
|
||||||
|
)))]
|
||||||
|
pub use unix::set_name;
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "solaris",
|
||||||
|
target_os = "illumos",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "hurd",
|
||||||
|
target_os = "vxworks",
|
||||||
|
target_os = "wasi",
|
||||||
|
target_vendor = "apple",
|
||||||
|
))]
|
||||||
|
pub use unix::sleep_until;
|
||||||
|
#[expect(dead_code)]
|
||||||
|
mod unsupported;
|
||||||
|
#[cfg(any(
|
||||||
|
target_env = "newlib",
|
||||||
|
target_os = "l4re",
|
||||||
|
target_os = "emscripten",
|
||||||
|
target_os = "redox",
|
||||||
|
target_os = "hurd",
|
||||||
|
target_os = "aix",
|
||||||
|
target_os = "wasi",
|
||||||
|
))]
|
||||||
|
pub use unsupported::set_name;
|
||||||
|
}
|
||||||
|
target_os = "vexos" => {
|
||||||
|
mod vexos;
|
||||||
|
pub use vexos::{sleep, sleep_until, yield_now};
|
||||||
|
#[expect(dead_code)]
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
}
|
||||||
|
all(target_family = "wasm", target_feature = "atomics") => {
|
||||||
|
mod wasm;
|
||||||
|
pub use wasm::sleep;
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
}
|
||||||
|
target_os = "windows" => {
|
||||||
|
mod windows;
|
||||||
|
pub use windows::{Thread, available_parallelism, current_os_id, set_name, set_name_wide, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
}
|
||||||
|
target_os = "xous" => {
|
||||||
|
mod xous;
|
||||||
|
pub use xous::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{current_os_id, set_name};
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, sleep, yield_now, DEFAULT_MIN_STACK_SIZE};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "solaris",
|
||||||
|
target_os = "illumos",
|
||||||
|
target_os = "dragonfly",
|
||||||
|
target_os = "hurd",
|
||||||
|
target_os = "vxworks",
|
||||||
|
target_os = "wasi",
|
||||||
|
target_vendor = "apple",
|
||||||
|
target_os = "motor",
|
||||||
|
target_os = "vexos"
|
||||||
|
)))]
|
||||||
|
pub fn sleep_until(deadline: crate::time::Instant) {
|
||||||
|
use crate::time::Instant;
|
||||||
|
|
||||||
|
// The clock source used for `sleep` might not be the same used for `Instant`.
|
||||||
|
// Since this function *must not* return before the deadline, we recheck the
|
||||||
|
// time after every call to `sleep`. See #149935 for an example of this
|
||||||
|
// occurring on older Windows systems.
|
||||||
|
while let Some(delay) = deadline.checked_duration_since(Instant::now()) {
|
||||||
|
// Sleep for the estimated time remaining until the deadline.
|
||||||
|
//
|
||||||
|
// If your system has a better way of estimating the delay time or
|
||||||
|
// provides a way to sleep until an absolute time, specialize this
|
||||||
|
// function for your system.
|
||||||
|
sleep(delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
crates/std/src/sys/thread/unsupported.rs
Normal file
46
crates/std/src/sys/thread/unsupported.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use crate::ffi::CStr;
|
||||||
|
use crate::io;
|
||||||
|
use crate::num::NonZero;
|
||||||
|
use crate::thread::ThreadInit;
|
||||||
|
use crate::time::Duration;
|
||||||
|
|
||||||
|
// Silence dead code warnings for the otherwise unused ThreadInit::init() call.
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn dummy_init_call(init: Box<ThreadInit>) {
|
||||||
|
drop(init.init());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Thread(!);
|
||||||
|
|
||||||
|
pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024;
|
||||||
|
|
||||||
|
impl Thread {
|
||||||
|
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||||
|
pub unsafe fn new(_stack: usize, _init: Box<ThreadInit>) -> io::Result<Thread> {
|
||||||
|
Err(io::Error::UNSUPPORTED_PLATFORM)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join(self) {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
|
||||||
|
Err(io::Error::UNKNOWN_THREAD_COUNT)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_os_id() -> Option<u64> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn yield_now() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_name(_name: &CStr) {
|
||||||
|
// nope
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sleep(_dur: Duration) {
|
||||||
|
panic!("can't sleep");
|
||||||
|
}
|
||||||
227
crates/std/src/sys/thread_local/mod.rs
Normal file
227
crates/std/src/sys/thread_local/mod.rs
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
//! Implementation of the `thread_local` macro.
|
||||||
|
//!
|
||||||
|
//! There are three different thread-local implementations:
|
||||||
|
//! * Some targets lack threading support, and hence have only one thread, so
|
||||||
|
//! the TLS data is stored in a normal `static`.
|
||||||
|
//! * Some targets support TLS natively via the dynamic linker and C runtime.
|
||||||
|
//! * On some targets, the OS provides a library-based TLS implementation. The
|
||||||
|
//! TLS data is heap-allocated and referenced using a TLS key.
|
||||||
|
//!
|
||||||
|
//! Each implementation provides a macro which generates the `LocalKey` `const`
|
||||||
|
//! used to reference the TLS variable, along with the necessary helper structs
|
||||||
|
//! to track the initialization/destruction state of the variable.
|
||||||
|
//!
|
||||||
|
//! Additionally, this module contains abstractions for the OS interfaces used
|
||||||
|
//! for these implementations.
|
||||||
|
|
||||||
|
#![cfg_attr(test, allow(unused))]
|
||||||
|
#![doc(hidden)]
|
||||||
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||||
|
#![unstable(
|
||||||
|
feature = "thread_local_internals",
|
||||||
|
reason = "internal details of the thread_local macro",
|
||||||
|
issue = "none"
|
||||||
|
)]
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
any(
|
||||||
|
all(target_family = "wasm", not(target_feature = "atomics")),
|
||||||
|
target_os = "uefi",
|
||||||
|
target_os = "zkvm",
|
||||||
|
target_os = "trusty",
|
||||||
|
target_os = "vexos",
|
||||||
|
target_os = "survos",
|
||||||
|
) => {
|
||||||
|
mod no_threads;
|
||||||
|
pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner};
|
||||||
|
pub(crate) use no_threads::{LocalPointer, local_pointer};
|
||||||
|
}
|
||||||
|
target_thread_local => {
|
||||||
|
mod native;
|
||||||
|
pub use native::{EagerStorage, LazyStorage, thread_local_inner};
|
||||||
|
pub(crate) use native::{LocalPointer, local_pointer};
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod os;
|
||||||
|
pub use os::{Storage, thread_local_inner, value_align};
|
||||||
|
pub(crate) use os::{LocalPointer, local_pointer};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The native TLS implementation needs a way to register destructors for its data.
|
||||||
|
/// This module contains platform-specific implementations of that register.
|
||||||
|
///
|
||||||
|
/// It turns out however that most platforms don't have a way to register a
|
||||||
|
/// destructor for each variable. On these platforms, we keep track of the
|
||||||
|
/// destructors ourselves and register (through the [`guard`] module) only a
|
||||||
|
/// single callback that runs all of the destructors in the list.
|
||||||
|
#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))]
|
||||||
|
pub(crate) mod destructors {
|
||||||
|
cfg_select! {
|
||||||
|
any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "android",
|
||||||
|
target_os = "fuchsia",
|
||||||
|
target_os = "redox",
|
||||||
|
target_os = "hurd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "dragonfly"
|
||||||
|
) => {
|
||||||
|
mod linux_like;
|
||||||
|
mod list;
|
||||||
|
pub(super) use linux_like::register;
|
||||||
|
pub(super) use list::run;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod list;
|
||||||
|
pub(super) use list::register;
|
||||||
|
pub(crate) use list::run;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This module provides a way to schedule the execution of the destructor list
|
||||||
|
/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable`
|
||||||
|
/// should ensure that these functions are called at the right times.
|
||||||
|
pub(crate) mod guard {
|
||||||
|
cfg_select! {
|
||||||
|
all(target_thread_local, target_vendor = "apple") => {
|
||||||
|
mod apple;
|
||||||
|
pub(crate) use apple::enable;
|
||||||
|
}
|
||||||
|
target_os = "windows" => {
|
||||||
|
mod windows;
|
||||||
|
pub(crate) use windows::enable;
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
all(target_family = "wasm", not(
|
||||||
|
all(target_os = "wasi", target_env = "p1", target_feature = "atomics")
|
||||||
|
)),
|
||||||
|
target_os = "uefi",
|
||||||
|
target_os = "zkvm",
|
||||||
|
target_os = "trusty",
|
||||||
|
target_os = "vexos",
|
||||||
|
) => {
|
||||||
|
pub(crate) fn enable() {
|
||||||
|
// FIXME: Right now there is no concept of "thread exit" on
|
||||||
|
// wasm, but this is likely going to show up at some point in
|
||||||
|
// the form of an exported symbol that the wasm runtime is going
|
||||||
|
// to be expected to call. For now we just leak everything, but
|
||||||
|
// if such a function starts to exist it will probably need to
|
||||||
|
// iterate the destructor list with these functions:
|
||||||
|
#[cfg(all(target_family = "wasm", target_feature = "atomics"))]
|
||||||
|
#[allow(unused)]
|
||||||
|
use super::destructors::run;
|
||||||
|
#[allow(unused)]
|
||||||
|
use crate::rt::thread_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
target_os = "hermit",
|
||||||
|
target_os = "xous",
|
||||||
|
) => {
|
||||||
|
// `std` is the only runtime, so it just calls the destructor functions
|
||||||
|
// itself when the time comes.
|
||||||
|
pub(crate) fn enable() {}
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod solid;
|
||||||
|
pub(crate) use solid::enable;
|
||||||
|
}
|
||||||
|
target_os = "survos" => {
|
||||||
|
// todo
|
||||||
|
pub(crate) fn enable() {}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod key;
|
||||||
|
pub(crate) use key::enable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `const`-creatable TLS keys.
|
||||||
|
///
|
||||||
|
/// Most OSs without native TLS will provide a library-based way to create TLS
|
||||||
|
/// storage. For each TLS variable, we create a key, which can then be used to
|
||||||
|
/// reference an entry in a thread-local table. This then associates each key
|
||||||
|
/// with a pointer which we can get and set to store our data.
|
||||||
|
pub(crate) mod key {
|
||||||
|
cfg_select! {
|
||||||
|
any(
|
||||||
|
all(
|
||||||
|
not(target_vendor = "apple"),
|
||||||
|
not(target_family = "wasm"),
|
||||||
|
target_family = "unix",
|
||||||
|
),
|
||||||
|
all(not(target_thread_local), target_vendor = "apple"),
|
||||||
|
target_os = "teeos",
|
||||||
|
all(target_os = "wasi", target_env = "p1", target_feature = "atomics"),
|
||||||
|
) => {
|
||||||
|
mod racy;
|
||||||
|
mod unix;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
pub(super) use racy::LazyKey;
|
||||||
|
pub(super) use unix::{Key, set};
|
||||||
|
#[cfg(any(not(target_thread_local), test))]
|
||||||
|
pub(super) use unix::get;
|
||||||
|
use unix::{create, destroy};
|
||||||
|
}
|
||||||
|
all(not(target_thread_local), target_os = "windows") => {
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
mod windows;
|
||||||
|
pub(super) use windows::{Key, LazyKey, get, run_dtors, set};
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
mod racy;
|
||||||
|
mod sgx;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
pub(super) use racy::LazyKey;
|
||||||
|
pub(super) use sgx::{Key, get, set};
|
||||||
|
use sgx::{create, destroy};
|
||||||
|
}
|
||||||
|
target_os = "xous" => {
|
||||||
|
mod racy;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
mod xous;
|
||||||
|
pub(super) use racy::LazyKey;
|
||||||
|
pub(crate) use xous::destroy_tls;
|
||||||
|
pub(super) use xous::{Key, get, set};
|
||||||
|
use xous::{create, destroy};
|
||||||
|
}
|
||||||
|
target_os = "motor" => {
|
||||||
|
mod racy;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
pub(super) use racy::LazyKey;
|
||||||
|
pub(super) use moto_rt::tls::{Key, get, set};
|
||||||
|
use moto_rt::tls::{create, destroy};
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a callback in a scenario which must not unwind (such as a `extern "C"
|
||||||
|
/// fn` declared in a user crate). If the callback unwinds anyway, then
|
||||||
|
/// `rtabort` with a message about thread local panicking on drop.
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn abort_on_dtor_unwind(f: impl FnOnce()) {
|
||||||
|
// Using a guard like this is lower cost.
|
||||||
|
let guard = DtorUnwindGuard;
|
||||||
|
f();
|
||||||
|
core::mem::forget(guard);
|
||||||
|
|
||||||
|
struct DtorUnwindGuard;
|
||||||
|
impl Drop for DtorUnwindGuard {
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// This is not terribly descriptive, but it doesn't need to be as we'll
|
||||||
|
// already have printed a panic message at this point.
|
||||||
|
rtabort!("thread local panicked on drop");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
150
crates/std/src/sys/thread_local/no_threads.rs
Normal file
150
crates/std/src/sys/thread_local/no_threads.rs
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
//! On some targets like wasm there's no threads, so no need to generate
|
||||||
|
//! thread locals and we can instead just use plain statics!
|
||||||
|
|
||||||
|
use crate::cell::{Cell, UnsafeCell};
|
||||||
|
use crate::mem::MaybeUninit;
|
||||||
|
use crate::ptr;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow_internal_unstable(thread_local_internals)]
|
||||||
|
#[allow_internal_unsafe]
|
||||||
|
#[unstable(feature = "thread_local_internals", issue = "none")]
|
||||||
|
#[rustc_macro_transparency = "semiopaque"]
|
||||||
|
pub macro thread_local_inner {
|
||||||
|
// used to generate the `LocalKey` value for const-initialized thread locals
|
||||||
|
(@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{
|
||||||
|
const __RUST_STD_INTERNAL_INIT: $t = $init;
|
||||||
|
|
||||||
|
// NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed.
|
||||||
|
unsafe {
|
||||||
|
$crate::thread::LocalKey::new(|_| {
|
||||||
|
$(#[$align_attr])*
|
||||||
|
static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::EagerStorage<$t> =
|
||||||
|
$crate::thread::local_impl::EagerStorage { value: __RUST_STD_INTERNAL_INIT };
|
||||||
|
&__RUST_STD_INTERNAL_VAL.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
|
||||||
|
// used to generate the `LocalKey` value for `thread_local!`
|
||||||
|
(@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{
|
||||||
|
#[inline]
|
||||||
|
fn __rust_std_internal_init_fn() -> $t { $init }
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
$crate::thread::LocalKey::new(|__rust_std_internal_init| {
|
||||||
|
$(#[$align_attr])*
|
||||||
|
static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new();
|
||||||
|
__RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
#[repr(transparent)] // Required for correctness of `#[rustc_align_static]`
|
||||||
|
pub struct EagerStorage<T> {
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: the target doesn't have threads.
|
||||||
|
unsafe impl<T> Sync for EagerStorage<T> {}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum State {
|
||||||
|
Initial,
|
||||||
|
Alive,
|
||||||
|
Destroying,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct LazyStorage<T> {
|
||||||
|
// This field must be first, for correctness of `#[rustc_align_static]`
|
||||||
|
value: UnsafeCell<MaybeUninit<T>>,
|
||||||
|
state: Cell<State>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> LazyStorage<T> {
|
||||||
|
pub const fn new() -> LazyStorage<T> {
|
||||||
|
LazyStorage {
|
||||||
|
value: UnsafeCell::new(MaybeUninit::uninit()),
|
||||||
|
state: Cell::new(State::Initial),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a pointer to the TLS value, potentially initializing it with the
|
||||||
|
/// provided parameters.
|
||||||
|
///
|
||||||
|
/// The resulting pointer may not be used after reentrant inialialization
|
||||||
|
/// has occurred.
|
||||||
|
#[inline]
|
||||||
|
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
|
||||||
|
if self.state.get() == State::Alive {
|
||||||
|
self.value.get() as *const T
|
||||||
|
} else {
|
||||||
|
self.initialize(i, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
fn initialize(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
|
||||||
|
let value = i.and_then(Option::take).unwrap_or_else(f);
|
||||||
|
|
||||||
|
// Destroy the old value if it is initialized
|
||||||
|
// FIXME(#110897): maybe panic on recursive initialization.
|
||||||
|
if self.state.get() == State::Alive {
|
||||||
|
self.state.set(State::Destroying);
|
||||||
|
// Safety: we check for no initialization during drop below
|
||||||
|
unsafe {
|
||||||
|
ptr::drop_in_place(self.value.get() as *mut T);
|
||||||
|
}
|
||||||
|
self.state.set(State::Initial);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guard against initialization during drop
|
||||||
|
if self.state.get() == State::Destroying {
|
||||||
|
panic!("Attempted to initialize thread-local while it is being dropped");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.value.get().write(MaybeUninit::new(value));
|
||||||
|
}
|
||||||
|
self.state.set(State::Alive);
|
||||||
|
|
||||||
|
self.value.get() as *const T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: the target doesn't have threads.
|
||||||
|
unsafe impl<T> Sync for LazyStorage<T> {}
|
||||||
|
|
||||||
|
#[rustc_macro_transparency = "semiopaque"]
|
||||||
|
pub(crate) macro local_pointer {
|
||||||
|
() => {},
|
||||||
|
($vis:vis static $name:ident; $($rest:tt)*) => {
|
||||||
|
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
|
||||||
|
$crate::sys::thread_local::local_pointer! { $($rest)* }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct LocalPointer {
|
||||||
|
p: Cell<*mut ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalPointer {
|
||||||
|
pub const fn __new() -> LocalPointer {
|
||||||
|
LocalPointer { p: Cell::new(ptr::null_mut()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> *mut () {
|
||||||
|
self.p.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&self, p: *mut ()) {
|
||||||
|
self.p.set(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: the target doesn't have threads.
|
||||||
|
unsafe impl Sync for LocalPointer {}
|
||||||
289
crates/std/src/sys/thread_local/os.rs
Normal file
289
crates/std/src/sys/thread_local/os.rs
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
use super::key::{Key, LazyKey, get, set};
|
||||||
|
use super::{abort_on_dtor_unwind, guard};
|
||||||
|
use crate::alloc::{self, GlobalAlloc, Layout, System};
|
||||||
|
use crate::cell::Cell;
|
||||||
|
use crate::marker::PhantomData;
|
||||||
|
use crate::mem::ManuallyDrop;
|
||||||
|
use crate::ops::Deref;
|
||||||
|
use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
|
||||||
|
use crate::ptr::{self, NonNull};
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow_internal_unstable(thread_local_internals)]
|
||||||
|
#[allow_internal_unsafe]
|
||||||
|
#[unstable(feature = "thread_local_internals", issue = "none")]
|
||||||
|
#[rustc_macro_transparency = "semiopaque"]
|
||||||
|
pub macro thread_local_inner {
|
||||||
|
// NOTE: we cannot import `Storage` or `LocalKey` with a `use` because that can shadow user
|
||||||
|
// provided type or type alias with a matching name. Please update the shadowing test in
|
||||||
|
// `tests/thread.rs` if these types are renamed.
|
||||||
|
|
||||||
|
// used to generate the `LocalKey` value for `thread_local!`.
|
||||||
|
(@key $t:ty, $($(#[$($align_attr:tt)*])+)?, $init:expr) => {{
|
||||||
|
#[inline]
|
||||||
|
fn __rust_std_internal_init_fn() -> $t { $init }
|
||||||
|
|
||||||
|
// NOTE: this cannot import `LocalKey` or `Storage` with a `use` because that can shadow
|
||||||
|
// user provided type or type alias with a matching name. Please update the shadowing test
|
||||||
|
// in `tests/thread.rs` if these types are renamed.
|
||||||
|
unsafe {
|
||||||
|
$crate::thread::LocalKey::new(|__rust_std_internal_init| {
|
||||||
|
static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::Storage<$t, {
|
||||||
|
$({
|
||||||
|
// Ensure that attributes have valid syntax
|
||||||
|
// and that the proper feature gate is enabled
|
||||||
|
$(#[$($align_attr)*])+
|
||||||
|
#[allow(unused)]
|
||||||
|
static DUMMY: () = ();
|
||||||
|
})?
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut final_align = $crate::thread::local_impl::value_align::<$t>();
|
||||||
|
$($($crate::thread::local_impl::thread_local_inner!(@align final_align, $($align_attr)*);)+)?
|
||||||
|
final_align
|
||||||
|
}>
|
||||||
|
= $crate::thread::local_impl::Storage::new();
|
||||||
|
__RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
|
||||||
|
// process a single `rustc_align_static` attribute
|
||||||
|
(@align $final_align:ident, rustc_align_static($($align:tt)*) $(, $($attr_rest:tt)+)?) => {
|
||||||
|
let new_align: $crate::primitive::usize = $($align)*;
|
||||||
|
if new_align > $final_align {
|
||||||
|
$final_align = new_align;
|
||||||
|
}
|
||||||
|
|
||||||
|
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
|
||||||
|
},
|
||||||
|
|
||||||
|
// process a single `cfg_attr` attribute
|
||||||
|
// by translating it into a `cfg`ed block and recursing.
|
||||||
|
// https://doc.rust-lang.org/reference/conditional-compilation.html#railroad-ConfigurationPredicate
|
||||||
|
|
||||||
|
(@align $final_align:ident, cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
|
||||||
|
#[cfg(true)]
|
||||||
|
{
|
||||||
|
$crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
|
||||||
|
}
|
||||||
|
|
||||||
|
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
|
||||||
|
},
|
||||||
|
|
||||||
|
(@align $final_align:ident, cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
|
||||||
|
#[cfg(false)]
|
||||||
|
{
|
||||||
|
$crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
|
||||||
|
}
|
||||||
|
|
||||||
|
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
|
||||||
|
},
|
||||||
|
|
||||||
|
(@align $final_align:ident, cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
|
||||||
|
#[cfg($cfg_pred)]
|
||||||
|
{
|
||||||
|
$crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
|
||||||
|
}
|
||||||
|
|
||||||
|
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use a regular global static to store this key; the state provided will then be
|
||||||
|
/// thread-local.
|
||||||
|
/// INVARIANT: ALIGN must be a valid alignment, and no less than `value_align::<T>`.
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct Storage<T, const ALIGN: usize> {
|
||||||
|
key: LazyKey,
|
||||||
|
marker: PhantomData<Cell<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T, const ALIGN: usize> Sync for Storage<T, ALIGN> {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct Value<T: 'static> {
|
||||||
|
// This field must be first, for correctness of `#[rustc_align_static]`
|
||||||
|
value: T,
|
||||||
|
// INVARIANT: if this value is stored under a TLS key, `key` must be that `key`.
|
||||||
|
key: Key,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn value_align<T: 'static>() -> usize {
|
||||||
|
crate::mem::align_of::<Value<T>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Equivalent to `Box<Value<T>, System>`, but potentially over-aligned.
|
||||||
|
struct AlignedSystemBox<T: 'static, const ALIGN: usize> {
|
||||||
|
ptr: NonNull<Value<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static, const ALIGN: usize> AlignedSystemBox<T, ALIGN> {
|
||||||
|
#[inline]
|
||||||
|
fn new(v: Value<T>) -> Self {
|
||||||
|
let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
|
||||||
|
|
||||||
|
// We use the System allocator here to avoid interfering with a potential
|
||||||
|
// Global allocator using thread-local storage.
|
||||||
|
let ptr: *mut Value<T> = (unsafe { System.alloc(layout) }).cast();
|
||||||
|
let Some(ptr) = NonNull::new(ptr) else {
|
||||||
|
alloc::handle_alloc_error(layout);
|
||||||
|
};
|
||||||
|
unsafe { ptr.write(v) };
|
||||||
|
Self { ptr }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_raw(b: Self) -> *mut Value<T> {
|
||||||
|
let md = ManuallyDrop::new(b);
|
||||||
|
md.ptr.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_raw(ptr: *mut Value<T>) -> Self {
|
||||||
|
Self { ptr: unsafe { NonNull::new_unchecked(ptr) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static, const ALIGN: usize> Deref for AlignedSystemBox<T, ALIGN> {
|
||||||
|
type Target = Value<T>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { &*(self.ptr.as_ptr()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static, const ALIGN: usize> Drop for AlignedSystemBox<T, ALIGN> {
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let unwind_result = catch_unwind(AssertUnwindSafe(|| self.ptr.drop_in_place()));
|
||||||
|
System.dealloc(self.ptr.as_ptr().cast(), layout);
|
||||||
|
if let Err(payload) = unwind_result {
|
||||||
|
resume_unwind(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static, const ALIGN: usize> Storage<T, ALIGN> {
|
||||||
|
pub const fn new() -> Storage<T, ALIGN> {
|
||||||
|
Storage { key: LazyKey::new(Some(destroy_value::<T, ALIGN>)), marker: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a pointer to the TLS value, potentially initializing it with the
|
||||||
|
/// provided parameters. If the TLS variable has been destroyed, a null
|
||||||
|
/// pointer is returned.
|
||||||
|
///
|
||||||
|
/// The resulting pointer may not be used after reentrant inialialization
|
||||||
|
/// or thread destruction has occurred.
|
||||||
|
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
|
||||||
|
let key = self.key.force();
|
||||||
|
let ptr = unsafe { get(key) as *mut Value<T> };
|
||||||
|
if ptr.addr() > 1 {
|
||||||
|
// SAFETY: the check ensured the pointer is safe (its destructor
|
||||||
|
// is not running) + it is coming from a trusted source (self).
|
||||||
|
unsafe { &(*ptr).value }
|
||||||
|
} else {
|
||||||
|
// SAFETY: trivially correct.
|
||||||
|
unsafe { Self::try_initialize(key, ptr, i, f) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// * `key` must be the result of calling `self.key.force()`
|
||||||
|
/// * `ptr` must be the current value associated with `key`.
|
||||||
|
unsafe fn try_initialize(
|
||||||
|
key: Key,
|
||||||
|
ptr: *mut Value<T>,
|
||||||
|
i: Option<&mut Option<T>>,
|
||||||
|
f: impl FnOnce() -> T,
|
||||||
|
) -> *const T {
|
||||||
|
if ptr.addr() == 1 {
|
||||||
|
// destructor is running
|
||||||
|
return ptr::null();
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = AlignedSystemBox::<T, ALIGN>::new(Value {
|
||||||
|
value: i.and_then(Option::take).unwrap_or_else(f),
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
let ptr = AlignedSystemBox::into_raw(value);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// * key came from a `LazyKey` and is thus correct.
|
||||||
|
// * `ptr` is a correct pointer that can be destroyed by the key destructor.
|
||||||
|
// * the value is stored under the key that it contains.
|
||||||
|
let old = unsafe {
|
||||||
|
let old = get(key) as *mut Value<T>;
|
||||||
|
set(key, ptr as *mut u8);
|
||||||
|
old
|
||||||
|
};
|
||||||
|
|
||||||
|
if !old.is_null() {
|
||||||
|
// If the variable was recursively initialized, drop the old value.
|
||||||
|
// SAFETY: We cannot be inside a `LocalKey::with` scope, as the
|
||||||
|
// initializer has already returned and the next scope only starts
|
||||||
|
// after we return the pointer. Therefore, there can be no references
|
||||||
|
// to the old value.
|
||||||
|
drop(unsafe { AlignedSystemBox::<T, ALIGN>::from_raw(old) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: We just created this value above.
|
||||||
|
unsafe { &(*ptr).value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn destroy_value<T: 'static, const ALIGN: usize>(ptr: *mut u8) {
|
||||||
|
// SAFETY:
|
||||||
|
//
|
||||||
|
// The OS TLS ensures that this key contains a null value when this
|
||||||
|
// destructor starts to run. We set it back to a sentinel value of 1 to
|
||||||
|
// ensure that any future calls to `get` for this thread will return
|
||||||
|
// `None`.
|
||||||
|
//
|
||||||
|
// Note that to prevent an infinite loop we reset it back to null right
|
||||||
|
// before we return from the destructor ourselves.
|
||||||
|
abort_on_dtor_unwind(|| {
|
||||||
|
let ptr = unsafe { AlignedSystemBox::<T, ALIGN>::from_raw(ptr as *mut Value<T>) };
|
||||||
|
let key = ptr.key;
|
||||||
|
// SAFETY: `key` is the TLS key `ptr` was stored under.
|
||||||
|
unsafe { set(key, ptr::without_provenance_mut(1)) };
|
||||||
|
drop(ptr);
|
||||||
|
// SAFETY: `key` is the TLS key `ptr` was stored under.
|
||||||
|
unsafe { set(key, ptr::null_mut()) };
|
||||||
|
// Make sure that the runtime cleanup will be performed
|
||||||
|
// after the next round of TLS destruction.
|
||||||
|
guard::enable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_macro_transparency = "semiopaque"]
|
||||||
|
pub(crate) macro local_pointer {
|
||||||
|
() => {},
|
||||||
|
($vis:vis static $name:ident; $($rest:tt)*) => {
|
||||||
|
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
|
||||||
|
$crate::sys::thread_local::local_pointer! { $($rest)* }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct LocalPointer {
|
||||||
|
key: LazyKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalPointer {
|
||||||
|
pub const fn __new() -> LocalPointer {
|
||||||
|
LocalPointer { key: LazyKey::new(None) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&'static self) -> *mut () {
|
||||||
|
unsafe { get(self.key.force()) as *mut () }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&'static self, p: *mut ()) {
|
||||||
|
unsafe { set(self.key.force(), p as *mut u8) }
|
||||||
|
}
|
||||||
|
}
|
||||||
53
crates/std/src/sys/time/mod.rs
Normal file
53
crates/std/src/sys/time/mod.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
cfg_select! {
|
||||||
|
target_os = "hermit" => {
|
||||||
|
mod hermit;
|
||||||
|
use hermit as imp;
|
||||||
|
}
|
||||||
|
target_os = "motor" => {
|
||||||
|
use moto_rt::time as imp;
|
||||||
|
}
|
||||||
|
all(target_vendor = "fortanix", target_env = "sgx") => {
|
||||||
|
mod sgx;
|
||||||
|
use sgx as imp;
|
||||||
|
}
|
||||||
|
target_os = "solid_asp3" => {
|
||||||
|
mod solid;
|
||||||
|
use solid as imp;
|
||||||
|
}
|
||||||
|
target_os = "uefi" => {
|
||||||
|
mod uefi;
|
||||||
|
use uefi as imp;
|
||||||
|
}
|
||||||
|
any(
|
||||||
|
target_os = "teeos",
|
||||||
|
target_family = "unix",
|
||||||
|
target_os = "wasi",
|
||||||
|
) => {
|
||||||
|
mod unix;
|
||||||
|
use unix as imp;
|
||||||
|
}
|
||||||
|
target_os = "vexos" => {
|
||||||
|
mod vexos;
|
||||||
|
#[expect(unused)]
|
||||||
|
mod unsupported;
|
||||||
|
|
||||||
|
mod imp {
|
||||||
|
pub use super::vexos::Instant;
|
||||||
|
pub use super::unsupported::{SystemTime, UNIX_EPOCH};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target_os = "windows" => {
|
||||||
|
mod windows;
|
||||||
|
use windows as imp;
|
||||||
|
}
|
||||||
|
target_os = "xous" => {
|
||||||
|
mod xous;
|
||||||
|
use xous as imp;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
mod unsupported;
|
||||||
|
use unsupported as imp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use imp::{Instant, SystemTime, UNIX_EPOCH};
|
||||||
49
crates/std/src/sys/time/unsupported.rs
Normal file
49
crates/std/src/sys/time/unsupported.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
use crate::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||||
|
pub struct Instant(Duration);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||||
|
pub struct SystemTime(Duration);
|
||||||
|
|
||||||
|
pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
|
||||||
|
|
||||||
|
impl Instant {
|
||||||
|
pub fn now() -> Instant {
|
||||||
|
panic!("time not implemented on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
|
||||||
|
self.0.checked_sub(other.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
|
||||||
|
Some(Instant(self.0.checked_add(*other)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
|
||||||
|
Some(Instant(self.0.checked_sub(*other)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemTime {
|
||||||
|
pub const MAX: SystemTime = SystemTime(Duration::MAX);
|
||||||
|
|
||||||
|
pub const MIN: SystemTime = SystemTime(Duration::ZERO);
|
||||||
|
|
||||||
|
pub fn now() -> SystemTime {
|
||||||
|
panic!("time not implemented on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||||
|
self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||||
|
Some(SystemTime(self.0.checked_add(*other)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||||
|
Some(SystemTime(self.0.checked_sub(*other)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
24
crates/std/src/thread.rs
Normal file
24
crates/std/src/thread.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
pub mod local;
|
||||||
|
|
||||||
|
pub use crate::sys::thread::*;
|
||||||
|
pub use local::LocalKey;
|
||||||
|
|
||||||
|
// Implementation details used by the thread_local!{} macro.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "thread_local_internals", issue = "none")]
|
||||||
|
pub mod local_impl {
|
||||||
|
pub use super::local::thread_local_process_attrs;
|
||||||
|
pub use crate::sys::thread_local::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ThreadInit {
|
||||||
|
pub handle: Thread,
|
||||||
|
pub rust_start: Box<dyn FnOnce() + Send>,
|
||||||
|
}
|
||||||
|
impl ThreadInit {
|
||||||
|
/// Initialize the 'current thread' mechanism on this thread, returning the
|
||||||
|
/// Rust entry point.
|
||||||
|
pub fn init(self: Box<Self>) -> Box<dyn FnOnce() + Send> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
865
crates/std/src/thread/local.rs
Normal file
865
crates/std/src/thread/local.rs
Normal file
@@ -0,0 +1,865 @@
|
|||||||
|
//! Thread local storage
|
||||||
|
|
||||||
|
#![unstable(feature = "thread_local_internals", issue = "none")]
|
||||||
|
|
||||||
|
use crate::cell::{Cell, RefCell};
|
||||||
|
use crate::error::Error;
|
||||||
|
use crate::fmt;
|
||||||
|
|
||||||
|
/// A thread local storage (TLS) key which owns its contents.
|
||||||
|
///
|
||||||
|
/// This key uses the fastest implementation available on the target platform.
|
||||||
|
/// It is instantiated with the [`thread_local!`] macro and the
|
||||||
|
/// primary method is the [`with`] method, though there are helpers to make
|
||||||
|
/// working with [`Cell`] types easier.
|
||||||
|
///
|
||||||
|
/// The [`with`] method yields a reference to the contained value which cannot
|
||||||
|
/// outlive the current thread or escape the given closure.
|
||||||
|
///
|
||||||
|
/// [`thread_local!`]: crate::thread_local
|
||||||
|
///
|
||||||
|
/// # Initialization and Destruction
|
||||||
|
///
|
||||||
|
/// Initialization is dynamically performed on the first call to a setter (e.g.
|
||||||
|
/// [`with`]) within a thread, and values that implement [`Drop`] get
|
||||||
|
/// destructed when a thread exits. Some platform-specific caveats apply, which
|
||||||
|
/// are explained below.
|
||||||
|
/// Note that, should the destructor panic, the whole process will be [aborted].
|
||||||
|
/// On platforms where initialization requires memory allocation, this is
|
||||||
|
/// performed directly through [`System`], allowing the [global allocator]
|
||||||
|
/// to make use of thread local storage.
|
||||||
|
///
|
||||||
|
/// A `LocalKey`'s initializer cannot recursively depend on itself. Using a
|
||||||
|
/// `LocalKey` in this way may cause panics, aborts, or infinite recursion on
|
||||||
|
/// the first call to `with`.
|
||||||
|
///
|
||||||
|
/// [`System`]: crate::alloc::System
|
||||||
|
/// [global allocator]: crate::alloc
|
||||||
|
/// [aborted]: crate::process::abort
|
||||||
|
///
|
||||||
|
/// # Single-thread Synchronization
|
||||||
|
///
|
||||||
|
/// Though there is no potential race with other threads, it is still possible to
|
||||||
|
/// obtain multiple references to the thread-local data in different places on
|
||||||
|
/// the call stack. For this reason, only shared (`&T`) references may be obtained.
|
||||||
|
///
|
||||||
|
/// To allow obtaining an exclusive mutable reference (`&mut T`), typically a
|
||||||
|
/// [`Cell`] or [`RefCell`] is used (see the [`std::cell`] for more information
|
||||||
|
/// on how exactly this works). To make this easier there are specialized
|
||||||
|
/// implementations for [`LocalKey<Cell<T>>`] and [`LocalKey<RefCell<T>>`].
|
||||||
|
///
|
||||||
|
/// [`std::cell`]: `crate::cell`
|
||||||
|
/// [`LocalKey<Cell<T>>`]: struct.LocalKey.html#impl-LocalKey<Cell<T>>
|
||||||
|
/// [`LocalKey<RefCell<T>>`]: struct.LocalKey.html#impl-LocalKey<RefCell<T>>
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
/// use std::thread;
|
||||||
|
///
|
||||||
|
/// // explicit `const {}` block enables more efficient initialization
|
||||||
|
/// thread_local!(static FOO: Cell<u32> = const { Cell::new(1) });
|
||||||
|
///
|
||||||
|
/// assert_eq!(FOO.get(), 1);
|
||||||
|
/// FOO.set(2);
|
||||||
|
///
|
||||||
|
/// // each thread starts out with the initial value of 1
|
||||||
|
/// let t = thread::spawn(move || {
|
||||||
|
/// assert_eq!(FOO.get(), 1);
|
||||||
|
/// FOO.set(3);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// // wait for the thread to complete and bail out on panic
|
||||||
|
/// t.join().unwrap();
|
||||||
|
///
|
||||||
|
/// // we retain our original value of 2 despite the child thread
|
||||||
|
/// assert_eq!(FOO.get(), 2);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Platform-specific behavior
|
||||||
|
///
|
||||||
|
/// Note that a "best effort" is made to ensure that destructors for types
|
||||||
|
/// stored in thread local storage are run, but not all platforms can guarantee
|
||||||
|
/// that destructors will be run for all types in thread local storage. For
|
||||||
|
/// example, there are a number of known caveats where destructors are not run:
|
||||||
|
///
|
||||||
|
/// 1. On Unix systems when pthread-based TLS is being used, destructors will
|
||||||
|
/// not be run for TLS values on the main thread when it exits. Note that the
|
||||||
|
/// application will exit immediately after the main thread exits as well.
|
||||||
|
/// 2. On all platforms it's possible for TLS to re-initialize other TLS slots
|
||||||
|
/// during destruction. Some platforms ensure that this cannot happen
|
||||||
|
/// infinitely by preventing re-initialization of any slot that has been
|
||||||
|
/// destroyed, but not all platforms have this guard. Those platforms that do
|
||||||
|
/// not guard typically have a synthetic limit after which point no more
|
||||||
|
/// destructors are run.
|
||||||
|
/// 3. When the process exits on Windows systems, TLS destructors may only be
|
||||||
|
/// run on the thread that causes the process to exit. This is because the
|
||||||
|
/// other threads may be forcibly terminated.
|
||||||
|
///
|
||||||
|
/// ## Synchronization in thread-local destructors
|
||||||
|
///
|
||||||
|
/// On Windows, synchronization operations (such as [`JoinHandle::join`]) in
|
||||||
|
/// thread local destructors are prone to deadlocks and so should be avoided.
|
||||||
|
/// This is because the [loader lock] is held while a destructor is run. The
|
||||||
|
/// lock is acquired whenever a thread starts or exits or when a DLL is loaded
|
||||||
|
/// or unloaded. Therefore these events are blocked for as long as a thread
|
||||||
|
/// local destructor is running.
|
||||||
|
///
|
||||||
|
/// [loader lock]: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
|
||||||
|
/// [`JoinHandle::join`]: crate::thread::JoinHandle::join
|
||||||
|
/// [`with`]: LocalKey::with
|
||||||
|
#[cfg_attr(not(test), rustc_diagnostic_item = "LocalKey")]
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub struct LocalKey<T: 'static> {
|
||||||
|
// This outer `LocalKey<T>` type is what's going to be stored in statics,
|
||||||
|
// but actual data inside will sometimes be tagged with #[thread_local].
|
||||||
|
// It's not valid for a true static to reference a #[thread_local] static,
|
||||||
|
// so we get around that by exposing an accessor through a layer of function
|
||||||
|
// indirection (this thunk).
|
||||||
|
//
|
||||||
|
// Note that the thunk is itself unsafe because the returned lifetime of the
|
||||||
|
// slot where data lives, `'static`, is not actually valid. The lifetime
|
||||||
|
// here is actually slightly shorter than the currently running thread!
|
||||||
|
//
|
||||||
|
// Although this is an extra layer of indirection, it should in theory be
|
||||||
|
// trivially devirtualizable by LLVM because the value of `inner` never
|
||||||
|
// changes and the constant should be readonly within a crate. This mainly
|
||||||
|
// only runs into problems when TLS statics are exported across crates.
|
||||||
|
inner: fn(Option<&mut Option<T>>) -> *const T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||||
|
impl<T: 'static> fmt::Debug for LocalKey<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("LocalKey").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow_internal_unstable(thread_local_internals)]
|
||||||
|
#[unstable(feature = "thread_local_internals", issue = "none")]
|
||||||
|
#[rustc_macro_transparency = "semiopaque"]
|
||||||
|
pub macro thread_local_process_attrs {
|
||||||
|
|
||||||
|
// Parse `cfg_attr` to figure out whether it's a `rustc_align_static`.
|
||||||
|
// Each `cfg_attr` can have zero or more attributes on the RHS, and can be nested.
|
||||||
|
|
||||||
|
// finished parsing the `cfg_attr`, it had no `rustc_align_static`
|
||||||
|
(
|
||||||
|
[] [$(#[$($prev_other_attrs:tt)*])*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
|
||||||
|
[$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
|
||||||
|
$($rest:tt)*
|
||||||
|
) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[$($prev_align_attrs_ret)*] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),*)]];
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// finished parsing the `cfg_attr`, it had nothing but `rustc_align_static`
|
||||||
|
(
|
||||||
|
[$(#[$($prev_align_attrs:tt)*])+] [];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
|
||||||
|
[$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
|
||||||
|
$($rest:tt)*
|
||||||
|
) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)*];
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// finished parsing the `cfg_attr`, it had a mix of `rustc_align_static` and other attrs
|
||||||
|
(
|
||||||
|
[$(#[$($prev_align_attrs:tt)*])+] [$(#[$($prev_other_attrs:tt)*])+];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
|
||||||
|
[$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
|
||||||
|
$($rest:tt)*
|
||||||
|
) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),+)]];
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// it's a `rustc_align_static`
|
||||||
|
(
|
||||||
|
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [rustc_align_static($($align_static_args:tt)*) $(, $($attr_rhs:tt)*)?] };
|
||||||
|
$($rest:tt)*
|
||||||
|
) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[$($prev_align_attrs)* #[rustc_align_static($($align_static_args)*)]] [$($prev_other_attrs)*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// it's a nested `cfg_attr(true, ...)`; recurse into RHS
|
||||||
|
(
|
||||||
|
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
|
||||||
|
$($rest:tt)*
|
||||||
|
) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[] [];
|
||||||
|
@processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] };
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// it's a nested `cfg_attr(false, ...)`; recurse into RHS
|
||||||
|
(
|
||||||
|
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
|
||||||
|
$($rest:tt)*
|
||||||
|
) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[] [];
|
||||||
|
@processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] };
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
// it's a nested `cfg_attr(..., ...)`; recurse into RHS
|
||||||
|
(
|
||||||
|
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:meta, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
|
||||||
|
$($rest:tt)*
|
||||||
|
) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[] [];
|
||||||
|
@processing_cfg_attr { pred: ($cfg_lhs), rhs: [$($cfg_rhs)*] };
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// it's some other attribute
|
||||||
|
(
|
||||||
|
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [$meta:meta $(, $($attr_rhs:tt)*)?] };
|
||||||
|
$($rest:tt)*
|
||||||
|
) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)* #[$meta]];
|
||||||
|
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
// Separate attributes into `rustc_align_static` and everything else:
|
||||||
|
|
||||||
|
// `rustc_align_static` attribute
|
||||||
|
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[rustc_align_static $($attr_rest:tt)*] $($rest:tt)*) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[$($prev_align_attrs)* #[rustc_align_static $($attr_rest)*]] [$($prev_other_attrs)*];
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// `cfg_attr(true, ...)` attribute; parse it
|
||||||
|
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(true, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[] [];
|
||||||
|
@processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] };
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// `cfg_attr(false, ...)` attribute; parse it
|
||||||
|
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(false, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[] [];
|
||||||
|
@processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] };
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// `cfg_attr(..., ...)` attribute; parse it
|
||||||
|
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[] [];
|
||||||
|
@processing_cfg_attr { pred: ($cfg_pred), rhs: [$($cfg_rhs)*] };
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// doc comment not followed by any other attributes; process it all at once to avoid blowing recursion limit
|
||||||
|
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; $(#[doc $($doc_rhs:tt)*])+ $vis:vis static $($rest:tt)*) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)* $(#[doc $($doc_rhs)*])+];
|
||||||
|
$vis static $($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// 8 lines of doc comment; process them all at once to avoid blowing recursion limit
|
||||||
|
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
|
||||||
|
#[doc $($doc_rhs_1:tt)*] #[doc $($doc_rhs_2:tt)*] #[doc $($doc_rhs_3:tt)*] #[doc $($doc_rhs_4:tt)*]
|
||||||
|
#[doc $($doc_rhs_5:tt)*] #[doc $($doc_rhs_6:tt)*] #[doc $($doc_rhs_7:tt)*] #[doc $($doc_rhs_8:tt)*]
|
||||||
|
$($rest:tt)*) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)*
|
||||||
|
#[doc $($doc_rhs_1)*] #[doc $($doc_rhs_2)*] #[doc $($doc_rhs_3)*] #[doc $($doc_rhs_4)*]
|
||||||
|
#[doc $($doc_rhs_5)*] #[doc $($doc_rhs_6)*] #[doc $($doc_rhs_7)*] #[doc $($doc_rhs_8)*]];
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
// other attribute
|
||||||
|
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[$($attr:tt)*] $($rest:tt)*) => (
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!(
|
||||||
|
[$($prev_align_attrs)*] [$($prev_other_attrs)* #[$($attr)*]];
|
||||||
|
$($rest)*
|
||||||
|
);
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
// Delegate to `thread_local_inner` once attributes are fully categorized:
|
||||||
|
|
||||||
|
// process `const` declaration and recurse
|
||||||
|
([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = const $init:block $(; $($($rest:tt)+)?)?) => (
|
||||||
|
$($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> =
|
||||||
|
$crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, const $init);
|
||||||
|
|
||||||
|
$($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)?
|
||||||
|
),
|
||||||
|
|
||||||
|
// process non-`const` declaration and recurse
|
||||||
|
([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = $init:expr $(; $($($rest:tt)+)?)?) => (
|
||||||
|
$($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> =
|
||||||
|
$crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, $init);
|
||||||
|
|
||||||
|
$($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)?
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Declare a new thread local storage key of type [`std::thread::LocalKey`].
|
||||||
|
///
|
||||||
|
/// # Syntax
|
||||||
|
///
|
||||||
|
/// The macro wraps any number of static declarations and makes them thread local.
|
||||||
|
/// Publicity and attributes for each static are allowed. Example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::{Cell, RefCell};
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// pub static FOO: Cell<u32> = const { Cell::new(1) };
|
||||||
|
///
|
||||||
|
/// static BAR: RefCell<Vec<f32>> = RefCell::new(vec![1.0, 2.0]);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(FOO.get(), 1);
|
||||||
|
/// BAR.with_borrow(|v| assert_eq!(v[1], 2.0));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Note that only shared references (`&T`) to the inner data may be obtained, so a
|
||||||
|
/// type such as [`Cell`] or [`RefCell`] is typically used to allow mutating access.
|
||||||
|
///
|
||||||
|
/// This macro supports a special `const {}` syntax that can be used
|
||||||
|
/// when the initialization expression can be evaluated as a constant.
|
||||||
|
/// This can enable a more efficient thread local implementation that
|
||||||
|
/// can avoid lazy initialization. For types that do not
|
||||||
|
/// [need to be dropped][crate::mem::needs_drop], this can enable an
|
||||||
|
/// even more efficient implementation that does not need to
|
||||||
|
/// track any additional state.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::RefCell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// pub static FOO: RefCell<Vec<u32>> = const { RefCell::new(Vec::new()) };
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// FOO.with_borrow(|v| assert_eq!(v.len(), 0));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// See [`LocalKey` documentation][`std::thread::LocalKey`] for more
|
||||||
|
/// information.
|
||||||
|
///
|
||||||
|
/// [`std::thread::LocalKey`]: crate::thread::LocalKey
|
||||||
|
#[macro_export]
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")]
|
||||||
|
#[allow_internal_unstable(thread_local_internals)]
|
||||||
|
macro_rules! thread_local {
|
||||||
|
() => {};
|
||||||
|
|
||||||
|
($($tt:tt)+) => {
|
||||||
|
$crate::thread::local_impl::thread_local_process_attrs!([] []; $($tt)+);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with).
|
||||||
|
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct AccessError;
|
||||||
|
|
||||||
|
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
|
||||||
|
impl fmt::Debug for AccessError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("AccessError").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
|
||||||
|
impl fmt::Display for AccessError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt("already destroyed", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
|
||||||
|
impl Error for AccessError {}
|
||||||
|
|
||||||
|
// This ensures the panicking code is outlined from `with` for `LocalKey`.
|
||||||
|
#[cfg_attr(not(panic = "immediate-abort"), inline(never))]
|
||||||
|
#[track_caller]
|
||||||
|
#[cold]
|
||||||
|
fn panic_access_error(err: AccessError) -> ! {
|
||||||
|
panic!("cannot access a Thread Local Storage value during or after destruction: {err:?}")
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> LocalKey<T> {
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(
|
||||||
|
feature = "thread_local_internals",
|
||||||
|
reason = "recently added to create a key",
|
||||||
|
issue = "none"
|
||||||
|
)]
|
||||||
|
pub const unsafe fn new(inner: fn(Option<&mut Option<T>>) -> *const T) -> LocalKey<T> {
|
||||||
|
LocalKey { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acquires a reference to the value in this TLS key.
|
||||||
|
///
|
||||||
|
/// This will lazily initialize the value if this thread has not referenced
|
||||||
|
/// this key yet.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function will `panic!()` if the key currently has its
|
||||||
|
/// destructor running, and it **may** panic if the destructor has
|
||||||
|
/// previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// thread_local! {
|
||||||
|
/// pub static STATIC: String = String::from("I am");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// STATIC.with(|original_value| format!("{original_value} initialized")),
|
||||||
|
/// "I am initialized",
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub fn with<F, R>(&'static self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&T) -> R,
|
||||||
|
{
|
||||||
|
match self.try_with(f) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(err) => panic_access_error(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acquires a reference to the value in this TLS key.
|
||||||
|
///
|
||||||
|
/// This will lazily initialize the value if this thread has not referenced
|
||||||
|
/// this key yet. If the key has been destroyed (which may happen if this is called
|
||||||
|
/// in a destructor), this function will return an [`AccessError`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function will still `panic!()` if the key is uninitialized and the
|
||||||
|
/// key's initializer panics.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// thread_local! {
|
||||||
|
/// pub static STATIC: String = String::from("I am");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// STATIC.try_with(|original_value| format!("{original_value} initialized")),
|
||||||
|
/// Ok(String::from("I am initialized")),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
|
||||||
|
#[inline]
|
||||||
|
pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>
|
||||||
|
where
|
||||||
|
F: FnOnce(&T) -> R,
|
||||||
|
{
|
||||||
|
let thread_local = unsafe { (self.inner)(None).as_ref().ok_or(AccessError)? };
|
||||||
|
Ok(f(thread_local))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acquires a reference to the value in this TLS key, initializing it with
|
||||||
|
/// `init` if it wasn't already initialized on this thread.
|
||||||
|
///
|
||||||
|
/// If `init` was used to initialize the thread local variable, `None` is
|
||||||
|
/// passed as the first argument to `f`. If it was already initialized,
|
||||||
|
/// `Some(init)` is passed to `f`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function will panic if the key currently has its destructor
|
||||||
|
/// running, and it **may** panic if the destructor has previously been run
|
||||||
|
/// for this thread.
|
||||||
|
fn initialize_with<F, R>(&'static self, init: T, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(Option<T>, &T) -> R,
|
||||||
|
{
|
||||||
|
let mut init = Some(init);
|
||||||
|
|
||||||
|
let reference = unsafe {
|
||||||
|
match (self.inner)(Some(&mut init)).as_ref() {
|
||||||
|
Some(r) => r,
|
||||||
|
None => panic_access_error(AccessError),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
f(init, reference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> LocalKey<Cell<T>> {
|
||||||
|
/// Sets or initializes the contained value.
|
||||||
|
///
|
||||||
|
/// Unlike the other methods, this will *not* run the lazy initializer of
|
||||||
|
/// the thread local. Instead, it will be directly initialized with the
|
||||||
|
/// given value if it wasn't initialized yet.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the key currently has its destructor running,
|
||||||
|
/// and it **may** panic if the destructor has previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: Cell<i32> = panic!("!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Calling X.get() here would result in a panic.
|
||||||
|
///
|
||||||
|
/// X.set(123); // But X.set() is fine, as it skips the initializer above.
|
||||||
|
///
|
||||||
|
/// assert_eq!(X.get(), 123);
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
|
||||||
|
pub fn set(&'static self, value: T) {
|
||||||
|
self.initialize_with(Cell::new(value), |value, cell| {
|
||||||
|
if let Some(value) = value {
|
||||||
|
// The cell was already initialized, so `value` wasn't used to
|
||||||
|
// initialize it. So we overwrite the current value with the
|
||||||
|
// new one instead.
|
||||||
|
cell.set(value.into_inner());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a copy of the contained value.
|
||||||
|
///
|
||||||
|
/// This will lazily initialize the value if this thread has not referenced
|
||||||
|
/// this key yet.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the key currently has its destructor running,
|
||||||
|
/// and it **may** panic if the destructor has previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: Cell<i32> = const { Cell::new(1) };
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(X.get(), 1);
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
|
||||||
|
pub fn get(&'static self) -> T
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
self.with(Cell::get)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the contained value, leaving `Default::default()` in its place.
|
||||||
|
///
|
||||||
|
/// This will lazily initialize the value if this thread has not referenced
|
||||||
|
/// this key yet.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the key currently has its destructor running,
|
||||||
|
/// and it **may** panic if the destructor has previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: Cell<Option<i32>> = const { Cell::new(Some(1)) };
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(X.take(), Some(1));
|
||||||
|
/// assert_eq!(X.take(), None);
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
|
||||||
|
pub fn take(&'static self) -> T
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
self.with(Cell::take)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces the contained value, returning the old value.
|
||||||
|
///
|
||||||
|
/// This will lazily initialize the value if this thread has not referenced
|
||||||
|
/// this key yet.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the key currently has its destructor running,
|
||||||
|
/// and it **may** panic if the destructor has previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: Cell<i32> = const { Cell::new(1) };
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// assert_eq!(X.replace(2), 1);
|
||||||
|
/// assert_eq!(X.replace(3), 2);
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
|
||||||
|
#[rustc_confusables("swap")]
|
||||||
|
pub fn replace(&'static self, value: T) -> T {
|
||||||
|
self.with(|cell| cell.replace(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the contained value using a function.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(local_key_cell_update)]
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: Cell<i32> = const { Cell::new(5) };
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// X.update(|x| x + 1);
|
||||||
|
/// assert_eq!(X.get(), 6);
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "local_key_cell_update", issue = "143989")]
|
||||||
|
pub fn update(&'static self, f: impl FnOnce(T) -> T)
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
self.with(|cell| cell.update(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> LocalKey<RefCell<T>> {
|
||||||
|
/// Acquires a reference to the contained value.
|
||||||
|
///
|
||||||
|
/// This will lazily initialize the value if this thread has not referenced
|
||||||
|
/// this key yet.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is currently mutably borrowed.
|
||||||
|
///
|
||||||
|
/// Panics if the key currently has its destructor running,
|
||||||
|
/// and it **may** panic if the destructor has previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::RefCell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// X.with_borrow(|v| assert!(v.is_empty()));
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
|
||||||
|
pub fn with_borrow<F, R>(&'static self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&T) -> R,
|
||||||
|
{
|
||||||
|
self.with(|cell| f(&cell.borrow()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acquires a mutable reference to the contained value.
|
||||||
|
///
|
||||||
|
/// This will lazily initialize the value if this thread has not referenced
|
||||||
|
/// this key yet.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is currently borrowed.
|
||||||
|
///
|
||||||
|
/// Panics if the key currently has its destructor running,
|
||||||
|
/// and it **may** panic if the destructor has previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::RefCell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// X.with_borrow_mut(|v| v.push(1));
|
||||||
|
///
|
||||||
|
/// X.with_borrow(|v| assert_eq!(*v, vec![1]));
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
|
||||||
|
pub fn with_borrow_mut<F, R>(&'static self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut T) -> R,
|
||||||
|
{
|
||||||
|
self.with(|cell| f(&mut cell.borrow_mut()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets or initializes the contained value.
|
||||||
|
///
|
||||||
|
/// Unlike the other methods, this will *not* run the lazy initializer of
|
||||||
|
/// the thread local. Instead, it will be directly initialized with the
|
||||||
|
/// given value if it wasn't initialized yet.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is currently borrowed.
|
||||||
|
///
|
||||||
|
/// Panics if the key currently has its destructor running,
|
||||||
|
/// and it **may** panic if the destructor has previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::RefCell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: RefCell<Vec<i32>> = panic!("!");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Calling X.with() here would result in a panic.
|
||||||
|
///
|
||||||
|
/// X.set(vec![1, 2, 3]); // But X.set() is fine, as it skips the initializer above.
|
||||||
|
///
|
||||||
|
/// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
|
||||||
|
pub fn set(&'static self, value: T) {
|
||||||
|
self.initialize_with(RefCell::new(value), |value, cell| {
|
||||||
|
if let Some(value) = value {
|
||||||
|
// The cell was already initialized, so `value` wasn't used to
|
||||||
|
// initialize it. So we overwrite the current value with the
|
||||||
|
// new one instead.
|
||||||
|
*cell.borrow_mut() = value.into_inner();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the contained value, leaving `Default::default()` in its place.
|
||||||
|
///
|
||||||
|
/// This will lazily initialize the value if this thread has not referenced
|
||||||
|
/// this key yet.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is currently borrowed.
|
||||||
|
///
|
||||||
|
/// Panics if the key currently has its destructor running,
|
||||||
|
/// and it **may** panic if the destructor has previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::RefCell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// X.with_borrow_mut(|v| v.push(1));
|
||||||
|
///
|
||||||
|
/// let a = X.take();
|
||||||
|
///
|
||||||
|
/// assert_eq!(a, vec![1]);
|
||||||
|
///
|
||||||
|
/// X.with_borrow(|v| assert!(v.is_empty()));
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
|
||||||
|
pub fn take(&'static self) -> T
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
self.with(RefCell::take)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces the contained value, returning the old value.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is currently borrowed.
|
||||||
|
///
|
||||||
|
/// Panics if the key currently has its destructor running,
|
||||||
|
/// and it **may** panic if the destructor has previously been run for this thread.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::RefCell;
|
||||||
|
///
|
||||||
|
/// thread_local! {
|
||||||
|
/// static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let prev = X.replace(vec![1, 2, 3]);
|
||||||
|
/// assert!(prev.is_empty());
|
||||||
|
///
|
||||||
|
/// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
|
||||||
|
#[rustc_confusables("swap")]
|
||||||
|
pub fn replace(&'static self, value: T) -> T {
|
||||||
|
self.with(|cell| cell.replace(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
859
crates/std/src/time.rs
Normal file
859
crates/std/src/time.rs
Normal file
@@ -0,0 +1,859 @@
|
|||||||
|
//! Temporal quantification.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! There are multiple ways to create a new [`Duration`]:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use std::time::Duration;
|
||||||
|
//! let five_seconds = Duration::from_secs(5);
|
||||||
|
//! assert_eq!(five_seconds, Duration::from_millis(5_000));
|
||||||
|
//! assert_eq!(five_seconds, Duration::from_micros(5_000_000));
|
||||||
|
//! assert_eq!(five_seconds, Duration::from_nanos(5_000_000_000));
|
||||||
|
//!
|
||||||
|
//! let ten_seconds = Duration::from_secs(10);
|
||||||
|
//! let seven_nanos = Duration::from_nanos(7);
|
||||||
|
//! let total = ten_seconds + seven_nanos;
|
||||||
|
//! assert_eq!(total, Duration::new(10, 7));
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Using [`Instant`] to calculate how long a function took to run:
|
||||||
|
//!
|
||||||
|
//! ```ignore (incomplete)
|
||||||
|
//! let now = Instant::now();
|
||||||
|
//!
|
||||||
|
//! // Calling a slow function, it may take a while
|
||||||
|
//! slow_function();
|
||||||
|
//!
|
||||||
|
//! let elapsed_time = now.elapsed();
|
||||||
|
//! println!("Running slow_function() took {} seconds.", elapsed_time.as_secs());
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
#![stable(feature = "time", since = "1.3.0")]
|
||||||
|
|
||||||
|
#[stable(feature = "time", since = "1.3.0")]
|
||||||
|
pub use core::time::Duration;
|
||||||
|
#[stable(feature = "duration_checked_float", since = "1.66.0")]
|
||||||
|
pub use core::time::TryFromFloatSecsError;
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
use crate::fmt;
|
||||||
|
use crate::ops::{Add, AddAssign, Sub, SubAssign};
|
||||||
|
use crate::sys::{FromInner, IntoInner, time};
|
||||||
|
|
||||||
|
/// A measurement of a monotonically nondecreasing clock.
|
||||||
|
/// Opaque and useful only with [`Duration`].
|
||||||
|
///
|
||||||
|
/// Instants are always guaranteed, barring [platform bugs], to be no less than any previously
|
||||||
|
/// measured instant when created, and are often useful for tasks such as measuring
|
||||||
|
/// benchmarks or timing how long an operation takes.
|
||||||
|
///
|
||||||
|
/// Note, however, that instants are **not** guaranteed to be **steady**. In other
|
||||||
|
/// words, each tick of the underlying clock might not be the same length (e.g.
|
||||||
|
/// some seconds may be longer than others). An instant may jump forwards or
|
||||||
|
/// experience time dilation (slow down or speed up), but it will never go
|
||||||
|
/// backwards.
|
||||||
|
/// As part of this non-guarantee it is also not specified whether system suspends count as
|
||||||
|
/// elapsed time or not. The behavior varies across platforms and Rust versions.
|
||||||
|
///
|
||||||
|
/// Instants are opaque types that can only be compared to one another. There is
|
||||||
|
/// no method to get "the number of seconds" from an instant. Instead, it only
|
||||||
|
/// allows measuring the duration between two instants (or comparing two
|
||||||
|
/// instants).
|
||||||
|
///
|
||||||
|
/// The size of an `Instant` struct may vary depending on the target operating
|
||||||
|
/// system.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::{Duration, Instant};
|
||||||
|
/// use std::thread::sleep;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let now = Instant::now();
|
||||||
|
///
|
||||||
|
/// // we sleep for 2 seconds
|
||||||
|
/// sleep(Duration::new(2, 0));
|
||||||
|
/// // it prints '2'
|
||||||
|
/// println!("{}", now.elapsed().as_secs());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [platform bugs]: Instant#monotonicity
|
||||||
|
///
|
||||||
|
/// # OS-specific behaviors
|
||||||
|
///
|
||||||
|
/// An `Instant` is a wrapper around system-specific types and it may behave
|
||||||
|
/// differently depending on the underlying operating system. For example,
|
||||||
|
/// the following snippet is fine on Linux but panics on macOS:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::{Instant, Duration};
|
||||||
|
///
|
||||||
|
/// let now = Instant::now();
|
||||||
|
/// let days_per_10_millennia = 365_2425;
|
||||||
|
/// let solar_seconds_per_day = 60 * 60 * 24;
|
||||||
|
/// let millennium_in_solar_seconds = 31_556_952_000;
|
||||||
|
/// assert_eq!(millennium_in_solar_seconds, days_per_10_millennia * solar_seconds_per_day / 10);
|
||||||
|
///
|
||||||
|
/// let duration = Duration::new(millennium_in_solar_seconds, 0);
|
||||||
|
/// println!("{:?}", now + duration);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For cross-platform code, you can comfortably use durations of up to around one hundred years.
|
||||||
|
///
|
||||||
|
/// # Underlying System calls
|
||||||
|
///
|
||||||
|
/// The following system calls are [currently] being used by `now()` to find out
|
||||||
|
/// the current time:
|
||||||
|
///
|
||||||
|
/// | Platform | System call |
|
||||||
|
/// |-----------|----------------------------------------------------------------------|
|
||||||
|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
|
||||||
|
/// | UNIX | [clock_gettime] with `CLOCK_MONOTONIC` |
|
||||||
|
/// | Darwin | [clock_gettime] with `CLOCK_UPTIME_RAW` |
|
||||||
|
/// | VXWorks | [clock_gettime] with `CLOCK_MONOTONIC` |
|
||||||
|
/// | SOLID | `get_tim` |
|
||||||
|
/// | WASI | [__wasi_clock_time_get] with `monotonic` |
|
||||||
|
/// | Windows | [QueryPerformanceCounter] |
|
||||||
|
///
|
||||||
|
/// [currently]: crate::io#platform-specific-behavior
|
||||||
|
/// [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter
|
||||||
|
/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
|
||||||
|
/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
|
||||||
|
/// [__wasi_clock_time_get]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get
|
||||||
|
/// [clock_gettime]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
|
||||||
|
///
|
||||||
|
/// **Disclaimer:** These system calls might change over time.
|
||||||
|
///
|
||||||
|
/// > Note: mathematical operations like [`add`] may panic if the underlying
|
||||||
|
/// > structure cannot represent the new point in time.
|
||||||
|
///
|
||||||
|
/// [`add`]: Instant::add
|
||||||
|
///
|
||||||
|
/// ## Monotonicity
|
||||||
|
///
|
||||||
|
/// On all platforms `Instant` will try to use an OS API that guarantees monotonic behavior
|
||||||
|
/// if available, which is the case for all [tier 1] platforms.
|
||||||
|
/// In practice such guarantees are – under rare circumstances – broken by hardware, virtualization
|
||||||
|
/// or operating system bugs. To work around these bugs and platforms not offering monotonic clocks
|
||||||
|
/// [`duration_since`], [`elapsed`] and [`sub`] saturate to zero. In older Rust versions this
|
||||||
|
/// lead to a panic instead. [`checked_duration_since`] can be used to detect and handle situations
|
||||||
|
/// where monotonicity is violated, or `Instant`s are subtracted in the wrong order.
|
||||||
|
///
|
||||||
|
/// This workaround obscures programming errors where earlier and later instants are accidentally
|
||||||
|
/// swapped. For this reason future Rust versions may reintroduce panics.
|
||||||
|
///
|
||||||
|
/// [tier 1]: https://doc.rust-lang.org/rustc/platform-support.html
|
||||||
|
/// [`duration_since`]: Instant::duration_since
|
||||||
|
/// [`elapsed`]: Instant::elapsed
|
||||||
|
/// [`sub`]: Instant::sub
|
||||||
|
/// [`checked_duration_since`]: Instant::checked_duration_since
|
||||||
|
///
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
#[cfg_attr(not(test), rustc_diagnostic_item = "Instant")]
|
||||||
|
pub struct Instant(time::Instant);
|
||||||
|
|
||||||
|
/// A measurement of the system clock, useful for talking to
|
||||||
|
/// external entities like the file system or other processes.
|
||||||
|
///
|
||||||
|
/// Distinct from the [`Instant`] type, this time measurement **is not
|
||||||
|
/// monotonic**. This means that you can save a file to the file system, then
|
||||||
|
/// save another file to the file system, **and the second file has a
|
||||||
|
/// `SystemTime` measurement earlier than the first**. In other words, an
|
||||||
|
/// operation that happens after another operation in real time may have an
|
||||||
|
/// earlier `SystemTime`!
|
||||||
|
///
|
||||||
|
/// Consequently, comparing two `SystemTime` instances to learn about the
|
||||||
|
/// duration between them returns a [`Result`] instead of an infallible [`Duration`]
|
||||||
|
/// to indicate that this sort of time drift may happen and needs to be handled.
|
||||||
|
///
|
||||||
|
/// Although a `SystemTime` cannot be directly inspected, the [`UNIX_EPOCH`]
|
||||||
|
/// constant is provided in this module as an anchor in time to learn
|
||||||
|
/// information about a `SystemTime`. By calculating the duration from this
|
||||||
|
/// fixed point in time, a `SystemTime` can be converted to a human-readable time,
|
||||||
|
/// or perhaps some other string representation.
|
||||||
|
///
|
||||||
|
/// The size of a `SystemTime` struct may vary depending on the target operating
|
||||||
|
/// system.
|
||||||
|
///
|
||||||
|
/// A `SystemTime` does not count leap seconds.
|
||||||
|
/// `SystemTime::now()`'s behavior around a leap second
|
||||||
|
/// is the same as the operating system's wall clock.
|
||||||
|
/// The precise behavior near a leap second
|
||||||
|
/// (e.g. whether the clock appears to run slow or fast, or stop, or jump)
|
||||||
|
/// depends on platform and configuration,
|
||||||
|
/// so should not be relied on.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::{Duration, SystemTime};
|
||||||
|
/// use std::thread::sleep;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let now = SystemTime::now();
|
||||||
|
///
|
||||||
|
/// // we sleep for 2 seconds
|
||||||
|
/// sleep(Duration::new(2, 0));
|
||||||
|
/// match now.elapsed() {
|
||||||
|
/// Ok(elapsed) => {
|
||||||
|
/// // it prints '2'
|
||||||
|
/// println!("{}", elapsed.as_secs());
|
||||||
|
/// }
|
||||||
|
/// Err(e) => {
|
||||||
|
/// // the system clock went backwards!
|
||||||
|
/// println!("Great Scott! {e:?}");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Platform-specific behavior
|
||||||
|
///
|
||||||
|
/// The precision of `SystemTime` can depend on the underlying OS-specific time format.
|
||||||
|
/// For example, on Windows the time is represented in 100 nanosecond intervals whereas Linux
|
||||||
|
/// can represent nanosecond intervals.
|
||||||
|
///
|
||||||
|
/// The following system calls are [currently] being used by `now()` to find out
|
||||||
|
/// the current time:
|
||||||
|
///
|
||||||
|
/// | Platform | System call |
|
||||||
|
/// |-----------|----------------------------------------------------------------------|
|
||||||
|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
|
||||||
|
/// | UNIX | [clock_gettime (Realtime Clock)] |
|
||||||
|
/// | Darwin | [clock_gettime (Realtime Clock)] |
|
||||||
|
/// | VXWorks | [clock_gettime (Realtime Clock)] |
|
||||||
|
/// | SOLID | `SOLID_RTC_ReadTime` |
|
||||||
|
/// | WASI | [__wasi_clock_time_get (Realtime Clock)] |
|
||||||
|
/// | Windows | [GetSystemTimePreciseAsFileTime] / [GetSystemTimeAsFileTime] |
|
||||||
|
///
|
||||||
|
/// [currently]: crate::io#platform-specific-behavior
|
||||||
|
/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
|
||||||
|
/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
|
||||||
|
/// [clock_gettime (Realtime Clock)]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
|
||||||
|
/// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get
|
||||||
|
/// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
|
||||||
|
/// [GetSystemTimeAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime
|
||||||
|
///
|
||||||
|
/// **Disclaimer:** These system calls might change over time.
|
||||||
|
///
|
||||||
|
/// > Note: mathematical operations like [`add`] may panic if the underlying
|
||||||
|
/// > structure cannot represent the new point in time.
|
||||||
|
///
|
||||||
|
/// [`add`]: SystemTime::add
|
||||||
|
/// [`UNIX_EPOCH`]: SystemTime::UNIX_EPOCH
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
pub struct SystemTime(time::SystemTime);
|
||||||
|
|
||||||
|
/// An error returned from the `duration_since` and `elapsed` methods on
|
||||||
|
/// `SystemTime`, used to learn how far in the opposite direction a system time
|
||||||
|
/// lies.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::thread::sleep;
|
||||||
|
/// use std::time::{Duration, SystemTime};
|
||||||
|
///
|
||||||
|
/// let sys_time = SystemTime::now();
|
||||||
|
/// sleep(Duration::from_secs(1));
|
||||||
|
/// let new_sys_time = SystemTime::now();
|
||||||
|
/// match sys_time.duration_since(new_sys_time) {
|
||||||
|
/// Ok(_) => {}
|
||||||
|
/// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
pub struct SystemTimeError(Duration);
|
||||||
|
|
||||||
|
impl Instant {
|
||||||
|
/// Returns an instant corresponding to "now".
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::time::Instant;
|
||||||
|
///
|
||||||
|
/// let now = Instant::now();
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
#[cfg_attr(not(test), rustc_diagnostic_item = "instant_now")]
|
||||||
|
pub fn now() -> Instant {
|
||||||
|
Instant(time::Instant::now())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed from another instant to this one,
|
||||||
|
/// or zero duration if that instant is later than this one.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Previous Rust versions panicked when `earlier` was later than `self`. Currently this
|
||||||
|
/// method saturates. Future versions may reintroduce the panic in some circumstances.
|
||||||
|
/// See [Monotonicity].
|
||||||
|
///
|
||||||
|
/// [Monotonicity]: Instant#monotonicity
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::{Duration, Instant};
|
||||||
|
/// use std::thread::sleep;
|
||||||
|
///
|
||||||
|
/// let now = Instant::now();
|
||||||
|
/// sleep(Duration::new(1, 0));
|
||||||
|
/// let new_now = Instant::now();
|
||||||
|
/// println!("{:?}", new_now.duration_since(now));
|
||||||
|
/// println!("{:?}", now.duration_since(new_now)); // 0ns
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
pub fn duration_since(&self, earlier: Instant) -> Duration {
|
||||||
|
self.checked_duration_since(earlier).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed from another instant to this one,
|
||||||
|
/// or None if that instant is later than this one.
|
||||||
|
///
|
||||||
|
/// Due to [monotonicity bugs], even under correct logical ordering of the passed `Instant`s,
|
||||||
|
/// this method can return `None`.
|
||||||
|
///
|
||||||
|
/// [monotonicity bugs]: Instant#monotonicity
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::{Duration, Instant};
|
||||||
|
/// use std::thread::sleep;
|
||||||
|
///
|
||||||
|
/// let now = Instant::now();
|
||||||
|
/// sleep(Duration::new(1, 0));
|
||||||
|
/// let new_now = Instant::now();
|
||||||
|
/// println!("{:?}", new_now.checked_duration_since(now));
|
||||||
|
/// println!("{:?}", now.checked_duration_since(new_now)); // None
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
#[stable(feature = "checked_duration_since", since = "1.39.0")]
|
||||||
|
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
|
||||||
|
self.0.checked_sub_instant(&earlier.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed from another instant to this one,
|
||||||
|
/// or zero duration if that instant is later than this one.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::{Duration, Instant};
|
||||||
|
/// use std::thread::sleep;
|
||||||
|
///
|
||||||
|
/// let now = Instant::now();
|
||||||
|
/// sleep(Duration::new(1, 0));
|
||||||
|
/// let new_now = Instant::now();
|
||||||
|
/// println!("{:?}", new_now.saturating_duration_since(now));
|
||||||
|
/// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
#[stable(feature = "checked_duration_since", since = "1.39.0")]
|
||||||
|
pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
|
||||||
|
self.checked_duration_since(earlier).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed since this instant.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Previous Rust versions panicked when the current time was earlier than self. Currently this
|
||||||
|
/// method returns a Duration of zero in that case. Future versions may reintroduce the panic.
|
||||||
|
/// See [Monotonicity].
|
||||||
|
///
|
||||||
|
/// [Monotonicity]: Instant#monotonicity
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::thread::sleep;
|
||||||
|
/// use std::time::{Duration, Instant};
|
||||||
|
///
|
||||||
|
/// let instant = Instant::now();
|
||||||
|
/// let three_secs = Duration::from_secs(3);
|
||||||
|
/// sleep(three_secs);
|
||||||
|
/// assert!(instant.elapsed() >= three_secs);
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
pub fn elapsed(&self) -> Duration {
|
||||||
|
Instant::now() - *self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
|
||||||
|
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
|
||||||
|
/// otherwise.
|
||||||
|
#[stable(feature = "time_checked_add", since = "1.34.0")]
|
||||||
|
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
|
||||||
|
self.0.checked_add_duration(&duration).map(Instant)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
|
||||||
|
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
|
||||||
|
/// otherwise.
|
||||||
|
#[stable(feature = "time_checked_add", since = "1.34.0")]
|
||||||
|
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
|
||||||
|
self.0.checked_sub_duration(&duration).map(Instant)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by platform specific `sleep_until` implementations such as the one used on Linux.
|
||||||
|
#[cfg_attr(
|
||||||
|
not(target_os = "linux"),
|
||||||
|
allow(unused, reason = "not every platform has a specific `sleep_until`")
|
||||||
|
)]
|
||||||
|
pub(crate) fn into_inner(self) -> time::Instant {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
impl Add<Duration> for Instant {
|
||||||
|
type Output = Instant;
|
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function may panic if the resulting point in time cannot be represented by the
|
||||||
|
/// underlying data structure. See [`Instant::checked_add`] for a version without panic.
|
||||||
|
fn add(self, other: Duration) -> Instant {
|
||||||
|
self.checked_add(other).expect("overflow when adding duration to instant")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time_augmented_assignment", since = "1.9.0")]
|
||||||
|
impl AddAssign<Duration> for Instant {
|
||||||
|
fn add_assign(&mut self, other: Duration) {
|
||||||
|
*self = *self + other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
impl Sub<Duration> for Instant {
|
||||||
|
type Output = Instant;
|
||||||
|
|
||||||
|
fn sub(self, other: Duration) -> Instant {
|
||||||
|
self.checked_sub(other).expect("overflow when subtracting duration from instant")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time_augmented_assignment", since = "1.9.0")]
|
||||||
|
impl SubAssign<Duration> for Instant {
|
||||||
|
fn sub_assign(&mut self, other: Duration) {
|
||||||
|
*self = *self - other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
impl Sub<Instant> for Instant {
|
||||||
|
type Output = Duration;
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed from another instant to this one,
|
||||||
|
/// or zero duration if that instant is later than this one.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Previous Rust versions panicked when `other` was later than `self`. Currently this
|
||||||
|
/// method saturates. Future versions may reintroduce the panic in some circumstances.
|
||||||
|
/// See [Monotonicity].
|
||||||
|
///
|
||||||
|
/// [Monotonicity]: Instant#monotonicity
|
||||||
|
fn sub(self, other: Instant) -> Duration {
|
||||||
|
self.duration_since(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
impl fmt::Debug for Instant {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemTime {
|
||||||
|
/// An anchor in time which can be used to create new `SystemTime` instances or
|
||||||
|
/// learn about where in time a `SystemTime` lies.
|
||||||
|
//
|
||||||
|
// NOTE! this documentation is duplicated, here and in std::time::UNIX_EPOCH.
|
||||||
|
// The two copies are not quite identical, because of the difference in naming.
|
||||||
|
///
|
||||||
|
/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with
|
||||||
|
/// respect to the system clock. Using `duration_since` on an existing
|
||||||
|
/// `SystemTime` instance can tell how far away from this point in time a
|
||||||
|
/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a
|
||||||
|
/// `SystemTime` instance to represent another fixed point in time.
|
||||||
|
///
|
||||||
|
/// `duration_since(UNIX_EPOCH).unwrap().as_secs()` returns
|
||||||
|
/// the number of non-leap seconds since the start of 1970 UTC.
|
||||||
|
/// This is a POSIX `time_t` (as a `u64`),
|
||||||
|
/// and is the same time representation as used in many Internet protocols.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::SystemTime;
|
||||||
|
///
|
||||||
|
/// match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
||||||
|
/// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()),
|
||||||
|
/// Err(_) => panic!("SystemTime before UNIX EPOCH!"),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "assoc_unix_epoch", since = "1.28.0")]
|
||||||
|
pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH;
|
||||||
|
|
||||||
|
/// Represents the maximum value representable by [`SystemTime`] on this platform.
|
||||||
|
///
|
||||||
|
/// This value differs a lot between platforms, but it is always the case
|
||||||
|
/// that any positive addition of a [`Duration`], whose value is greater
|
||||||
|
/// than or equal to the time precision of the operating system, to
|
||||||
|
/// [`SystemTime::MAX`] will fail.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// #![feature(time_systemtime_limits)]
|
||||||
|
/// use std::time::{Duration, SystemTime};
|
||||||
|
///
|
||||||
|
/// // Adding zero will change nothing.
|
||||||
|
/// assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX));
|
||||||
|
///
|
||||||
|
/// // But adding just one second will already fail ...
|
||||||
|
/// //
|
||||||
|
/// // Keep in mind that this in fact may succeed, if the Duration is
|
||||||
|
/// // smaller than the time precision of the operating system, which
|
||||||
|
/// // happens to be 1ns on most operating systems, with Windows being the
|
||||||
|
/// // notable exception by using 100ns, hence why this example uses 1s.
|
||||||
|
/// assert_eq!(SystemTime::MAX.checked_add(Duration::new(1, 0)), None);
|
||||||
|
///
|
||||||
|
/// // Utilize this for saturating arithmetic to improve error handling.
|
||||||
|
/// // In this case, we will use a certificate with a timestamp in the
|
||||||
|
/// // future as a practical example.
|
||||||
|
/// let configured_offset = Duration::from_secs(60 * 60 * 24);
|
||||||
|
/// let valid_after =
|
||||||
|
/// SystemTime::now()
|
||||||
|
/// .checked_add(configured_offset)
|
||||||
|
/// .unwrap_or(SystemTime::MAX);
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
|
||||||
|
pub const MAX: SystemTime = SystemTime(time::SystemTime::MAX);
|
||||||
|
|
||||||
|
/// Represents the minimum value representable by [`SystemTime`] on this platform.
|
||||||
|
///
|
||||||
|
/// This value differs a lot between platforms, but it is always the case
|
||||||
|
/// that any positive subtraction of a [`Duration`] from, whose value is
|
||||||
|
/// greater than or equal to the time precision of the operating system, to
|
||||||
|
/// [`SystemTime::MIN`] will fail.
|
||||||
|
///
|
||||||
|
/// Depending on the platform, this may be either less than or equal to
|
||||||
|
/// [`SystemTime::UNIX_EPOCH`], depending on whether the operating system
|
||||||
|
/// supports the representation of timestamps before the Unix epoch or not.
|
||||||
|
/// However, it is always guaranteed that a [`SystemTime::UNIX_EPOCH`] fits
|
||||||
|
/// between a [`SystemTime::MIN`] and [`SystemTime::MAX`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(time_systemtime_limits)]
|
||||||
|
/// use std::time::{Duration, SystemTime};
|
||||||
|
///
|
||||||
|
/// // Subtracting zero will change nothing.
|
||||||
|
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN));
|
||||||
|
///
|
||||||
|
/// // But subtracting just one second will already fail.
|
||||||
|
/// //
|
||||||
|
/// // Keep in mind that this in fact may succeed, if the Duration is
|
||||||
|
/// // smaller than the time precision of the operating system, which
|
||||||
|
/// // happens to be 1ns on most operating systems, with Windows being the
|
||||||
|
/// // notable exception by using 100ns, hence why this example uses 1s.
|
||||||
|
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::new(1, 0)), None);
|
||||||
|
///
|
||||||
|
/// // Utilize this for saturating arithmetic to improve error handling.
|
||||||
|
/// // In this case, we will use a cache expiry as a practical example.
|
||||||
|
/// let configured_expiry = Duration::from_secs(60 * 3);
|
||||||
|
/// let expiry_threshold =
|
||||||
|
/// SystemTime::now()
|
||||||
|
/// .checked_sub(configured_expiry)
|
||||||
|
/// .unwrap_or(SystemTime::MIN);
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
|
||||||
|
pub const MIN: SystemTime = SystemTime(time::SystemTime::MIN);
|
||||||
|
|
||||||
|
/// Returns the system time corresponding to "now".
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::time::SystemTime;
|
||||||
|
///
|
||||||
|
/// let sys_time = SystemTime::now();
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
pub fn now() -> SystemTime {
|
||||||
|
SystemTime(time::SystemTime::now())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed from an earlier point in time.
|
||||||
|
///
|
||||||
|
/// This function may fail because measurements taken earlier are not
|
||||||
|
/// guaranteed to always be before later measurements (due to anomalies such
|
||||||
|
/// as the system clock being adjusted either forwards or backwards).
|
||||||
|
/// [`Instant`] can be used to measure elapsed time without this risk of failure.
|
||||||
|
///
|
||||||
|
/// If successful, <code>[Ok]\([Duration])</code> is returned where the duration represents
|
||||||
|
/// the amount of time elapsed from the specified measurement to this one.
|
||||||
|
///
|
||||||
|
/// Returns an [`Err`] if `earlier` is later than `self`, and the error
|
||||||
|
/// contains how far from `self` the time is.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::SystemTime;
|
||||||
|
///
|
||||||
|
/// let sys_time = SystemTime::now();
|
||||||
|
/// let new_sys_time = SystemTime::now();
|
||||||
|
/// let difference = new_sys_time.duration_since(sys_time)
|
||||||
|
/// .expect("Clock may have gone backwards");
|
||||||
|
/// println!("{difference:?}");
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
pub fn duration_since(&self, earlier: SystemTime) -> Result<Duration, SystemTimeError> {
|
||||||
|
self.0.sub_time(&earlier.0).map_err(SystemTimeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the difference from this system time to the
|
||||||
|
/// current clock time.
|
||||||
|
///
|
||||||
|
/// This function may fail as the underlying system clock is susceptible to
|
||||||
|
/// drift and updates (e.g., the system clock could go backwards), so this
|
||||||
|
/// function might not always succeed. If successful, <code>[Ok]\([Duration])</code> is
|
||||||
|
/// returned where the duration represents the amount of time elapsed from
|
||||||
|
/// this time measurement to the current time.
|
||||||
|
///
|
||||||
|
/// To measure elapsed time reliably, use [`Instant`] instead.
|
||||||
|
///
|
||||||
|
/// Returns an [`Err`] if `self` is later than the current system time, and
|
||||||
|
/// the error contains how far from the current system time `self` is.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::thread::sleep;
|
||||||
|
/// use std::time::{Duration, SystemTime};
|
||||||
|
///
|
||||||
|
/// let sys_time = SystemTime::now();
|
||||||
|
/// let one_sec = Duration::from_secs(1);
|
||||||
|
/// sleep(one_sec);
|
||||||
|
/// assert!(sys_time.elapsed().unwrap() >= one_sec);
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
pub fn elapsed(&self) -> Result<Duration, SystemTimeError> {
|
||||||
|
SystemTime::now().duration_since(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
|
||||||
|
/// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None`
|
||||||
|
/// otherwise.
|
||||||
|
///
|
||||||
|
/// In the case that the `duration` is smaller than the time precision of the operating
|
||||||
|
/// system, `Some(self)` will be returned.
|
||||||
|
#[stable(feature = "time_checked_add", since = "1.34.0")]
|
||||||
|
pub fn checked_add(&self, duration: Duration) -> Option<SystemTime> {
|
||||||
|
self.0.checked_add_duration(&duration).map(SystemTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
|
||||||
|
/// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None`
|
||||||
|
/// otherwise.
|
||||||
|
///
|
||||||
|
/// In the case that the `duration` is smaller than the time precision of the operating
|
||||||
|
/// system, `Some(self)` will be returned.
|
||||||
|
#[stable(feature = "time_checked_add", since = "1.34.0")]
|
||||||
|
pub fn checked_sub(&self, duration: Duration) -> Option<SystemTime> {
|
||||||
|
self.0.checked_sub_duration(&duration).map(SystemTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saturating [`SystemTime`] addition, computing `self + duration`,
|
||||||
|
/// returning [`SystemTime::MAX`] if overflow occurred.
|
||||||
|
///
|
||||||
|
/// In the case that the `duration` is smaller than the time precision of
|
||||||
|
/// the operating system, `self` will be returned.
|
||||||
|
#[unstable(feature = "time_saturating_systemtime", issue = "151199")]
|
||||||
|
pub fn saturating_add(&self, duration: Duration) -> SystemTime {
|
||||||
|
self.checked_add(duration).unwrap_or(SystemTime::MAX)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saturating [`SystemTime`] subtraction, computing `self - duration`,
|
||||||
|
/// returning [`SystemTime::MIN`] if overflow occurred.
|
||||||
|
///
|
||||||
|
/// In the case that the `duration` is smaller than the time precision of
|
||||||
|
/// the operating system, `self` will be returned.
|
||||||
|
#[unstable(feature = "time_saturating_systemtime", issue = "151199")]
|
||||||
|
pub fn saturating_sub(&self, duration: Duration) -> SystemTime {
|
||||||
|
self.checked_sub(duration).unwrap_or(SystemTime::MIN)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saturating computation of time elapsed from an earlier point in time,
|
||||||
|
/// returning [`Duration::ZERO`] in the case that `earlier` is later or
|
||||||
|
/// equal to `self`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// #![feature(time_saturating_systemtime)]
|
||||||
|
/// use std::time::{Duration, SystemTime};
|
||||||
|
///
|
||||||
|
/// let now = SystemTime::now();
|
||||||
|
/// let prev = now.saturating_sub(Duration::new(1, 0));
|
||||||
|
///
|
||||||
|
/// // now - prev should return non-zero.
|
||||||
|
/// assert_eq!(now.saturating_duration_since(prev), Duration::new(1, 0));
|
||||||
|
/// assert!(now.duration_since(prev).is_ok());
|
||||||
|
///
|
||||||
|
/// // prev - now should return zero (and fail with the non-saturating).
|
||||||
|
/// assert_eq!(prev.saturating_duration_since(now), Duration::ZERO);
|
||||||
|
/// assert!(prev.duration_since(now).is_err());
|
||||||
|
///
|
||||||
|
/// // now - now should return zero (and work with the non-saturating).
|
||||||
|
/// assert_eq!(now.saturating_duration_since(now), Duration::ZERO);
|
||||||
|
/// assert!(now.duration_since(now).is_ok());
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "time_saturating_systemtime", issue = "151199")]
|
||||||
|
pub fn saturating_duration_since(&self, earlier: SystemTime) -> Duration {
|
||||||
|
self.duration_since(earlier).unwrap_or(Duration::ZERO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
impl Add<Duration> for SystemTime {
|
||||||
|
type Output = SystemTime;
|
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function may panic if the resulting point in time cannot be represented by the
|
||||||
|
/// underlying data structure. See [`SystemTime::checked_add`] for a version without panic.
|
||||||
|
fn add(self, dur: Duration) -> SystemTime {
|
||||||
|
self.checked_add(dur).expect("overflow when adding duration to instant")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time_augmented_assignment", since = "1.9.0")]
|
||||||
|
impl AddAssign<Duration> for SystemTime {
|
||||||
|
fn add_assign(&mut self, other: Duration) {
|
||||||
|
*self = *self + other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
impl Sub<Duration> for SystemTime {
|
||||||
|
type Output = SystemTime;
|
||||||
|
|
||||||
|
fn sub(self, dur: Duration) -> SystemTime {
|
||||||
|
self.checked_sub(dur).expect("overflow when subtracting duration from instant")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time_augmented_assignment", since = "1.9.0")]
|
||||||
|
impl SubAssign<Duration> for SystemTime {
|
||||||
|
fn sub_assign(&mut self, other: Duration) {
|
||||||
|
*self = *self - other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
impl fmt::Debug for SystemTime {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An anchor in time which can be used to create new `SystemTime` instances or
|
||||||
|
/// learn about where in time a `SystemTime` lies.
|
||||||
|
//
|
||||||
|
// NOTE! this documentation is duplicated, here and in SystemTime::UNIX_EPOCH.
|
||||||
|
// The two copies are not quite identical, because of the difference in naming.
|
||||||
|
///
|
||||||
|
/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with
|
||||||
|
/// respect to the system clock. Using `duration_since` on an existing
|
||||||
|
/// [`SystemTime`] instance can tell how far away from this point in time a
|
||||||
|
/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a
|
||||||
|
/// [`SystemTime`] instance to represent another fixed point in time.
|
||||||
|
///
|
||||||
|
/// `duration_since(UNIX_EPOCH).unwrap().as_secs()` returns
|
||||||
|
/// the number of non-leap seconds since the start of 1970 UTC.
|
||||||
|
/// This is a POSIX `time_t` (as a `u64`),
|
||||||
|
/// and is the same time representation as used in many Internet protocols.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
///
|
||||||
|
/// match SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||||
|
/// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()),
|
||||||
|
/// Err(_) => panic!("SystemTime before UNIX EPOCH!"),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
pub const UNIX_EPOCH: SystemTime = SystemTime(time::UNIX_EPOCH);
|
||||||
|
|
||||||
|
impl SystemTimeError {
|
||||||
|
/// Returns the positive duration which represents how far forward the
|
||||||
|
/// second system time was from the first.
|
||||||
|
///
|
||||||
|
/// A `SystemTimeError` is returned from the [`SystemTime::duration_since`]
|
||||||
|
/// and [`SystemTime::elapsed`] methods whenever the second system time
|
||||||
|
/// represents a point later in time than the `self` of the method call.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::thread::sleep;
|
||||||
|
/// use std::time::{Duration, SystemTime};
|
||||||
|
///
|
||||||
|
/// let sys_time = SystemTime::now();
|
||||||
|
/// sleep(Duration::from_secs(1));
|
||||||
|
/// let new_sys_time = SystemTime::now();
|
||||||
|
/// match sys_time.duration_since(new_sys_time) {
|
||||||
|
/// Ok(_) => {}
|
||||||
|
/// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
pub fn duration(&self) -> Duration {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
impl Error for SystemTimeError {}
|
||||||
|
|
||||||
|
#[stable(feature = "time2", since = "1.8.0")]
|
||||||
|
impl fmt::Display for SystemTimeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "second time provided was later than self")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromInner<time::SystemTime> for SystemTime {
|
||||||
|
fn from_inner(time: time::SystemTime) -> SystemTime {
|
||||||
|
SystemTime(time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoInner<time::SystemTime> for SystemTime {
|
||||||
|
fn into_inner(self) -> time::SystemTime {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
2
ilm.ld
2
ilm.ld
@@ -5,7 +5,7 @@ OUTPUT_ARCH(riscv)
|
|||||||
ENTRY(_start)
|
ENTRY(_start)
|
||||||
|
|
||||||
MEMORY {
|
MEMORY {
|
||||||
RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 128M
|
RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 512M
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
|
|||||||
66
justfile
66
justfile
@@ -2,6 +2,7 @@ release := ""
|
|||||||
qemu_flags := ""
|
qemu_flags := ""
|
||||||
cargo_flags := "" + if release != "" { "--release" } else { "" }
|
cargo_flags := "" + if release != "" { "--release" } else { "" }
|
||||||
bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" }
|
bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" }
|
||||||
|
rust_src := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src"
|
||||||
|
|
||||||
default: run
|
default: run
|
||||||
|
|
||||||
@@ -12,6 +13,67 @@ mount_filesystem:
|
|||||||
sync_filesystem:
|
sync_filesystem:
|
||||||
sync
|
sync
|
||||||
|
|
||||||
|
cp_std path:
|
||||||
|
@echo "Copying {{ path }}"
|
||||||
|
@mkdir {{ "crates/std/src" / parent_directory(path) }} -p
|
||||||
|
@cp {{ rust_src / path }} {{ "crates/std/src" / path }}
|
||||||
|
@sed -i -f patches.sed {{ "crates/std/src" / path }}
|
||||||
|
|
||||||
|
insert_target line path:
|
||||||
|
@sed -i '{{ line }}a \target_os = "survos",' {{ "crates/std/src" / path }}
|
||||||
|
|
||||||
|
update_std:
|
||||||
|
@# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs, sync.rs
|
||||||
|
# @just cp_std "process.rs"
|
||||||
|
# @just cp_std "fs.rs"
|
||||||
|
@just cp_std "time.rs"
|
||||||
|
@just cp_std "hash/mod.rs"
|
||||||
|
@just cp_std "hash/random.rs"
|
||||||
|
@just cp_std "num/mod.rs"
|
||||||
|
@just cp_std "ffi/c_str.rs"
|
||||||
|
@just cp_std "ffi/mod.rs"
|
||||||
|
@just cp_std "ffi/os_str.rs"
|
||||||
|
@just cp_std "ffi/os_str/tests.rs"
|
||||||
|
@just cp_std "thread/local.rs"
|
||||||
|
@just cp_std "sys/exit.rs"
|
||||||
|
@just cp_std "sys/env_consts.rs"
|
||||||
|
@just cp_std "sys/configure_builtins.rs"
|
||||||
|
@just cp_std "sys/cmath.rs"
|
||||||
|
@just cp_std "sys/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/thread/mod.rs"
|
||||||
|
@just cp_std "sys/thread/unsupported.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/random/mod.rs"
|
||||||
|
@just cp_std "sys/random/unsupported.rs"
|
||||||
|
@just cp_std "sys/env/mod.rs"
|
||||||
|
@just cp_std "sys/env/common.rs"
|
||||||
|
@just cp_std "sys/env/unsupported.rs"
|
||||||
|
@just cp_std "sys/os_str/mod.rs"
|
||||||
|
@just cp_std "sys/os_str/bytes.rs"
|
||||||
|
@just cp_std "sys/os_str/bytes/tests.rs"
|
||||||
|
@just cp_std "sys/path/mod.rs"
|
||||||
|
@just cp_std "sys/fs/mod.rs"
|
||||||
|
@just cp_std "sys/fs/common.rs"
|
||||||
|
@just cp_std "sys/fs/unsupported.rs"
|
||||||
|
@just insert_target 108 "sys/random/mod.rs"
|
||||||
|
@just insert_target 125 "sys/random/mod.rs"
|
||||||
|
@# Copied but edited for the moment
|
||||||
|
# @just cp_std "sys/thread_local/mod.rs"
|
||||||
|
# @just cp_std "sys/alloc/mod.rs"
|
||||||
|
# @just cp_std "alloc.rs"
|
||||||
|
# @just cp_std "path.rs"
|
||||||
|
# @just cp_std "sys/path/unix.rs"
|
||||||
|
|
||||||
build_user_prog prog:
|
build_user_prog prog:
|
||||||
RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tuser.ld -C link-arg=-pie" cargo b {{ cargo_flags }} --package {{ prog }}
|
RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tuser.ld -C link-arg=-pie" cargo b {{ cargo_flags }} --package {{ prog }}
|
||||||
riscv64-elf-strip {{ bin_path / prog }}
|
riscv64-elf-strip {{ bin_path / prog }}
|
||||||
@@ -34,8 +96,8 @@ qemu := f"qemu-system-riscv64 \
|
|||||||
-device bochs-display \
|
-device bochs-display \
|
||||||
-device virtio-keyboard-pci \
|
-device virtio-keyboard-pci \
|
||||||
-device virtio-mouse-pci \
|
-device virtio-mouse-pci \
|
||||||
-device loader,file=disk.img,addr=0x90000000 \
|
-device loader,file=disk.img,addr=0xA0000000 \
|
||||||
-bios none -m 512M {{qemu_flags}}"
|
-bios none -m 1024M {{qemu_flags}}"
|
||||||
|
|
||||||
# -trace \"virtio*\"
|
# -trace \"virtio*\"
|
||||||
# -d guest_errors,unimp,int"
|
# -d guest_errors,unimp,int"
|
||||||
|
|||||||
9
patches.sed
Normal file
9
patches.sed
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
s|crate::bstr::ByteStr|alloc_crate::bstr::ByteStr|g
|
||||||
|
s|crate::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
|
||||||
|
s|crate::sync::Arc|alloc_crate::sync::Arc|g
|
||||||
|
s|alloc::ffi|alloc_crate::ffi|g
|
||||||
|
s|alloc::slice::Join|alloc_crate::slice::Join|g
|
||||||
|
# /target_os = "xous",/a \ target_os = "survos",
|
||||||
|
|
||||||
|
# Ajouter d'autres modifications facilement ici :
|
||||||
|
# s|ancien_texte|nouveau_texte|g
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
"target-endian": "little",
|
"target-endian": "little",
|
||||||
"target-pointer-width": 64,
|
"target-pointer-width": 64,
|
||||||
"arch": "riscv64",
|
"arch": "riscv64",
|
||||||
"os": "none",
|
"os": "survos",
|
||||||
"vendor": "unknown",
|
"vendor": "unknown",
|
||||||
"env": "",
|
"env": "",
|
||||||
"features": "+i,+m,+a,+zicsr",
|
"features": "+i,+m,+a,+zicsr",
|
||||||
|
|||||||
2
src/drivers.rs
Normal file
2
src/drivers.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod keyboard;
|
||||||
|
pub mod mouse;
|
||||||
67
src/drivers/keyboard.rs
Normal file
67
src/drivers/keyboard.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use crate::{
|
||||||
|
keymap::{KeyType, ModifierType, map_keycode},
|
||||||
|
tty::TTY0,
|
||||||
|
virtio::{
|
||||||
|
Virtqueue,
|
||||||
|
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver},
|
||||||
|
},
|
||||||
|
virtual_fs::{FILE_SYSTEM, VirtualFileSystem},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct KeyboardState {
|
||||||
|
// ctrl_modifier: bool,
|
||||||
|
pub shift_modifier: bool,
|
||||||
|
pub alt_gr_modifier: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyboardState {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
// ctrl_modifier: false,
|
||||||
|
shift_modifier: false,
|
||||||
|
alt_gr_modifier: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
||||||
|
pub static mut KBD_DRIVER: VirtioPciDriver<KeyboardState> = unsafe {
|
||||||
|
VirtioPciDriver::new(
|
||||||
|
|state, event| {
|
||||||
|
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
|
||||||
|
kbd_buffer
|
||||||
|
.write(core::mem::transmute::<
|
||||||
|
&VirtioInputEvent,
|
||||||
|
&[u8; size_of::<VirtioInputEvent>()],
|
||||||
|
>(event))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if event.is_key() {
|
||||||
|
let event = event.as_key_event();
|
||||||
|
#[allow(clippy::single_match)]
|
||||||
|
match map_keycode(event.code, state) {
|
||||||
|
KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
let to_send = c.encode_utf8(&mut buf);
|
||||||
|
for c in to_send.as_bytes() {
|
||||||
|
TTY0.buffer.borrow_mut().push(*c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyType::Modifier(ModifierType::Shift) => {
|
||||||
|
state.shift_modifier = !state.shift_modifier;
|
||||||
|
}
|
||||||
|
KeyType::Modifier(ModifierType::AltGr) => {
|
||||||
|
state.alt_gr_modifier = !state.alt_gr_modifier;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
// println!("event: {:#?}", event);
|
||||||
|
} else {
|
||||||
|
// println!("key pressed, {:#?}", event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
KeyboardState::new(),
|
||||||
|
&mut KBD_QUEUE,
|
||||||
|
)
|
||||||
|
};
|
||||||
38
src/drivers/mouse.rs
Normal file
38
src/drivers/mouse.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use crate::{
|
||||||
|
cursor::{clear_cursor, draw_cursor},
|
||||||
|
vga::Vga,
|
||||||
|
virtio::{self, Virtqueue, input::VirtioPciDriver},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static mut MOUSE_POSITION: (u16, u16) = (0, 0);
|
||||||
|
|
||||||
|
static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
||||||
|
pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe {
|
||||||
|
VirtioPciDriver::new(
|
||||||
|
|_, event| {
|
||||||
|
if event.is_relative() {
|
||||||
|
let event = event.as_relative_event();
|
||||||
|
|
||||||
|
clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
|
||||||
|
|
||||||
|
match event.code {
|
||||||
|
virtio::input::EventCodeRelative::X => {
|
||||||
|
MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16
|
||||||
|
}
|
||||||
|
virtio::input::EventCodeRelative::Y => {
|
||||||
|
MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
|
||||||
|
|
||||||
|
// println!("mouse moved relatively, {:#?}", event);
|
||||||
|
} else {
|
||||||
|
// println!("mouse moved, {:#?}", event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
&mut MOUSE_QUEUE,
|
||||||
|
)
|
||||||
|
};
|
||||||
@@ -12,7 +12,7 @@ use io::{IoBase, Read, Seek, Write};
|
|||||||
|
|
||||||
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
|
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
|
||||||
|
|
||||||
const DISK_ADDR: *const u8 = 0x9000_0000 as *const _;
|
const DISK_ADDR: *const u8 = 0xA000_0000 as *const _;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Simple disk backend that reads from a fixed memory region.
|
/// Simple disk backend that reads from a fixed memory region.
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ use log::info;
|
|||||||
use shared::syscall::SysCall;
|
use shared::syscall::SysCall;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
KBD_DRIVER, MOUSE_DRIVER,
|
|
||||||
boot::sbi::{ExtensionID, TimerFunctionID},
|
boot::sbi::{ExtensionID, TimerFunctionID},
|
||||||
clear_csr,
|
clear_csr,
|
||||||
|
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
|
||||||
process::{ExecutionContext, exit_process, sleep},
|
process::{ExecutionContext, exit_process, sleep},
|
||||||
read_csr,
|
read_csr,
|
||||||
riscv::{disable_interrupt, dump_cpu},
|
riscv::{disable_interrupt, dump_cpu},
|
||||||
@@ -160,6 +160,15 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
|
|
||||||
unsafe { (*interrupt_state).a[0] = fd as u64 };
|
unsafe { (*interrupt_state).a[0] = fd as u64 };
|
||||||
}
|
}
|
||||||
|
SysCall::Close => {
|
||||||
|
let fd = a1;
|
||||||
|
|
||||||
|
let mut scheduler = SCHEDULER.lock();
|
||||||
|
let current_process = scheduler.get_current_process();
|
||||||
|
let mut vnode = current_process.fd_table[fd as usize].take().unwrap();
|
||||||
|
|
||||||
|
vnode.close();
|
||||||
|
}
|
||||||
SysCall::Write => {
|
SysCall::Write => {
|
||||||
let fd = a1;
|
let fd = a1;
|
||||||
let buf =
|
let buf =
|
||||||
@@ -168,7 +177,8 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
let mut scheduler = SCHEDULER.lock();
|
let mut scheduler = SCHEDULER.lock();
|
||||||
let current_process = scheduler.get_current_process();
|
let current_process = scheduler.get_current_process();
|
||||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||||
vnode.write(buf).unwrap();
|
let res = vnode.write(buf).unwrap();
|
||||||
|
unsafe { (*interrupt_state).a[0] = res as u64 };
|
||||||
}
|
}
|
||||||
SysCall::Read => {
|
SysCall::Read => {
|
||||||
let fd = a1;
|
let fd = a1;
|
||||||
@@ -182,6 +192,8 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
if res == 0 && !buf.is_empty() {
|
if res == 0 && !buf.is_empty() {
|
||||||
loop_syscall(interrupt_state);
|
loop_syscall(interrupt_state);
|
||||||
scheduler.schedule(&mut interrupt_state);
|
scheduler.schedule(&mut interrupt_state);
|
||||||
|
} else {
|
||||||
|
unsafe { (*interrupt_state).a[0] = res as u64 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SysCall::Seek => {
|
SysCall::Seek => {
|
||||||
@@ -197,6 +209,17 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||||
vnode.seek(seek).unwrap();
|
vnode.seek(seek).unwrap();
|
||||||
}
|
}
|
||||||
|
SysCall::Spawn => {
|
||||||
|
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
|
||||||
|
let mut scheduler = SCHEDULER.lock();
|
||||||
|
scheduler.create_process_from_file(path, 0, core::ptr::null());
|
||||||
|
}
|
||||||
|
SysCall::ExecVE => {
|
||||||
|
// let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
|
||||||
|
// let mut scheduler = SCHEDULER.lock();
|
||||||
|
// scheduler.create_process_from_file(path, &[]);
|
||||||
|
unimplemented!("ExecVE is not implemented")
|
||||||
|
}
|
||||||
SysCall::Alloc => {
|
SysCall::Alloc => {
|
||||||
let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap();
|
let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap();
|
||||||
// Allocate memory and put the pointer in a0
|
// Allocate memory and put the pointer in a0
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::KeyboardState;
|
use crate::drivers::keyboard::KeyboardState;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[derive_const(PartialEq, Eq)]
|
#[derive_const(PartialEq, Eq)]
|
||||||
|
|||||||
125
src/main.rs
125
src/main.rs
@@ -12,7 +12,8 @@
|
|||||||
arbitrary_self_types_pointers,
|
arbitrary_self_types_pointers,
|
||||||
derive_const,
|
derive_const,
|
||||||
const_cmp,
|
const_cmp,
|
||||||
const_trait_impl
|
const_trait_impl,
|
||||||
|
trait_alias
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use core::sync::atomic::AtomicBool;
|
use core::sync::atomic::AtomicBool;
|
||||||
@@ -22,20 +23,15 @@ use embedded_alloc::LlffHeap as Heap;
|
|||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cursor::{clear_cursor, draw_cursor},
|
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
|
||||||
io::init_log,
|
io::init_log,
|
||||||
keymap::{KeyType, ModifierType, map_keycode},
|
pci::scan_virtio_devices,
|
||||||
pci::{PciDeviceIterator, scan_virtio_devices},
|
|
||||||
riscv::enable_supervisor_interrupt,
|
riscv::enable_supervisor_interrupt,
|
||||||
scheduler::{SCHEDULER, idle},
|
scheduler::{SCHEDULER, idle},
|
||||||
tty::TTY0,
|
|
||||||
user::{proc2, test},
|
user::{proc2, test},
|
||||||
vga::Vga,
|
vga::Vga,
|
||||||
virtio::{
|
virtio::input::init_plic_pci,
|
||||||
Virtqueue,
|
virtual_fs::init_file_system,
|
||||||
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver, init_plic_pci},
|
|
||||||
},
|
|
||||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, init_file_system},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@@ -44,6 +40,7 @@ mod critical_section;
|
|||||||
mod cursor;
|
mod cursor;
|
||||||
mod data_structures;
|
mod data_structures;
|
||||||
mod draw;
|
mod draw;
|
||||||
|
mod drivers;
|
||||||
mod fs;
|
mod fs;
|
||||||
mod interrupt;
|
mod interrupt;
|
||||||
mod io;
|
mod io;
|
||||||
@@ -75,97 +72,6 @@ const _: () = assert!(core::mem::size_of::<usize>() == core::mem::size_of::<u64>
|
|||||||
#[cfg(not(target_endian = "little"))]
|
#[cfg(not(target_endian = "little"))]
|
||||||
compile_error! {"This kernel implementation assume endianness is little-endian. Some memory access like PCI could not work in big-endian."}
|
compile_error! {"This kernel implementation assume endianness is little-endian. Some memory access like PCI could not work in big-endian."}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
pub struct KeyboardState {
|
|
||||||
// ctrl_modifier: bool,
|
|
||||||
pub shift_modifier: bool,
|
|
||||||
pub alt_gr_modifier: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyboardState {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
// ctrl_modifier: false,
|
|
||||||
shift_modifier: false,
|
|
||||||
alt_gr_modifier: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
|
||||||
pub static mut KBD_DRIVER: VirtioPciDriver<KeyboardState> = unsafe {
|
|
||||||
VirtioPciDriver::new(
|
|
||||||
|state, event| {
|
|
||||||
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
|
|
||||||
kbd_buffer
|
|
||||||
.write(core::mem::transmute::<
|
|
||||||
&VirtioInputEvent,
|
|
||||||
&[u8; size_of::<VirtioInputEvent>()],
|
|
||||||
>(event))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if event.is_key() {
|
|
||||||
let event = event.as_key_event();
|
|
||||||
#[allow(clippy::single_match)]
|
|
||||||
match map_keycode(event.code, state) {
|
|
||||||
KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => {
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
let to_send = c.encode_utf8(&mut buf);
|
|
||||||
for c in to_send.as_bytes() {
|
|
||||||
TTY0.buffer.borrow_mut().push(*c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
KeyType::Modifier(ModifierType::Shift) => {
|
|
||||||
state.shift_modifier = !state.shift_modifier;
|
|
||||||
}
|
|
||||||
KeyType::Modifier(ModifierType::AltGr) => {
|
|
||||||
state.alt_gr_modifier = !state.alt_gr_modifier;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
println!("event: {:#?}", event);
|
|
||||||
} else {
|
|
||||||
// println!("key pressed, {:#?}", event);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
KeyboardState::new(),
|
|
||||||
&mut KBD_QUEUE,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
pub static mut MOUSE_POSITION: (u16, u16) = (0, 0);
|
|
||||||
|
|
||||||
static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
|
||||||
pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe {
|
|
||||||
VirtioPciDriver::new(
|
|
||||||
|_, event| {
|
|
||||||
if event.is_relative() {
|
|
||||||
let event = event.as_relative_event();
|
|
||||||
|
|
||||||
clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
|
|
||||||
|
|
||||||
match event.code {
|
|
||||||
virtio::input::EventCodeRelative::X => {
|
|
||||||
MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16
|
|
||||||
}
|
|
||||||
virtio::input::EventCodeRelative::Y => {
|
|
||||||
MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
|
|
||||||
|
|
||||||
// println!("mouse moved relatively, {:#?}", event);
|
|
||||||
} else {
|
|
||||||
// println!("mouse moved, {:#?}", event);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
&mut MOUSE_QUEUE,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn supervisor_mode_entry() {
|
pub extern "C" fn supervisor_mode_entry() {
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -180,19 +86,20 @@ pub extern "C" fn supervisor_mode_entry() {
|
|||||||
info!("Hello World !");
|
info!("Hello World !");
|
||||||
// unsafe { Vga.draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
|
// unsafe { Vga.draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
|
||||||
|
|
||||||
SCHEDULER.lock().create_process(Box::new(test), "proc1");
|
// let binding = Box::leak(Box::new(["coucou".as_bytes()]));
|
||||||
SCHEDULER.lock().create_process(Box::new(proc2), "proc2");
|
SCHEDULER
|
||||||
|
.lock()
|
||||||
|
.create_process(Box::new(test), "proc1", 0, core::ptr::null());
|
||||||
|
SCHEDULER
|
||||||
|
.lock()
|
||||||
|
.create_process(Box::new(proc2), "proc2", 0, core::ptr::null());
|
||||||
|
|
||||||
SCHEDULER
|
SCHEDULER
|
||||||
.lock()
|
.lock()
|
||||||
.create_process_from_file("/usr/bin/test_pic");
|
.create_process_from_file("/usr/bin/test_pic", 0, core::ptr::null());
|
||||||
|
|
||||||
enable_supervisor_interrupt();
|
enable_supervisor_interrupt();
|
||||||
|
|
||||||
for pci in PciDeviceIterator::new() {
|
|
||||||
println!("{:x?}", pci.vendor_and_device_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let (pci_keyboard, pci_mouse) = scan_virtio_devices();
|
let (pci_keyboard, pci_mouse) = scan_virtio_devices();
|
||||||
let (pci_keyboard, pci_mouse) = (pci_keyboard.unwrap(), pci_mouse.unwrap());
|
let (pci_keyboard, pci_mouse) = (pci_keyboard.unwrap(), pci_mouse.unwrap());
|
||||||
@@ -214,5 +121,5 @@ pub extern "C" fn supervisor_mode_entry() {
|
|||||||
init_plic_pci(pci_keyboard.irq);
|
init_plic_pci(pci_keyboard.irq);
|
||||||
init_plic_pci(pci_mouse.irq);
|
init_plic_pci(pci_mouse.irq);
|
||||||
}
|
}
|
||||||
idle();
|
idle(0, core::ptr::null());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ use crate::{
|
|||||||
riscv::SStatus,
|
riscv::SStatus,
|
||||||
scheduler::{ACTIVE_PID, SCHEDULER, Scheduler},
|
scheduler::{ACTIVE_PID, SCHEDULER, Scheduler},
|
||||||
time::elapsed_time_since_startup,
|
time::elapsed_time_since_startup,
|
||||||
tty::TTY0,
|
|
||||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode},
|
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,6 +65,9 @@ pub struct ExecutionContext {
|
|||||||
pub mstatus: u64,
|
pub mstatus: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ProcessEntryTrait = Fn(isize, *const *const u8);
|
||||||
|
pub type ProcessEntry = dyn ProcessEntryTrait;
|
||||||
|
|
||||||
/// Represents a process in the system.
|
/// Represents a process in the system.
|
||||||
///
|
///
|
||||||
/// Each process has its own execution context, stack,
|
/// Each process has its own execution context, stack,
|
||||||
@@ -78,13 +80,13 @@ pub struct Process {
|
|||||||
/// Current state of the process.
|
/// Current state of the process.
|
||||||
pub state: ProcessState,
|
pub state: ProcessState,
|
||||||
/// Optional entry point for the process code.
|
/// Optional entry point for the process code.
|
||||||
pub entry: Option<Box<dyn Fn()>>,
|
pub entry: Option<Box<ProcessEntry>>,
|
||||||
/// Wake time for sleeping processes.
|
/// Wake time for sleeping processes.
|
||||||
pub wake_time: Duration,
|
pub wake_time: Duration,
|
||||||
/// Saved execution context.
|
/// Saved execution context.
|
||||||
pub ctx: ExecutionContext,
|
pub ctx: ExecutionContext,
|
||||||
/// Process stack.
|
/// Process stack.
|
||||||
pub stack: [u64; STACK_SIZE],
|
pub stack: Box<[u64; STACK_SIZE]>,
|
||||||
/// File descriptor table.
|
/// File descriptor table.
|
||||||
pub fd_table: Vec<Option<Box<dyn VirtualNode>>>,
|
pub fd_table: Vec<Option<Box<dyn VirtualNode>>>,
|
||||||
}
|
}
|
||||||
@@ -109,7 +111,7 @@ impl Default for Process {
|
|||||||
mepc: core::ptr::null(),
|
mepc: core::ptr::null(),
|
||||||
mstatus: 0,
|
mstatus: 0,
|
||||||
},
|
},
|
||||||
stack: [0; _],
|
stack: unsafe { Box::new_zeroed().assume_init() },
|
||||||
entry: None,
|
entry: None,
|
||||||
fd_table: Vec::new(),
|
fd_table: Vec::new(),
|
||||||
}
|
}
|
||||||
@@ -150,7 +152,12 @@ impl Scheduler {
|
|||||||
/// Attempts to open `path`, load its contents into memory and create a new
|
/// Attempts to open `path`, load its contents into memory and create a new
|
||||||
/// kernel process that will execute the loaded binary. Returns the PID of the
|
/// kernel process that will execute the loaded binary. Returns the PID of the
|
||||||
/// created process, or -1 on failure.
|
/// created process, or -1 on failure.
|
||||||
pub fn create_process_from_file<T: AsRef<Path>>(&mut self, path: T) -> i64 {
|
pub fn create_process_from_file<T: AsRef<Path>>(
|
||||||
|
&mut self,
|
||||||
|
path: T,
|
||||||
|
argc: isize,
|
||||||
|
argv: *const *const u8,
|
||||||
|
) -> i64 {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let name = path.as_str();
|
let name = path.as_str();
|
||||||
|
|
||||||
@@ -231,25 +238,30 @@ impl Scheduler {
|
|||||||
// Entry point
|
// Entry point
|
||||||
let entry_va = gelf.entry;
|
let entry_va = gelf.entry;
|
||||||
let entry_addr = unsafe { base.add((entry_va - min_vaddr) as usize) } as *const u8;
|
let entry_addr = unsafe { base.add((entry_va - min_vaddr) as usize) } as *const u8;
|
||||||
let entry_fn =
|
let entry_fn = unsafe {
|
||||||
unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(entry_addr) };
|
core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>(
|
||||||
let wrapper = Box::new(move || {
|
entry_addr,
|
||||||
entry_fn();
|
)
|
||||||
});
|
};
|
||||||
|
let wrapper = move |argc, argv| {
|
||||||
|
entry_fn(argc, argv);
|
||||||
|
};
|
||||||
println!("Program loaded at : {:x?}", entry_addr);
|
println!("Program loaded at : {:x?}", entry_addr);
|
||||||
return self.create_process(wrapper, name);
|
return self.create_process(wrapper, name, argc, argv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: treat the file as a raw binary blob and execute in-place
|
// Fallback: treat the file as a raw binary blob and execute in-place
|
||||||
let entry_point = unsafe {
|
let entry_point = unsafe {
|
||||||
core::mem::transmute::<*const u8, extern "C" fn()>(Vec::leak(content).as_ptr())
|
core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>(
|
||||||
|
Vec::leak(content).as_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let wrapper = move |argc, argv| {
|
||||||
|
entry_point(argc, argv);
|
||||||
};
|
};
|
||||||
let wrapper = Box::new(move || {
|
|
||||||
entry_point();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.create_process(wrapper, name)
|
self.create_process(wrapper, name, argc, argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new process with the specified code and name.
|
/// Creates a new process with the specified code and name.
|
||||||
@@ -271,39 +283,48 @@ impl Scheduler {
|
|||||||
///
|
///
|
||||||
/// The provided `code` function will be executed when the process is first
|
/// The provided `code` function will be executed when the process is first
|
||||||
/// scheduled. Returns the new PID, or -1 if the process table is full.
|
/// scheduled. Returns the new PID, or -1 if the process table is full.
|
||||||
pub fn create_process<T: Into<String>, F: Fn() + 'static + Send>(
|
pub fn create_process<T: Into<String>, F: ProcessEntryTrait + 'static + Send>(
|
||||||
&mut self,
|
&mut self,
|
||||||
code: Box<F>,
|
code: F,
|
||||||
name: T,
|
name: T,
|
||||||
|
argc: isize,
|
||||||
|
argv: *const *const u8,
|
||||||
) -> i64 {
|
) -> i64 {
|
||||||
// SAFETY: Initializing process in the global table.
|
// SAFETY: Initializing process in the global table.
|
||||||
// Access is safe because we verified bounds and found a Dead slot.
|
// Access is safe because we verified bounds and found a Dead slot.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.process_table
|
self.process_table.insert(self.next_pid, Process::default());
|
||||||
.insert(self.next_pid, Box::new(Process::default()));
|
|
||||||
let process = self.process_table.get_mut(&self.next_pid).unwrap();
|
let process = self.process_table.get_mut(&self.next_pid).unwrap();
|
||||||
|
|
||||||
// Configure process metadata
|
// Configure process metadata
|
||||||
process.pid = self.next_pid as i64;
|
process.pid = self.next_pid as i64;
|
||||||
process.name = name.into();
|
process.name = name.into();
|
||||||
process.state = ProcessState::Activable;
|
process.state = ProcessState::Activable;
|
||||||
process.entry = Some(code);
|
process.entry = Some(Box::new(code));
|
||||||
|
|
||||||
process.fd_table = Vec::new();
|
process.fd_table = Vec::new();
|
||||||
FILE_SYSTEM.mount(
|
// FILE_SYSTEM.mount(
|
||||||
format!("/proc/{}/0", process.pid).into(),
|
// format!("/proc/{}/0", process.pid).into(),
|
||||||
Box::new(TTY0.clone()),
|
// Box::new(TTY0.clone()),
|
||||||
);
|
// );
|
||||||
// FD 0
|
// FD 0
|
||||||
process.fd_table.push(Some(
|
process
|
||||||
FILE_SYSTEM
|
.fd_table
|
||||||
.open(format!("/proc/{}/0", process.pid).as_ref())
|
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
|
||||||
.unwrap(),
|
// FD 1
|
||||||
));
|
process
|
||||||
|
.fd_table
|
||||||
|
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
|
||||||
|
// FD 2
|
||||||
|
process
|
||||||
|
.fd_table
|
||||||
|
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
|
||||||
|
|
||||||
// Configure execution context
|
// Configure execution context
|
||||||
// a0 contains the pointer to the function to execute
|
// a0 contains the pointer to the function to execute
|
||||||
process.ctx.a[0] = &raw const *process.entry.as_ref().unwrap_unchecked() as u64;
|
process.ctx.a[0] = &raw const *process.entry.as_ref().unwrap_unchecked() as u64;
|
||||||
|
process.ctx.a[1] = argc as u64;
|
||||||
|
process.ctx.a[2] = argv as u64;
|
||||||
|
|
||||||
// mepc points to process_launcher which will call the function
|
// mepc points to process_launcher which will call the function
|
||||||
process.ctx.mepc = process_launcher as *const _;
|
process.ctx.mepc = process_launcher as *const _;
|
||||||
@@ -338,10 +359,15 @@ impl Scheduler {
|
|||||||
/// This function is installed into the process `mepc` so that when the new
|
/// This function is installed into the process `mepc` so that when the new
|
||||||
/// process is scheduled it will run this launcher which calls the user
|
/// process is scheduled it will run this launcher which calls the user
|
||||||
/// function and ensures the process exits cleanly.
|
/// function and ensures the process exits cleanly.
|
||||||
extern "C" fn process_launcher(code: *const Box<dyn Fn()>) {
|
#[allow(improper_ctypes_definitions)]
|
||||||
|
extern "C" fn process_launcher(
|
||||||
|
code: *const Box<ProcessEntry>,
|
||||||
|
argc: isize,
|
||||||
|
argv: *const *const u8,
|
||||||
|
) {
|
||||||
// SAFETY: The code pointer was initialized in create_process
|
// SAFETY: The code pointer was initialized in create_process
|
||||||
// and points to a valid function.
|
// and points to a valid function.
|
||||||
unsafe { (*code)() };
|
unsafe { (*code)(argc, argv) };
|
||||||
|
|
||||||
// If user code didn't exit explicitly, call exit() to clean up the process
|
// If user code didn't exit explicitly, call exit() to clean up the process
|
||||||
exit();
|
exit();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::{
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Scheduler {
|
pub struct Scheduler {
|
||||||
pub next_pid: u64,
|
pub next_pid: u64,
|
||||||
pub process_table: BTreeMap<u64, Box<Process>>,
|
pub process_table: BTreeMap<u64, Process>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static ACTIVE_PID: AtomicU64 = AtomicU64::new(0);
|
pub static ACTIVE_PID: AtomicU64 = AtomicU64::new(0);
|
||||||
@@ -30,7 +30,7 @@ pub static SCHEDULER: Mutex<LazyCell<Scheduler>> = Mutex::new(LazyCell::new(|| S
|
|||||||
/// Idle loop executed when there is no runnable process.
|
/// Idle loop executed when there is no runnable process.
|
||||||
///
|
///
|
||||||
/// Uses the `wfi` instruction to yield the CPU while waiting for interrupts.
|
/// Uses the `wfi` instruction to yield the CPU while waiting for interrupts.
|
||||||
pub fn idle() {
|
pub fn idle(_argc: isize, _argv: *const *const u8) {
|
||||||
loop {
|
loop {
|
||||||
// write_string_temp("idle");
|
// write_string_temp("idle");
|
||||||
// info!("idle");
|
// info!("idle");
|
||||||
@@ -47,7 +47,7 @@ impl Scheduler {
|
|||||||
/// it as the active process.
|
/// it as the active process.
|
||||||
pub fn init(&mut self) {
|
pub fn init(&mut self) {
|
||||||
info!("scheduler init");
|
info!("scheduler init");
|
||||||
self.create_process(Box::new(idle), "idle");
|
self.create_process(Box::new(idle), "idle", 0, core::ptr::null());
|
||||||
self.process_table.get_mut(&0).unwrap().state = ProcessState::Active;
|
self.process_table.get_mut(&0).unwrap().state = ProcessState::Active;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,9 +69,7 @@ impl Scheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut current_process_iter = self
|
let mut current_process_iter = self.process_table.range_mut(prev_pid + 1..);
|
||||||
.process_table
|
|
||||||
.range_mut((Bound::Excluded(prev_pid), Bound::Unbounded));
|
|
||||||
|
|
||||||
ACTIVE_PID.store(
|
ACTIVE_PID.store(
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ use core::time::Duration;
|
|||||||
|
|
||||||
use shared::syscall::{sleep, write_string_temp};
|
use shared::syscall::{sleep, write_string_temp};
|
||||||
|
|
||||||
pub fn test() {
|
pub fn test(_argc: isize, _argv: *const *const u8) {
|
||||||
loop {
|
loop {
|
||||||
|
// write_string_temp(str::from_utf8(_args[0]).unwrap());
|
||||||
write_string_temp("test");
|
write_string_temp("test");
|
||||||
sleep(Duration::new(2, 0));
|
sleep(Duration::new(2, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn proc2() {
|
pub fn proc2(_argc: isize, _argv: *const *const u8) {
|
||||||
loop {
|
loop {
|
||||||
write_string_temp("proc2");
|
write_string_temp("proc2");
|
||||||
sleep(Duration::new(3, 0));
|
sleep(Duration::new(3, 0));
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ pub struct VirtioPciDriver<S, F: Fn(&mut S, &VirtioInputEvent) = fn(&mut S, &Vir
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
|
#[allow(unused)]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
Sync = 0,
|
Sync = 0,
|
||||||
@@ -31,6 +32,7 @@ pub enum EventType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
|
#[allow(unused)]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum EventCodeRelative {
|
pub enum EventCodeRelative {
|
||||||
X = 0,
|
X = 0,
|
||||||
@@ -39,6 +41,7 @@ pub enum EventCodeRelative {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
|
#[allow(unused)]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum EventCodeValue {
|
pub enum EventCodeValue {
|
||||||
Released = 0,
|
Released = 0,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use io::SeekFrom;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
draw::{Color, Draw, FONT_HEIGHT, FONT_WIDTH},
|
draw::{Color, Draw, FONT_HEIGHT, FONT_WIDTH},
|
||||||
println,
|
|
||||||
vga::{self, Vga},
|
vga::{self, Vga},
|
||||||
virtual_fs::{self, FILE_SYSTEM, VirtualFileSystem},
|
virtual_fs::{self, FILE_SYSTEM, VirtualFileSystem},
|
||||||
};
|
};
|
||||||
@@ -41,7 +40,6 @@ impl VirtualConsole {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_char(&mut self, c: char) {
|
pub fn write_char(&mut self, c: char) {
|
||||||
println!("char_console: {:?}", c as u64);
|
|
||||||
let mut last_cursor = self.cursor;
|
let mut last_cursor = self.cursor;
|
||||||
match c {
|
match c {
|
||||||
'\n' => {
|
'\n' => {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use hashbrown::HashMap;
|
|||||||
use io::{IoBase, Read, Seek, Write};
|
use io::{IoBase, Read, Seek, Write};
|
||||||
|
|
||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
|
pub mod null;
|
||||||
pub mod stdin;
|
pub mod stdin;
|
||||||
pub mod virtual_stdin;
|
pub mod virtual_stdin;
|
||||||
|
|
||||||
@@ -16,10 +17,12 @@ use crate::{
|
|||||||
fs::Disk,
|
fs::Disk,
|
||||||
tty::TTY0,
|
tty::TTY0,
|
||||||
vga::Vga,
|
vga::Vga,
|
||||||
virtual_fs::{keyboard::KeyboardBuffer, virtual_stdin::VirtualStdin},
|
virtual_fs::{keyboard::KeyboardBuffer, null::Null, virtual_stdin::VirtualStdin},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {}
|
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {
|
||||||
|
fn close(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait VirtualFileSystem: Debug {
|
pub trait VirtualFileSystem: Debug {
|
||||||
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, ()>;
|
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, ()>;
|
||||||
@@ -62,6 +65,7 @@ pub unsafe fn init_file_system() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
FILE_SYSTEM.mount("/dev/fb0".into(), Box::new(VGAFileSystem));
|
FILE_SYSTEM.mount("/dev/fb0".into(), Box::new(VGAFileSystem));
|
||||||
FILE_SYSTEM.mount("/dev/tty0".into(), Box::new(TTY0.clone()));
|
FILE_SYSTEM.mount("/dev/tty0".into(), Box::new(TTY0.clone()));
|
||||||
|
FILE_SYSTEM.mount("/dev/null".into(), Box::new(Null));
|
||||||
FILE_SYSTEM.mount(
|
FILE_SYSTEM.mount(
|
||||||
"/dev/input/keyboard".into(),
|
"/dev/input/keyboard".into(),
|
||||||
Box::new(KeyboardBuffer::new()),
|
Box::new(KeyboardBuffer::new()),
|
||||||
|
|||||||
51
src/virtual_fs/null.rs
Normal file
51
src/virtual_fs/null.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use alloc::boxed::Box;
|
||||||
|
use io::{IoBase, Read, Seek, Write};
|
||||||
|
|
||||||
|
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Null;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NullNode;
|
||||||
|
|
||||||
|
impl VirtualFileSystem for Null {
|
||||||
|
fn open(
|
||||||
|
&mut self,
|
||||||
|
path: &bffs::path::Path,
|
||||||
|
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, ()> {
|
||||||
|
if !path.is_empty() {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(Box::new(NullNode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoBase for NullNode {
|
||||||
|
type Error = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for NullNode {
|
||||||
|
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Seek for NullNode {
|
||||||
|
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for NullNode {
|
||||||
|
fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtualNode for NullNode {}
|
||||||
7
user/shell/Cargo.toml
Normal file
7
user/shell/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "shell"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
std = { path = "../../crates/std" }
|
||||||
8
user/shell/src/main.rs
Normal file
8
user/shell/src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#![feature(custom_std)]
|
||||||
|
|
||||||
|
fn main() -> isize {
|
||||||
|
println!(
|
||||||
|
"Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! "
|
||||||
|
);
|
||||||
|
0
|
||||||
|
}
|
||||||
@@ -4,4 +4,4 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
os-std = { path = "../../crates/os-std" }
|
std = { path = "../../crates/std" }
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#![no_std]
|
#![feature(custom_std)]
|
||||||
#![no_main]
|
#![allow(unused)]
|
||||||
|
|
||||||
// use core::time::Duration;
|
use std::{
|
||||||
|
io::{Read, Write, stdin},
|
||||||
use os_std::syscall;
|
syscall,
|
||||||
os_std::custom_std_setup! {}
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// let mut input = String::new();
|
// let mut input = String::new();
|
||||||
@@ -12,19 +12,13 @@ fn main() {
|
|||||||
// syscall::seek(&mut file, SeekFrom::End(-3));
|
// syscall::seek(&mut file, SeekFrom::End(-3));
|
||||||
// syscall::write(&mut file, &[255; 6400 * 50]);
|
// syscall::write(&mut file, &[255; 6400 * 50]);
|
||||||
// syscall::sleep(Duration::from_secs_f64(2.0));
|
// syscall::sleep(Duration::from_secs_f64(2.0));
|
||||||
let mut stdin = syscall::open("/dev/tty0");
|
syscall::close(0);
|
||||||
let mut file = syscall::open("/dev/tty0");
|
let mut tty = syscall::open("/dev/tty0");
|
||||||
|
syscall::spawn("/usr/bin/shell");
|
||||||
loop {
|
loop {
|
||||||
let mut test = [0; 2];
|
let mut test = [0; 2];
|
||||||
syscall::read(&mut stdin, &mut test);
|
let len = stdin().read(&mut test).unwrap();
|
||||||
let len = *test.iter().find(|x| **x == 0).unwrap_or(&1) + 1;
|
tty.write_all(str::from_utf8(&test[..len as usize]).unwrap().as_bytes())
|
||||||
syscall::write(
|
.unwrap();
|
||||||
&mut file,
|
|
||||||
str::from_utf8(&test[..len as usize]).unwrap().as_bytes(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// println!(
|
|
||||||
// "Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! {:?}",
|
|
||||||
// str::from_utf8(&test)
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user