Compare commits
5 Commits
51780b3a78
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ae0593c972 | |||
| f966a1239e | |||
| 15ecefb5fb | |||
| 897775f63a | |||
| fadecc7c95 |
@@ -3,8 +3,6 @@ target = "riscv64.json"
|
|||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
json-target-spec = true
|
json-target-spec = true
|
||||||
build-std = ["core", "compiler_builtins", "alloc"]
|
|
||||||
build-std-features = ["compiler-builtins-mem"]
|
|
||||||
|
|
||||||
[target.riscv64]
|
[target.riscv64]
|
||||||
rustflags = [
|
rustflags = [
|
||||||
|
|||||||
8
.gdbinit
8
.gdbinit
@@ -1,6 +1,6 @@
|
|||||||
file target/riscv64/debug/kernel-rust
|
# file target/riscv64/debug/kernel-rust
|
||||||
target remote localhost:1234
|
target remote localhost:1234
|
||||||
break machine_mode_entry
|
# break machine_mode_entry
|
||||||
# break *0x800bf000
|
break *0x800cfdd8
|
||||||
# add-symbol-file target/riscv64/debug/test_pic 0x800bf000
|
add-symbol-file target/riscv64/debug/agetty 0x800cfdd8
|
||||||
c
|
c
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,3 +7,6 @@
|
|||||||
disk.img
|
disk.img
|
||||||
**/*.mem
|
**/*.mem
|
||||||
mnt
|
mnt
|
||||||
|
|
||||||
|
sysroot/lib/rustlib/riscv64
|
||||||
|
library/alloc
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/bytes-struct","crates/io","crates/std", "crates/shared", "user/*"]
|
members = ["crates/ansii","user/*"]
|
||||||
|
exclude = ["library", "build-tools", "crates/io"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "kernel-rust"
|
name = "kernel-rust"
|
||||||
@@ -12,10 +13,11 @@ bitflags = "2"
|
|||||||
embedded-alloc = "0.7"
|
embedded-alloc = "0.7"
|
||||||
kernel-macros = { path = "crates/kernel-macros" }
|
kernel-macros = { path = "crates/kernel-macros" }
|
||||||
bytes-struct = { path = "crates/bytes-struct" }
|
bytes-struct = { path = "crates/bytes-struct" }
|
||||||
|
ansii = { path = "crates/ansii" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
critical-section = { version = "1", features = ["restore-state-bool"] }
|
critical-section = { version = "1", features = ["restore-state-bool"] }
|
||||||
bffs = { path = "crates/bffs", features = ["alloc"] }
|
bffs = { path = "crates/bffs", features = ["alloc"] }
|
||||||
io = { path = "crates/io", features = ["alloc"] }
|
io = { package = "no-std-io", path = "crates/io" }
|
||||||
shared = { path = "crates/shared", features = ["kernel"] }
|
shared = { path = "crates/shared", features = ["kernel"] }
|
||||||
goblin = { version = "0.7", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
|
goblin = { version = "0.7", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
|
||||||
hashbrown = "0.16"
|
hashbrown = "0.16"
|
||||||
|
|||||||
2
build-tools/.cargo/config.toml
Normal file
2
build-tools/.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[build]
|
||||||
|
target = "x86_64-unknown-linux-gnu"
|
||||||
14
build-tools/Cargo.toml
Normal file
14
build-tools/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "build-tools"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "gen-symbols"
|
||||||
|
path = "src/gen_symbols.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rayon = "1.11"
|
||||||
|
object = "0.32"
|
||||||
|
addr2line = "0.21"
|
||||||
|
rustc-demangle = "0.1"
|
||||||
107
build-tools/src/gen_symbols.rs
Normal file
107
build-tools/src/gen_symbols.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
use addr2line::Context;
|
||||||
|
use object::{Object, ObjectSymbol, SymbolKind};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufWriter, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RawSymbol {
|
||||||
|
addr: u64,
|
||||||
|
line: u32,
|
||||||
|
name_off: u32,
|
||||||
|
file_off: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let elf_path = "../target/riscv64/debug/kernel-rust";
|
||||||
|
let bin_data = std::fs::read(elf_path)?;
|
||||||
|
let obj_file = object::File::parse(&*bin_data)?;
|
||||||
|
|
||||||
|
let context = Context::new(&obj_file)?;
|
||||||
|
|
||||||
|
let mut symbols_list = Vec::new();
|
||||||
|
let mut string_table = Vec::new();
|
||||||
|
let mut str_cache = HashMap::new();
|
||||||
|
|
||||||
|
// Helper pour gérer la table des chaînes (String Table)
|
||||||
|
let mut add_string = |s: &str| -> u32 {
|
||||||
|
*str_cache.entry(s.to_string()).or_insert_with(|| {
|
||||||
|
let off = string_table.len() as u32;
|
||||||
|
string_table.extend_from_slice(s.as_bytes());
|
||||||
|
string_table.push(0); // Null terminator
|
||||||
|
off
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Extraction des symboles depuis {}...", elf_path);
|
||||||
|
|
||||||
|
obj_file.symbols().enumerate().for_each(|(_i, sym)| {
|
||||||
|
// On ne garde que les fonctions (Text)
|
||||||
|
if sym.kind() == SymbolKind::Text && sym.size() > 0 {
|
||||||
|
let addr = sym.address();
|
||||||
|
let raw_name = sym.name().unwrap_or("unknown");
|
||||||
|
let name = format!("{:#}", rustc_demangle::demangle(raw_name));
|
||||||
|
|
||||||
|
let mut frames = context.find_frames(addr).skip_all_loads().unwrap();
|
||||||
|
|
||||||
|
let (file, line) = if let Ok(Some(frame)) = frames.next() {
|
||||||
|
let f = frame
|
||||||
|
.location
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|l| {
|
||||||
|
l.file.and_then(|file| {
|
||||||
|
Path::new(file)
|
||||||
|
.strip_prefix(std::env::current_dir().unwrap().parent().unwrap())
|
||||||
|
.map_or(Some(file), |p| p.to_str())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or("unknown");
|
||||||
|
let l = frame.location.as_ref().and_then(|l| l.line).unwrap_or(0);
|
||||||
|
(f, l)
|
||||||
|
} else {
|
||||||
|
("unknown", 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
symbols_list.push(RawSymbol {
|
||||||
|
addr,
|
||||||
|
line,
|
||||||
|
name_off: add_string(&name),
|
||||||
|
file_off: add_string(file),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tri par adresse pour la recherche binaire au runtime
|
||||||
|
symbols_list.sort_by_key(|s| s.addr);
|
||||||
|
|
||||||
|
// Écriture du fichier symbols.bin
|
||||||
|
let mut f = BufWriter::new(File::create("../target/symbols.bin")?);
|
||||||
|
|
||||||
|
// Header : [u64: count] [u64: string_table_offset]
|
||||||
|
let header_size = 16;
|
||||||
|
let sym_table_size = symbols_list.len() * std::mem::size_of::<RawSymbol>();
|
||||||
|
f.write_all(&(symbols_list.len() as u64).to_le_bytes())?;
|
||||||
|
f.write_all(&((header_size + sym_table_size) as u64).to_le_bytes())?;
|
||||||
|
|
||||||
|
// Table des symboles
|
||||||
|
for sym in &symbols_list {
|
||||||
|
unsafe {
|
||||||
|
let bytes = std::slice::from_raw_parts(
|
||||||
|
(sym as *const RawSymbol) as *const u8,
|
||||||
|
std::mem::size_of::<RawSymbol>(),
|
||||||
|
);
|
||||||
|
f.write_all(bytes)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table des noms
|
||||||
|
f.write_all(&string_table)?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Terminé ! {} symboles écrits dans symbols.bin",
|
||||||
|
symbols_list.len()
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
7
crates/ansii/Cargo.toml
Normal file
7
crates/ansii/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "ansii"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
winnow = { version = "1", default-features = false, features = ["binary", "ascii"] }
|
||||||
66
crates/ansii/src/lib.rs
Normal file
66
crates/ansii/src/lib.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use winnow::Result;
|
||||||
|
use winnow::ascii::dec_uint;
|
||||||
|
use winnow::combinator::{alt, preceded, seq};
|
||||||
|
use winnow::error::ContextError;
|
||||||
|
use winnow::prelude::*;
|
||||||
|
|
||||||
|
pub enum AnsiiEscape {
|
||||||
|
Color16(ColorPlace, Color16Type, u8),
|
||||||
|
Color256(u8),
|
||||||
|
ColorRGB(ColorPlace, u8, u8, u8),
|
||||||
|
}
|
||||||
|
pub enum ColorPlace {
|
||||||
|
Foreground,
|
||||||
|
Background,
|
||||||
|
}
|
||||||
|
pub enum Color16Type {
|
||||||
|
Normal,
|
||||||
|
Bold,
|
||||||
|
Brigth,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_color16(input: &mut &str) -> Result<AnsiiEscape> {
|
||||||
|
let (c, _) = preceded("[", seq!(dec_uint, "m")).parse_next(input)?;
|
||||||
|
if c < 30 || c == 38 || c == 48 || c > 49 {
|
||||||
|
Err(ContextError::new())
|
||||||
|
} else {
|
||||||
|
Ok(AnsiiEscape::Color16(
|
||||||
|
ColorPlace::Foreground,
|
||||||
|
Color16Type::Normal,
|
||||||
|
c,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_color_rgb(input: &mut &str) -> Result<AnsiiEscape> {
|
||||||
|
let (t, _, r, _, g, _, b, _) = preceded(
|
||||||
|
"[",
|
||||||
|
seq!(
|
||||||
|
alt(("38", "48")),
|
||||||
|
";",
|
||||||
|
dec_uint,
|
||||||
|
";",
|
||||||
|
dec_uint,
|
||||||
|
";",
|
||||||
|
dec_uint,
|
||||||
|
"m"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.parse_next(input)?;
|
||||||
|
Ok(AnsiiEscape::ColorRGB(
|
||||||
|
if t == "38" {
|
||||||
|
ColorPlace::Foreground
|
||||||
|
} else {
|
||||||
|
ColorPlace::Background
|
||||||
|
},
|
||||||
|
r,
|
||||||
|
g,
|
||||||
|
b,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_ansii(input: &mut &str) -> Result<AnsiiEscape> {
|
||||||
|
alt((parse_color_rgb, parse_color16)).parse_next(input)
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
io = { path = "../io" }
|
io = { package = "no-std-io", path = "../io" }
|
||||||
bitflags = "2"
|
bitflags = "2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::error::Error;
|
use io::Read;
|
||||||
use io::{Read, ReadLeExt};
|
|
||||||
|
use crate::io_ext::ReadLeExt;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Fat32BootSector {
|
pub struct Fat32BootSector {
|
||||||
@@ -94,7 +95,7 @@ impl Fat32BootSector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fat32BootSector {
|
impl Fat32BootSector {
|
||||||
pub fn deserialize<T: Read>(disk: &mut T) -> Result<Self, Error<T::Error>> {
|
pub fn deserialize<T: Read>(disk: &mut T) -> Result<Self, io::Error> {
|
||||||
let mut jump_boot = [0u8; _];
|
let mut jump_boot = [0u8; _];
|
||||||
disk.read_exact(&mut jump_boot)?;
|
disk.read_exact(&mut jump_boot)?;
|
||||||
let mut oem_name = [0u8; _];
|
let mut oem_name = [0u8; _];
|
||||||
|
|||||||
@@ -1,36 +1,32 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||||
entry::{DirEntry, DirectoryIterator},
|
entry::{DirEntry, DirectoryIterator},
|
||||||
error::Error,
|
|
||||||
file::{File, RawFile},
|
file::{File, RawFile},
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
use io::{self, IoBase, Read, Seek, Write};
|
use io::{self, Read, Seek, Write};
|
||||||
|
|
||||||
pub struct Dir<'a, T> {
|
pub struct Dir<'a, T> {
|
||||||
raw: RawFile<'a, T>,
|
raw: RawFile<'a, T>,
|
||||||
fs: &'a Fat32FileSystem<T>,
|
fs: &'a Fat32FileSystem<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: IoBase> IoBase for Dir<'a, T> {
|
|
||||||
type Error = Error<T::Error>;
|
|
||||||
}
|
|
||||||
impl<'a, T: ReadSeek> Seek for Dir<'a, T> {
|
impl<'a, T: ReadSeek> Seek for Dir<'a, T> {
|
||||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||||
self.raw.seek(pos)
|
self.raw.seek(pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T: ReadSeek> Read for Dir<'a, T> {
|
impl<'a, T: ReadSeek> Read for Dir<'a, T> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
self.raw.read(buf)
|
self.raw.read(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T: ReadWriteSeek> Write for Dir<'a, T> {
|
impl<'a, T: ReadWriteSeek> Write for Dir<'a, T> {
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
self.raw.write(buf)
|
self.raw.write(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
self.raw.flush()
|
self.raw.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +40,7 @@ impl<'a, T> Dir<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T: ReadSeek> Dir<'a, T> {
|
impl<'a, T: ReadSeek> Dir<'a, T> {
|
||||||
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'a, T>, Error<T::Error>> {
|
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'a, T>, io::Error> {
|
||||||
if path.as_ref().is_absolute() {
|
if path.as_ref().is_absolute() {
|
||||||
return self.fs.open_entry(path);
|
return self.fs.open_entry(path);
|
||||||
}
|
}
|
||||||
@@ -56,16 +52,16 @@ impl<'a, T: ReadSeek> Dir<'a, T> {
|
|||||||
if f.is_dir() {
|
if f.is_dir() {
|
||||||
return f.to_dir().open_entry(entry_name);
|
return f.to_dir().open_entry(entry_name);
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::NotFound);
|
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(f);
|
return Ok(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(Error::NotFound)
|
Err(io::Error::from(io::ErrorKind::NotFound))
|
||||||
}
|
}
|
||||||
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'a, T>, Error<T::Error>> {
|
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'a, T>, io::Error> {
|
||||||
if path.as_ref().is_absolute() {
|
if path.as_ref().is_absolute() {
|
||||||
return self.fs.open_file(path);
|
return self.fs.open_file(path);
|
||||||
}
|
}
|
||||||
@@ -74,19 +70,19 @@ impl<'a, T: ReadSeek> Dir<'a, T> {
|
|||||||
match dirname {
|
match dirname {
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
if !entry.is_dir() {
|
if !entry.is_dir() {
|
||||||
return Err(Error::NotFound);
|
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||||
}
|
}
|
||||||
entry.to_dir().open_file(name)
|
entry.to_dir().open_file(name)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if !entry.is_file() {
|
if !entry.is_file() {
|
||||||
return Err(Error::NotFound);
|
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||||
}
|
}
|
||||||
Ok(entry.to_file())
|
Ok(entry.to_file())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> Result<Self, Error<T::Error>> {
|
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> Result<Self, io::Error> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
if path.is_absolute() {
|
if path.is_absolute() {
|
||||||
return self.fs.open_dir(path);
|
return self.fs.open_dir(path);
|
||||||
@@ -94,7 +90,7 @@ impl<'a, T: ReadSeek> Dir<'a, T> {
|
|||||||
let (start, dirname) = path.split_path();
|
let (start, dirname) = path.split_path();
|
||||||
let entry = self.open_entry(start)?;
|
let entry = self.open_entry(start)?;
|
||||||
if !entry.is_dir() {
|
if !entry.is_dir() {
|
||||||
return Err(Error::NotFound);
|
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||||
}
|
}
|
||||||
match dirname {
|
match dirname {
|
||||||
Some(name) => entry.to_dir().open_dir(name),
|
Some(name) => entry.to_dir().open_dir(name),
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use core::{array::IntoIter, iter::Copied, str::Utf8Error};
|
use core::{array::IntoIter, iter::Copied, str::Utf8Error};
|
||||||
use core::{char::DecodeUtf16, marker::PhantomData, slice::Iter};
|
use core::{char::DecodeUtf16, marker::PhantomData, slice::Iter};
|
||||||
|
|
||||||
|
use crate::io_ext::ReadLeExt;
|
||||||
use crate::{
|
use crate::{
|
||||||
Fat32FileSystem, ReadSeek,
|
Fat32FileSystem, ReadSeek,
|
||||||
consts::FATAttr,
|
consts::FATAttr,
|
||||||
dir::Dir,
|
dir::Dir,
|
||||||
error::Error,
|
|
||||||
file::{File, RawFile},
|
file::{File, RawFile},
|
||||||
};
|
};
|
||||||
|
|
||||||
use io::{IoBase, Read, ReadLeExt};
|
use io::Read;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::borrow::ToOwned;
|
use alloc::borrow::ToOwned;
|
||||||
@@ -33,7 +33,7 @@ pub struct FatDirEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FatDirEntry {
|
impl FatDirEntry {
|
||||||
pub fn deserialize<T: Read>(reader: &mut T) -> Result<Self, T::Error> {
|
pub fn deserialize<T: Read>(reader: &mut T) -> Result<Self, io::Error> {
|
||||||
let mut name = [0u8; _];
|
let mut name = [0u8; _];
|
||||||
reader.read_exact(&mut name)?;
|
reader.read_exact(&mut name)?;
|
||||||
let attr = reader.read_u8()?;
|
let attr = reader.read_u8()?;
|
||||||
@@ -131,7 +131,7 @@ pub struct LongFileNameBuilder<T> {
|
|||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: IoBase> LongFileNameBuilder<T> {
|
impl<T> LongFileNameBuilder<T> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: [0; _],
|
inner: [0; _],
|
||||||
@@ -160,12 +160,12 @@ impl<T: IoBase> LongFileNameBuilder<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn to_string(&self) -> Result<String, Error<T::Error>> {
|
pub fn to_string(&self) -> Result<String, io::Error> {
|
||||||
self.iter().try_collect::<String>()
|
self.iter().try_collect::<String>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: IoBase> IntoIterator for LongFileNameBuilder<T> {
|
impl<T> IntoIterator for LongFileNameBuilder<T> {
|
||||||
type Item = <LongFileNameIterator<T, IntoIter<u16, 256>> as Iterator>::Item;
|
type Item = <LongFileNameIterator<T, IntoIter<u16, 256>> as Iterator>::Item;
|
||||||
|
|
||||||
type IntoIter = LongFileNameIterator<T, IntoIter<u16, 256>>;
|
type IntoIter = LongFileNameIterator<T, IntoIter<u16, 256>>;
|
||||||
@@ -178,7 +178,7 @@ impl<T: IoBase> IntoIterator for LongFileNameBuilder<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: IoBase> LongFileNameBuilder<T> {
|
impl<T> LongFileNameBuilder<T> {
|
||||||
pub fn iter(&self) -> LongFileNameIterator<T, Copied<Iter<'_, u16>>> {
|
pub fn iter(&self) -> LongFileNameIterator<T, Copied<Iter<'_, u16>>> {
|
||||||
LongFileNameIterator {
|
LongFileNameIterator {
|
||||||
iterator: char::decode_utf16(self.inner.iter().copied()),
|
iterator: char::decode_utf16(self.inner.iter().copied()),
|
||||||
@@ -192,8 +192,8 @@ pub struct LongFileNameIterator<T, I: Iterator<Item = u16>> {
|
|||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: IoBase, I: Iterator<Item = u16>> Iterator for LongFileNameIterator<T, I> {
|
impl<T, I: Iterator<Item = u16>> Iterator for LongFileNameIterator<T, I> {
|
||||||
type Item = Result<char, Error<T::Error>>;
|
type Item = Result<char, io::Error>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self.iterator.next()? {
|
match self.iterator.next()? {
|
||||||
@@ -204,12 +204,15 @@ impl<T: IoBase, I: Iterator<Item = u16>> Iterator for LongFileNameIterator<T, I>
|
|||||||
Some(Ok(value))
|
Some(Ok(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => Some(Err(Error::UnsupportedFileNameCharacter)),
|
Err(_) => Some(Err(io::Error::new(
|
||||||
|
io::ErrorKind::Unsupported,
|
||||||
|
"Unsupported file name character",
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: IoBase> Default for LongFileNameBuilder<T> {
|
impl<T> Default for LongFileNameBuilder<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@@ -226,7 +229,7 @@ impl<'a, T> DirectoryIterator<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ReadSeek + 'a> Iterator for DirectoryIterator<'a, T> {
|
impl<'a, T: ReadSeek + 'a> Iterator for DirectoryIterator<'a, T> {
|
||||||
type Item = Result<DirEntry<'a, T>, Error<T::Error>>;
|
type Item = Result<DirEntry<'a, T>, io::Error>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let mut lfn_builder = LongFileNameBuilder::new();
|
let mut lfn_builder = LongFileNameBuilder::new();
|
||||||
@@ -283,7 +286,7 @@ pub struct DirEntry<'a, T> {
|
|||||||
fs: &'a Fat32FileSystem<T>,
|
fs: &'a Fat32FileSystem<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: IoBase> DirEntry<'a, T> {
|
impl<'a, T> DirEntry<'a, T> {
|
||||||
fn new(
|
fn new(
|
||||||
entry: FatDirEntry,
|
entry: FatDirEntry,
|
||||||
short_name: [u8; 11],
|
short_name: [u8; 11],
|
||||||
@@ -298,13 +301,16 @@ impl<'a, T: IoBase> DirEntry<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn name(&self) -> Result<String, Error<T::Error>> {
|
pub fn name(&self) -> Result<String, io::Error> {
|
||||||
if let Some(long_name) = self.long_name() {
|
if let Some(long_name) = self.long_name() {
|
||||||
long_name.to_string()
|
long_name.to_string()
|
||||||
} else {
|
} else {
|
||||||
self.short_name()
|
self.short_name().map(|n| n.to_owned()).map_err(|_| {
|
||||||
.map(|n| n.to_owned())
|
io::Error::new(
|
||||||
.map_err(|_| Error::UnsupportedFileNameCharacter)
|
io::ErrorKind::Unsupported,
|
||||||
|
"Unsupported file name character",
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn long_name(&self) -> Option<&LongFileNameBuilder<T>> {
|
pub fn long_name(&self) -> Option<&LongFileNameBuilder<T>> {
|
||||||
|
|||||||
@@ -1,79 +1 @@
|
|||||||
use io::error::IoError;
|
|
||||||
|
|
||||||
/// Error enum with all errors that can be returned by functions from this crate
|
|
||||||
///
|
|
||||||
/// Generic parameter `T` is a type of external error returned by the user provided storage
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum Error<T> {
|
|
||||||
/// A user provided storage instance returned an error during an input/output operation.
|
|
||||||
Io(T),
|
|
||||||
/// A read operation cannot be completed because an end of a file has been reached prematurely.
|
|
||||||
UnexpectedEof,
|
|
||||||
/// A write operation cannot be completed because `Write::write` returned 0.
|
|
||||||
WriteZero,
|
|
||||||
/// A parameter was incorrect.
|
|
||||||
InvalidInput,
|
|
||||||
/// A requested file or directory has not been found.
|
|
||||||
NotFound,
|
|
||||||
/// A file or a directory with the same name already exists.
|
|
||||||
AlreadyExists,
|
|
||||||
/// An operation cannot be finished because a directory is not empty.
|
|
||||||
DirectoryIsNotEmpty,
|
|
||||||
/// File system internal structures are corrupted/invalid.
|
|
||||||
CorruptedFileSystem,
|
|
||||||
/// There is not enough free space on the storage to finish the requested operation.
|
|
||||||
NotEnoughSpace,
|
|
||||||
/// The provided file name is either too long or empty.
|
|
||||||
InvalidFileNameLength,
|
|
||||||
/// The provided file name contains an invalid character.
|
|
||||||
UnsupportedFileNameCharacter,
|
|
||||||
/// The file content contains invalid UTF-8 characters.
|
|
||||||
InvalidUTF8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: IoError> From<T> for Error<T> {
|
|
||||||
fn from(error: T) -> Self {
|
|
||||||
Error::Io(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: core::fmt::Display> core::fmt::Display for Error<T> {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Error::Io(io_error) => write!(f, "IO error: {}", io_error),
|
|
||||||
Error::UnexpectedEof => write!(f, "Unexpected end of file"),
|
|
||||||
Error::NotEnoughSpace => write!(f, "Not enough space"),
|
|
||||||
Error::WriteZero => write!(f, "Write zero"),
|
|
||||||
Error::InvalidInput => write!(f, "Invalid input"),
|
|
||||||
Error::InvalidFileNameLength => write!(f, "Invalid file name length"),
|
|
||||||
Error::UnsupportedFileNameCharacter => write!(f, "Unsupported file name character"),
|
|
||||||
Error::DirectoryIsNotEmpty => write!(f, "Directory is not empty"),
|
|
||||||
Error::NotFound => write!(f, "No such file or directory"),
|
|
||||||
Error::AlreadyExists => write!(f, "File or directory already exists"),
|
|
||||||
Error::CorruptedFileSystem => write!(f, "Corrupted file system"),
|
|
||||||
Error::InvalidUTF8 => write!(f, "File contains invalid UTF-8 characters"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: core::fmt::Debug + IoError> IoError for Error<T> {
|
|
||||||
fn is_interrupted(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Error::<T>::Io(io_error) => io_error.is_interrupted(),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_unexpected_eof_error() -> Self {
|
|
||||||
Error::<T>::UnexpectedEof
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_write_zero_error() -> Self {
|
|
||||||
Error::<T>::WriteZero
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_invalid_utf8_error() -> Self {
|
|
||||||
Error::<T>::InvalidUTF8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||||
consts::{FAT32_BAD_CLUSTER, FAT32_END_OF_CHAIN},
|
consts::{FAT32_BAD_CLUSTER, FAT32_END_OF_CHAIN},
|
||||||
error::Error,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use io::{self, IoBase, Read, Seek, Write};
|
use io::{self, Read, Seek, Write};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RawFile<'a, T> {
|
pub struct RawFile<'a, T> {
|
||||||
@@ -30,12 +29,8 @@ impl<'a, T> RawFile<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: IoBase> IoBase for RawFile<'a, T> {
|
|
||||||
type Error = Error<T::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: ReadSeek> Seek for RawFile<'a, T> {
|
impl<'a, T: ReadSeek> Seek for RawFile<'a, T> {
|
||||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||||
let new_pos = match pos {
|
let new_pos = match pos {
|
||||||
io::SeekFrom::Start(s) => s,
|
io::SeekFrom::Start(s) => s,
|
||||||
io::SeekFrom::Current(c) => (self.pos as i64 + c) as u64,
|
io::SeekFrom::Current(c) => (self.pos as i64 + c) as u64,
|
||||||
@@ -67,7 +62,7 @@ impl<'a, T: ReadSeek> Seek for RawFile<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ReadSeek> Read for RawFile<'a, T> {
|
impl<'a, T: ReadSeek> Read for RawFile<'a, T> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||||
let max_len = self
|
let max_len = self
|
||||||
.size
|
.size
|
||||||
.map(|size| core::cmp::min(buf.len(), size as usize - self.pos as usize))
|
.map(|size| core::cmp::min(buf.len(), size as usize - self.pos as usize))
|
||||||
@@ -116,11 +111,11 @@ impl<'a, T: ReadSeek> Read for RawFile<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T: ReadWriteSeek> Write for RawFile<'a, T> {
|
impl<'a, T: ReadWriteSeek> Write for RawFile<'a, T> {
|
||||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
|
fn write(&mut self, _buf: &[u8]) -> Result<usize, io::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
fn flush(&mut self) -> Result<(), io::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,25 +127,22 @@ pub struct File<'a, T> {
|
|||||||
fs: &'a Fat32FileSystem<T>,
|
fs: &'a Fat32FileSystem<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: IoBase> IoBase for File<'a, T> {
|
|
||||||
type Error = Error<T::Error>;
|
|
||||||
}
|
|
||||||
impl<'a, T: ReadSeek> Seek for File<'a, T> {
|
impl<'a, T: ReadSeek> Seek for File<'a, T> {
|
||||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||||
self.raw.seek(pos)
|
self.raw.seek(pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T: ReadSeek> Read for File<'a, T> {
|
impl<'a, T: ReadSeek> Read for File<'a, T> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||||
self.raw.read(buf)
|
self.raw.read(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T: ReadWriteSeek> Write for File<'a, T> {
|
impl<'a, T: ReadWriteSeek> Write for File<'a, T> {
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||||
self.raw.write(buf)
|
self.raw.write(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
fn flush(&mut self) -> Result<(), io::Error> {
|
||||||
self.raw.flush()
|
self.raw.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
crates/bffs/src/io_ext.rs
Normal file
27
crates/bffs/src/io_ext.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use io::{Read, Result};
|
||||||
|
|
||||||
|
pub trait ReadLeExt {
|
||||||
|
fn read_u8(&mut self) -> Result<u8>;
|
||||||
|
fn read_u16_le(&mut self) -> Result<u16>;
|
||||||
|
fn read_u32_le(&mut self) -> Result<u32>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Read> ReadLeExt for T {
|
||||||
|
fn read_u8(&mut self) -> Result<u8> {
|
||||||
|
let mut buf = [0_u8; 1];
|
||||||
|
self.read_exact(&mut buf)?;
|
||||||
|
Ok(buf[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u16_le(&mut self) -> Result<u16> {
|
||||||
|
let mut buf = [0_u8; 2];
|
||||||
|
self.read_exact(&mut buf)?;
|
||||||
|
Ok(u16::from_le_bytes(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u32_le(&mut self) -> Result<u32> {
|
||||||
|
let mut buf = [0_u8; 4];
|
||||||
|
self.read_exact(&mut buf)?;
|
||||||
|
Ok(u32::from_le_bytes(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,15 @@
|
|||||||
#![feature(iterator_try_collect, iter_order_by)]
|
#![feature(iterator_try_collect, iter_order_by)]
|
||||||
|
#![allow(unused_features)]
|
||||||
#![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)]
|
#![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)]
|
||||||
|
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
boot_sector::Fat32BootSector,
|
boot_sector::Fat32BootSector, consts::FAT32_CLUSTER_MASK, dir::Dir, entry::{DirEntry, FatEntry}, file::{File, RawFile}, io_ext::ReadLeExt, path::Path
|
||||||
consts::FAT32_CLUSTER_MASK,
|
|
||||||
dir::Dir,
|
|
||||||
entry::{DirEntry, FatEntry},
|
|
||||||
error::Error,
|
|
||||||
file::{File, RawFile},
|
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use io::{Read, ReadLeExt, Seek, Write};
|
use io::{Read, Seek, Write};
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@@ -23,8 +18,8 @@ pub mod boot_sector;
|
|||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod dir;
|
pub mod dir;
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
pub mod error;
|
|
||||||
pub mod file;
|
pub mod file;
|
||||||
|
pub mod io_ext;
|
||||||
pub mod path;
|
pub mod path;
|
||||||
|
|
||||||
pub trait ReadSeek: Read + Seek {}
|
pub trait ReadSeek: Read + Seek {}
|
||||||
@@ -46,7 +41,7 @@ pub struct Fat32FsInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fat32FsInfo {
|
impl Fat32FsInfo {
|
||||||
pub fn deserialize<T: Read>(disk: &mut T) -> Result<Self, Error<T::Error>> {
|
pub fn deserialize<T: Read>(disk: &mut T) -> Result<Self, io::Error> {
|
||||||
let lead_signature = disk.read_u32_le()?;
|
let lead_signature = disk.read_u32_le()?;
|
||||||
let mut reserved1 = [0u8; _];
|
let mut reserved1 = [0u8; _];
|
||||||
disk.read_exact(&mut reserved1)?;
|
disk.read_exact(&mut reserved1)?;
|
||||||
@@ -81,7 +76,7 @@ impl<T> Display for Fat32FileSystem<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ReadSeek> Fat32FileSystem<T> {
|
impl<T: ReadSeek> Fat32FileSystem<T> {
|
||||||
pub fn new(mut device: T) -> Result<Self, Error<T::Error>> {
|
pub fn new(mut device: T) -> Result<Self, io::Error> {
|
||||||
device.seek(io::SeekFrom::Start(0))?;
|
device.seek(io::SeekFrom::Start(0))?;
|
||||||
|
|
||||||
let boot_sector = Fat32BootSector::deserialize(&mut device)?;
|
let boot_sector = Fat32BootSector::deserialize(&mut device)?;
|
||||||
@@ -91,7 +86,7 @@ impl<T: ReadSeek> Fat32FileSystem<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Get the next cluster from the current one
|
/// Get the next cluster from the current one
|
||||||
fn get_next_cluster(&self, current_cluster: u32) -> Result<u32, Error<T::Error>> {
|
fn get_next_cluster(&self, current_cluster: u32) -> Result<u32, io::Error> {
|
||||||
let fat_offset =
|
let fat_offset =
|
||||||
self.fat_start_offset() + (current_cluster as u64 * size_of::<FatEntry>() as u64);
|
self.fat_start_offset() + (current_cluster as u64 * size_of::<FatEntry>() as u64);
|
||||||
self.device
|
self.device
|
||||||
@@ -136,15 +131,15 @@ impl<T> Fat32FileSystem<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: ReadSeek> Fat32FileSystem<T> {
|
impl<T: ReadSeek> Fat32FileSystem<T> {
|
||||||
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'_, T>, Error<T::Error>> {
|
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'_, T>, io::Error> {
|
||||||
let path = path.as_ref().as_str().trim_start_matches("/");
|
let path = path.as_ref().as_str().trim_start_matches("/");
|
||||||
self.root_directory().open_entry(path)
|
self.root_directory().open_entry(path)
|
||||||
}
|
}
|
||||||
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> Result<Dir<'_, T>, Error<T::Error>> {
|
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> Result<Dir<'_, T>, io::Error> {
|
||||||
let path = path.as_ref().as_str().trim_start_matches("/");
|
let path = path.as_ref().as_str().trim_start_matches("/");
|
||||||
self.root_directory().open_dir(path)
|
self.root_directory().open_dir(path)
|
||||||
}
|
}
|
||||||
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'_, T>, Error<T::Error>> {
|
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'_, T>, io::Error> {
|
||||||
let path = path.as_ref().as_str().trim_start_matches("/");
|
let path = path.as_ref().as_str().trim_start_matches("/");
|
||||||
self.root_directory().open_file(path)
|
self.root_directory().open_file(path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub fn main() {
|
|||||||
|
|
||||||
// walk(fs.root_directory());
|
// walk(fs.root_directory());
|
||||||
|
|
||||||
let mut f = fs.open_file("/usr/bin/test_pic").unwrap();
|
let mut f = fs.open_file("/usr/bin/agetty").unwrap();
|
||||||
let mut content = Vec::new();
|
let mut content = Vec::new();
|
||||||
f.read_to_end(&mut content).unwrap();
|
f.read_to_end(&mut content).unwrap();
|
||||||
println!("file content len: {}", content.len());
|
println!("file content len: {}", content.len());
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use core::fmt::Display;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use core::{borrow::Borrow, ops::Deref};
|
use core::{borrow::Borrow, ops::Deref};
|
||||||
|
|
||||||
@@ -12,6 +13,12 @@ pub struct Path {
|
|||||||
inner: str,
|
inner: str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Path {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{}", &self.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&str> for &Path {
|
impl From<&str> for &Path {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
unsafe { &*(value as *const str as *const Path) }
|
unsafe { &*(value as *const str as *const Path) }
|
||||||
|
|||||||
3
crates/io/.gitignore
vendored
Normal file
3
crates/io/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.cargo
|
||||||
|
|
||||||
|
target
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "io"
|
name = "no-std-io"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = []
|
std = []
|
||||||
std = ["alloc"]
|
|
||||||
|
|||||||
49
crates/io/justfile
Normal file
49
crates/io/justfile
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
RUST_SRC := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src"
|
||||||
|
|
||||||
|
copy-io:
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
FILES=(
|
||||||
|
"io/error/repr_unpacked.rs"
|
||||||
|
"io/error/repr_bitpacked.rs"
|
||||||
|
"io/error.rs"
|
||||||
|
"io/buffered/mod.rs"
|
||||||
|
"io/buffered/linewriter.rs"
|
||||||
|
"io/buffered/linewritershim.rs"
|
||||||
|
"io/buffered/bufwriter.rs"
|
||||||
|
"io/buffered/bufreader.rs"
|
||||||
|
"io/buffered/bufreader/buffer.rs"
|
||||||
|
"io/copy.rs"
|
||||||
|
"io/cursor.rs"
|
||||||
|
"io/impls.rs"
|
||||||
|
"io/prelude.rs"
|
||||||
|
"io/util.rs"
|
||||||
|
"io/mod.rs"
|
||||||
|
)
|
||||||
|
|
||||||
|
for f in "${FILES[@]}"; do
|
||||||
|
echo "Processing $f..."
|
||||||
|
DEST="src/$f"
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$DEST")"
|
||||||
|
|
||||||
|
cp "{{ RUST_SRC }}/$f" "$DEST"
|
||||||
|
sed -i -E -n '$!N; /^#\[cfg\(test\)\]\nmod tests/d; P; D' "$DEST"
|
||||||
|
|
||||||
|
if [[ "$f" == "io/error.rs" ]]; then
|
||||||
|
sed -i "s/alloc::/alloc_crate::/g" "$DEST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$f" == "io/mod.rs" ]]; then
|
||||||
|
sed -i "/mod pipe/d" "$DEST"
|
||||||
|
sed -i "/self::pipe/d" "$DEST"
|
||||||
|
sed -i "/mod stdio/d" "$DEST"
|
||||||
|
sed -i "/stdio::/d" "$DEST"
|
||||||
|
sed -i "/self::stdio/d" "$DEST"
|
||||||
|
sed -i "/feature = \"is_terminal\"/d" "$DEST"
|
||||||
|
sed -i "/feature = \"print_internals\"/d" "$DEST"
|
||||||
|
sed -i "/feature = \"internal_output_capture\"/d" "$DEST"
|
||||||
|
sed -i "/feature = \"anonymous_pipe\"/d" "$DEST"
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/// Trait that should be implemented by errors returned from the user supplied storage.
|
|
||||||
///
|
|
||||||
/// Implementations for `std::io::Error` and `()` are provided by this crate.
|
|
||||||
pub trait IoError: core::fmt::Debug {
|
|
||||||
fn is_interrupted(&self) -> bool;
|
|
||||||
fn new_unexpected_eof_error() -> Self;
|
|
||||||
fn new_write_zero_error() -> Self;
|
|
||||||
fn new_invalid_utf8_error() -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IoError for () {
|
|
||||||
fn is_interrupted(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_unexpected_eof_error() -> Self {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_write_zero_error() -> Self {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_invalid_utf8_error() -> Self {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
||||||
impl IoError for std::io::Error {
|
|
||||||
fn is_interrupted(&self) -> bool {
|
|
||||||
self.kind() == std::io::ErrorKind::Interrupted
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_unexpected_eof_error() -> Self {
|
|
||||||
Self::new(
|
|
||||||
std::io::ErrorKind::UnexpectedEof,
|
|
||||||
"failed to fill whole buffer",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_write_zero_error() -> Self {
|
|
||||||
Self::new(
|
|
||||||
std::io::ErrorKind::WriteZero,
|
|
||||||
"failed to write whole buffer",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,8 +5,6 @@ mod bufwriter;
|
|||||||
mod linewriter;
|
mod linewriter;
|
||||||
mod linewritershim;
|
mod linewritershim;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||||
pub use bufwriter::WriterPanicked;
|
pub use bufwriter::WriterPanicked;
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
use super::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write};
|
use super::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write};
|
||||||
use crate::alloc::Allocator;
|
use crate::alloc::Allocator;
|
||||||
use crate::cmp;
|
use crate::cmp;
|
||||||
use alloc_crate::collections::VecDeque;
|
use crate::collections::VecDeque;
|
||||||
use crate::io::IoSlice;
|
use crate::io::IoSlice;
|
||||||
use crate::mem::MaybeUninit;
|
use crate::mem::MaybeUninit;
|
||||||
use crate::sys::io::{CopyState, kernel_copy};
|
use crate::sys::io::{CopyState, kernel_copy};
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
/// Copies the entire contents of a reader into a writer.
|
/// Copies the entire contents of a reader into a writer.
|
||||||
///
|
///
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use crate::alloc::Allocator;
|
use crate::alloc::Allocator;
|
||||||
use crate::cmp;
|
use crate::cmp;
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
// On 64-bit platforms, `io::Error` may use a bit-packed representation to
|
// On 64-bit platforms, `io::Error` may use a bit-packed representation to
|
||||||
// reduce size. However, this representation assumes that error codes are
|
// reduce size. However, this representation assumes that error codes are
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use crate::alloc::Allocator;
|
use crate::alloc::Allocator;
|
||||||
use alloc_crate::collections::VecDeque;
|
use crate::collections::VecDeque;
|
||||||
use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||||
use crate::{cmp, fmt, mem, str};
|
use crate::{cmp, fmt, mem, str};
|
||||||
|
|
||||||
@@ -1,54 +1,338 @@
|
|||||||
#[cfg(test)]
|
//! Traits, helpers, and type definitions for core I/O functionality.
|
||||||
mod tests;
|
//!
|
||||||
|
//! The `std::io` module contains a number of common things you'll need
|
||||||
|
//! when doing input and output. The most core part of this module is
|
||||||
|
//! the [`Read`] and [`Write`] traits, which provide the
|
||||||
|
//! most general interface for reading and writing input and output.
|
||||||
|
//!
|
||||||
|
//! ## Read and Write
|
||||||
|
//!
|
||||||
|
//! Because they are traits, [`Read`] and [`Write`] are implemented by a number
|
||||||
|
//! of other types, and you can implement them for your types too. As such,
|
||||||
|
//! you'll see a few different types of I/O throughout the documentation in
|
||||||
|
//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec<T>`]s. For
|
||||||
|
//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on
|
||||||
|
//! [`File`]s:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use std::io;
|
||||||
|
//! use std::io::prelude::*;
|
||||||
|
//! use std::fs::File;
|
||||||
|
//!
|
||||||
|
//! fn main() -> io::Result<()> {
|
||||||
|
//! let mut f = File::open("foo.txt")?;
|
||||||
|
//! let mut buffer = [0; 10];
|
||||||
|
//!
|
||||||
|
//! // read up to 10 bytes
|
||||||
|
//! let n = f.read(&mut buffer)?;
|
||||||
|
//!
|
||||||
|
//! println!("The bytes: {:?}", &buffer[..n]);
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`Read`] and [`Write`] are so important, implementors of the two traits have a
|
||||||
|
//! nickname: readers and writers. So you'll sometimes see 'a reader' instead
|
||||||
|
//! of 'a type that implements the [`Read`] trait'. Much easier!
|
||||||
|
//!
|
||||||
|
//! ## Seek and BufRead
|
||||||
|
//!
|
||||||
|
//! Beyond that, there are two important traits that are provided: [`Seek`]
|
||||||
|
//! and [`BufRead`]. Both of these build on top of a reader to control
|
||||||
|
//! how the reading happens. [`Seek`] lets you control where the next byte is
|
||||||
|
//! coming from:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use std::io;
|
||||||
|
//! use std::io::prelude::*;
|
||||||
|
//! use std::io::SeekFrom;
|
||||||
|
//! use std::fs::File;
|
||||||
|
//!
|
||||||
|
//! fn main() -> io::Result<()> {
|
||||||
|
//! let mut f = File::open("foo.txt")?;
|
||||||
|
//! let mut buffer = [0; 10];
|
||||||
|
//!
|
||||||
|
//! // skip to the last 10 bytes of the file
|
||||||
|
//! f.seek(SeekFrom::End(-10))?;
|
||||||
|
//!
|
||||||
|
//! // read up to 10 bytes
|
||||||
|
//! let n = f.read(&mut buffer)?;
|
||||||
|
//!
|
||||||
|
//! println!("The bytes: {:?}", &buffer[..n]);
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but
|
||||||
|
//! to show it off, we'll need to talk about buffers in general. Keep reading!
|
||||||
|
//!
|
||||||
|
//! ## BufReader and BufWriter
|
||||||
|
//!
|
||||||
|
//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be
|
||||||
|
//! making near-constant calls to the operating system. To help with this,
|
||||||
|
//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap
|
||||||
|
//! readers and writers. The wrapper uses a buffer, reducing the number of
|
||||||
|
//! calls and providing nicer methods for accessing exactly what you want.
|
||||||
|
//!
|
||||||
|
//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra
|
||||||
|
//! methods to any reader:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use std::io;
|
||||||
|
//! use std::io::prelude::*;
|
||||||
|
//! use std::io::BufReader;
|
||||||
|
//! use std::fs::File;
|
||||||
|
//!
|
||||||
|
//! fn main() -> io::Result<()> {
|
||||||
|
//! let f = File::open("foo.txt")?;
|
||||||
|
//! let mut reader = BufReader::new(f);
|
||||||
|
//! let mut buffer = String::new();
|
||||||
|
//!
|
||||||
|
//! // read a line into buffer
|
||||||
|
//! reader.read_line(&mut buffer)?;
|
||||||
|
//!
|
||||||
|
//! println!("{buffer}");
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call
|
||||||
|
//! to [`write`][`Write::write`]:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use std::io;
|
||||||
|
//! use std::io::prelude::*;
|
||||||
|
//! use std::io::BufWriter;
|
||||||
|
//! use std::fs::File;
|
||||||
|
//!
|
||||||
|
//! fn main() -> io::Result<()> {
|
||||||
|
//! let f = File::create("foo.txt")?;
|
||||||
|
//! {
|
||||||
|
//! let mut writer = BufWriter::new(f);
|
||||||
|
//!
|
||||||
|
//! // write a byte to the buffer
|
||||||
|
//! writer.write(&[42])?;
|
||||||
|
//!
|
||||||
|
//! } // the buffer is flushed once writer goes out of scope
|
||||||
|
//!
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Standard input and output
|
||||||
|
//!
|
||||||
|
//! A very common source of input is standard input:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use std::io;
|
||||||
|
//!
|
||||||
|
//! fn main() -> io::Result<()> {
|
||||||
|
//! let mut input = String::new();
|
||||||
|
//!
|
||||||
|
//! io::stdin().read_line(&mut input)?;
|
||||||
|
//!
|
||||||
|
//! println!("You typed: {}", input.trim());
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Note that you cannot use the [`?` operator] in functions that do not return
|
||||||
|
//! a [`Result<T, E>`][`Result`]. Instead, you can call [`.unwrap()`]
|
||||||
|
//! or `match` on the return value to catch any possible errors:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use std::io;
|
||||||
|
//!
|
||||||
|
//! let mut input = String::new();
|
||||||
|
//!
|
||||||
|
//! io::stdin().read_line(&mut input).unwrap();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! And a very common source of output is standard output:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use std::io;
|
||||||
|
//! use std::io::prelude::*;
|
||||||
|
//!
|
||||||
|
//! fn main() -> io::Result<()> {
|
||||||
|
//! io::stdout().write(&[42])?;
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Of course, using [`io::stdout`] directly is less common than something like
|
||||||
|
//! [`println!`].
|
||||||
|
//!
|
||||||
|
//! ## Iterator types
|
||||||
|
//!
|
||||||
|
//! A large number of the structures provided by `std::io` are for various
|
||||||
|
//! ways of iterating over I/O. For example, [`Lines`] is used to split over
|
||||||
|
//! lines:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use std::io;
|
||||||
|
//! use std::io::prelude::*;
|
||||||
|
//! use std::io::BufReader;
|
||||||
|
//! use std::fs::File;
|
||||||
|
//!
|
||||||
|
//! fn main() -> io::Result<()> {
|
||||||
|
//! let f = File::open("foo.txt")?;
|
||||||
|
//! let reader = BufReader::new(f);
|
||||||
|
//!
|
||||||
|
//! for line in reader.lines() {
|
||||||
|
//! println!("{}", line?);
|
||||||
|
//! }
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Functions
|
||||||
|
//!
|
||||||
|
//! There are a number of [functions][functions-list] that offer access to various
|
||||||
|
//! features. For example, we can use three of these functions to copy everything
|
||||||
|
//! from standard input to standard output:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use std::io;
|
||||||
|
//!
|
||||||
|
//! fn main() -> io::Result<()> {
|
||||||
|
//! io::copy(&mut io::stdin(), &mut io::stdout())?;
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [functions-list]: #functions-1
|
||||||
|
//!
|
||||||
|
//! ## io::Result
|
||||||
|
//!
|
||||||
|
//! Last, but certainly not least, is [`io::Result`]. This type is used
|
||||||
|
//! as the return type of many `std::io` functions that can cause an error, and
|
||||||
|
//! can be returned from your own functions as well. Many of the examples in this
|
||||||
|
//! module use the [`?` operator]:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use std::io;
|
||||||
|
//!
|
||||||
|
//! fn read_input() -> io::Result<()> {
|
||||||
|
//! let mut input = String::new();
|
||||||
|
//!
|
||||||
|
//! io::stdin().read_line(&mut input)?;
|
||||||
|
//!
|
||||||
|
//! println!("You typed: {}", input.trim());
|
||||||
|
//!
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The return type of `read_input()`, [`io::Result<()>`][`io::Result`], is a very
|
||||||
|
//! common type for functions which don't have a 'real' return value, but do want to
|
||||||
|
//! return errors if they happen. In this case, the only purpose of this function is
|
||||||
|
//! to read the line and print it, so we use `()`.
|
||||||
|
//!
|
||||||
|
//! ## Platform-specific behavior
|
||||||
|
//!
|
||||||
|
//! Many I/O functions throughout the standard library are documented to indicate
|
||||||
|
//! what various library or syscalls they are delegated to. This is done to help
|
||||||
|
//! applications both understand what's happening under the hood as well as investigate
|
||||||
|
//! any possibly unclear semantics. Note, however, that this is informative, not a binding
|
||||||
|
//! contract. The implementation of many of these functions are subject to change over
|
||||||
|
//! time and may call fewer or more syscalls/library functions.
|
||||||
|
//!
|
||||||
|
//! ## I/O Safety
|
||||||
|
//!
|
||||||
|
//! Rust follows an I/O safety discipline that is comparable to its memory safety discipline. This
|
||||||
|
//! means that file descriptors can be *exclusively owned*. (Here, "file descriptor" is meant to
|
||||||
|
//! subsume similar concepts that exist across a wide range of operating systems even if they might
|
||||||
|
//! use a different name, such as "handle".) An exclusively owned file descriptor is one that no
|
||||||
|
//! other code is allowed to access in any way, but the owner is allowed to access and even close
|
||||||
|
//! it any time. A type that owns its file descriptor should usually close it in its `drop`
|
||||||
|
//! function. Types like [`File`] own their file descriptor. Similarly, file descriptors
|
||||||
|
//! can be *borrowed*, granting the temporary right to perform operations on this file descriptor.
|
||||||
|
//! This indicates that the file descriptor will not be closed for the lifetime of the borrow, but
|
||||||
|
//! it does *not* imply any right to close this file descriptor, since it will likely be owned by
|
||||||
|
//! someone else.
|
||||||
|
//!
|
||||||
|
//! The platform-specific parts of the Rust standard library expose types that reflect these
|
||||||
|
//! concepts, see [`os::unix`] and [`os::windows`].
|
||||||
|
//!
|
||||||
|
//! To uphold I/O safety, it is crucial that no code acts on file descriptors it does not own or
|
||||||
|
//! borrow, and no code closes file descriptors it does not own. In other words, a safe function
|
||||||
|
//! that takes a regular integer, treats it as a file descriptor, and acts on it, is *unsound*.
|
||||||
|
//!
|
||||||
|
//! Not upholding I/O safety and acting on a file descriptor without proof of ownership can lead to
|
||||||
|
//! misbehavior and even Undefined Behavior in code that relies on ownership of its file
|
||||||
|
//! descriptors: a closed file descriptor could be re-allocated, so the original owner of that file
|
||||||
|
//! descriptor is now working on the wrong file. Some code might even rely on fully encapsulating
|
||||||
|
//! its file descriptors with no operations being performed by any other part of the program.
|
||||||
|
//!
|
||||||
|
//! Note that exclusive ownership of a file descriptor does *not* imply exclusive ownership of the
|
||||||
|
//! underlying kernel object that the file descriptor references (also called "open file description" on
|
||||||
|
//! some operating systems). File descriptors basically work like [`Arc`]: when you receive an owned
|
||||||
|
//! file descriptor, you cannot know whether there are any other file descriptors that reference the
|
||||||
|
//! same kernel object. However, when you create a new kernel object, you know that you are holding
|
||||||
|
//! the only reference to it. Just be careful not to lend it to anyone, since they can obtain a
|
||||||
|
//! clone and then you can no longer know what the reference count is! In that sense, [`OwnedFd`] is
|
||||||
|
//! like `Arc` and [`BorrowedFd<'a>`] is like `&'a Arc` (and similar for the Windows types). In
|
||||||
|
//! particular, given a `BorrowedFd<'a>`, you are not allowed to close the file descriptor -- just
|
||||||
|
//! like how, given a `&'a Arc`, you are not allowed to decrement the reference count and
|
||||||
|
//! potentially free the underlying object. There is no equivalent to `Box` for file descriptors in
|
||||||
|
//! the standard library (that would be a type that guarantees that the reference count is `1`),
|
||||||
|
//! however, it would be possible for a crate to define a type with those semantics.
|
||||||
|
//!
|
||||||
|
//! [`File`]: crate::fs::File
|
||||||
|
//! [`TcpStream`]: crate::net::TcpStream
|
||||||
|
//! [`io::stdout`]: stdout
|
||||||
|
//! [`io::Result`]: self::Result
|
||||||
|
//! [`?` operator]: ../../book/appendix-02-operators.html
|
||||||
|
//! [`Result`]: crate::result::Result
|
||||||
|
//! [`.unwrap()`]: crate::result::Result::unwrap
|
||||||
|
//! [`os::unix`]: ../os/unix/io/index.html
|
||||||
|
//! [`os::windows`]: ../os/windows/io/index.html
|
||||||
|
//! [`OwnedFd`]: ../os/fd/struct.OwnedFd.html
|
||||||
|
//! [`BorrowedFd<'a>`]: ../os/fd/struct.BorrowedFd.html
|
||||||
|
//! [`Arc`]: crate::sync::Arc
|
||||||
|
|
||||||
pub mod error;
|
#![stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub use self::buffered::{BufReader, BufWriter, IntoInnerError, LineWriter};
|
|
||||||
pub use self::error::{Error, ErrorKind, Result, SimpleMessage, const_error};
|
|
||||||
pub mod buffered;
|
|
||||||
pub mod copy;
|
|
||||||
pub mod cursor;
|
|
||||||
pub mod impls;
|
|
||||||
pub mod pipe;
|
|
||||||
pub mod stdio;
|
|
||||||
pub mod prelude;
|
|
||||||
pub mod util;
|
|
||||||
|
|
||||||
|
|
||||||
|
#[unstable(feature = "read_buf", issue = "78485")]
|
||||||
|
pub use core::io::{BorrowedBuf, BorrowedCursor};
|
||||||
|
use core::slice::memchr;
|
||||||
|
|
||||||
|
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||||
|
pub use self::buffered::WriterPanicked;
|
||||||
|
#[unstable(feature = "raw_os_error_ty", issue = "107792")]
|
||||||
|
pub use self::error::RawOsError;
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "io_const_error_internals", issue = "none")]
|
||||||
|
pub use self::error::SimpleMessage;
|
||||||
|
#[unstable(feature = "io_const_error", issue = "133448")]
|
||||||
|
pub use self::error::const_error;
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[doc(no_inline, hidden)]
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use self::{
|
||||||
|
buffered::{BufReader, BufWriter, IntoInnerError, LineWriter},
|
||||||
|
copy::copy,
|
||||||
|
cursor::Cursor,
|
||||||
|
error::{Error, ErrorKind, Result},
|
||||||
|
util::{Empty, Repeat, Sink, empty, repeat, sink},
|
||||||
|
};
|
||||||
use crate::mem::{MaybeUninit, take};
|
use crate::mem::{MaybeUninit, take};
|
||||||
use crate::ops::{Deref, DerefMut};
|
use crate::ops::{Deref, DerefMut};
|
||||||
use crate::{cmp, fmt, slice, str, sys};
|
use crate::{cmp, fmt, slice, str, sys};
|
||||||
#[unstable(feature = "read_buf", issue = "78485")]
|
|
||||||
pub use core::io::{BorrowedBuf, BorrowedCursor};
|
|
||||||
pub use cursor::Cursor;
|
|
||||||
use core::slice::memchr;
|
|
||||||
pub use stdio::try_set_output_capture;
|
|
||||||
|
|
||||||
use crate::fs::File;
|
mod buffered;
|
||||||
use io::IoBase;
|
pub(crate) mod copy;
|
||||||
// pub use io::Read;
|
mod cursor;
|
||||||
// pub use io::Seek;
|
mod error;
|
||||||
// pub use io::SeekFrom;
|
mod impls;
|
||||||
// pub use io::Write;
|
pub mod prelude;
|
||||||
|
mod util;
|
||||||
pub struct Stdin;
|
|
||||||
|
|
||||||
impl IoBase for Stdin {
|
|
||||||
type Error = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl io::Read for Stdin {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
|
|
||||||
unsafe { File::from_raw_fd(0).read(buf) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stdin() -> Stdin {
|
|
||||||
Stdin
|
|
||||||
}
|
|
||||||
|
|
||||||
// Part took from the real std
|
|
||||||
|
|
||||||
const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
|
const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
|
||||||
|
|
||||||
|
|
||||||
struct Guard<'a> {
|
struct Guard<'a> {
|
||||||
buf: &'a mut Vec<u8>,
|
buf: &'a mut Vec<u8>,
|
||||||
len: usize,
|
len: usize,
|
||||||
@@ -62,14 +346,30 @@ impl Drop for Guard<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Several `read_to_string` and `read_line` methods in the standard library will
|
||||||
|
// append data into a `String` buffer, but we need to be pretty careful when
|
||||||
|
// doing this. The implementation will just call `.as_mut_vec()` and then
|
||||||
|
// delegate to a byte-oriented reading method, but we must ensure that when
|
||||||
|
// returning we never leave `buf` in a state such that it contains invalid UTF-8
|
||||||
|
// in its bounds.
|
||||||
|
//
|
||||||
|
// To this end, we use an RAII guard (to protect against panics) which updates
|
||||||
|
// the length of the string when it is dropped. This guard initially truncates
|
||||||
|
// the string to the prior length and only after we've validated that the
|
||||||
|
// new contents are valid UTF-8 do we allow it to set a longer length.
|
||||||
|
//
|
||||||
|
// The unsafety in this function is twofold:
|
||||||
|
//
|
||||||
|
// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8
|
||||||
|
// checks.
|
||||||
|
// 2. We're passing a raw buffer to the function `f`, and it is expected that
|
||||||
|
// the function only *appends* bytes to the buffer. We'll get undefined
|
||||||
|
// behavior if existing bytes are overwritten to have non-UTF-8 data.
|
||||||
pub(crate) unsafe fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize>
|
pub(crate) unsafe fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Vec<u8>) -> Result<usize>,
|
F: FnOnce(&mut Vec<u8>) -> Result<usize>,
|
||||||
{
|
{
|
||||||
let mut g = Guard {
|
let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } };
|
||||||
len: buf.len(),
|
|
||||||
buf: unsafe { buf.as_mut_vec() },
|
|
||||||
};
|
|
||||||
let ret = f(g.buf);
|
let ret = f(g.buf);
|
||||||
|
|
||||||
// SAFETY: the caller promises to only append data to `buf`
|
// SAFETY: the caller promises to only append data to `buf`
|
||||||
@@ -101,10 +401,7 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
|
|||||||
// Optionally limit the maximum bytes read on each iteration.
|
// Optionally limit the maximum bytes read on each iteration.
|
||||||
// This adds an arbitrary fiddle factor to allow for more data than we expect.
|
// This adds an arbitrary fiddle factor to allow for more data than we expect.
|
||||||
let mut max_read_size = size_hint
|
let mut max_read_size = size_hint
|
||||||
.and_then(|s| {
|
.and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE))
|
||||||
s.checked_add(1024)?
|
|
||||||
.checked_next_multiple_of(DEFAULT_BUF_SIZE)
|
|
||||||
})
|
|
||||||
.unwrap_or(DEFAULT_BUF_SIZE);
|
.unwrap_or(DEFAULT_BUF_SIZE);
|
||||||
|
|
||||||
let mut initialized = 0; // Extra initialized bytes from previous loop iteration
|
let mut initialized = 0; // Extra initialized bytes from previous loop iteration
|
||||||
@@ -245,10 +542,7 @@ pub(crate) fn default_read_vectored<F>(read: F, bufs: &mut [IoSliceMut<'_>]) ->
|
|||||||
where
|
where
|
||||||
F: FnOnce(&mut [u8]) -> Result<usize>,
|
F: FnOnce(&mut [u8]) -> Result<usize>,
|
||||||
{
|
{
|
||||||
let buf = bufs
|
let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b);
|
||||||
.iter_mut()
|
|
||||||
.find(|b| !b.is_empty())
|
|
||||||
.map_or(&mut [][..], |b| &mut **b);
|
|
||||||
read(buf)
|
read(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,10 +550,7 @@ pub(crate) fn default_write_vectored<F>(write: F, bufs: &[IoSlice<'_>]) -> Resul
|
|||||||
where
|
where
|
||||||
F: FnOnce(&[u8]) -> Result<usize>,
|
F: FnOnce(&[u8]) -> Result<usize>,
|
||||||
{
|
{
|
||||||
let buf = bufs
|
let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b);
|
||||||
.iter()
|
|
||||||
.find(|b| !b.is_empty())
|
|
||||||
.map_or(&[][..], |b| &**b);
|
|
||||||
write(buf)
|
write(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,11 +565,7 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [
|
|||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !buf.is_empty() {
|
if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||||
Err(Error::READ_EXACT_EOF)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_read_buf<F>(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()>
|
pub(crate) fn default_read_buf<F>(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()>
|
||||||
@@ -333,10 +620,7 @@ pub(crate) fn default_write_fmt<W: Write + ?Sized>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output = Adapter {
|
let mut output = Adapter { inner: this, error: Ok(()) };
|
||||||
inner: this,
|
|
||||||
error: Ok(()),
|
|
||||||
};
|
|
||||||
match fmt::write(&mut output, args) {
|
match fmt::write(&mut output, args) {
|
||||||
Ok(()) => Ok(()),
|
Ok(()) => Ok(()),
|
||||||
Err(..) => {
|
Err(..) => {
|
||||||
@@ -354,6 +638,79 @@ pub(crate) fn default_write_fmt<W: Write + ?Sized>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The `Read` trait allows for reading bytes from a source.
|
||||||
|
///
|
||||||
|
/// Implementors of the `Read` trait are called 'readers'.
|
||||||
|
///
|
||||||
|
/// Readers are defined by one required method, [`read()`]. Each call to [`read()`]
|
||||||
|
/// will attempt to pull bytes from this source into a provided buffer. A
|
||||||
|
/// number of other methods are implemented in terms of [`read()`], giving
|
||||||
|
/// implementors a number of ways to read bytes while only needing to implement
|
||||||
|
/// a single method.
|
||||||
|
///
|
||||||
|
/// Readers are intended to be composable with one another. Many implementors
|
||||||
|
/// throughout [`std::io`] take and provide types which implement the `Read`
|
||||||
|
/// trait.
|
||||||
|
///
|
||||||
|
/// Please note that each call to [`read()`] may involve a system call, and
|
||||||
|
/// therefore, using something that implements [`BufRead`], such as
|
||||||
|
/// [`BufReader`], will be more efficient.
|
||||||
|
///
|
||||||
|
/// Repeated calls to the reader use the same cursor, so for example
|
||||||
|
/// calling `read_to_end` twice on a [`File`] will only return the file's
|
||||||
|
/// contents once. It's recommended to first call `rewind()` in that case.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// [`File`]s implement `Read`:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io;
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> io::Result<()> {
|
||||||
|
/// let mut f = File::open("foo.txt")?;
|
||||||
|
/// let mut buffer = [0; 10];
|
||||||
|
///
|
||||||
|
/// // read up to 10 bytes
|
||||||
|
/// f.read(&mut buffer)?;
|
||||||
|
///
|
||||||
|
/// let mut buffer = Vec::new();
|
||||||
|
/// // read the whole file
|
||||||
|
/// f.read_to_end(&mut buffer)?;
|
||||||
|
///
|
||||||
|
/// // read into a String, so that you don't need to do the conversion.
|
||||||
|
/// let mut buffer = String::new();
|
||||||
|
/// f.read_to_string(&mut buffer)?;
|
||||||
|
///
|
||||||
|
/// // and more! See the other methods for more details.
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use std::io;
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
///
|
||||||
|
/// fn main() -> io::Result<()> {
|
||||||
|
/// let mut b = "This string will be read".as_bytes();
|
||||||
|
/// let mut buffer = [0; 10];
|
||||||
|
///
|
||||||
|
/// // read up to 10 bytes
|
||||||
|
/// b.read(&mut buffer)?;
|
||||||
|
///
|
||||||
|
/// // etc... it works exactly as a File does!
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`read()`]: Read::read
|
||||||
|
/// [`&str`]: prim@str
|
||||||
|
/// [`std::io`]: self
|
||||||
|
/// [`File`]: crate::fs::File
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[doc(notable_trait)]
|
#[doc(notable_trait)]
|
||||||
#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")]
|
#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")]
|
||||||
@@ -451,8 +808,7 @@ pub trait Read {
|
|||||||
/// buffer provided, or an empty one if none exists.
|
/// buffer provided, or an empty one if none exists.
|
||||||
#[stable(feature = "iovec", since = "1.36.0")]
|
#[stable(feature = "iovec", since = "1.36.0")]
|
||||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
|
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
|
||||||
// default_read_vectored(|b| self.read(b), bufs)
|
default_read_vectored(|b| self.read(b), bufs)
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if this `Read`er has an efficient `read_vectored`
|
/// Determines if this `Read`er has an efficient `read_vectored`
|
||||||
@@ -562,8 +918,7 @@ pub trait Read {
|
|||||||
/// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve
|
/// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||||
// default_read_to_end(self, buf, None)
|
default_read_to_end(self, buf, None)
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads all bytes until EOF in this source, appending them to `buf`.
|
/// Reads all bytes until EOF in this source, appending them to `buf`.
|
||||||
@@ -619,8 +974,7 @@ pub trait Read {
|
|||||||
/// [`std::fs::read_to_string`]: crate::fs::read_to_string
|
/// [`std::fs::read_to_string`]: crate::fs::read_to_string
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
|
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
|
||||||
// default_read_to_string(self, buf, None)
|
default_read_to_string(self, buf, None)
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the exact number of bytes required to fill `buf`.
|
/// Reads the exact number of bytes required to fill `buf`.
|
||||||
@@ -673,8 +1027,7 @@ pub trait Read {
|
|||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "read_exact", since = "1.6.0")]
|
#[stable(feature = "read_exact", since = "1.6.0")]
|
||||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
|
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
|
||||||
// default_read_exact(self, buf)
|
default_read_exact(self, buf)
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pull some bytes from this source into the specified buffer.
|
/// Pull some bytes from this source into the specified buffer.
|
||||||
@@ -687,8 +1040,7 @@ pub trait Read {
|
|||||||
/// This method makes it possible to return both data and an error but it is advised against.
|
/// This method makes it possible to return both data and an error but it is advised against.
|
||||||
#[unstable(feature = "read_buf", issue = "78485")]
|
#[unstable(feature = "read_buf", issue = "78485")]
|
||||||
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
|
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
|
||||||
// default_read_buf(|b| self.read(b), buf)
|
default_read_buf(|b| self.read(b), buf)
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the exact number of bytes required to fill `cursor`.
|
/// Reads the exact number of bytes required to fill `cursor`.
|
||||||
@@ -711,8 +1063,7 @@ pub trait Read {
|
|||||||
/// If this function returns an error, all bytes read will be appended to `cursor`.
|
/// If this function returns an error, all bytes read will be appended to `cursor`.
|
||||||
#[unstable(feature = "read_buf", issue = "78485")]
|
#[unstable(feature = "read_buf", issue = "78485")]
|
||||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
|
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
|
||||||
// default_read_buf_exact(self, cursor)
|
default_read_buf_exact(self, cursor)
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a "by reference" adapter for this instance of `Read`.
|
/// Creates a "by reference" adapter for this instance of `Read`.
|
||||||
@@ -835,11 +1186,7 @@ pub trait Read {
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
Chain {
|
Chain { first: self, second: next, done_first: false }
|
||||||
first: self,
|
|
||||||
second: next,
|
|
||||||
done_first: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an adapter which will read at most `limit` bytes from it.
|
/// Creates an adapter which will read at most `limit` bytes from it.
|
||||||
@@ -878,11 +1225,7 @@ pub trait Read {
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
Take {
|
Take { inner: self, len: limit, limit }
|
||||||
inner: self,
|
|
||||||
len: limit,
|
|
||||||
limit,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read and return a fixed array of bytes from this source.
|
/// Read and return a fixed array of bytes from this source.
|
||||||
@@ -1936,6 +2279,55 @@ fn skip_until<R: BufRead + ?Sized>(r: &mut R, delim: u8) -> Result<usize> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it
|
||||||
|
/// to perform extra ways of reading.
|
||||||
|
///
|
||||||
|
/// For example, reading line-by-line is inefficient without using a buffer, so
|
||||||
|
/// if you want to read by line, you'll need `BufRead`, which includes a
|
||||||
|
/// [`read_line`] method as well as a [`lines`] iterator.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// A locked standard input implements `BufRead`:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io;
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
///
|
||||||
|
/// let stdin = io::stdin();
|
||||||
|
/// for line in stdin.lock().lines() {
|
||||||
|
/// println!("{}", line?);
|
||||||
|
/// }
|
||||||
|
/// # std::io::Result::Ok(())
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If you have something that implements [`Read`], you can use the [`BufReader`
|
||||||
|
/// type][`BufReader`] to turn it into a `BufRead`.
|
||||||
|
///
|
||||||
|
/// For example, [`File`] implements [`Read`], but not `BufRead`.
|
||||||
|
/// [`BufReader`] to the rescue!
|
||||||
|
///
|
||||||
|
/// [`File`]: crate::fs::File
|
||||||
|
/// [`read_line`]: BufRead::read_line
|
||||||
|
/// [`lines`]: BufRead::lines
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::{self, BufReader};
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// fn main() -> io::Result<()> {
|
||||||
|
/// let f = File::open("foo.txt")?;
|
||||||
|
/// let f = BufReader::new(f);
|
||||||
|
///
|
||||||
|
/// for line in f.lines() {
|
||||||
|
/// let line = line?;
|
||||||
|
/// println!("{line}");
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")]
|
#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")]
|
||||||
pub trait BufRead: Read {
|
pub trait BufRead: Read {
|
||||||
@@ -2259,10 +2651,7 @@ pub trait BufRead: Read {
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
Split {
|
Split { buf: self, delim: byte }
|
||||||
buf: self,
|
|
||||||
delim: byte,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the lines of this reader.
|
/// Returns an iterator over the lines of this reader.
|
||||||
@@ -2302,6 +2691,7 @@ pub trait BufRead: Read {
|
|||||||
Lines { buf: self }
|
Lines { buf: self }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adapter to chain together two readers.
|
/// Adapter to chain together two readers.
|
||||||
///
|
///
|
||||||
/// This struct is generally created by calling [`chain`] on a reader.
|
/// This struct is generally created by calling [`chain`] on a reader.
|
||||||
@@ -2467,11 +2857,7 @@ impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn consume(&mut self, amt: usize) {
|
fn consume(&mut self, amt: usize) {
|
||||||
if !self.done_first {
|
if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) }
|
||||||
self.first.consume(amt)
|
|
||||||
} else {
|
|
||||||
self.second.consume(amt)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> {
|
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> {
|
||||||
@@ -2501,15 +2887,13 @@ impl<T, U> SizeHint for Chain<T, U> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn upper_bound(&self) -> Option<usize> {
|
fn upper_bound(&self) -> Option<usize> {
|
||||||
match (
|
match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) {
|
||||||
SizeHint::upper_bound(&self.first),
|
|
||||||
SizeHint::upper_bound(&self.second),
|
|
||||||
) {
|
|
||||||
(Some(first), Some(second)) => first.checked_add(second),
|
(Some(first), Some(second)) => first.checked_add(second),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reader adapter which limits the bytes read from an underlying reader.
|
/// Reader adapter which limits the bytes read from an underlying reader.
|
||||||
///
|
///
|
||||||
/// This struct is generally created by calling [`take`] on a reader.
|
/// This struct is generally created by calling [`take`] on a reader.
|
||||||
@@ -2792,11 +3176,7 @@ impl<T: Seek> Seek for Take<T> {
|
|||||||
self.limit = self.limit.wrapping_sub(offset as u64);
|
self.limit = self.limit.wrapping_sub(offset as u64);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let offset = if new_position > self.position() {
|
let offset = if new_position > self.position() { i64::MAX } else { i64::MIN };
|
||||||
i64::MAX
|
|
||||||
} else {
|
|
||||||
i64::MIN
|
|
||||||
};
|
|
||||||
self.inner.seek_relative(offset)?;
|
self.inner.seek_relative(offset)?;
|
||||||
self.limit = self.limit.wrapping_sub(offset as u64);
|
self.limit = self.limit.wrapping_sub(offset as u64);
|
||||||
}
|
}
|
||||||
@@ -2812,11 +3192,7 @@ impl<T: Seek> Seek for Take<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn seek_relative(&mut self, offset: i64) -> Result<()> {
|
fn seek_relative(&mut self, offset: i64) -> Result<()> {
|
||||||
if !self
|
if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) {
|
||||||
.position()
|
|
||||||
.checked_add_signed(offset)
|
|
||||||
.is_some_and(|p| p <= self.len)
|
|
||||||
{
|
|
||||||
return Err(ErrorKind::InvalidInput.into());
|
return Err(ErrorKind::InvalidInput.into());
|
||||||
}
|
}
|
||||||
self.inner.seek_relative(offset)?;
|
self.inner.seek_relative(offset)?;
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
#![allow(missing_copy_implementations)]
|
#![allow(missing_copy_implementations)]
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::io::{
|
use crate::io::{
|
||||||
@@ -1,299 +1,70 @@
|
|||||||
#![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
pub mod error;
|
#![cfg_attr(not(feature = "std"), stable(feature = "rust1", since = "1.0.0"))]
|
||||||
|
#![cfg_attr(
|
||||||
|
not(feature = "std"),
|
||||||
|
allow(
|
||||||
|
internal_features,
|
||||||
|
incomplete_features,
|
||||||
|
clippy::all,
|
||||||
|
dead_code,
|
||||||
|
unused_imports
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
#![cfg_attr(
|
||||||
|
not(feature = "std"),
|
||||||
|
feature(
|
||||||
|
fmt_internals,
|
||||||
|
rustc_attrs,
|
||||||
|
decl_macro,
|
||||||
|
allow_internal_unstable,
|
||||||
|
staged_api,
|
||||||
|
core_io_borrowed_buf,
|
||||||
|
specialization,
|
||||||
|
prelude_import,
|
||||||
|
allocator_api,
|
||||||
|
slice_internals,
|
||||||
|
doc_notable_trait,
|
||||||
|
rustdoc_internals,
|
||||||
|
io_const_error,
|
||||||
|
maybe_uninit_array_assume_init,
|
||||||
|
try_with_capacity,
|
||||||
|
maybe_uninit_fill
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
|
||||||
use error::IoError;
|
#[cfg(not(feature = "std"))]
|
||||||
|
pub mod io;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
pub(crate) mod sys;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub use io::*;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(not(feature = "std"))]
|
||||||
extern crate alloc;
|
mod std_prelude {
|
||||||
|
pub(crate) use core::prelude::rust_2024::*;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[allow(unused_imports)]
|
||||||
use alloc::string::String;
|
pub(crate) use super::alloc_crate::{
|
||||||
#[cfg(feature = "alloc")]
|
boxed::Box,
|
||||||
use alloc::vec::Vec;
|
string::{String, ToString},
|
||||||
|
vec,
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[prelude_import]
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub(crate) use std_prelude::*;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
extern crate alloc as alloc_crate;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
pub(crate) use alloc_crate::alloc;
|
||||||
|
|
||||||
/// Provides IO error as an associated type.
|
#[cfg(not(feature = "std"))]
|
||||||
///
|
use alloc_crate::collections;
|
||||||
/// Must be implemented for all types that also implement at least one of the following traits: `Read`, `Write`,
|
#[cfg(not(feature = "std"))]
|
||||||
/// `Seek`.
|
use core::{cmp, error, fmt, hint, mem, ops, ptr, result, slice, str};
|
||||||
pub trait IoBase {
|
|
||||||
/// Type of errors returned by input/output operations.
|
|
||||||
type Error: IoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `Read` trait allows for reading bytes from a source.
|
#[cfg(feature = "std")]
|
||||||
///
|
pub use std::io::*;
|
||||||
/// It is based on the `std::io::Read` trait.
|
|
||||||
pub trait Read: IoBase {
|
|
||||||
/// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
|
|
||||||
///
|
|
||||||
/// This function does not provide any guarantees about whether it blocks waiting for data, but if an object needs
|
|
||||||
/// to block for a read and cannot, it will typically signal this via an Err return value.
|
|
||||||
///
|
|
||||||
/// If the return value of this method is `Ok(n)`, then it must be guaranteed that `0 <= n <= buf.len()`. A nonzero
|
|
||||||
/// `n` value indicates that the buffer buf has been filled in with n bytes of data from this source. If `n` is
|
|
||||||
/// `0`, then it can indicate one of two scenarios:
|
|
||||||
///
|
|
||||||
/// 1. This reader has reached its "end of file" and will likely no longer be able to produce bytes. Note that this
|
|
||||||
/// does not mean that the reader will always no longer be able to produce bytes.
|
|
||||||
/// 2. The buffer specified was 0 bytes in length.
|
|
||||||
///
|
|
||||||
/// It is not an error if the returned value `n` is smaller than the buffer size, even when the reader is not at
|
|
||||||
/// the end of the stream yet. This may happen for example because fewer bytes are actually available right now
|
|
||||||
/// (e. g. being close to end-of-file) or because `read()` was interrupted by a signal.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// If this function encounters any form of I/O or other error, an error will be returned. If an error is returned
|
|
||||||
/// then it must be guaranteed that no bytes were read.
|
|
||||||
/// An error for which `IoError::is_interrupted` returns true is non-fatal and the read operation should be retried
|
|
||||||
/// if there is nothing else to do.
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
|
|
||||||
|
|
||||||
/// Read the exact number of bytes required to fill `buf`.
|
|
||||||
///
|
|
||||||
/// This function reads as many bytes as necessary to completely fill the specified buffer `buf`.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// If this function encounters an error for which `IoError::is_interrupted` returns true then the error is ignored
|
|
||||||
/// and the operation will continue.
|
|
||||||
///
|
|
||||||
/// If this function encounters an end of file before completely filling the buffer, it returns an error
|
|
||||||
/// instantiated by a call to `IoError::new_unexpected_eof_error`. The contents of `buf` are unspecified in this
|
|
||||||
/// case.
|
|
||||||
///
|
|
||||||
/// If this function returns an error, it is unspecified how many bytes it has read, but it will never read more
|
|
||||||
/// than would be necessary to completely fill the buffer.
|
|
||||||
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
while !buf.is_empty() {
|
|
||||||
match self.read(buf) {
|
|
||||||
Ok(0) => break,
|
|
||||||
Ok(n) => {
|
|
||||||
let tmp = buf;
|
|
||||||
buf = &mut tmp[n..];
|
|
||||||
}
|
|
||||||
Err(ref e) if e.is_interrupted() => {}
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if buf.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Self::Error::new_unexpected_eof_error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Self::Error> {
|
|
||||||
const CHUNK_SIZE: usize = 32;
|
|
||||||
let start_len = buf.len();
|
|
||||||
loop {
|
|
||||||
let mut chunk_buf = [0; CHUNK_SIZE];
|
|
||||||
let read = self.read(&mut chunk_buf)?;
|
|
||||||
buf.extend_from_slice(&chunk_buf[..read]);
|
|
||||||
|
|
||||||
if read == 0 {
|
|
||||||
return Ok(buf.len() - start_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
fn read_to_string(&mut self, buf: &mut String) -> Result<usize, Self::Error> {
|
|
||||||
let read = self.read_to_end(unsafe { buf.as_mut_vec() })?;
|
|
||||||
|
|
||||||
if str::from_utf8(buf.as_bytes()).is_err() {
|
|
||||||
Err(Self::Error::new_invalid_utf8_error())
|
|
||||||
} else {
|
|
||||||
Ok(read)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `Write` trait allows for writing bytes into the sink.
|
|
||||||
///
|
|
||||||
/// It is based on the `std::io::Write` trait.
|
|
||||||
pub trait Write: IoBase {
|
|
||||||
/// Write a buffer into this writer, returning how many bytes were written.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Each call to write may generate an I/O error indicating that the operation could not be completed. If an error
|
|
||||||
/// is returned then no bytes in the buffer were written to this writer.
|
|
||||||
/// It is not considered an error if the entire buffer could not be written to this writer.
|
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error>;
|
|
||||||
|
|
||||||
/// Attempts to write an entire buffer into this writer.
|
|
||||||
///
|
|
||||||
/// This method will continuously call `write` until there is no more data to be written or an error is returned.
|
|
||||||
/// Errors for which `IoError::is_interrupted` method returns true are being skipped. This method will not return
|
|
||||||
/// until the entire buffer has been successfully written or such an error occurs.
|
|
||||||
/// If `write` returns 0 before the entire buffer has been written this method will return an error instantiated by
|
|
||||||
/// a call to `IoError::new_write_zero_error`.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// This function will return the first error for which `IoError::is_interrupted` method returns false that `write`
|
|
||||||
/// returns.
|
|
||||||
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
while !buf.is_empty() {
|
|
||||||
match self.write(buf) {
|
|
||||||
Ok(0) => {
|
|
||||||
return Err(Self::Error::new_write_zero_error());
|
|
||||||
}
|
|
||||||
Ok(n) => buf = &buf[n..],
|
|
||||||
Err(ref e) if e.is_interrupted() => {}
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// It is considered an error if not all bytes could be written due to I/O errors or EOF being reached.
|
|
||||||
fn flush(&mut self) -> Result<(), Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enumeration of possible methods to seek within an I/O object.
|
|
||||||
///
|
|
||||||
/// It is based on the `std::io::SeekFrom` enum.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum SeekFrom {
|
|
||||||
/// Sets the offset to the provided number of bytes.
|
|
||||||
Start(u64),
|
|
||||||
/// Sets the offset to the size of this object plus the specified number of bytes.
|
|
||||||
End(i64),
|
|
||||||
/// Sets the offset to the current position plus the specified number of bytes.
|
|
||||||
Current(i64),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `Seek` trait provides a cursor which can be moved within a stream of bytes.
|
|
||||||
///
|
|
||||||
/// It is based on the `std::io::Seek` trait.
|
|
||||||
pub trait Seek: IoBase {
|
|
||||||
/// Seek to an offset, in bytes, in a stream.
|
|
||||||
///
|
|
||||||
/// A seek beyond the end of a stream or to a negative position is not allowed.
|
|
||||||
///
|
|
||||||
/// If the seek operation completed successfully, this method returns the new position from the start of the
|
|
||||||
/// stream. That position can be used later with `SeekFrom::Start`.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Seeking to a negative offset is considered an error.
|
|
||||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
||||||
impl From<SeekFrom> for std::io::SeekFrom {
|
|
||||||
fn from(from: SeekFrom) -> Self {
|
|
||||||
match from {
|
|
||||||
SeekFrom::Start(n) => std::io::SeekFrom::Start(n),
|
|
||||||
SeekFrom::End(n) => std::io::SeekFrom::End(n),
|
|
||||||
SeekFrom::Current(n) => std::io::SeekFrom::Current(n),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
||||||
impl From<std::io::SeekFrom> for SeekFrom {
|
|
||||||
fn from(from: std::io::SeekFrom) -> Self {
|
|
||||||
match from {
|
|
||||||
std::io::SeekFrom::Start(n) => SeekFrom::Start(n),
|
|
||||||
std::io::SeekFrom::End(n) => SeekFrom::End(n),
|
|
||||||
std::io::SeekFrom::Current(n) => SeekFrom::Current(n),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
||||||
impl IoBase for std::fs::File {
|
|
||||||
type Error = std::io::Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
||||||
impl<T: std::io::Read + IoBase<Error = std::io::Error>> Read for T {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
|
||||||
self.read(buf).map_err(Error::Io)
|
|
||||||
}
|
|
||||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.read_exact(buf).map_err(Error::Io)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
||||||
impl<T: std::io::Write + IoBase<Error = std::io::Error>> Write for T {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
|
||||||
self.write(buf).map_err(Error::Io)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.write_all(buf).map_err(Error::Io)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
|
||||||
self.flush().map_err(Error::Io)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
|
||||||
impl<T: std::io::Seek + IoBase<Error = std::io::Error>> Seek for T {
|
|
||||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
|
|
||||||
self.seek(pos.into()).map_err(Error::Io)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ReadLeExt {
|
|
||||||
type Error;
|
|
||||||
fn read_u8(&mut self) -> Result<u8, Self::Error>;
|
|
||||||
fn read_u16_le(&mut self) -> Result<u16, Self::Error>;
|
|
||||||
fn read_u32_le(&mut self) -> Result<u32, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Read> ReadLeExt for T {
|
|
||||||
type Error = <Self as IoBase>::Error;
|
|
||||||
|
|
||||||
fn read_u8(&mut self) -> Result<u8, Self::Error> {
|
|
||||||
let mut buf = [0_u8; 1];
|
|
||||||
self.read_exact(&mut buf)?;
|
|
||||||
Ok(buf[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_u16_le(&mut self) -> Result<u16, Self::Error> {
|
|
||||||
let mut buf = [0_u8; 2];
|
|
||||||
self.read_exact(&mut buf)?;
|
|
||||||
Ok(u16::from_le_bytes(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_u32_le(&mut self) -> Result<u32, Self::Error> {
|
|
||||||
let mut buf = [0_u8; 4];
|
|
||||||
self.read_exact(&mut buf)?;
|
|
||||||
Ok(u32::from_le_bytes(buf))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) trait WriteLeExt {
|
|
||||||
type Error;
|
|
||||||
fn write_u8(&mut self, n: u8) -> Result<(), Self::Error>;
|
|
||||||
fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error>;
|
|
||||||
fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Write> WriteLeExt for T {
|
|
||||||
type Error = <Self as IoBase>::Error;
|
|
||||||
|
|
||||||
fn write_u8(&mut self, n: u8) -> Result<(), Self::Error> {
|
|
||||||
self.write_all(&[n])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error> {
|
|
||||||
self.write_all(&n.to_le_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error> {
|
|
||||||
self.write_all(&n.to_le_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
1
crates/io/src/sys.rs
Normal file
1
crates/io/src/sys.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub(crate) mod io;
|
||||||
4
crates/io/src/sys/io/error/mod.rs
Normal file
4
crates/io/src/sys/io/error/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
mod generic;
|
||||||
|
pub use generic::*;
|
||||||
|
|
||||||
|
pub type RawOsError = i32;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::mem;
|
use core::mem;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct IoSlice<'a>(&'a [u8]);
|
pub struct IoSlice<'a>(&'a [u8]);
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
|
|
||||||
pub enum CopyState {
|
pub enum CopyState {
|
||||||
#[cfg_attr(not(any(target_os = "linux", target_os = "android")), expect(dead_code))]
|
#[cfg_attr(not(any(target_os = "linux", target_os = "android")), expect(dead_code))]
|
||||||
Ended(u64),
|
Ended(u64),
|
||||||
Fallback(u64),
|
Fallback(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_select! {
|
|
||||||
any(target_os = "linux", target_os = "android") => {
|
|
||||||
mod linux;
|
|
||||||
pub use linux::kernel_copy;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
use crate::io::{Result, Read, Write};
|
use crate::io::{Result, Read, Write};
|
||||||
|
|
||||||
pub fn kernel_copy<R: ?Sized, W: ?Sized>(_reader: &mut R, _writer: &mut W) -> Result<CopyState>
|
pub fn kernel_copy<R: ?Sized, W: ?Sized>(_reader: &mut R, _writer: &mut W) -> Result<CopyState>
|
||||||
@@ -19,5 +14,3 @@ cfg_select! {
|
|||||||
{
|
{
|
||||||
Ok(CopyState::Fallback(0))
|
Ok(CopyState::Fallback(0))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
26
crates/io/src/sys/io/mod.rs
Normal file
26
crates/io/src/sys/io/mod.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
mod error;
|
||||||
|
|
||||||
|
mod io_slice {
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod is_terminal {
|
||||||
|
mod unsupported;
|
||||||
|
pub use unsupported::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod kernel_copy;
|
||||||
|
|
||||||
|
pub use error::{RawOsError, decode_error_kind, errno, error_string, is_interrupted};
|
||||||
|
pub use io_slice::{IoSlice, IoSliceMut};
|
||||||
|
pub use is_terminal::is_terminal;
|
||||||
|
pub use kernel_copy::{CopyState, kernel_copy};
|
||||||
|
|
||||||
|
// Bare metal platforms usually have very small amounts of RAM
|
||||||
|
// (in the order of hundreds of KB)
|
||||||
|
pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") {
|
||||||
|
512
|
||||||
|
} else {
|
||||||
|
8 * 1024
|
||||||
|
};
|
||||||
@@ -7,6 +7,6 @@ edition = "2024"
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
image = "0.25"
|
image = { version = "0.25", default-features = false, features = ["png"] }
|
||||||
syn = { version = "2", features = ["full"] }
|
syn = { version = "2", features = ["full"] }
|
||||||
zyn = "0.5"
|
zyn = "0.5"
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "os-std-macros"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro2 = "1"
|
|
||||||
quote = "1"
|
|
||||||
syn = { version = "2", features = ["full"] }
|
|
||||||
@@ -5,7 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bffs = { path = "../bffs" }
|
bffs = { path = "../bffs" }
|
||||||
io = { path = "../io" }
|
io = { package = "no-std-io", path = "../io" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
kernel = []
|
kernel = []
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use io::{IoBase, Read, Write};
|
use io::{Read, Write};
|
||||||
|
|
||||||
use crate::syscall;
|
use crate::syscall;
|
||||||
|
|
||||||
@@ -19,22 +19,18 @@ impl File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoBase for File {
|
|
||||||
type Error = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for File {
|
impl Read for File {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||||
Ok(syscall::read(self.as_fd(), buf) as usize)
|
Ok(syscall::read(self.as_fd(), buf) as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for File {
|
impl Write for File {
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||||
Ok(syscall::write(self.as_fd(), buf) as usize)
|
Ok(syscall::write(self.as_fd(), buf) as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
fn flush(&mut self) -> Result<(), io::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
use core::{alloc::Layout, time::Duration};
|
use core::{alloc::Layout, time::Duration};
|
||||||
|
|
||||||
use bffs::path::Path;
|
|
||||||
use io::SeekFrom;
|
|
||||||
|
|
||||||
use crate::fs::File;
|
|
||||||
|
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
pub enum SysCall {
|
pub enum SysCall {
|
||||||
Read = 0,
|
Read = 0,
|
||||||
@@ -18,8 +13,7 @@ pub enum SysCall {
|
|||||||
ExecVE = 59,
|
ExecVE = 59,
|
||||||
Exit = 60,
|
Exit = 60,
|
||||||
NanoSleep = 101,
|
NanoSleep = 101,
|
||||||
WriteIntTemp = 998,
|
WaitPid = 247,
|
||||||
WriteTemp = 999,
|
|
||||||
Unimplemented = 1 << 31,
|
Unimplemented = 1 << 31,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +31,7 @@ impl From<u64> for SysCall {
|
|||||||
59 => SysCall::ExecVE,
|
59 => SysCall::ExecVE,
|
||||||
60 => SysCall::Exit,
|
60 => SysCall::Exit,
|
||||||
101 => SysCall::NanoSleep,
|
101 => SysCall::NanoSleep,
|
||||||
998 => SysCall::WriteIntTemp,
|
247 => SysCall::WaitPid,
|
||||||
999 => SysCall::WriteTemp,
|
|
||||||
_ => SysCall::Unimplemented,
|
_ => SysCall::Unimplemented,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,10 +93,12 @@ macro_rules! syscall {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exit() {
|
pub fn exit() -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
syscall!(SysCall::Exit);
|
syscall!(SysCall::Exit);
|
||||||
}
|
}
|
||||||
|
#[allow(clippy::empty_loop)]
|
||||||
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sleep(duration: Duration) {
|
pub fn sleep(duration: Duration) {
|
||||||
@@ -113,21 +108,8 @@ pub fn sleep(duration: Duration) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_string_temp(content: &str) {
|
#[allow(unknown_lints)]
|
||||||
unsafe {
|
#[allow(fuzzy_provenance_casts)]
|
||||||
syscall!(
|
|
||||||
SysCall::WriteTemp,
|
|
||||||
content.as_ptr() as u64,
|
|
||||||
content.len() as u64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn write_int_temp(content: u64) {
|
|
||||||
unsafe {
|
|
||||||
syscall!(SysCall::WriteIntTemp, content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn alloc(layout: Layout) -> *mut u8 {
|
pub fn alloc(layout: Layout) -> *mut u8 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let size = layout.size();
|
let size = layout.size();
|
||||||
@@ -143,13 +125,12 @@ pub fn dealloc(ptr: *mut u8, layout: core::alloc::Layout) {
|
|||||||
syscall!(SysCall::Dealloc, ptr as u64, size as u64, align as u64);
|
syscall!(SysCall::Dealloc, ptr as u64, size as u64, align as u64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn open<P: AsRef<Path>>(path: P) -> File {
|
pub fn open(path: &str) -> u64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let path_str = path.as_ref().as_str();
|
let ptr = path.as_ptr();
|
||||||
let ptr = path_str.as_ptr();
|
let size = path.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::from_raw_fd(fd)
|
fd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn close(file_descriptor: u64) {
|
pub fn close(file_descriptor: u64) {
|
||||||
@@ -173,29 +154,36 @@ pub fn read(file_descriptor: u64, buf: &mut [u8]) -> u64 {
|
|||||||
len
|
len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn seek(file_descriptor: u64, seek: SeekFrom) {
|
/// seek_type: 0 -> start, 1 -> end, 2 -> current
|
||||||
|
pub fn seek(file_descriptor: u64, seek_type: u8, seek: u64) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let (discriminant, value) = match seek {
|
syscall!(SysCall::Seek, file_descriptor, seek_type as u64, seek);
|
||||||
SeekFrom::Start(v) => (0, v),
|
|
||||||
SeekFrom::End(v) => (1, v as u64),
|
|
||||||
SeekFrom::Current(v) => (2, v as u64),
|
|
||||||
};
|
|
||||||
syscall!(SysCall::Seek, file_descriptor, discriminant, value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn spawn<P: AsRef<Path>>(path: P) {
|
pub fn spawn(path: &str, argc: isize, argv: *const *const u8) -> u64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let path_str = path.as_ref().as_str();
|
let ptr = path.as_ptr();
|
||||||
let ptr = path_str.as_ptr();
|
let size = path.len();
|
||||||
let size = path_str.len();
|
let (pid, ..) = syscall!(
|
||||||
syscall!(SysCall::Spawn, ptr as u64, size as u64);
|
SysCall::Spawn,
|
||||||
|
ptr as u64,
|
||||||
|
size as u64,
|
||||||
|
argc as u64,
|
||||||
|
argv as u64
|
||||||
|
);
|
||||||
|
pid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn execve<P: AsRef<Path>>(path: P) {
|
pub fn execve(path: &str) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let path_str = path.as_ref().as_str();
|
let ptr = path.as_ptr();
|
||||||
let ptr = path_str.as_ptr();
|
let size = path.len();
|
||||||
let size = path_str.len();
|
|
||||||
syscall!(SysCall::ExecVE, ptr as u64, size as u64);
|
syscall!(SysCall::ExecVE, ptr as u64, size as u64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn waitpid(pid: usize) {
|
||||||
|
unsafe {
|
||||||
|
syscall!(SysCall::WaitPid, pid as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "std"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
hashbrown = "0.16"
|
|
||||||
os-std-macros = { path = "../os-std-macros" }
|
|
||||||
shared = { path = "../shared", features = ["user"] }
|
|
||||||
io = { path = "../io", features = ["alloc"] }
|
|
||||||
@@ -1,490 +0,0 @@
|
|||||||
//! Memory allocation APIs.
|
|
||||||
//!
|
|
||||||
//! In a given program, the standard library has one “global” memory allocator
|
|
||||||
//! that is used for example by `Box<T>` and `Vec<T>`.
|
|
||||||
//!
|
|
||||||
//! Currently the default global allocator is unspecified. Libraries, however,
|
|
||||||
//! like `cdylib`s and `staticlib`s are guaranteed to use the [`System`] by
|
|
||||||
//! default.
|
|
||||||
//!
|
|
||||||
//! # The `#[global_allocator]` attribute
|
|
||||||
//!
|
|
||||||
//! This attribute allows configuring the choice of global allocator.
|
|
||||||
//! You can use this to implement a completely custom global allocator
|
|
||||||
//! to route all[^system-alloc] default allocation requests to a custom object.
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! use std::alloc::{GlobalAlloc, System, Layout};
|
|
||||||
//!
|
|
||||||
//! struct MyAllocator;
|
|
||||||
//!
|
|
||||||
//! unsafe impl GlobalAlloc for MyAllocator {
|
|
||||||
//! unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
||||||
//! unsafe { System.alloc(layout) }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
|
||||||
//! unsafe { System.dealloc(ptr, layout) }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[global_allocator]
|
|
||||||
//! static GLOBAL: MyAllocator = MyAllocator;
|
|
||||||
//!
|
|
||||||
//! fn main() {
|
|
||||||
//! // This `Vec` will allocate memory through `GLOBAL` above
|
|
||||||
//! let mut v = Vec::new();
|
|
||||||
//! v.push(1);
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! The attribute is used on a `static` item whose type implements the
|
|
||||||
//! [`GlobalAlloc`] trait. This type can be provided by an external library:
|
|
||||||
//!
|
|
||||||
//! ```rust,ignore (demonstrates crates.io usage)
|
|
||||||
//! use jemallocator::Jemalloc;
|
|
||||||
//!
|
|
||||||
//! #[global_allocator]
|
|
||||||
//! static GLOBAL: Jemalloc = Jemalloc;
|
|
||||||
//!
|
|
||||||
//! fn main() {}
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! The `#[global_allocator]` can only be used once in a crate
|
|
||||||
//! or its recursive dependencies.
|
|
||||||
//!
|
|
||||||
//! [^system-alloc]: Note that the Rust standard library internals may still
|
|
||||||
//! directly call [`System`] when necessary (for example for the runtime
|
|
||||||
//! support typically required to implement a global allocator, see [re-entrance] on [`GlobalAlloc`]
|
|
||||||
//! for more details).
|
|
||||||
//!
|
|
||||||
//! [re-entrance]: trait.GlobalAlloc.html#re-entrance
|
|
||||||
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
#![stable(feature = "alloc_module", since = "1.28.0")]
|
|
||||||
|
|
||||||
use core::ptr::NonNull;
|
|
||||||
use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
|
|
||||||
use core::{hint, mem, ptr};
|
|
||||||
|
|
||||||
#[stable(feature = "alloc_module", since = "1.28.0")]
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use alloc_crate::alloc::*;
|
|
||||||
|
|
||||||
/// The default memory allocator provided by the operating system.
|
|
||||||
///
|
|
||||||
/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
|
|
||||||
/// plus related functions. However, it is not valid to mix use of the backing
|
|
||||||
/// system allocator with `System`, as this implementation may include extra
|
|
||||||
/// work, such as to serve alignment requests greater than the alignment
|
|
||||||
/// provided directly by the backing system allocator.
|
|
||||||
///
|
|
||||||
/// This type implements the [`GlobalAlloc`] trait. Currently the default
|
|
||||||
/// global allocator is unspecified. Libraries, however, like `cdylib`s and
|
|
||||||
/// `staticlib`s are guaranteed to use the [`System`] by default and as such
|
|
||||||
/// work as if they had this definition:
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use std::alloc::System;
|
|
||||||
///
|
|
||||||
/// #[global_allocator]
|
|
||||||
/// static A: System = System;
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// let a = Box::new(4); // Allocates from the system allocator.
|
|
||||||
/// println!("{a}");
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// You can also define your own wrapper around `System` if you'd like, such as
|
|
||||||
/// keeping track of the number of all bytes allocated:
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use std::alloc::{System, GlobalAlloc, Layout};
|
|
||||||
/// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
|
|
||||||
///
|
|
||||||
/// struct Counter;
|
|
||||||
///
|
|
||||||
/// static ALLOCATED: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
///
|
|
||||||
/// unsafe impl GlobalAlloc for Counter {
|
|
||||||
/// unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
||||||
/// let ret = unsafe { System.alloc(layout) };
|
|
||||||
/// if !ret.is_null() {
|
|
||||||
/// ALLOCATED.fetch_add(layout.size(), Relaxed);
|
|
||||||
/// }
|
|
||||||
/// ret
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
|
||||||
/// unsafe { System.dealloc(ptr, layout); }
|
|
||||||
/// ALLOCATED.fetch_sub(layout.size(), Relaxed);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[global_allocator]
|
|
||||||
/// static A: Counter = Counter;
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// println!("allocated bytes before main: {}", ALLOCATED.load(Relaxed));
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// It can also be used directly to allocate memory independently of whatever
|
|
||||||
/// global allocator has been selected for a Rust program. For example if a Rust
|
|
||||||
/// program opts in to using jemalloc as the global allocator, `System` will
|
|
||||||
/// still allocate memory using `malloc` and `HeapAlloc`.
|
|
||||||
#[stable(feature = "alloc_system_type", since = "1.28.0")]
|
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
|
||||||
pub struct System;
|
|
||||||
|
|
||||||
impl System {
|
|
||||||
#[inline]
|
|
||||||
fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocError> {
|
|
||||||
match layout.size() {
|
|
||||||
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling_ptr(), 0)),
|
|
||||||
// SAFETY: `layout` is non-zero in size,
|
|
||||||
size => unsafe {
|
|
||||||
let raw_ptr = if zeroed {
|
|
||||||
GlobalAlloc::alloc_zeroed(self, layout)
|
|
||||||
} else {
|
|
||||||
GlobalAlloc::alloc(self, layout)
|
|
||||||
};
|
|
||||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
|
||||||
Ok(NonNull::slice_from_raw_parts(ptr, size))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: Same as `Allocator::grow`
|
|
||||||
#[inline]
|
|
||||||
unsafe fn grow_impl(
|
|
||||||
&self,
|
|
||||||
ptr: NonNull<u8>,
|
|
||||||
old_layout: Layout,
|
|
||||||
new_layout: Layout,
|
|
||||||
zeroed: bool,
|
|
||||||
) -> Result<NonNull<[u8]>, AllocError> {
|
|
||||||
debug_assert!(
|
|
||||||
new_layout.size() >= old_layout.size(),
|
|
||||||
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
|
|
||||||
);
|
|
||||||
|
|
||||||
match old_layout.size() {
|
|
||||||
0 => self.alloc_impl(new_layout, zeroed),
|
|
||||||
|
|
||||||
// SAFETY: `new_size` is non-zero as `new_size` is greater than or equal to `old_size`
|
|
||||||
// as required by safety conditions and the `old_size == 0` case was handled in the
|
|
||||||
// previous match arm. Other conditions must be upheld by the caller
|
|
||||||
old_size if old_layout.align() == new_layout.align() => unsafe {
|
|
||||||
let new_size = new_layout.size();
|
|
||||||
|
|
||||||
// `realloc` probably checks for `new_size >= old_layout.size()` or something similar.
|
|
||||||
hint::assert_unchecked(new_size >= old_layout.size());
|
|
||||||
|
|
||||||
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
|
|
||||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
|
||||||
if zeroed {
|
|
||||||
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
|
|
||||||
}
|
|
||||||
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
|
|
||||||
},
|
|
||||||
|
|
||||||
// SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
|
|
||||||
// both the old and new memory allocation are valid for reads and writes for `old_size`
|
|
||||||
// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
|
|
||||||
// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
|
|
||||||
// for `dealloc` must be upheld by the caller.
|
|
||||||
old_size => unsafe {
|
|
||||||
let new_ptr = self.alloc_impl(new_layout, zeroed)?;
|
|
||||||
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
|
|
||||||
Allocator::deallocate(self, ptr, old_layout);
|
|
||||||
Ok(new_ptr)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Allocator impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl,
|
|
||||||
// which is in `std::sys::*::alloc`.
|
|
||||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
|
||||||
unsafe impl Allocator for System {
|
|
||||||
#[inline]
|
|
||||||
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
|
||||||
self.alloc_impl(layout, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
|
||||||
self.alloc_impl(layout, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
|
||||||
if layout.size() != 0 {
|
|
||||||
// SAFETY: `layout` is non-zero in size,
|
|
||||||
// other conditions must be upheld by the caller
|
|
||||||
unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn grow(
|
|
||||||
&self,
|
|
||||||
ptr: NonNull<u8>,
|
|
||||||
old_layout: Layout,
|
|
||||||
new_layout: Layout,
|
|
||||||
) -> Result<NonNull<[u8]>, AllocError> {
|
|
||||||
// SAFETY: all conditions must be upheld by the caller
|
|
||||||
unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn grow_zeroed(
|
|
||||||
&self,
|
|
||||||
ptr: NonNull<u8>,
|
|
||||||
old_layout: Layout,
|
|
||||||
new_layout: Layout,
|
|
||||||
) -> Result<NonNull<[u8]>, AllocError> {
|
|
||||||
// SAFETY: all conditions must be upheld by the caller
|
|
||||||
unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn shrink(
|
|
||||||
&self,
|
|
||||||
ptr: NonNull<u8>,
|
|
||||||
old_layout: Layout,
|
|
||||||
new_layout: Layout,
|
|
||||||
) -> Result<NonNull<[u8]>, AllocError> {
|
|
||||||
debug_assert!(
|
|
||||||
new_layout.size() <= old_layout.size(),
|
|
||||||
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
|
|
||||||
);
|
|
||||||
|
|
||||||
match new_layout.size() {
|
|
||||||
// SAFETY: conditions must be upheld by the caller
|
|
||||||
0 => unsafe {
|
|
||||||
Allocator::deallocate(self, ptr, old_layout);
|
|
||||||
Ok(NonNull::slice_from_raw_parts(new_layout.dangling_ptr(), 0))
|
|
||||||
},
|
|
||||||
|
|
||||||
// SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
|
|
||||||
new_size if old_layout.align() == new_layout.align() => unsafe {
|
|
||||||
// `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
|
|
||||||
hint::assert_unchecked(new_size <= old_layout.size());
|
|
||||||
|
|
||||||
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
|
|
||||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
|
||||||
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
|
|
||||||
},
|
|
||||||
|
|
||||||
// SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
|
|
||||||
// both the old and new memory allocation are valid for reads and writes for `new_size`
|
|
||||||
// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
|
|
||||||
// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
|
|
||||||
// for `dealloc` must be upheld by the caller.
|
|
||||||
new_size => unsafe {
|
|
||||||
let new_ptr = Allocator::allocate(self, new_layout)?;
|
|
||||||
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
|
|
||||||
Allocator::deallocate(self, ptr, old_layout);
|
|
||||||
Ok(new_ptr)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
|
|
||||||
|
|
||||||
/// Registers a custom allocation error hook, replacing any that was previously registered.
|
|
||||||
///
|
|
||||||
/// The allocation error hook is invoked when an infallible memory allocation fails — that is,
|
|
||||||
/// as a consequence of calling [`handle_alloc_error`] — before the runtime aborts.
|
|
||||||
///
|
|
||||||
/// The allocation error hook is a global resource. [`take_alloc_error_hook`] may be used to
|
|
||||||
/// retrieve a previously registered hook and wrap or discard it.
|
|
||||||
///
|
|
||||||
/// # What the provided `hook` function should expect
|
|
||||||
///
|
|
||||||
/// The hook function is provided with a [`Layout`] struct which contains information
|
|
||||||
/// about the allocation that failed.
|
|
||||||
///
|
|
||||||
/// The hook function may choose to panic or abort; in the event that it returns normally, this
|
|
||||||
/// will cause an immediate abort.
|
|
||||||
///
|
|
||||||
/// Since [`take_alloc_error_hook`] is a safe function that allows retrieving the hook, the hook
|
|
||||||
/// function must be _sound_ to call even if no memory allocations were attempted.
|
|
||||||
///
|
|
||||||
/// # The default hook
|
|
||||||
///
|
|
||||||
/// The default hook, used if [`set_alloc_error_hook`] is never called, prints a message to
|
|
||||||
/// standard error (and then returns, causing the runtime to abort the process).
|
|
||||||
/// Compiler options may cause it to panic instead, and the default behavior may be changed
|
|
||||||
/// to panicking in future versions of Rust.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// #![feature(alloc_error_hook)]
|
|
||||||
///
|
|
||||||
/// use std::alloc::{Layout, set_alloc_error_hook};
|
|
||||||
///
|
|
||||||
/// fn custom_alloc_error_hook(layout: Layout) {
|
|
||||||
/// panic!("memory allocation of {} bytes failed", layout.size());
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// set_alloc_error_hook(custom_alloc_error_hook);
|
|
||||||
/// ```
|
|
||||||
#[unstable(feature = "alloc_error_hook", issue = "51245")]
|
|
||||||
pub fn set_alloc_error_hook(hook: fn(Layout)) {
|
|
||||||
HOOK.store(hook as *mut (), Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
// /// Unregisters the current allocation error hook, returning it.
|
|
||||||
// ///
|
|
||||||
// /// *See also the function [`set_alloc_error_hook`].*
|
|
||||||
// ///
|
|
||||||
// /// If no custom hook is registered, the default hook will be returned.
|
|
||||||
// #[unstable(feature = "alloc_error_hook", issue = "51245")]
|
|
||||||
// pub fn take_alloc_error_hook() -> fn(Layout) {
|
|
||||||
// let hook = HOOK.swap(ptr::null_mut(), Ordering::Acquire);
|
|
||||||
// if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[optimize(size)]
|
|
||||||
// fn default_alloc_error_hook(layout: Layout) {
|
|
||||||
// if cfg!(panic = "immediate-abort") {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // This is the default path taken on OOM, and the only path taken on stable with std.
|
|
||||||
// // Crucially, it does *not* call any user-defined code, and therefore users do not have to
|
|
||||||
// // worry about allocation failure causing reentrancy issues. That makes it different from
|
|
||||||
// // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error
|
|
||||||
// // handler that is called when there is no `#[alloc_error_handler]`), which triggers a
|
|
||||||
// // regular panic and thus can invoke a user-defined panic hook, executing arbitrary
|
|
||||||
// // user-defined code.
|
|
||||||
|
|
||||||
// static PREV_ALLOC_FAILURE: AtomicBool = AtomicBool::new(false);
|
|
||||||
// if PREV_ALLOC_FAILURE.swap(true, Ordering::Relaxed) {
|
|
||||||
// // Don't try to print a backtrace if a previous alloc error happened. This likely means
|
|
||||||
// // there is not enough memory to print a backtrace, although it could also mean that two
|
|
||||||
// // threads concurrently run out of memory.
|
|
||||||
// rtprintpanic!(
|
|
||||||
// "memory allocation of {} bytes failed\nskipping backtrace printing to avoid potential recursion\n",
|
|
||||||
// layout.size()
|
|
||||||
// );
|
|
||||||
// return;
|
|
||||||
// } else {
|
|
||||||
// rtprintpanic!("memory allocation of {} bytes failed\n", layout.size());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let Some(mut out) = crate::sys::stdio::panic_output() else {
|
|
||||||
// return;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Use a lock to prevent mixed output in multithreading context.
|
|
||||||
// // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
|
|
||||||
// // Make sure to not take this lock until after checking PREV_ALLOC_FAILURE to avoid deadlocks
|
|
||||||
// // when there is too little memory to print a backtrace.
|
|
||||||
// let mut lock = crate::sys::backtrace::lock();
|
|
||||||
|
|
||||||
// match crate::panic::get_backtrace_style() {
|
|
||||||
// Some(crate::panic::BacktraceStyle::Short) => {
|
|
||||||
// drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Short))
|
|
||||||
// }
|
|
||||||
// Some(crate::panic::BacktraceStyle::Full) => {
|
|
||||||
// drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Full))
|
|
||||||
// }
|
|
||||||
// Some(crate::panic::BacktraceStyle::Off) => {
|
|
||||||
// use crate::io::Write;
|
|
||||||
// let _ = writeln!(
|
|
||||||
// out,
|
|
||||||
// "note: run with `RUST_BACKTRACE=1` environment variable to display a \
|
|
||||||
// backtrace"
|
|
||||||
// );
|
|
||||||
// if cfg!(miri) {
|
|
||||||
// let _ = writeln!(
|
|
||||||
// out,
|
|
||||||
// "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \
|
|
||||||
// for the environment variable to have an effect"
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // If backtraces aren't supported or are forced-off, do nothing.
|
|
||||||
// None => {}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[cfg(not(test))]
|
|
||||||
// #[doc(hidden)]
|
|
||||||
// #[alloc_error_handler]
|
|
||||||
// #[unstable(feature = "alloc_internals", issue = "none")]
|
|
||||||
// pub fn rust_oom(layout: Layout) -> ! {
|
|
||||||
// crate::sys::backtrace::__rust_end_short_backtrace(|| {
|
|
||||||
// let hook = HOOK.load(Ordering::Acquire);
|
|
||||||
// let hook: fn(Layout) =
|
|
||||||
// if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } };
|
|
||||||
// hook(layout);
|
|
||||||
// crate::process::abort()
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(unused_attributes)]
|
|
||||||
#[unstable(feature = "alloc_internals", issue = "none")]
|
|
||||||
pub mod __default_lib_allocator {
|
|
||||||
use super::{GlobalAlloc, Layout, System};
|
|
||||||
// These magic symbol names are used as a fallback for implementing the
|
|
||||||
// `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is
|
|
||||||
// no `#[global_allocator]` attribute.
|
|
||||||
|
|
||||||
// for symbol names src/librustc_ast/expand/allocator.rs
|
|
||||||
// for signatures src/librustc_allocator/lib.rs
|
|
||||||
|
|
||||||
// linkage directives are provided as part of the current compiler allocator
|
|
||||||
// ABI
|
|
||||||
|
|
||||||
#[rustc_std_internal_symbol]
|
|
||||||
pub unsafe extern "C" fn __rdl_alloc(size: usize, align: usize) -> *mut u8 {
|
|
||||||
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
|
|
||||||
// `GlobalAlloc::alloc`.
|
|
||||||
unsafe {
|
|
||||||
let layout = Layout::from_size_align_unchecked(size, align);
|
|
||||||
System.alloc(layout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustc_std_internal_symbol]
|
|
||||||
pub unsafe extern "C" fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) {
|
|
||||||
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
|
|
||||||
// `GlobalAlloc::dealloc`.
|
|
||||||
unsafe { System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustc_std_internal_symbol]
|
|
||||||
pub unsafe extern "C" fn __rdl_realloc(
|
|
||||||
ptr: *mut u8,
|
|
||||||
old_size: usize,
|
|
||||||
align: usize,
|
|
||||||
new_size: usize,
|
|
||||||
) -> *mut u8 {
|
|
||||||
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
|
|
||||||
// `GlobalAlloc::realloc`.
|
|
||||||
unsafe {
|
|
||||||
let old_layout = Layout::from_size_align_unchecked(old_size, align);
|
|
||||||
System.realloc(ptr, old_layout, new_size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustc_std_internal_symbol]
|
|
||||||
pub unsafe extern "C" fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
|
|
||||||
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
|
|
||||||
// `GlobalAlloc::alloc_zeroed`.
|
|
||||||
unsafe {
|
|
||||||
let layout = Layout::from_size_align_unchecked(size, align);
|
|
||||||
System.alloc_zeroed(layout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
//! The `ByteStr` and `ByteString` types and trait implementations.
|
|
||||||
|
|
||||||
#[unstable(feature = "bstr", issue = "134915")]
|
|
||||||
pub use alloc_crate::bstr::{ByteStr, ByteString};
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
|||||||
//! Unordered containers, implemented as hash-tables
|
|
||||||
|
|
||||||
pub mod map;
|
|
||||||
pub mod set;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
|||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
pub struct HashMap<K, V, T> {
|
|
||||||
_phantom: PhantomData<(K, V, T)>,
|
|
||||||
}
|
|
||||||
pub use alloc_crate::collections;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
use crate::ffi::OsString;
|
|
||||||
|
|
||||||
pub fn var_os(s: &str) -> Option<OsString> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub use core::error::Error;
|
|
||||||
#[unstable(feature = "error_generic_member_access", issue = "99301")]
|
|
||||||
pub use core::error::{Request, request_ref, request_value};
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
//! [`CStr`], [`CString`], and related types.
|
|
||||||
|
|
||||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
|
||||||
pub use alloc_crate::ffi::c_str::FromVecWithNulError;
|
|
||||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
|
||||||
pub use alloc_crate::ffi::c_str::IntoStringError;
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub use alloc_crate::ffi::c_str::{CString, NulError};
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub use core::ffi::c_str::CStr;
|
|
||||||
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
|
|
||||||
pub use core::ffi::c_str::FromBytesUntilNulError;
|
|
||||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
|
||||||
pub use core::ffi::c_str::FromBytesWithNulError;
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
//! Utilities related to FFI bindings.
|
|
||||||
//!
|
|
||||||
//! This module provides utilities to handle data across non-Rust
|
|
||||||
//! interfaces, like other programming languages and the underlying
|
|
||||||
//! operating system. It is mainly of use for FFI (Foreign Function
|
|
||||||
//! Interface) bindings and code that needs to exchange C-like strings
|
|
||||||
//! with other languages.
|
|
||||||
//!
|
|
||||||
//! # Overview
|
|
||||||
//!
|
|
||||||
//! Rust represents owned strings with the [`String`] type, and
|
|
||||||
//! borrowed slices of strings with the [`str`] primitive. Both are
|
|
||||||
//! always in UTF-8 encoding, and may contain nul bytes in the middle,
|
|
||||||
//! i.e., if you look at the bytes that make up the string, there may
|
|
||||||
//! be a `\0` among them. Both `String` and `str` store their length
|
|
||||||
//! explicitly; there are no nul terminators at the end of strings
|
|
||||||
//! like in C.
|
|
||||||
//!
|
|
||||||
//! C strings are different from Rust strings:
|
|
||||||
//!
|
|
||||||
//! * **Encodings** - Rust strings are UTF-8, but C strings may use
|
|
||||||
//! other encodings. If you are using a string from C, you should
|
|
||||||
//! check its encoding explicitly, rather than just assuming that it
|
|
||||||
//! is UTF-8 like you can do in Rust.
|
|
||||||
//!
|
|
||||||
//! * **Character size** - C strings may use `char` or `wchar_t`-sized
|
|
||||||
//! characters; please **note** that C's `char` is different from Rust's.
|
|
||||||
//! The C standard leaves the actual sizes of those types open to
|
|
||||||
//! interpretation, but defines different APIs for strings made up of
|
|
||||||
//! each character type. Rust strings are always UTF-8, so different
|
|
||||||
//! Unicode characters will be encoded in a variable number of bytes
|
|
||||||
//! each. The Rust type [`char`] represents a '[Unicode scalar
|
|
||||||
//! value]', which is similar to, but not the same as, a '[Unicode
|
|
||||||
//! code point]'.
|
|
||||||
//!
|
|
||||||
//! * **Nul terminators and implicit string lengths** - Often, C
|
|
||||||
//! strings are nul-terminated, i.e., they have a `\0` character at the
|
|
||||||
//! end. The length of a string buffer is not stored, but has to be
|
|
||||||
//! calculated; to compute the length of a string, C code must
|
|
||||||
//! manually call a function like `strlen()` for `char`-based strings,
|
|
||||||
//! or `wcslen()` for `wchar_t`-based ones. Those functions return
|
|
||||||
//! the number of characters in the string excluding the nul
|
|
||||||
//! terminator, so the buffer length is really `len+1` characters.
|
|
||||||
//! Rust strings don't have a nul terminator; their length is always
|
|
||||||
//! stored and does not need to be calculated. While in Rust
|
|
||||||
//! accessing a string's length is an *O*(1) operation (because the
|
|
||||||
//! length is stored); in C it is an *O*(*n*) operation because the
|
|
||||||
//! length needs to be computed by scanning the string for the nul
|
|
||||||
//! terminator.
|
|
||||||
//!
|
|
||||||
//! * **Internal nul characters** - When C strings have a nul
|
|
||||||
//! terminator character, this usually means that they cannot have nul
|
|
||||||
//! characters in the middle — a nul character would essentially
|
|
||||||
//! truncate the string. Rust strings *can* have nul characters in
|
|
||||||
//! the middle, because nul does not have to mark the end of the
|
|
||||||
//! string in Rust.
|
|
||||||
//!
|
|
||||||
//! # Representations of non-Rust strings
|
|
||||||
//!
|
|
||||||
//! [`CString`] and [`CStr`] are useful when you need to transfer
|
|
||||||
//! UTF-8 strings to and from languages with a C ABI, like Python.
|
|
||||||
//!
|
|
||||||
//! * **From Rust to C:** [`CString`] represents an owned, C-friendly
|
|
||||||
//! string: it is nul-terminated, and has no internal nul characters.
|
|
||||||
//! Rust code can create a [`CString`] out of a normal string (provided
|
|
||||||
//! that the string doesn't have nul characters in the middle), and
|
|
||||||
//! then use a variety of methods to obtain a raw <code>\*mut [u8]</code> that can
|
|
||||||
//! then be passed as an argument to functions which use the C
|
|
||||||
//! conventions for strings.
|
|
||||||
//!
|
|
||||||
//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it
|
|
||||||
//! is what you would use to wrap a raw <code>\*const [u8]</code> that you got from
|
|
||||||
//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array
|
|
||||||
//! of bytes. Once you have a [`CStr`], you can convert it to a Rust
|
|
||||||
//! <code>&[str]</code> if it's valid UTF-8, or lossily convert it by adding
|
|
||||||
//! replacement characters.
|
|
||||||
//!
|
|
||||||
//! [`OsString`] and [`OsStr`] are useful when you need to transfer
|
|
||||||
//! strings to and from the operating system itself, or when capturing
|
|
||||||
//! the output of external commands. Conversions between [`OsString`],
|
|
||||||
//! [`OsStr`] and Rust strings work similarly to those for [`CString`]
|
|
||||||
//! and [`CStr`].
|
|
||||||
//!
|
|
||||||
//! * [`OsString`] losslessly represents an owned platform string. However, this
|
|
||||||
//! representation is not necessarily in a form native to the platform.
|
|
||||||
//! In the Rust standard library, various APIs that transfer strings to/from the operating
|
|
||||||
//! system use [`OsString`] instead of plain strings. For example,
|
|
||||||
//! [`env::var_os()`] is used to query environment variables; it
|
|
||||||
//! returns an <code>[Option]<[OsString]></code>. If the environment variable
|
|
||||||
//! exists you will get a <code>[Some]\(os_string)</code>, which you can
|
|
||||||
//! *then* try to convert to a Rust string. This yields a [`Result`], so that
|
|
||||||
//! your code can detect errors in case the environment variable did
|
|
||||||
//! not in fact contain valid Unicode data.
|
|
||||||
//!
|
|
||||||
//! * [`OsStr`] losslessly represents a borrowed reference to a platform string.
|
|
||||||
//! However, this representation is not necessarily in a form native to the platform.
|
|
||||||
//! It can be converted into a UTF-8 Rust string slice in a similar way to
|
|
||||||
//! [`OsString`].
|
|
||||||
//!
|
|
||||||
//! # Conversions
|
|
||||||
//!
|
|
||||||
//! ## On Unix
|
|
||||||
//!
|
|
||||||
//! On Unix, [`OsStr`] implements the
|
|
||||||
//! <code>std::os::unix::ffi::[OsStrExt][unix.OsStrExt]</code> trait, which
|
|
||||||
//! augments it with two methods, [`from_bytes`] and [`as_bytes`].
|
|
||||||
//! These do inexpensive conversions from and to byte slices.
|
|
||||||
//!
|
|
||||||
//! Additionally, on Unix [`OsString`] implements the
|
|
||||||
//! <code>std::os::unix::ffi::[OsStringExt][unix.OsStringExt]</code> trait,
|
|
||||||
//! which provides [`from_vec`] and [`into_vec`] methods that consume
|
|
||||||
//! their arguments, and take or produce vectors of [`u8`].
|
|
||||||
//!
|
|
||||||
//! ## On Windows
|
|
||||||
//!
|
|
||||||
//! An [`OsStr`] can be losslessly converted to a native Windows string. And
|
|
||||||
//! a native Windows string can be losslessly converted to an [`OsString`].
|
|
||||||
//!
|
|
||||||
//! On Windows, [`OsStr`] implements the
|
|
||||||
//! <code>std::os::windows::ffi::[OsStrExt][windows.OsStrExt]</code> trait,
|
|
||||||
//! which provides an [`encode_wide`] method. This provides an
|
|
||||||
//! iterator that can be [`collect`]ed into a vector of [`u16`]. After a nul
|
|
||||||
//! characters is appended, this is the same as a native Windows string.
|
|
||||||
//!
|
|
||||||
//! Additionally, on Windows [`OsString`] implements the
|
|
||||||
//! <code>std::os::windows:ffi::[OsStringExt][windows.OsStringExt]</code>
|
|
||||||
//! trait, which provides a [`from_wide`] method to convert a native Windows
|
|
||||||
//! string (without the terminating nul character) to an [`OsString`].
|
|
||||||
//!
|
|
||||||
//! ## Other platforms
|
|
||||||
//!
|
|
||||||
//! Many other platforms provide their own extension traits in a
|
|
||||||
//! `std::os::*::ffi` module.
|
|
||||||
//!
|
|
||||||
//! ## On all platforms
|
|
||||||
//!
|
|
||||||
//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of
|
|
||||||
//! UTF-8; see [`OsString`] for more details on its encoding on different platforms.
|
|
||||||
//!
|
|
||||||
//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_encoded_bytes`] and
|
|
||||||
//! [`OsStr::from_encoded_bytes_unchecked`].
|
|
||||||
//!
|
|
||||||
//! For basic string processing, see [`OsStr::slice_encoded_bytes`].
|
|
||||||
//!
|
|
||||||
//! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
|
|
||||||
//! [Unicode code point]: https://www.unicode.org/glossary/#code_point
|
|
||||||
//! [`env::set_var()`]: crate::env::set_var "env::set_var"
|
|
||||||
//! [`env::var_os()`]: crate::env::var_os "env::var_os"
|
|
||||||
//! [unix.OsStringExt]: crate::os::unix::ffi::OsStringExt "os::unix::ffi::OsStringExt"
|
|
||||||
//! [`from_vec`]: crate::os::unix::ffi::OsStringExt::from_vec "os::unix::ffi::OsStringExt::from_vec"
|
|
||||||
//! [`into_vec`]: crate::os::unix::ffi::OsStringExt::into_vec "os::unix::ffi::OsStringExt::into_vec"
|
|
||||||
//! [unix.OsStrExt]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt"
|
|
||||||
//! [`from_bytes`]: crate::os::unix::ffi::OsStrExt::from_bytes "os::unix::ffi::OsStrExt::from_bytes"
|
|
||||||
//! [`as_bytes`]: crate::os::unix::ffi::OsStrExt::as_bytes "os::unix::ffi::OsStrExt::as_bytes"
|
|
||||||
//! [`OsStrExt`]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt"
|
|
||||||
//! [windows.OsStrExt]: crate::os::windows::ffi::OsStrExt "os::windows::ffi::OsStrExt"
|
|
||||||
//! [`encode_wide`]: crate::os::windows::ffi::OsStrExt::encode_wide "os::windows::ffi::OsStrExt::encode_wide"
|
|
||||||
//! [`collect`]: crate::iter::Iterator::collect "iter::Iterator::collect"
|
|
||||||
//! [windows.OsStringExt]: crate::os::windows::ffi::OsStringExt "os::windows::ffi::OsStringExt"
|
|
||||||
//! [`from_wide`]: crate::os::windows::ffi::OsStringExt::from_wide "os::windows::ffi::OsStringExt::from_wide"
|
|
||||||
|
|
||||||
#![stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
|
|
||||||
#[stable(feature = "c_str_module", since = "1.88.0")]
|
|
||||||
pub mod c_str;
|
|
||||||
|
|
||||||
#[stable(feature = "core_c_void", since = "1.30.0")]
|
|
||||||
pub use core::ffi::c_void;
|
|
||||||
#[unstable(
|
|
||||||
feature = "c_variadic",
|
|
||||||
reason = "the `c_variadic` feature has not been properly tested on \
|
|
||||||
all supported platforms",
|
|
||||||
issue = "44930"
|
|
||||||
)]
|
|
||||||
pub use core::ffi::{VaArgSafe, VaList};
|
|
||||||
#[stable(feature = "core_ffi_c", since = "1.64.0")]
|
|
||||||
pub use core::ffi::{
|
|
||||||
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
|
|
||||||
c_ulong, c_ulonglong, c_ushort,
|
|
||||||
};
|
|
||||||
#[unstable(feature = "c_size_t", issue = "88345")]
|
|
||||||
pub use core::ffi::{c_ptrdiff_t, c_size_t, c_ssize_t};
|
|
||||||
|
|
||||||
#[doc(inline)]
|
|
||||||
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
|
|
||||||
pub use self::c_str::FromBytesUntilNulError;
|
|
||||||
#[doc(inline)]
|
|
||||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
|
||||||
pub use self::c_str::FromBytesWithNulError;
|
|
||||||
#[doc(inline)]
|
|
||||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
|
||||||
pub use self::c_str::FromVecWithNulError;
|
|
||||||
#[doc(inline)]
|
|
||||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
|
||||||
pub use self::c_str::IntoStringError;
|
|
||||||
#[doc(inline)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub use self::c_str::NulError;
|
|
||||||
#[doc(inline)]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub use self::c_str::{CStr, CString};
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use self::os_str::{OsStr, OsString};
|
|
||||||
|
|
||||||
#[stable(feature = "os_str_display", since = "1.87.0")]
|
|
||||||
pub mod os_str;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,311 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use crate::mem::MaybeUninit;
|
|
||||||
use crate::ptr;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_string_with_capacity() {
|
|
||||||
let os_string = OsString::with_capacity(0);
|
|
||||||
assert_eq!(0, os_string.inner.into_inner().capacity());
|
|
||||||
|
|
||||||
let os_string = OsString::with_capacity(10);
|
|
||||||
assert_eq!(10, os_string.inner.into_inner().capacity());
|
|
||||||
|
|
||||||
let mut os_string = OsString::with_capacity(0);
|
|
||||||
os_string.push("abc");
|
|
||||||
assert!(os_string.inner.into_inner().capacity() >= 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_string_clear() {
|
|
||||||
let mut os_string = OsString::from("abc");
|
|
||||||
assert_eq!(3, os_string.inner.as_inner().len());
|
|
||||||
|
|
||||||
os_string.clear();
|
|
||||||
assert_eq!(&os_string, "");
|
|
||||||
assert_eq!(0, os_string.inner.as_inner().len());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_string_leak() {
|
|
||||||
let os_string = OsString::from("have a cake");
|
|
||||||
let (len, cap) = (os_string.len(), os_string.capacity());
|
|
||||||
let leaked = os_string.leak();
|
|
||||||
assert_eq!(leaked.as_encoded_bytes(), b"have a cake");
|
|
||||||
unsafe { drop(String::from_raw_parts(leaked as *mut OsStr as _, len, cap)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_string_capacity() {
|
|
||||||
let os_string = OsString::with_capacity(0);
|
|
||||||
assert_eq!(0, os_string.capacity());
|
|
||||||
|
|
||||||
let os_string = OsString::with_capacity(10);
|
|
||||||
assert_eq!(10, os_string.capacity());
|
|
||||||
|
|
||||||
let mut os_string = OsString::with_capacity(0);
|
|
||||||
os_string.push("abc");
|
|
||||||
assert!(os_string.capacity() >= 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_string_reserve() {
|
|
||||||
let mut os_string = OsString::new();
|
|
||||||
assert_eq!(os_string.capacity(), 0);
|
|
||||||
|
|
||||||
os_string.reserve(2);
|
|
||||||
assert!(os_string.capacity() >= 2);
|
|
||||||
|
|
||||||
for _ in 0..16 {
|
|
||||||
os_string.push("a");
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(os_string.capacity() >= 16);
|
|
||||||
os_string.reserve(16);
|
|
||||||
assert!(os_string.capacity() >= 32);
|
|
||||||
|
|
||||||
os_string.push("a");
|
|
||||||
|
|
||||||
os_string.reserve(16);
|
|
||||||
assert!(os_string.capacity() >= 33)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_string_reserve_exact() {
|
|
||||||
let mut os_string = OsString::new();
|
|
||||||
assert_eq!(os_string.capacity(), 0);
|
|
||||||
|
|
||||||
os_string.reserve_exact(2);
|
|
||||||
assert!(os_string.capacity() >= 2);
|
|
||||||
|
|
||||||
for _ in 0..16 {
|
|
||||||
os_string.push("a");
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(os_string.capacity() >= 16);
|
|
||||||
os_string.reserve_exact(16);
|
|
||||||
assert!(os_string.capacity() >= 32);
|
|
||||||
|
|
||||||
os_string.push("a");
|
|
||||||
|
|
||||||
os_string.reserve_exact(16);
|
|
||||||
assert!(os_string.capacity() >= 33)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_string_join() {
|
|
||||||
let strings = [OsStr::new("hello"), OsStr::new("dear"), OsStr::new("world")];
|
|
||||||
assert_eq!("hello", strings[..1].join(OsStr::new(" ")));
|
|
||||||
assert_eq!("hello dear world", strings.join(OsStr::new(" ")));
|
|
||||||
assert_eq!("hellodearworld", strings.join(OsStr::new("")));
|
|
||||||
assert_eq!("hello.\n dear.\n world", strings.join(OsStr::new(".\n ")));
|
|
||||||
|
|
||||||
assert_eq!("dear world", strings[1..].join(&OsString::from(" ")));
|
|
||||||
|
|
||||||
let strings_abc = [OsString::from("a"), OsString::from("b"), OsString::from("c")];
|
|
||||||
assert_eq!("a b c", strings_abc.join(OsStr::new(" ")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_string_default() {
|
|
||||||
let os_string: OsString = Default::default();
|
|
||||||
assert_eq!("", &os_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_is_empty() {
|
|
||||||
let mut os_string = OsString::new();
|
|
||||||
assert!(os_string.is_empty());
|
|
||||||
|
|
||||||
os_string.push("abc");
|
|
||||||
assert!(!os_string.is_empty());
|
|
||||||
|
|
||||||
os_string.clear();
|
|
||||||
assert!(os_string.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_len() {
|
|
||||||
let mut os_string = OsString::new();
|
|
||||||
assert_eq!(0, os_string.len());
|
|
||||||
|
|
||||||
os_string.push("abc");
|
|
||||||
assert_eq!(3, os_string.len());
|
|
||||||
|
|
||||||
os_string.clear();
|
|
||||||
assert_eq!(0, os_string.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_default() {
|
|
||||||
let os_str: &OsStr = Default::default();
|
|
||||||
assert_eq!("", os_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn into_boxed() {
|
|
||||||
let orig = "Hello, world!";
|
|
||||||
let os_str = OsStr::new(orig);
|
|
||||||
let boxed: Box<OsStr> = Box::from(os_str);
|
|
||||||
let os_string = os_str.to_owned().into_boxed_os_str().into_os_string();
|
|
||||||
assert_eq!(os_str, &*boxed);
|
|
||||||
assert_eq!(&*boxed, &*os_string);
|
|
||||||
assert_eq!(&*os_string, os_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn boxed_default() {
|
|
||||||
let boxed = <Box<OsStr>>::default();
|
|
||||||
assert!(boxed.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_str_clone_into() {
|
|
||||||
let mut os_string = OsString::with_capacity(123);
|
|
||||||
os_string.push("hello");
|
|
||||||
let os_str = OsStr::new("bonjour");
|
|
||||||
os_str.clone_into(&mut os_string);
|
|
||||||
assert_eq!(os_str, os_string);
|
|
||||||
assert!(os_string.capacity() >= 123);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn into_rc() {
|
|
||||||
let orig = "Hello, world!";
|
|
||||||
let os_str = OsStr::new(orig);
|
|
||||||
let rc: Rc<OsStr> = Rc::from(os_str);
|
|
||||||
let arc: Arc<OsStr> = Arc::from(os_str);
|
|
||||||
|
|
||||||
assert_eq!(&*rc, os_str);
|
|
||||||
assert_eq!(&*arc, os_str);
|
|
||||||
|
|
||||||
let rc2: Rc<OsStr> = Rc::from(os_str.to_owned());
|
|
||||||
let arc2: Arc<OsStr> = Arc::from(os_str.to_owned());
|
|
||||||
|
|
||||||
assert_eq!(&*rc2, os_str);
|
|
||||||
assert_eq!(&*arc2, os_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn slice_encoded_bytes() {
|
|
||||||
let os_str = OsStr::new("123θგ🦀");
|
|
||||||
// ASCII
|
|
||||||
let digits = os_str.slice_encoded_bytes(..3);
|
|
||||||
assert_eq!(digits, "123");
|
|
||||||
let three = os_str.slice_encoded_bytes(2..3);
|
|
||||||
assert_eq!(three, "3");
|
|
||||||
// 2-byte UTF-8
|
|
||||||
let theta = os_str.slice_encoded_bytes(3..5);
|
|
||||||
assert_eq!(theta, "θ");
|
|
||||||
// 3-byte UTF-8
|
|
||||||
let gani = os_str.slice_encoded_bytes(5..8);
|
|
||||||
assert_eq!(gani, "გ");
|
|
||||||
// 4-byte UTF-8
|
|
||||||
let crab = os_str.slice_encoded_bytes(8..);
|
|
||||||
assert_eq!(crab, "🦀");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn slice_out_of_bounds() {
|
|
||||||
let crab = OsStr::new("🦀");
|
|
||||||
let _ = crab.slice_encoded_bytes(..5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn slice_mid_char() {
|
|
||||||
let crab = OsStr::new("🦀");
|
|
||||||
let _ = crab.slice_encoded_bytes(..2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "byte index 1 is not an OsStr boundary")]
|
|
||||||
fn slice_invalid_data() {
|
|
||||||
use crate::os::unix::ffi::OsStrExt;
|
|
||||||
|
|
||||||
let os_string = OsStr::from_bytes(b"\xFF\xFF");
|
|
||||||
let _ = os_string.slice_encoded_bytes(1..);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "byte index 1 is not an OsStr boundary")]
|
|
||||||
fn slice_partial_utf8() {
|
|
||||||
use crate::os::unix::ffi::{OsStrExt, OsStringExt};
|
|
||||||
|
|
||||||
let part_crab = OsStr::from_bytes(&"🦀".as_bytes()[..3]);
|
|
||||||
let mut os_string = OsString::from_vec(vec![0xFF]);
|
|
||||||
os_string.push(part_crab);
|
|
||||||
let _ = os_string.slice_encoded_bytes(1..);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[test]
|
|
||||||
fn slice_invalid_edge() {
|
|
||||||
use crate::os::unix::ffi::{OsStrExt, OsStringExt};
|
|
||||||
|
|
||||||
let os_string = OsStr::from_bytes(b"a\xFFa");
|
|
||||||
assert_eq!(os_string.slice_encoded_bytes(..1), "a");
|
|
||||||
assert_eq!(os_string.slice_encoded_bytes(1..), OsStr::from_bytes(b"\xFFa"));
|
|
||||||
assert_eq!(os_string.slice_encoded_bytes(..2), OsStr::from_bytes(b"a\xFF"));
|
|
||||||
assert_eq!(os_string.slice_encoded_bytes(2..), "a");
|
|
||||||
|
|
||||||
let os_string = OsStr::from_bytes(&"abc🦀".as_bytes()[..6]);
|
|
||||||
assert_eq!(os_string.slice_encoded_bytes(..3), "abc");
|
|
||||||
assert_eq!(os_string.slice_encoded_bytes(3..), OsStr::from_bytes(b"\xF0\x9F\xA6"));
|
|
||||||
|
|
||||||
let mut os_string = OsString::from_vec(vec![0xFF]);
|
|
||||||
os_string.push("🦀");
|
|
||||||
assert_eq!(os_string.slice_encoded_bytes(..1), OsStr::from_bytes(b"\xFF"));
|
|
||||||
assert_eq!(os_string.slice_encoded_bytes(1..), "🦀");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "byte index 3 lies between surrogate codepoints")]
|
|
||||||
fn slice_between_surrogates() {
|
|
||||||
use crate::os::windows::ffi::OsStringExt;
|
|
||||||
|
|
||||||
let os_string = OsString::from_wide(&[0xD800, 0xD800]);
|
|
||||||
assert_eq!(os_string.as_encoded_bytes(), &[0xED, 0xA0, 0x80, 0xED, 0xA0, 0x80]);
|
|
||||||
let _ = os_string.slice_encoded_bytes(..3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
#[test]
|
|
||||||
fn slice_surrogate_edge() {
|
|
||||||
use crate::os::windows::ffi::OsStringExt;
|
|
||||||
|
|
||||||
let surrogate = OsString::from_wide(&[0xD800]);
|
|
||||||
let mut pre_crab = surrogate.clone();
|
|
||||||
pre_crab.push("🦀");
|
|
||||||
assert_eq!(pre_crab.slice_encoded_bytes(..3), surrogate);
|
|
||||||
assert_eq!(pre_crab.slice_encoded_bytes(3..), "🦀");
|
|
||||||
|
|
||||||
let mut post_crab = OsString::from("🦀");
|
|
||||||
post_crab.push(&surrogate);
|
|
||||||
assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀");
|
|
||||||
assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn clone_to_uninit() {
|
|
||||||
let a = OsStr::new("hello.txt");
|
|
||||||
|
|
||||||
let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<OsStr>(a)];
|
|
||||||
unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) };
|
|
||||||
assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() });
|
|
||||||
|
|
||||||
let mut b: Box<OsStr> = OsStr::new("world.exe").into();
|
|
||||||
assert_eq!(size_of_val::<OsStr>(a), size_of_val::<OsStr>(&b));
|
|
||||||
assert_ne!(a, &*b);
|
|
||||||
unsafe { a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b).cast()) };
|
|
||||||
assert_eq!(a, &*b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn debug() {
|
|
||||||
let s = "'single quotes'";
|
|
||||||
assert_eq!(format!("{:?}", OsStr::new(s)), format!("{:?}", s));
|
|
||||||
}
|
|
||||||
3532
crates/std/src/fs.rs
3532
crates/std/src/fs.rs
File diff suppressed because it is too large
Load Diff
@@ -1,91 +0,0 @@
|
|||||||
//! Generic hashing support.
|
|
||||||
//!
|
|
||||||
//! This module provides a generic way to compute the [hash] of a value.
|
|
||||||
//! Hashes are most commonly used with [`HashMap`] and [`HashSet`].
|
|
||||||
//!
|
|
||||||
//! [hash]: https://en.wikipedia.org/wiki/Hash_function
|
|
||||||
//! [`HashMap`]: ../../std/collections/struct.HashMap.html
|
|
||||||
//! [`HashSet`]: ../../std/collections/struct.HashSet.html
|
|
||||||
//!
|
|
||||||
//! The simplest way to make a type hashable is to use `#[derive(Hash)]`:
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! use std::hash::{DefaultHasher, Hash, Hasher};
|
|
||||||
//!
|
|
||||||
//! #[derive(Hash)]
|
|
||||||
//! struct Person {
|
|
||||||
//! id: u32,
|
|
||||||
//! name: String,
|
|
||||||
//! phone: u64,
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! let person1 = Person {
|
|
||||||
//! id: 5,
|
|
||||||
//! name: "Janet".to_string(),
|
|
||||||
//! phone: 555_666_7777,
|
|
||||||
//! };
|
|
||||||
//! let person2 = Person {
|
|
||||||
//! id: 5,
|
|
||||||
//! name: "Bob".to_string(),
|
|
||||||
//! phone: 555_666_7777,
|
|
||||||
//! };
|
|
||||||
//!
|
|
||||||
//! assert!(calculate_hash(&person1) != calculate_hash(&person2));
|
|
||||||
//!
|
|
||||||
//! fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
|
||||||
//! let mut s = DefaultHasher::new();
|
|
||||||
//! t.hash(&mut s);
|
|
||||||
//! s.finish()
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! If you need more control over how a value is hashed, you need to implement
|
|
||||||
//! the [`Hash`] trait:
|
|
||||||
//!
|
|
||||||
//! ```rust
|
|
||||||
//! use std::hash::{DefaultHasher, Hash, Hasher};
|
|
||||||
//!
|
|
||||||
//! struct Person {
|
|
||||||
//! id: u32,
|
|
||||||
//! # #[allow(dead_code)]
|
|
||||||
//! name: String,
|
|
||||||
//! phone: u64,
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! impl Hash for Person {
|
|
||||||
//! fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
//! self.id.hash(state);
|
|
||||||
//! self.phone.hash(state);
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! let person1 = Person {
|
|
||||||
//! id: 5,
|
|
||||||
//! name: "Janet".to_string(),
|
|
||||||
//! phone: 555_666_7777,
|
|
||||||
//! };
|
|
||||||
//! let person2 = Person {
|
|
||||||
//! id: 5,
|
|
||||||
//! name: "Bob".to_string(),
|
|
||||||
//! phone: 555_666_7777,
|
|
||||||
//! };
|
|
||||||
//!
|
|
||||||
//! assert_eq!(calculate_hash(&person1), calculate_hash(&person2));
|
|
||||||
//!
|
|
||||||
//! fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
|
||||||
//! let mut s = DefaultHasher::new();
|
|
||||||
//! t.hash(&mut s);
|
|
||||||
//! s.finish()
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
#![stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
|
|
||||||
pub(crate) mod random;
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub use core::hash::*;
|
|
||||||
|
|
||||||
#[stable(feature = "std_hash_exports", since = "1.76.0")]
|
|
||||||
pub use self::random::{DefaultHasher, RandomState};
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
//! This module exists to isolate [`RandomState`] and [`DefaultHasher`] outside of the
|
|
||||||
//! [`collections`] module without actually publicly exporting them, so that parts of that
|
|
||||||
//! implementation can more easily be moved to the [`alloc`] crate.
|
|
||||||
//!
|
|
||||||
//! Although its items are public and contain stability attributes, they can't actually be accessed
|
|
||||||
//! outside this crate.
|
|
||||||
//!
|
|
||||||
//! [`collections`]: crate::collections
|
|
||||||
|
|
||||||
use super::{BuildHasher, Hasher, SipHasher13};
|
|
||||||
use crate::cell::Cell;
|
|
||||||
use crate::fmt;
|
|
||||||
use crate::sys::random::hashmap_random_keys;
|
|
||||||
|
|
||||||
/// `RandomState` is the default state for [`HashMap`] types.
|
|
||||||
///
|
|
||||||
/// A particular instance `RandomState` will create the same instances of
|
|
||||||
/// [`Hasher`], but the hashers created by two different `RandomState`
|
|
||||||
/// instances are unlikely to produce the same result for the same values.
|
|
||||||
///
|
|
||||||
/// [`HashMap`]: crate::collections::HashMap
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::collections::HashMap;
|
|
||||||
/// use std::hash::RandomState;
|
|
||||||
///
|
|
||||||
/// let s = RandomState::new();
|
|
||||||
/// let mut map = HashMap::with_hasher(s);
|
|
||||||
/// map.insert(1, 2);
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RandomState {
|
|
||||||
k0: u64,
|
|
||||||
k1: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RandomState {
|
|
||||||
/// Constructs a new `RandomState` that is initialized with random keys.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::hash::RandomState;
|
|
||||||
///
|
|
||||||
/// let s = RandomState::new();
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
// rand
|
|
||||||
#[must_use]
|
|
||||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
|
||||||
pub fn new() -> RandomState {
|
|
||||||
// Historically this function did not cache keys from the OS and instead
|
|
||||||
// simply always called `rand::thread_rng().gen()` twice. In #31356 it
|
|
||||||
// was discovered, however, that because we re-seed the thread-local RNG
|
|
||||||
// from the OS periodically that this can cause excessive slowdown when
|
|
||||||
// many hash maps are created on a thread. To solve this performance
|
|
||||||
// trap we cache the first set of randomly generated keys per-thread.
|
|
||||||
//
|
|
||||||
// Later in #36481 it was discovered that exposing a deterministic
|
|
||||||
// iteration order allows a form of DOS attack. To counter that we
|
|
||||||
// increment one of the seeds on every RandomState creation, giving
|
|
||||||
// every corresponding HashMap a different iteration order.
|
|
||||||
thread_local!(static KEYS: Cell<(u64, u64)> = {
|
|
||||||
Cell::new(hashmap_random_keys())
|
|
||||||
});
|
|
||||||
|
|
||||||
KEYS.with(|keys| {
|
|
||||||
let (k0, k1) = keys.get();
|
|
||||||
keys.set((k0.wrapping_add(1), k1));
|
|
||||||
RandomState { k0, k1 }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
|
||||||
impl BuildHasher for RandomState {
|
|
||||||
type Hasher = DefaultHasher;
|
|
||||||
#[inline]
|
|
||||||
fn build_hasher(&self) -> DefaultHasher {
|
|
||||||
DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The default [`Hasher`] used by [`RandomState`].
|
|
||||||
///
|
|
||||||
/// The internal algorithm is not specified, and so it and its hashes should
|
|
||||||
/// not be relied upon over releases.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
|
||||||
pub struct DefaultHasher(SipHasher13);
|
|
||||||
|
|
||||||
impl DefaultHasher {
|
|
||||||
/// Creates a new `DefaultHasher`.
|
|
||||||
///
|
|
||||||
/// This hasher is not guaranteed to be the same as all other
|
|
||||||
/// `DefaultHasher` instances, but is the same as all other `DefaultHasher`
|
|
||||||
/// instances created through `new` or `default`.
|
|
||||||
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
|
|
||||||
#[inline]
|
|
||||||
#[rustc_const_unstable(feature = "const_default", issue = "143894")]
|
|
||||||
#[must_use]
|
|
||||||
pub const fn new() -> DefaultHasher {
|
|
||||||
DefaultHasher(SipHasher13::new_with_keys(0, 0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
|
|
||||||
#[rustc_const_unstable(feature = "const_default", issue = "143894")]
|
|
||||||
impl const Default for DefaultHasher {
|
|
||||||
/// Creates a new `DefaultHasher` using [`new`].
|
|
||||||
/// See its documentation for more.
|
|
||||||
///
|
|
||||||
/// [`new`]: DefaultHasher::new
|
|
||||||
#[inline]
|
|
||||||
fn default() -> DefaultHasher {
|
|
||||||
DefaultHasher::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
|
|
||||||
impl Hasher for DefaultHasher {
|
|
||||||
// The underlying `SipHasher13` doesn't override the other
|
|
||||||
// `write_*` methods, so it's ok not to forward them here.
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn write(&mut self, msg: &[u8]) {
|
|
||||||
self.0.write(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn write_str(&mut self, s: &str) {
|
|
||||||
self.0.write_str(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn finish(&self) -> u64 {
|
|
||||||
self.0.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
|
||||||
impl Default for RandomState {
|
|
||||||
/// Constructs a new `RandomState`.
|
|
||||||
#[inline]
|
|
||||||
fn default() -> RandomState {
|
|
||||||
RandomState::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
|
||||||
impl fmt::Debug for RandomState {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("RandomState").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
use crate::cmp::{max, min};
|
|
||||||
use alloc_crate::collections::VecDeque;
|
|
||||||
use crate::io;
|
|
||||||
use crate::io::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn copy_copies() {
|
|
||||||
let mut r = repeat(0).take(4);
|
|
||||||
let mut w = sink();
|
|
||||||
assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
|
|
||||||
|
|
||||||
let mut r = repeat(0).take(1 << 17);
|
|
||||||
assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ShortReader {
|
|
||||||
cap: usize,
|
|
||||||
read_size: usize,
|
|
||||||
observed_buffer: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for ShortReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
|
||||||
let bytes = min(self.cap, self.read_size).min(buf.len());
|
|
||||||
self.cap -= bytes;
|
|
||||||
self.observed_buffer = max(self.observed_buffer, buf.len());
|
|
||||||
Ok(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WriteObserver {
|
|
||||||
observed_buffer: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for WriteObserver {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
|
||||||
self.observed_buffer = max(self.observed_buffer, buf.len());
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn copy_specializes_bufwriter() {
|
|
||||||
let cap = 117 * 1024;
|
|
||||||
let buf_sz = 16 * 1024;
|
|
||||||
let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
|
|
||||||
let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 });
|
|
||||||
assert_eq!(
|
|
||||||
copy(&mut r, &mut w).unwrap(),
|
|
||||||
cap as u64,
|
|
||||||
"expected the whole capacity to be copied"
|
|
||||||
);
|
|
||||||
assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader");
|
|
||||||
assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn copy_specializes_bufreader() {
|
|
||||||
let mut source = vec![0; 768 * 1024];
|
|
||||||
source[1] = 42;
|
|
||||||
let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source));
|
|
||||||
|
|
||||||
let mut sink = Vec::new();
|
|
||||||
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
|
|
||||||
assert_eq!(source.as_slice(), sink.as_slice());
|
|
||||||
|
|
||||||
let buf_sz = 71 * 1024;
|
|
||||||
assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition");
|
|
||||||
|
|
||||||
let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source));
|
|
||||||
let mut sink = WriteObserver { observed_buffer: 0 };
|
|
||||||
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
|
|
||||||
assert_eq!(
|
|
||||||
sink.observed_buffer, buf_sz,
|
|
||||||
"expected a large buffer to be provided to the writer"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn copy_specializes_to_vec() {
|
|
||||||
let cap = DEFAULT_BUF_SIZE * 10;
|
|
||||||
let mut source = ShortReader { cap, observed_buffer: 0, read_size: DEFAULT_BUF_SIZE };
|
|
||||||
let mut sink = Vec::new();
|
|
||||||
let copied = io::copy(&mut source, &mut sink).unwrap();
|
|
||||||
assert_eq!(cap as u64, copied);
|
|
||||||
assert_eq!(sink.len() as u64, copied);
|
|
||||||
assert!(
|
|
||||||
source.observed_buffer > DEFAULT_BUF_SIZE,
|
|
||||||
"expected a large buffer to be provided to the reader, got {}",
|
|
||||||
source.observed_buffer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn copy_specializes_from_vecdeque() {
|
|
||||||
let mut source = VecDeque::with_capacity(100 * 1024);
|
|
||||||
for _ in 0..20 * 1024 {
|
|
||||||
source.push_front(0);
|
|
||||||
}
|
|
||||||
for _ in 0..20 * 1024 {
|
|
||||||
source.push_back(0);
|
|
||||||
}
|
|
||||||
let mut sink = WriteObserver { observed_buffer: 0 };
|
|
||||||
assert_eq!(40 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
|
|
||||||
assert_eq!(20 * 1024, sink.observed_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn copy_specializes_from_slice() {
|
|
||||||
let mut source = [1; 60 * 1024].as_slice();
|
|
||||||
let mut sink = WriteObserver { observed_buffer: 0 };
|
|
||||||
assert_eq!(60 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
|
|
||||||
assert_eq!(60 * 1024, sink.observed_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
mod io_benches {
|
|
||||||
use test::Bencher;
|
|
||||||
|
|
||||||
use crate::fs::{File, OpenOptions};
|
|
||||||
use crate::io::BufReader;
|
|
||||||
use crate::io::prelude::*;
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
#[cfg_attr(target_os = "emscripten", ignore)] // no /dev
|
|
||||||
fn bench_copy_buf_reader(b: &mut Bencher) {
|
|
||||||
let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed");
|
|
||||||
// use dyn to avoid specializations unrelated to readbuf
|
|
||||||
let dyn_in = &mut file_in as &mut dyn Read;
|
|
||||||
let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0));
|
|
||||||
let mut writer =
|
|
||||||
OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed");
|
|
||||||
|
|
||||||
const BYTES: u64 = 1024 * 1024;
|
|
||||||
|
|
||||||
b.bytes = BYTES;
|
|
||||||
|
|
||||||
b.iter(|| {
|
|
||||||
reader.get_mut().set_limit(BYTES);
|
|
||||||
crate::io::copy(&mut reader, &mut writer).unwrap()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,567 +0,0 @@
|
|||||||
use crate::io::prelude::*;
|
|
||||||
use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec_writer() {
|
|
||||||
let mut writer = Vec::new();
|
|
||||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
|
||||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
|
||||||
assert_eq!(
|
|
||||||
writer
|
|
||||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
|
||||||
.unwrap(),
|
|
||||||
3
|
|
||||||
);
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
||||||
assert_eq!(writer, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mem_writer() {
|
|
||||||
let mut writer = Cursor::new(Vec::new());
|
|
||||||
writer.set_position(10);
|
|
||||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
|
||||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
|
||||||
assert_eq!(
|
|
||||||
writer
|
|
||||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
|
||||||
.unwrap(),
|
|
||||||
3
|
|
||||||
);
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
||||||
assert_eq!(&writer.get_ref()[..10], &[0; 10]);
|
|
||||||
assert_eq!(&writer.get_ref()[10..], b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mem_writer_preallocated() {
|
|
||||||
let mut writer = Cursor::new(vec![0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 10]);
|
|
||||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
|
||||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mem_mut_writer() {
|
|
||||||
let mut vec = Vec::new();
|
|
||||||
let mut writer = Cursor::new(&mut vec);
|
|
||||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
|
||||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
|
||||||
assert_eq!(
|
|
||||||
writer
|
|
||||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
|
||||||
.unwrap(),
|
|
||||||
3
|
|
||||||
);
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_slice_writer<T>(writer: &mut Cursor<T>)
|
|
||||||
where
|
|
||||||
T: AsRef<[u8]>,
|
|
||||||
Cursor<T>: Write,
|
|
||||||
{
|
|
||||||
assert_eq!(writer.position(), 0);
|
|
||||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.position(), 1);
|
|
||||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
|
||||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
|
||||||
assert_eq!(writer.position(), 8);
|
|
||||||
assert_eq!(writer.write(&[]).unwrap(), 0);
|
|
||||||
assert_eq!(writer.position(), 8);
|
|
||||||
|
|
||||||
assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.write(&[10]).unwrap(), 0);
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
assert_eq!(writer.get_ref().as_ref(), b);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_slice_writer_vectored<T>(writer: &mut Cursor<T>)
|
|
||||||
where
|
|
||||||
T: AsRef<[u8]>,
|
|
||||||
Cursor<T>: Write,
|
|
||||||
{
|
|
||||||
assert_eq!(writer.position(), 0);
|
|
||||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.position(), 1);
|
|
||||||
assert_eq!(
|
|
||||||
writer.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),]).unwrap(),
|
|
||||||
7,
|
|
||||||
);
|
|
||||||
assert_eq!(writer.position(), 8);
|
|
||||||
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
|
||||||
assert_eq!(writer.position(), 8);
|
|
||||||
|
|
||||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0);
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
assert_eq!(writer.get_ref().as_ref(), b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_box_slice_writer() {
|
|
||||||
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
|
||||||
test_slice_writer(&mut writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_box_slice_writer_vectored() {
|
|
||||||
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
|
||||||
test_slice_writer_vectored(&mut writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_array_writer() {
|
|
||||||
let mut writer = Cursor::new([0u8; 9]);
|
|
||||||
test_slice_writer(&mut writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_array_writer_vectored() {
|
|
||||||
let mut writer = Cursor::new([0u8; 9]);
|
|
||||||
test_slice_writer_vectored(&mut writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buf_writer() {
|
|
||||||
let mut buf = [0 as u8; 9];
|
|
||||||
let mut writer = Cursor::new(&mut buf[..]);
|
|
||||||
test_slice_writer(&mut writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buf_writer_vectored() {
|
|
||||||
let mut buf = [0 as u8; 9];
|
|
||||||
let mut writer = Cursor::new(&mut buf[..]);
|
|
||||||
test_slice_writer_vectored(&mut writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buf_writer_seek() {
|
|
||||||
let mut buf = [0 as u8; 8];
|
|
||||||
{
|
|
||||||
let mut writer = Cursor::new(&mut buf[..]);
|
|
||||||
assert_eq!(writer.position(), 0);
|
|
||||||
assert_eq!(writer.write(&[1]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.position(), 1);
|
|
||||||
|
|
||||||
assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2);
|
|
||||||
assert_eq!(writer.position(), 2);
|
|
||||||
assert_eq!(writer.write(&[2]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.position(), 3);
|
|
||||||
|
|
||||||
assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1);
|
|
||||||
assert_eq!(writer.position(), 1);
|
|
||||||
assert_eq!(writer.write(&[3]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.position(), 2);
|
|
||||||
|
|
||||||
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
|
|
||||||
assert_eq!(writer.position(), 7);
|
|
||||||
assert_eq!(writer.write(&[4]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.position(), 8);
|
|
||||||
}
|
|
||||||
let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buf_writer_error() {
|
|
||||||
let mut buf = [0 as u8; 2];
|
|
||||||
let mut writer = Cursor::new(&mut buf[..]);
|
|
||||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.write(&[0, 0]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.write(&[0, 0]).unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mem_reader() {
|
|
||||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
assert_eq!(reader.position(), 0);
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
|
||||||
assert_eq!(reader.position(), 1);
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
|
||||||
assert_eq!(reader.position(), 5);
|
|
||||||
let b: &[_] = &[1, 2, 3, 4];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
|
||||||
let b: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(&buf[..3], b);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mem_reader_vectored() {
|
|
||||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
|
||||||
assert_eq!(reader.position(), 0);
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(
|
|
||||||
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
assert_eq!(reader.position(), 1);
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
let mut buf1 = [0; 4];
|
|
||||||
let mut buf2 = [0; 4];
|
|
||||||
assert_eq!(
|
|
||||||
reader
|
|
||||||
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),])
|
|
||||||
.unwrap(),
|
|
||||||
7,
|
|
||||||
);
|
|
||||||
let b1: &[_] = &[1, 2, 3, 4];
|
|
||||||
let b2: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(buf1, b1);
|
|
||||||
assert_eq!(&buf2[..3], b2);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_boxed_slice_reader() {
|
|
||||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
assert_eq!(reader.position(), 0);
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
|
||||||
assert_eq!(reader.position(), 1);
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
|
||||||
assert_eq!(reader.position(), 5);
|
|
||||||
let b: &[_] = &[1, 2, 3, 4];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
|
||||||
let b: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(&buf[..3], b);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_boxed_slice_reader_vectored() {
|
|
||||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
|
||||||
assert_eq!(reader.position(), 0);
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(
|
|
||||||
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
assert_eq!(reader.position(), 1);
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
let mut buf1 = [0; 4];
|
|
||||||
let mut buf2 = [0; 4];
|
|
||||||
assert_eq!(
|
|
||||||
reader
|
|
||||||
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
|
||||||
.unwrap(),
|
|
||||||
7,
|
|
||||||
);
|
|
||||||
let b1: &[_] = &[1, 2, 3, 4];
|
|
||||||
let b2: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(buf1, b1);
|
|
||||||
assert_eq!(&buf2[..3], b2);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_to_end() {
|
|
||||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
|
||||||
let mut v = Vec::new();
|
|
||||||
reader.read_to_end(&mut v).unwrap();
|
|
||||||
assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_slice_reader() {
|
|
||||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
let reader = &mut &in_buf[..];
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
|
||||||
assert_eq!(reader.len(), 7);
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(&buf[..], b);
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
|
||||||
assert_eq!(reader.len(), 3);
|
|
||||||
let b: &[_] = &[1, 2, 3, 4];
|
|
||||||
assert_eq!(&buf[..], b);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
|
||||||
let b: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(&buf[..3], b);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_slice_reader_vectored() {
|
|
||||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
let reader = &mut &in_buf[..];
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(
|
|
||||||
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
assert_eq!(reader.len(), 7);
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
let mut buf1 = [0; 4];
|
|
||||||
let mut buf2 = [0; 4];
|
|
||||||
assert_eq!(
|
|
||||||
reader
|
|
||||||
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
|
||||||
.unwrap(),
|
|
||||||
7,
|
|
||||||
);
|
|
||||||
let b1: &[_] = &[1, 2, 3, 4];
|
|
||||||
let b2: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(buf1, b1);
|
|
||||||
assert_eq!(&buf2[..3], b2);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_exact() {
|
|
||||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
let reader = &mut &in_buf[..];
|
|
||||||
let mut buf = [];
|
|
||||||
assert!(reader.read_exact(&mut buf).is_ok());
|
|
||||||
let mut buf = [8];
|
|
||||||
assert!(reader.read_exact(&mut buf).is_ok());
|
|
||||||
assert_eq!(buf[0], 0);
|
|
||||||
assert_eq!(reader.len(), 7);
|
|
||||||
let mut buf = [0, 0, 0, 0, 0, 0, 0];
|
|
||||||
assert!(reader.read_exact(&mut buf).is_ok());
|
|
||||||
assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]);
|
|
||||||
assert_eq!(reader.len(), 0);
|
|
||||||
let mut buf = [0];
|
|
||||||
assert!(reader.read_exact(&mut buf).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_buf_reader() {
|
|
||||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
let mut reader = Cursor::new(&in_buf[..]);
|
|
||||||
let mut buf = [];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
assert_eq!(reader.position(), 0);
|
|
||||||
let mut buf = [0];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
|
||||||
assert_eq!(reader.position(), 1);
|
|
||||||
let b: &[_] = &[0];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
|
||||||
assert_eq!(reader.position(), 5);
|
|
||||||
let b: &[_] = &[1, 2, 3, 4];
|
|
||||||
assert_eq!(buf, b);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
|
||||||
let b: &[_] = &[5, 6, 7];
|
|
||||||
assert_eq!(&buf[..3], b);
|
|
||||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn seek_past_end() {
|
|
||||||
let buf = [0xff];
|
|
||||||
let mut r = Cursor::new(&buf[..]);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
|
||||||
assert_eq!(r.read(&mut [0]).unwrap(), 0);
|
|
||||||
|
|
||||||
let mut r = Cursor::new(vec![10]);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
|
||||||
assert_eq!(r.read(&mut [0]).unwrap(), 0);
|
|
||||||
|
|
||||||
let mut buf = [0];
|
|
||||||
let mut r = Cursor::new(&mut buf[..]);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
|
||||||
assert_eq!(r.write(&[3]).unwrap(), 0);
|
|
||||||
|
|
||||||
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
|
||||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
|
||||||
assert_eq!(r.write(&[3]).unwrap(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn seek_past_i64() {
|
|
||||||
let buf = [0xff];
|
|
||||||
let mut r = Cursor::new(&buf[..]);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
|
||||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
|
||||||
|
|
||||||
let mut r = Cursor::new(vec![10]);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
|
||||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
|
||||||
|
|
||||||
let mut buf = [0];
|
|
||||||
let mut r = Cursor::new(&mut buf[..]);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
|
||||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
|
||||||
|
|
||||||
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
|
||||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
|
||||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
|
||||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn seek_before_0() {
|
|
||||||
let buf = [0xff];
|
|
||||||
let mut r = Cursor::new(&buf[..]);
|
|
||||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
|
||||||
|
|
||||||
let mut r = Cursor::new(vec![10]);
|
|
||||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
|
||||||
|
|
||||||
let mut buf = [0];
|
|
||||||
let mut r = Cursor::new(&mut buf[..]);
|
|
||||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
|
||||||
|
|
||||||
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
|
||||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_seekable_mem_writer() {
|
|
||||||
let mut writer = Cursor::new(Vec::<u8>::new());
|
|
||||||
assert_eq!(writer.position(), 0);
|
|
||||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
|
||||||
assert_eq!(writer.position(), 1);
|
|
||||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
|
||||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
|
||||||
assert_eq!(writer.position(), 8);
|
|
||||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
|
|
||||||
assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0);
|
|
||||||
assert_eq!(writer.position(), 0);
|
|
||||||
assert_eq!(writer.write(&[3, 4]).unwrap(), 2);
|
|
||||||
let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
|
|
||||||
assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3);
|
|
||||||
assert_eq!(writer.write(&[0, 1]).unwrap(), 2);
|
|
||||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
|
|
||||||
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
|
|
||||||
assert_eq!(writer.write(&[1, 2]).unwrap(), 2);
|
|
||||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
|
|
||||||
assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10);
|
|
||||||
assert_eq!(writer.write(&[1]).unwrap(), 1);
|
|
||||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1];
|
|
||||||
assert_eq!(&writer.get_ref()[..], b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_seek_past_end() {
|
|
||||||
let mut r = Cursor::new(Vec::new());
|
|
||||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
|
||||||
assert_eq!(r.write(&[3]).unwrap(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn vec_seek_before_0() {
|
|
||||||
let mut r = Cursor::new(Vec::new());
|
|
||||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
fn vec_seek_and_write_past_usize_max() {
|
|
||||||
let mut c = Cursor::new(Vec::new());
|
|
||||||
c.set_position(usize::MAX as u64 + 1);
|
|
||||||
assert!(c.write_all(&[1, 2, 3]).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_partial_eq() {
|
|
||||||
assert_eq!(Cursor::new(Vec::<u8>::new()), Cursor::new(Vec::<u8>::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_eq() {
|
|
||||||
struct AssertEq<T: Eq>(pub T);
|
|
||||||
|
|
||||||
let _: AssertEq<Cursor<Vec<u8>>> = AssertEq(Cursor::new(Vec::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn const_cursor() {
|
|
||||||
const CURSOR: Cursor<&[u8]> = Cursor::new(&[0]);
|
|
||||||
const _: &&[u8] = CURSOR.get_ref();
|
|
||||||
const _: u64 = CURSOR.position();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_write_vec(b: &mut test::Bencher) {
|
|
||||||
let slice = &[1; 128];
|
|
||||||
|
|
||||||
b.iter(|| {
|
|
||||||
let mut buf = b"some random data to overwrite".to_vec();
|
|
||||||
let mut cursor = Cursor::new(&mut buf);
|
|
||||||
|
|
||||||
let _ = cursor.write_all(slice);
|
|
||||||
test::black_box(&cursor);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_write_vec_vectored(b: &mut test::Bencher) {
|
|
||||||
let slices = [
|
|
||||||
IoSlice::new(&[1; 128]),
|
|
||||||
IoSlice::new(&[2; 256]),
|
|
||||||
IoSlice::new(&[3; 512]),
|
|
||||||
IoSlice::new(&[4; 1024]),
|
|
||||||
IoSlice::new(&[5; 2048]),
|
|
||||||
IoSlice::new(&[6; 4096]),
|
|
||||||
IoSlice::new(&[7; 8192]),
|
|
||||||
IoSlice::new(&[8; 8192 * 2]),
|
|
||||||
];
|
|
||||||
|
|
||||||
b.iter(|| {
|
|
||||||
let mut buf = b"some random data to overwrite".to_vec();
|
|
||||||
let mut cursor = Cursor::new(&mut buf);
|
|
||||||
|
|
||||||
let mut slices = slices;
|
|
||||||
let _ = cursor.write_all_vectored(&mut slices);
|
|
||||||
test::black_box(&cursor);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error};
|
|
||||||
use crate::sys::io::{decode_error_kind, error_string};
|
|
||||||
use crate::{assert_matches, error, fmt};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_size() {
|
|
||||||
assert!(size_of::<Error>() <= size_of::<[usize; 2]>());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_debug_error() {
|
|
||||||
let code = 6;
|
|
||||||
let msg = error_string(code);
|
|
||||||
let kind = decode_error_kind(code);
|
|
||||||
let err = Error {
|
|
||||||
repr: Repr::new_custom(Box::new(Custom {
|
|
||||||
kind: ErrorKind::InvalidInput,
|
|
||||||
error: Box::new(Error { repr: super::Repr::new_os(code) }),
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
let expected = format!(
|
|
||||||
"Custom {{ \
|
|
||||||
kind: InvalidInput, \
|
|
||||||
error: Os {{ \
|
|
||||||
code: {:?}, \
|
|
||||||
kind: {:?}, \
|
|
||||||
message: {:?} \
|
|
||||||
}} \
|
|
||||||
}}",
|
|
||||||
code, kind, msg
|
|
||||||
);
|
|
||||||
assert_eq!(format!("{err:?}"), expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_downcasting() {
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TestError;
|
|
||||||
|
|
||||||
impl fmt::Display for TestError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str("asdf")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for TestError {}
|
|
||||||
|
|
||||||
// we have to call all of these UFCS style right now since method
|
|
||||||
// resolution won't implicitly drop the Send+Sync bounds
|
|
||||||
let mut err = Error::new(ErrorKind::Other, TestError);
|
|
||||||
assert!(err.get_ref().unwrap().is::<TestError>());
|
|
||||||
assert_eq!("asdf", err.get_ref().unwrap().to_string());
|
|
||||||
assert!(err.get_mut().unwrap().is::<TestError>());
|
|
||||||
let extracted = err.into_inner().unwrap();
|
|
||||||
extracted.downcast::<TestError>().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_const() {
|
|
||||||
const E: Error = const_error!(ErrorKind::NotFound, "hello");
|
|
||||||
|
|
||||||
assert_eq!(E.kind(), ErrorKind::NotFound);
|
|
||||||
assert_eq!(E.to_string(), "hello");
|
|
||||||
assert!(format!("{E:?}").contains("\"hello\""));
|
|
||||||
assert!(format!("{E:?}").contains("NotFound"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_os_packing() {
|
|
||||||
for code in -20..20 {
|
|
||||||
let e = Error::from_raw_os_error(code);
|
|
||||||
assert_eq!(e.raw_os_error(), Some(code));
|
|
||||||
assert_matches!(
|
|
||||||
e.repr.data(),
|
|
||||||
ErrorData::Os(c) if c == code,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_errorkind_packing() {
|
|
||||||
assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound);
|
|
||||||
assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied);
|
|
||||||
assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized);
|
|
||||||
// Check that the innards look like what we want.
|
|
||||||
assert_matches!(
|
|
||||||
Error::from(ErrorKind::OutOfMemory).repr.data(),
|
|
||||||
ErrorData::Simple(ErrorKind::OutOfMemory),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_simple_message_packing() {
|
|
||||||
use super::ErrorKind::*;
|
|
||||||
use super::SimpleMessage;
|
|
||||||
macro_rules! check_simple_msg {
|
|
||||||
($err:expr, $kind:ident, $msg:literal) => {{
|
|
||||||
let e = &$err;
|
|
||||||
// Check that the public api is right.
|
|
||||||
assert_eq!(e.kind(), $kind);
|
|
||||||
assert!(format!("{e:?}").contains($msg));
|
|
||||||
// and we got what we expected
|
|
||||||
assert_matches!(
|
|
||||||
e.repr.data(),
|
|
||||||
ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg })
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
let not_static = const_error!(Uncategorized, "not a constant!");
|
|
||||||
check_simple_msg!(not_static, Uncategorized, "not a constant!");
|
|
||||||
|
|
||||||
const CONST: Error = const_error!(NotFound, "definitely a constant!");
|
|
||||||
check_simple_msg!(CONST, NotFound, "definitely a constant!");
|
|
||||||
|
|
||||||
static STATIC: Error = const_error!(BrokenPipe, "a constant, sort of!");
|
|
||||||
check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
struct Bojji(bool);
|
|
||||||
impl error::Error for Bojji {}
|
|
||||||
impl fmt::Display for Bojji {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "ah! {:?}", self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_custom_error_packing() {
|
|
||||||
use super::Custom;
|
|
||||||
let test = Error::new(ErrorKind::Uncategorized, Bojji(true));
|
|
||||||
assert_matches!(
|
|
||||||
test.repr.data(),
|
|
||||||
ErrorData::Custom(Custom {
|
|
||||||
kind: ErrorKind::Uncategorized,
|
|
||||||
error,
|
|
||||||
}) if error.downcast_ref::<Bojji>().as_deref() == Some(&Bojji(true)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct E;
|
|
||||||
|
|
||||||
impl fmt::Display for E {
|
|
||||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for E {}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_std_io_error_downcast() {
|
|
||||||
// Case 1: custom error, downcast succeeds
|
|
||||||
let io_error = Error::new(ErrorKind::Other, Bojji(true));
|
|
||||||
let e: Bojji = io_error.downcast().unwrap();
|
|
||||||
assert!(e.0);
|
|
||||||
|
|
||||||
// Case 2: custom error, downcast fails
|
|
||||||
let io_error = Error::new(ErrorKind::Other, Bojji(true));
|
|
||||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
|
||||||
|
|
||||||
// ensures that the custom error is intact
|
|
||||||
assert_eq!(ErrorKind::Other, io_error.kind());
|
|
||||||
let e: Bojji = io_error.downcast().unwrap();
|
|
||||||
assert!(e.0);
|
|
||||||
|
|
||||||
// Case 3: os error
|
|
||||||
let errno = 20;
|
|
||||||
let io_error = Error::from_raw_os_error(errno);
|
|
||||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
|
||||||
|
|
||||||
assert_eq!(errno, io_error.raw_os_error().unwrap());
|
|
||||||
|
|
||||||
// Case 4: simple
|
|
||||||
let kind = ErrorKind::OutOfMemory;
|
|
||||||
let io_error: Error = kind.into();
|
|
||||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
|
||||||
|
|
||||||
assert_eq!(kind, io_error.kind());
|
|
||||||
|
|
||||||
// Case 5: simple message
|
|
||||||
const SIMPLE_MESSAGE: SimpleMessage =
|
|
||||||
SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" };
|
|
||||||
let io_error = Error::from_static_message(&SIMPLE_MESSAGE);
|
|
||||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
|
||||||
|
|
||||||
assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind());
|
|
||||||
assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}"));
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
use crate::io::prelude::*;
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_read_slice(b: &mut test::Bencher) {
|
|
||||||
let buf = [5; 1024];
|
|
||||||
let mut dst = [0; 128];
|
|
||||||
|
|
||||||
b.iter(|| {
|
|
||||||
let mut rd = &buf[..];
|
|
||||||
for _ in 0..8 {
|
|
||||||
let _ = rd.read(&mut dst);
|
|
||||||
test::black_box(&dst);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_write_slice(b: &mut test::Bencher) {
|
|
||||||
let mut buf = [0; 1024];
|
|
||||||
let src = [5; 128];
|
|
||||||
|
|
||||||
b.iter(|| {
|
|
||||||
let mut wr = &mut buf[..];
|
|
||||||
for _ in 0..8 {
|
|
||||||
let _ = wr.write_all(&src);
|
|
||||||
test::black_box(&wr);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_read_vec(b: &mut test::Bencher) {
|
|
||||||
let buf = vec![5; 1024];
|
|
||||||
let mut dst = [0; 128];
|
|
||||||
|
|
||||||
b.iter(|| {
|
|
||||||
let mut rd = &buf[..];
|
|
||||||
for _ in 0..8 {
|
|
||||||
let _ = rd.read(&mut dst);
|
|
||||||
test::black_box(&dst);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_write_vec(b: &mut test::Bencher) {
|
|
||||||
let mut buf = Vec::with_capacity(1024);
|
|
||||||
let src = [5; 128];
|
|
||||||
|
|
||||||
b.iter(|| {
|
|
||||||
let mut wr = &mut buf[..];
|
|
||||||
for _ in 0..8 {
|
|
||||||
let _ = wr.write_all(&src);
|
|
||||||
test::black_box(&wr);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,295 +0,0 @@
|
|||||||
use crate::io;
|
|
||||||
use crate::sys::{FromInner, IntoInner, pipe as imp};
|
|
||||||
|
|
||||||
/// Creates an anonymous pipe.
|
|
||||||
///
|
|
||||||
/// # Behavior
|
|
||||||
///
|
|
||||||
/// A pipe is a one-way data channel provided by the OS, which works across processes. A pipe is
|
|
||||||
/// typically used to communicate between two or more separate processes, as there are better,
|
|
||||||
/// faster ways to communicate within a single process.
|
|
||||||
///
|
|
||||||
/// In particular:
|
|
||||||
///
|
|
||||||
/// * A read on a [`PipeReader`] blocks until the pipe is non-empty.
|
|
||||||
/// * A write on a [`PipeWriter`] blocks when the pipe is full.
|
|
||||||
/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
|
|
||||||
/// returns EOF.
|
|
||||||
/// * [`PipeWriter`] can be shared, and multiple processes or threads can write to it at once, but
|
|
||||||
/// writes (above a target-specific threshold) may have their data interleaved.
|
|
||||||
/// * [`PipeReader`] can be shared, and multiple processes or threads can read it at once. Any
|
|
||||||
/// given byte will only get consumed by one reader. There are no guarantees about data
|
|
||||||
/// interleaving.
|
|
||||||
/// * Portable applications cannot assume any atomicity of messages larger than a single byte.
|
|
||||||
///
|
|
||||||
/// # Platform-specific behavior
|
|
||||||
///
|
|
||||||
/// This function currently corresponds to the `pipe` function on Unix and the
|
|
||||||
/// `CreatePipe` function on Windows.
|
|
||||||
///
|
|
||||||
/// Note that this [may change in the future][changes].
|
|
||||||
///
|
|
||||||
/// # Capacity
|
|
||||||
///
|
|
||||||
/// Pipe capacity is platform dependent. To quote the Linux [man page]:
|
|
||||||
///
|
|
||||||
/// > Different implementations have different limits for the pipe capacity. Applications should
|
|
||||||
/// > not rely on a particular capacity: an application should be designed so that a reading process
|
|
||||||
/// > consumes data as soon as it is available, so that a writing process does not remain blocked.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # #[cfg(miri)] fn main() {}
|
|
||||||
/// # #[cfg(not(miri))]
|
|
||||||
/// # fn main() -> std::io::Result<()> {
|
|
||||||
/// use std::io::{Read, Write, pipe};
|
|
||||||
/// use std::process::Command;
|
|
||||||
/// let (ping_reader, mut ping_writer) = pipe()?;
|
|
||||||
/// let (mut pong_reader, pong_writer) = pipe()?;
|
|
||||||
///
|
|
||||||
/// // Spawn a child process that echoes its input.
|
|
||||||
/// let mut echo_command = Command::new("cat");
|
|
||||||
/// echo_command.stdin(ping_reader);
|
|
||||||
/// echo_command.stdout(pong_writer);
|
|
||||||
/// let mut echo_child = echo_command.spawn()?;
|
|
||||||
///
|
|
||||||
/// // Send input to the child process. Note that because we're writing all the input before we
|
|
||||||
/// // read any output, this could deadlock if the child's input and output pipe buffers both
|
|
||||||
/// // filled up. Those buffers are usually at least a few KB, so "hello" is fine, but for longer
|
|
||||||
/// // inputs we'd need to read and write at the same time, e.g. using threads.
|
|
||||||
/// ping_writer.write_all(b"hello")?;
|
|
||||||
///
|
|
||||||
/// // `cat` exits when it reads EOF from stdin, but that can't happen while any ping writer
|
|
||||||
/// // remains open. We need to drop our ping writer, or read_to_string will deadlock below.
|
|
||||||
/// drop(ping_writer);
|
|
||||||
///
|
|
||||||
/// // The pong reader can't report EOF while any pong writer remains open. Our Command object is
|
|
||||||
/// // holding a pong writer, and again read_to_string will deadlock if we don't drop it.
|
|
||||||
/// drop(echo_command);
|
|
||||||
///
|
|
||||||
/// let mut buf = String::new();
|
|
||||||
/// // Block until `cat` closes its stdout (a pong writer).
|
|
||||||
/// pong_reader.read_to_string(&mut buf)?;
|
|
||||||
/// assert_eq!(&buf, "hello");
|
|
||||||
///
|
|
||||||
/// // At this point we know `cat` has exited, but we still need to wait to clean up the "zombie".
|
|
||||||
/// echo_child.wait()?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
/// [changes]: io#platform-specific-behavior
|
|
||||||
/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
|
|
||||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
|
||||||
#[inline]
|
|
||||||
pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> {
|
|
||||||
imp::pipe().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read end of an anonymous pipe.
|
|
||||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PipeReader(pub(crate) imp::Pipe);
|
|
||||||
|
|
||||||
/// Write end of an anonymous pipe.
|
|
||||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PipeWriter(pub(crate) imp::Pipe);
|
|
||||||
|
|
||||||
impl FromInner<imp::Pipe> for PipeReader {
|
|
||||||
fn from_inner(inner: imp::Pipe) -> Self {
|
|
||||||
Self(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoInner<imp::Pipe> for PipeReader {
|
|
||||||
fn into_inner(self) -> imp::Pipe {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromInner<imp::Pipe> for PipeWriter {
|
|
||||||
fn from_inner(inner: imp::Pipe) -> Self {
|
|
||||||
Self(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoInner<imp::Pipe> for PipeWriter {
|
|
||||||
fn into_inner(self) -> imp::Pipe {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PipeReader {
|
|
||||||
/// Creates a new [`PipeReader`] instance that shares the same underlying file description.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # #[cfg(miri)] fn main() {}
|
|
||||||
/// # #[cfg(not(miri))]
|
|
||||||
/// # fn main() -> std::io::Result<()> {
|
|
||||||
/// use std::fs;
|
|
||||||
/// use std::io::{pipe, Write};
|
|
||||||
/// use std::process::Command;
|
|
||||||
/// const NUM_SLOT: u8 = 2;
|
|
||||||
/// const NUM_PROC: u8 = 5;
|
|
||||||
/// const OUTPUT: &str = "work.txt";
|
|
||||||
///
|
|
||||||
/// let mut jobs = vec![];
|
|
||||||
/// let (reader, mut writer) = pipe()?;
|
|
||||||
///
|
|
||||||
/// // Write NUM_SLOT characters the pipe.
|
|
||||||
/// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
|
|
||||||
///
|
|
||||||
/// // Spawn several processes that read a character from the pipe, do some work, then
|
|
||||||
/// // write back to the pipe. When the pipe is empty, the processes block, so only
|
|
||||||
/// // NUM_SLOT processes can be working at any given time.
|
|
||||||
/// for _ in 0..NUM_PROC {
|
|
||||||
/// jobs.push(
|
|
||||||
/// Command::new("bash")
|
|
||||||
/// .args(["-c",
|
|
||||||
/// &format!(
|
|
||||||
/// "read -n 1\n\
|
|
||||||
/// echo -n 'x' >> '{OUTPUT}'\n\
|
|
||||||
/// echo -n '|'",
|
|
||||||
/// ),
|
|
||||||
/// ])
|
|
||||||
/// .stdin(reader.try_clone()?)
|
|
||||||
/// .stdout(writer.try_clone()?)
|
|
||||||
/// .spawn()?,
|
|
||||||
/// );
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Wait for all jobs to finish.
|
|
||||||
/// for mut job in jobs {
|
|
||||||
/// job.wait()?;
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Check our work and clean up.
|
|
||||||
/// let xs = fs::read_to_string(OUTPUT)?;
|
|
||||||
/// fs::remove_file(OUTPUT)?;
|
|
||||||
/// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
|
||||||
pub fn try_clone(&self) -> io::Result<Self> {
|
|
||||||
self.0.try_clone().map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PipeWriter {
|
|
||||||
/// Creates a new [`PipeWriter`] instance that shares the same underlying file description.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # #[cfg(miri)] fn main() {}
|
|
||||||
/// # #[cfg(not(miri))]
|
|
||||||
/// # fn main() -> std::io::Result<()> {
|
|
||||||
/// use std::process::Command;
|
|
||||||
/// use std::io::{pipe, Read};
|
|
||||||
/// let (mut reader, writer) = pipe()?;
|
|
||||||
///
|
|
||||||
/// // Spawn a process that writes to stdout and stderr.
|
|
||||||
/// let mut peer = Command::new("bash")
|
|
||||||
/// .args([
|
|
||||||
/// "-c",
|
|
||||||
/// "echo -n foo\n\
|
|
||||||
/// echo -n bar >&2"
|
|
||||||
/// ])
|
|
||||||
/// .stdout(writer.try_clone()?)
|
|
||||||
/// .stderr(writer)
|
|
||||||
/// .spawn()?;
|
|
||||||
///
|
|
||||||
/// // Read and check the result.
|
|
||||||
/// let mut msg = String::new();
|
|
||||||
/// reader.read_to_string(&mut msg)?;
|
|
||||||
/// assert_eq!(&msg, "foobar");
|
|
||||||
///
|
|
||||||
/// peer.wait()?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
|
||||||
pub fn try_clone(&self) -> io::Result<Self> {
|
|
||||||
self.0.try_clone().map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
|
||||||
impl io::Read for &PipeReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
self.0.read(buf)
|
|
||||||
}
|
|
||||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
|
||||||
self.0.read_vectored(bufs)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_read_vectored(&self) -> bool {
|
|
||||||
self.0.is_read_vectored()
|
|
||||||
}
|
|
||||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
|
||||||
self.0.read_to_end(buf)
|
|
||||||
}
|
|
||||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
|
||||||
self.0.read_buf(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
|
||||||
impl io::Read for PipeReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
self.0.read(buf)
|
|
||||||
}
|
|
||||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
|
||||||
self.0.read_vectored(bufs)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_read_vectored(&self) -> bool {
|
|
||||||
self.0.is_read_vectored()
|
|
||||||
}
|
|
||||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
|
||||||
self.0.read_to_end(buf)
|
|
||||||
}
|
|
||||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
|
||||||
self.0.read_buf(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
|
||||||
impl io::Write for &PipeWriter {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.0.write(buf)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
|
||||||
self.0.write_vectored(bufs)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_write_vectored(&self) -> bool {
|
|
||||||
self.0.is_write_vectored()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
|
||||||
impl io::Write for PipeWriter {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.0.write(buf)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
|
||||||
self.0.write_vectored(bufs)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_write_vectored(&self) -> bool {
|
|
||||||
self.0.is_write_vectored()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
use crate::io::{Read, Write, pipe};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(all(any(unix, windows), not(miri)))]
|
|
||||||
fn pipe_creation_clone_and_rw() {
|
|
||||||
let (rx, tx) = pipe().unwrap();
|
|
||||||
|
|
||||||
tx.try_clone().unwrap().write_all(b"12345").unwrap();
|
|
||||||
drop(tx);
|
|
||||||
|
|
||||||
let mut rx2 = rx.try_clone().unwrap();
|
|
||||||
drop(rx);
|
|
||||||
|
|
||||||
let mut s = String::new();
|
|
||||||
rx2.read_to_string(&mut s).unwrap();
|
|
||||||
drop(rx2);
|
|
||||||
assert_eq!(s, "12345");
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,947 +0,0 @@
|
|||||||
use super::{BorrowedBuf, Cursor, SeekFrom, repeat};
|
|
||||||
use crate::cmp::{self, min};
|
|
||||||
use crate::io::{
|
|
||||||
self, BufRead, BufReader, DEFAULT_BUF_SIZE, IoSlice, IoSliceMut, Read, Seek, Write,
|
|
||||||
};
|
|
||||||
use crate::mem::MaybeUninit;
|
|
||||||
use crate::ops::Deref;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_until() {
|
|
||||||
let mut buf = Cursor::new(&b"12"[..]);
|
|
||||||
let mut v = Vec::new();
|
|
||||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2);
|
|
||||||
assert_eq!(v, b"12");
|
|
||||||
|
|
||||||
let mut buf = Cursor::new(&b"1233"[..]);
|
|
||||||
let mut v = Vec::new();
|
|
||||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3);
|
|
||||||
assert_eq!(v, b"123");
|
|
||||||
v.truncate(0);
|
|
||||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1);
|
|
||||||
assert_eq!(v, b"3");
|
|
||||||
v.truncate(0);
|
|
||||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0);
|
|
||||||
assert_eq!(v, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn skip_until() {
|
|
||||||
let bytes: &[u8] = b"read\0ignore\0read\0ignore\0read\0ignore\0";
|
|
||||||
let mut reader = BufReader::new(bytes);
|
|
||||||
|
|
||||||
// read from the bytes, alternating between
|
|
||||||
// consuming `read\0`s and skipping `ignore\0`s
|
|
||||||
loop {
|
|
||||||
// consume `read\0`
|
|
||||||
let mut out = Vec::new();
|
|
||||||
let read = reader.read_until(0, &mut out).unwrap();
|
|
||||||
if read == 0 {
|
|
||||||
// eof
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
assert_eq!(out, b"read\0");
|
|
||||||
assert_eq!(read, b"read\0".len());
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip past `ignore\0`
|
|
||||||
let skipped = reader.skip_until(0).unwrap();
|
|
||||||
assert_eq!(skipped, b"ignore\0".len());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we are at the end of the byte slice and that we can skip no further
|
|
||||||
// also ensure skip_until matches the behavior of read_until at EOF
|
|
||||||
let skipped = reader.skip_until(0).unwrap();
|
|
||||||
assert_eq!(skipped, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn split() {
|
|
||||||
let buf = Cursor::new(&b"12"[..]);
|
|
||||||
let mut s = buf.split(b'3');
|
|
||||||
assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
|
|
||||||
assert!(s.next().is_none());
|
|
||||||
|
|
||||||
let buf = Cursor::new(&b"1233"[..]);
|
|
||||||
let mut s = buf.split(b'3');
|
|
||||||
assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
|
|
||||||
assert_eq!(s.next().unwrap().unwrap(), vec![]);
|
|
||||||
assert!(s.next().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_line() {
|
|
||||||
let mut buf = Cursor::new(&b"12"[..]);
|
|
||||||
let mut v = String::new();
|
|
||||||
assert_eq!(buf.read_line(&mut v).unwrap(), 2);
|
|
||||||
assert_eq!(v, "12");
|
|
||||||
|
|
||||||
let mut buf = Cursor::new(&b"12\n\n"[..]);
|
|
||||||
let mut v = String::new();
|
|
||||||
assert_eq!(buf.read_line(&mut v).unwrap(), 3);
|
|
||||||
assert_eq!(v, "12\n");
|
|
||||||
v.truncate(0);
|
|
||||||
assert_eq!(buf.read_line(&mut v).unwrap(), 1);
|
|
||||||
assert_eq!(v, "\n");
|
|
||||||
v.truncate(0);
|
|
||||||
assert_eq!(buf.read_line(&mut v).unwrap(), 0);
|
|
||||||
assert_eq!(v, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn lines() {
|
|
||||||
let buf = Cursor::new(&b"12\r"[..]);
|
|
||||||
let mut s = buf.lines();
|
|
||||||
assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string());
|
|
||||||
assert!(s.next().is_none());
|
|
||||||
|
|
||||||
let buf = Cursor::new(&b"12\r\n\n"[..]);
|
|
||||||
let mut s = buf.lines();
|
|
||||||
assert_eq!(s.next().unwrap().unwrap(), "12".to_string());
|
|
||||||
assert_eq!(s.next().unwrap().unwrap(), "".to_string());
|
|
||||||
assert!(s.next().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn buf_read_has_data_left() {
|
|
||||||
let mut buf = Cursor::new(&b"abcd"[..]);
|
|
||||||
assert!(buf.has_data_left().unwrap());
|
|
||||||
buf.read_exact(&mut [0; 2]).unwrap();
|
|
||||||
assert!(buf.has_data_left().unwrap());
|
|
||||||
buf.read_exact(&mut [0; 2]).unwrap();
|
|
||||||
assert!(!buf.has_data_left().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_to_end() {
|
|
||||||
let mut c = Cursor::new(&b""[..]);
|
|
||||||
let mut v = Vec::new();
|
|
||||||
assert_eq!(c.read_to_end(&mut v).unwrap(), 0);
|
|
||||||
assert_eq!(v, []);
|
|
||||||
|
|
||||||
let mut c = Cursor::new(&b"1"[..]);
|
|
||||||
let mut v = Vec::new();
|
|
||||||
assert_eq!(c.read_to_end(&mut v).unwrap(), 1);
|
|
||||||
assert_eq!(v, b"1");
|
|
||||||
|
|
||||||
let cap = if cfg!(miri) { 1024 } else { 1024 * 1024 };
|
|
||||||
let data = (0..cap).map(|i| (i / 3) as u8).collect::<Vec<_>>();
|
|
||||||
let mut v = Vec::new();
|
|
||||||
let (a, b) = data.split_at(data.len() / 2);
|
|
||||||
assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len());
|
|
||||||
assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len());
|
|
||||||
assert_eq!(v, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_to_string() {
|
|
||||||
let mut c = Cursor::new(&b""[..]);
|
|
||||||
let mut v = String::new();
|
|
||||||
assert_eq!(c.read_to_string(&mut v).unwrap(), 0);
|
|
||||||
assert_eq!(v, "");
|
|
||||||
|
|
||||||
let mut c = Cursor::new(&b"1"[..]);
|
|
||||||
let mut v = String::new();
|
|
||||||
assert_eq!(c.read_to_string(&mut v).unwrap(), 1);
|
|
||||||
assert_eq!(v, "1");
|
|
||||||
|
|
||||||
let mut c = Cursor::new(&b"\xff"[..]);
|
|
||||||
let mut v = String::new();
|
|
||||||
assert!(c.read_to_string(&mut v).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_exact() {
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
|
|
||||||
let mut c = Cursor::new(&b""[..]);
|
|
||||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
|
||||||
|
|
||||||
let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..]));
|
|
||||||
c.read_exact(&mut buf).unwrap();
|
|
||||||
assert_eq!(&buf, b"1234");
|
|
||||||
c.read_exact(&mut buf).unwrap();
|
|
||||||
assert_eq!(&buf, b"5678");
|
|
||||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_exact_slice() {
|
|
||||||
let mut buf = [0; 4];
|
|
||||||
|
|
||||||
let mut c = &b""[..];
|
|
||||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
|
||||||
|
|
||||||
let mut c = &b"123"[..];
|
|
||||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
|
||||||
// make sure the optimized (early returning) method is being used
|
|
||||||
assert_eq!(&buf, &[0; 4]);
|
|
||||||
|
|
||||||
let mut c = &b"1234"[..];
|
|
||||||
c.read_exact(&mut buf).unwrap();
|
|
||||||
assert_eq!(&buf, b"1234");
|
|
||||||
|
|
||||||
let mut c = &b"56789"[..];
|
|
||||||
c.read_exact(&mut buf).unwrap();
|
|
||||||
assert_eq!(&buf, b"5678");
|
|
||||||
assert_eq!(c, b"9");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_buf_exact() {
|
|
||||||
let buf: &mut [_] = &mut [0; 4];
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
|
|
||||||
let mut c = Cursor::new(&b""[..]);
|
|
||||||
assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
|
||||||
|
|
||||||
let mut c = Cursor::new(&b"123456789"[..]);
|
|
||||||
c.read_buf_exact(buf.unfilled()).unwrap();
|
|
||||||
assert_eq!(buf.filled(), b"1234");
|
|
||||||
|
|
||||||
buf.clear();
|
|
||||||
|
|
||||||
c.read_buf_exact(buf.unfilled()).unwrap();
|
|
||||||
assert_eq!(buf.filled(), b"5678");
|
|
||||||
|
|
||||||
buf.clear();
|
|
||||||
|
|
||||||
assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn borrowed_cursor_advance_overflow() {
|
|
||||||
let mut buf = [0; 512];
|
|
||||||
let mut buf = BorrowedBuf::from(&mut buf[..]);
|
|
||||||
buf.unfilled().advance(1);
|
|
||||||
buf.unfilled().advance(usize::MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn take_eof() {
|
|
||||||
struct R;
|
|
||||||
|
|
||||||
impl Read for R {
|
|
||||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
|
||||||
Err(io::const_error!(io::ErrorKind::Other, ""))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl BufRead for R {
|
|
||||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
|
||||||
Err(io::const_error!(io::ErrorKind::Other, ""))
|
|
||||||
}
|
|
||||||
fn consume(&mut self, _amt: usize) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = [0; 1];
|
|
||||||
assert_eq!(0, R.take(0).read(&mut buf).unwrap());
|
|
||||||
assert_eq!(b"", R.take(0).fill_buf().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmp_bufread<Br1: BufRead, Br2: BufRead>(mut br1: Br1, mut br2: Br2, exp: &[u8]) {
|
|
||||||
let mut cat = Vec::new();
|
|
||||||
loop {
|
|
||||||
let consume = {
|
|
||||||
let buf1 = br1.fill_buf().unwrap();
|
|
||||||
let buf2 = br2.fill_buf().unwrap();
|
|
||||||
let minlen = if buf1.len() < buf2.len() { buf1.len() } else { buf2.len() };
|
|
||||||
assert_eq!(buf1[..minlen], buf2[..minlen]);
|
|
||||||
cat.extend_from_slice(&buf1[..minlen]);
|
|
||||||
minlen
|
|
||||||
};
|
|
||||||
if consume == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
br1.consume(consume);
|
|
||||||
br2.consume(consume);
|
|
||||||
}
|
|
||||||
assert_eq!(br1.fill_buf().unwrap().len(), 0);
|
|
||||||
assert_eq!(br2.fill_buf().unwrap().len(), 0);
|
|
||||||
assert_eq!(&cat[..], &exp[..])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn chain_bufread() {
|
|
||||||
let testdata = b"ABCDEFGHIJKL";
|
|
||||||
let chain1 =
|
|
||||||
(&testdata[..3]).chain(&testdata[3..6]).chain(&testdata[6..9]).chain(&testdata[9..]);
|
|
||||||
let chain2 = (&testdata[..4]).chain(&testdata[4..8]).chain(&testdata[8..]);
|
|
||||||
cmp_bufread(chain1, chain2, &testdata[..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn chain_splitted_char() {
|
|
||||||
let chain = b"\xc3".chain(b"\xa9".as_slice());
|
|
||||||
assert_eq!(crate::io::read_to_string(chain).unwrap(), "é");
|
|
||||||
|
|
||||||
let mut chain = b"\xc3".chain(b"\xa9\n".as_slice());
|
|
||||||
let mut buf = String::new();
|
|
||||||
assert_eq!(chain.read_line(&mut buf).unwrap(), 3);
|
|
||||||
assert_eq!(buf, "é\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bufreader_size_hint() {
|
|
||||||
let testdata = b"ABCDEFGHIJKL";
|
|
||||||
let mut buf_reader = BufReader::new(&testdata[..]);
|
|
||||||
assert_eq!(buf_reader.buffer().len(), 0);
|
|
||||||
|
|
||||||
let buffer_length = testdata.len();
|
|
||||||
buf_reader.fill_buf().unwrap();
|
|
||||||
|
|
||||||
// Check that size hint matches buffer contents
|
|
||||||
let mut buffered_bytes = buf_reader.bytes();
|
|
||||||
let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
|
|
||||||
assert_eq!(lower_bound, buffer_length);
|
|
||||||
|
|
||||||
// Check that size hint matches buffer contents after advancing
|
|
||||||
buffered_bytes.next().unwrap().unwrap();
|
|
||||||
let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
|
|
||||||
assert_eq!(lower_bound, buffer_length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_size_hint() {
|
|
||||||
let size_hint = io::empty().bytes().size_hint();
|
|
||||||
assert_eq!(size_hint, (0, Some(0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn slice_size_hint() {
|
|
||||||
let size_hint = (&[1, 2, 3]).bytes().size_hint();
|
|
||||||
assert_eq!(size_hint, (3, Some(3)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn take_size_hint() {
|
|
||||||
let size_hint = (&[1, 2, 3]).take(2).bytes().size_hint();
|
|
||||||
assert_eq!(size_hint, (2, Some(2)));
|
|
||||||
|
|
||||||
let size_hint = (&[1, 2, 3]).take(4).bytes().size_hint();
|
|
||||||
assert_eq!(size_hint, (3, Some(3)));
|
|
||||||
|
|
||||||
let size_hint = io::repeat(0).take(3).bytes().size_hint();
|
|
||||||
assert_eq!(size_hint, (3, Some(3)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn chain_empty_size_hint() {
|
|
||||||
let chain = io::empty().chain(io::empty());
|
|
||||||
let size_hint = chain.bytes().size_hint();
|
|
||||||
assert_eq!(size_hint, (0, Some(0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn chain_size_hint() {
|
|
||||||
let testdata = b"ABCDEFGHIJKL";
|
|
||||||
let mut buf_reader_1 = BufReader::new(&testdata[..6]);
|
|
||||||
let mut buf_reader_2 = BufReader::new(&testdata[6..]);
|
|
||||||
|
|
||||||
buf_reader_1.fill_buf().unwrap();
|
|
||||||
buf_reader_2.fill_buf().unwrap();
|
|
||||||
|
|
||||||
let chain = buf_reader_1.chain(buf_reader_2);
|
|
||||||
let size_hint = chain.bytes().size_hint();
|
|
||||||
assert_eq!(size_hint, (testdata.len(), Some(testdata.len())));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn chain_zero_length_read_is_not_eof() {
|
|
||||||
let a = b"A";
|
|
||||||
let b = b"B";
|
|
||||||
let mut s = String::new();
|
|
||||||
let mut chain = (&a[..]).chain(&b[..]);
|
|
||||||
chain.read(&mut []).unwrap();
|
|
||||||
chain.read_to_string(&mut s).unwrap();
|
|
||||||
assert_eq!("AB", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
#[cfg_attr(miri, ignore)] // Miri isn't fast...
|
|
||||||
fn bench_read_to_end(b: &mut test::Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut lr = repeat(1).take(10000000);
|
|
||||||
let mut vec = Vec::with_capacity(1024);
|
|
||||||
super::default_read_to_end(&mut lr, &mut vec, None)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn seek_len() -> io::Result<()> {
|
|
||||||
let mut c = Cursor::new(vec![0; 15]);
|
|
||||||
assert_eq!(c.stream_len()?, 15);
|
|
||||||
|
|
||||||
c.seek(SeekFrom::End(0))?;
|
|
||||||
let old_pos = c.stream_position()?;
|
|
||||||
assert_eq!(c.stream_len()?, 15);
|
|
||||||
assert_eq!(c.stream_position()?, old_pos);
|
|
||||||
|
|
||||||
c.seek(SeekFrom::Start(7))?;
|
|
||||||
c.seek(SeekFrom::Current(2))?;
|
|
||||||
let old_pos = c.stream_position()?;
|
|
||||||
assert_eq!(c.stream_len()?, 15);
|
|
||||||
assert_eq!(c.stream_position()?, old_pos);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn seek_position() -> io::Result<()> {
|
|
||||||
// All `asserts` are duplicated here to make sure the method does not
|
|
||||||
// change anything about the seek state.
|
|
||||||
let mut c = Cursor::new(vec![0; 15]);
|
|
||||||
assert_eq!(c.stream_position()?, 0);
|
|
||||||
assert_eq!(c.stream_position()?, 0);
|
|
||||||
|
|
||||||
c.seek(SeekFrom::End(0))?;
|
|
||||||
assert_eq!(c.stream_position()?, 15);
|
|
||||||
assert_eq!(c.stream_position()?, 15);
|
|
||||||
|
|
||||||
c.seek(SeekFrom::Start(7))?;
|
|
||||||
c.seek(SeekFrom::Current(2))?;
|
|
||||||
assert_eq!(c.stream_position()?, 9);
|
|
||||||
assert_eq!(c.stream_position()?, 9);
|
|
||||||
|
|
||||||
c.seek(SeekFrom::End(-3))?;
|
|
||||||
c.seek(SeekFrom::Current(1))?;
|
|
||||||
c.seek(SeekFrom::Current(-5))?;
|
|
||||||
assert_eq!(c.stream_position()?, 8);
|
|
||||||
assert_eq!(c.stream_position()?, 8);
|
|
||||||
|
|
||||||
c.rewind()?;
|
|
||||||
assert_eq!(c.stream_position()?, 0);
|
|
||||||
assert_eq!(c.stream_position()?, 0);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn take_seek() -> io::Result<()> {
|
|
||||||
let mut buf = Cursor::new(b"0123456789");
|
|
||||||
buf.set_position(2);
|
|
||||||
let mut take = buf.by_ref().take(4);
|
|
||||||
let mut buf1 = [0u8; 1];
|
|
||||||
let mut buf2 = [0u8; 2];
|
|
||||||
assert_eq!(take.position(), 0);
|
|
||||||
|
|
||||||
assert_eq!(take.seek(SeekFrom::Start(0))?, 0);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'2', b'3']);
|
|
||||||
assert_eq!(take.seek(SeekFrom::Start(1))?, 1);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'3', b'4']);
|
|
||||||
assert_eq!(take.seek(SeekFrom::Start(2))?, 2);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'4', b'5']);
|
|
||||||
assert_eq!(take.seek(SeekFrom::Start(3))?, 3);
|
|
||||||
take.read_exact(&mut buf1)?;
|
|
||||||
assert_eq!(buf1, [b'5']);
|
|
||||||
assert_eq!(take.seek(SeekFrom::Start(4))?, 4);
|
|
||||||
assert_eq!(take.read(&mut buf1)?, 0);
|
|
||||||
|
|
||||||
assert_eq!(take.seek(SeekFrom::End(0))?, 4);
|
|
||||||
assert_eq!(take.seek(SeekFrom::End(-1))?, 3);
|
|
||||||
take.read_exact(&mut buf1)?;
|
|
||||||
assert_eq!(buf1, [b'5']);
|
|
||||||
assert_eq!(take.seek(SeekFrom::End(-2))?, 2);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'4', b'5']);
|
|
||||||
assert_eq!(take.seek(SeekFrom::End(-3))?, 1);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'3', b'4']);
|
|
||||||
assert_eq!(take.seek(SeekFrom::End(-4))?, 0);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'2', b'3']);
|
|
||||||
|
|
||||||
assert_eq!(take.seek(SeekFrom::Current(0))?, 2);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'4', b'5']);
|
|
||||||
|
|
||||||
assert_eq!(take.seek(SeekFrom::Current(-3))?, 1);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'3', b'4']);
|
|
||||||
|
|
||||||
assert_eq!(take.seek(SeekFrom::Current(-1))?, 2);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'4', b'5']);
|
|
||||||
|
|
||||||
assert_eq!(take.seek(SeekFrom::Current(-4))?, 0);
|
|
||||||
take.read_exact(&mut buf2)?;
|
|
||||||
assert_eq!(buf2, [b'2', b'3']);
|
|
||||||
|
|
||||||
assert_eq!(take.seek(SeekFrom::Current(2))?, 4);
|
|
||||||
assert_eq!(take.read(&mut buf1)?, 0);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn take_seek_error() {
|
|
||||||
let buf = Cursor::new(b"0123456789");
|
|
||||||
let mut take = buf.take(2);
|
|
||||||
assert!(take.seek(SeekFrom::Start(3)).is_err());
|
|
||||||
assert!(take.seek(SeekFrom::End(1)).is_err());
|
|
||||||
assert!(take.seek(SeekFrom::End(-3)).is_err());
|
|
||||||
assert!(take.seek(SeekFrom::Current(-1)).is_err());
|
|
||||||
assert!(take.seek(SeekFrom::Current(3)).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ExampleHugeRangeOfZeroes {
|
|
||||||
position: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for ExampleHugeRangeOfZeroes {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
let max = buf.len().min(usize::MAX);
|
|
||||||
for i in 0..max {
|
|
||||||
if self.position == u64::MAX {
|
|
||||||
return Ok(i);
|
|
||||||
}
|
|
||||||
self.position += 1;
|
|
||||||
buf[i] = 0;
|
|
||||||
}
|
|
||||||
Ok(max)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Seek for ExampleHugeRangeOfZeroes {
|
|
||||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
|
||||||
match pos {
|
|
||||||
io::SeekFrom::Start(i) => self.position = i,
|
|
||||||
io::SeekFrom::End(i) if i >= 0 => self.position = u64::MAX,
|
|
||||||
io::SeekFrom::End(i) => self.position = self.position - i.unsigned_abs(),
|
|
||||||
io::SeekFrom::Current(i) => {
|
|
||||||
self.position = if i >= 0 {
|
|
||||||
self.position.saturating_add(i.unsigned_abs())
|
|
||||||
} else {
|
|
||||||
self.position.saturating_sub(i.unsigned_abs())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(self.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn take_seek_big_offsets() -> io::Result<()> {
|
|
||||||
let inner = ExampleHugeRangeOfZeroes { position: 1 };
|
|
||||||
let mut take = inner.take(u64::MAX - 2);
|
|
||||||
assert_eq!(take.seek(io::SeekFrom::Start(u64::MAX - 2))?, u64::MAX - 2);
|
|
||||||
assert_eq!(take.inner.position, u64::MAX - 1);
|
|
||||||
assert_eq!(take.seek(io::SeekFrom::Start(0))?, 0);
|
|
||||||
assert_eq!(take.inner.position, 1);
|
|
||||||
assert_eq!(take.seek(io::SeekFrom::End(-1))?, u64::MAX - 3);
|
|
||||||
assert_eq!(take.inner.position, u64::MAX - 2);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// A simple example reader which uses the default implementation of
|
|
||||||
// read_to_end.
|
|
||||||
struct ExampleSliceReader<'a> {
|
|
||||||
slice: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Read for ExampleSliceReader<'a> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
let len = cmp::min(self.slice.len(), buf.len());
|
|
||||||
buf[..len].copy_from_slice(&self.slice[..len]);
|
|
||||||
self.slice = &self.slice[len..];
|
|
||||||
Ok(len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_read_to_end_capacity() -> io::Result<()> {
|
|
||||||
let input = &b"foo"[..];
|
|
||||||
|
|
||||||
// read_to_end() takes care not to over-allocate when a buffer is the
|
|
||||||
// exact size needed.
|
|
||||||
let mut vec1 = Vec::with_capacity(input.len());
|
|
||||||
ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?;
|
|
||||||
assert_eq!(vec1.len(), input.len());
|
|
||||||
assert_eq!(vec1.capacity(), input.len(), "did not allocate more");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn io_slice_mut_advance_slices() {
|
|
||||||
let mut buf1 = [1; 8];
|
|
||||||
let mut buf2 = [2; 16];
|
|
||||||
let mut buf3 = [3; 8];
|
|
||||||
let mut bufs = &mut [
|
|
||||||
IoSliceMut::new(&mut buf1),
|
|
||||||
IoSliceMut::new(&mut buf2),
|
|
||||||
IoSliceMut::new(&mut buf3),
|
|
||||||
][..];
|
|
||||||
|
|
||||||
// Only in a single buffer..
|
|
||||||
IoSliceMut::advance_slices(&mut bufs, 1);
|
|
||||||
assert_eq!(bufs[0].deref(), [1; 7].as_ref());
|
|
||||||
assert_eq!(bufs[1].deref(), [2; 16].as_ref());
|
|
||||||
assert_eq!(bufs[2].deref(), [3; 8].as_ref());
|
|
||||||
|
|
||||||
// Removing a buffer, leaving others as is.
|
|
||||||
IoSliceMut::advance_slices(&mut bufs, 7);
|
|
||||||
assert_eq!(bufs[0].deref(), [2; 16].as_ref());
|
|
||||||
assert_eq!(bufs[1].deref(), [3; 8].as_ref());
|
|
||||||
|
|
||||||
// Removing a buffer and removing from the next buffer.
|
|
||||||
IoSliceMut::advance_slices(&mut bufs, 18);
|
|
||||||
assert_eq!(bufs[0].deref(), [3; 6].as_ref());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn io_slice_mut_advance_slices_empty_slice() {
|
|
||||||
let mut empty_bufs = &mut [][..];
|
|
||||||
IoSliceMut::advance_slices(&mut empty_bufs, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn io_slice_mut_advance_slices_beyond_total_length() {
|
|
||||||
let mut buf1 = [1; 8];
|
|
||||||
let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..];
|
|
||||||
|
|
||||||
IoSliceMut::advance_slices(&mut bufs, 9);
|
|
||||||
assert!(bufs.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn io_slice_advance_slices() {
|
|
||||||
let buf1 = [1; 8];
|
|
||||||
let buf2 = [2; 16];
|
|
||||||
let buf3 = [3; 8];
|
|
||||||
let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..];
|
|
||||||
|
|
||||||
// Only in a single buffer..
|
|
||||||
IoSlice::advance_slices(&mut bufs, 1);
|
|
||||||
assert_eq!(bufs[0].deref(), [1; 7].as_ref());
|
|
||||||
assert_eq!(bufs[1].deref(), [2; 16].as_ref());
|
|
||||||
assert_eq!(bufs[2].deref(), [3; 8].as_ref());
|
|
||||||
|
|
||||||
// Removing a buffer, leaving others as is.
|
|
||||||
IoSlice::advance_slices(&mut bufs, 7);
|
|
||||||
assert_eq!(bufs[0].deref(), [2; 16].as_ref());
|
|
||||||
assert_eq!(bufs[1].deref(), [3; 8].as_ref());
|
|
||||||
|
|
||||||
// Removing a buffer and removing from the next buffer.
|
|
||||||
IoSlice::advance_slices(&mut bufs, 18);
|
|
||||||
assert_eq!(bufs[0].deref(), [3; 6].as_ref());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn io_slice_advance_slices_empty_slice() {
|
|
||||||
let mut empty_bufs = &mut [][..];
|
|
||||||
IoSlice::advance_slices(&mut empty_bufs, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn io_slice_advance_slices_beyond_total_length() {
|
|
||||||
let buf1 = [1; 8];
|
|
||||||
let mut bufs = &mut [IoSlice::new(&buf1)][..];
|
|
||||||
|
|
||||||
IoSlice::advance_slices(&mut bufs, 9);
|
|
||||||
assert!(bufs.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn io_slice_as_slice() {
|
|
||||||
let buf = [1; 8];
|
|
||||||
let slice = IoSlice::new(&buf).as_slice();
|
|
||||||
assert_eq!(slice, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn io_slice_into_slice() {
|
|
||||||
let mut buf = [1; 8];
|
|
||||||
let slice = IoSliceMut::new(&mut buf).into_slice();
|
|
||||||
assert_eq!(slice, [1; 8]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new writer that reads from at most `n_bufs` and reads
|
|
||||||
/// `per_call` bytes (in total) per call to write.
|
|
||||||
fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter {
|
|
||||||
TestWriter { n_bufs, per_call, written: Vec::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestWriter {
|
|
||||||
n_bufs: usize,
|
|
||||||
per_call: usize,
|
|
||||||
written: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for TestWriter {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.write_vectored(&[IoSlice::new(buf)])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
|
||||||
let mut left = self.per_call;
|
|
||||||
let mut written = 0;
|
|
||||||
for buf in bufs.iter().take(self.n_bufs) {
|
|
||||||
let n = min(left, buf.len());
|
|
||||||
self.written.extend_from_slice(&buf[0..n]);
|
|
||||||
left -= n;
|
|
||||||
written += n;
|
|
||||||
}
|
|
||||||
Ok(written)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_writer_read_from_one_buf() {
|
|
||||||
let mut writer = test_writer(1, 2);
|
|
||||||
|
|
||||||
assert_eq!(writer.write(&[]).unwrap(), 0);
|
|
||||||
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
|
||||||
|
|
||||||
// Read at most 2 bytes.
|
|
||||||
assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2);
|
|
||||||
let bufs = &[IoSlice::new(&[2, 2, 2])];
|
|
||||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 2);
|
|
||||||
|
|
||||||
// Only read from first buf.
|
|
||||||
let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])];
|
|
||||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 1);
|
|
||||||
|
|
||||||
assert_eq!(writer.written, &[1, 1, 2, 2, 3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_writer_read_from_multiple_bufs() {
|
|
||||||
let mut writer = test_writer(3, 3);
|
|
||||||
|
|
||||||
// Read at most 3 bytes from two buffers.
|
|
||||||
let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])];
|
|
||||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
|
|
||||||
|
|
||||||
// Read at most 3 bytes from three buffers.
|
|
||||||
let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])];
|
|
||||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
|
|
||||||
|
|
||||||
assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_write_all_vectored() {
|
|
||||||
#[rustfmt::skip] // Becomes unreadable otherwise.
|
|
||||||
let tests: Vec<(_, &'static [u8])> = vec![
|
|
||||||
(vec![], &[]),
|
|
||||||
(vec![IoSlice::new(&[]), IoSlice::new(&[])], &[]),
|
|
||||||
(vec![IoSlice::new(&[1])], &[1]),
|
|
||||||
(vec![IoSlice::new(&[1, 2])], &[1, 2]),
|
|
||||||
(vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]),
|
|
||||||
(vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]),
|
|
||||||
(vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]),
|
|
||||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]),
|
|
||||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]),
|
|
||||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]),
|
|
||||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
|
|
||||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
|
|
||||||
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]),
|
|
||||||
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]),
|
|
||||||
(vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]),
|
|
||||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]),
|
|
||||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]),
|
|
||||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]),
|
|
||||||
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]),
|
|
||||||
];
|
|
||||||
|
|
||||||
let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)];
|
|
||||||
|
|
||||||
for (n_bufs, per_call) in writer_configs.iter().copied() {
|
|
||||||
for (mut input, wanted) in tests.clone().into_iter() {
|
|
||||||
let mut writer = test_writer(n_bufs, per_call);
|
|
||||||
assert!(writer.write_all_vectored(&mut *input).is_ok());
|
|
||||||
assert_eq!(&*writer.written, &*wanted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue 94981
|
|
||||||
#[test]
|
|
||||||
#[should_panic = "number of read bytes exceeds limit"]
|
|
||||||
fn test_take_wrong_length() {
|
|
||||||
struct LieAboutSize(bool);
|
|
||||||
|
|
||||||
impl Read for LieAboutSize {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
// Lie about the read size at first time of read.
|
|
||||||
if core::mem::take(&mut self.0) { Ok(buf.len() + 1) } else { Ok(buf.len()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buffer = vec![0; 4];
|
|
||||||
let mut reader = LieAboutSize(true).take(4);
|
|
||||||
// Primed the `Limit` by lying about the read size.
|
|
||||||
let _ = reader.read(&mut buffer[..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn slice_read_exact_eof() {
|
|
||||||
let slice = &b"123456"[..];
|
|
||||||
|
|
||||||
let mut r = slice;
|
|
||||||
assert!(r.read_exact(&mut [0; 10]).is_err());
|
|
||||||
assert!(r.is_empty());
|
|
||||||
|
|
||||||
let mut r = slice;
|
|
||||||
let buf = &mut [0; 10];
|
|
||||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
|
||||||
assert!(r.read_buf_exact(buf.unfilled()).is_err());
|
|
||||||
assert!(r.is_empty());
|
|
||||||
assert_eq!(buf.filled(), b"123456");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn cursor_read_exact_eof() {
|
|
||||||
let slice = Cursor::new(b"123456");
|
|
||||||
|
|
||||||
let mut r = slice.clone();
|
|
||||||
assert!(r.read_exact(&mut [0; 10]).is_err());
|
|
||||||
assert!(Cursor::split(&r).1.is_empty());
|
|
||||||
|
|
||||||
let mut r = slice;
|
|
||||||
let buf = &mut [0; 10];
|
|
||||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
|
||||||
assert!(r.read_buf_exact(buf.unfilled()).is_err());
|
|
||||||
assert!(Cursor::split(&r).1.is_empty());
|
|
||||||
assert_eq!(buf.filled(), b"123456");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_take_read(b: &mut test::Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut buf = [0; 64];
|
|
||||||
|
|
||||||
[255; 128].take(64).read(&mut buf).unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_take_read_buf(b: &mut test::Bencher) {
|
|
||||||
b.iter(|| {
|
|
||||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 64];
|
|
||||||
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
|
|
||||||
[255; 128].take(64).read_buf(buf.unfilled()).unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue #120603
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn read_buf_broken_read() {
|
|
||||||
struct MalformedRead;
|
|
||||||
|
|
||||||
impl Read for MalformedRead {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
// broken length calculation
|
|
||||||
Ok(buf.len() + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = BufReader::new(MalformedRead).fill_buf();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_buf_full_read() {
|
|
||||||
struct FullRead;
|
|
||||||
|
|
||||||
impl Read for FullRead {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(BufReader::new(FullRead).fill_buf().unwrap().len(), DEFAULT_BUF_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DataAndErrorReader(&'static [u8]);
|
|
||||||
|
|
||||||
impl Read for DataAndErrorReader {
|
|
||||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
panic!("We want tests to use `read_buf`")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
|
||||||
self.0.read_buf(buf).unwrap();
|
|
||||||
Err(io::Error::other("error"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_buf_data_and_error_take() {
|
|
||||||
let mut buf = [0; 64];
|
|
||||||
let mut buf = io::BorrowedBuf::from(buf.as_mut_slice());
|
|
||||||
|
|
||||||
let mut r = DataAndErrorReader(&[4, 5, 6]).take(1);
|
|
||||||
assert!(r.read_buf(buf.unfilled()).is_err());
|
|
||||||
assert_eq!(buf.filled(), &[4]);
|
|
||||||
|
|
||||||
assert!(r.read_buf(buf.unfilled()).is_ok());
|
|
||||||
assert_eq!(buf.filled(), &[4]);
|
|
||||||
assert_eq!(r.get_ref().0, &[5, 6]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_buf_data_and_error_buf() {
|
|
||||||
let mut r = BufReader::new(DataAndErrorReader(&[4, 5, 6]));
|
|
||||||
|
|
||||||
assert!(r.fill_buf().is_err());
|
|
||||||
assert_eq!(r.fill_buf().unwrap(), &[4, 5, 6]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_buf_data_and_error_read_to_end() {
|
|
||||||
let mut r = DataAndErrorReader(&[4, 5, 6]);
|
|
||||||
|
|
||||||
let mut v = Vec::with_capacity(200);
|
|
||||||
assert!(r.read_to_end(&mut v).is_err());
|
|
||||||
|
|
||||||
assert_eq!(v, &[4, 5, 6]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_to_end_error() {
|
|
||||||
struct ErrorReader;
|
|
||||||
|
|
||||||
impl Read for ErrorReader {
|
|
||||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
Err(io::Error::other("error"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut r = [4, 5, 6].chain(ErrorReader);
|
|
||||||
|
|
||||||
let mut v = Vec::with_capacity(200);
|
|
||||||
assert!(r.read_to_end(&mut v).is_err());
|
|
||||||
|
|
||||||
assert_eq!(v, &[4, 5, 6]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn try_oom_error() {
|
|
||||||
use alloc::alloc::Layout;
|
|
||||||
use alloc::collections::{TryReserveError, TryReserveErrorKind};
|
|
||||||
|
|
||||||
// We simulate a `Vec::try_reserve` error rather than attempting a huge size for real. This way
|
|
||||||
// we're not subject to the whims of optimization that might skip the actual allocation, and it
|
|
||||||
// also works for 32-bit targets and miri that might not OOM at all.
|
|
||||||
let layout = Layout::new::<u8>();
|
|
||||||
let kind = TryReserveErrorKind::AllocError { layout, non_exhaustive: () };
|
|
||||||
let reserve_err = TryReserveError::from(kind);
|
|
||||||
|
|
||||||
let io_err = io::Error::from(reserve_err);
|
|
||||||
assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind());
|
|
||||||
}
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
use crate::fmt;
|
|
||||||
use crate::io::prelude::*;
|
|
||||||
use crate::io::{
|
|
||||||
BorrowedBuf, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, SeekFrom, Sink, empty, repeat, sink,
|
|
||||||
};
|
|
||||||
use crate::mem::MaybeUninit;
|
|
||||||
|
|
||||||
struct ErrorDisplay;
|
|
||||||
|
|
||||||
impl fmt::Display for ErrorDisplay {
|
|
||||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
Err(fmt::Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PanicDisplay;
|
|
||||||
|
|
||||||
impl fmt::Display for PanicDisplay {
|
|
||||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn test_sinking<W: Write>(mut w: W) {
|
|
||||||
assert_eq!(w.write(&[]).unwrap(), 0);
|
|
||||||
assert_eq!(w.write(&[0]).unwrap(), 1);
|
|
||||||
assert_eq!(w.write(&[0; 1024]).unwrap(), 1024);
|
|
||||||
w.write_all(&[]).unwrap();
|
|
||||||
w.write_all(&[0]).unwrap();
|
|
||||||
w.write_all(&[0; 1024]).unwrap();
|
|
||||||
let mut bufs =
|
|
||||||
[IoSlice::new(&[]), IoSlice::new(&[0]), IoSlice::new(&[0; 1024]), IoSlice::new(&[])];
|
|
||||||
assert!(w.is_write_vectored());
|
|
||||||
assert_eq!(w.write_vectored(&[]).unwrap(), 0);
|
|
||||||
assert_eq!(w.write_vectored(&bufs).unwrap(), 1025);
|
|
||||||
w.write_all_vectored(&mut []).unwrap();
|
|
||||||
w.write_all_vectored(&mut bufs).unwrap();
|
|
||||||
assert!(w.flush().is_ok());
|
|
||||||
assert_eq!(w.by_ref().write(&[0; 1024]).unwrap(), 1024);
|
|
||||||
// Ignores fmt arguments
|
|
||||||
w.write_fmt(format_args!("{}", ErrorDisplay)).unwrap();
|
|
||||||
w.write_fmt(format_args!("{}", PanicDisplay)).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sink_sinks() {
|
|
||||||
test_sinking(sink());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_reads() {
|
|
||||||
let mut e = empty();
|
|
||||||
assert_eq!(e.read(&mut []).unwrap(), 0);
|
|
||||||
assert_eq!(e.read(&mut [0]).unwrap(), 0);
|
|
||||||
assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
|
|
||||||
assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0);
|
|
||||||
|
|
||||||
e.read_exact(&mut []).unwrap();
|
|
||||||
assert_eq!(e.read_exact(&mut [0]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
|
||||||
assert_eq!(e.read_exact(&mut [0; 1024]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
|
||||||
|
|
||||||
assert!(!e.is_read_vectored());
|
|
||||||
assert_eq!(e.read_vectored(&mut []).unwrap(), 0);
|
|
||||||
let (mut buf1, mut buf1024) = ([0], [0; 1024]);
|
|
||||||
let bufs = &mut [
|
|
||||||
IoSliceMut::new(&mut []),
|
|
||||||
IoSliceMut::new(&mut buf1),
|
|
||||||
IoSliceMut::new(&mut buf1024),
|
|
||||||
IoSliceMut::new(&mut []),
|
|
||||||
];
|
|
||||||
assert_eq!(e.read_vectored(bufs).unwrap(), 0);
|
|
||||||
|
|
||||||
let buf: &mut [MaybeUninit<_>] = &mut [];
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
e.read_buf(buf.unfilled()).unwrap();
|
|
||||||
assert_eq!(buf.len(), 0);
|
|
||||||
assert_eq!(buf.init_len(), 0);
|
|
||||||
|
|
||||||
let buf: &mut [_] = &mut [MaybeUninit::uninit()];
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
e.read_buf(buf.unfilled()).unwrap();
|
|
||||||
assert_eq!(buf.len(), 0);
|
|
||||||
assert_eq!(buf.init_len(), 0);
|
|
||||||
|
|
||||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
e.read_buf(buf.unfilled()).unwrap();
|
|
||||||
assert_eq!(buf.len(), 0);
|
|
||||||
assert_eq!(buf.init_len(), 0);
|
|
||||||
|
|
||||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap();
|
|
||||||
assert_eq!(buf.len(), 0);
|
|
||||||
assert_eq!(buf.init_len(), 0);
|
|
||||||
|
|
||||||
let buf: &mut [MaybeUninit<_>] = &mut [];
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
e.read_buf_exact(buf.unfilled()).unwrap();
|
|
||||||
assert_eq!(buf.len(), 0);
|
|
||||||
assert_eq!(buf.init_len(), 0);
|
|
||||||
|
|
||||||
let buf: &mut [_] = &mut [MaybeUninit::uninit()];
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
|
||||||
assert_eq!(buf.len(), 0);
|
|
||||||
assert_eq!(buf.init_len(), 0);
|
|
||||||
|
|
||||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
|
||||||
assert_eq!(buf.len(), 0);
|
|
||||||
assert_eq!(buf.init_len(), 0);
|
|
||||||
|
|
||||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
|
||||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
|
||||||
assert_eq!(
|
|
||||||
Read::by_ref(&mut e).read_buf_exact(buf.unfilled()).unwrap_err().kind(),
|
|
||||||
ErrorKind::UnexpectedEof,
|
|
||||||
);
|
|
||||||
assert_eq!(buf.len(), 0);
|
|
||||||
assert_eq!(buf.init_len(), 0);
|
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
|
|
||||||
assert_eq!(buf, vec![]);
|
|
||||||
let mut buf = vec![1, 2, 3];
|
|
||||||
assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
|
|
||||||
assert_eq!(buf, vec![1, 2, 3]);
|
|
||||||
|
|
||||||
let mut buf = String::new();
|
|
||||||
assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
|
|
||||||
assert_eq!(buf, "");
|
|
||||||
let mut buf = "hello".to_owned();
|
|
||||||
assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
|
|
||||||
assert_eq!(buf, "hello");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_seeks() {
|
|
||||||
let mut e = empty();
|
|
||||||
assert!(matches!(e.seek(SeekFrom::Start(0)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::Start(1)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::Start(u64::MAX)), Ok(0)));
|
|
||||||
|
|
||||||
assert!(matches!(e.seek(SeekFrom::End(i64::MIN)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::End(-1)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::End(0)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::End(1)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::End(i64::MAX)), Ok(0)));
|
|
||||||
|
|
||||||
assert!(matches!(e.seek(SeekFrom::Current(i64::MIN)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::Current(-1)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::Current(0)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::Current(1)), Ok(0)));
|
|
||||||
assert!(matches!(e.seek(SeekFrom::Current(i64::MAX)), Ok(0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_sinks() {
|
|
||||||
test_sinking(empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn repeat_repeats() {
|
|
||||||
let mut r = repeat(4);
|
|
||||||
let mut b = [0; 1024];
|
|
||||||
assert_eq!(r.read(&mut b).unwrap(), 1024);
|
|
||||||
assert!(b.iter().all(|b| *b == 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn take_some_bytes() {
|
|
||||||
assert_eq!(repeat(4).take(100).bytes().count(), 100);
|
|
||||||
assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4);
|
|
||||||
assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn const_utils() {
|
|
||||||
const _: Empty = empty();
|
|
||||||
const _: Repeat = repeat(b'c');
|
|
||||||
const _: Sink = sink();
|
|
||||||
}
|
|
||||||
@@ -1,329 +0,0 @@
|
|||||||
#![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,
|
|
||||||
unfulfilled_lint_expectations
|
|
||||||
)]
|
|
||||||
#![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,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[rustc_std_internal_symbol]
|
|
||||||
pub unsafe fn __rust_start_panic(_payload: &mut dyn core::panic::PanicPayload) -> u32 {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
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 bstr;
|
|
||||||
pub mod collections;
|
|
||||||
pub mod env;
|
|
||||||
pub mod panic;
|
|
||||||
pub mod panicking;
|
|
||||||
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!();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
//! 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};
|
|
||||||
@@ -1,534 +0,0 @@
|
|||||||
//! Panic support in the standard library.
|
|
||||||
|
|
||||||
#![stable(feature = "std_panic", since = "1.9.0")]
|
|
||||||
|
|
||||||
use crate::any::Any;
|
|
||||||
use crate::sync::atomic::{Atomic, AtomicU8, Ordering};
|
|
||||||
use crate::sync::{Condvar, Mutex, RwLock};
|
|
||||||
use crate::thread::Result;
|
|
||||||
use crate::{collections, fmt, panicking};
|
|
||||||
|
|
||||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
|
||||||
#[deprecated(
|
|
||||||
since = "1.82.0",
|
|
||||||
note = "use `PanicHookInfo` instead",
|
|
||||||
suggestion = "std::panic::PanicHookInfo"
|
|
||||||
)]
|
|
||||||
/// A struct providing information about a panic.
|
|
||||||
///
|
|
||||||
/// `PanicInfo` has been renamed to [`PanicHookInfo`] to avoid confusion with
|
|
||||||
/// [`core::panic::PanicInfo`].
|
|
||||||
pub type PanicInfo<'a> = PanicHookInfo<'a>;
|
|
||||||
|
|
||||||
/// A struct providing information about a panic.
|
|
||||||
///
|
|
||||||
/// `PanicHookInfo` structure is passed to a panic hook set by the [`set_hook`] function.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```should_panic
|
|
||||||
/// use std::panic;
|
|
||||||
///
|
|
||||||
/// panic::set_hook(Box::new(|panic_info| {
|
|
||||||
/// println!("panic occurred: {panic_info}");
|
|
||||||
/// }));
|
|
||||||
///
|
|
||||||
/// panic!("critical system failure");
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
|
|
||||||
#[stable(feature = "panic_hook_info", since = "1.81.0")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PanicHookInfo<'a> {
|
|
||||||
payload: &'a (dyn Any + Send),
|
|
||||||
location: &'a Location<'a>,
|
|
||||||
can_unwind: bool,
|
|
||||||
force_no_backtrace: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PanicHookInfo<'a> {
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn new(
|
|
||||||
location: &'a Location<'a>,
|
|
||||||
payload: &'a (dyn Any + Send),
|
|
||||||
can_unwind: bool,
|
|
||||||
force_no_backtrace: bool,
|
|
||||||
) -> Self {
|
|
||||||
PanicHookInfo { payload, location, can_unwind, force_no_backtrace }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the payload associated with the panic.
|
|
||||||
///
|
|
||||||
/// This will commonly, but not always, be a `&'static str` or [`String`].
|
|
||||||
/// If you only care about such payloads, use [`payload_as_str`] instead.
|
|
||||||
///
|
|
||||||
/// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a
|
|
||||||
/// panic payload of type `&'static str` or `String`.
|
|
||||||
///
|
|
||||||
/// Only an invocation of [`panic_any`]
|
|
||||||
/// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string)
|
|
||||||
/// can result in a panic payload other than a `&'static str` or `String`.
|
|
||||||
///
|
|
||||||
/// [`String`]: ../../std/string/struct.String.html
|
|
||||||
/// [`payload_as_str`]: PanicHookInfo::payload_as_str
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```should_panic
|
|
||||||
/// use std::panic;
|
|
||||||
///
|
|
||||||
/// panic::set_hook(Box::new(|panic_info| {
|
|
||||||
/// if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
|
|
||||||
/// println!("panic occurred: {s:?}");
|
|
||||||
/// } else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
|
|
||||||
/// println!("panic occurred: {s:?}");
|
|
||||||
/// } else {
|
|
||||||
/// println!("panic occurred");
|
|
||||||
/// }
|
|
||||||
/// }));
|
|
||||||
///
|
|
||||||
/// panic!("Normal panic");
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
#[inline]
|
|
||||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
|
||||||
pub fn payload(&self) -> &(dyn Any + Send) {
|
|
||||||
self.payload
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the payload associated with the panic, if it is a string.
|
|
||||||
///
|
|
||||||
/// This returns the payload if it is of type `&'static str` or `String`.
|
|
||||||
///
|
|
||||||
/// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a
|
|
||||||
/// panic payload where `payload_as_str` returns `Some`.
|
|
||||||
///
|
|
||||||
/// Only an invocation of [`panic_any`]
|
|
||||||
/// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string)
|
|
||||||
/// can result in a panic payload where `payload_as_str` returns `None`.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```should_panic
|
|
||||||
/// std::panic::set_hook(Box::new(|panic_info| {
|
|
||||||
/// if let Some(s) = panic_info.payload_as_str() {
|
|
||||||
/// println!("panic occurred: {s:?}");
|
|
||||||
/// } else {
|
|
||||||
/// println!("panic occurred");
|
|
||||||
/// }
|
|
||||||
/// }));
|
|
||||||
///
|
|
||||||
/// panic!("Normal panic");
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
#[inline]
|
|
||||||
#[stable(feature = "panic_payload_as_str", since = "1.91.0")]
|
|
||||||
pub fn payload_as_str(&self) -> Option<&str> {
|
|
||||||
if let Some(s) = self.payload.downcast_ref::<&str>() {
|
|
||||||
Some(s)
|
|
||||||
} else if let Some(s) = self.payload.downcast_ref::<String>() {
|
|
||||||
Some(s)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns information about the location from which the panic originated,
|
|
||||||
/// if available.
|
|
||||||
///
|
|
||||||
/// This method will currently always return [`Some`], but this may change
|
|
||||||
/// in future versions.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```should_panic
|
|
||||||
/// use std::panic;
|
|
||||||
///
|
|
||||||
/// panic::set_hook(Box::new(|panic_info| {
|
|
||||||
/// if let Some(location) = panic_info.location() {
|
|
||||||
/// println!("panic occurred in file '{}' at line {}",
|
|
||||||
/// location.file(),
|
|
||||||
/// location.line(),
|
|
||||||
/// );
|
|
||||||
/// } else {
|
|
||||||
/// println!("panic occurred but can't get location information...");
|
|
||||||
/// }
|
|
||||||
/// }));
|
|
||||||
///
|
|
||||||
/// panic!("Normal panic");
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
#[inline]
|
|
||||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
|
||||||
pub fn location(&self) -> Option<&Location<'_>> {
|
|
||||||
// NOTE: If this is changed to sometimes return None,
|
|
||||||
// deal with that case in std::panicking::default_hook and core::panicking::panic_fmt.
|
|
||||||
Some(&self.location)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the panic handler is allowed to unwind the stack from
|
|
||||||
/// the point where the panic occurred.
|
|
||||||
///
|
|
||||||
/// This is true for most kinds of panics with the exception of panics
|
|
||||||
/// caused by trying to unwind out of a `Drop` implementation or a function
|
|
||||||
/// whose ABI does not support unwinding.
|
|
||||||
///
|
|
||||||
/// It is safe for a panic handler to unwind even when this function returns
|
|
||||||
/// false, however this will simply cause the panic handler to be called
|
|
||||||
/// again.
|
|
||||||
#[must_use]
|
|
||||||
#[inline]
|
|
||||||
#[unstable(feature = "panic_can_unwind", issue = "92988")]
|
|
||||||
pub fn can_unwind(&self) -> bool {
|
|
||||||
self.can_unwind
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(
|
|
||||||
feature = "panic_internals",
|
|
||||||
reason = "internal details of the implementation of the `panic!` and related macros",
|
|
||||||
issue = "none"
|
|
||||||
)]
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[inline]
|
|
||||||
pub fn force_no_backtrace(&self) -> bool {
|
|
||||||
self.force_no_backtrace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "panic_hook_display", since = "1.26.0")]
|
|
||||||
impl fmt::Display for PanicHookInfo<'_> {
|
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
formatter.write_str("panicked at ")?;
|
|
||||||
self.location.fmt(formatter)?;
|
|
||||||
if let Some(payload) = self.payload_as_str() {
|
|
||||||
formatter.write_str(":\n")?;
|
|
||||||
formatter.write_str(payload)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
|
|
||||||
#[allow_internal_unstable(libstd_sys_internals, const_format_args, panic_internals, rt)]
|
|
||||||
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")]
|
|
||||||
#[rustc_macro_transparency = "semiopaque"]
|
|
||||||
pub macro panic_2015 {
|
|
||||||
() => ({
|
|
||||||
$crate::rt::begin_panic("explicit panic")
|
|
||||||
}),
|
|
||||||
($msg:expr $(,)?) => ({
|
|
||||||
$crate::rt::begin_panic($msg);
|
|
||||||
}),
|
|
||||||
// Special-case the single-argument case for const_panic.
|
|
||||||
("{}", $arg:expr $(,)?) => ({
|
|
||||||
$crate::rt::panic_display(&$arg);
|
|
||||||
}),
|
|
||||||
($fmt:expr, $($arg:tt)+) => ({
|
|
||||||
// Semicolon to prevent temporaries inside the formatting machinery from
|
|
||||||
// being considered alive in the caller after the panic_fmt call.
|
|
||||||
$crate::rt::panic_fmt($crate::const_format_args!($fmt, $($arg)+));
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
|
||||||
pub use core::panic::Location;
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
|
|
||||||
pub use core::panic::panic_2021;
|
|
||||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
|
||||||
pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
|
|
||||||
|
|
||||||
#[unstable(feature = "panic_update_hook", issue = "92649")]
|
|
||||||
pub use crate::panicking::update_hook;
|
|
||||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
|
||||||
pub use crate::panicking::{set_hook, take_hook};
|
|
||||||
|
|
||||||
/// Panics the current thread with the given message as the panic payload.
|
|
||||||
///
|
|
||||||
/// The message can be of any (`Any + Send`) type, not just strings.
|
|
||||||
///
|
|
||||||
/// The message is wrapped in a `Box<'static + Any + Send>`, which can be
|
|
||||||
/// accessed later using [`PanicHookInfo::payload`].
|
|
||||||
///
|
|
||||||
/// See the [`panic!`] macro for more information about panicking.
|
|
||||||
#[stable(feature = "panic_any", since = "1.51.0")]
|
|
||||||
#[inline]
|
|
||||||
#[track_caller]
|
|
||||||
#[cfg_attr(not(test), rustc_diagnostic_item = "panic_any")]
|
|
||||||
pub fn panic_any<M: 'static + Any + Send>(msg: M) -> ! {
|
|
||||||
crate::panicking::begin_panic(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
|
||||||
impl<T: ?Sized> UnwindSafe for Mutex<T> {}
|
|
||||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
|
||||||
impl<T: ?Sized> UnwindSafe for RwLock<T> {}
|
|
||||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
|
||||||
impl UnwindSafe for Condvar {}
|
|
||||||
|
|
||||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
|
|
||||||
impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
|
|
||||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
|
|
||||||
impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}
|
|
||||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
|
|
||||||
impl RefUnwindSafe for Condvar {}
|
|
||||||
|
|
||||||
// https://github.com/rust-lang/rust/issues/62301
|
|
||||||
#[stable(feature = "hashbrown", since = "1.36.0")]
|
|
||||||
impl<K, V, S> UnwindSafe for collections::HashMap<K, V, S>
|
|
||||||
where
|
|
||||||
K: UnwindSafe,
|
|
||||||
V: UnwindSafe,
|
|
||||||
S: UnwindSafe,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "abort_unwind", issue = "130338")]
|
|
||||||
pub use core::panic::abort_unwind;
|
|
||||||
|
|
||||||
/// Invokes a closure, capturing the cause of an unwinding panic if one occurs.
|
|
||||||
///
|
|
||||||
/// This function will return `Ok` with the closure's result if the closure does
|
|
||||||
/// not panic, and will return `Err(cause)` if the closure panics. The `cause`
|
|
||||||
/// returned is the object with which panic was originally invoked.
|
|
||||||
///
|
|
||||||
/// Rust functions that are expected to be called from foreign code that does
|
|
||||||
/// not support unwinding (such as C compiled with `-fno-exceptions`) should be
|
|
||||||
/// defined using `extern "C"`, which ensures that if the Rust code panics, it
|
|
||||||
/// is automatically caught and the process is aborted. If this is the desired
|
|
||||||
/// behavior, it is not necessary to use `catch_unwind` explicitly. This
|
|
||||||
/// function should instead be used when more graceful error-handling is needed.
|
|
||||||
///
|
|
||||||
/// It is **not** recommended to use this function for a general try/catch
|
|
||||||
/// mechanism. The [`Result`] type is more appropriate to use for functions that
|
|
||||||
/// can fail on a regular basis. Additionally, this function is not guaranteed
|
|
||||||
/// to catch all panics, see the "Notes" section below.
|
|
||||||
///
|
|
||||||
/// The closure provided is required to adhere to the [`UnwindSafe`] trait to
|
|
||||||
/// ensure that all captured variables are safe to cross this boundary. The
|
|
||||||
/// purpose of this bound is to encode the concept of [exception safety][rfc] in
|
|
||||||
/// the type system. Most usage of this function should not need to worry about
|
|
||||||
/// this bound as programs are naturally unwind safe without `unsafe` code. If
|
|
||||||
/// it becomes a problem the [`AssertUnwindSafe`] wrapper struct can be used to
|
|
||||||
/// quickly assert that the usage here is indeed unwind safe.
|
|
||||||
///
|
|
||||||
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
|
|
||||||
///
|
|
||||||
/// # Notes
|
|
||||||
///
|
|
||||||
/// This function **might not catch all Rust panics**. A Rust panic is not
|
|
||||||
/// always implemented via unwinding, but can be implemented by aborting the
|
|
||||||
/// process as well. This function *only* catches unwinding panics, not those
|
|
||||||
/// that abort the process.
|
|
||||||
///
|
|
||||||
/// If a custom panic hook has been set, it will be invoked before the panic is
|
|
||||||
/// caught, before unwinding.
|
|
||||||
///
|
|
||||||
/// Although unwinding into Rust code with a foreign exception (e.g. an
|
|
||||||
/// exception thrown from C++ code, or a `panic!` in Rust code compiled or
|
|
||||||
/// linked with a different runtime) via an appropriate ABI (e.g. `"C-unwind"`)
|
|
||||||
/// is permitted, catching such an exception using this function will have one
|
|
||||||
/// of two behaviors, and it is unspecified which will occur:
|
|
||||||
///
|
|
||||||
/// * The process aborts, after executing all destructors of `f` and the
|
|
||||||
/// functions it called.
|
|
||||||
/// * The function returns a `Result::Err` containing an opaque type.
|
|
||||||
///
|
|
||||||
/// Finally, be **careful in how you drop the result of this function**. If it
|
|
||||||
/// is `Err`, it contains the panic payload, and dropping that may in turn
|
|
||||||
/// panic!
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::panic;
|
|
||||||
///
|
|
||||||
/// let result = panic::catch_unwind(|| {
|
|
||||||
/// println!("hello!");
|
|
||||||
/// });
|
|
||||||
/// assert!(result.is_ok());
|
|
||||||
///
|
|
||||||
/// let result = panic::catch_unwind(|| {
|
|
||||||
/// panic!("oh no!");
|
|
||||||
/// });
|
|
||||||
/// assert!(result.is_err());
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "catch_unwind", since = "1.9.0")]
|
|
||||||
pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
|
||||||
unsafe { panicking::catch_unwind(f) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Triggers a panic without invoking the panic hook.
|
|
||||||
///
|
|
||||||
/// This is designed to be used in conjunction with [`catch_unwind`] to, for
|
|
||||||
/// example, carry a panic across a layer of C code.
|
|
||||||
///
|
|
||||||
/// # Notes
|
|
||||||
///
|
|
||||||
/// Note that panics in Rust are not always implemented via unwinding, but they
|
|
||||||
/// may be implemented by aborting the process. If this function is called when
|
|
||||||
/// panics are implemented this way then this function will abort the process,
|
|
||||||
/// not trigger an unwind.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```should_panic
|
|
||||||
/// use std::panic;
|
|
||||||
///
|
|
||||||
/// let result = panic::catch_unwind(|| {
|
|
||||||
/// if 1 != 2 {
|
|
||||||
/// panic!("oh no!");
|
|
||||||
/// }
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// if let Err(err) = result {
|
|
||||||
/// panic::resume_unwind(err);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "resume_unwind", since = "1.9.0")]
|
|
||||||
pub fn resume_unwind(payload: Box<dyn Any + Send>) -> ! {
|
|
||||||
panicking::resume_unwind(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes all future panics abort directly without running the panic hook or unwinding.
|
|
||||||
///
|
|
||||||
/// There is no way to undo this; the effect lasts until the process exits or
|
|
||||||
/// execs (or the equivalent).
|
|
||||||
///
|
|
||||||
/// # Use after fork
|
|
||||||
///
|
|
||||||
/// This function is particularly useful for calling after `libc::fork`. After `fork`, in a
|
|
||||||
/// multithreaded program it is (on many platforms) not safe to call the allocator. It is also
|
|
||||||
/// generally highly undesirable for an unwind to unwind past the `fork`, because that results in
|
|
||||||
/// the unwind propagating to code that was only ever expecting to run in the parent.
|
|
||||||
///
|
|
||||||
/// `panic::always_abort()` helps avoid both of these. It directly avoids any further unwinding,
|
|
||||||
/// and if there is a panic, the abort will occur without allocating provided that the arguments to
|
|
||||||
/// panic can be formatted without allocating.
|
|
||||||
///
|
|
||||||
/// Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// #![feature(panic_always_abort)]
|
|
||||||
/// use std::panic;
|
|
||||||
///
|
|
||||||
/// panic::always_abort();
|
|
||||||
///
|
|
||||||
/// let _ = panic::catch_unwind(|| {
|
|
||||||
/// panic!("inside the catch");
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// // We will have aborted already, due to the panic.
|
|
||||||
/// unreachable!();
|
|
||||||
/// ```
|
|
||||||
#[unstable(feature = "panic_always_abort", issue = "84438")]
|
|
||||||
pub fn always_abort() {
|
|
||||||
crate::panicking::panic_count::set_always_abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The configuration for whether and how the default panic hook will capture
|
|
||||||
/// and display the backtrace.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
#[unstable(feature = "panic_backtrace_config", issue = "93346")]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum BacktraceStyle {
|
|
||||||
/// Prints a terser backtrace which ideally only contains relevant
|
|
||||||
/// information.
|
|
||||||
Short,
|
|
||||||
/// Prints a backtrace with all possible information.
|
|
||||||
Full,
|
|
||||||
/// Disable collecting and displaying backtraces.
|
|
||||||
Off,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BacktraceStyle {
|
|
||||||
pub(crate) fn full() -> Option<Self> {
|
|
||||||
if cfg!(feature = "backtrace") { Some(BacktraceStyle::Full) } else { None }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_u8(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
BacktraceStyle::Short => 1,
|
|
||||||
BacktraceStyle::Full => 2,
|
|
||||||
BacktraceStyle::Off => 3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_u8(s: u8) -> Option<Self> {
|
|
||||||
match s {
|
|
||||||
1 => Some(BacktraceStyle::Short),
|
|
||||||
2 => Some(BacktraceStyle::Full),
|
|
||||||
3 => Some(BacktraceStyle::Off),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tracks whether we should/can capture a backtrace, and how we should display
|
|
||||||
// that backtrace.
|
|
||||||
//
|
|
||||||
// Internally stores equivalent of an Option<BacktraceStyle>.
|
|
||||||
static SHOULD_CAPTURE: Atomic<u8> = AtomicU8::new(0);
|
|
||||||
|
|
||||||
/// Configures whether the default panic hook will capture and display a
|
|
||||||
/// backtrace.
|
|
||||||
///
|
|
||||||
/// The default value for this setting may be set by the `RUST_BACKTRACE`
|
|
||||||
/// environment variable; see the details in [`get_backtrace_style`].
|
|
||||||
#[unstable(feature = "panic_backtrace_config", issue = "93346")]
|
|
||||||
pub fn set_backtrace_style(style: BacktraceStyle) {
|
|
||||||
if cfg!(feature = "backtrace") {
|
|
||||||
// If the `backtrace` feature of this crate is enabled, set the backtrace style.
|
|
||||||
SHOULD_CAPTURE.store(style.as_u8(), Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks whether the standard library's panic hook will capture and print a
|
|
||||||
/// backtrace.
|
|
||||||
///
|
|
||||||
/// This function will, if a backtrace style has not been set via
|
|
||||||
/// [`set_backtrace_style`], read the environment variable `RUST_BACKTRACE` to
|
|
||||||
/// determine a default value for the backtrace formatting:
|
|
||||||
///
|
|
||||||
/// The first call to `get_backtrace_style` may read the `RUST_BACKTRACE`
|
|
||||||
/// environment variable if `set_backtrace_style` has not been called to
|
|
||||||
/// override the default value. After a call to `set_backtrace_style` or
|
|
||||||
/// `get_backtrace_style`, any changes to `RUST_BACKTRACE` will have no effect.
|
|
||||||
///
|
|
||||||
/// `RUST_BACKTRACE` is read according to these rules:
|
|
||||||
///
|
|
||||||
/// * `0` for `BacktraceStyle::Off`
|
|
||||||
/// * `full` for `BacktraceStyle::Full`
|
|
||||||
/// * `1` for `BacktraceStyle::Short`
|
|
||||||
/// * Other values are currently `BacktraceStyle::Short`, but this may change in
|
|
||||||
/// the future
|
|
||||||
///
|
|
||||||
/// Returns `None` if backtraces aren't currently supported.
|
|
||||||
#[unstable(feature = "panic_backtrace_config", issue = "93346")]
|
|
||||||
pub fn get_backtrace_style() -> Option<BacktraceStyle> {
|
|
||||||
if !cfg!(feature = "backtrace") {
|
|
||||||
// If the `backtrace` feature of this crate isn't enabled quickly return
|
|
||||||
// `Unsupported` so this can be constant propagated all over the place
|
|
||||||
// to optimize away callers.
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let current = SHOULD_CAPTURE.load(Ordering::Relaxed);
|
|
||||||
if let Some(style) = BacktraceStyle::from_u8(current) {
|
|
||||||
return Some(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
let format = match crate::env::var_os("RUST_BACKTRACE") {
|
|
||||||
Some(x) if &x == "0" => BacktraceStyle::Off,
|
|
||||||
Some(x) if &x == "full" => BacktraceStyle::Full,
|
|
||||||
Some(_) => BacktraceStyle::Short,
|
|
||||||
None if crate::sys::backtrace::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full,
|
|
||||||
None => BacktraceStyle::Off,
|
|
||||||
};
|
|
||||||
|
|
||||||
match SHOULD_CAPTURE.compare_exchange(0, format.as_u8(), Ordering::Relaxed, Ordering::Relaxed) {
|
|
||||||
Ok(_) => Some(format),
|
|
||||||
Err(new) => BacktraceStyle::from_u8(new),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,894 +0,0 @@
|
|||||||
//! Implementation of various bits and pieces of the `panic!` macro and
|
|
||||||
//! associated runtime pieces.
|
|
||||||
//!
|
|
||||||
//! Specifically, this module contains the implementation of:
|
|
||||||
//!
|
|
||||||
//! * Panic hooks
|
|
||||||
//! * Executing a panic up to doing the actual implementation
|
|
||||||
//! * Shims around "try"
|
|
||||||
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
use core::panic::{Location, PanicPayload};
|
|
||||||
|
|
||||||
// make sure to use the stderr output configured
|
|
||||||
// by libtest in the real copy of std
|
|
||||||
#[cfg(test)]
|
|
||||||
use realstd::io::try_set_output_capture;
|
|
||||||
|
|
||||||
use crate::any::Any;
|
|
||||||
#[cfg(not(test))]
|
|
||||||
use crate::io::try_set_output_capture;
|
|
||||||
use crate::mem::{self, ManuallyDrop};
|
|
||||||
use crate::panic::{BacktraceStyle, PanicHookInfo};
|
|
||||||
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
|
|
||||||
use crate::sync::nonpoison::RwLock;
|
|
||||||
use crate::sys::backtrace;
|
|
||||||
use crate::sys::stdio::panic_output;
|
|
||||||
use crate::{fmt, intrinsics, process, thread};
|
|
||||||
|
|
||||||
// This forces codegen of the function called by panic!() inside the std crate, rather than in
|
|
||||||
// downstream crates. Primarily this is useful for rustc's codegen tests, which rely on noticing
|
|
||||||
// complete removal of panic from generated IR. Since begin_panic is inline(never), it's only
|
|
||||||
// codegen'd once per crate-graph so this pushes that to std rather than our codegen test crates.
|
|
||||||
//
|
|
||||||
// (See https://github.com/rust-lang/rust/pull/123244 for more info on why).
|
|
||||||
//
|
|
||||||
// If this is causing problems we can also modify those codegen tests to use a crate type like
|
|
||||||
// cdylib which doesn't export "Rust" symbols to downstream linkage units.
|
|
||||||
#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[used(compiler)]
|
|
||||||
pub static EMPTY_PANIC: fn(&'static str) -> ! =
|
|
||||||
begin_panic::<&'static str> as fn(&'static str) -> !;
|
|
||||||
|
|
||||||
// Binary interface to the panic runtime that the standard library depends on.
|
|
||||||
//
|
|
||||||
// The standard library is tagged with `#![needs_panic_runtime]` (introduced in
|
|
||||||
// RFC 1513) to indicate that it requires some other crate tagged with
|
|
||||||
// `#![panic_runtime]` to exist somewhere. Each panic runtime is intended to
|
|
||||||
// implement these symbols (with the same signatures) so we can get matched up
|
|
||||||
// to them.
|
|
||||||
//
|
|
||||||
// One day this may look a little less ad-hoc with the compiler helping out to
|
|
||||||
// hook up these functions, but it is not this day!
|
|
||||||
#[allow(improper_ctypes)]
|
|
||||||
unsafe extern "C" {
|
|
||||||
#[rustc_std_internal_symbol]
|
|
||||||
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "Rust" {
|
|
||||||
/// `PanicPayload` lazily performs allocation only when needed (this avoids
|
|
||||||
/// allocations when using the "abort" panic runtime).
|
|
||||||
#[rustc_std_internal_symbol]
|
|
||||||
fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function is called by the panic runtime if FFI code catches a Rust
|
|
||||||
/// panic but doesn't rethrow it. We don't support this case since it messes
|
|
||||||
/// with our panic count.
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[rustc_std_internal_symbol]
|
|
||||||
extern "C" fn __rust_drop_panic() -> ! {
|
|
||||||
rtabort!("Rust panics must be rethrown");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function is called by the panic runtime if it catches an exception
|
|
||||||
/// object which does not correspond to a Rust panic.
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[rustc_std_internal_symbol]
|
|
||||||
extern "C" fn __rust_foreign_exception() -> ! {
|
|
||||||
rtabort!("Rust cannot catch foreign exceptions");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
enum Hook {
|
|
||||||
#[default]
|
|
||||||
Default,
|
|
||||||
Custom(Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hook {
|
|
||||||
#[inline]
|
|
||||||
fn into_box(self) -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> {
|
|
||||||
match self {
|
|
||||||
Hook::Default => Box::new(default_hook),
|
|
||||||
Hook::Custom(hook) => hook,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static HOOK: RwLock<Hook> = RwLock::new(Hook::Default);
|
|
||||||
|
|
||||||
/// Registers a custom panic hook, replacing the previously registered hook.
|
|
||||||
///
|
|
||||||
/// The panic hook is invoked when a thread panics, but before the panic runtime
|
|
||||||
/// is invoked. As such, the hook will run with both the aborting and unwinding
|
|
||||||
/// runtimes.
|
|
||||||
///
|
|
||||||
/// The default hook, which is registered at startup, prints a message to standard error and
|
|
||||||
/// generates a backtrace if requested. This behavior can be customized using the `set_hook` function.
|
|
||||||
/// The current hook can be retrieved while reinstating the default hook with the [`take_hook`]
|
|
||||||
/// function.
|
|
||||||
///
|
|
||||||
/// [`take_hook`]: ./fn.take_hook.html
|
|
||||||
///
|
|
||||||
/// The hook is provided with a `PanicHookInfo` struct which contains information
|
|
||||||
/// about the origin of the panic, including the payload passed to `panic!` and
|
|
||||||
/// the source code location from which the panic originated.
|
|
||||||
///
|
|
||||||
/// The panic hook is a global resource.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if called from a panicking thread.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// The following will print "Custom panic hook":
|
|
||||||
///
|
|
||||||
/// ```should_panic
|
|
||||||
/// use std::panic;
|
|
||||||
///
|
|
||||||
/// panic::set_hook(Box::new(|_| {
|
|
||||||
/// println!("Custom panic hook");
|
|
||||||
/// }));
|
|
||||||
///
|
|
||||||
/// panic!("Normal panic");
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
|
||||||
pub fn set_hook(hook: Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send>) {
|
|
||||||
if thread::panicking() {
|
|
||||||
panic!("cannot modify the panic hook from a panicking thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop the old hook after changing the hook to avoid deadlocking if its
|
|
||||||
// destructor panics.
|
|
||||||
drop(HOOK.replace(Hook::Custom(hook)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unregisters the current panic hook and returns it, registering the default hook
|
|
||||||
/// in its place.
|
|
||||||
///
|
|
||||||
/// *See also the function [`set_hook`].*
|
|
||||||
///
|
|
||||||
/// [`set_hook`]: ./fn.set_hook.html
|
|
||||||
///
|
|
||||||
/// If the default hook is registered it will be returned, but remain registered.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if called from a panicking thread.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// The following will print "Normal panic":
|
|
||||||
///
|
|
||||||
/// ```should_panic
|
|
||||||
/// use std::panic;
|
|
||||||
///
|
|
||||||
/// panic::set_hook(Box::new(|_| {
|
|
||||||
/// println!("Custom panic hook");
|
|
||||||
/// }));
|
|
||||||
///
|
|
||||||
/// let _ = panic::take_hook();
|
|
||||||
///
|
|
||||||
/// panic!("Normal panic");
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
|
||||||
pub fn take_hook() -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> {
|
|
||||||
if thread::panicking() {
|
|
||||||
panic!("cannot modify the panic hook from a panicking thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
HOOK.replace(Hook::Default).into_box()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Atomic combination of [`take_hook`] and [`set_hook`]. Use this to replace the panic handler with
|
|
||||||
/// a new panic handler that does something and then executes the old handler.
|
|
||||||
///
|
|
||||||
/// [`take_hook`]: ./fn.take_hook.html
|
|
||||||
/// [`set_hook`]: ./fn.set_hook.html
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if called from a panicking thread.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// The following will print the custom message, and then the normal output of panic.
|
|
||||||
///
|
|
||||||
/// ```should_panic
|
|
||||||
/// #![feature(panic_update_hook)]
|
|
||||||
/// use std::panic;
|
|
||||||
///
|
|
||||||
/// // Equivalent to
|
|
||||||
/// // let prev = panic::take_hook();
|
|
||||||
/// // panic::set_hook(Box::new(move |info| {
|
|
||||||
/// // println!("...");
|
|
||||||
/// // prev(info);
|
|
||||||
/// // }));
|
|
||||||
/// panic::update_hook(move |prev, info| {
|
|
||||||
/// println!("Print custom message and execute panic handler as usual");
|
|
||||||
/// prev(info);
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// panic!("Custom and then normal");
|
|
||||||
/// ```
|
|
||||||
#[unstable(feature = "panic_update_hook", issue = "92649")]
|
|
||||||
pub fn update_hook<F>(hook_fn: F)
|
|
||||||
where
|
|
||||||
F: Fn(&(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static), &PanicHookInfo<'_>)
|
|
||||||
+ Sync
|
|
||||||
+ Send
|
|
||||||
+ 'static,
|
|
||||||
{
|
|
||||||
if thread::panicking() {
|
|
||||||
panic!("cannot modify the panic hook from a panicking thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut hook = HOOK.write();
|
|
||||||
let prev = mem::take(&mut *hook).into_box();
|
|
||||||
*hook = Hook::Custom(Box::new(move |info| hook_fn(&prev, info)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The default panic handler.
|
|
||||||
#[optimize(size)]
|
|
||||||
fn default_hook(info: &PanicHookInfo<'_>) {
|
|
||||||
// If this is a double panic, make sure that we print a backtrace
|
|
||||||
// for this panic. Otherwise only print it if logging is enabled.
|
|
||||||
let backtrace = if info.force_no_backtrace() {
|
|
||||||
None
|
|
||||||
} else if panic_count::get_count() >= 2 {
|
|
||||||
BacktraceStyle::full()
|
|
||||||
} else {
|
|
||||||
crate::panic::get_backtrace_style()
|
|
||||||
};
|
|
||||||
|
|
||||||
// The current implementation always returns `Some`.
|
|
||||||
let location = info.location().unwrap();
|
|
||||||
|
|
||||||
let msg = payload_as_str(info.payload());
|
|
||||||
|
|
||||||
let write = #[optimize(size)]
|
|
||||||
|err: &mut dyn crate::io::Write| {
|
|
||||||
// Use a lock to prevent mixed output in multithreading context.
|
|
||||||
// Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
|
|
||||||
let mut lock = backtrace::lock();
|
|
||||||
|
|
||||||
thread::with_current_name(|name| {
|
|
||||||
let name = name.unwrap_or("<unnamed>");
|
|
||||||
let tid = thread::current_os_id();
|
|
||||||
|
|
||||||
// Try to write the panic message to a buffer first to prevent other concurrent outputs
|
|
||||||
// interleaving with it.
|
|
||||||
let mut buffer = [0u8; 512];
|
|
||||||
let mut cursor = crate::io::Cursor::new(&mut buffer[..]);
|
|
||||||
|
|
||||||
let write_msg = |dst: &mut dyn crate::io::Write| {
|
|
||||||
// We add a newline to ensure the panic message appears at the start of a line.
|
|
||||||
writeln!(dst, "\nthread '{name}' ({tid}) panicked at {location}:\n{msg}")
|
|
||||||
};
|
|
||||||
|
|
||||||
if write_msg(&mut cursor).is_ok() {
|
|
||||||
let pos = cursor.position() as usize;
|
|
||||||
let _ = err.write_all(&buffer[0..pos]);
|
|
||||||
} else {
|
|
||||||
// The message did not fit into the buffer, write it directly instead.
|
|
||||||
let _ = write_msg(err);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
static FIRST_PANIC: Atomic<bool> = AtomicBool::new(true);
|
|
||||||
|
|
||||||
match backtrace {
|
|
||||||
Some(BacktraceStyle::Short) => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
Some(BacktraceStyle::Full) => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
Some(BacktraceStyle::Off) => {
|
|
||||||
if FIRST_PANIC.swap(false, Ordering::Relaxed) {
|
|
||||||
let _ = writeln!(
|
|
||||||
err,
|
|
||||||
"note: run with `RUST_BACKTRACE=1` environment variable to display a \
|
|
||||||
backtrace"
|
|
||||||
);
|
|
||||||
if cfg!(miri) {
|
|
||||||
let _ = writeln!(
|
|
||||||
err,
|
|
||||||
"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 => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(Some(local)) = try_set_output_capture(None) {
|
|
||||||
write(&mut *local.lock().unwrap_or_else(|e| e.into_inner()));
|
|
||||||
try_set_output_capture(Some(local)).ok();
|
|
||||||
} else if let Some(mut out) = panic_output() {
|
|
||||||
write(&mut out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[cfg(panic = "immediate-abort")]
|
|
||||||
#[unstable(feature = "update_panic_count", issue = "none")]
|
|
||||||
pub mod panic_count {
|
|
||||||
/// A reason for forcing an immediate abort on panic.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum MustAbort {
|
|
||||||
AlwaysAbort,
|
|
||||||
PanicInHook,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn increase(run_panic_hook: bool) -> Option<MustAbort> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finished_panic_hook() {}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn decrease() {}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_always_abort() {}
|
|
||||||
|
|
||||||
// Disregards ALWAYS_ABORT_FLAG
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_count() -> usize {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
#[inline]
|
|
||||||
pub fn count_is_zero() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[cfg(not(panic = "immediate-abort"))]
|
|
||||||
#[unstable(feature = "update_panic_count", issue = "none")]
|
|
||||||
pub mod panic_count {
|
|
||||||
use crate::cell::Cell;
|
|
||||||
use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1);
|
|
||||||
|
|
||||||
/// A reason for forcing an immediate abort on panic.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum MustAbort {
|
|
||||||
AlwaysAbort,
|
|
||||||
PanicInHook,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panic count for the current thread and whether a panic hook is currently
|
|
||||||
// being executed..
|
|
||||||
thread_local! {
|
|
||||||
static LOCAL_PANIC_COUNT: Cell<(usize, bool)> = const { Cell::new((0, false)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sum of panic counts from all threads. The purpose of this is to have
|
|
||||||
// a fast path in `count_is_zero` (which is used by `panicking`). In any particular
|
|
||||||
// thread, if that thread currently views `GLOBAL_PANIC_COUNT` as being zero,
|
|
||||||
// then `LOCAL_PANIC_COUNT` in that thread is zero. This invariant holds before
|
|
||||||
// and after increase and decrease, but not necessarily during their execution.
|
|
||||||
//
|
|
||||||
// Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG)
|
|
||||||
// records whether panic::always_abort() has been called. This can only be
|
|
||||||
// set, never cleared.
|
|
||||||
// panic::always_abort() is usually called to prevent memory allocations done by
|
|
||||||
// the panic handling in the child created by `libc::fork`.
|
|
||||||
// Memory allocations performed in a child created with `libc::fork` are undefined
|
|
||||||
// behavior in most operating systems.
|
|
||||||
// Accessing LOCAL_PANIC_COUNT in a child created by `libc::fork` would lead to a memory
|
|
||||||
// allocation. Only GLOBAL_PANIC_COUNT can be accessed in this situation. This is
|
|
||||||
// sufficient because a child process will always have exactly one thread only.
|
|
||||||
// See also #85261 for details.
|
|
||||||
//
|
|
||||||
// This could be viewed as a struct containing a single bit and an n-1-bit
|
|
||||||
// value, but if we wrote it like that it would be more than a single word,
|
|
||||||
// and even a newtype around usize would be clumsy because we need atomics.
|
|
||||||
// But we use such a tuple for the return type of increase().
|
|
||||||
//
|
|
||||||
// Stealing a bit is fine because it just amounts to assuming that each
|
|
||||||
// panicking thread consumes at least 2 bytes of address space.
|
|
||||||
static GLOBAL_PANIC_COUNT: Atomic<usize> = AtomicUsize::new(0);
|
|
||||||
|
|
||||||
// Increases the global and local panic count, and returns whether an
|
|
||||||
// immediate abort is required.
|
|
||||||
//
|
|
||||||
// This also updates thread-local state to keep track of whether a panic
|
|
||||||
// hook is currently executing.
|
|
||||||
pub fn increase(run_panic_hook: bool) -> Option<MustAbort> {
|
|
||||||
let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed);
|
|
||||||
if global_count & ALWAYS_ABORT_FLAG != 0 {
|
|
||||||
// Do *not* access thread-local state, we might be after a `fork`.
|
|
||||||
return Some(MustAbort::AlwaysAbort);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL_PANIC_COUNT.with(|c| {
|
|
||||||
let (count, in_panic_hook) = c.get();
|
|
||||||
if in_panic_hook {
|
|
||||||
return Some(MustAbort::PanicInHook);
|
|
||||||
}
|
|
||||||
c.set((count + 1, run_panic_hook));
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finished_panic_hook() {
|
|
||||||
LOCAL_PANIC_COUNT.with(|c| {
|
|
||||||
let (count, _) = c.get();
|
|
||||||
c.set((count, false));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decrease() {
|
|
||||||
GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed);
|
|
||||||
LOCAL_PANIC_COUNT.with(|c| {
|
|
||||||
let (count, _) = c.get();
|
|
||||||
c.set((count - 1, false));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_always_abort() {
|
|
||||||
GLOBAL_PANIC_COUNT.fetch_or(ALWAYS_ABORT_FLAG, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disregards ALWAYS_ABORT_FLAG
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_count() -> usize {
|
|
||||||
LOCAL_PANIC_COUNT.with(|c| c.get().0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disregards ALWAYS_ABORT_FLAG
|
|
||||||
#[must_use]
|
|
||||||
#[inline]
|
|
||||||
pub fn count_is_zero() -> bool {
|
|
||||||
if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) & !ALWAYS_ABORT_FLAG == 0 {
|
|
||||||
// Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads
|
|
||||||
// (including the current one) will have `LOCAL_PANIC_COUNT`
|
|
||||||
// equal to zero, so TLS access can be avoided.
|
|
||||||
//
|
|
||||||
// In terms of performance, a relaxed atomic load is similar to a normal
|
|
||||||
// aligned memory read (e.g., a mov instruction in x86), but with some
|
|
||||||
// compiler optimization restrictions. On the other hand, a TLS access
|
|
||||||
// might require calling a non-inlinable function (such as `__tls_get_addr`
|
|
||||||
// when using the GD TLS model).
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
is_zero_slow_path()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow path is in a separate function to reduce the amount of code
|
|
||||||
// inlined from `count_is_zero`.
|
|
||||||
#[inline(never)]
|
|
||||||
#[cold]
|
|
||||||
fn is_zero_slow_path() -> bool {
|
|
||||||
LOCAL_PANIC_COUNT.with(|c| c.get().0 == 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub use realstd::rt::panic_count;
|
|
||||||
|
|
||||||
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
|
|
||||||
#[cfg(panic = "immediate-abort")]
|
|
||||||
pub unsafe fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
|
|
||||||
Ok(f())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
|
|
||||||
#[cfg(not(panic = "immediate-abort"))]
|
|
||||||
pub unsafe fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
|
|
||||||
union Data<F, R> {
|
|
||||||
f: ManuallyDrop<F>,
|
|
||||||
r: ManuallyDrop<R>,
|
|
||||||
p: ManuallyDrop<Box<dyn Any + Send>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// We do some sketchy operations with ownership here for the sake of
|
|
||||||
// performance. We can only pass pointers down to `do_call` (can't pass
|
|
||||||
// objects by value), so we do all the ownership tracking here manually
|
|
||||||
// using a union.
|
|
||||||
//
|
|
||||||
// We go through a transition where:
|
|
||||||
//
|
|
||||||
// * First, we set the data field `f` to be the argumentless closure that we're going to call.
|
|
||||||
// * When we make the function call, the `do_call` function below, we take
|
|
||||||
// ownership of the function pointer. At this point the `data` union is
|
|
||||||
// entirely uninitialized.
|
|
||||||
// * If the closure successfully returns, we write the return value into the
|
|
||||||
// data's return slot (field `r`).
|
|
||||||
// * If the closure panics (`do_catch` below), we write the panic payload into field `p`.
|
|
||||||
// * Finally, when we come back out of the `try` intrinsic we're
|
|
||||||
// in one of two states:
|
|
||||||
//
|
|
||||||
// 1. The closure didn't panic, in which case the return value was
|
|
||||||
// filled in. We move it out of `data.r` and return it.
|
|
||||||
// 2. The closure panicked, in which case the panic payload was
|
|
||||||
// filled in. We move it out of `data.p` and return it.
|
|
||||||
//
|
|
||||||
// Once we stack all that together we should have the "most efficient'
|
|
||||||
// method of calling a catch panic whilst juggling ownership.
|
|
||||||
let mut data = Data { f: ManuallyDrop::new(f) };
|
|
||||||
|
|
||||||
let data_ptr = (&raw mut data) as *mut u8;
|
|
||||||
// SAFETY:
|
|
||||||
//
|
|
||||||
// Access to the union's fields: this is `std` and we know that the `catch_unwind`
|
|
||||||
// intrinsic fills in the `r` or `p` union field based on its return value.
|
|
||||||
//
|
|
||||||
// The call to `intrinsics::catch_unwind` is made safe by:
|
|
||||||
// - `do_call`, the first argument, can be called with the initial `data_ptr`.
|
|
||||||
// - `do_catch`, the second argument, can be called with the `data_ptr` as well.
|
|
||||||
// See their safety preconditions for more information
|
|
||||||
unsafe {
|
|
||||||
return if intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
|
|
||||||
Ok(ManuallyDrop::into_inner(data.r))
|
|
||||||
} else {
|
|
||||||
Err(ManuallyDrop::into_inner(data.p))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// We consider unwinding to be rare, so mark this function as cold. However,
|
|
||||||
// do not mark it no-inline -- that decision is best to leave to the
|
|
||||||
// optimizer (in most cases this function is not inlined even as a normal,
|
|
||||||
// non-cold function, though, as of the writing of this comment).
|
|
||||||
#[cold]
|
|
||||||
#[optimize(size)]
|
|
||||||
unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
|
|
||||||
// SAFETY: The whole unsafe block hinges on a correct implementation of
|
|
||||||
// the panic handler `__rust_panic_cleanup`. As such we can only
|
|
||||||
// assume it returns the correct thing for `Box::from_raw` to work
|
|
||||||
// without undefined behavior.
|
|
||||||
let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) };
|
|
||||||
panic_count::decrease();
|
|
||||||
obj
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY:
|
|
||||||
// data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
|
|
||||||
// Its must contains a valid `f` (type: F) value that can be use to fill
|
|
||||||
// `data.r`.
|
|
||||||
//
|
|
||||||
// This function cannot be marked as `unsafe` because `intrinsics::catch_unwind`
|
|
||||||
// expects normal function pointers.
|
|
||||||
#[inline]
|
|
||||||
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
|
|
||||||
// SAFETY: this is the responsibility of the caller, see above.
|
|
||||||
unsafe {
|
|
||||||
let data = data as *mut Data<F, R>;
|
|
||||||
let data = &mut (*data);
|
|
||||||
let f = ManuallyDrop::take(&mut data.f);
|
|
||||||
data.r = ManuallyDrop::new(f());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We *do* want this part of the catch to be inlined: this allows the
|
|
||||||
// compiler to properly track accesses to the Data union and optimize it
|
|
||||||
// away most of the time.
|
|
||||||
//
|
|
||||||
// SAFETY:
|
|
||||||
// data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
|
|
||||||
// Since this uses `cleanup` it also hinges on a correct implementation of
|
|
||||||
// `__rustc_panic_cleanup`.
|
|
||||||
//
|
|
||||||
// This function cannot be marked as `unsafe` because `intrinsics::catch_unwind`
|
|
||||||
// expects normal function pointers.
|
|
||||||
#[inline]
|
|
||||||
#[rustc_nounwind] // `intrinsic::catch_unwind` requires catch fn to be nounwind
|
|
||||||
fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
|
|
||||||
// SAFETY: this is the responsibility of the caller, see above.
|
|
||||||
//
|
|
||||||
// When `__rustc_panic_cleaner` is correctly implemented we can rely
|
|
||||||
// on `obj` being the correct thing to pass to `data.p` (after wrapping
|
|
||||||
// in `ManuallyDrop`).
|
|
||||||
unsafe {
|
|
||||||
let data = data as *mut Data<F, R>;
|
|
||||||
let data = &mut (*data);
|
|
||||||
let obj = cleanup(payload);
|
|
||||||
data.p = ManuallyDrop::new(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines whether the current thread is unwinding because of panic.
|
|
||||||
#[inline]
|
|
||||||
pub fn panicking() -> bool {
|
|
||||||
!panic_count::count_is_zero()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Entry point of panics from the core crate (`panic_impl` lang item).
|
|
||||||
#[cfg(not(any(test, doctest)))]
|
|
||||||
#[panic_handler]
|
|
||||||
pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
|
|
||||||
struct FormatStringPayload<'a> {
|
|
||||||
inner: &'a core::panic::PanicMessage<'a>,
|
|
||||||
string: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormatStringPayload<'_> {
|
|
||||||
fn fill(&mut self) -> &mut String {
|
|
||||||
let inner = self.inner;
|
|
||||||
// Lazily, the first time this gets called, run the actual string formatting.
|
|
||||||
self.string.get_or_insert_with(|| {
|
|
||||||
let mut s = String::new();
|
|
||||||
let mut fmt = fmt::Formatter::new(&mut s, fmt::FormattingOptions::new());
|
|
||||||
let _err = fmt::Display::fmt(&inner, &mut fmt);
|
|
||||||
s
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl PanicPayload for FormatStringPayload<'_> {
|
|
||||||
fn take_box(&mut self) -> *mut (dyn Any + Send) {
|
|
||||||
// We do two allocations here, unfortunately. But (a) they're required with the current
|
|
||||||
// scheme, and (b) we don't handle panic + OOM properly anyway (see comment in
|
|
||||||
// begin_panic below).
|
|
||||||
let contents = mem::take(self.fill());
|
|
||||||
Box::into_raw(Box::new(contents))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&mut self) -> &(dyn Any + Send) {
|
|
||||||
self.fill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for FormatStringPayload<'_> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
if let Some(s) = &self.string {
|
|
||||||
f.write_str(s)
|
|
||||||
} else {
|
|
||||||
fmt::Display::fmt(&self.inner, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StaticStrPayload(&'static str);
|
|
||||||
|
|
||||||
unsafe impl PanicPayload for StaticStrPayload {
|
|
||||||
fn take_box(&mut self) -> *mut (dyn Any + Send) {
|
|
||||||
Box::into_raw(Box::new(self.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&mut self) -> &(dyn Any + Send) {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_str(&mut self) -> Option<&str> {
|
|
||||||
Some(self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for StaticStrPayload {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str(self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let loc = info.location().unwrap(); // The current implementation always returns Some
|
|
||||||
let msg = info.message();
|
|
||||||
crate::sys::backtrace::__rust_end_short_backtrace(move || {
|
|
||||||
if let Some(s) = msg.as_str() {
|
|
||||||
panic_with_hook(
|
|
||||||
&mut StaticStrPayload(s),
|
|
||||||
loc,
|
|
||||||
info.can_unwind(),
|
|
||||||
info.force_no_backtrace(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
panic_with_hook(
|
|
||||||
&mut FormatStringPayload { inner: &msg, string: None },
|
|
||||||
loc,
|
|
||||||
info.can_unwind(),
|
|
||||||
info.force_no_backtrace(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is the entry point of panicking for the non-format-string variants of
|
|
||||||
/// panic!() and assert!(). In particular, this is the only entry point that supports
|
|
||||||
/// arbitrary payloads, not just format strings.
|
|
||||||
#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
|
|
||||||
#[cfg_attr(not(any(test, doctest)), lang = "begin_panic")]
|
|
||||||
// lang item for CTFE panic support
|
|
||||||
// never inline unless panic=immediate-abort to avoid code
|
|
||||||
// bloat at the call sites as much as possible
|
|
||||||
#[cfg_attr(not(panic = "immediate-abort"), inline(never), cold, optimize(size))]
|
|
||||||
#[cfg_attr(panic = "immediate-abort", inline)]
|
|
||||||
#[track_caller]
|
|
||||||
#[rustc_do_not_const_check] // hooked by const-eval
|
|
||||||
pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
|
|
||||||
if cfg!(panic = "immediate-abort") {
|
|
||||||
intrinsics::abort()
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Payload<A> {
|
|
||||||
inner: Option<A>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<A: Send + 'static> PanicPayload for Payload<A> {
|
|
||||||
fn take_box(&mut self) -> *mut (dyn Any + Send) {
|
|
||||||
// Note that this should be the only allocation performed in this code path. Currently
|
|
||||||
// this means that panic!() on OOM will invoke this code path, but then again we're not
|
|
||||||
// really ready for panic on OOM anyway. If we do start doing this, then we should
|
|
||||||
// propagate this allocation to be performed in the parent of this thread instead of the
|
|
||||||
// thread that's panicking.
|
|
||||||
let data = match self.inner.take() {
|
|
||||||
Some(a) => Box::new(a) as Box<dyn Any + Send>,
|
|
||||||
None => process::abort(),
|
|
||||||
};
|
|
||||||
Box::into_raw(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&mut self) -> &(dyn Any + Send) {
|
|
||||||
match self.inner {
|
|
||||||
Some(ref a) => a,
|
|
||||||
None => process::abort(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: 'static> fmt::Display for Payload<A> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match &self.inner {
|
|
||||||
Some(a) => f.write_str(payload_as_str(a)),
|
|
||||||
None => process::abort(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let loc = Location::caller();
|
|
||||||
crate::sys::backtrace::__rust_end_short_backtrace(move || {
|
|
||||||
panic_with_hook(
|
|
||||||
&mut Payload { inner: Some(msg) },
|
|
||||||
loc,
|
|
||||||
/* can_unwind */ true,
|
|
||||||
/* force_no_backtrace */ false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn payload_as_str(payload: &dyn Any) -> &str {
|
|
||||||
if let Some(&s) = payload.downcast_ref::<&'static str>() {
|
|
||||||
s
|
|
||||||
} else if let Some(s) = payload.downcast_ref::<String>() {
|
|
||||||
s.as_str()
|
|
||||||
} else {
|
|
||||||
"Box<dyn Any>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Central point for dispatching panics.
|
|
||||||
///
|
|
||||||
/// Executes the primary logic for a panic, including checking for recursive
|
|
||||||
/// panics, panic hooks, and finally dispatching to the panic runtime to either
|
|
||||||
/// abort or unwind.
|
|
||||||
#[optimize(size)]
|
|
||||||
fn panic_with_hook(
|
|
||||||
payload: &mut dyn PanicPayload,
|
|
||||||
location: &Location<'_>,
|
|
||||||
can_unwind: bool,
|
|
||||||
force_no_backtrace: bool,
|
|
||||||
) -> ! {
|
|
||||||
let must_abort = panic_count::increase(true);
|
|
||||||
|
|
||||||
// Check if we need to abort immediately.
|
|
||||||
if let Some(must_abort) = must_abort {
|
|
||||||
match must_abort {
|
|
||||||
panic_count::MustAbort::PanicInHook => {
|
|
||||||
// Don't try to format the message in this case, perhaps that is causing the
|
|
||||||
// recursive panics. However if the message is just a string, no user-defined
|
|
||||||
// code is involved in printing it, so that is risk-free.
|
|
||||||
let message: &str = payload.as_str().unwrap_or_default();
|
|
||||||
rtprintpanic!(
|
|
||||||
"panicked at {location}:\n{message}\nthread panicked while processing panic. aborting.\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
panic_count::MustAbort::AlwaysAbort => {
|
|
||||||
// Unfortunately, this does not print a backtrace, because creating
|
|
||||||
// a `Backtrace` will allocate, which we must avoid here.
|
|
||||||
rtprintpanic!("aborting due to panic at {location}:\n{payload}\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crate::process::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
match *HOOK.read() {
|
|
||||||
// Some platforms (like wasm) know that printing to stderr won't ever actually
|
|
||||||
// print anything, and if that's the case we can skip the default
|
|
||||||
// hook. Since string formatting happens lazily when calling `payload`
|
|
||||||
// methods, this means we avoid formatting the string at all!
|
|
||||||
// (The panic runtime might still call `payload.take_box()` though and trigger
|
|
||||||
// formatting.)
|
|
||||||
Hook::Default if panic_output().is_none() => {}
|
|
||||||
Hook::Default => {
|
|
||||||
default_hook(&PanicHookInfo::new(
|
|
||||||
location,
|
|
||||||
payload.get(),
|
|
||||||
can_unwind,
|
|
||||||
force_no_backtrace,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Hook::Custom(ref hook) => {
|
|
||||||
hook(&PanicHookInfo::new(location, payload.get(), can_unwind, force_no_backtrace));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indicate that we have finished executing the panic hook. After this point
|
|
||||||
// it is fine if there is a panic while executing destructors, as long as it
|
|
||||||
// it contained within a `catch_unwind`.
|
|
||||||
panic_count::finished_panic_hook();
|
|
||||||
|
|
||||||
if !can_unwind {
|
|
||||||
// If a thread panics while running destructors or tries to unwind
|
|
||||||
// through a nounwind function (e.g. extern "C") then we cannot continue
|
|
||||||
// unwinding and have to abort immediately.
|
|
||||||
rtprintpanic!("thread caused non-unwinding panic. aborting.\n");
|
|
||||||
crate::process::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
rust_panic(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is the entry point for `resume_unwind`.
|
|
||||||
/// It just forwards the payload to the panic runtime.
|
|
||||||
#[cfg_attr(panic = "immediate-abort", inline)]
|
|
||||||
pub fn resume_unwind(payload: Box<dyn Any + Send>) -> ! {
|
|
||||||
panic_count::increase(false);
|
|
||||||
|
|
||||||
struct RewrapBox(Box<dyn Any + Send>);
|
|
||||||
|
|
||||||
unsafe impl PanicPayload for RewrapBox {
|
|
||||||
fn take_box(&mut self) -> *mut (dyn Any + Send) {
|
|
||||||
Box::into_raw(mem::replace(&mut self.0, Box::new(())))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&mut self) -> &(dyn Any + Send) {
|
|
||||||
&*self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for RewrapBox {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.write_str(payload_as_str(&self.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rust_panic(&mut RewrapBox(payload))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A function with a fixed suffix (through `rustc_std_internal_symbol`)
|
|
||||||
/// on which to slap yer breakpoints.
|
|
||||||
#[inline(never)]
|
|
||||||
#[cfg_attr(not(test), rustc_std_internal_symbol)]
|
|
||||||
#[cfg(not(panic = "immediate-abort"))]
|
|
||||||
fn rust_panic(msg: &mut dyn PanicPayload) -> ! {
|
|
||||||
let code = unsafe { __rust_start_panic(msg) };
|
|
||||||
rtabort!("failed to initiate panic, error {code}")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(not(test), rustc_std_internal_symbol)]
|
|
||||||
#[cfg(panic = "immediate-abort")]
|
|
||||||
fn rust_panic(_: &mut dyn PanicPayload) -> ! {
|
|
||||||
crate::intrinsics::abort();
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,181 +0,0 @@
|
|||||||
pub mod rust_2024 {
|
|
||||||
pub use crate::print;
|
|
||||||
pub use crate::println;
|
|
||||||
pub use alloc::format;
|
|
||||||
pub use alloc::vec;
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use crate::borrow::ToOwned;
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use crate::boxed::Box;
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use crate::string::{String, ToString};
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[doc(no_inline)]
|
|
||||||
pub use crate::vec::Vec;
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn abort() -> ! {
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
macro_rules! rtabort {
|
|
||||||
($($t:tt)*) => {{
|
|
||||||
loop {}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
macro_rules! rtprintpanic {
|
|
||||||
($($t:tt)*) => {{}};
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
pub mod barrier;
|
|
||||||
pub mod lazy_lock;
|
|
||||||
pub mod mpmc;
|
|
||||||
pub mod mpsc;
|
|
||||||
pub mod nonpoison;
|
|
||||||
pub mod once;
|
|
||||||
pub mod once_lock;
|
|
||||||
pub mod poison;
|
|
||||||
pub mod reentrant_lock;
|
|
||||||
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub use core::sync::atomic;
|
|
||||||
|
|
||||||
pub use once::Once;
|
|
||||||
pub use once::OnceState;
|
|
||||||
|
|
||||||
pub use poison::LockResult;
|
|
||||||
pub use poison::Mutex;
|
|
||||||
pub use poison::MutexGuard;
|
|
||||||
pub use poison::PoisonError;
|
|
||||||
pub use poison::TryLockError;
|
|
||||||
pub use poison::TryLockResult;
|
|
||||||
pub use poison::Condvar;
|
|
||||||
pub use poison::RwLock;
|
|
||||||
pub use once_lock::OnceLock;
|
|
||||||
pub use reentrant_lock::ReentrantLock;
|
|
||||||
pub use reentrant_lock::ReentrantLockGuard;
|
|
||||||
pub use alloc_crate::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
||||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
|
||||||
pub struct WaitTimeoutResult(bool);
|
|
||||||
|
|
||||||
impl WaitTimeoutResult {
|
|
||||||
/// Returns `true` if the wait was known to have timed out.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// This example spawns a thread which will sleep 20 milliseconds before
|
|
||||||
/// updating a boolean value and then notifying the condvar.
|
|
||||||
///
|
|
||||||
/// The main thread will wait with a 10 millisecond timeout on the condvar
|
|
||||||
/// and will leave the loop upon timeout.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::{Arc, Condvar, Mutex};
|
|
||||||
/// use std::thread;
|
|
||||||
/// use std::time::Duration;
|
|
||||||
///
|
|
||||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
|
||||||
/// let pair2 = Arc::clone(&pair);
|
|
||||||
///
|
|
||||||
/// # let handle =
|
|
||||||
/// thread::spawn(move || {
|
|
||||||
/// let (lock, cvar) = &*pair2;
|
|
||||||
///
|
|
||||||
/// // Let's wait 20 milliseconds before notifying the condvar.
|
|
||||||
/// thread::sleep(Duration::from_millis(20));
|
|
||||||
///
|
|
||||||
/// let mut started = lock.lock().unwrap();
|
|
||||||
/// // We update the boolean value.
|
|
||||||
/// *started = true;
|
|
||||||
/// cvar.notify_one();
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// // Wait for the thread to start up.
|
|
||||||
/// let (lock, cvar) = &*pair;
|
|
||||||
/// loop {
|
|
||||||
/// // Let's put a timeout on the condvar's wait.
|
|
||||||
/// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap();
|
|
||||||
/// // 10 milliseconds have passed.
|
|
||||||
/// if result.1.timed_out() {
|
|
||||||
/// // timed out now and we can leave.
|
|
||||||
/// break
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// # // Prevent leaks for Miri.
|
|
||||||
/// # let _ = handle.join();
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
|
||||||
pub fn timed_out(&self) -> bool {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
use crate::fmt;
|
|
||||||
use crate::panic::RefUnwindSafe;
|
|
||||||
use crate::sync::nonpoison::{Condvar, Mutex};
|
|
||||||
|
|
||||||
/// A barrier enables multiple threads to synchronize the beginning
|
|
||||||
/// of some computation.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::Barrier;
|
|
||||||
/// use std::thread;
|
|
||||||
///
|
|
||||||
/// let n = 10;
|
|
||||||
/// let barrier = Barrier::new(n);
|
|
||||||
/// thread::scope(|s| {
|
|
||||||
/// for _ in 0..n {
|
|
||||||
/// // The same messages will be printed together.
|
|
||||||
/// // You will NOT see any interleaving.
|
|
||||||
/// s.spawn(|| {
|
|
||||||
/// println!("before wait");
|
|
||||||
/// barrier.wait();
|
|
||||||
/// println!("after wait");
|
|
||||||
/// });
|
|
||||||
/// }
|
|
||||||
/// });
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub struct Barrier {
|
|
||||||
lock: Mutex<BarrierState>,
|
|
||||||
cvar: Condvar,
|
|
||||||
num_threads: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
|
|
||||||
impl RefUnwindSafe for Barrier {}
|
|
||||||
|
|
||||||
// The inner state of a double barrier
|
|
||||||
struct BarrierState {
|
|
||||||
count: usize,
|
|
||||||
generation_id: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
|
|
||||||
/// in the [`Barrier`] have rendezvoused.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::Barrier;
|
|
||||||
///
|
|
||||||
/// let barrier = Barrier::new(1);
|
|
||||||
/// let barrier_wait_result = barrier.wait();
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub struct BarrierWaitResult(bool);
|
|
||||||
|
|
||||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
|
||||||
impl fmt::Debug for Barrier {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("Barrier").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Barrier {
|
|
||||||
/// Creates a new barrier that can block a given number of threads.
|
|
||||||
///
|
|
||||||
/// A barrier will block all threads which call [`wait()`] until the `n`th thread calls [`wait()`],
|
|
||||||
/// and then wake up all threads at once.
|
|
||||||
///
|
|
||||||
/// [`wait()`]: Barrier::wait
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::Barrier;
|
|
||||||
///
|
|
||||||
/// let barrier = Barrier::new(10);
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[rustc_const_stable(feature = "const_barrier", since = "1.78.0")]
|
|
||||||
#[must_use]
|
|
||||||
#[inline]
|
|
||||||
pub const fn new(n: usize) -> Barrier {
|
|
||||||
Barrier {
|
|
||||||
lock: Mutex::new(BarrierState { count: 0, generation_id: 0 }),
|
|
||||||
cvar: Condvar::new(),
|
|
||||||
num_threads: n,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Blocks the current thread until all threads have rendezvoused here.
|
|
||||||
///
|
|
||||||
/// Barriers are re-usable after all threads have rendezvoused once, and can
|
|
||||||
/// be used continuously.
|
|
||||||
///
|
|
||||||
/// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
|
|
||||||
/// returns `true` from [`BarrierWaitResult::is_leader()`] when returning
|
|
||||||
/// from this function, and all other threads will receive a result that
|
|
||||||
/// will return `false` from [`BarrierWaitResult::is_leader()`].
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::Barrier;
|
|
||||||
/// use std::thread;
|
|
||||||
///
|
|
||||||
/// let n = 10;
|
|
||||||
/// let barrier = Barrier::new(n);
|
|
||||||
/// thread::scope(|s| {
|
|
||||||
/// for _ in 0..n {
|
|
||||||
/// // The same messages will be printed together.
|
|
||||||
/// // You will NOT see any interleaving.
|
|
||||||
/// s.spawn(|| {
|
|
||||||
/// println!("before wait");
|
|
||||||
/// barrier.wait();
|
|
||||||
/// println!("after wait");
|
|
||||||
/// });
|
|
||||||
/// }
|
|
||||||
/// });
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub fn wait(&self) -> BarrierWaitResult {
|
|
||||||
let mut lock = self.lock.lock();
|
|
||||||
let local_gen = lock.generation_id;
|
|
||||||
lock.count += 1;
|
|
||||||
if lock.count < self.num_threads {
|
|
||||||
self.cvar.wait_while(&mut lock, |state| local_gen == state.generation_id);
|
|
||||||
BarrierWaitResult(false)
|
|
||||||
} else {
|
|
||||||
lock.count = 0;
|
|
||||||
lock.generation_id = lock.generation_id.wrapping_add(1);
|
|
||||||
self.cvar.notify_all();
|
|
||||||
BarrierWaitResult(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
|
||||||
impl fmt::Debug for BarrierWaitResult {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("BarrierWaitResult").field("is_leader", &self.is_leader()).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BarrierWaitResult {
|
|
||||||
/// Returns `true` if this thread is the "leader thread" for the call to
|
|
||||||
/// [`Barrier::wait()`].
|
|
||||||
///
|
|
||||||
/// Only one thread will have `true` returned from their result, all other
|
|
||||||
/// threads will have `false` returned.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::Barrier;
|
|
||||||
///
|
|
||||||
/// let barrier = Barrier::new(1);
|
|
||||||
/// let barrier_wait_result = barrier.wait();
|
|
||||||
/// println!("{:?}", barrier_wait_result.is_leader());
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_leader(&self) -> bool {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,422 +0,0 @@
|
|||||||
use super::once::OnceExclusiveState;
|
|
||||||
use crate::cell::UnsafeCell;
|
|
||||||
use crate::mem::ManuallyDrop;
|
|
||||||
use crate::ops::{Deref, DerefMut};
|
|
||||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
|
||||||
use crate::sync::Once;
|
|
||||||
use crate::{fmt, ptr};
|
|
||||||
|
|
||||||
// We use the state of a Once as discriminant value. Upon creation, the state is
|
|
||||||
// "incomplete" and `f` contains the initialization closure. In the first call to
|
|
||||||
// `call_once`, `f` is taken and run. If it succeeds, `value` is set and the state
|
|
||||||
// is changed to "complete". If it panics, the Once is poisoned, so none of the
|
|
||||||
// two fields is initialized.
|
|
||||||
union Data<T, F> {
|
|
||||||
value: ManuallyDrop<T>,
|
|
||||||
f: ManuallyDrop<F>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A value which is initialized on the first access.
|
|
||||||
///
|
|
||||||
/// This type is a thread-safe [`LazyCell`], and can be used in statics.
|
|
||||||
/// Since initialization may be called from multiple threads, any
|
|
||||||
/// dereferencing call will block the calling thread if another
|
|
||||||
/// initialization routine is currently running.
|
|
||||||
///
|
|
||||||
/// [`LazyCell`]: crate::cell::LazyCell
|
|
||||||
///
|
|
||||||
/// # Poisoning
|
|
||||||
///
|
|
||||||
/// If the initialization closure passed to [`LazyLock::new`] panics, the lock will be poisoned.
|
|
||||||
/// Once the lock is poisoned, any threads that attempt to access this lock (via a dereference
|
|
||||||
/// or via an explicit call to [`force()`]) will panic.
|
|
||||||
///
|
|
||||||
/// This concept is similar to that of poisoning in the [`std::sync::poison`] module. A key
|
|
||||||
/// difference, however, is that poisoning in `LazyLock` is _unrecoverable_. All future accesses of
|
|
||||||
/// the lock from other threads will panic, whereas a type in [`std::sync::poison`] like
|
|
||||||
/// [`std::sync::poison::Mutex`] allows recovery via [`PoisonError::into_inner()`].
|
|
||||||
///
|
|
||||||
/// [`force()`]: LazyLock::force
|
|
||||||
/// [`std::sync::poison`]: crate::sync::poison
|
|
||||||
/// [`std::sync::poison::Mutex`]: crate::sync::poison::Mutex
|
|
||||||
/// [`PoisonError::into_inner()`]: crate::sync::poison::PoisonError::into_inner
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// Initialize static variables with `LazyLock`.
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::LazyLock;
|
|
||||||
///
|
|
||||||
/// // Note: static items do not call [`Drop`] on program termination, so this won't be deallocated.
|
|
||||||
/// // this is fine, as the OS can deallocate the terminated program faster than we can free memory
|
|
||||||
/// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional.
|
|
||||||
/// static DEEP_THOUGHT: LazyLock<String> = LazyLock::new(|| {
|
|
||||||
/// # mod another_crate {
|
|
||||||
/// # pub fn great_question() -> String { "42".to_string() }
|
|
||||||
/// # }
|
|
||||||
/// // M3 Ultra takes about 16 million years in --release config
|
|
||||||
/// another_crate::great_question()
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// // The `String` is built, stored in the `LazyLock`, and returned as `&String`.
|
|
||||||
/// let _ = &*DEEP_THOUGHT;
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Initialize fields with `LazyLock`.
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::LazyLock;
|
|
||||||
///
|
|
||||||
/// #[derive(Debug)]
|
|
||||||
/// struct UseCellLock {
|
|
||||||
/// number: LazyLock<u32>,
|
|
||||||
/// }
|
|
||||||
/// fn main() {
|
|
||||||
/// let lock: LazyLock<u32> = LazyLock::new(|| 0u32);
|
|
||||||
///
|
|
||||||
/// let data = UseCellLock { number: lock };
|
|
||||||
/// println!("{}", *data.number);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
pub struct LazyLock<T, F = fn() -> T> {
|
|
||||||
// FIXME(nonpoison_once): if possible, switch to nonpoison version once it is available
|
|
||||||
once: Once,
|
|
||||||
data: UnsafeCell<Data<T, F>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
|
|
||||||
/// Creates a new lazy value with the given initializing function.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::LazyLock;
|
|
||||||
///
|
|
||||||
/// let hello = "Hello, World!".to_string();
|
|
||||||
///
|
|
||||||
/// let lazy = LazyLock::new(|| hello.to_uppercase());
|
|
||||||
///
|
|
||||||
/// assert_eq!(&*lazy, "HELLO, WORLD!");
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
#[rustc_const_stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
pub const fn new(f: F) -> LazyLock<T, F> {
|
|
||||||
LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new lazy value that is already initialized.
|
|
||||||
#[inline]
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn preinit(value: T) -> LazyLock<T, F> {
|
|
||||||
let once = Once::new();
|
|
||||||
once.call_once(|| {});
|
|
||||||
LazyLock { once, data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes this `LazyLock` returning the stored value.
|
|
||||||
///
|
|
||||||
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the lock is poisoned.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// #![feature(lazy_cell_into_inner)]
|
|
||||||
///
|
|
||||||
/// use std::sync::LazyLock;
|
|
||||||
///
|
|
||||||
/// let hello = "Hello, World!".to_string();
|
|
||||||
///
|
|
||||||
/// let lazy = LazyLock::new(|| hello.to_uppercase());
|
|
||||||
///
|
|
||||||
/// assert_eq!(&*lazy, "HELLO, WORLD!");
|
|
||||||
/// assert_eq!(LazyLock::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string()));
|
|
||||||
/// ```
|
|
||||||
#[unstable(feature = "lazy_cell_into_inner", issue = "125623")]
|
|
||||||
pub fn into_inner(mut this: Self) -> Result<T, F> {
|
|
||||||
let state = this.once.state();
|
|
||||||
match state {
|
|
||||||
OnceExclusiveState::Poisoned => panic_poisoned(),
|
|
||||||
state => {
|
|
||||||
let this = ManuallyDrop::new(this);
|
|
||||||
let data = unsafe { ptr::read(&this.data) }.into_inner();
|
|
||||||
match state {
|
|
||||||
OnceExclusiveState::Incomplete => {
|
|
||||||
Err(ManuallyDrop::into_inner(unsafe { data.f }))
|
|
||||||
}
|
|
||||||
OnceExclusiveState::Complete => {
|
|
||||||
Ok(ManuallyDrop::into_inner(unsafe { data.value }))
|
|
||||||
}
|
|
||||||
OnceExclusiveState::Poisoned => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Forces the evaluation of this lazy value and returns a mutable reference to
|
|
||||||
/// the result.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
|
||||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
|
||||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
|
||||||
///
|
|
||||||
/// [`new()`]: LazyLock::new
|
|
||||||
/// [`force()`]: LazyLock::force
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::LazyLock;
|
|
||||||
///
|
|
||||||
/// let mut lazy = LazyLock::new(|| 92);
|
|
||||||
///
|
|
||||||
/// let p = LazyLock::force_mut(&mut lazy);
|
|
||||||
/// assert_eq!(*p, 92);
|
|
||||||
/// *p = 44;
|
|
||||||
/// assert_eq!(*lazy, 44);
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[stable(feature = "lazy_get", since = "1.94.0")]
|
|
||||||
pub fn force_mut(this: &mut LazyLock<T, F>) -> &mut T {
|
|
||||||
#[cold]
|
|
||||||
/// # Safety
|
|
||||||
/// May only be called when the state is `Incomplete`.
|
|
||||||
unsafe fn really_init_mut<T, F: FnOnce() -> T>(this: &mut LazyLock<T, F>) -> &mut T {
|
|
||||||
struct PoisonOnPanic<'a, T, F>(&'a mut LazyLock<T, F>);
|
|
||||||
impl<T, F> Drop for PoisonOnPanic<'_, T, F> {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.0.once.set_state(OnceExclusiveState::Poisoned);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: We always poison if the initializer panics (then we never check the data),
|
|
||||||
// or set the data on success.
|
|
||||||
let f = unsafe { ManuallyDrop::take(&mut this.data.get_mut().f) };
|
|
||||||
// INVARIANT: Initiated from mutable reference, don't drop because we read it.
|
|
||||||
let guard = PoisonOnPanic(this);
|
|
||||||
let data = f();
|
|
||||||
guard.0.data.get_mut().value = ManuallyDrop::new(data);
|
|
||||||
guard.0.once.set_state(OnceExclusiveState::Complete);
|
|
||||||
core::mem::forget(guard);
|
|
||||||
// SAFETY: We put the value there above.
|
|
||||||
unsafe { &mut this.data.get_mut().value }
|
|
||||||
}
|
|
||||||
|
|
||||||
let state = this.once.state();
|
|
||||||
match state {
|
|
||||||
OnceExclusiveState::Poisoned => panic_poisoned(),
|
|
||||||
// SAFETY: The `Once` states we completed the initialization.
|
|
||||||
OnceExclusiveState::Complete => unsafe { &mut this.data.get_mut().value },
|
|
||||||
// SAFETY: The state is `Incomplete`.
|
|
||||||
OnceExclusiveState::Incomplete => unsafe { really_init_mut(this) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Forces the evaluation of this lazy value and returns a reference to
|
|
||||||
/// result. This is equivalent to the `Deref` impl, but is explicit.
|
|
||||||
///
|
|
||||||
/// This method will block the calling thread if another initialization
|
|
||||||
/// routine is currently running.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
|
||||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
|
||||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
|
||||||
///
|
|
||||||
/// [`new()`]: LazyLock::new
|
|
||||||
/// [`force()`]: LazyLock::force
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::LazyLock;
|
|
||||||
///
|
|
||||||
/// let lazy = LazyLock::new(|| 92);
|
|
||||||
///
|
|
||||||
/// assert_eq!(LazyLock::force(&lazy), &92);
|
|
||||||
/// assert_eq!(&*lazy, &92);
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
#[rustc_should_not_be_called_on_const_items]
|
|
||||||
pub fn force(this: &LazyLock<T, F>) -> &T {
|
|
||||||
this.once.call_once_force(|state| {
|
|
||||||
if state.is_poisoned() {
|
|
||||||
panic_poisoned();
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: `call_once` only runs this closure once, ever.
|
|
||||||
let data = unsafe { &mut *this.data.get() };
|
|
||||||
let f = unsafe { ManuallyDrop::take(&mut data.f) };
|
|
||||||
let value = f();
|
|
||||||
data.value = ManuallyDrop::new(value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// SAFETY:
|
|
||||||
// There are four possible scenarios:
|
|
||||||
// * the closure was called and initialized `value`.
|
|
||||||
// * the closure was called and panicked, so this point is never reached.
|
|
||||||
// * the closure was not called, but a previous call initialized `value`.
|
|
||||||
// * the closure was not called because the Once is poisoned, which we handled above.
|
|
||||||
// So `value` has definitely been initialized and will not be modified again.
|
|
||||||
unsafe { &*(*this.data.get()).value }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, F> LazyLock<T, F> {
|
|
||||||
/// Returns a mutable reference to the value if initialized. Otherwise (if uninitialized or
|
|
||||||
/// poisoned), returns `None`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::LazyLock;
|
|
||||||
///
|
|
||||||
/// let mut lazy = LazyLock::new(|| 92);
|
|
||||||
///
|
|
||||||
/// assert_eq!(LazyLock::get_mut(&mut lazy), None);
|
|
||||||
/// let _ = LazyLock::force(&lazy);
|
|
||||||
/// *LazyLock::get_mut(&mut lazy).unwrap() = 44;
|
|
||||||
/// assert_eq!(*lazy, 44);
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[stable(feature = "lazy_get", since = "1.94.0")]
|
|
||||||
pub fn get_mut(this: &mut LazyLock<T, F>) -> Option<&mut T> {
|
|
||||||
// `state()` does not perform an atomic load, so prefer it over `is_complete()`.
|
|
||||||
let state = this.once.state();
|
|
||||||
match state {
|
|
||||||
// SAFETY:
|
|
||||||
// The closure has been run successfully, so `value` has been initialized.
|
|
||||||
OnceExclusiveState::Complete => Some(unsafe { &mut this.data.get_mut().value }),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the value if initialized. Otherwise (if uninitialized or poisoned),
|
|
||||||
/// returns `None`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::sync::LazyLock;
|
|
||||||
///
|
|
||||||
/// let lazy = LazyLock::new(|| 92);
|
|
||||||
///
|
|
||||||
/// assert_eq!(LazyLock::get(&lazy), None);
|
|
||||||
/// let _ = LazyLock::force(&lazy);
|
|
||||||
/// assert_eq!(LazyLock::get(&lazy), Some(&92));
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[stable(feature = "lazy_get", since = "1.94.0")]
|
|
||||||
#[rustc_should_not_be_called_on_const_items]
|
|
||||||
pub fn get(this: &LazyLock<T, F>) -> Option<&T> {
|
|
||||||
if this.once.is_completed() {
|
|
||||||
// SAFETY:
|
|
||||||
// The closure has been run successfully, so `value` has been initialized
|
|
||||||
// and will not be modified again.
|
|
||||||
Some(unsafe { &(*this.data.get()).value })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
impl<T, F> Drop for LazyLock<T, F> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
match self.once.state() {
|
|
||||||
OnceExclusiveState::Incomplete => unsafe {
|
|
||||||
ManuallyDrop::drop(&mut self.data.get_mut().f)
|
|
||||||
},
|
|
||||||
OnceExclusiveState::Complete => unsafe {
|
|
||||||
ManuallyDrop::drop(&mut self.data.get_mut().value)
|
|
||||||
},
|
|
||||||
OnceExclusiveState::Poisoned => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
/// Dereferences the value.
|
|
||||||
///
|
|
||||||
/// This method will block the calling thread if another initialization
|
|
||||||
/// routine is currently running.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
|
||||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
|
||||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
|
||||||
///
|
|
||||||
/// [`new()`]: LazyLock::new
|
|
||||||
/// [`force()`]: LazyLock::force
|
|
||||||
#[inline]
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
LazyLock::force(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "lazy_deref_mut", since = "1.89.0")]
|
|
||||||
impl<T, F: FnOnce() -> T> DerefMut for LazyLock<T, F> {
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
|
||||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
|
||||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
|
||||||
///
|
|
||||||
/// [`new()`]: LazyLock::new
|
|
||||||
/// [`force()`]: LazyLock::force
|
|
||||||
#[inline]
|
|
||||||
fn deref_mut(&mut self) -> &mut T {
|
|
||||||
LazyLock::force_mut(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
impl<T: Default> Default for LazyLock<T> {
|
|
||||||
/// Creates a new lazy value using `Default` as the initializing function.
|
|
||||||
#[inline]
|
|
||||||
fn default() -> LazyLock<T> {
|
|
||||||
LazyLock::new(T::default)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let mut d = f.debug_tuple("LazyLock");
|
|
||||||
match LazyLock::get(self) {
|
|
||||||
Some(v) => d.field(v),
|
|
||||||
None => d.field(&format_args!("<uninit>")),
|
|
||||||
};
|
|
||||||
d.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cold]
|
|
||||||
#[inline(never)]
|
|
||||||
fn panic_poisoned() -> ! {
|
|
||||||
panic!("LazyLock instance has previously been poisoned")
|
|
||||||
}
|
|
||||||
|
|
||||||
// We never create a `&F` from a `&LazyLock<T, F>` so it is fine
|
|
||||||
// to not impl `Sync` for `F`.
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {}
|
|
||||||
// auto-derived `Send` impl is OK.
|
|
||||||
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {}
|
|
||||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
|
||||||
impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {}
|
|
||||||
@@ -1,569 +0,0 @@
|
|||||||
//! Bounded channel based on a preallocated array.
|
|
||||||
//!
|
|
||||||
//! This flavor has a fixed, positive capacity.
|
|
||||||
//!
|
|
||||||
//! The implementation is based on Dmitry Vyukov's bounded MPMC queue.
|
|
||||||
//!
|
|
||||||
//! Source:
|
|
||||||
//! - <http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue>
|
|
||||||
//! - <https://docs.google.com/document/d/1yIAYmbvL3JxOKOjuCyon7JhW4cSv1wy5hC0ApeGMV9s/pub>
|
|
||||||
|
|
||||||
use super::context::Context;
|
|
||||||
use super::error::*;
|
|
||||||
use super::select::{Operation, Selected, Token};
|
|
||||||
use super::utils::{Backoff, CachePadded};
|
|
||||||
use super::waker::SyncWaker;
|
|
||||||
use crate::cell::UnsafeCell;
|
|
||||||
use crate::mem::MaybeUninit;
|
|
||||||
use crate::ptr;
|
|
||||||
use crate::sync::atomic::{self, Atomic, AtomicUsize, Ordering};
|
|
||||||
use crate::time::Instant;
|
|
||||||
|
|
||||||
/// A slot in a channel.
|
|
||||||
struct Slot<T> {
|
|
||||||
/// The current stamp.
|
|
||||||
stamp: Atomic<usize>,
|
|
||||||
|
|
||||||
/// The message in this slot. Either read out in `read` or dropped through
|
|
||||||
/// `discard_all_messages`.
|
|
||||||
msg: UnsafeCell<MaybeUninit<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The token type for the array flavor.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct ArrayToken {
|
|
||||||
/// Slot to read from or write to.
|
|
||||||
slot: *const u8,
|
|
||||||
|
|
||||||
/// Stamp to store into the slot after reading or writing.
|
|
||||||
stamp: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ArrayToken {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
ArrayToken { slot: ptr::null(), stamp: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bounded channel based on a preallocated array.
|
|
||||||
pub(crate) struct Channel<T> {
|
|
||||||
/// The head of the channel.
|
|
||||||
///
|
|
||||||
/// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but
|
|
||||||
/// packed into a single `usize`. The lower bits represent the index, while the upper bits
|
|
||||||
/// represent the lap. The mark bit in the head is always zero.
|
|
||||||
///
|
|
||||||
/// Messages are popped from the head of the channel.
|
|
||||||
head: CachePadded<Atomic<usize>>,
|
|
||||||
|
|
||||||
/// The tail of the channel.
|
|
||||||
///
|
|
||||||
/// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but
|
|
||||||
/// packed into a single `usize`. The lower bits represent the index, while the upper bits
|
|
||||||
/// represent the lap. The mark bit indicates that the channel is disconnected.
|
|
||||||
///
|
|
||||||
/// Messages are pushed into the tail of the channel.
|
|
||||||
tail: CachePadded<Atomic<usize>>,
|
|
||||||
|
|
||||||
/// The buffer holding slots.
|
|
||||||
buffer: Box<[Slot<T>]>,
|
|
||||||
|
|
||||||
/// The channel capacity.
|
|
||||||
cap: usize,
|
|
||||||
|
|
||||||
/// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`.
|
|
||||||
one_lap: usize,
|
|
||||||
|
|
||||||
/// If this bit is set in the tail, that means the channel is disconnected.
|
|
||||||
mark_bit: usize,
|
|
||||||
|
|
||||||
/// Senders waiting while the channel is full.
|
|
||||||
senders: SyncWaker,
|
|
||||||
|
|
||||||
/// Receivers waiting while the channel is empty and not disconnected.
|
|
||||||
receivers: SyncWaker,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Channel<T> {
|
|
||||||
/// Creates a bounded channel of capacity `cap`.
|
|
||||||
pub(crate) fn with_capacity(cap: usize) -> Self {
|
|
||||||
assert!(cap > 0, "capacity must be positive");
|
|
||||||
|
|
||||||
// Compute constants `mark_bit` and `one_lap`.
|
|
||||||
let mark_bit = (cap + 1).next_power_of_two();
|
|
||||||
let one_lap = mark_bit * 2;
|
|
||||||
|
|
||||||
// Head is initialized to `{ lap: 0, mark: 0, index: 0 }`.
|
|
||||||
let head = 0;
|
|
||||||
// Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`.
|
|
||||||
let tail = 0;
|
|
||||||
|
|
||||||
// Allocate a buffer of `cap` slots initialized
|
|
||||||
// with stamps.
|
|
||||||
let buffer: Box<[Slot<T>]> = (0..cap)
|
|
||||||
.map(|i| {
|
|
||||||
// Set the stamp to `{ lap: 0, mark: 0, index: i }`.
|
|
||||||
Slot { stamp: AtomicUsize::new(i), msg: UnsafeCell::new(MaybeUninit::uninit()) }
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Channel {
|
|
||||||
buffer,
|
|
||||||
cap,
|
|
||||||
one_lap,
|
|
||||||
mark_bit,
|
|
||||||
head: CachePadded::new(AtomicUsize::new(head)),
|
|
||||||
tail: CachePadded::new(AtomicUsize::new(tail)),
|
|
||||||
senders: SyncWaker::new(),
|
|
||||||
receivers: SyncWaker::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to reserve a slot for sending a message.
|
|
||||||
fn start_send(&self, token: &mut Token) -> bool {
|
|
||||||
let backoff = Backoff::new();
|
|
||||||
let mut tail = self.tail.load(Ordering::Relaxed);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Check if the channel is disconnected.
|
|
||||||
if tail & self.mark_bit != 0 {
|
|
||||||
token.array.slot = ptr::null();
|
|
||||||
token.array.stamp = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deconstruct the tail.
|
|
||||||
let index = tail & (self.mark_bit - 1);
|
|
||||||
let lap = tail & !(self.one_lap - 1);
|
|
||||||
|
|
||||||
// Inspect the corresponding slot.
|
|
||||||
debug_assert!(index < self.buffer.len());
|
|
||||||
let slot = unsafe { self.buffer.get_unchecked(index) };
|
|
||||||
let stamp = slot.stamp.load(Ordering::Acquire);
|
|
||||||
|
|
||||||
// If the tail and the stamp match, we may attempt to push.
|
|
||||||
if tail == stamp {
|
|
||||||
let new_tail = if index + 1 < self.cap {
|
|
||||||
// Same lap, incremented index.
|
|
||||||
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
|
|
||||||
tail + 1
|
|
||||||
} else {
|
|
||||||
// One lap forward, index wraps around to zero.
|
|
||||||
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
|
|
||||||
lap.wrapping_add(self.one_lap)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Try moving the tail.
|
|
||||||
match self.tail.compare_exchange_weak(
|
|
||||||
tail,
|
|
||||||
new_tail,
|
|
||||||
Ordering::SeqCst,
|
|
||||||
Ordering::Relaxed,
|
|
||||||
) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Prepare the token for the follow-up call to `write`.
|
|
||||||
token.array.slot = slot as *const Slot<T> as *const u8;
|
|
||||||
token.array.stamp = tail + 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
backoff.spin_light();
|
|
||||||
tail = self.tail.load(Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if stamp.wrapping_add(self.one_lap) == tail + 1 {
|
|
||||||
atomic::fence(Ordering::SeqCst);
|
|
||||||
let head = self.head.load(Ordering::Relaxed);
|
|
||||||
|
|
||||||
// If the head lags one lap behind the tail as well...
|
|
||||||
if head.wrapping_add(self.one_lap) == tail {
|
|
||||||
// ...then the channel is full.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
backoff.spin_light();
|
|
||||||
tail = self.tail.load(Ordering::Relaxed);
|
|
||||||
} else {
|
|
||||||
// Snooze because we need to wait for the stamp to get updated.
|
|
||||||
backoff.spin_heavy();
|
|
||||||
tail = self.tail.load(Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes a message into the channel.
|
|
||||||
pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
|
|
||||||
// If there is no slot, the channel is disconnected.
|
|
||||||
if token.array.slot.is_null() {
|
|
||||||
return Err(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the message into the slot and update the stamp.
|
|
||||||
unsafe {
|
|
||||||
let slot: &Slot<T> = &*(token.array.slot as *const Slot<T>);
|
|
||||||
slot.msg.get().write(MaybeUninit::new(msg));
|
|
||||||
slot.stamp.store(token.array.stamp, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wake a sleeping receiver.
|
|
||||||
self.receivers.notify();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to reserve a slot for receiving a message.
|
|
||||||
fn start_recv(&self, token: &mut Token) -> bool {
|
|
||||||
let backoff = Backoff::new();
|
|
||||||
let mut head = self.head.load(Ordering::Relaxed);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Deconstruct the head.
|
|
||||||
let index = head & (self.mark_bit - 1);
|
|
||||||
let lap = head & !(self.one_lap - 1);
|
|
||||||
|
|
||||||
// Inspect the corresponding slot.
|
|
||||||
debug_assert!(index < self.buffer.len());
|
|
||||||
let slot = unsafe { self.buffer.get_unchecked(index) };
|
|
||||||
let stamp = slot.stamp.load(Ordering::Acquire);
|
|
||||||
|
|
||||||
// If the stamp is ahead of the head by 1, we may attempt to pop.
|
|
||||||
if head + 1 == stamp {
|
|
||||||
let new = if index + 1 < self.cap {
|
|
||||||
// Same lap, incremented index.
|
|
||||||
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
|
|
||||||
head + 1
|
|
||||||
} else {
|
|
||||||
// One lap forward, index wraps around to zero.
|
|
||||||
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
|
|
||||||
lap.wrapping_add(self.one_lap)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Try moving the head.
|
|
||||||
match self.head.compare_exchange_weak(
|
|
||||||
head,
|
|
||||||
new,
|
|
||||||
Ordering::SeqCst,
|
|
||||||
Ordering::Relaxed,
|
|
||||||
) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Prepare the token for the follow-up call to `read`.
|
|
||||||
token.array.slot = slot as *const Slot<T> as *const u8;
|
|
||||||
token.array.stamp = head.wrapping_add(self.one_lap);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
backoff.spin_light();
|
|
||||||
head = self.head.load(Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if stamp == head {
|
|
||||||
atomic::fence(Ordering::SeqCst);
|
|
||||||
let tail = self.tail.load(Ordering::Relaxed);
|
|
||||||
|
|
||||||
// If the tail equals the head, that means the channel is empty.
|
|
||||||
if (tail & !self.mark_bit) == head {
|
|
||||||
// If the channel is disconnected...
|
|
||||||
if tail & self.mark_bit != 0 {
|
|
||||||
// ...then receive an error.
|
|
||||||
token.array.slot = ptr::null();
|
|
||||||
token.array.stamp = 0;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// Otherwise, the receive operation is not ready.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
backoff.spin_light();
|
|
||||||
head = self.head.load(Ordering::Relaxed);
|
|
||||||
} else {
|
|
||||||
// Snooze because we need to wait for the stamp to get updated.
|
|
||||||
backoff.spin_heavy();
|
|
||||||
head = self.head.load(Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a message from the channel.
|
|
||||||
pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
|
|
||||||
if token.array.slot.is_null() {
|
|
||||||
// The channel is disconnected.
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the message from the slot and update the stamp.
|
|
||||||
let msg = unsafe {
|
|
||||||
let slot: &Slot<T> = &*(token.array.slot as *const Slot<T>);
|
|
||||||
|
|
||||||
let msg = slot.msg.get().read().assume_init();
|
|
||||||
slot.stamp.store(token.array.stamp, Ordering::Release);
|
|
||||||
msg
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wake a sleeping sender.
|
|
||||||
self.senders.notify();
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to send a message into the channel.
|
|
||||||
pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
if self.start_send(token) {
|
|
||||||
unsafe { self.write(token, msg).map_err(TrySendError::Disconnected) }
|
|
||||||
} else {
|
|
||||||
Err(TrySendError::Full(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a message into the channel.
|
|
||||||
pub(crate) fn send(
|
|
||||||
&self,
|
|
||||||
msg: T,
|
|
||||||
deadline: Option<Instant>,
|
|
||||||
) -> Result<(), SendTimeoutError<T>> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
loop {
|
|
||||||
// Try sending a message.
|
|
||||||
if self.start_send(token) {
|
|
||||||
let res = unsafe { self.write(token, msg) };
|
|
||||||
return res.map_err(SendTimeoutError::Disconnected);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(d) = deadline {
|
|
||||||
if Instant::now() >= d {
|
|
||||||
return Err(SendTimeoutError::Timeout(msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Context::with(|cx| {
|
|
||||||
// Prepare for blocking until a receiver wakes us up.
|
|
||||||
let oper = Operation::hook(token);
|
|
||||||
self.senders.register(oper, cx);
|
|
||||||
|
|
||||||
// Has the channel become ready just now?
|
|
||||||
if !self.is_full() || self.is_disconnected() {
|
|
||||||
let _ = cx.try_select(Selected::Aborted);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block the current thread.
|
|
||||||
// SAFETY: the context belongs to the current thread.
|
|
||||||
let sel = unsafe { cx.wait_until(deadline) };
|
|
||||||
|
|
||||||
match sel {
|
|
||||||
Selected::Waiting => unreachable!(),
|
|
||||||
Selected::Aborted | Selected::Disconnected => {
|
|
||||||
self.senders.unregister(oper).unwrap();
|
|
||||||
}
|
|
||||||
Selected::Operation(_) => {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to receive a message without blocking.
|
|
||||||
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
|
|
||||||
if self.start_recv(token) {
|
|
||||||
unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
|
|
||||||
} else {
|
|
||||||
Err(TryRecvError::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receives a message from the channel.
|
|
||||||
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
loop {
|
|
||||||
// Try receiving a message.
|
|
||||||
if self.start_recv(token) {
|
|
||||||
let res = unsafe { self.read(token) };
|
|
||||||
return res.map_err(|_| RecvTimeoutError::Disconnected);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(d) = deadline {
|
|
||||||
if Instant::now() >= d {
|
|
||||||
return Err(RecvTimeoutError::Timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Context::with(|cx| {
|
|
||||||
// Prepare for blocking until a sender wakes us up.
|
|
||||||
let oper = Operation::hook(token);
|
|
||||||
self.receivers.register(oper, cx);
|
|
||||||
|
|
||||||
// Has the channel become ready just now?
|
|
||||||
if !self.is_empty() || self.is_disconnected() {
|
|
||||||
let _ = cx.try_select(Selected::Aborted);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block the current thread.
|
|
||||||
// SAFETY: the context belongs to the current thread.
|
|
||||||
let sel = unsafe { cx.wait_until(deadline) };
|
|
||||||
|
|
||||||
match sel {
|
|
||||||
Selected::Waiting => unreachable!(),
|
|
||||||
Selected::Aborted | Selected::Disconnected => {
|
|
||||||
self.receivers.unregister(oper).unwrap();
|
|
||||||
// If the channel was disconnected, we still have to check for remaining
|
|
||||||
// messages.
|
|
||||||
}
|
|
||||||
Selected::Operation(_) => {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the current number of messages inside the channel.
|
|
||||||
pub(crate) fn len(&self) -> usize {
|
|
||||||
loop {
|
|
||||||
// Load the tail, then load the head.
|
|
||||||
let tail = self.tail.load(Ordering::SeqCst);
|
|
||||||
let head = self.head.load(Ordering::SeqCst);
|
|
||||||
|
|
||||||
// If the tail didn't change, we've got consistent values to work with.
|
|
||||||
if self.tail.load(Ordering::SeqCst) == tail {
|
|
||||||
let hix = head & (self.mark_bit - 1);
|
|
||||||
let tix = tail & (self.mark_bit - 1);
|
|
||||||
|
|
||||||
return if hix < tix {
|
|
||||||
tix - hix
|
|
||||||
} else if hix > tix {
|
|
||||||
self.cap - hix + tix
|
|
||||||
} else if (tail & !self.mark_bit) == head {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
self.cap
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the capacity of the channel.
|
|
||||||
#[allow(clippy::unnecessary_wraps)] // This is intentional.
|
|
||||||
pub(crate) fn capacity(&self) -> Option<usize> {
|
|
||||||
Some(self.cap)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disconnects senders and wakes up all blocked receivers.
|
|
||||||
///
|
|
||||||
/// Returns `true` if this call disconnected the channel.
|
|
||||||
pub(crate) fn disconnect_senders(&self) -> bool {
|
|
||||||
let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
|
|
||||||
|
|
||||||
if tail & self.mark_bit == 0 {
|
|
||||||
self.receivers.disconnect();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disconnects receivers and wakes up all blocked senders.
|
|
||||||
///
|
|
||||||
/// Returns `true` if this call disconnected the channel.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// May only be called once upon dropping the last receiver. The
|
|
||||||
/// destruction of all other receivers must have been observed with acquire
|
|
||||||
/// ordering or stronger.
|
|
||||||
pub(crate) unsafe fn disconnect_receivers(&self) -> bool {
|
|
||||||
let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
|
|
||||||
let disconnected = if tail & self.mark_bit == 0 {
|
|
||||||
self.senders.disconnect();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe { self.discard_all_messages(tail) };
|
|
||||||
disconnected
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Discards all messages.
|
|
||||||
///
|
|
||||||
/// `tail` should be the current (and therefore last) value of `tail`.
|
|
||||||
///
|
|
||||||
/// # Panicking
|
|
||||||
/// If a destructor panics, the remaining messages are leaked, matching the
|
|
||||||
/// behavior of the unbounded channel.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// This method must only be called when dropping the last receiver. The
|
|
||||||
/// destruction of all other receivers must have been observed with acquire
|
|
||||||
/// ordering or stronger.
|
|
||||||
unsafe fn discard_all_messages(&self, tail: usize) {
|
|
||||||
debug_assert!(self.is_disconnected());
|
|
||||||
|
|
||||||
// Only receivers modify `head`, so since we are the last one,
|
|
||||||
// this value will not change and will not be observed (since
|
|
||||||
// no new messages can be sent after disconnection).
|
|
||||||
let mut head = self.head.load(Ordering::Relaxed);
|
|
||||||
let tail = tail & !self.mark_bit;
|
|
||||||
|
|
||||||
let backoff = Backoff::new();
|
|
||||||
loop {
|
|
||||||
// Deconstruct the head.
|
|
||||||
let index = head & (self.mark_bit - 1);
|
|
||||||
let lap = head & !(self.one_lap - 1);
|
|
||||||
|
|
||||||
// Inspect the corresponding slot.
|
|
||||||
debug_assert!(index < self.buffer.len());
|
|
||||||
let slot = unsafe { self.buffer.get_unchecked(index) };
|
|
||||||
let stamp = slot.stamp.load(Ordering::Acquire);
|
|
||||||
|
|
||||||
// If the stamp is ahead of the head by 1, we may drop the message.
|
|
||||||
if head + 1 == stamp {
|
|
||||||
head = if index + 1 < self.cap {
|
|
||||||
// Same lap, incremented index.
|
|
||||||
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
|
|
||||||
head + 1
|
|
||||||
} else {
|
|
||||||
// One lap forward, index wraps around to zero.
|
|
||||||
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
|
|
||||||
lap.wrapping_add(self.one_lap)
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
(*slot.msg.get()).assume_init_drop();
|
|
||||||
}
|
|
||||||
// If the tail equals the head, that means the channel is empty.
|
|
||||||
} else if tail == head {
|
|
||||||
return;
|
|
||||||
// Otherwise, a sender is about to write into the slot, so we need
|
|
||||||
// to wait for it to update the stamp.
|
|
||||||
} else {
|
|
||||||
backoff.spin_heavy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the channel is disconnected.
|
|
||||||
pub(crate) fn is_disconnected(&self) -> bool {
|
|
||||||
self.tail.load(Ordering::SeqCst) & self.mark_bit != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the channel is empty.
|
|
||||||
pub(crate) fn is_empty(&self) -> bool {
|
|
||||||
let head = self.head.load(Ordering::SeqCst);
|
|
||||||
let tail = self.tail.load(Ordering::SeqCst);
|
|
||||||
|
|
||||||
// Is the tail equal to the head?
|
|
||||||
//
|
|
||||||
// Note: If the head changes just before we load the tail, that means there was a moment
|
|
||||||
// when the channel was not empty, so it is safe to just return `false`.
|
|
||||||
(tail & !self.mark_bit) == head
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the channel is full.
|
|
||||||
pub(crate) fn is_full(&self) -> bool {
|
|
||||||
let tail = self.tail.load(Ordering::SeqCst);
|
|
||||||
let head = self.head.load(Ordering::SeqCst);
|
|
||||||
|
|
||||||
// Is the head lagging one lap behind tail?
|
|
||||||
//
|
|
||||||
// Note: If the tail changes just before we load the head, that means there was a moment
|
|
||||||
// when the channel was not full, so it is safe to just return `false`.
|
|
||||||
head.wrapping_add(self.one_lap) == tail & !self.mark_bit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
//! Thread-local channel context.
|
|
||||||
|
|
||||||
use super::select::Selected;
|
|
||||||
use super::waker::current_thread_id;
|
|
||||||
use crate::cell::Cell;
|
|
||||||
use crate::ptr;
|
|
||||||
use alloc_crate::sync::Arc;
|
|
||||||
use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize, Ordering};
|
|
||||||
use crate::thread::{self, Thread};
|
|
||||||
use crate::time::Instant;
|
|
||||||
|
|
||||||
/// Thread-local context.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Context {
|
|
||||||
inner: Arc<Inner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inner representation of `Context`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Inner {
|
|
||||||
/// Selected operation.
|
|
||||||
select: Atomic<usize>,
|
|
||||||
|
|
||||||
/// A slot into which another thread may store a pointer to its `Packet`.
|
|
||||||
packet: Atomic<*mut ()>,
|
|
||||||
|
|
||||||
/// Thread handle.
|
|
||||||
thread: Thread,
|
|
||||||
|
|
||||||
/// Thread id.
|
|
||||||
thread_id: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
/// Creates a new context for the duration of the closure.
|
|
||||||
#[inline]
|
|
||||||
pub fn with<F, R>(f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&Context) -> R,
|
|
||||||
{
|
|
||||||
thread_local! {
|
|
||||||
/// Cached thread-local context.
|
|
||||||
static CONTEXT: Cell<Option<Context>> = Cell::new(Some(Context::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut f = Some(f);
|
|
||||||
let mut f = |cx: &Context| -> R {
|
|
||||||
let f = f.take().unwrap();
|
|
||||||
f(cx)
|
|
||||||
};
|
|
||||||
|
|
||||||
CONTEXT
|
|
||||||
.try_with(|cell| match cell.take() {
|
|
||||||
None => f(&Context::new()),
|
|
||||||
Some(cx) => {
|
|
||||||
cx.reset();
|
|
||||||
let res = f(&cx);
|
|
||||||
cell.set(Some(cx));
|
|
||||||
res
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|_| f(&Context::new()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Context`.
|
|
||||||
#[cold]
|
|
||||||
fn new() -> Context {
|
|
||||||
Context {
|
|
||||||
inner: Arc::new(Inner {
|
|
||||||
select: AtomicUsize::new(Selected::Waiting.into()),
|
|
||||||
packet: AtomicPtr::new(ptr::null_mut()),
|
|
||||||
thread: thread::current_or_unnamed(),
|
|
||||||
thread_id: current_thread_id(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets `select` and `packet`.
|
|
||||||
#[inline]
|
|
||||||
fn reset(&self) {
|
|
||||||
self.inner.select.store(Selected::Waiting.into(), Ordering::Release);
|
|
||||||
self.inner.packet.store(ptr::null_mut(), Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to select an operation.
|
|
||||||
///
|
|
||||||
/// On failure, the previously selected operation is returned.
|
|
||||||
#[inline]
|
|
||||||
pub fn try_select(&self, select: Selected) -> Result<(), Selected> {
|
|
||||||
self.inner
|
|
||||||
.select
|
|
||||||
.compare_exchange(
|
|
||||||
Selected::Waiting.into(),
|
|
||||||
select.into(),
|
|
||||||
Ordering::AcqRel,
|
|
||||||
Ordering::Acquire,
|
|
||||||
)
|
|
||||||
.map(|_| ())
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores a packet.
|
|
||||||
///
|
|
||||||
/// This method must be called after `try_select` succeeds and there is a packet to provide.
|
|
||||||
#[inline]
|
|
||||||
pub fn store_packet(&self, packet: *mut ()) {
|
|
||||||
if !packet.is_null() {
|
|
||||||
self.inner.packet.store(packet, Ordering::Release);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Waits until an operation is selected and returns it.
|
|
||||||
///
|
|
||||||
/// If the deadline is reached, `Selected::Aborted` will be selected.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// This may only be called from the thread this `Context` belongs to.
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn wait_until(&self, deadline: Option<Instant>) -> Selected {
|
|
||||||
loop {
|
|
||||||
// Check whether an operation has been selected.
|
|
||||||
let sel = Selected::from(self.inner.select.load(Ordering::Acquire));
|
|
||||||
if sel != Selected::Waiting {
|
|
||||||
return sel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's a deadline, park the current thread until the deadline is reached.
|
|
||||||
if let Some(end) = deadline {
|
|
||||||
let now = Instant::now();
|
|
||||||
|
|
||||||
if now < end {
|
|
||||||
// SAFETY: guaranteed by caller.
|
|
||||||
unsafe { self.inner.thread.park_timeout(end - now) };
|
|
||||||
} else {
|
|
||||||
// The deadline has been reached. Try aborting select.
|
|
||||||
return match self.try_select(Selected::Aborted) {
|
|
||||||
Ok(()) => Selected::Aborted,
|
|
||||||
Err(s) => s,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// SAFETY: guaranteed by caller.
|
|
||||||
unsafe { self.inner.thread.park() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unparks the thread this context belongs to.
|
|
||||||
#[inline]
|
|
||||||
pub fn unpark(&self) {
|
|
||||||
self.inner.thread.unpark();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the id of the thread this context belongs to.
|
|
||||||
#[inline]
|
|
||||||
pub fn thread_id(&self) -> usize {
|
|
||||||
self.inner.thread_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering};
|
|
||||||
use crate::{ops, process};
|
|
||||||
|
|
||||||
/// Reference counter internals.
|
|
||||||
struct Counter<C> {
|
|
||||||
/// The number of senders associated with the channel.
|
|
||||||
senders: Atomic<usize>,
|
|
||||||
|
|
||||||
/// The number of receivers associated with the channel.
|
|
||||||
receivers: Atomic<usize>,
|
|
||||||
|
|
||||||
/// Set to `true` if the last sender or the last receiver reference deallocates the channel.
|
|
||||||
destroy: Atomic<bool>,
|
|
||||||
|
|
||||||
/// The internal channel.
|
|
||||||
chan: C,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wraps a channel into the reference counter.
|
|
||||||
pub(crate) fn new<C>(chan: C) -> (Sender<C>, Receiver<C>) {
|
|
||||||
let counter = Box::into_raw(Box::new(Counter {
|
|
||||||
senders: AtomicUsize::new(1),
|
|
||||||
receivers: AtomicUsize::new(1),
|
|
||||||
destroy: AtomicBool::new(false),
|
|
||||||
chan,
|
|
||||||
}));
|
|
||||||
let s = Sender { counter };
|
|
||||||
let r = Receiver { counter };
|
|
||||||
(s, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The sending side.
|
|
||||||
pub(crate) struct Sender<C> {
|
|
||||||
counter: *mut Counter<C>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> Sender<C> {
|
|
||||||
/// Returns the internal `Counter`.
|
|
||||||
fn counter(&self) -> &Counter<C> {
|
|
||||||
unsafe { &*self.counter }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Acquires another sender reference.
|
|
||||||
pub(crate) fn acquire(&self) -> Sender<C> {
|
|
||||||
let count = self.counter().senders.fetch_add(1, Ordering::Relaxed);
|
|
||||||
|
|
||||||
// Cloning senders and calling `mem::forget` on the clones could potentially overflow the
|
|
||||||
// counter. It's very difficult to recover sensibly from such degenerate scenarios so we
|
|
||||||
// just abort when the count becomes very large.
|
|
||||||
if count > isize::MAX as usize {
|
|
||||||
process::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
Sender { counter: self.counter }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Releases the sender reference.
|
|
||||||
///
|
|
||||||
/// Function `disconnect` will be called if this is the last sender reference.
|
|
||||||
pub(crate) unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
|
|
||||||
if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 {
|
|
||||||
disconnect(&self.counter().chan);
|
|
||||||
|
|
||||||
if self.counter().destroy.swap(true, Ordering::AcqRel) {
|
|
||||||
drop(unsafe { Box::from_raw(self.counter) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> ops::Deref for Sender<C> {
|
|
||||||
type Target = C;
|
|
||||||
|
|
||||||
fn deref(&self) -> &C {
|
|
||||||
&self.counter().chan
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> PartialEq for Sender<C> {
|
|
||||||
fn eq(&self, other: &Sender<C>) -> bool {
|
|
||||||
self.counter == other.counter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The receiving side.
|
|
||||||
pub(crate) struct Receiver<C> {
|
|
||||||
counter: *mut Counter<C>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> Receiver<C> {
|
|
||||||
/// Returns the internal `Counter`.
|
|
||||||
fn counter(&self) -> &Counter<C> {
|
|
||||||
unsafe { &*self.counter }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Acquires another receiver reference.
|
|
||||||
pub(crate) fn acquire(&self) -> Receiver<C> {
|
|
||||||
let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed);
|
|
||||||
|
|
||||||
// Cloning receivers and calling `mem::forget` on the clones could potentially overflow the
|
|
||||||
// counter. It's very difficult to recover sensibly from such degenerate scenarios so we
|
|
||||||
// just abort when the count becomes very large.
|
|
||||||
if count > isize::MAX as usize {
|
|
||||||
process::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
Receiver { counter: self.counter }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Releases the receiver reference.
|
|
||||||
///
|
|
||||||
/// Function `disconnect` will be called if this is the last receiver reference.
|
|
||||||
pub(crate) unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
|
|
||||||
if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 {
|
|
||||||
disconnect(&self.counter().chan);
|
|
||||||
|
|
||||||
if self.counter().destroy.swap(true, Ordering::AcqRel) {
|
|
||||||
drop(unsafe { Box::from_raw(self.counter) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> ops::Deref for Receiver<C> {
|
|
||||||
type Target = C;
|
|
||||||
|
|
||||||
fn deref(&self) -> &C {
|
|
||||||
&self.counter().chan
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> PartialEq for Receiver<C> {
|
|
||||||
fn eq(&self, other: &Receiver<C>) -> bool {
|
|
||||||
self.counter == other.counter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
pub use crate::sync::mpsc::{RecvError, RecvTimeoutError, SendError, TryRecvError, TrySendError};
|
|
||||||
use crate::{error, fmt};
|
|
||||||
|
|
||||||
/// An error returned from the [`send_timeout`] method.
|
|
||||||
///
|
|
||||||
/// The error contains the message being sent so it can be recovered.
|
|
||||||
///
|
|
||||||
/// [`send_timeout`]: super::Sender::send_timeout
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
|
||||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
|
||||||
pub enum SendTimeoutError<T> {
|
|
||||||
/// The message could not be sent because the channel is full and the operation timed out.
|
|
||||||
///
|
|
||||||
/// If this is a zero-capacity channel, then the error indicates that there was no receiver
|
|
||||||
/// available to receive the message and the operation timed out.
|
|
||||||
Timeout(T),
|
|
||||||
|
|
||||||
/// The message could not be sent because the channel is disconnected.
|
|
||||||
Disconnected(T),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
|
||||||
impl<T> fmt::Debug for SendTimeoutError<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
"SendTimeoutError(..)".fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
|
||||||
impl<T> fmt::Display for SendTimeoutError<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
SendTimeoutError::Timeout(..) => "timed out waiting on send operation".fmt(f),
|
|
||||||
SendTimeoutError::Disconnected(..) => "sending on a disconnected channel".fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
|
||||||
impl<T> error::Error for SendTimeoutError<T> {}
|
|
||||||
|
|
||||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
|
||||||
impl<T> From<SendError<T>> for SendTimeoutError<T> {
|
|
||||||
fn from(err: SendError<T>) -> SendTimeoutError<T> {
|
|
||||||
match err {
|
|
||||||
SendError(e) => SendTimeoutError::Disconnected(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,668 +0,0 @@
|
|||||||
//! Unbounded channel implemented as a linked list.
|
|
||||||
|
|
||||||
use super::context::Context;
|
|
||||||
use super::error::*;
|
|
||||||
use super::select::{Operation, Selected, Token};
|
|
||||||
use super::utils::{Backoff, CachePadded};
|
|
||||||
use super::waker::SyncWaker;
|
|
||||||
use crate::cell::UnsafeCell;
|
|
||||||
use crate::marker::PhantomData;
|
|
||||||
use crate::mem::MaybeUninit;
|
|
||||||
use crate::ptr;
|
|
||||||
use crate::sync::atomic::{self, Atomic, AtomicPtr, AtomicUsize, Ordering};
|
|
||||||
use crate::time::Instant;
|
|
||||||
|
|
||||||
// Bits indicating the state of a slot:
|
|
||||||
// * If a message has been written into the slot, `WRITE` is set.
|
|
||||||
// * If a message has been read from the slot, `READ` is set.
|
|
||||||
// * If the block is being destroyed, `DESTROY` is set.
|
|
||||||
const WRITE: usize = 1;
|
|
||||||
const READ: usize = 2;
|
|
||||||
const DESTROY: usize = 4;
|
|
||||||
|
|
||||||
// Each block covers one "lap" of indices.
|
|
||||||
const LAP: usize = 32;
|
|
||||||
// The maximum number of messages a block can hold.
|
|
||||||
const BLOCK_CAP: usize = LAP - 1;
|
|
||||||
// How many lower bits are reserved for metadata.
|
|
||||||
const SHIFT: usize = 1;
|
|
||||||
// Has two different purposes:
|
|
||||||
// * If set in head, indicates that the block is not the last one.
|
|
||||||
// * If set in tail, indicates that the channel is disconnected.
|
|
||||||
const MARK_BIT: usize = 1;
|
|
||||||
|
|
||||||
/// A slot in a block.
|
|
||||||
struct Slot<T> {
|
|
||||||
/// The message.
|
|
||||||
msg: UnsafeCell<MaybeUninit<T>>,
|
|
||||||
|
|
||||||
/// The state of the slot.
|
|
||||||
state: Atomic<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Slot<T> {
|
|
||||||
/// Waits until a message is written into the slot.
|
|
||||||
fn wait_write(&self) {
|
|
||||||
let backoff = Backoff::new();
|
|
||||||
while self.state.load(Ordering::Acquire) & WRITE == 0 {
|
|
||||||
backoff.spin_heavy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A block in a linked list.
|
|
||||||
///
|
|
||||||
/// Each block in the list can hold up to `BLOCK_CAP` messages.
|
|
||||||
struct Block<T> {
|
|
||||||
/// The next block in the linked list.
|
|
||||||
next: Atomic<*mut Block<T>>,
|
|
||||||
|
|
||||||
/// Slots for messages.
|
|
||||||
slots: [Slot<T>; BLOCK_CAP],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Block<T> {
|
|
||||||
/// Creates an empty block.
|
|
||||||
fn new() -> Box<Block<T>> {
|
|
||||||
// SAFETY: This is safe because:
|
|
||||||
// [1] `Block::next` (Atomic<*mut _>) may be safely zero initialized.
|
|
||||||
// [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4].
|
|
||||||
// [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it
|
|
||||||
// holds a MaybeUninit.
|
|
||||||
// [4] `Slot::state` (Atomic<usize>) may be safely zero initialized.
|
|
||||||
unsafe { Box::new_zeroed().assume_init() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Waits until the next pointer is set.
|
|
||||||
fn wait_next(&self) -> *mut Block<T> {
|
|
||||||
let backoff = Backoff::new();
|
|
||||||
loop {
|
|
||||||
let next = self.next.load(Ordering::Acquire);
|
|
||||||
if !next.is_null() {
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
backoff.spin_heavy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the `DESTROY` bit in slots starting from `start` and destroys the block.
|
|
||||||
unsafe fn destroy(this: *mut Block<T>, start: usize) {
|
|
||||||
// It is not necessary to set the `DESTROY` bit in the last slot because that slot has
|
|
||||||
// begun destruction of the block.
|
|
||||||
for i in start..BLOCK_CAP - 1 {
|
|
||||||
let slot = unsafe { (*this).slots.get_unchecked(i) };
|
|
||||||
|
|
||||||
// Mark the `DESTROY` bit if a thread is still using the slot.
|
|
||||||
if slot.state.load(Ordering::Acquire) & READ == 0
|
|
||||||
&& slot.state.fetch_or(DESTROY, Ordering::AcqRel) & READ == 0
|
|
||||||
{
|
|
||||||
// If a thread is still using the slot, it will continue destruction of the block.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No thread is using the block, now it is safe to destroy it.
|
|
||||||
drop(unsafe { Box::from_raw(this) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A position in a channel.
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Position<T> {
|
|
||||||
/// The index in the channel.
|
|
||||||
index: Atomic<usize>,
|
|
||||||
|
|
||||||
/// The block in the linked list.
|
|
||||||
block: Atomic<*mut Block<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The token type for the list flavor.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct ListToken {
|
|
||||||
/// The block of slots.
|
|
||||||
block: *const u8,
|
|
||||||
|
|
||||||
/// The offset into the block.
|
|
||||||
offset: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ListToken {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
ListToken { block: ptr::null(), offset: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unbounded channel implemented as a linked list.
|
|
||||||
///
|
|
||||||
/// Each message sent into the channel is assigned a sequence number, i.e. an index. Indices are
|
|
||||||
/// represented as numbers of type `usize` and wrap on overflow.
|
|
||||||
///
|
|
||||||
/// Consecutive messages are grouped into blocks in order to put less pressure on the allocator and
|
|
||||||
/// improve cache efficiency.
|
|
||||||
pub(crate) struct Channel<T> {
|
|
||||||
/// The head of the channel.
|
|
||||||
head: CachePadded<Position<T>>,
|
|
||||||
|
|
||||||
/// The tail of the channel.
|
|
||||||
tail: CachePadded<Position<T>>,
|
|
||||||
|
|
||||||
/// Receivers waiting while the channel is empty and not disconnected.
|
|
||||||
receivers: SyncWaker,
|
|
||||||
|
|
||||||
/// Indicates that dropping a `Channel<T>` may drop messages of type `T`.
|
|
||||||
_marker: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Channel<T> {
|
|
||||||
/// Creates a new unbounded channel.
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
Channel {
|
|
||||||
head: CachePadded::new(Position {
|
|
||||||
block: AtomicPtr::new(ptr::null_mut()),
|
|
||||||
index: AtomicUsize::new(0),
|
|
||||||
}),
|
|
||||||
tail: CachePadded::new(Position {
|
|
||||||
block: AtomicPtr::new(ptr::null_mut()),
|
|
||||||
index: AtomicUsize::new(0),
|
|
||||||
}),
|
|
||||||
receivers: SyncWaker::new(),
|
|
||||||
_marker: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to reserve a slot for sending a message.
|
|
||||||
fn start_send(&self, token: &mut Token) -> bool {
|
|
||||||
let backoff = Backoff::new();
|
|
||||||
let mut tail = self.tail.index.load(Ordering::Acquire);
|
|
||||||
let mut block = self.tail.block.load(Ordering::Acquire);
|
|
||||||
let mut next_block = None;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Check if the channel is disconnected.
|
|
||||||
if tail & MARK_BIT != 0 {
|
|
||||||
token.list.block = ptr::null();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the offset of the index into the block.
|
|
||||||
let offset = (tail >> SHIFT) % LAP;
|
|
||||||
|
|
||||||
// If we reached the end of the block, wait until the next one is installed.
|
|
||||||
if offset == BLOCK_CAP {
|
|
||||||
backoff.spin_heavy();
|
|
||||||
tail = self.tail.index.load(Ordering::Acquire);
|
|
||||||
block = self.tail.block.load(Ordering::Acquire);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're going to have to install the next block, allocate it in advance in order to
|
|
||||||
// make the wait for other threads as short as possible.
|
|
||||||
if offset + 1 == BLOCK_CAP && next_block.is_none() {
|
|
||||||
next_block = Some(Block::<T>::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is the first message to be sent into the channel, we need to allocate the
|
|
||||||
// first block and install it.
|
|
||||||
if block.is_null() {
|
|
||||||
let new = Box::into_raw(Block::<T>::new());
|
|
||||||
|
|
||||||
if self
|
|
||||||
.tail
|
|
||||||
.block
|
|
||||||
.compare_exchange(block, new, Ordering::Release, Ordering::Relaxed)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
// This yield point leaves the channel in a half-initialized state where the
|
|
||||||
// tail.block pointer is set but the head.block is not. This is used to
|
|
||||||
// facilitate the test in src/tools/miri/tests/pass/issues/issue-139553.rs
|
|
||||||
#[cfg(miri)]
|
|
||||||
crate::thread::yield_now();
|
|
||||||
self.head.block.store(new, Ordering::Release);
|
|
||||||
block = new;
|
|
||||||
} else {
|
|
||||||
next_block = unsafe { Some(Box::from_raw(new)) };
|
|
||||||
tail = self.tail.index.load(Ordering::Acquire);
|
|
||||||
block = self.tail.block.load(Ordering::Acquire);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_tail = tail + (1 << SHIFT);
|
|
||||||
|
|
||||||
// Try advancing the tail forward.
|
|
||||||
match self.tail.index.compare_exchange_weak(
|
|
||||||
tail,
|
|
||||||
new_tail,
|
|
||||||
Ordering::SeqCst,
|
|
||||||
Ordering::Acquire,
|
|
||||||
) {
|
|
||||||
Ok(_) => unsafe {
|
|
||||||
// If we've reached the end of the block, install the next one.
|
|
||||||
if offset + 1 == BLOCK_CAP {
|
|
||||||
let next_block = Box::into_raw(next_block.unwrap());
|
|
||||||
self.tail.block.store(next_block, Ordering::Release);
|
|
||||||
self.tail.index.fetch_add(1 << SHIFT, Ordering::Release);
|
|
||||||
(*block).next.store(next_block, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
token.list.block = block as *const u8;
|
|
||||||
token.list.offset = offset;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
Err(_) => {
|
|
||||||
backoff.spin_light();
|
|
||||||
tail = self.tail.index.load(Ordering::Acquire);
|
|
||||||
block = self.tail.block.load(Ordering::Acquire);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes a message into the channel.
|
|
||||||
pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
|
|
||||||
// If there is no slot, the channel is disconnected.
|
|
||||||
if token.list.block.is_null() {
|
|
||||||
return Err(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the message into the slot.
|
|
||||||
let block = token.list.block as *mut Block<T>;
|
|
||||||
let offset = token.list.offset;
|
|
||||||
unsafe {
|
|
||||||
let slot = (*block).slots.get_unchecked(offset);
|
|
||||||
slot.msg.get().write(MaybeUninit::new(msg));
|
|
||||||
slot.state.fetch_or(WRITE, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wake a sleeping receiver.
|
|
||||||
self.receivers.notify();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to reserve a slot for receiving a message.
|
|
||||||
fn start_recv(&self, token: &mut Token) -> bool {
|
|
||||||
let backoff = Backoff::new();
|
|
||||||
let mut head = self.head.index.load(Ordering::Acquire);
|
|
||||||
let mut block = self.head.block.load(Ordering::Acquire);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Calculate the offset of the index into the block.
|
|
||||||
let offset = (head >> SHIFT) % LAP;
|
|
||||||
|
|
||||||
// If we reached the end of the block, wait until the next one is installed.
|
|
||||||
if offset == BLOCK_CAP {
|
|
||||||
backoff.spin_heavy();
|
|
||||||
head = self.head.index.load(Ordering::Acquire);
|
|
||||||
block = self.head.block.load(Ordering::Acquire);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_head = head + (1 << SHIFT);
|
|
||||||
|
|
||||||
if new_head & MARK_BIT == 0 {
|
|
||||||
atomic::fence(Ordering::SeqCst);
|
|
||||||
let tail = self.tail.index.load(Ordering::Relaxed);
|
|
||||||
|
|
||||||
// If the tail equals the head, that means the channel is empty.
|
|
||||||
if head >> SHIFT == tail >> SHIFT {
|
|
||||||
// If the channel is disconnected...
|
|
||||||
if tail & MARK_BIT != 0 {
|
|
||||||
// ...then receive an error.
|
|
||||||
token.list.block = ptr::null();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// Otherwise, the receive operation is not ready.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If head and tail are not in the same block, set `MARK_BIT` in head.
|
|
||||||
if (head >> SHIFT) / LAP != (tail >> SHIFT) / LAP {
|
|
||||||
new_head |= MARK_BIT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The block can be null here only if the first message is being sent into the channel.
|
|
||||||
// In that case, just wait until it gets initialized.
|
|
||||||
if block.is_null() {
|
|
||||||
backoff.spin_heavy();
|
|
||||||
head = self.head.index.load(Ordering::Acquire);
|
|
||||||
block = self.head.block.load(Ordering::Acquire);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try moving the head index forward.
|
|
||||||
match self.head.index.compare_exchange_weak(
|
|
||||||
head,
|
|
||||||
new_head,
|
|
||||||
Ordering::SeqCst,
|
|
||||||
Ordering::Acquire,
|
|
||||||
) {
|
|
||||||
Ok(_) => unsafe {
|
|
||||||
// If we've reached the end of the block, move to the next one.
|
|
||||||
if offset + 1 == BLOCK_CAP {
|
|
||||||
let next = (*block).wait_next();
|
|
||||||
let mut next_index = (new_head & !MARK_BIT).wrapping_add(1 << SHIFT);
|
|
||||||
if !(*next).next.load(Ordering::Relaxed).is_null() {
|
|
||||||
next_index |= MARK_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.head.block.store(next, Ordering::Release);
|
|
||||||
self.head.index.store(next_index, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
token.list.block = block as *const u8;
|
|
||||||
token.list.offset = offset;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
Err(_) => {
|
|
||||||
backoff.spin_light();
|
|
||||||
head = self.head.index.load(Ordering::Acquire);
|
|
||||||
block = self.head.block.load(Ordering::Acquire);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a message from the channel.
|
|
||||||
pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
|
|
||||||
if token.list.block.is_null() {
|
|
||||||
// The channel is disconnected.
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the message.
|
|
||||||
let block = token.list.block as *mut Block<T>;
|
|
||||||
let offset = token.list.offset;
|
|
||||||
unsafe {
|
|
||||||
let slot = (*block).slots.get_unchecked(offset);
|
|
||||||
slot.wait_write();
|
|
||||||
let msg = slot.msg.get().read().assume_init();
|
|
||||||
|
|
||||||
// Destroy the block if we've reached the end, or if another thread wanted to destroy but
|
|
||||||
// couldn't because we were busy reading from the slot.
|
|
||||||
if offset + 1 == BLOCK_CAP {
|
|
||||||
Block::destroy(block, 0);
|
|
||||||
} else if slot.state.fetch_or(READ, Ordering::AcqRel) & DESTROY != 0 {
|
|
||||||
Block::destroy(block, offset + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to send a message into the channel.
|
|
||||||
pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
|
|
||||||
self.send(msg, None).map_err(|err| match err {
|
|
||||||
SendTimeoutError::Disconnected(msg) => TrySendError::Disconnected(msg),
|
|
||||||
SendTimeoutError::Timeout(_) => unreachable!(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a message into the channel.
|
|
||||||
pub(crate) fn send(
|
|
||||||
&self,
|
|
||||||
msg: T,
|
|
||||||
_deadline: Option<Instant>,
|
|
||||||
) -> Result<(), SendTimeoutError<T>> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
assert!(self.start_send(token));
|
|
||||||
unsafe { self.write(token, msg).map_err(SendTimeoutError::Disconnected) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to receive a message without blocking.
|
|
||||||
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
|
|
||||||
if self.start_recv(token) {
|
|
||||||
unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
|
|
||||||
} else {
|
|
||||||
Err(TryRecvError::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receives a message from the channel.
|
|
||||||
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
loop {
|
|
||||||
if self.start_recv(token) {
|
|
||||||
unsafe {
|
|
||||||
return self.read(token).map_err(|_| RecvTimeoutError::Disconnected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(d) = deadline {
|
|
||||||
if Instant::now() >= d {
|
|
||||||
return Err(RecvTimeoutError::Timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare for blocking until a sender wakes us up.
|
|
||||||
Context::with(|cx| {
|
|
||||||
let oper = Operation::hook(token);
|
|
||||||
self.receivers.register(oper, cx);
|
|
||||||
|
|
||||||
// Has the channel become ready just now?
|
|
||||||
if !self.is_empty() || self.is_disconnected() {
|
|
||||||
let _ = cx.try_select(Selected::Aborted);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block the current thread.
|
|
||||||
// SAFETY: the context belongs to the current thread.
|
|
||||||
let sel = unsafe { cx.wait_until(deadline) };
|
|
||||||
|
|
||||||
match sel {
|
|
||||||
Selected::Waiting => unreachable!(),
|
|
||||||
Selected::Aborted | Selected::Disconnected => {
|
|
||||||
self.receivers.unregister(oper).unwrap();
|
|
||||||
// If the channel was disconnected, we still have to check for remaining
|
|
||||||
// messages.
|
|
||||||
}
|
|
||||||
Selected::Operation(_) => {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the current number of messages inside the channel.
|
|
||||||
pub(crate) fn len(&self) -> usize {
|
|
||||||
loop {
|
|
||||||
// Load the tail index, then load the head index.
|
|
||||||
let mut tail = self.tail.index.load(Ordering::SeqCst);
|
|
||||||
let mut head = self.head.index.load(Ordering::SeqCst);
|
|
||||||
|
|
||||||
// If the tail index didn't change, we've got consistent indices to work with.
|
|
||||||
if self.tail.index.load(Ordering::SeqCst) == tail {
|
|
||||||
// Erase the lower bits.
|
|
||||||
tail &= !((1 << SHIFT) - 1);
|
|
||||||
head &= !((1 << SHIFT) - 1);
|
|
||||||
|
|
||||||
// Fix up indices if they fall onto block ends.
|
|
||||||
if (tail >> SHIFT) & (LAP - 1) == LAP - 1 {
|
|
||||||
tail = tail.wrapping_add(1 << SHIFT);
|
|
||||||
}
|
|
||||||
if (head >> SHIFT) & (LAP - 1) == LAP - 1 {
|
|
||||||
head = head.wrapping_add(1 << SHIFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotate indices so that head falls into the first block.
|
|
||||||
let lap = (head >> SHIFT) / LAP;
|
|
||||||
tail = tail.wrapping_sub((lap * LAP) << SHIFT);
|
|
||||||
head = head.wrapping_sub((lap * LAP) << SHIFT);
|
|
||||||
|
|
||||||
// Remove the lower bits.
|
|
||||||
tail >>= SHIFT;
|
|
||||||
head >>= SHIFT;
|
|
||||||
|
|
||||||
// Return the difference minus the number of blocks between tail and head.
|
|
||||||
return tail - head - tail / LAP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the capacity of the channel.
|
|
||||||
pub(crate) fn capacity(&self) -> Option<usize> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disconnects senders and wakes up all blocked receivers.
|
|
||||||
///
|
|
||||||
/// Returns `true` if this call disconnected the channel.
|
|
||||||
pub(crate) fn disconnect_senders(&self) -> bool {
|
|
||||||
let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst);
|
|
||||||
|
|
||||||
if tail & MARK_BIT == 0 {
|
|
||||||
self.receivers.disconnect();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disconnects receivers.
|
|
||||||
///
|
|
||||||
/// Returns `true` if this call disconnected the channel.
|
|
||||||
pub(crate) fn disconnect_receivers(&self) -> bool {
|
|
||||||
let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst);
|
|
||||||
|
|
||||||
if tail & MARK_BIT == 0 {
|
|
||||||
// If receivers are dropped first, discard all messages to free
|
|
||||||
// memory eagerly.
|
|
||||||
self.discard_all_messages();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Discards all messages.
|
|
||||||
///
|
|
||||||
/// This method should only be called when all receivers are dropped.
|
|
||||||
fn discard_all_messages(&self) {
|
|
||||||
let backoff = Backoff::new();
|
|
||||||
let mut tail = self.tail.index.load(Ordering::Acquire);
|
|
||||||
loop {
|
|
||||||
let offset = (tail >> SHIFT) % LAP;
|
|
||||||
if offset != BLOCK_CAP {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// New updates to tail will be rejected by MARK_BIT and aborted unless it's
|
|
||||||
// at boundary. We need to wait for the updates take affect otherwise there
|
|
||||||
// can be memory leaks.
|
|
||||||
backoff.spin_heavy();
|
|
||||||
tail = self.tail.index.load(Ordering::Acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut head = self.head.index.load(Ordering::Acquire);
|
|
||||||
// The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts
|
|
||||||
// to initialize the first block before noticing that the receivers disconnected. Late allocations
|
|
||||||
// will be deallocated by the sender in Drop.
|
|
||||||
let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel);
|
|
||||||
|
|
||||||
// If we're going to be dropping messages we need to synchronize with initialization
|
|
||||||
if head >> SHIFT != tail >> SHIFT {
|
|
||||||
// The block can be null here only if a sender is in the process of initializing the
|
|
||||||
// channel while another sender managed to send a message by inserting it into the
|
|
||||||
// semi-initialized channel and advanced the tail.
|
|
||||||
// In that case, just wait until it gets initialized.
|
|
||||||
while block.is_null() {
|
|
||||||
backoff.spin_heavy();
|
|
||||||
block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// After this point `head.block` is not modified again and it will be deallocated if it's
|
|
||||||
// non-null. The `Drop` code of the channel, which runs after this function, also attempts
|
|
||||||
// to deallocate `head.block` if it's non-null. Therefore this function must maintain the
|
|
||||||
// invariant that if a deallocation of head.block is attempted then it must also be set to
|
|
||||||
// NULL. Failing to do so will lead to the Drop code attempting a double free. For this
|
|
||||||
// reason both reads above do an atomic swap instead of a simple atomic load.
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// Drop all messages between head and tail and deallocate the heap-allocated blocks.
|
|
||||||
while head >> SHIFT != tail >> SHIFT {
|
|
||||||
let offset = (head >> SHIFT) % LAP;
|
|
||||||
|
|
||||||
if offset < BLOCK_CAP {
|
|
||||||
// Drop the message in the slot.
|
|
||||||
let slot = (*block).slots.get_unchecked(offset);
|
|
||||||
slot.wait_write();
|
|
||||||
let p = &mut *slot.msg.get();
|
|
||||||
p.as_mut_ptr().drop_in_place();
|
|
||||||
} else {
|
|
||||||
(*block).wait_next();
|
|
||||||
// Deallocate the block and move to the next one.
|
|
||||||
let next = (*block).next.load(Ordering::Acquire);
|
|
||||||
drop(Box::from_raw(block));
|
|
||||||
block = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
head = head.wrapping_add(1 << SHIFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deallocate the last remaining block.
|
|
||||||
if !block.is_null() {
|
|
||||||
drop(Box::from_raw(block));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
head &= !MARK_BIT;
|
|
||||||
self.head.index.store(head, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the channel is disconnected.
|
|
||||||
pub(crate) fn is_disconnected(&self) -> bool {
|
|
||||||
self.tail.index.load(Ordering::SeqCst) & MARK_BIT != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the channel is empty.
|
|
||||||
pub(crate) fn is_empty(&self) -> bool {
|
|
||||||
let head = self.head.index.load(Ordering::SeqCst);
|
|
||||||
let tail = self.tail.index.load(Ordering::SeqCst);
|
|
||||||
head >> SHIFT == tail >> SHIFT
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the channel is full.
|
|
||||||
pub(crate) fn is_full(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Drop for Channel<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let mut head = self.head.index.load(Ordering::Relaxed);
|
|
||||||
let mut tail = self.tail.index.load(Ordering::Relaxed);
|
|
||||||
let mut block = self.head.block.load(Ordering::Relaxed);
|
|
||||||
|
|
||||||
// Erase the lower bits.
|
|
||||||
head &= !((1 << SHIFT) - 1);
|
|
||||||
tail &= !((1 << SHIFT) - 1);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// Drop all messages between head and tail and deallocate the heap-allocated blocks.
|
|
||||||
while head != tail {
|
|
||||||
let offset = (head >> SHIFT) % LAP;
|
|
||||||
|
|
||||||
if offset < BLOCK_CAP {
|
|
||||||
// Drop the message in the slot.
|
|
||||||
let slot = (*block).slots.get_unchecked(offset);
|
|
||||||
let p = &mut *slot.msg.get();
|
|
||||||
p.as_mut_ptr().drop_in_place();
|
|
||||||
} else {
|
|
||||||
// Deallocate the block and move to the next one.
|
|
||||||
let next = (*block).next.load(Ordering::Relaxed);
|
|
||||||
drop(Box::from_raw(block));
|
|
||||||
block = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
head = head.wrapping_add(1 << SHIFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deallocate the last remaining block.
|
|
||||||
if !block.is_null() {
|
|
||||||
drop(Box::from_raw(block));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,71 +0,0 @@
|
|||||||
/// Temporary data that gets initialized during a blocking operation, and is consumed by
|
|
||||||
/// `read` or `write`.
|
|
||||||
///
|
|
||||||
/// Each field contains data associated with a specific channel flavor.
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct Token {
|
|
||||||
pub(crate) array: super::array::ArrayToken,
|
|
||||||
pub(crate) list: super::list::ListToken,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) zero: super::zero::ZeroToken,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Identifier associated with an operation by a specific thread on a specific channel.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct Operation(usize);
|
|
||||||
|
|
||||||
impl Operation {
|
|
||||||
/// Creates an operation identifier from a mutable reference.
|
|
||||||
///
|
|
||||||
/// This function essentially just turns the address of the reference into a number. The
|
|
||||||
/// reference should point to a variable that is specific to the thread and the operation,
|
|
||||||
/// and is alive for the entire duration of a blocking operation.
|
|
||||||
#[inline]
|
|
||||||
pub fn hook<T>(r: &mut T) -> Operation {
|
|
||||||
let val = r as *mut T as usize;
|
|
||||||
// Make sure that the pointer address doesn't equal the numerical representation of
|
|
||||||
// `Selected::{Waiting, Aborted, Disconnected}`.
|
|
||||||
assert!(val > 2);
|
|
||||||
Operation(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Current state of a blocking operation.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Selected {
|
|
||||||
/// Still waiting for an operation.
|
|
||||||
Waiting,
|
|
||||||
|
|
||||||
/// The attempt to block the current thread has been aborted.
|
|
||||||
Aborted,
|
|
||||||
|
|
||||||
/// An operation became ready because a channel is disconnected.
|
|
||||||
Disconnected,
|
|
||||||
|
|
||||||
/// An operation became ready because a message can be sent or received.
|
|
||||||
Operation(Operation),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<usize> for Selected {
|
|
||||||
#[inline]
|
|
||||||
fn from(val: usize) -> Selected {
|
|
||||||
match val {
|
|
||||||
0 => Selected::Waiting,
|
|
||||||
1 => Selected::Aborted,
|
|
||||||
2 => Selected::Disconnected,
|
|
||||||
oper => Selected::Operation(Operation(oper)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<usize> for Selected {
|
|
||||||
#[inline]
|
|
||||||
fn into(self) -> usize {
|
|
||||||
match self {
|
|
||||||
Selected::Waiting => 0,
|
|
||||||
Selected::Aborted => 1,
|
|
||||||
Selected::Disconnected => 2,
|
|
||||||
Selected::Operation(Operation(val)) => val,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// Ensure that thread_local init with `const { 0 }` still has unique address at run-time
|
|
||||||
#[test]
|
|
||||||
fn waker_current_thread_id() {
|
|
||||||
let first = super::waker::current_thread_id();
|
|
||||||
let t = crate::thread::spawn(move || {
|
|
||||||
let second = super::waker::current_thread_id();
|
|
||||||
assert_ne!(first, second);
|
|
||||||
assert_eq!(second, super::waker::current_thread_id());
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(first, super::waker::current_thread_id());
|
|
||||||
t.join().unwrap();
|
|
||||||
assert_eq!(first, super::waker::current_thread_id());
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
use crate::cell::Cell;
|
|
||||||
use crate::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
/// Pads and aligns a value to the length of a cache line.
|
|
||||||
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
|
|
||||||
// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache
|
|
||||||
// lines at a time, so we have to align to 128 bytes rather than 64.
|
|
||||||
//
|
|
||||||
// Sources:
|
|
||||||
// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
|
|
||||||
// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107
|
|
||||||
//
|
|
||||||
// ARM's big.LITTLE architecture has asymmetric cores and "big" cores have 128-byte cache line size.
|
|
||||||
//
|
|
||||||
// Sources:
|
|
||||||
// - https://www.mono-project.com/news/2016/09/12/arm64-icache/
|
|
||||||
//
|
|
||||||
// powerpc64 has 128-byte cache line size.
|
|
||||||
//
|
|
||||||
// Sources:
|
|
||||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9
|
|
||||||
#[cfg_attr(
|
|
||||||
any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64",),
|
|
||||||
repr(align(128))
|
|
||||||
)]
|
|
||||||
// arm, mips and mips64 have 32-byte cache line size.
|
|
||||||
//
|
|
||||||
// Sources:
|
|
||||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7
|
|
||||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7
|
|
||||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7
|
|
||||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9
|
|
||||||
#[cfg_attr(
|
|
||||||
any(
|
|
||||||
target_arch = "arm",
|
|
||||||
target_arch = "mips",
|
|
||||||
target_arch = "mips32r6",
|
|
||||||
target_arch = "mips64",
|
|
||||||
target_arch = "mips64r6",
|
|
||||||
),
|
|
||||||
repr(align(32))
|
|
||||||
)]
|
|
||||||
// s390x has 256-byte cache line size.
|
|
||||||
//
|
|
||||||
// Sources:
|
|
||||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7
|
|
||||||
#[cfg_attr(target_arch = "s390x", repr(align(256)))]
|
|
||||||
// x86, wasm and riscv have 64-byte cache line size.
|
|
||||||
//
|
|
||||||
// Sources:
|
|
||||||
// - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9
|
|
||||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7
|
|
||||||
// - https://github.com/golang/go/blob/5e31f78c8a4ed1b872ddc194f0cd1ae931b37d7e/src/internal/cpu/cpu_riscv64.go#L7
|
|
||||||
//
|
|
||||||
// All others are assumed to have 64-byte cache line size.
|
|
||||||
#[cfg_attr(
|
|
||||||
not(any(
|
|
||||||
target_arch = "x86_64",
|
|
||||||
target_arch = "aarch64",
|
|
||||||
target_arch = "powerpc64",
|
|
||||||
target_arch = "arm",
|
|
||||||
target_arch = "mips",
|
|
||||||
target_arch = "mips32r6",
|
|
||||||
target_arch = "mips64",
|
|
||||||
target_arch = "mips64r6",
|
|
||||||
target_arch = "s390x",
|
|
||||||
)),
|
|
||||||
repr(align(64))
|
|
||||||
)]
|
|
||||||
pub struct CachePadded<T> {
|
|
||||||
value: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> CachePadded<T> {
|
|
||||||
/// Pads and aligns a value to the length of a cache line.
|
|
||||||
pub fn new(value: T) -> CachePadded<T> {
|
|
||||||
CachePadded::<T> { value }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deref for CachePadded<T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
&self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> DerefMut for CachePadded<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SPIN_LIMIT: u32 = 6;
|
|
||||||
|
|
||||||
/// Performs quadratic backoff in spin loops.
|
|
||||||
pub struct Backoff {
|
|
||||||
step: Cell<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Backoff {
|
|
||||||
/// Creates a new `Backoff`.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Backoff { step: Cell::new(0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Backs off using lightweight spinning.
|
|
||||||
///
|
|
||||||
/// This method should be used for retrying an operation because another thread made
|
|
||||||
/// progress. i.e. on CAS failure.
|
|
||||||
#[inline]
|
|
||||||
pub fn spin_light(&self) {
|
|
||||||
let step = self.step.get().min(SPIN_LIMIT);
|
|
||||||
for _ in 0..step.pow(2) {
|
|
||||||
crate::hint::spin_loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.step.set(self.step.get() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Backs off using heavyweight spinning.
|
|
||||||
///
|
|
||||||
/// This method should be used in blocking loops where parking the thread is not an option.
|
|
||||||
#[inline]
|
|
||||||
pub fn spin_heavy(&self) {
|
|
||||||
if self.step.get() <= SPIN_LIMIT {
|
|
||||||
for _ in 0..self.step.get().pow(2) {
|
|
||||||
crate::hint::spin_loop()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
crate::thread::yield_now();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.step.set(self.step.get() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
//! Waking mechanism for threads blocked on channel operations.
|
|
||||||
|
|
||||||
use super::context::Context;
|
|
||||||
use super::select::{Operation, Selected};
|
|
||||||
use crate::ptr;
|
|
||||||
use crate::sync::Mutex;
|
|
||||||
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
|
|
||||||
|
|
||||||
/// Represents a thread blocked on a specific channel operation.
|
|
||||||
pub(crate) struct Entry {
|
|
||||||
/// The operation.
|
|
||||||
pub(crate) oper: Operation,
|
|
||||||
|
|
||||||
/// Optional packet.
|
|
||||||
pub(crate) packet: *mut (),
|
|
||||||
|
|
||||||
/// Context associated with the thread owning this operation.
|
|
||||||
pub(crate) cx: Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A queue of threads blocked on channel operations.
|
|
||||||
///
|
|
||||||
/// This data structure is used by threads to register blocking operations and get woken up once
|
|
||||||
/// an operation becomes ready.
|
|
||||||
pub(crate) struct Waker {
|
|
||||||
/// A list of select operations.
|
|
||||||
selectors: Vec<Entry>,
|
|
||||||
|
|
||||||
/// A list of operations waiting to be ready.
|
|
||||||
observers: Vec<Entry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Waker {
|
|
||||||
/// Creates a new `Waker`.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
Waker { selectors: Vec::new(), observers: Vec::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registers a select operation.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn register(&mut self, oper: Operation, cx: &Context) {
|
|
||||||
self.register_with_packet(oper, ptr::null_mut(), cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registers a select operation and a packet.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn register_with_packet(&mut self, oper: Operation, packet: *mut (), cx: &Context) {
|
|
||||||
self.selectors.push(Entry { oper, packet, cx: cx.clone() });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unregisters a select operation.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn unregister(&mut self, oper: Operation) -> Option<Entry> {
|
|
||||||
if let Some((i, _)) =
|
|
||||||
self.selectors.iter().enumerate().find(|&(_, entry)| entry.oper == oper)
|
|
||||||
{
|
|
||||||
let entry = self.selectors.remove(i);
|
|
||||||
Some(entry)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to find another thread's entry, select the operation, and wake it up.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn try_select(&mut self) -> Option<Entry> {
|
|
||||||
if self.selectors.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let thread_id = current_thread_id();
|
|
||||||
|
|
||||||
self.selectors
|
|
||||||
.iter()
|
|
||||||
.position(|selector| {
|
|
||||||
// Does the entry belong to a different thread?
|
|
||||||
selector.cx.thread_id() != thread_id
|
|
||||||
&& selector // Try selecting this operation.
|
|
||||||
.cx
|
|
||||||
.try_select(Selected::Operation(selector.oper))
|
|
||||||
.is_ok()
|
|
||||||
&& {
|
|
||||||
// Provide the packet.
|
|
||||||
selector.cx.store_packet(selector.packet);
|
|
||||||
// Wake the thread up.
|
|
||||||
selector.cx.unpark();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// Remove the entry from the queue to keep it clean and improve
|
|
||||||
// performance.
|
|
||||||
.map(|pos| self.selectors.remove(pos))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notifies all operations waiting to be ready.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn notify(&mut self) {
|
|
||||||
for entry in self.observers.drain(..) {
|
|
||||||
if entry.cx.try_select(Selected::Operation(entry.oper)).is_ok() {
|
|
||||||
entry.cx.unpark();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notifies all registered operations that the channel is disconnected.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn disconnect(&mut self) {
|
|
||||||
for entry in self.selectors.iter() {
|
|
||||||
if entry.cx.try_select(Selected::Disconnected).is_ok() {
|
|
||||||
// Wake the thread up.
|
|
||||||
//
|
|
||||||
// Here we don't remove the entry from the queue. Registered threads must
|
|
||||||
// unregister from the waker by themselves. They might also want to recover the
|
|
||||||
// packet value and destroy it, if necessary.
|
|
||||||
entry.cx.unpark();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Waker {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
debug_assert_eq!(self.selectors.len(), 0);
|
|
||||||
debug_assert_eq!(self.observers.len(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A waker that can be shared among threads without locking.
|
|
||||||
///
|
|
||||||
/// This is a simple wrapper around `Waker` that internally uses a mutex for synchronization.
|
|
||||||
pub(crate) struct SyncWaker {
|
|
||||||
/// The inner `Waker`.
|
|
||||||
inner: Mutex<Waker>,
|
|
||||||
|
|
||||||
/// `true` if the waker is empty.
|
|
||||||
is_empty: Atomic<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SyncWaker {
|
|
||||||
/// Creates a new `SyncWaker`.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
SyncWaker { inner: Mutex::new(Waker::new()), is_empty: AtomicBool::new(true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registers the current thread with an operation.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn register(&self, oper: Operation, cx: &Context) {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
inner.register(oper, cx);
|
|
||||||
self.is_empty
|
|
||||||
.store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unregisters an operation previously registered by the current thread.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn unregister(&self, oper: Operation) -> Option<Entry> {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
let entry = inner.unregister(oper);
|
|
||||||
self.is_empty
|
|
||||||
.store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
|
|
||||||
entry
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to find one thread (not the current one), select its operation, and wake it up.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn notify(&self) {
|
|
||||||
if !self.is_empty.load(Ordering::SeqCst) {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
if !self.is_empty.load(Ordering::SeqCst) {
|
|
||||||
inner.try_select();
|
|
||||||
inner.notify();
|
|
||||||
self.is_empty.store(
|
|
||||||
inner.selectors.is_empty() && inner.observers.is_empty(),
|
|
||||||
Ordering::SeqCst,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notifies all threads that the channel is disconnected.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn disconnect(&self) {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
inner.disconnect();
|
|
||||||
self.is_empty
|
|
||||||
.store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for SyncWaker {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
debug_assert!(self.is_empty.load(Ordering::SeqCst));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a unique id for the current thread.
|
|
||||||
#[inline]
|
|
||||||
pub fn current_thread_id() -> usize {
|
|
||||||
// `u8` is not drop so this variable will be available during thread destruction,
|
|
||||||
// whereas `thread::current()` would not be
|
|
||||||
thread_local! { static DUMMY: u8 = const { 0 } }
|
|
||||||
DUMMY.with(|x| (x as *const u8).addr())
|
|
||||||
}
|
|
||||||
@@ -1,319 +0,0 @@
|
|||||||
//! Zero-capacity channel.
|
|
||||||
//!
|
|
||||||
//! This kind of channel is also known as *rendezvous* channel.
|
|
||||||
|
|
||||||
use super::context::Context;
|
|
||||||
use super::error::*;
|
|
||||||
use super::select::{Operation, Selected, Token};
|
|
||||||
use super::utils::Backoff;
|
|
||||||
use super::waker::Waker;
|
|
||||||
use crate::cell::UnsafeCell;
|
|
||||||
use crate::marker::PhantomData;
|
|
||||||
use crate::sync::Mutex;
|
|
||||||
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
|
|
||||||
use crate::time::Instant;
|
|
||||||
use crate::{fmt, ptr};
|
|
||||||
|
|
||||||
/// A pointer to a packet.
|
|
||||||
pub(crate) struct ZeroToken(*mut ());
|
|
||||||
|
|
||||||
impl Default for ZeroToken {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(ptr::null_mut())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ZeroToken {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(&(self.0 as usize), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A slot for passing one message from a sender to a receiver.
|
|
||||||
struct Packet<T> {
|
|
||||||
/// Equals `true` if the packet is allocated on the stack.
|
|
||||||
on_stack: bool,
|
|
||||||
|
|
||||||
/// Equals `true` once the packet is ready for reading or writing.
|
|
||||||
ready: Atomic<bool>,
|
|
||||||
|
|
||||||
/// The message.
|
|
||||||
msg: UnsafeCell<Option<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Packet<T> {
|
|
||||||
/// Creates an empty packet on the stack.
|
|
||||||
fn empty_on_stack() -> Packet<T> {
|
|
||||||
Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(None) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a packet on the stack, containing a message.
|
|
||||||
fn message_on_stack(msg: T) -> Packet<T> {
|
|
||||||
Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(Some(msg)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Waits until the packet becomes ready for reading or writing.
|
|
||||||
fn wait_ready(&self) {
|
|
||||||
let backoff = Backoff::new();
|
|
||||||
while !self.ready.load(Ordering::Acquire) {
|
|
||||||
backoff.spin_heavy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inner representation of a zero-capacity channel.
|
|
||||||
struct Inner {
|
|
||||||
/// Senders waiting to pair up with a receive operation.
|
|
||||||
senders: Waker,
|
|
||||||
|
|
||||||
/// Receivers waiting to pair up with a send operation.
|
|
||||||
receivers: Waker,
|
|
||||||
|
|
||||||
/// Equals `true` when the channel is disconnected.
|
|
||||||
is_disconnected: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Zero-capacity channel.
|
|
||||||
pub(crate) struct Channel<T> {
|
|
||||||
/// Inner representation of the channel.
|
|
||||||
inner: Mutex<Inner>,
|
|
||||||
|
|
||||||
/// Indicates that dropping a `Channel<T>` may drop values of type `T`.
|
|
||||||
_marker: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Channel<T> {
|
|
||||||
/// Constructs a new zero-capacity channel.
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
Channel {
|
|
||||||
inner: Mutex::new(Inner {
|
|
||||||
senders: Waker::new(),
|
|
||||||
receivers: Waker::new(),
|
|
||||||
is_disconnected: false,
|
|
||||||
}),
|
|
||||||
_marker: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes a message into the packet.
|
|
||||||
pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
|
|
||||||
// If there is no packet, the channel is disconnected.
|
|
||||||
if token.zero.0.is_null() {
|
|
||||||
return Err(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let packet = &*(token.zero.0 as *const Packet<T>);
|
|
||||||
packet.msg.get().write(Some(msg));
|
|
||||||
packet.ready.store(true, Ordering::Release);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a message from the packet.
|
|
||||||
pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
|
|
||||||
// If there is no packet, the channel is disconnected.
|
|
||||||
if token.zero.0.is_null() {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let packet = unsafe { &*(token.zero.0 as *const Packet<T>) };
|
|
||||||
|
|
||||||
if packet.on_stack {
|
|
||||||
// The message has been in the packet from the beginning, so there is no need to wait
|
|
||||||
// for it. However, after reading the message, we need to set `ready` to `true` in
|
|
||||||
// order to signal that the packet can be destroyed.
|
|
||||||
let msg = unsafe { packet.msg.get().replace(None) }.unwrap();
|
|
||||||
packet.ready.store(true, Ordering::Release);
|
|
||||||
Ok(msg)
|
|
||||||
} else {
|
|
||||||
// Wait until the message becomes available, then read it and destroy the
|
|
||||||
// heap-allocated packet.
|
|
||||||
packet.wait_ready();
|
|
||||||
unsafe {
|
|
||||||
let msg = packet.msg.get().replace(None).unwrap();
|
|
||||||
drop(Box::from_raw(token.zero.0 as *mut Packet<T>));
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to send a message into the channel.
|
|
||||||
pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
|
|
||||||
// If there's a waiting receiver, pair up with it.
|
|
||||||
if let Some(operation) = inner.receivers.try_select() {
|
|
||||||
token.zero.0 = operation.packet;
|
|
||||||
drop(inner);
|
|
||||||
unsafe {
|
|
||||||
self.write(token, msg).ok().unwrap();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
} else if inner.is_disconnected {
|
|
||||||
Err(TrySendError::Disconnected(msg))
|
|
||||||
} else {
|
|
||||||
Err(TrySendError::Full(msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a message into the channel.
|
|
||||||
pub(crate) fn send(
|
|
||||||
&self,
|
|
||||||
msg: T,
|
|
||||||
deadline: Option<Instant>,
|
|
||||||
) -> Result<(), SendTimeoutError<T>> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
|
|
||||||
// If there's a waiting receiver, pair up with it.
|
|
||||||
if let Some(operation) = inner.receivers.try_select() {
|
|
||||||
token.zero.0 = operation.packet;
|
|
||||||
drop(inner);
|
|
||||||
unsafe {
|
|
||||||
self.write(token, msg).ok().unwrap();
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if inner.is_disconnected {
|
|
||||||
return Err(SendTimeoutError::Disconnected(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
Context::with(|cx| {
|
|
||||||
// Prepare for blocking until a receiver wakes us up.
|
|
||||||
let oper = Operation::hook(token);
|
|
||||||
let mut packet = Packet::<T>::message_on_stack(msg);
|
|
||||||
inner.senders.register_with_packet(oper, (&raw mut packet) as *mut (), cx);
|
|
||||||
inner.receivers.notify();
|
|
||||||
drop(inner);
|
|
||||||
|
|
||||||
// Block the current thread.
|
|
||||||
// SAFETY: the context belongs to the current thread.
|
|
||||||
let sel = unsafe { cx.wait_until(deadline) };
|
|
||||||
|
|
||||||
match sel {
|
|
||||||
Selected::Waiting => unreachable!(),
|
|
||||||
Selected::Aborted => {
|
|
||||||
self.inner.lock().unwrap().senders.unregister(oper).unwrap();
|
|
||||||
let msg = unsafe { packet.msg.get().replace(None).unwrap() };
|
|
||||||
Err(SendTimeoutError::Timeout(msg))
|
|
||||||
}
|
|
||||||
Selected::Disconnected => {
|
|
||||||
self.inner.lock().unwrap().senders.unregister(oper).unwrap();
|
|
||||||
let msg = unsafe { packet.msg.get().replace(None).unwrap() };
|
|
||||||
Err(SendTimeoutError::Disconnected(msg))
|
|
||||||
}
|
|
||||||
Selected::Operation(_) => {
|
|
||||||
// Wait until the message is read, then drop the packet.
|
|
||||||
packet.wait_ready();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to receive a message without blocking.
|
|
||||||
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
|
|
||||||
// If there's a waiting sender, pair up with it.
|
|
||||||
if let Some(operation) = inner.senders.try_select() {
|
|
||||||
token.zero.0 = operation.packet;
|
|
||||||
drop(inner);
|
|
||||||
unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
|
|
||||||
} else if inner.is_disconnected {
|
|
||||||
Err(TryRecvError::Disconnected)
|
|
||||||
} else {
|
|
||||||
Err(TryRecvError::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Receives a message from the channel.
|
|
||||||
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
|
|
||||||
let token = &mut Token::default();
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
|
|
||||||
// If there's a waiting sender, pair up with it.
|
|
||||||
if let Some(operation) = inner.senders.try_select() {
|
|
||||||
token.zero.0 = operation.packet;
|
|
||||||
drop(inner);
|
|
||||||
unsafe {
|
|
||||||
return self.read(token).map_err(|_| RecvTimeoutError::Disconnected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if inner.is_disconnected {
|
|
||||||
return Err(RecvTimeoutError::Disconnected);
|
|
||||||
}
|
|
||||||
|
|
||||||
Context::with(|cx| {
|
|
||||||
// Prepare for blocking until a sender wakes us up.
|
|
||||||
let oper = Operation::hook(token);
|
|
||||||
let mut packet = Packet::<T>::empty_on_stack();
|
|
||||||
inner.receivers.register_with_packet(oper, (&raw mut packet) as *mut (), cx);
|
|
||||||
inner.senders.notify();
|
|
||||||
drop(inner);
|
|
||||||
|
|
||||||
// Block the current thread.
|
|
||||||
// SAFETY: the context belongs to the current thread.
|
|
||||||
let sel = unsafe { cx.wait_until(deadline) };
|
|
||||||
|
|
||||||
match sel {
|
|
||||||
Selected::Waiting => unreachable!(),
|
|
||||||
Selected::Aborted => {
|
|
||||||
self.inner.lock().unwrap().receivers.unregister(oper).unwrap();
|
|
||||||
Err(RecvTimeoutError::Timeout)
|
|
||||||
}
|
|
||||||
Selected::Disconnected => {
|
|
||||||
self.inner.lock().unwrap().receivers.unregister(oper).unwrap();
|
|
||||||
Err(RecvTimeoutError::Disconnected)
|
|
||||||
}
|
|
||||||
Selected::Operation(_) => {
|
|
||||||
// Wait until the message is provided, then read it.
|
|
||||||
packet.wait_ready();
|
|
||||||
unsafe { Ok(packet.msg.get().replace(None).unwrap()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disconnects the channel and wakes up all blocked senders and receivers.
|
|
||||||
///
|
|
||||||
/// Returns `true` if this call disconnected the channel.
|
|
||||||
pub(crate) fn disconnect(&self) -> bool {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
|
|
||||||
if !inner.is_disconnected {
|
|
||||||
inner.is_disconnected = true;
|
|
||||||
inner.senders.disconnect();
|
|
||||||
inner.receivers.disconnect();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the current number of messages inside the channel.
|
|
||||||
pub(crate) fn len(&self) -> usize {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the capacity of the channel.
|
|
||||||
#[allow(clippy::unnecessary_wraps)] // This is intentional.
|
|
||||||
pub(crate) fn capacity(&self) -> Option<usize> {
|
|
||||||
Some(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the channel is empty.
|
|
||||||
pub(crate) fn is_empty(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the channel is full.
|
|
||||||
pub(crate) fn is_full(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,45 +0,0 @@
|
|||||||
//! Non-poisoning synchronous locks.
|
|
||||||
//!
|
|
||||||
//! The difference from the locks in the [`poison`] module is that the locks in this module will not
|
|
||||||
//! become poisoned when a thread panics while holding a guard.
|
|
||||||
//!
|
|
||||||
//! [`poison`]: super::poison
|
|
||||||
|
|
||||||
use crate::fmt;
|
|
||||||
|
|
||||||
/// A type alias for the result of a nonblocking locking method.
|
|
||||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
|
||||||
pub type TryLockResult<Guard> = Result<Guard, WouldBlock>;
|
|
||||||
|
|
||||||
/// A lock could not be acquired at this time because the operation would otherwise block.
|
|
||||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
|
||||||
pub struct WouldBlock;
|
|
||||||
|
|
||||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
|
||||||
impl fmt::Debug for WouldBlock {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
"WouldBlock".fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
|
||||||
impl fmt::Display for WouldBlock {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
"try_lock failed because the operation would block".fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
|
||||||
pub use self::condvar::Condvar;
|
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
|
||||||
pub use self::mutex::MappedMutexGuard;
|
|
||||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
|
||||||
pub use self::mutex::{Mutex, MutexGuard};
|
|
||||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
|
||||||
pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
|
|
||||||
#[unstable(feature = "nonpoison_rwlock", issue = "134645")]
|
|
||||||
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
|
||||||
|
|
||||||
mod condvar;
|
|
||||||
mod mutex;
|
|
||||||
mod rwlock;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user