Change io crate & add a small shell
This commit is contained in:
@@ -7,5 +7,4 @@ json-target-spec = true
|
||||
[target.riscv64]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tilm.ld",
|
||||
"--sysroot", "sysroot"
|
||||
]
|
||||
|
||||
2
.gdbinit
2
.gdbinit
@@ -2,5 +2,5 @@
|
||||
target remote localhost:1234
|
||||
# break machine_mode_entry
|
||||
break *0x800cfdd8
|
||||
add-symbol-file target/riscv64/debug/test_pic 0x800cfdd8
|
||||
add-symbol-file target/riscv64/debug/agetty 0x800cfdd8
|
||||
c
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["user/*"]
|
||||
exclude = ["library", "build-tools"]
|
||||
members = ["crates/ansii","user/*"]
|
||||
exclude = ["library", "build-tools", "crates/io"]
|
||||
|
||||
[package]
|
||||
name = "kernel-rust"
|
||||
@@ -13,10 +13,11 @@ bitflags = "2"
|
||||
embedded-alloc = "0.7"
|
||||
kernel-macros = { path = "crates/kernel-macros" }
|
||||
bytes-struct = { path = "crates/bytes-struct" }
|
||||
ansii = { path = "crates/ansii" }
|
||||
log = "0.4"
|
||||
critical-section = { version = "1", features = ["restore-state-bool"] }
|
||||
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"] }
|
||||
goblin = { version = "0.7", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
|
||||
hashbrown = "0.16"
|
||||
|
||||
@@ -37,7 +37,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
println!("Extraction des symboles depuis {}...", elf_path);
|
||||
|
||||
obj_file.symbols().enumerate().for_each(|(i, sym)| {
|
||||
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();
|
||||
@@ -76,20 +76,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Tri par adresse pour la recherche binaire au runtime
|
||||
symbols_list.sort_by_key(|s| s.addr);
|
||||
|
||||
let idx = match symbols_list.binary_search_by_key(&(0x000000008004073c), |s| s.addr) {
|
||||
Ok(i) => i,
|
||||
Err(i) if i > 0 => i - 1,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
println!("{:?}", unsafe {
|
||||
get_str(
|
||||
string_table
|
||||
.as_ptr()
|
||||
.add(symbols_list[idx].name_off as usize),
|
||||
)
|
||||
});
|
||||
|
||||
// Écriture du fichier symbols.bin
|
||||
let mut f = BufWriter::new(File::create("../target/symbols.bin")?);
|
||||
|
||||
@@ -119,10 +105,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
unsafe fn get_str(ptr: *const u8) -> &'static str {
|
||||
let mut len = 0;
|
||||
while *ptr.add(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len))
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
[dependencies]
|
||||
io = { path = "../io" }
|
||||
io = { package = "no-std-io", path = "../io" }
|
||||
bitflags = "2"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::error::Error;
|
||||
use io::{Read, ReadLeExt};
|
||||
use io::Read;
|
||||
|
||||
use crate::io_ext::ReadLeExt;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Fat32BootSector {
|
||||
@@ -94,7 +95,7 @@ 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; _];
|
||||
disk.read_exact(&mut jump_boot)?;
|
||||
let mut oem_name = [0u8; _];
|
||||
|
||||
@@ -1,36 +1,32 @@
|
||||
use crate::{
|
||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||
entry::{DirEntry, DirectoryIterator},
|
||||
error::Error,
|
||||
file::{File, RawFile},
|
||||
path::Path,
|
||||
};
|
||||
use io::{self, IoBase, Read, Seek, Write};
|
||||
use io::{self, Read, Seek, Write};
|
||||
|
||||
pub struct Dir<'a, T> {
|
||||
raw: RawFile<'a, 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> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.raw.flush()
|
||||
}
|
||||
}
|
||||
@@ -44,7 +40,7 @@ impl<'a, T> 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() {
|
||||
return self.fs.open_entry(path);
|
||||
}
|
||||
@@ -56,16 +52,16 @@ impl<'a, T: ReadSeek> Dir<'a, T> {
|
||||
if f.is_dir() {
|
||||
return f.to_dir().open_entry(entry_name);
|
||||
} else {
|
||||
return Err(Error::NotFound);
|
||||
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||
}
|
||||
} else {
|
||||
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() {
|
||||
return self.fs.open_file(path);
|
||||
}
|
||||
@@ -74,19 +70,19 @@ impl<'a, T: ReadSeek> Dir<'a, T> {
|
||||
match dirname {
|
||||
Some(name) => {
|
||||
if !entry.is_dir() {
|
||||
return Err(Error::NotFound);
|
||||
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||
}
|
||||
entry.to_dir().open_file(name)
|
||||
}
|
||||
None => {
|
||||
if !entry.is_file() {
|
||||
return Err(Error::NotFound);
|
||||
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||
}
|
||||
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();
|
||||
if path.is_absolute() {
|
||||
return self.fs.open_dir(path);
|
||||
@@ -94,7 +90,7 @@ impl<'a, T: ReadSeek> Dir<'a, T> {
|
||||
let (start, dirname) = path.split_path();
|
||||
let entry = self.open_entry(start)?;
|
||||
if !entry.is_dir() {
|
||||
return Err(Error::NotFound);
|
||||
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||
}
|
||||
match dirname {
|
||||
Some(name) => entry.to_dir().open_dir(name),
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use core::{array::IntoIter, iter::Copied, str::Utf8Error};
|
||||
use core::{char::DecodeUtf16, marker::PhantomData, slice::Iter};
|
||||
|
||||
use crate::io_ext::ReadLeExt;
|
||||
use crate::{
|
||||
Fat32FileSystem, ReadSeek,
|
||||
consts::FATAttr,
|
||||
dir::Dir,
|
||||
error::Error,
|
||||
file::{File, RawFile},
|
||||
};
|
||||
|
||||
use io::{IoBase, Read, ReadLeExt};
|
||||
use io::Read;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::borrow::ToOwned;
|
||||
@@ -33,7 +33,7 @@ pub struct 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; _];
|
||||
reader.read_exact(&mut name)?;
|
||||
let attr = reader.read_u8()?;
|
||||
@@ -131,7 +131,7 @@ pub struct LongFileNameBuilder<T> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: IoBase> LongFileNameBuilder<T> {
|
||||
impl<T> LongFileNameBuilder<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: [0; _],
|
||||
@@ -160,12 +160,12 @@ impl<T: IoBase> LongFileNameBuilder<T> {
|
||||
}
|
||||
}
|
||||
#[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>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IoBase> IntoIterator for LongFileNameBuilder<T> {
|
||||
impl<T> IntoIterator for LongFileNameBuilder<T> {
|
||||
type Item = <LongFileNameIterator<T, IntoIter<u16, 256>> as Iterator>::Item;
|
||||
|
||||
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>>> {
|
||||
LongFileNameIterator {
|
||||
iterator: char::decode_utf16(self.inner.iter().copied()),
|
||||
@@ -192,8 +192,8 @@ pub struct LongFileNameIterator<T, I: Iterator<Item = u16>> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: IoBase, I: Iterator<Item = u16>> Iterator for LongFileNameIterator<T, I> {
|
||||
type Item = Result<char, Error<T::Error>>;
|
||||
impl<T, I: Iterator<Item = u16>> Iterator for LongFileNameIterator<T, I> {
|
||||
type Item = Result<char, io::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.iterator.next()? {
|
||||
@@ -204,12 +204,15 @@ impl<T: IoBase, I: Iterator<Item = u16>> Iterator for LongFileNameIterator<T, I>
|
||||
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 {
|
||||
Self::new()
|
||||
}
|
||||
@@ -226,7 +229,7 @@ impl<'a, T> 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> {
|
||||
let mut lfn_builder = LongFileNameBuilder::new();
|
||||
@@ -283,7 +286,7 @@ pub struct DirEntry<'a, T> {
|
||||
fs: &'a Fat32FileSystem<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: IoBase> DirEntry<'a, T> {
|
||||
impl<'a, T> DirEntry<'a, T> {
|
||||
fn new(
|
||||
entry: FatDirEntry,
|
||||
short_name: [u8; 11],
|
||||
@@ -298,13 +301,16 @@ impl<'a, T: IoBase> DirEntry<'a, T> {
|
||||
}
|
||||
}
|
||||
#[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() {
|
||||
long_name.to_string()
|
||||
} else {
|
||||
self.short_name()
|
||||
.map(|n| n.to_owned())
|
||||
.map_err(|_| Error::UnsupportedFileNameCharacter)
|
||||
self.short_name().map(|n| n.to_owned()).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"Unsupported file name character",
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
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::{
|
||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||
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)]
|
||||
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> {
|
||||
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 {
|
||||
io::SeekFrom::Start(s) => s,
|
||||
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> {
|
||||
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
|
||||
.size
|
||||
.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> {
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, io::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -132,25 +127,22 @@ pub struct File<'a, 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> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,10 @@ use core::cell::RefCell;
|
||||
use core::fmt::Display;
|
||||
|
||||
use crate::{
|
||||
boot_sector::Fat32BootSector,
|
||||
consts::FAT32_CLUSTER_MASK,
|
||||
dir::Dir,
|
||||
entry::{DirEntry, FatEntry},
|
||||
error::Error,
|
||||
file::{File, RawFile},
|
||||
path::Path,
|
||||
boot_sector::Fat32BootSector, consts::FAT32_CLUSTER_MASK, dir::Dir, entry::{DirEntry, FatEntry}, file::{File, RawFile}, io_ext::ReadLeExt, path::Path
|
||||
};
|
||||
|
||||
use io::{Read, ReadLeExt, Seek, Write};
|
||||
use io::{Read, Seek, Write};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
@@ -24,8 +18,8 @@ pub mod boot_sector;
|
||||
pub mod consts;
|
||||
pub mod dir;
|
||||
pub mod entry;
|
||||
pub mod error;
|
||||
pub mod file;
|
||||
pub mod io_ext;
|
||||
pub mod path;
|
||||
|
||||
pub trait ReadSeek: Read + Seek {}
|
||||
@@ -47,7 +41,7 @@ pub struct 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 mut reserved1 = [0u8; _];
|
||||
disk.read_exact(&mut reserved1)?;
|
||||
@@ -82,7 +76,7 @@ impl<T> Display for 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))?;
|
||||
|
||||
let boot_sector = Fat32BootSector::deserialize(&mut device)?;
|
||||
@@ -92,7 +86,7 @@ impl<T: ReadSeek> Fat32FileSystem<T> {
|
||||
})
|
||||
}
|
||||
/// 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 =
|
||||
self.fat_start_offset() + (current_cluster as u64 * size_of::<FatEntry>() as u64);
|
||||
self.device
|
||||
@@ -137,15 +131,15 @@ impl<T> 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("/");
|
||||
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("/");
|
||||
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("/");
|
||||
self.root_directory().open_file(path)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ pub fn main() {
|
||||
|
||||
// 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();
|
||||
f.read_to_end(&mut content).unwrap();
|
||||
println!("file content len: {}", content.len());
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use core::fmt::Display;
|
||||
#[cfg(feature = "alloc")]
|
||||
use core::{borrow::Borrow, ops::Deref};
|
||||
|
||||
@@ -12,6 +13,12 @@ pub struct Path {
|
||||
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 {
|
||||
fn from(value: &str) -> Self {
|
||||
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]
|
||||
name = "io"
|
||||
name = "no-std-io"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
std = ["alloc"]
|
||||
std = []
|
||||
|
||||
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",
|
||||
)
|
||||
}
|
||||
}
|
||||
592
crates/io/src/io/buffered/bufreader.rs
Normal file
592
crates/io/src/io/buffered/bufreader.rs
Normal file
@@ -0,0 +1,592 @@
|
||||
mod buffer;
|
||||
|
||||
use buffer::Buffer;
|
||||
|
||||
use crate::fmt;
|
||||
use crate::io::{
|
||||
self, BorrowedCursor, BufRead, DEFAULT_BUF_SIZE, IoSliceMut, Read, Seek, SeekFrom, SizeHint,
|
||||
SpecReadByte, uninlined_slow_read_byte,
|
||||
};
|
||||
|
||||
/// The `BufReader<R>` struct adds buffering to any reader.
|
||||
///
|
||||
/// It can be excessively inefficient to work directly with a [`Read`] instance.
|
||||
/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`]
|
||||
/// results in a system call. A `BufReader<R>` performs large, infrequent reads on
|
||||
/// the underlying [`Read`] and maintains an in-memory buffer of the results.
|
||||
///
|
||||
/// `BufReader<R>` can improve the speed of programs that make *small* and
|
||||
/// *repeated* read calls to the same file or network socket. It does not
|
||||
/// help when reading very large amounts at once, or reading just one or a few
|
||||
/// times. It also provides no advantage when reading from a source that is
|
||||
/// already in memory, like a <code>[Vec]\<u8></code>.
|
||||
///
|
||||
/// When the `BufReader<R>` is dropped, the contents of its buffer will be
|
||||
/// discarded. Creating multiple instances of a `BufReader<R>` on the same
|
||||
/// stream can cause data loss. Reading from the underlying reader after
|
||||
/// unwrapping the `BufReader<R>` with [`BufReader::into_inner`] can also cause
|
||||
/// data loss.
|
||||
///
|
||||
/// [`TcpStream::read`]: crate::net::TcpStream::read
|
||||
/// [`TcpStream`]: crate::net::TcpStream
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f);
|
||||
///
|
||||
/// let mut line = String::new();
|
||||
/// let len = reader.read_line(&mut line)?;
|
||||
/// println!("First line is {len} bytes long");
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct BufReader<R: ?Sized> {
|
||||
buf: Buffer,
|
||||
inner: R,
|
||||
}
|
||||
|
||||
impl<R: Read> BufReader<R> {
|
||||
/// Creates a new `BufReader<R>` with a default buffer capacity. The default is currently 8 KiB,
|
||||
/// but may change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let reader = BufReader::new(f);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(inner: R) -> BufReader<R> {
|
||||
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||
}
|
||||
|
||||
pub(crate) fn try_new_buffer() -> io::Result<Buffer> {
|
||||
Buffer::try_with_capacity(DEFAULT_BUF_SIZE)
|
||||
}
|
||||
|
||||
pub(crate) fn with_buffer(inner: R, buf: Buffer) -> Self {
|
||||
Self { inner, buf }
|
||||
}
|
||||
|
||||
/// Creates a new `BufReader<R>` with the specified buffer capacity.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a buffer with ten bytes of capacity:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let reader = BufReader::with_capacity(10, f);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> {
|
||||
BufReader { inner, buf: Buffer::with_capacity(capacity) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + ?Sized> BufReader<R> {
|
||||
/// Attempt to look ahead `n` bytes.
|
||||
///
|
||||
/// `n` must be less than or equal to `capacity`.
|
||||
///
|
||||
/// The returned slice may be less than `n` bytes long if
|
||||
/// end of file is reached.
|
||||
///
|
||||
/// After calling this method, you may call [`consume`](BufRead::consume)
|
||||
/// with a value less than or equal to `n` to advance over some or all of
|
||||
/// the returned bytes.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(bufreader_peek)]
|
||||
/// use std::io::{Read, BufReader};
|
||||
///
|
||||
/// let mut bytes = &b"oh, hello there"[..];
|
||||
/// let mut rdr = BufReader::with_capacity(6, &mut bytes);
|
||||
/// assert_eq!(rdr.peek(2).unwrap(), b"oh");
|
||||
/// let mut buf = [0; 4];
|
||||
/// rdr.read(&mut buf[..]).unwrap();
|
||||
/// assert_eq!(&buf, b"oh, ");
|
||||
/// assert_eq!(rdr.peek(5).unwrap(), b"hello");
|
||||
/// let mut s = String::new();
|
||||
/// rdr.read_to_string(&mut s).unwrap();
|
||||
/// assert_eq!(&s, "hello there");
|
||||
/// assert_eq!(rdr.peek(1).unwrap().len(), 0);
|
||||
/// ```
|
||||
#[unstable(feature = "bufreader_peek", issue = "128405")]
|
||||
pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> {
|
||||
assert!(n <= self.capacity());
|
||||
while n > self.buf.buffer().len() {
|
||||
if self.buf.pos() > 0 {
|
||||
self.buf.backshift();
|
||||
}
|
||||
let new = self.buf.read_more(&mut self.inner)?;
|
||||
if new == 0 {
|
||||
// end of file, no more bytes to read
|
||||
return Ok(&self.buf.buffer()[..]);
|
||||
}
|
||||
debug_assert_eq!(self.buf.pos(), 0);
|
||||
}
|
||||
Ok(&self.buf.buffer()[..n])
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ?Sized> BufReader<R> {
|
||||
/// Gets a reference to the underlying reader.
|
||||
///
|
||||
/// It is inadvisable to directly read from the underlying reader.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f1 = File::open("log.txt")?;
|
||||
/// let reader = BufReader::new(f1);
|
||||
///
|
||||
/// let f2 = reader.get_ref();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_ref(&self) -> &R {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying reader.
|
||||
///
|
||||
/// It is inadvisable to directly read from the underlying reader.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f1 = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f1);
|
||||
///
|
||||
/// let f2 = reader.get_mut();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_mut(&mut self) -> &mut R {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns a reference to the internally buffered data.
|
||||
///
|
||||
/// Unlike [`fill_buf`], this will not attempt to fill the buffer if it is empty.
|
||||
///
|
||||
/// [`fill_buf`]: BufRead::fill_buf
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::{BufReader, BufRead};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f);
|
||||
/// assert!(reader.buffer().is_empty());
|
||||
///
|
||||
/// if reader.fill_buf()?.len() > 0 {
|
||||
/// assert!(!reader.buffer().is_empty());
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "bufreader_buffer", since = "1.37.0")]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
self.buf.buffer()
|
||||
}
|
||||
|
||||
/// Returns the number of bytes the internal buffer can hold at once.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::{BufReader, BufRead};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::open("log.txt")?;
|
||||
/// let mut reader = BufReader::new(f);
|
||||
///
|
||||
/// let capacity = reader.capacity();
|
||||
/// let buffer = reader.fill_buf()?;
|
||||
/// assert!(buffer.len() <= capacity);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "buffered_io_capacity", since = "1.46.0")]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.capacity()
|
||||
}
|
||||
|
||||
/// Unwraps this `BufReader<R>`, returning the underlying reader.
|
||||
///
|
||||
/// Note that any leftover data in the internal buffer is lost. Therefore,
|
||||
/// a following read from the underlying reader may lead to data loss.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufReader;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f1 = File::open("log.txt")?;
|
||||
/// let reader = BufReader::new(f1);
|
||||
///
|
||||
/// let f2 = reader.into_inner();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> R
|
||||
where
|
||||
R: Sized,
|
||||
{
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Invalidates all data in the internal buffer.
|
||||
#[inline]
|
||||
pub(in crate::io) fn discard_buffer(&mut self) {
|
||||
self.buf.discard_buffer()
|
||||
}
|
||||
}
|
||||
|
||||
// This is only used by a test which asserts that the initialization-tracking is correct.
|
||||
#[cfg(test)]
|
||||
impl<R: ?Sized> BufReader<R> {
|
||||
#[allow(missing_docs)]
|
||||
pub fn initialized(&self) -> usize {
|
||||
self.buf.initialized()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Seek> BufReader<R> {
|
||||
/// Seeks relative to the current position. If the new position lies within the buffer,
|
||||
/// the buffer will not be flushed, allowing for more efficient seeks.
|
||||
/// This method does not return the location of the underlying reader, so the caller
|
||||
/// must track this information themselves if it is required.
|
||||
#[stable(feature = "bufreader_seek_relative", since = "1.53.0")]
|
||||
pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
let pos = self.buf.pos() as u64;
|
||||
if offset < 0 {
|
||||
if let Some(_) = pos.checked_sub((-offset) as u64) {
|
||||
self.buf.unconsume((-offset) as usize);
|
||||
return Ok(());
|
||||
}
|
||||
} else if let Some(new_pos) = pos.checked_add(offset as u64) {
|
||||
if new_pos <= self.buf.filled() as u64 {
|
||||
self.buf.consume(offset as usize);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
self.seek(SeekFrom::Current(offset)).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> SpecReadByte for BufReader<R>
|
||||
where
|
||||
Self: Read,
|
||||
{
|
||||
#[inline]
|
||||
fn spec_read_byte(&mut self) -> Option<io::Result<u8>> {
|
||||
let mut byte = 0;
|
||||
if self.buf.consume_with(1, |claimed| byte = claimed[0]) {
|
||||
return Some(Ok(byte));
|
||||
}
|
||||
|
||||
// Fallback case, only reached once per buffer refill.
|
||||
uninlined_slow_read_byte(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: ?Sized + Read> Read for BufReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// If we don't have any buffered data and we're doing a massive read
|
||||
// (larger than our internal buffer), bypass our internal buffer
|
||||
// entirely.
|
||||
if self.buf.pos() == self.buf.filled() && buf.len() >= self.capacity() {
|
||||
self.discard_buffer();
|
||||
return self.inner.read(buf);
|
||||
}
|
||||
let mut rem = self.fill_buf()?;
|
||||
let nread = rem.read(buf)?;
|
||||
self.consume(nread);
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
// If we don't have any buffered data and we're doing a massive read
|
||||
// (larger than our internal buffer), bypass our internal buffer
|
||||
// entirely.
|
||||
if self.buf.pos() == self.buf.filled() && cursor.capacity() >= self.capacity() {
|
||||
self.discard_buffer();
|
||||
return self.inner.read_buf(cursor);
|
||||
}
|
||||
|
||||
let prev = cursor.written();
|
||||
|
||||
let mut rem = self.fill_buf()?;
|
||||
rem.read_buf(cursor.reborrow())?; // actually never fails
|
||||
|
||||
self.consume(cursor.written() - prev); //slice impl of read_buf known to never unfill buf
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Small read_exacts from a BufReader are extremely common when used with a deserializer.
|
||||
// The default implementation calls read in a loop, which results in surprisingly poor code
|
||||
// generation for the common path where the buffer has enough bytes to fill the passed-in
|
||||
// buffer.
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
if self.buf.consume_with(buf.len(), |claimed| buf.copy_from_slice(claimed)) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
crate::io::default_read_exact(self, buf)
|
||||
}
|
||||
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
if self.buf.consume_with(cursor.capacity(), |claimed| cursor.append(claimed)) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
crate::io::default_read_buf_exact(self, cursor)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
|
||||
if self.buf.pos() == self.buf.filled() && total_len >= self.capacity() {
|
||||
self.discard_buffer();
|
||||
return self.inner.read_vectored(bufs);
|
||||
}
|
||||
let mut rem = self.fill_buf()?;
|
||||
let nread = rem.read_vectored(bufs)?;
|
||||
|
||||
self.consume(nread);
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
self.inner.is_read_vectored()
|
||||
}
|
||||
|
||||
// The inner reader might have an optimized `read_to_end`. Drain our buffer and then
|
||||
// delegate to the inner implementation.
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let inner_buf = self.buffer();
|
||||
buf.try_reserve(inner_buf.len())?;
|
||||
buf.extend_from_slice(inner_buf);
|
||||
let nread = inner_buf.len();
|
||||
self.discard_buffer();
|
||||
Ok(nread + self.inner.read_to_end(buf)?)
|
||||
}
|
||||
|
||||
// The inner reader might have an optimized `read_to_end`. Drain our buffer and then
|
||||
// delegate to the inner implementation.
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
// In the general `else` case below we must read bytes into a side buffer, check
|
||||
// that they are valid UTF-8, and then append them to `buf`. This requires a
|
||||
// potentially large memcpy.
|
||||
//
|
||||
// If `buf` is empty--the most common case--we can leverage `append_to_string`
|
||||
// to read directly into `buf`'s internal byte buffer, saving an allocation and
|
||||
// a memcpy.
|
||||
if buf.is_empty() {
|
||||
// `append_to_string`'s safety relies on the buffer only being appended to since
|
||||
// it only checks the UTF-8 validity of new data. If there were existing content in
|
||||
// `buf` then an untrustworthy reader (i.e. `self.inner`) could not only append
|
||||
// bytes but also modify existing bytes and render them invalid. On the other hand,
|
||||
// if `buf` is empty then by definition any writes must be appends and
|
||||
// `append_to_string` will validate all of the new bytes.
|
||||
unsafe { crate::io::append_to_string(buf, |b| self.read_to_end(b)) }
|
||||
} else {
|
||||
// We cannot append our byte buffer directly onto the `buf` String as there could
|
||||
// be an incomplete UTF-8 sequence that has only been partially read. We must read
|
||||
// everything into a side buffer first and then call `from_utf8` on the complete
|
||||
// buffer.
|
||||
let mut bytes = Vec::new();
|
||||
self.read_to_end(&mut bytes)?;
|
||||
let string = crate::str::from_utf8(&bytes).map_err(|_| io::Error::INVALID_UTF8)?;
|
||||
*buf += string;
|
||||
Ok(string.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: ?Sized + Read> BufRead for BufReader<R> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
self.buf.fill_buf(&mut self.inner)
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.buf.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R> fmt::Debug for BufReader<R>
|
||||
where
|
||||
R: ?Sized + fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("BufReader")
|
||||
.field("reader", &&self.inner)
|
||||
.field(
|
||||
"buffer",
|
||||
&format_args!("{}/{}", self.buf.filled() - self.buf.pos(), self.capacity()),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: ?Sized + Seek> Seek for BufReader<R> {
|
||||
/// Seek to an offset, in bytes, in the underlying reader.
|
||||
///
|
||||
/// The position used for seeking with <code>[SeekFrom::Current]\(_)</code> is the
|
||||
/// position the underlying reader would be at if the `BufReader<R>` had no
|
||||
/// internal buffer.
|
||||
///
|
||||
/// Seeking always discards the internal buffer, even if the seek position
|
||||
/// would otherwise fall within it. This guarantees that calling
|
||||
/// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader
|
||||
/// at the same position.
|
||||
///
|
||||
/// To seek without discarding the internal buffer, use [`BufReader::seek_relative`].
|
||||
///
|
||||
/// See [`std::io::Seek`] for more details.
|
||||
///
|
||||
/// Note: In the edge case where you're seeking with <code>[SeekFrom::Current]\(n)</code>
|
||||
/// where `n` minus the internal buffer length overflows an `i64`, two
|
||||
/// seeks will be performed instead of one. If the second seek returns
|
||||
/// [`Err`], the underlying reader will be left at the same position it would
|
||||
/// have if you called `seek` with <code>[SeekFrom::Current]\(0)</code>.
|
||||
///
|
||||
/// [`std::io::Seek`]: Seek
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
let result: u64;
|
||||
if let SeekFrom::Current(n) = pos {
|
||||
let remainder = (self.buf.filled() - self.buf.pos()) as i64;
|
||||
// it should be safe to assume that remainder fits within an i64 as the alternative
|
||||
// means we managed to allocate 8 exbibytes and that's absurd.
|
||||
// But it's not out of the realm of possibility for some weird underlying reader to
|
||||
// support seeking by i64::MIN so we need to handle underflow when subtracting
|
||||
// remainder.
|
||||
if let Some(offset) = n.checked_sub(remainder) {
|
||||
result = self.inner.seek(SeekFrom::Current(offset))?;
|
||||
} else {
|
||||
// seek backwards by our remainder, and then by the offset
|
||||
self.inner.seek(SeekFrom::Current(-remainder))?;
|
||||
self.discard_buffer();
|
||||
result = self.inner.seek(SeekFrom::Current(n))?;
|
||||
}
|
||||
} else {
|
||||
// Seeking with Start/End doesn't care about our buffer length.
|
||||
result = self.inner.seek(pos)?;
|
||||
}
|
||||
self.discard_buffer();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Returns the current seek position from the start of the stream.
|
||||
///
|
||||
/// The value returned is equivalent to `self.seek(SeekFrom::Current(0))`
|
||||
/// but does not flush the internal buffer. Due to this optimization the
|
||||
/// function does not guarantee that calling `.into_inner()` immediately
|
||||
/// afterwards will yield the underlying reader at the same position. Use
|
||||
/// [`BufReader::seek`] instead if you require that guarantee.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the position of the inner reader is smaller
|
||||
/// than the amount of buffered data. That can happen if the inner reader
|
||||
/// has an incorrect implementation of [`Seek::stream_position`], or if the
|
||||
/// position has gone out of sync due to calling [`Seek::seek`] directly on
|
||||
/// the underlying reader.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::{
|
||||
/// io::{self, BufRead, BufReader, Seek},
|
||||
/// fs::File,
|
||||
/// };
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut f = BufReader::new(File::open("foo.txt")?);
|
||||
///
|
||||
/// let before = f.stream_position()?;
|
||||
/// f.read_line(&mut String::new())?;
|
||||
/// let after = f.stream_position()?;
|
||||
///
|
||||
/// println!("The first line was {} bytes long", after - before);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
let remainder = (self.buf.filled() - self.buf.pos()) as u64;
|
||||
self.inner.stream_position().map(|pos| {
|
||||
pos.checked_sub(remainder).expect(
|
||||
"overflow when subtracting remaining buffer size from inner stream position",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Seeks relative to the current position.
|
||||
///
|
||||
/// If the new position lies within the buffer, the buffer will not be
|
||||
/// flushed, allowing for more efficient seeks. This method does not return
|
||||
/// the location of the underlying reader, so the caller must track this
|
||||
/// information themselves if it is required.
|
||||
fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
self.seek_relative(offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> SizeHint for BufReader<T> {
|
||||
#[inline]
|
||||
fn lower_bound(&self) -> usize {
|
||||
SizeHint::lower_bound(self.get_ref()) + self.buffer().len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn upper_bound(&self) -> Option<usize> {
|
||||
SizeHint::upper_bound(self.get_ref()).and_then(|up| self.buffer().len().checked_add(up))
|
||||
}
|
||||
}
|
||||
155
crates/io/src/io/buffered/bufreader/buffer.rs
Normal file
155
crates/io/src/io/buffered/bufreader/buffer.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
//! An encapsulation of `BufReader`'s buffer management logic.
|
||||
//!
|
||||
//! This module factors out the basic functionality of `BufReader` in order to protect two core
|
||||
//! invariants:
|
||||
//! * `filled` bytes of `buf` are always initialized
|
||||
//! * `pos` is always <= `filled`
|
||||
//! Since this module encapsulates the buffer management logic, we can ensure that the range
|
||||
//! `pos..filled` is always a valid index into the initialized region of the buffer. This means
|
||||
//! that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so
|
||||
//! without encountering any runtime bounds checks.
|
||||
|
||||
use crate::cmp;
|
||||
use crate::io::{self, BorrowedBuf, ErrorKind, Read};
|
||||
use crate::mem::MaybeUninit;
|
||||
|
||||
pub struct Buffer {
|
||||
// The buffer.
|
||||
buf: Box<[MaybeUninit<u8>]>,
|
||||
// The current seek offset into `buf`, must always be <= `filled`.
|
||||
pos: usize,
|
||||
// Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are
|
||||
// initialized with bytes from a read.
|
||||
filled: usize,
|
||||
// This is the max number of bytes returned across all `fill_buf` calls. We track this so that we
|
||||
// can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much of its
|
||||
// defensive initialization as possible. Note that while this often the same as `filled`, it
|
||||
// doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and
|
||||
// omitting this is a huge perf regression for `Read` impls that do not.
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let buf = Box::new_uninit_slice(capacity);
|
||||
Self { buf, pos: 0, filled: 0, initialized: 0 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_with_capacity(capacity: usize) -> io::Result<Self> {
|
||||
match Box::try_new_uninit_slice(capacity) {
|
||||
Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }),
|
||||
Err(_) => {
|
||||
Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
// SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and
|
||||
// that region is initialized because those are all invariants of this type.
|
||||
unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn filled(&self) -> usize {
|
||||
self.filled
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pos(&self) -> usize {
|
||||
self.pos
|
||||
}
|
||||
|
||||
// This is only used by a test which asserts that the initialization-tracking is correct.
|
||||
#[cfg(test)]
|
||||
pub fn initialized(&self) -> usize {
|
||||
self.initialized
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn discard_buffer(&mut self) {
|
||||
self.pos = 0;
|
||||
self.filled = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn consume(&mut self, amt: usize) {
|
||||
self.pos = cmp::min(self.pos + amt, self.filled);
|
||||
}
|
||||
|
||||
/// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to
|
||||
/// `visitor` and return true. If there are not enough bytes available, return false.
|
||||
#[inline]
|
||||
pub fn consume_with<V>(&mut self, amt: usize, mut visitor: V) -> bool
|
||||
where
|
||||
V: FnMut(&[u8]),
|
||||
{
|
||||
if let Some(claimed) = self.buffer().get(..amt) {
|
||||
visitor(claimed);
|
||||
// If the indexing into self.buffer() succeeds, amt must be a valid increment.
|
||||
self.pos += amt;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unconsume(&mut self, amt: usize) {
|
||||
self.pos = self.pos.saturating_sub(amt);
|
||||
}
|
||||
|
||||
/// Read more bytes into the buffer without discarding any of its contents
|
||||
pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<usize> {
|
||||
let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]);
|
||||
let old_init = self.initialized - self.filled;
|
||||
unsafe {
|
||||
buf.set_init(old_init);
|
||||
}
|
||||
reader.read_buf(buf.unfilled())?;
|
||||
self.filled += buf.len();
|
||||
self.initialized += buf.init_len() - old_init;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
/// Remove bytes that have already been read from the buffer.
|
||||
pub fn backshift(&mut self) {
|
||||
self.buf.copy_within(self.pos..self.filled, 0);
|
||||
self.filled -= self.pos;
|
||||
self.pos = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
|
||||
// If we've reached the end of our internal buffer then we need to fetch
|
||||
// some more data from the reader.
|
||||
// Branch using `>=` instead of the more correct `==`
|
||||
// to tell the compiler that the pos..cap slice is always valid.
|
||||
if self.pos >= self.filled {
|
||||
debug_assert!(self.pos == self.filled);
|
||||
|
||||
let mut buf = BorrowedBuf::from(&mut *self.buf);
|
||||
// SAFETY: `self.filled` bytes will always have been initialized.
|
||||
unsafe {
|
||||
buf.set_init(self.initialized);
|
||||
}
|
||||
|
||||
let result = reader.read_buf(buf.unfilled());
|
||||
|
||||
self.pos = 0;
|
||||
self.filled = buf.len();
|
||||
self.initialized = buf.init_len();
|
||||
|
||||
result?;
|
||||
}
|
||||
Ok(self.buffer())
|
||||
}
|
||||
}
|
||||
680
crates/io/src/io/buffered/bufwriter.rs
Normal file
680
crates/io/src/io/buffered/bufwriter.rs
Normal file
@@ -0,0 +1,680 @@
|
||||
use crate::io::{
|
||||
self, DEFAULT_BUF_SIZE, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write,
|
||||
};
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::{error, fmt, ptr};
|
||||
|
||||
/// Wraps a writer and buffers its output.
|
||||
///
|
||||
/// It can be excessively inefficient to work directly with something that
|
||||
/// implements [`Write`]. For example, every call to
|
||||
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
|
||||
/// `BufWriter<W>` keeps an in-memory buffer of data and writes it to an underlying
|
||||
/// writer in large, infrequent batches.
|
||||
///
|
||||
/// `BufWriter<W>` can improve the speed of programs that make *small* and
|
||||
/// *repeated* write calls to the same file or network socket. It does not
|
||||
/// help when writing very large amounts at once, or writing just one or a few
|
||||
/// times. It also provides no advantage when writing to a destination that is
|
||||
/// in memory, like a <code>[Vec]\<u8></code>.
|
||||
///
|
||||
/// It is critical to call [`flush`] before `BufWriter<W>` is dropped. Though
|
||||
/// dropping will attempt to flush the contents of the buffer, any errors
|
||||
/// that happen in the process of dropping will be ignored. Calling [`flush`]
|
||||
/// ensures that the buffer is empty and thus dropping will not even attempt
|
||||
/// file operations.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Let's write the numbers one through ten to a [`TcpStream`]:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// stream.write(&[i+1]).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Because we're not buffering, we write each one in turn, incurring the
|
||||
/// overhead of a system call per byte written. We can fix this with a
|
||||
/// `BufWriter<W>`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// stream.write(&[i+1]).unwrap();
|
||||
/// }
|
||||
/// stream.flush().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// By wrapping the stream with a `BufWriter<W>`, these ten writes are all grouped
|
||||
/// together by the buffer and will all be written out in one system call when
|
||||
/// the `stream` is flushed.
|
||||
///
|
||||
/// [`TcpStream::write`]: crate::net::TcpStream::write
|
||||
/// [`TcpStream`]: crate::net::TcpStream
|
||||
/// [`flush`]: BufWriter::flush
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct BufWriter<W: ?Sized + Write> {
|
||||
// The buffer. Avoid using this like a normal `Vec` in common code paths.
|
||||
// That is, don't use `buf.push`, `buf.extend_from_slice`, or any other
|
||||
// methods that require bounds checking or the like. This makes an enormous
|
||||
// difference to performance (we may want to stop using a `Vec` entirely).
|
||||
buf: Vec<u8>,
|
||||
// #30888: If the inner writer panics in a call to write, we don't want to
|
||||
// write the buffered data a second time in BufWriter's destructor. This
|
||||
// flag tells the Drop impl if it should skip the flush.
|
||||
panicked: bool,
|
||||
inner: W,
|
||||
}
|
||||
|
||||
impl<W: Write> BufWriter<W> {
|
||||
/// Creates a new `BufWriter<W>` with a default buffer capacity. The default is currently 8 KiB,
|
||||
/// but may change in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(inner: W) -> BufWriter<W> {
|
||||
BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||
}
|
||||
|
||||
pub(crate) fn try_new_buffer() -> io::Result<Vec<u8>> {
|
||||
Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| {
|
||||
io::const_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer")
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn with_buffer(inner: W, buf: Vec<u8>) -> Self {
|
||||
Self { inner, buf, panicked: false }
|
||||
}
|
||||
|
||||
/// Creates a new `BufWriter<W>` with at least the specified buffer capacity.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a buffer with a buffer of at least a hundred bytes.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let stream = TcpStream::connect("127.0.0.1:34254").unwrap();
|
||||
/// let mut buffer = BufWriter::with_capacity(100, stream);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize, inner: W) -> BufWriter<W> {
|
||||
BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false }
|
||||
}
|
||||
|
||||
/// Unwraps this `BufWriter<W>`, returning the underlying writer.
|
||||
///
|
||||
/// The buffer is written out before returning the writer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An [`Err`] will be returned if an error occurs while flushing the buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // unwrap the TcpStream and flush the buffer
|
||||
/// let stream = buffer.into_inner().unwrap();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
|
||||
match self.flush_buf() {
|
||||
Err(e) => Err(IntoInnerError::new(self, e)),
|
||||
Ok(()) => Ok(self.into_parts().0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Disassembles this `BufWriter<W>`, returning the underlying writer, and any buffered but
|
||||
/// unwritten data.
|
||||
///
|
||||
/// If the underlying writer panicked, it is not known what portion of the data was written.
|
||||
/// In this case, we return `WriterPanicked` for the buffered data (from which the buffer
|
||||
/// contents can still be recovered).
|
||||
///
|
||||
/// `into_parts` makes no attempt to flush data and cannot fail.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::{BufWriter, Write};
|
||||
///
|
||||
/// let mut buffer = [0u8; 10];
|
||||
/// let mut stream = BufWriter::new(buffer.as_mut());
|
||||
/// write!(stream, "too much data").unwrap();
|
||||
/// stream.flush().expect_err("it doesn't fit");
|
||||
/// let (recovered_writer, buffered_data) = stream.into_parts();
|
||||
/// assert_eq!(recovered_writer.len(), 0);
|
||||
/// assert_eq!(&buffered_data.unwrap(), b"ata");
|
||||
/// ```
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub fn into_parts(self) -> (W, Result<Vec<u8>, WriterPanicked>) {
|
||||
let mut this = ManuallyDrop::new(self);
|
||||
let buf = mem::take(&mut this.buf);
|
||||
let buf = if !this.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
|
||||
|
||||
// SAFETY: double-drops are prevented by putting `this` in a ManuallyDrop that is never dropped
|
||||
let inner = unsafe { ptr::read(&this.inner) };
|
||||
|
||||
(inner, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Write> BufWriter<W> {
|
||||
/// Send data in our local buffer into the inner writer, looping as
|
||||
/// necessary until either it's all been sent or an error occurs.
|
||||
///
|
||||
/// Because all the data in the buffer has been reported to our owner as
|
||||
/// "successfully written" (by returning nonzero success values from
|
||||
/// `write`), any 0-length writes from `inner` must be reported as i/o
|
||||
/// errors from this method.
|
||||
pub(in crate::io) fn flush_buf(&mut self) -> io::Result<()> {
|
||||
/// Helper struct to ensure the buffer is updated after all the writes
|
||||
/// are complete. It tracks the number of written bytes and drains them
|
||||
/// all from the front of the buffer when dropped.
|
||||
struct BufGuard<'a> {
|
||||
buffer: &'a mut Vec<u8>,
|
||||
written: usize,
|
||||
}
|
||||
|
||||
impl<'a> BufGuard<'a> {
|
||||
fn new(buffer: &'a mut Vec<u8>) -> Self {
|
||||
Self { buffer, written: 0 }
|
||||
}
|
||||
|
||||
/// The unwritten part of the buffer
|
||||
fn remaining(&self) -> &[u8] {
|
||||
&self.buffer[self.written..]
|
||||
}
|
||||
|
||||
/// Flag some bytes as removed from the front of the buffer
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.written += amt;
|
||||
}
|
||||
|
||||
/// true if all of the bytes have been written
|
||||
fn done(&self) -> bool {
|
||||
self.written >= self.buffer.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BufGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
if self.written > 0 {
|
||||
self.buffer.drain(..self.written);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut guard = BufGuard::new(&mut self.buf);
|
||||
while !guard.done() {
|
||||
self.panicked = true;
|
||||
let r = self.inner.write(guard.remaining());
|
||||
self.panicked = false;
|
||||
|
||||
match r {
|
||||
Ok(0) => {
|
||||
return Err(io::const_error!(
|
||||
ErrorKind::WriteZero,
|
||||
"failed to write the buffered data",
|
||||
));
|
||||
}
|
||||
Ok(n) => guard.consume(n),
|
||||
Err(ref e) if e.is_interrupted() => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Buffer some data without flushing it, regardless of the size of the
|
||||
/// data. Writes as much as possible without exceeding capacity. Returns
|
||||
/// the number of bytes written.
|
||||
pub(super) fn write_to_buf(&mut self, buf: &[u8]) -> usize {
|
||||
let available = self.spare_capacity();
|
||||
let amt_to_buffer = available.min(buf.len());
|
||||
|
||||
// SAFETY: `amt_to_buffer` is <= buffer's spare capacity by construction.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(&buf[..amt_to_buffer]);
|
||||
}
|
||||
|
||||
amt_to_buffer
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying writer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // we can use reference just like buffer
|
||||
/// let reference = buffer.get_ref();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_ref(&self) -> &W {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying writer.
|
||||
///
|
||||
/// It is inadvisable to directly write to the underlying writer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // we can use reference just like buffer
|
||||
/// let reference = buffer.get_mut();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_mut(&mut self) -> &mut W {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns a reference to the internally buffered data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // See how many bytes are currently buffered
|
||||
/// let bytes_buffered = buf_writer.buffer().len();
|
||||
/// ```
|
||||
#[stable(feature = "bufreader_buffer", since = "1.37.0")]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the internal buffer.
|
||||
///
|
||||
/// This can be used to write data directly into the buffer without triggering writers
|
||||
/// to the underlying writer.
|
||||
///
|
||||
/// That the buffer is a `Vec` is an implementation detail.
|
||||
/// Callers should not modify the capacity as there currently is no public API to do so
|
||||
/// and thus any capacity changes would be unexpected by the user.
|
||||
pub(in crate::io) fn buffer_mut(&mut self) -> &mut Vec<u8> {
|
||||
&mut self.buf
|
||||
}
|
||||
|
||||
/// Returns the number of bytes the internal buffer can hold without flushing.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // Check the capacity of the inner buffer
|
||||
/// let capacity = buf_writer.capacity();
|
||||
/// // Calculate how many bytes can be written without flushing
|
||||
/// let without_flush = capacity - buf_writer.buffer().len();
|
||||
/// ```
|
||||
#[stable(feature = "buffered_io_capacity", since = "1.46.0")]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buf.capacity()
|
||||
}
|
||||
|
||||
// Ensure this function does not get inlined into `write`, so that it
|
||||
// remains inlineable and its common path remains as short as possible.
|
||||
// If this function ends up being called frequently relative to `write`,
|
||||
// it's likely a sign that the client is using an improperly sized buffer
|
||||
// or their write patterns are somewhat pathological.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn write_cold(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if buf.len() > self.spare_capacity() {
|
||||
self.flush_buf()?;
|
||||
}
|
||||
|
||||
// Why not len > capacity? To avoid a needless trip through the buffer when the input
|
||||
// exactly fills it. We'd just need to flush it to the underlying writer anyway.
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write(buf);
|
||||
self.panicked = false;
|
||||
r
|
||||
} else {
|
||||
// Write to the buffer. In this case, we write to the buffer even if it fills it
|
||||
// exactly. Doing otherwise would mean flushing the buffer, then writing this
|
||||
// input to the inner writer, which in many cases would be a worse strategy.
|
||||
|
||||
// SAFETY: There was either enough spare capacity already, or there wasn't and we
|
||||
// flushed the buffer to ensure that there is. In the latter case, we know that there
|
||||
// is because flushing ensured that our entire buffer is spare capacity, and we entered
|
||||
// this block because the input buffer length is less than that capacity. In either
|
||||
// case, it's safe to write the input buffer to our buffer.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure this function does not get inlined into `write_all`, so that it
|
||||
// remains inlineable and its common path remains as short as possible.
|
||||
// If this function ends up being called frequently relative to `write_all`,
|
||||
// it's likely a sign that the client is using an improperly sized buffer
|
||||
// or their write patterns are somewhat pathological.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn write_all_cold(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
// Normally, `write_all` just calls `write` in a loop. We can do better
|
||||
// by calling `self.get_mut().write_all()` directly, which avoids
|
||||
// round trips through the buffer in the event of a series of partial
|
||||
// writes in some circumstances.
|
||||
|
||||
if buf.len() > self.spare_capacity() {
|
||||
self.flush_buf()?;
|
||||
}
|
||||
|
||||
// Why not len > capacity? To avoid a needless trip through the buffer when the input
|
||||
// exactly fills it. We'd just need to flush it to the underlying writer anyway.
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write_all(buf);
|
||||
self.panicked = false;
|
||||
r
|
||||
} else {
|
||||
// Write to the buffer. In this case, we write to the buffer even if it fills it
|
||||
// exactly. Doing otherwise would mean flushing the buffer, then writing this
|
||||
// input to the inner writer, which in many cases would be a worse strategy.
|
||||
|
||||
// SAFETY: There was either enough spare capacity already, or there wasn't and we
|
||||
// flushed the buffer to ensure that there is. In the latter case, we know that there
|
||||
// is because flushing ensured that our entire buffer is spare capacity, and we entered
|
||||
// this block because the input buffer length is less than that capacity. In either
|
||||
// case, it's safe to write the input buffer to our buffer.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Requires `buf.len() <= self.buf.capacity() - self.buf.len()`,
|
||||
// i.e., that input buffer length is less than or equal to spare capacity.
|
||||
#[inline]
|
||||
unsafe fn write_to_buffer_unchecked(&mut self, buf: &[u8]) {
|
||||
debug_assert!(buf.len() <= self.spare_capacity());
|
||||
let old_len = self.buf.len();
|
||||
let buf_len = buf.len();
|
||||
let src = buf.as_ptr();
|
||||
unsafe {
|
||||
let dst = self.buf.as_mut_ptr().add(old_len);
|
||||
ptr::copy_nonoverlapping(src, dst, buf_len);
|
||||
self.buf.set_len(old_len + buf_len);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn spare_capacity(&self) -> usize {
|
||||
self.buf.capacity() - self.buf.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
/// Error returned for the buffered data from `BufWriter::into_parts`, when the underlying
|
||||
/// writer has previously panicked. Contains the (possibly partly written) buffered data.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::{self, BufWriter, Write};
|
||||
/// use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
///
|
||||
/// struct PanickingWriter;
|
||||
/// impl Write for PanickingWriter {
|
||||
/// fn write(&mut self, buf: &[u8]) -> io::Result<usize> { panic!() }
|
||||
/// fn flush(&mut self) -> io::Result<()> { panic!() }
|
||||
/// }
|
||||
///
|
||||
/// let mut stream = BufWriter::new(PanickingWriter);
|
||||
/// write!(stream, "some data").unwrap();
|
||||
/// let result = catch_unwind(AssertUnwindSafe(|| {
|
||||
/// stream.flush().unwrap()
|
||||
/// }));
|
||||
/// assert!(result.is_err());
|
||||
/// let (recovered_writer, buffered_data) = stream.into_parts();
|
||||
/// assert!(matches!(recovered_writer, PanickingWriter));
|
||||
/// assert_eq!(buffered_data.unwrap_err().into_inner(), b"some data");
|
||||
/// ```
|
||||
pub struct WriterPanicked {
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl WriterPanicked {
|
||||
/// Returns the perhaps-unwritten data. Some of this data may have been written by the
|
||||
/// panicking call(s) to the underlying writer, so simply writing it again is not a good idea.
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub fn into_inner(self) -> Vec<u8> {
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
impl error::Error for WriterPanicked {}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
impl fmt::Display for WriterPanicked {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"BufWriter inner writer panicked, what data remains unwritten is not known".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
impl fmt::Debug for WriterPanicked {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("WriterPanicked")
|
||||
.field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> Write for BufWriter<W> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// Use < instead of <= to avoid a needless trip through the buffer in some cases.
|
||||
// See `write_cold` for details.
|
||||
if buf.len() < self.spare_capacity() {
|
||||
// SAFETY: safe by above conditional.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
} else {
|
||||
self.write_cold(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
// Use < instead of <= to avoid a needless trip through the buffer in some cases.
|
||||
// See `write_all_cold` for details.
|
||||
if buf.len() < self.spare_capacity() {
|
||||
// SAFETY: safe by above conditional.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
self.write_all_cold(buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
// FIXME: Consider applying `#[inline]` / `#[inline(never)]` optimizations already applied
|
||||
// to `write` and `write_all`. The performance benefits can be significant. See #79930.
|
||||
if self.get_ref().is_write_vectored() {
|
||||
// We have to handle the possibility that the total length of the buffers overflows
|
||||
// `usize` (even though this can only happen if multiple `IoSlice`s reference the
|
||||
// same underlying buffer, as otherwise the buffers wouldn't fit in memory). If the
|
||||
// computation overflows, then surely the input cannot fit in our buffer, so we forward
|
||||
// to the inner writer's `write_vectored` method to let it handle it appropriately.
|
||||
let mut saturated_total_len: usize = 0;
|
||||
|
||||
for buf in bufs {
|
||||
saturated_total_len = saturated_total_len.saturating_add(buf.len());
|
||||
|
||||
if saturated_total_len > self.spare_capacity() && !self.buf.is_empty() {
|
||||
// Flush if the total length of the input exceeds our buffer's spare capacity.
|
||||
// If we would have overflowed, this condition also holds, and we need to flush.
|
||||
self.flush_buf()?;
|
||||
}
|
||||
|
||||
if saturated_total_len >= self.buf.capacity() {
|
||||
// Forward to our inner writer if the total length of the input is greater than or
|
||||
// equal to our buffer capacity. If we would have overflowed, this condition also
|
||||
// holds, and we punt to the inner writer.
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write_vectored(bufs);
|
||||
self.panicked = false;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// `saturated_total_len < self.buf.capacity()` implies that we did not saturate.
|
||||
|
||||
// SAFETY: We checked whether or not the spare capacity was large enough above. If
|
||||
// it was, then we're safe already. If it wasn't, we flushed, making sufficient
|
||||
// room for any input <= the buffer size, which includes this input.
|
||||
unsafe {
|
||||
bufs.iter().for_each(|b| self.write_to_buffer_unchecked(b));
|
||||
};
|
||||
|
||||
Ok(saturated_total_len)
|
||||
} else {
|
||||
let mut iter = bufs.iter();
|
||||
let mut total_written = if let Some(buf) = iter.by_ref().find(|&buf| !buf.is_empty()) {
|
||||
// This is the first non-empty slice to write, so if it does
|
||||
// not fit in the buffer, we still get to flush and proceed.
|
||||
if buf.len() > self.spare_capacity() {
|
||||
self.flush_buf()?;
|
||||
}
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
// The slice is at least as large as the buffering capacity,
|
||||
// so it's better to write it directly, bypassing the buffer.
|
||||
self.panicked = true;
|
||||
let r = self.get_mut().write(buf);
|
||||
self.panicked = false;
|
||||
return r;
|
||||
} else {
|
||||
// SAFETY: We checked whether or not the spare capacity was large enough above.
|
||||
// If it was, then we're safe already. If it wasn't, we flushed, making
|
||||
// sufficient room for any input <= the buffer size, which includes this input.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
buf.len()
|
||||
}
|
||||
} else {
|
||||
return Ok(0);
|
||||
};
|
||||
debug_assert!(total_written != 0);
|
||||
for buf in iter {
|
||||
if buf.len() <= self.spare_capacity() {
|
||||
// SAFETY: safe by above conditional.
|
||||
unsafe {
|
||||
self.write_to_buffer_unchecked(buf);
|
||||
}
|
||||
|
||||
// This cannot overflow `usize`. If we are here, we've written all of the bytes
|
||||
// so far to our buffer, and we've ensured that we never exceed the buffer's
|
||||
// capacity. Therefore, `total_written` <= `self.buf.capacity()` <= `usize::MAX`.
|
||||
total_written += buf.len();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(total_written)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.flush_buf().and_then(|()| self.get_mut().flush())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> fmt::Debug for BufWriter<W>
|
||||
where
|
||||
W: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("BufWriter")
|
||||
.field("writer", &&self.inner)
|
||||
.field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity()))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write + Seek> Seek for BufWriter<W> {
|
||||
/// Seek to the offset, in bytes, in the underlying writer.
|
||||
///
|
||||
/// Seeking always writes out the internal buffer before seeking.
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
self.flush_buf()?;
|
||||
self.get_mut().seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> Drop for BufWriter<W> {
|
||||
fn drop(&mut self) {
|
||||
if !self.panicked {
|
||||
// dtors should not panic, so we ignore a failed flush
|
||||
let _r = self.flush_buf();
|
||||
}
|
||||
}
|
||||
}
|
||||
235
crates/io/src/io/buffered/linewriter.rs
Normal file
235
crates/io/src/io/buffered/linewriter.rs
Normal file
@@ -0,0 +1,235 @@
|
||||
use crate::fmt;
|
||||
use crate::io::buffered::LineWriterShim;
|
||||
use crate::io::{self, BufWriter, IntoInnerError, IoSlice, Write};
|
||||
|
||||
/// Wraps a writer and buffers output to it, flushing whenever a newline
|
||||
/// (`0x0a`, `'\n'`) is detected.
|
||||
///
|
||||
/// The [`BufWriter`] struct wraps a writer and buffers its output.
|
||||
/// But it only does this batched write when it goes out of scope, or when the
|
||||
/// internal buffer is full. Sometimes, you'd prefer to write each line as it's
|
||||
/// completed, rather than the entire buffer at once. Enter `LineWriter`. It
|
||||
/// does exactly that.
|
||||
///
|
||||
/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the
|
||||
/// `LineWriter` goes out of scope or when its internal buffer is full.
|
||||
///
|
||||
/// If there's still a partial line in the buffer when the `LineWriter` is
|
||||
/// dropped, it will flush those contents.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// We can use `LineWriter` to write one line at a time, significantly
|
||||
/// reducing the number of actual writes to the file.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::{self, File};
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let road_not_taken = b"I shall be telling this with a sigh
|
||||
/// Somewhere ages and ages hence:
|
||||
/// Two roads diverged in a wood, and I -
|
||||
/// I took the one less traveled by,
|
||||
/// And that has made all the difference.";
|
||||
///
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let mut file = LineWriter::new(file);
|
||||
///
|
||||
/// file.write_all(b"I shall be telling this with a sigh")?;
|
||||
///
|
||||
/// // No bytes are written until a newline is encountered (or
|
||||
/// // the internal buffer is filled).
|
||||
/// assert_eq!(fs::read_to_string("poem.txt")?, "");
|
||||
/// file.write_all(b"\n")?;
|
||||
/// assert_eq!(
|
||||
/// fs::read_to_string("poem.txt")?,
|
||||
/// "I shall be telling this with a sigh\n",
|
||||
/// );
|
||||
///
|
||||
/// // Write the rest of the poem.
|
||||
/// file.write_all(b"Somewhere ages and ages hence:
|
||||
/// Two roads diverged in a wood, and I -
|
||||
/// I took the one less traveled by,
|
||||
/// And that has made all the difference.")?;
|
||||
///
|
||||
/// // The last line of the poem doesn't end in a newline, so
|
||||
/// // we have to flush or drop the `LineWriter` to finish
|
||||
/// // writing.
|
||||
/// file.flush()?;
|
||||
///
|
||||
/// // Confirm the whole poem was written.
|
||||
/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct LineWriter<W: ?Sized + Write> {
|
||||
inner: BufWriter<W>,
|
||||
}
|
||||
|
||||
impl<W: Write> LineWriter<W> {
|
||||
/// Creates a new `LineWriter`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let file = LineWriter::new(file);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new(inner: W) -> LineWriter<W> {
|
||||
// Lines typically aren't that long, don't use a giant buffer
|
||||
LineWriter::with_capacity(1024, inner)
|
||||
}
|
||||
|
||||
/// Creates a new `LineWriter` with at least the specified capacity for the
|
||||
/// internal buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let file = LineWriter::with_capacity(100, file);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize, inner: W) -> LineWriter<W> {
|
||||
LineWriter { inner: BufWriter::with_capacity(capacity, inner) }
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying writer.
|
||||
///
|
||||
/// Caution must be taken when calling methods on the mutable reference
|
||||
/// returned as extra writes could corrupt the output stream.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let mut file = LineWriter::new(file);
|
||||
///
|
||||
/// // we can use reference just like file
|
||||
/// let reference = file.get_mut();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_mut(&mut self) -> &mut W {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Unwraps this `LineWriter`, returning the underlying writer.
|
||||
///
|
||||
/// The internal buffer is written out before returning the writer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An [`Err`] will be returned if an error occurs while flushing the buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
///
|
||||
/// let writer: LineWriter<File> = LineWriter::new(file);
|
||||
///
|
||||
/// let file: File = writer.into_inner()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
|
||||
self.inner.into_inner().map_err(|err| err.new_wrapped(|inner| LineWriter { inner }))
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Write> LineWriter<W> {
|
||||
/// Gets a reference to the underlying writer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::LineWriter;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let file = File::create("poem.txt")?;
|
||||
/// let file = LineWriter::new(file);
|
||||
///
|
||||
/// let reference = file.get_ref();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn get_ref(&self) -> &W {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> Write for LineWriter<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
LineWriterShim::new(&mut self.inner).write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
LineWriterShim::new(&mut self.inner).write_vectored(bufs)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.inner.is_write_vectored()
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
LineWriterShim::new(&mut self.inner).write_all(buf)
|
||||
}
|
||||
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
LineWriterShim::new(&mut self.inner).write_all_vectored(bufs)
|
||||
}
|
||||
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
LineWriterShim::new(&mut self.inner).write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: ?Sized + Write> fmt::Debug for LineWriter<W>
|
||||
where
|
||||
W: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_struct("LineWriter")
|
||||
.field("writer", &self.get_ref())
|
||||
.field(
|
||||
"buffer",
|
||||
&format_args!("{}/{}", self.inner.buffer().len(), self.inner.capacity()),
|
||||
)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
297
crates/io/src/io/buffered/linewritershim.rs
Normal file
297
crates/io/src/io/buffered/linewritershim.rs
Normal file
@@ -0,0 +1,297 @@
|
||||
use core::slice::memchr;
|
||||
|
||||
use crate::io::{self, BufWriter, IoSlice, Write};
|
||||
|
||||
/// Private helper struct for implementing the line-buffered writing logic.
|
||||
///
|
||||
/// This shim temporarily wraps a BufWriter, and uses its internals to
|
||||
/// implement a line-buffered writer (specifically by using the internal
|
||||
/// methods like write_to_buf and flush_buf). In this way, a more
|
||||
/// efficient abstraction can be created than one that only had access to
|
||||
/// `write` and `flush`, without needlessly duplicating a lot of the
|
||||
/// implementation details of BufWriter. This also allows existing
|
||||
/// `BufWriters` to be temporarily given line-buffering logic; this is what
|
||||
/// enables Stdout to be alternately in line-buffered or block-buffered mode.
|
||||
#[derive(Debug)]
|
||||
pub struct LineWriterShim<'a, W: ?Sized + Write> {
|
||||
buffer: &'a mut BufWriter<W>,
|
||||
}
|
||||
|
||||
impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> {
|
||||
pub fn new(buffer: &'a mut BufWriter<W>) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
|
||||
/// Gets a reference to the inner writer (that is, the writer
|
||||
/// wrapped by the BufWriter).
|
||||
fn inner(&self) -> &W {
|
||||
self.buffer.get_ref()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the inner writer (that is, the writer
|
||||
/// wrapped by the BufWriter). Be careful with this writer, as writes to
|
||||
/// it will bypass the buffer.
|
||||
fn inner_mut(&mut self) -> &mut W {
|
||||
self.buffer.get_mut()
|
||||
}
|
||||
|
||||
/// Gets the content currently buffered in self.buffer
|
||||
fn buffered(&self) -> &[u8] {
|
||||
self.buffer.buffer()
|
||||
}
|
||||
|
||||
/// Flushes the buffer iff the last byte is a newline (indicating that an
|
||||
/// earlier write only succeeded partially, and we want to retry flushing
|
||||
/// the buffered line before continuing with a subsequent write).
|
||||
fn flush_if_completed_line(&mut self) -> io::Result<()> {
|
||||
match self.buffered().last().copied() {
|
||||
Some(b'\n') => self.buffer.flush_buf(),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> {
|
||||
/// Writes some data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// the last newline is sent directly to the underlying writer, and data
|
||||
/// after it is buffered. Returns the number of bytes written.
|
||||
///
|
||||
/// This function operates on a "best effort basis"; in keeping with the
|
||||
/// convention of `Write::write`, it makes at most one attempt to write
|
||||
/// new data to the underlying writer. If that write only reports a partial
|
||||
/// success, the remaining data will be buffered.
|
||||
///
|
||||
/// Because this function attempts to send completed lines to the underlying
|
||||
/// writer, it will also flush the existing buffer if it ends with a
|
||||
/// newline, even if the incoming data does not contain any newlines.
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let newline_idx = match memchr::memrchr(b'\n', buf) {
|
||||
// If there are no new newlines (that is, if this write is less than
|
||||
// one line), just do a regular buffered write (which may flush if
|
||||
// we exceed the inner buffer's size)
|
||||
None => {
|
||||
self.flush_if_completed_line()?;
|
||||
return self.buffer.write(buf);
|
||||
}
|
||||
// Otherwise, arrange for the lines to be written directly to the
|
||||
// inner writer.
|
||||
Some(newline_idx) => newline_idx + 1,
|
||||
};
|
||||
|
||||
// Flush existing content to prepare for our write. We have to do this
|
||||
// before attempting to write `buf` in order to maintain consistency;
|
||||
// if we add `buf` to the buffer then try to flush it all at once,
|
||||
// we're obligated to return Ok(), which would mean suppressing any
|
||||
// errors that occur during flush.
|
||||
self.buffer.flush_buf()?;
|
||||
|
||||
// This is what we're going to try to write directly to the inner
|
||||
// writer. The rest will be buffered, if nothing goes wrong.
|
||||
let lines = &buf[..newline_idx];
|
||||
|
||||
// Write `lines` directly to the inner writer. In keeping with the
|
||||
// `write` convention, make at most one attempt to add new (unbuffered)
|
||||
// data. Because this write doesn't touch the BufWriter state directly,
|
||||
// and the buffer is known to be empty, we don't need to worry about
|
||||
// self.buffer.panicked here.
|
||||
let flushed = self.inner_mut().write(lines)?;
|
||||
|
||||
// If buffer returns Ok(0), propagate that to the caller without
|
||||
// doing additional buffering; otherwise we're just guaranteeing
|
||||
// an "ErrorKind::WriteZero" later.
|
||||
if flushed == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Now that the write has succeeded, buffer the rest (or as much of
|
||||
// the rest as possible). If there were any unwritten newlines, we
|
||||
// only buffer out to the last unwritten newline that fits in the
|
||||
// buffer; this helps prevent flushing partial lines on subsequent
|
||||
// calls to LineWriterShim::write.
|
||||
|
||||
// Handle the cases in order of most-common to least-common, under
|
||||
// the presumption that most writes succeed in totality, and that most
|
||||
// writes are smaller than the buffer.
|
||||
// - Is this a partial line (ie, no newlines left in the unwritten tail)
|
||||
// - If not, does the data out to the last unwritten newline fit in
|
||||
// the buffer?
|
||||
// - If not, scan for the last newline that *does* fit in the buffer
|
||||
let tail = if flushed >= newline_idx {
|
||||
let tail = &buf[flushed..];
|
||||
// Avoid unnecessary short writes by not splitting the remaining
|
||||
// bytes if they're larger than the buffer.
|
||||
// They can be written in full by the next call to write.
|
||||
if tail.len() >= self.buffer.capacity() {
|
||||
return Ok(flushed);
|
||||
}
|
||||
tail
|
||||
} else if newline_idx - flushed <= self.buffer.capacity() {
|
||||
&buf[flushed..newline_idx]
|
||||
} else {
|
||||
let scan_area = &buf[flushed..];
|
||||
let scan_area = &scan_area[..self.buffer.capacity()];
|
||||
match memchr::memrchr(b'\n', scan_area) {
|
||||
Some(newline_idx) => &scan_area[..newline_idx + 1],
|
||||
None => scan_area,
|
||||
}
|
||||
};
|
||||
|
||||
let buffered = self.buffer.write_to_buf(tail);
|
||||
Ok(flushed + buffered)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.buffer.flush()
|
||||
}
|
||||
|
||||
/// Writes some vectored data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// and including the buffer containing the last newline is sent directly to
|
||||
/// the inner writer, and the data after it is buffered. Returns the number
|
||||
/// of bytes written.
|
||||
///
|
||||
/// This function operates on a "best effort basis"; in keeping with the
|
||||
/// convention of `Write::write`, it makes at most one attempt to write
|
||||
/// new data to the underlying writer.
|
||||
///
|
||||
/// Because this function attempts to send completed lines to the underlying
|
||||
/// writer, it will also flush the existing buffer if it contains any
|
||||
/// newlines.
|
||||
///
|
||||
/// Because sorting through an array of `IoSlice` can be a bit convoluted,
|
||||
/// This method differs from write in the following ways:
|
||||
///
|
||||
/// - It attempts to write the full content of all the buffers up to and
|
||||
/// including the one containing the last newline. This means that it
|
||||
/// may attempt to write a partial line, that buffer has data past the
|
||||
/// newline.
|
||||
/// - If the write only reports partial success, it does not attempt to
|
||||
/// find the precise location of the written bytes and buffer the rest.
|
||||
///
|
||||
/// If the underlying vector doesn't support vectored writing, we instead
|
||||
/// simply write the first non-empty buffer with `write`. This way, we
|
||||
/// get the benefits of more granular partial-line handling without losing
|
||||
/// anything in efficiency
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
// If there's no specialized behavior for write_vectored, just use
|
||||
// write. This has the benefit of more granular partial-line handling.
|
||||
if !self.is_write_vectored() {
|
||||
return match bufs.iter().find(|buf| !buf.is_empty()) {
|
||||
Some(buf) => self.write(buf),
|
||||
None => Ok(0),
|
||||
};
|
||||
}
|
||||
|
||||
// Find the buffer containing the last newline
|
||||
// FIXME: This is overly slow if there are very many bufs and none contain
|
||||
// newlines. e.g. writev() on Linux only writes up to 1024 slices, so
|
||||
// scanning the rest is wasted effort. This makes write_all_vectored()
|
||||
// quadratic.
|
||||
let last_newline_buf_idx = bufs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i));
|
||||
|
||||
// If there are no new newlines (that is, if this write is less than
|
||||
// one line), just do a regular buffered write
|
||||
let last_newline_buf_idx = match last_newline_buf_idx {
|
||||
// No newlines; just do a normal buffered write
|
||||
None => {
|
||||
self.flush_if_completed_line()?;
|
||||
return self.buffer.write_vectored(bufs);
|
||||
}
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
// Flush existing content to prepare for our write
|
||||
self.buffer.flush_buf()?;
|
||||
|
||||
// This is what we're going to try to write directly to the inner
|
||||
// writer. The rest will be buffered, if nothing goes wrong.
|
||||
let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1);
|
||||
|
||||
// Write `lines` directly to the inner writer. In keeping with the
|
||||
// `write` convention, make at most one attempt to add new (unbuffered)
|
||||
// data. Because this write doesn't touch the BufWriter state directly,
|
||||
// and the buffer is known to be empty, we don't need to worry about
|
||||
// self.panicked here.
|
||||
let flushed = self.inner_mut().write_vectored(lines)?;
|
||||
|
||||
// If inner returns Ok(0), propagate that to the caller without
|
||||
// doing additional buffering; otherwise we're just guaranteeing
|
||||
// an "ErrorKind::WriteZero" later.
|
||||
if flushed == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Don't try to reconstruct the exact amount written; just bail
|
||||
// in the event of a partial write
|
||||
let mut lines_len: usize = 0;
|
||||
for buf in lines {
|
||||
// With overlapping/duplicate slices the total length may in theory
|
||||
// exceed usize::MAX
|
||||
lines_len = lines_len.saturating_add(buf.len());
|
||||
if flushed < lines_len {
|
||||
return Ok(flushed);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the write has succeeded, buffer the rest (or as much of the
|
||||
// rest as possible)
|
||||
let buffered: usize = tail
|
||||
.iter()
|
||||
.filter(|buf| !buf.is_empty())
|
||||
.map(|buf| self.buffer.write_to_buf(buf))
|
||||
.take_while(|&n| n > 0)
|
||||
.sum();
|
||||
|
||||
Ok(flushed + buffered)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.inner().is_write_vectored()
|
||||
}
|
||||
|
||||
/// Writes some data into this BufWriter with line buffering.
|
||||
///
|
||||
/// This means that, if any newlines are present in the data, the data up to
|
||||
/// the last newline is sent directly to the underlying writer, and data
|
||||
/// after it is buffered.
|
||||
///
|
||||
/// Because this function attempts to send completed lines to the underlying
|
||||
/// writer, it will also flush the existing buffer if it contains any
|
||||
/// newlines, even if the incoming data does not contain any newlines.
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
match memchr::memrchr(b'\n', buf) {
|
||||
// If there are no new newlines (that is, if this write is less than
|
||||
// one line), just do a regular buffered write (which may flush if
|
||||
// we exceed the inner buffer's size)
|
||||
None => {
|
||||
self.flush_if_completed_line()?;
|
||||
self.buffer.write_all(buf)
|
||||
}
|
||||
Some(newline_idx) => {
|
||||
let (lines, tail) = buf.split_at(newline_idx + 1);
|
||||
|
||||
if self.buffered().is_empty() {
|
||||
self.inner_mut().write_all(lines)?;
|
||||
} else {
|
||||
// If there is any buffered data, we add the incoming lines
|
||||
// to that buffer before flushing, which saves us at least
|
||||
// one write call. We can't really do this with `write`,
|
||||
// since we can't do this *and* not suppress errors *and*
|
||||
// report a consistent state to the caller in a return
|
||||
// value, but here in write_all it's fine.
|
||||
self.buffer.write_all(lines)?;
|
||||
self.buffer.flush_buf()?;
|
||||
}
|
||||
|
||||
self.buffer.write_all(tail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
187
crates/io/src/io/buffered/mod.rs
Normal file
187
crates/io/src/io/buffered/mod.rs
Normal file
@@ -0,0 +1,187 @@
|
||||
//! Buffering wrappers for I/O traits
|
||||
|
||||
mod bufreader;
|
||||
mod bufwriter;
|
||||
mod linewriter;
|
||||
mod linewritershim;
|
||||
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub use bufwriter::WriterPanicked;
|
||||
use linewritershim::LineWriterShim;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter};
|
||||
use crate::io::Error;
|
||||
use crate::{error, fmt};
|
||||
|
||||
/// An error returned by [`BufWriter::into_inner`] which combines an error that
|
||||
/// happened while writing out the buffer, and the buffered writer object
|
||||
/// which may be used to recover from the condition.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // do stuff with the stream
|
||||
///
|
||||
/// // we want to get our `TcpStream` back, so let's try:
|
||||
///
|
||||
/// let stream = match stream.into_inner() {
|
||||
/// Ok(s) => s,
|
||||
/// Err(e) => {
|
||||
/// // Here, e is an IntoInnerError
|
||||
/// panic!("An error occurred");
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct IntoInnerError<W>(W, Error);
|
||||
|
||||
impl<W> IntoInnerError<W> {
|
||||
/// Constructs a new IntoInnerError
|
||||
fn new(writer: W, error: Error) -> Self {
|
||||
Self(writer, error)
|
||||
}
|
||||
|
||||
/// Helper to construct a new IntoInnerError; intended to help with
|
||||
/// adapters that wrap other adapters
|
||||
fn new_wrapped<W2>(self, f: impl FnOnce(W) -> W2) -> IntoInnerError<W2> {
|
||||
let Self(writer, error) = self;
|
||||
IntoInnerError::new(f(writer), error)
|
||||
}
|
||||
|
||||
/// Returns the error which caused the call to [`BufWriter::into_inner()`]
|
||||
/// to fail.
|
||||
///
|
||||
/// This error was returned when attempting to write the internal buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // do stuff with the stream
|
||||
///
|
||||
/// // we want to get our `TcpStream` back, so let's try:
|
||||
///
|
||||
/// let stream = match stream.into_inner() {
|
||||
/// Ok(s) => s,
|
||||
/// Err(e) => {
|
||||
/// // Here, e is an IntoInnerError, let's log the inner error.
|
||||
/// //
|
||||
/// // We'll just 'log' to stdout for this example.
|
||||
/// println!("{}", e.error());
|
||||
///
|
||||
/// panic!("An unexpected error occurred.");
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn error(&self) -> &Error {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Returns the buffered writer instance which generated the error.
|
||||
///
|
||||
/// The returned object can be used for error recovery, such as
|
||||
/// re-inspecting the buffer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::BufWriter;
|
||||
/// use std::net::TcpStream;
|
||||
///
|
||||
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
|
||||
///
|
||||
/// // do stuff with the stream
|
||||
///
|
||||
/// // we want to get our `TcpStream` back, so let's try:
|
||||
///
|
||||
/// let stream = match stream.into_inner() {
|
||||
/// Ok(s) => s,
|
||||
/// Err(e) => {
|
||||
/// // Here, e is an IntoInnerError, let's re-examine the buffer:
|
||||
/// let buffer = e.into_inner();
|
||||
///
|
||||
/// // do stuff to try to recover
|
||||
///
|
||||
/// // afterwards, let's just return the stream
|
||||
/// buffer.into_inner().unwrap()
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> W {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Consumes the [`IntoInnerError`] and returns the error which caused the call to
|
||||
/// [`BufWriter::into_inner()`] to fail. Unlike `error`, this can be used to
|
||||
/// obtain ownership of the underlying error.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use std::io::{BufWriter, ErrorKind, Write};
|
||||
///
|
||||
/// let mut not_enough_space = [0u8; 10];
|
||||
/// let mut stream = BufWriter::new(not_enough_space.as_mut());
|
||||
/// write!(stream, "this cannot be actually written").unwrap();
|
||||
/// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small");
|
||||
/// let err = into_inner_err.into_error();
|
||||
/// assert_eq!(err.kind(), ErrorKind::WriteZero);
|
||||
/// ```
|
||||
#[stable(feature = "io_into_inner_error_parts", since = "1.55.0")]
|
||||
pub fn into_error(self) -> Error {
|
||||
self.1
|
||||
}
|
||||
|
||||
/// Consumes the [`IntoInnerError`] and returns the error which caused the call to
|
||||
/// [`BufWriter::into_inner()`] to fail, and the underlying writer.
|
||||
///
|
||||
/// This can be used to simply obtain ownership of the underlying error; it can also be used for
|
||||
/// advanced error recovery.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use std::io::{BufWriter, ErrorKind, Write};
|
||||
///
|
||||
/// let mut not_enough_space = [0u8; 10];
|
||||
/// let mut stream = BufWriter::new(not_enough_space.as_mut());
|
||||
/// write!(stream, "this cannot be actually written").unwrap();
|
||||
/// let into_inner_err = stream.into_inner().expect_err("now we discover it's too small");
|
||||
/// let (err, recovered_writer) = into_inner_err.into_parts();
|
||||
/// assert_eq!(err.kind(), ErrorKind::WriteZero);
|
||||
/// assert_eq!(recovered_writer.buffer(), b"t be actually written");
|
||||
/// ```
|
||||
#[stable(feature = "io_into_inner_error_parts", since = "1.55.0")]
|
||||
pub fn into_parts(self) -> (Error, W) {
|
||||
(self.1, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W> From<IntoInnerError<W>> for Error {
|
||||
fn from(iie: IntoInnerError<W>) -> Error {
|
||||
iie.1
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: Send + fmt::Debug> error::Error for IntoInnerError<W> {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W> fmt::Display for IntoInnerError<W> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.error().fmt(f)
|
||||
}
|
||||
}
|
||||
295
crates/io/src/io/copy.rs
Normal file
295
crates/io/src/io/copy.rs
Normal file
@@ -0,0 +1,295 @@
|
||||
use super::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write};
|
||||
use crate::alloc::Allocator;
|
||||
use crate::cmp;
|
||||
use crate::collections::VecDeque;
|
||||
use crate::io::IoSlice;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::sys::io::{CopyState, kernel_copy};
|
||||
|
||||
|
||||
/// Copies the entire contents of a reader into a writer.
|
||||
///
|
||||
/// This function will continuously read data from `reader` and then
|
||||
/// write it into `writer` in a streaming fashion until `reader`
|
||||
/// returns EOF.
|
||||
///
|
||||
/// On success, the total number of bytes that were copied from
|
||||
/// `reader` to `writer` is returned.
|
||||
///
|
||||
/// If you want to copy the contents of one file to another and you’re
|
||||
/// working with filesystem paths, see the [`fs::copy`] function.
|
||||
///
|
||||
/// [`fs::copy`]: crate::fs::copy
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error immediately if any call to [`read`] or
|
||||
/// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are
|
||||
/// handled by this function and the underlying operation is retried.
|
||||
///
|
||||
/// [`read`]: Read::read
|
||||
/// [`write`]: Write::write
|
||||
/// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut reader: &[u8] = b"hello";
|
||||
/// let mut writer: Vec<u8> = vec![];
|
||||
///
|
||||
/// io::copy(&mut reader, &mut writer)?;
|
||||
///
|
||||
/// assert_eq!(&b"hello"[..], &writer[..]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// On Linux (including Android), this function uses `copy_file_range(2)`,
|
||||
/// `sendfile(2)` or `splice(2)` syscalls to move data directly between file
|
||||
/// descriptors if possible.
|
||||
///
|
||||
/// Note that platform-specific behavior [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: crate::io#platform-specific-behavior
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
|
||||
where
|
||||
R: Read,
|
||||
W: Write,
|
||||
{
|
||||
match kernel_copy(reader, writer)? {
|
||||
CopyState::Ended(copied) => Ok(copied),
|
||||
CopyState::Fallback(copied) => {
|
||||
generic_copy(reader, writer).map(|additional| copied + additional)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The userspace read-write-loop implementation of `io::copy` that is used when
|
||||
/// OS-specific specializations for copy offloading are not available or not applicable.
|
||||
fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
|
||||
where
|
||||
R: Read,
|
||||
W: Write,
|
||||
{
|
||||
let read_buf = BufferedReaderSpec::buffer_size(reader);
|
||||
let write_buf = BufferedWriterSpec::buffer_size(writer);
|
||||
|
||||
if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf {
|
||||
return BufferedReaderSpec::copy_to(reader, writer);
|
||||
}
|
||||
|
||||
BufferedWriterSpec::copy_from(writer, reader)
|
||||
}
|
||||
|
||||
/// Specialization of the read-write loop that reuses the internal
|
||||
/// buffer of a BufReader. If there's no buffer then the writer side
|
||||
/// should be used instead.
|
||||
trait BufferedReaderSpec {
|
||||
fn buffer_size(&self) -> usize;
|
||||
|
||||
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64>;
|
||||
}
|
||||
|
||||
impl<T> BufferedReaderSpec for T
|
||||
where
|
||||
Self: Read,
|
||||
T: ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
default fn buffer_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result<u64> {
|
||||
unreachable!("only called from specializations")
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferedReaderSpec for &[u8] {
|
||||
fn buffer_size(&self) -> usize {
|
||||
// prefer this specialization since the source "buffer" is all we'll ever need,
|
||||
// even if it's small
|
||||
usize::MAX
|
||||
}
|
||||
|
||||
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
|
||||
let len = self.len();
|
||||
to.write_all(self)?;
|
||||
*self = &self[len..];
|
||||
Ok(len as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Allocator> BufferedReaderSpec for VecDeque<u8, A> {
|
||||
fn buffer_size(&self) -> usize {
|
||||
// prefer this specialization since the source "buffer" is all we'll ever need,
|
||||
// even if it's small
|
||||
usize::MAX
|
||||
}
|
||||
|
||||
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
|
||||
let len = self.len();
|
||||
let (front, back) = self.as_slices();
|
||||
let bufs = &mut [IoSlice::new(front), IoSlice::new(back)];
|
||||
to.write_all_vectored(bufs)?;
|
||||
self.clear();
|
||||
Ok(len as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> BufferedReaderSpec for BufReader<I>
|
||||
where
|
||||
Self: Read,
|
||||
I: ?Sized,
|
||||
{
|
||||
fn buffer_size(&self) -> usize {
|
||||
self.capacity()
|
||||
}
|
||||
|
||||
fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
|
||||
let mut len = 0;
|
||||
|
||||
loop {
|
||||
// Hack: this relies on `impl Read for BufReader` always calling fill_buf
|
||||
// if the buffer is empty, even for empty slices.
|
||||
// It can't be called directly here since specialization prevents us
|
||||
// from adding I: Read
|
||||
match self.read(&mut []) {
|
||||
Ok(_) => {}
|
||||
Err(e) if e.is_interrupted() => continue,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
let buf = self.buffer();
|
||||
if self.buffer().len() == 0 {
|
||||
return Ok(len);
|
||||
}
|
||||
|
||||
// In case the writer side is a BufWriter then its write_all
|
||||
// implements an optimization that passes through large
|
||||
// buffers to the underlying writer. That code path is #[cold]
|
||||
// but we're still avoiding redundant memcopies when doing
|
||||
// a copy between buffered inputs and outputs.
|
||||
to.write_all(buf)?;
|
||||
len += buf.len() as u64;
|
||||
self.discard_buffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialization of the read-write loop that either uses a stack buffer
|
||||
/// or reuses the internal buffer of a BufWriter
|
||||
trait BufferedWriterSpec: Write {
|
||||
fn buffer_size(&self) -> usize;
|
||||
|
||||
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64>;
|
||||
}
|
||||
|
||||
impl<W: Write + ?Sized> BufferedWriterSpec for W {
|
||||
#[inline]
|
||||
default fn buffer_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
default fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
|
||||
stack_buffer_copy(reader, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> {
|
||||
fn buffer_size(&self) -> usize {
|
||||
self.capacity()
|
||||
}
|
||||
|
||||
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
|
||||
if self.capacity() < DEFAULT_BUF_SIZE {
|
||||
return stack_buffer_copy(reader, self);
|
||||
}
|
||||
|
||||
let mut len = 0;
|
||||
let mut init = 0;
|
||||
|
||||
loop {
|
||||
let buf = self.buffer_mut();
|
||||
let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into();
|
||||
|
||||
unsafe {
|
||||
// SAFETY: init is either 0 or the init_len from the previous iteration.
|
||||
read_buf.set_init(init);
|
||||
}
|
||||
|
||||
if read_buf.capacity() >= DEFAULT_BUF_SIZE {
|
||||
let mut cursor = read_buf.unfilled();
|
||||
match reader.read_buf(cursor.reborrow()) {
|
||||
Ok(()) => {
|
||||
let bytes_read = cursor.written();
|
||||
|
||||
if bytes_read == 0 {
|
||||
return Ok(len);
|
||||
}
|
||||
|
||||
init = read_buf.init_len() - bytes_read;
|
||||
len += bytes_read as u64;
|
||||
|
||||
// SAFETY: BorrowedBuf guarantees all of its filled bytes are init
|
||||
unsafe { buf.set_len(buf.len() + bytes_read) };
|
||||
|
||||
// Read again if the buffer still has enough capacity, as BufWriter itself would do
|
||||
// This will occur if the reader returns short reads
|
||||
}
|
||||
Err(ref e) if e.is_interrupted() => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
// All the bytes that were already in the buffer are initialized,
|
||||
// treat them as such when the buffer is flushed.
|
||||
init += buf.len();
|
||||
|
||||
self.flush_buf()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferedWriterSpec for Vec<u8> {
|
||||
fn buffer_size(&self) -> usize {
|
||||
cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len())
|
||||
}
|
||||
|
||||
fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
|
||||
reader.read_to_end(self).map(|bytes| u64::try_from(bytes).expect("usize overflowed u64"))
|
||||
}
|
||||
}
|
||||
|
||||
fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
|
||||
reader: &mut R,
|
||||
writer: &mut W,
|
||||
) -> Result<u64> {
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); DEFAULT_BUF_SIZE];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
|
||||
let mut len = 0;
|
||||
|
||||
loop {
|
||||
match reader.read_buf(buf.unfilled()) {
|
||||
Ok(()) => {}
|
||||
Err(e) if e.is_interrupted() => continue,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if buf.filled().is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
len += buf.filled().len() as u64;
|
||||
writer.write_all(buf.filled())?;
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
755
crates/io/src/io/cursor.rs
Normal file
755
crates/io/src/io/cursor.rs
Normal file
@@ -0,0 +1,755 @@
|
||||
|
||||
use crate::alloc::Allocator;
|
||||
use crate::cmp;
|
||||
use crate::io::prelude::*;
|
||||
use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
|
||||
|
||||
/// A `Cursor` wraps an in-memory buffer and provides it with a
|
||||
/// [`Seek`] implementation.
|
||||
///
|
||||
/// `Cursor`s are used with in-memory buffers, anything implementing
|
||||
/// <code>[AsRef]<\[u8]></code>, to allow them to implement [`Read`] and/or [`Write`],
|
||||
/// allowing these buffers to be used anywhere you might use a reader or writer
|
||||
/// that does actual I/O.
|
||||
///
|
||||
/// The standard library implements some I/O traits on various types which
|
||||
/// are commonly used as a buffer, like <code>Cursor<[Vec]\<u8>></code> and
|
||||
/// <code>Cursor<[&\[u8\]][bytes]></code>.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// We may want to write bytes to a [`File`] in our production
|
||||
/// code, but use an in-memory buffer in our tests. We can do this with
|
||||
/// `Cursor`:
|
||||
///
|
||||
/// [bytes]: crate::slice "slice"
|
||||
/// [`File`]: crate::fs::File
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::{self, SeekFrom};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// // a library function we've written
|
||||
/// fn write_ten_bytes_at_end<W: Write + Seek>(mut writer: W) -> io::Result<()> {
|
||||
/// writer.seek(SeekFrom::End(-10))?;
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// writer.write(&[i])?;
|
||||
/// }
|
||||
///
|
||||
/// // all went well
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// # fn foo() -> io::Result<()> {
|
||||
/// // Here's some code that uses this library function.
|
||||
/// //
|
||||
/// // We might want to use a BufReader here for efficiency, but let's
|
||||
/// // keep this example focused.
|
||||
/// let mut file = File::create("foo.txt")?;
|
||||
/// // First, we need to allocate 10 bytes to be able to write into.
|
||||
/// file.set_len(10)?;
|
||||
///
|
||||
/// write_ten_bytes_at_end(&mut file)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
///
|
||||
/// // now let's write a test
|
||||
/// #[test]
|
||||
/// fn test_writes_bytes() {
|
||||
/// // setting up a real File is much slower than an in-memory buffer,
|
||||
/// // let's use a cursor instead
|
||||
/// use std::io::Cursor;
|
||||
/// let mut buff = Cursor::new(vec![0; 15]);
|
||||
///
|
||||
/// write_ten_bytes_at_end(&mut buff).unwrap();
|
||||
///
|
||||
/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[derive(Debug, Default, Eq, PartialEq)]
|
||||
pub struct Cursor<T> {
|
||||
inner: T,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
impl<T> Cursor<T> {
|
||||
/// Creates a new cursor wrapping the provided underlying in-memory buffer.
|
||||
///
|
||||
/// Cursor initial position is `0` even if underlying buffer (e.g., [`Vec`])
|
||||
/// is not empty. So writing to cursor starts with overwriting [`Vec`]
|
||||
/// content, not with appending to it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn new(inner: T) -> Cursor<T> {
|
||||
Cursor { pos: 0, inner }
|
||||
}
|
||||
|
||||
/// Consumes this cursor, returning the underlying value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let vec = buff.into_inner();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying value in this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let reference = buff.get_ref();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying value in this cursor.
|
||||
///
|
||||
/// Care should be taken to avoid modifying the internal I/O state of the
|
||||
/// underlying value as it may corrupt this cursor's position.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let reference = buff.get_mut();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_mut_cursor", since = "1.86.0")]
|
||||
pub const fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns the current position of this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::SeekFrom;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.position(), 0);
|
||||
///
|
||||
/// buff.seek(SeekFrom::Current(2)).unwrap();
|
||||
/// assert_eq!(buff.position(), 2);
|
||||
///
|
||||
/// buff.seek(SeekFrom::Current(-1)).unwrap();
|
||||
/// assert_eq!(buff.position(), 1);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn position(&self) -> u64 {
|
||||
self.pos
|
||||
}
|
||||
|
||||
/// Sets the position of this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.position(), 0);
|
||||
///
|
||||
/// buff.set_position(2);
|
||||
/// assert_eq!(buff.position(), 2);
|
||||
///
|
||||
/// buff.set_position(4);
|
||||
/// assert_eq!(buff.position(), 4);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_mut_cursor", since = "1.86.0")]
|
||||
pub const fn set_position(&mut self, pos: u64) {
|
||||
self.pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
/// Splits the underlying slice at the cursor position and returns them.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(cursor_split)]
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.split(), ([].as_slice(), [1, 2, 3, 4, 5].as_slice()));
|
||||
///
|
||||
/// buff.set_position(2);
|
||||
/// assert_eq!(buff.split(), ([1, 2].as_slice(), [3, 4, 5].as_slice()));
|
||||
///
|
||||
/// buff.set_position(6);
|
||||
/// assert_eq!(buff.split(), ([1, 2, 3, 4, 5].as_slice(), [].as_slice()));
|
||||
/// ```
|
||||
#[unstable(feature = "cursor_split", issue = "86369")]
|
||||
pub fn split(&self) -> (&[u8], &[u8]) {
|
||||
let slice = self.inner.as_ref();
|
||||
let pos = self.pos.min(slice.len() as u64);
|
||||
slice.split_at(pos as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Cursor<T>
|
||||
where
|
||||
T: AsMut<[u8]>,
|
||||
{
|
||||
/// Splits the underlying slice at the cursor position and returns them
|
||||
/// mutably.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(cursor_split)]
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.split_mut(), ([].as_mut_slice(), [1, 2, 3, 4, 5].as_mut_slice()));
|
||||
///
|
||||
/// buff.set_position(2);
|
||||
/// assert_eq!(buff.split_mut(), ([1, 2].as_mut_slice(), [3, 4, 5].as_mut_slice()));
|
||||
///
|
||||
/// buff.set_position(6);
|
||||
/// assert_eq!(buff.split_mut(), ([1, 2, 3, 4, 5].as_mut_slice(), [].as_mut_slice()));
|
||||
/// ```
|
||||
#[unstable(feature = "cursor_split", issue = "86369")]
|
||||
pub fn split_mut(&mut self) -> (&mut [u8], &mut [u8]) {
|
||||
let slice = self.inner.as_mut();
|
||||
let pos = self.pos.min(slice.len() as u64);
|
||||
slice.split_at_mut(pos as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Clone for Cursor<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Cursor { inner: self.inner.clone(), pos: self.pos }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_from(&mut self, other: &Self) {
|
||||
self.inner.clone_from(&other.inner);
|
||||
self.pos = other.pos;
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> io::Seek for Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
|
||||
let (base_pos, offset) = match style {
|
||||
SeekFrom::Start(n) => {
|
||||
self.pos = n;
|
||||
return Ok(n);
|
||||
}
|
||||
SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n),
|
||||
SeekFrom::Current(n) => (self.pos, n),
|
||||
};
|
||||
match base_pos.checked_add_signed(offset) {
|
||||
Some(n) => {
|
||||
self.pos = n;
|
||||
Ok(self.pos)
|
||||
}
|
||||
None => Err(io::const_error!(
|
||||
ErrorKind::InvalidInput,
|
||||
"invalid seek to a negative or overflowing position",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
Ok(self.inner.as_ref().len() as u64)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
Ok(self.pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Read for Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let n = Read::read(&mut Cursor::split(self).1, buf)?;
|
||||
self.pos += n as u64;
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let prev_written = cursor.written();
|
||||
|
||||
Read::read_buf(&mut Cursor::split(self).1, cursor.reborrow())?;
|
||||
|
||||
self.pos += (cursor.written() - prev_written) as u64;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let mut nread = 0;
|
||||
for buf in bufs {
|
||||
let n = self.read(buf)?;
|
||||
nread += n;
|
||||
if n < buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
let result = Read::read_exact(&mut Cursor::split(self).1, buf);
|
||||
|
||||
match result {
|
||||
Ok(_) => self.pos += buf.len() as u64,
|
||||
// The only possible error condition is EOF, so place the cursor at "EOF"
|
||||
Err(_) => self.pos = self.inner.as_ref().len() as u64,
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let prev_written = cursor.written();
|
||||
|
||||
let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow());
|
||||
self.pos += (cursor.written() - prev_written) as u64;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let content = Cursor::split(self).1;
|
||||
let len = content.len();
|
||||
buf.try_reserve(len)?;
|
||||
buf.extend_from_slice(content);
|
||||
self.pos += len as u64;
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
let content =
|
||||
crate::str::from_utf8(Cursor::split(self).1).map_err(|_| io::Error::INVALID_UTF8)?;
|
||||
let len = content.len();
|
||||
buf.try_reserve(len)?;
|
||||
buf.push_str(content);
|
||||
self.pos += len as u64;
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> BufRead for Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
Ok(Cursor::split(self).1)
|
||||
}
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.pos += amt as u64;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-resizing write implementation
|
||||
#[inline]
|
||||
fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<usize> {
|
||||
let pos = cmp::min(*pos_mut, slice.len() as u64);
|
||||
let amt = (&mut slice[(pos as usize)..]).write(buf)?;
|
||||
*pos_mut += amt as u64;
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn slice_write_vectored(
|
||||
pos_mut: &mut u64,
|
||||
slice: &mut [u8],
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> io::Result<usize> {
|
||||
let mut nwritten = 0;
|
||||
for buf in bufs {
|
||||
let n = slice_write(pos_mut, slice, buf)?;
|
||||
nwritten += n;
|
||||
if n < buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(nwritten)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> {
|
||||
let n = slice_write(pos_mut, slice, buf)?;
|
||||
if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn slice_write_all_vectored(
|
||||
pos_mut: &mut u64,
|
||||
slice: &mut [u8],
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> io::Result<()> {
|
||||
for buf in bufs {
|
||||
let n = slice_write(pos_mut, slice, buf)?;
|
||||
if n < buf.len() {
|
||||
return Err(io::Error::WRITE_ALL_EOF);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reserves the required space, and pads the vec with 0s if necessary.
|
||||
fn reserve_and_pad<A: Allocator>(
|
||||
pos_mut: &mut u64,
|
||||
vec: &mut Vec<u8, A>,
|
||||
buf_len: usize,
|
||||
) -> io::Result<usize> {
|
||||
let pos: usize = (*pos_mut).try_into().map_err(|_| {
|
||||
io::const_error!(
|
||||
ErrorKind::InvalidInput,
|
||||
"cursor position exceeds maximum possible vector length",
|
||||
)
|
||||
})?;
|
||||
|
||||
// For safety reasons, we don't want these numbers to overflow
|
||||
// otherwise our allocation won't be enough
|
||||
let desired_cap = pos.saturating_add(buf_len);
|
||||
if desired_cap > vec.capacity() {
|
||||
// We want our vec's total capacity
|
||||
// to have room for (pos+buf_len) bytes. Reserve allocates
|
||||
// based on additional elements from the length, so we need to
|
||||
// reserve the difference
|
||||
vec.reserve(desired_cap - vec.len());
|
||||
}
|
||||
// Pad if pos is above the current len.
|
||||
if pos > vec.len() {
|
||||
let diff = pos - vec.len();
|
||||
// Unfortunately, `resize()` would suffice but the optimiser does not
|
||||
// realise the `reserve` it does can be eliminated. So we do it manually
|
||||
// to eliminate that extra branch
|
||||
let spare = vec.spare_capacity_mut();
|
||||
debug_assert!(spare.len() >= diff);
|
||||
// Safety: we have allocated enough capacity for this.
|
||||
// And we are only writing, not reading
|
||||
unsafe {
|
||||
spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0));
|
||||
vec.set_len(pos);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(pos)
|
||||
}
|
||||
|
||||
/// Writes the slice to the vec without allocating.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `vec` must have `buf.len()` spare capacity.
|
||||
unsafe fn vec_write_all_unchecked<A>(pos: usize, vec: &mut Vec<u8, A>, buf: &[u8]) -> usize
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
debug_assert!(vec.capacity() >= pos + buf.len());
|
||||
unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) };
|
||||
pos + buf.len()
|
||||
}
|
||||
|
||||
/// Resizing `write_all` implementation for [`Cursor`].
|
||||
///
|
||||
/// Cursor is allowed to have a pre-allocated and initialised
|
||||
/// vector body, but with a position of 0. This means the [`Write`]
|
||||
/// will overwrite the contents of the vec.
|
||||
///
|
||||
/// This also allows for the vec body to be empty, but with a position of N.
|
||||
/// This means that [`Write`] will pad the vec with 0 initially,
|
||||
/// before writing anything from that point
|
||||
fn vec_write_all<A>(pos_mut: &mut u64, vec: &mut Vec<u8, A>, buf: &[u8]) -> io::Result<usize>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
let buf_len = buf.len();
|
||||
let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?;
|
||||
|
||||
// Write the buf then progress the vec forward if necessary
|
||||
// Safety: we have ensured that the capacity is available
|
||||
// and that all bytes get written up to pos
|
||||
unsafe {
|
||||
pos = vec_write_all_unchecked(pos, vec, buf);
|
||||
if pos > vec.len() {
|
||||
vec.set_len(pos);
|
||||
}
|
||||
};
|
||||
|
||||
// Bump us forward
|
||||
*pos_mut += buf_len as u64;
|
||||
Ok(buf_len)
|
||||
}
|
||||
|
||||
/// Resizing `write_all_vectored` implementation for [`Cursor`].
|
||||
///
|
||||
/// Cursor is allowed to have a pre-allocated and initialised
|
||||
/// vector body, but with a position of 0. This means the [`Write`]
|
||||
/// will overwrite the contents of the vec.
|
||||
///
|
||||
/// This also allows for the vec body to be empty, but with a position of N.
|
||||
/// This means that [`Write`] will pad the vec with 0 initially,
|
||||
/// before writing anything from that point
|
||||
fn vec_write_all_vectored<A>(
|
||||
pos_mut: &mut u64,
|
||||
vec: &mut Vec<u8, A>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> io::Result<usize>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
// For safety reasons, we don't want this sum to overflow ever.
|
||||
// If this saturates, the reserve should panic to avoid any unsound writing.
|
||||
let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len()));
|
||||
let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?;
|
||||
|
||||
// Write the buf then progress the vec forward if necessary
|
||||
// Safety: we have ensured that the capacity is available
|
||||
// and that all bytes get written up to the last pos
|
||||
unsafe {
|
||||
for buf in bufs {
|
||||
pos = vec_write_all_unchecked(pos, vec, buf);
|
||||
}
|
||||
if pos > vec.len() {
|
||||
vec.set_len(pos);
|
||||
}
|
||||
}
|
||||
|
||||
// Bump us forward
|
||||
*pos_mut += buf_len as u64;
|
||||
Ok(buf_len)
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Write for Cursor<&mut [u8]> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
slice_write(&mut self.pos, self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
slice_write_vectored(&mut self.pos, self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
slice_write_all(&mut self.pos, self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
slice_write_all_vectored(&mut self.pos, self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cursor_mut_vec", since = "1.25.0")]
|
||||
impl<A> Write for Cursor<&mut Vec<u8, A>>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
vec_write_all(&mut self.pos, self.inner, buf)
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
vec_write_all_vectored(&mut self.pos, self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
vec_write_all(&mut self.pos, self.inner, buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
vec_write_all_vectored(&mut self.pos, self.inner, bufs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<A> Write for Cursor<Vec<u8, A>>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
vec_write_all(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
vec_write_all_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
vec_write_all(&mut self.pos, &mut self.inner, buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
vec_write_all_vectored(&mut self.pos, &mut self.inner, bufs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cursor_box_slice", since = "1.5.0")]
|
||||
impl<A> Write for Cursor<Box<[u8], A>>
|
||||
where
|
||||
A: Allocator,
|
||||
{
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
slice_write(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
slice_write_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
slice_write_all(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
slice_write_all_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "cursor_array", since = "1.61.0")]
|
||||
impl<const N: usize> Write for Cursor<[u8; N]> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
slice_write(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
slice_write_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
slice_write_all(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
slice_write_all_vectored(&mut self.pos, &mut self.inner, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
1083
crates/io/src/io/error.rs
Normal file
1083
crates/io/src/io/error.rs
Normal file
File diff suppressed because it is too large
Load Diff
411
crates/io/src/io/error/repr_bitpacked.rs
Normal file
411
crates/io/src/io/error/repr_bitpacked.rs
Normal file
@@ -0,0 +1,411 @@
|
||||
//! This is a densely packed error representation which is used on targets with
|
||||
//! 64-bit pointers.
|
||||
//!
|
||||
//! (Note that `bitpacked` vs `unpacked` here has no relationship to
|
||||
//! `#[repr(packed)]`, it just refers to attempting to use any available bits in
|
||||
//! a more clever manner than `rustc`'s default layout algorithm would).
|
||||
//!
|
||||
//! Conceptually, it stores the same data as the "unpacked" equivalent we use on
|
||||
//! other targets. Specifically, you can imagine it as an optimized version of
|
||||
//! the following enum (which is roughly equivalent to what's stored by
|
||||
//! `repr_unpacked::Repr`, e.g. `super::ErrorData<Box<Custom>>`):
|
||||
//!
|
||||
//! ```ignore (exposition-only)
|
||||
//! enum ErrorData {
|
||||
//! Os(i32),
|
||||
//! Simple(ErrorKind),
|
||||
//! SimpleMessage(&'static SimpleMessage),
|
||||
//! Custom(Box<Custom>),
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! However, it packs this data into a 64bit non-zero value.
|
||||
//!
|
||||
//! This optimization not only allows `io::Error` to occupy a single pointer,
|
||||
//! but improves `io::Result` as well, especially for situations like
|
||||
//! `io::Result<()>` (which is now 64 bits) or `io::Result<u64>` (which is now
|
||||
//! 128 bits), which are quite common.
|
||||
//!
|
||||
//! # Layout
|
||||
//! Tagged values are 64 bits, with the 2 least significant bits used for the
|
||||
//! tag. This means there are 4 "variants":
|
||||
//!
|
||||
//! - **Tag 0b00**: The first variant is equivalent to
|
||||
//! `ErrorData::SimpleMessage`, and holds a `&'static SimpleMessage` directly.
|
||||
//!
|
||||
//! `SimpleMessage` has an alignment >= 4 (which is requested with
|
||||
//! `#[repr(align)]` and checked statically at the bottom of this file), which
|
||||
//! means every `&'static SimpleMessage` should have the both tag bits as 0,
|
||||
//! meaning its tagged and untagged representation are equivalent.
|
||||
//!
|
||||
//! This means we can skip tagging it, which is necessary as this variant can
|
||||
//! be constructed from a `const fn`, which probably cannot tag pointers (or
|
||||
//! at least it would be difficult).
|
||||
//!
|
||||
//! - **Tag 0b01**: The other pointer variant holds the data for
|
||||
//! `ErrorData::Custom` and the remaining 62 bits are used to store a
|
||||
//! `Box<Custom>`. `Custom` also has alignment >= 4, so the bottom two bits
|
||||
//! are free to use for the tag.
|
||||
//!
|
||||
//! The only important thing to note is that `ptr::wrapping_add` and
|
||||
//! `ptr::wrapping_sub` are used to tag the pointer, rather than bitwise
|
||||
//! operations. This should preserve the pointer's provenance, which would
|
||||
//! otherwise be lost.
|
||||
//!
|
||||
//! - **Tag 0b10**: Holds the data for `ErrorData::Os(i32)`. We store the `i32`
|
||||
//! in the pointer's most significant 32 bits, and don't use the bits `2..32`
|
||||
//! for anything. Using the top 32 bits is just to let us easily recover the
|
||||
//! `i32` code with the correct sign.
|
||||
//!
|
||||
//! - **Tag 0b11**: Holds the data for `ErrorData::Simple(ErrorKind)`. This
|
||||
//! stores the `ErrorKind` in the top 32 bits as well, although it doesn't
|
||||
//! occupy nearly that many. Most of the bits are unused here, but it's not
|
||||
//! like we need them for anything else yet.
|
||||
//!
|
||||
//! # Use of `NonNull<()>`
|
||||
//!
|
||||
//! Everything is stored in a `NonNull<()>`, which is odd, but actually serves a
|
||||
//! purpose.
|
||||
//!
|
||||
//! Conceptually you might think of this more like:
|
||||
//!
|
||||
//! ```ignore (exposition-only)
|
||||
//! union Repr {
|
||||
//! // holds integer (Simple/Os) variants, and
|
||||
//! // provides access to the tag bits.
|
||||
//! bits: NonZero<u64>,
|
||||
//! // Tag is 0, so this is stored untagged.
|
||||
//! msg: &'static SimpleMessage,
|
||||
//! // Tagged (offset) `Box<Custom>` pointer.
|
||||
//! tagged_custom: NonNull<()>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! But there are a few problems with this:
|
||||
//!
|
||||
//! 1. Union access is equivalent to a transmute, so this representation would
|
||||
//! require we transmute between integers and pointers in at least one
|
||||
//! direction, which may be UB (and even if not, it is likely harder for a
|
||||
//! compiler to reason about than explicit ptr->int operations).
|
||||
//!
|
||||
//! 2. Even if all fields of a union have a niche, the union itself doesn't,
|
||||
//! although this may change in the future. This would make things like
|
||||
//! `io::Result<()>` and `io::Result<usize>` larger, which defeats part of
|
||||
//! the motivation of this bitpacking.
|
||||
//!
|
||||
//! Storing everything in a `NonZero<usize>` (or some other integer) would be a
|
||||
//! bit more traditional for pointer tagging, but it would lose provenance
|
||||
//! information, couldn't be constructed from a `const fn`, and would probably
|
||||
//! run into other issues as well.
|
||||
//!
|
||||
//! The `NonNull<()>` seems like the only alternative, even if it's fairly odd
|
||||
//! to use a pointer type to store something that may hold an integer, some of
|
||||
//! the time.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::num::NonZeroUsize;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage};
|
||||
|
||||
// The 2 least-significant bits are used as tag.
|
||||
const TAG_MASK: usize = 0b11;
|
||||
const TAG_SIMPLE_MESSAGE: usize = 0b00;
|
||||
const TAG_CUSTOM: usize = 0b01;
|
||||
const TAG_OS: usize = 0b10;
|
||||
const TAG_SIMPLE: usize = 0b11;
|
||||
|
||||
/// The internal representation.
|
||||
///
|
||||
/// See the module docs for more, this is just a way to hack in a check that we
|
||||
/// indeed are not unwind-safe.
|
||||
///
|
||||
/// ```compile_fail,E0277
|
||||
/// fn is_unwind_safe<T: core::panic::UnwindSafe>() {}
|
||||
/// is_unwind_safe::<std::io::Error>();
|
||||
/// ```
|
||||
#[repr(transparent)]
|
||||
#[rustc_insignificant_dtor]
|
||||
pub(super) struct Repr(NonNull<()>, PhantomData<ErrorData<Box<Custom>>>);
|
||||
|
||||
// All the types `Repr` stores internally are Send + Sync, and so is it.
|
||||
unsafe impl Send for Repr {}
|
||||
unsafe impl Sync for Repr {}
|
||||
|
||||
impl Repr {
|
||||
pub(super) fn new_custom(b: Box<Custom>) -> Self {
|
||||
let p = Box::into_raw(b).cast::<u8>();
|
||||
// Should only be possible if an allocator handed out a pointer with
|
||||
// wrong alignment.
|
||||
debug_assert_eq!(p.addr() & TAG_MASK, 0);
|
||||
// Note: We know `TAG_CUSTOM <= size_of::<Custom>()` (static_assert at
|
||||
// end of file), and both the start and end of the expression must be
|
||||
// valid without address space wraparound due to `Box`'s semantics.
|
||||
//
|
||||
// This means it would be correct to implement this using `ptr::add`
|
||||
// (rather than `ptr::wrapping_add`), but it's unclear this would give
|
||||
// any benefit, so we just use `wrapping_add` instead.
|
||||
let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>();
|
||||
// Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`,
|
||||
// because `p`'s alignment means it isn't allowed to have any of the
|
||||
// `TAG_BITS` set (you can verify that addition and bitwise-or are the
|
||||
// same when the operands have no bits in common using a truth table).
|
||||
//
|
||||
// Then, `TAG_CUSTOM | p` is not zero, as that would require
|
||||
// `TAG_CUSTOM` and `p` both be zero, and neither is (as `p` came from a
|
||||
// box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore,
|
||||
// `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the
|
||||
// `new_unchecked` is safe.
|
||||
let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData);
|
||||
// quickly smoke-check we encoded the right thing (This generally will
|
||||
// only run in std's tests, unless the user uses -Zbuild-std)
|
||||
debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed");
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn new_os(code: RawOsError) -> Self {
|
||||
let utagged = ((code as usize) << 32) | TAG_OS;
|
||||
// Safety: `TAG_OS` is not zero, so the result of the `|` is not 0.
|
||||
let res = Self(
|
||||
NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }),
|
||||
PhantomData,
|
||||
);
|
||||
// quickly smoke-check we encoded the right thing (This generally will
|
||||
// only run in std's tests, unless the user uses -Zbuild-std)
|
||||
debug_assert!(
|
||||
matches!(res.data(), ErrorData::Os(c) if c == code),
|
||||
"repr(os) encoding failed for {code}"
|
||||
);
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn new_simple(kind: ErrorKind) -> Self {
|
||||
let utagged = ((kind as usize) << 32) | TAG_SIMPLE;
|
||||
// Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0.
|
||||
let res = Self(
|
||||
NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }),
|
||||
PhantomData,
|
||||
);
|
||||
// quickly smoke-check we encoded the right thing (This generally will
|
||||
// only run in std's tests, unless the user uses -Zbuild-std)
|
||||
debug_assert!(
|
||||
matches!(res.data(), ErrorData::Simple(k) if k == kind),
|
||||
"repr(simple) encoding failed {:?}",
|
||||
kind,
|
||||
);
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self {
|
||||
// Safety: References are never null.
|
||||
Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn data(&self) -> ErrorData<&Custom> {
|
||||
// Safety: We're a Repr, decode_repr is fine.
|
||||
unsafe { decode_repr(self.0, |c| &*c) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> {
|
||||
// Safety: We're a Repr, decode_repr is fine.
|
||||
unsafe { decode_repr(self.0, |c| &mut *c) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn into_data(self) -> ErrorData<Box<Custom>> {
|
||||
let this = core::mem::ManuallyDrop::new(self);
|
||||
// Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is
|
||||
// safe because we prevent double-drop using `ManuallyDrop`.
|
||||
unsafe { decode_repr(this.0, |p| Box::from_raw(p)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Repr {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is
|
||||
// safe because we're being dropped.
|
||||
unsafe {
|
||||
let _ = decode_repr(self.0, |p| Box::<Custom>::from_raw(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shared helper to decode a `Repr`'s internal pointer into an ErrorData.
|
||||
//
|
||||
// Safety: `ptr`'s bits should be encoded as described in the document at the
|
||||
// top (it should `some_repr.0`)
|
||||
#[inline]
|
||||
unsafe fn decode_repr<C, F>(ptr: NonNull<()>, make_custom: F) -> ErrorData<C>
|
||||
where
|
||||
F: FnOnce(*mut Custom) -> C,
|
||||
{
|
||||
let bits = ptr.as_ptr().addr();
|
||||
match bits & TAG_MASK {
|
||||
TAG_OS => {
|
||||
let code = ((bits as i64) >> 32) as RawOsError;
|
||||
ErrorData::Os(code)
|
||||
}
|
||||
TAG_SIMPLE => {
|
||||
let kind_bits = (bits >> 32) as u32;
|
||||
let kind = kind_from_prim(kind_bits).unwrap_or_else(|| {
|
||||
debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits);
|
||||
// This means the `ptr` passed in was not valid, which violates
|
||||
// the unsafe contract of `decode_repr`.
|
||||
//
|
||||
// Using this rather than unwrap meaningfully improves the code
|
||||
// for callers which only care about one variant (usually
|
||||
// `Custom`)
|
||||
unsafe { core::hint::unreachable_unchecked() };
|
||||
});
|
||||
ErrorData::Simple(kind)
|
||||
}
|
||||
TAG_SIMPLE_MESSAGE => {
|
||||
// SAFETY: per tag
|
||||
unsafe { ErrorData::SimpleMessage(&*ptr.cast::<SimpleMessage>().as_ptr()) }
|
||||
}
|
||||
TAG_CUSTOM => {
|
||||
// It would be correct for us to use `ptr::byte_sub` here (see the
|
||||
// comment above the `wrapping_add` call in `new_custom` for why),
|
||||
// but it isn't clear that it makes a difference, so we don't.
|
||||
let custom = ptr.as_ptr().wrapping_byte_sub(TAG_CUSTOM).cast::<Custom>();
|
||||
ErrorData::Custom(make_custom(custom))
|
||||
}
|
||||
_ => {
|
||||
// Can't happen, and compiler can tell
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This compiles to the same code as the check+transmute, but doesn't require
|
||||
// unsafe, or to hard-code max ErrorKind or its size in a way the compiler
|
||||
// couldn't verify.
|
||||
#[inline]
|
||||
fn kind_from_prim(ek: u32) -> Option<ErrorKind> {
|
||||
macro_rules! from_prim {
|
||||
($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{
|
||||
// Force a compile error if the list gets out of date.
|
||||
const _: fn(e: $Enum) = |e: $Enum| match e {
|
||||
$($Enum::$Variant => ()),*
|
||||
};
|
||||
match $prim {
|
||||
$(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)*
|
||||
_ => None,
|
||||
}
|
||||
}}
|
||||
}
|
||||
from_prim!(ek => ErrorKind {
|
||||
NotFound,
|
||||
PermissionDenied,
|
||||
ConnectionRefused,
|
||||
ConnectionReset,
|
||||
HostUnreachable,
|
||||
NetworkUnreachable,
|
||||
ConnectionAborted,
|
||||
NotConnected,
|
||||
AddrInUse,
|
||||
AddrNotAvailable,
|
||||
NetworkDown,
|
||||
BrokenPipe,
|
||||
AlreadyExists,
|
||||
WouldBlock,
|
||||
NotADirectory,
|
||||
IsADirectory,
|
||||
DirectoryNotEmpty,
|
||||
ReadOnlyFilesystem,
|
||||
FilesystemLoop,
|
||||
StaleNetworkFileHandle,
|
||||
InvalidInput,
|
||||
InvalidData,
|
||||
TimedOut,
|
||||
WriteZero,
|
||||
StorageFull,
|
||||
NotSeekable,
|
||||
QuotaExceeded,
|
||||
FileTooLarge,
|
||||
ResourceBusy,
|
||||
ExecutableFileBusy,
|
||||
Deadlock,
|
||||
CrossesDevices,
|
||||
TooManyLinks,
|
||||
InvalidFilename,
|
||||
ArgumentListTooLong,
|
||||
Interrupted,
|
||||
Other,
|
||||
UnexpectedEof,
|
||||
Unsupported,
|
||||
OutOfMemory,
|
||||
InProgress,
|
||||
Uncategorized,
|
||||
})
|
||||
}
|
||||
|
||||
// Some static checking to alert us if a change breaks any of the assumptions
|
||||
// that our encoding relies on for correctness and soundness. (Some of these are
|
||||
// a bit overly thorough/cautious, admittedly)
|
||||
//
|
||||
// If any of these are hit on a platform that std supports, we should likely
|
||||
// just use `repr_unpacked.rs` there instead (unless the fix is easy).
|
||||
macro_rules! static_assert {
|
||||
($condition:expr) => {
|
||||
const _: () = assert!($condition);
|
||||
};
|
||||
(@usize_eq: $lhs:expr, $rhs:expr) => {
|
||||
const _: [(); $lhs] = [(); $rhs];
|
||||
};
|
||||
}
|
||||
|
||||
// The bitpacking we use requires pointers be exactly 64 bits.
|
||||
static_assert!(@usize_eq: size_of::<NonNull<()>>(), 8);
|
||||
|
||||
// We also require pointers and usize be the same size.
|
||||
static_assert!(@usize_eq: size_of::<NonNull<()>>(), size_of::<usize>());
|
||||
|
||||
// `Custom` and `SimpleMessage` need to be thin pointers.
|
||||
static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8);
|
||||
static_assert!(@usize_eq: size_of::<Box<Custom>>(), 8);
|
||||
|
||||
static_assert!((TAG_MASK + 1).is_power_of_two());
|
||||
// And they must have sufficient alignment.
|
||||
static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1);
|
||||
static_assert!(align_of::<Custom>() >= TAG_MASK + 1);
|
||||
|
||||
static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE);
|
||||
static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM);
|
||||
static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS);
|
||||
static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE, TAG_SIMPLE);
|
||||
|
||||
// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we
|
||||
// offset a pointer by this value, and expect it to both be within the same
|
||||
// object, and to not wrap around the address space. See the comment in that
|
||||
// function for further details.
|
||||
//
|
||||
// Actually, at the moment we use `ptr::wrapping_add`, not `ptr::add`, so this
|
||||
// check isn't needed for that one, although the assertion that we don't
|
||||
// actually wrap around in that wrapping_add does simplify the safety reasoning
|
||||
// elsewhere considerably.
|
||||
static_assert!(size_of::<Custom>() >= TAG_CUSTOM);
|
||||
|
||||
// These two store a payload which is allowed to be zero, so they must be
|
||||
// non-zero to preserve the `NonNull`'s range invariant.
|
||||
static_assert!(TAG_OS != 0);
|
||||
static_assert!(TAG_SIMPLE != 0);
|
||||
// We can't tag `SimpleMessage`s, the tag must be 0.
|
||||
static_assert!(@usize_eq: TAG_SIMPLE_MESSAGE, 0);
|
||||
|
||||
// Check that the point of all of this still holds.
|
||||
//
|
||||
// We'd check against `io::Error`, but *technically* it's allowed to vary,
|
||||
// as it's not `#[repr(transparent)]`/`#[repr(C)]`. We could add that, but
|
||||
// the `#[repr()]` would show up in rustdoc, which might be seen as a stable
|
||||
// commitment.
|
||||
static_assert!(@usize_eq: size_of::<Repr>(), 8);
|
||||
static_assert!(@usize_eq: size_of::<Option<Repr>>(), 8);
|
||||
static_assert!(@usize_eq: size_of::<Result<(), Repr>>(), 8);
|
||||
static_assert!(@usize_eq: size_of::<Result<usize, Repr>>(), 16);
|
||||
50
crates/io/src/io/error/repr_unpacked.rs
Normal file
50
crates/io/src/io/error/repr_unpacked.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
//! This is a fairly simple unpacked error representation that's used on
|
||||
//! non-64bit targets, where the packed 64 bit representation wouldn't work, and
|
||||
//! would have no benefit.
|
||||
|
||||
use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage};
|
||||
|
||||
type Inner = ErrorData<Box<Custom>>;
|
||||
|
||||
pub(super) struct Repr(Inner);
|
||||
|
||||
impl Repr {
|
||||
#[inline]
|
||||
pub(super) fn new_custom(b: Box<Custom>) -> Self {
|
||||
Self(Inner::Custom(b))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn new_os(code: RawOsError) -> Self {
|
||||
Self(Inner::Os(code))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn new_simple(kind: ErrorKind) -> Self {
|
||||
Self(Inner::Simple(kind))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self {
|
||||
Self(Inner::SimpleMessage(m))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn into_data(self) -> ErrorData<Box<Custom>> {
|
||||
self.0
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn data(&self) -> ErrorData<&Custom> {
|
||||
match &self.0 {
|
||||
Inner::Os(c) => ErrorData::Os(*c),
|
||||
Inner::Simple(k) => ErrorData::Simple(*k),
|
||||
Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m),
|
||||
Inner::Custom(m) => ErrorData::Custom(&*m),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> {
|
||||
match &mut self.0 {
|
||||
Inner::Os(c) => ErrorData::Os(*c),
|
||||
Inner::Simple(k) => ErrorData::Simple(*k),
|
||||
Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m),
|
||||
Inner::Custom(m) => ErrorData::Custom(&mut *m),
|
||||
}
|
||||
}
|
||||
}
|
||||
715
crates/io/src/io/impls.rs
Normal file
715
crates/io/src/io/impls.rs
Normal file
@@ -0,0 +1,715 @@
|
||||
|
||||
use crate::alloc::Allocator;
|
||||
use crate::collections::VecDeque;
|
||||
use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||
use crate::{cmp, fmt, mem, str};
|
||||
|
||||
// =============================================================================
|
||||
// Forwarding implementations
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: Read + ?Sized> Read for &mut R {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
(**self).read(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
(**self).read_buf(cursor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
(**self).read_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
(**self).is_read_vectored()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_to_end(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_to_string(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
(**self).read_exact(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
(**self).read_buf_exact(cursor)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: Write + ?Sized> Write for &mut W {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
(**self).write(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
(**self).write_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
(**self).is_write_vectored()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
(**self).flush()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
(**self).write_all(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
(**self).write_all_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
(**self).write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<S: Seek + ?Sized> Seek for &mut S {
|
||||
#[inline]
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
(**self).seek(pos)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rewind(&mut self) -> io::Result<()> {
|
||||
(**self).rewind()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_position()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
(**self).seek_relative(offset)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<B: BufRead + ?Sized> BufRead for &mut B {
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
(**self).fill_buf()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
(**self).consume(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_data_left(&mut self) -> io::Result<bool> {
|
||||
(**self).has_data_left()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_until(byte, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_until(&mut self, byte: u8) -> io::Result<usize> {
|
||||
(**self).skip_until(byte)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_line(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R: Read + ?Sized> Read for Box<R> {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
(**self).read(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
(**self).read_buf(cursor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
(**self).read_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
(**self).is_read_vectored()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_to_end(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_to_string(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
(**self).read_exact(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
(**self).read_buf_exact(cursor)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W: Write + ?Sized> Write for Box<W> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
(**self).write(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
(**self).write_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
(**self).is_write_vectored()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
(**self).flush()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
(**self).write_all(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
(**self).write_all_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
(**self).write_fmt(fmt)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<S: Seek + ?Sized> Seek for Box<S> {
|
||||
#[inline]
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
(**self).seek(pos)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rewind(&mut self) -> io::Result<()> {
|
||||
(**self).rewind()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
(**self).stream_position()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
|
||||
(**self).seek_relative(offset)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<B: BufRead + ?Sized> BufRead for Box<B> {
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
(**self).fill_buf()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
(**self).consume(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_data_left(&mut self) -> io::Result<bool> {
|
||||
(**self).has_data_left()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
(**self).read_until(byte, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_until(&mut self, byte: u8) -> io::Result<usize> {
|
||||
(**self).skip_until(byte)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
(**self).read_line(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// In-memory buffer implementations
|
||||
|
||||
/// Read is implemented for `&[u8]` by copying from the slice.
|
||||
///
|
||||
/// Note that reading updates the slice to point to the yet unread part.
|
||||
/// The slice will be empty when EOF is reached.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Read for &[u8] {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let amt = cmp::min(buf.len(), self.len());
|
||||
let (a, b) = self.split_at(amt);
|
||||
|
||||
// First check if the amount of bytes we want to read is small:
|
||||
// `copy_from_slice` will generally expand to a call to `memcpy`, and
|
||||
// for a single byte the overhead is significant.
|
||||
if amt == 1 {
|
||||
buf[0] = a[0];
|
||||
} else {
|
||||
buf[..amt].copy_from_slice(a);
|
||||
}
|
||||
|
||||
*self = b;
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let amt = cmp::min(cursor.capacity(), self.len());
|
||||
let (a, b) = self.split_at(amt);
|
||||
|
||||
cursor.append(a);
|
||||
|
||||
*self = b;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let mut nread = 0;
|
||||
for buf in bufs {
|
||||
nread += self.read(buf)?;
|
||||
if self.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
if buf.len() > self.len() {
|
||||
// `read_exact` makes no promise about the content of `buf` if it
|
||||
// fails so don't bother about that.
|
||||
*self = &self[self.len()..];
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
let (a, b) = self.split_at(buf.len());
|
||||
|
||||
// First check if the amount of bytes we want to read is small:
|
||||
// `copy_from_slice` will generally expand to a call to `memcpy`, and
|
||||
// for a single byte the overhead is significant.
|
||||
if buf.len() == 1 {
|
||||
buf[0] = a[0];
|
||||
} else {
|
||||
buf.copy_from_slice(a);
|
||||
}
|
||||
|
||||
*self = b;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
if cursor.capacity() > self.len() {
|
||||
// Append everything we can to the cursor.
|
||||
cursor.append(*self);
|
||||
*self = &self[self.len()..];
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
let (a, b) = self.split_at(cursor.capacity());
|
||||
|
||||
cursor.append(a);
|
||||
|
||||
*self = b;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let len = self.len();
|
||||
buf.try_reserve(len)?;
|
||||
buf.extend_from_slice(*self);
|
||||
*self = &self[len..];
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
let content = str::from_utf8(self).map_err(|_| io::Error::INVALID_UTF8)?;
|
||||
let len = self.len();
|
||||
buf.try_reserve(len)?;
|
||||
buf.push_str(content);
|
||||
*self = &self[len..];
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl BufRead for &[u8] {
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
Ok(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
*self = &self[amt..];
|
||||
}
|
||||
}
|
||||
|
||||
/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting
|
||||
/// its data.
|
||||
///
|
||||
/// Note that writing updates the slice to point to the yet unwritten part.
|
||||
/// The slice will be empty when it has been completely overwritten.
|
||||
///
|
||||
/// If the number of bytes to be written exceeds the size of the slice, write operations will
|
||||
/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of
|
||||
/// kind `ErrorKind::WriteZero`.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Write for &mut [u8] {
|
||||
#[inline]
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
let amt = cmp::min(data.len(), self.len());
|
||||
let (a, b) = mem::take(self).split_at_mut(amt);
|
||||
a.copy_from_slice(&data[..amt]);
|
||||
*self = b;
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let mut nwritten = 0;
|
||||
for buf in bufs {
|
||||
nwritten += self.write(buf)?;
|
||||
if self.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(nwritten)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
|
||||
if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
for buf in bufs {
|
||||
if self.write(buf)? < buf.len() {
|
||||
return Err(io::Error::WRITE_ALL_EOF);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Write is implemented for `Vec<u8>` by appending to the vector.
|
||||
/// The vector will grow as needed.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<A: Allocator> Write for Vec<u8, A> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let len = bufs.iter().map(|b| b.len()).sum();
|
||||
self.reserve(len);
|
||||
for buf in bufs {
|
||||
self.extend_from_slice(buf);
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.extend_from_slice(buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
self.write_vectored(bufs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Read is implemented for `VecDeque<u8>` by consuming bytes from the front of the `VecDeque`.
|
||||
#[stable(feature = "vecdeque_read_write", since = "1.63.0")]
|
||||
impl<A: Allocator> Read for VecDeque<u8, A> {
|
||||
/// Fill `buf` with the contents of the "front" slice as returned by
|
||||
/// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are
|
||||
/// discontiguous, multiple calls to `read` will be needed to read the entire content.
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let (ref mut front, _) = self.as_slices();
|
||||
let n = Read::read(front, buf)?;
|
||||
self.drain(..n);
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
let (front, back) = self.as_slices();
|
||||
|
||||
// Use only the front buffer if it is big enough to fill `buf`, else use
|
||||
// the back buffer too.
|
||||
match buf.split_at_mut_checked(front.len()) {
|
||||
None => buf.copy_from_slice(&front[..buf.len()]),
|
||||
Some((buf_front, buf_back)) => match back.split_at_checked(buf_back.len()) {
|
||||
Some((back, _)) => {
|
||||
buf_front.copy_from_slice(front);
|
||||
buf_back.copy_from_slice(back);
|
||||
}
|
||||
None => {
|
||||
self.clear();
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
self.drain(..buf.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let (ref mut front, _) = self.as_slices();
|
||||
let n = cmp::min(cursor.capacity(), front.len());
|
||||
Read::read_buf(front, cursor)?;
|
||||
self.drain(..n);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
let len = cursor.capacity();
|
||||
let (front, back) = self.as_slices();
|
||||
|
||||
match front.split_at_checked(cursor.capacity()) {
|
||||
Some((front, _)) => cursor.append(front),
|
||||
None => {
|
||||
cursor.append(front);
|
||||
match back.split_at_checked(cursor.capacity()) {
|
||||
Some((back, _)) => cursor.append(back),
|
||||
None => {
|
||||
cursor.append(back);
|
||||
self.clear();
|
||||
return Err(io::Error::READ_EXACT_EOF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.drain(..len);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
// The total len is known upfront so we can reserve it in a single call.
|
||||
let len = self.len();
|
||||
buf.try_reserve(len)?;
|
||||
|
||||
let (front, back) = self.as_slices();
|
||||
buf.extend_from_slice(front);
|
||||
buf.extend_from_slice(back);
|
||||
self.clear();
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
// SAFETY: We only append to the buffer
|
||||
unsafe { io::append_to_string(buf, |buf| self.read_to_end(buf)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// BufRead is implemented for `VecDeque<u8>` by reading bytes from the front of the `VecDeque`.
|
||||
#[stable(feature = "vecdeque_buf_read", since = "1.75.0")]
|
||||
impl<A: Allocator> BufRead for VecDeque<u8, A> {
|
||||
/// Returns the contents of the "front" slice as returned by
|
||||
/// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are
|
||||
/// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content.
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
let (front, _) = self.as_slices();
|
||||
Ok(front)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.drain(..amt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Write is implemented for `VecDeque<u8>` by appending to the `VecDeque`, growing it as needed.
|
||||
#[stable(feature = "vecdeque_read_write", since = "1.63.0")]
|
||||
impl<A: Allocator> Write for VecDeque<u8, A> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.extend(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let len = bufs.iter().map(|b| b.len()).sum();
|
||||
self.reserve(len);
|
||||
for buf in bufs {
|
||||
self.extend(&**buf);
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
self.extend(buf);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
self.write_vectored(bufs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "read_buf", issue = "78485")]
|
||||
impl<'a> io::Write for core::io::BorrowedCursor<'a> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let amt = cmp::min(buf.len(), self.capacity());
|
||||
self.append(&buf[..amt]);
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let mut nwritten = 0;
|
||||
for buf in bufs {
|
||||
let n = self.write(buf)?;
|
||||
nwritten += n;
|
||||
if n < buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(nwritten)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
|
||||
if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
for buf in bufs {
|
||||
if self.write(buf)? < buf.len() {
|
||||
return Err(io::Error::WRITE_ALL_EOF);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
3393
crates/io/src/io/mod.rs
Normal file
3393
crates/io/src/io/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
14
crates/io/src/io/prelude.rs
Normal file
14
crates/io/src/io/prelude.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
//! The I/O Prelude.
|
||||
//!
|
||||
//! The purpose of this module is to alleviate imports of many common I/O traits
|
||||
//! by adding a glob import to the top of I/O heavy modules:
|
||||
//!
|
||||
//! ```
|
||||
//! # #![allow(unused_imports)]
|
||||
//! use std::io::prelude::*;
|
||||
//! ```
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use super::{BufRead, Read, Seek, Write};
|
||||
446
crates/io/src/io/util.rs
Normal file
446
crates/io/src/io/util.rs
Normal file
@@ -0,0 +1,446 @@
|
||||
#![allow(missing_copy_implementations)]
|
||||
|
||||
|
||||
use crate::fmt;
|
||||
use crate::io::{
|
||||
self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write,
|
||||
};
|
||||
|
||||
/// `Empty` ignores any data written via [`Write`], and will always be empty
|
||||
/// (returning zero bytes) when read via [`Read`].
|
||||
///
|
||||
/// This struct is generally created by calling [`empty()`]. Please
|
||||
/// see the documentation of [`empty()`] for more details.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Empty;
|
||||
|
||||
/// Creates a value that is always at EOF for reads, and ignores all data written.
|
||||
///
|
||||
/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`]
|
||||
/// and the contents of the buffer will not be inspected.
|
||||
///
|
||||
/// All calls to [`read`] from the returned reader will return [`Ok(0)`].
|
||||
///
|
||||
/// [`Ok(buf.len())`]: Ok
|
||||
/// [`Ok(0)`]: Ok
|
||||
///
|
||||
/// [`write`]: Write::write
|
||||
/// [`read`]: Read::read
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::{self, Write};
|
||||
///
|
||||
/// let buffer = vec![1, 2, 3, 5, 8];
|
||||
/// let num_bytes = io::empty().write(&buffer).unwrap();
|
||||
/// assert_eq!(num_bytes, 5);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::{self, Read};
|
||||
///
|
||||
/// let mut buffer = String::new();
|
||||
/// io::empty().read_to_string(&mut buffer).unwrap();
|
||||
/// assert!(buffer.is_empty());
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn empty() -> Empty {
|
||||
Empty
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Read for Empty {
|
||||
#[inline]
|
||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
// Do not force `Chain<Empty, T>` or `Chain<T, Empty>` to use vectored
|
||||
// reads, unless the other reader is vectored.
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl BufRead for Empty {
|
||||
#[inline]
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
Ok(&[])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(&mut self, _n: usize) {}
|
||||
|
||||
#[inline]
|
||||
fn has_data_left(&mut self) -> io::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_until(&mut self, _byte: u8, _buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_until(&mut self, _byte: u8) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_line(&mut self, _buf: &mut String) -> io::Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "empty_seek", since = "1.51.0")]
|
||||
impl Seek for Empty {
|
||||
#[inline]
|
||||
fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl SizeHint for Empty {
|
||||
#[inline]
|
||||
fn upper_bound(&self) -> Option<usize> {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "empty_write", since = "1.73.0")]
|
||||
impl Write for Empty {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "empty_write", since = "1.73.0")]
|
||||
impl Write for &Empty {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A reader which yields one byte over and over and over and over and over and...
|
||||
///
|
||||
/// This struct is generally created by calling [`repeat()`]. Please
|
||||
/// see the documentation of [`repeat()`] for more details.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Repeat {
|
||||
byte: u8,
|
||||
}
|
||||
|
||||
/// Creates an instance of a reader that infinitely repeats one byte.
|
||||
///
|
||||
/// All reads from this reader will succeed by filling the specified buffer with
|
||||
/// the given byte.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::{self, Read};
|
||||
///
|
||||
/// let mut buffer = [0; 3];
|
||||
/// io::repeat(0b101).read_exact(&mut buffer).unwrap();
|
||||
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn repeat(byte: u8) -> Repeat {
|
||||
Repeat { byte }
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Read for Repeat {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
buf.fill(self.byte);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
buf.fill(self.byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
// SAFETY: No uninit bytes are being written.
|
||||
unsafe { buf.as_mut() }.write_filled(self.byte);
|
||||
// SAFETY: the entire unfilled portion of buf has been initialized.
|
||||
unsafe { buf.advance_unchecked(buf.capacity()) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.read_buf(buf)
|
||||
}
|
||||
|
||||
/// This function is not supported by `io::Repeat`, because there's no end of its data
|
||||
fn read_to_end(&mut self, _: &mut Vec<u8>) -> io::Result<usize> {
|
||||
Err(io::Error::from(io::ErrorKind::OutOfMemory))
|
||||
}
|
||||
|
||||
/// This function is not supported by `io::Repeat`, because there's no end of its data
|
||||
fn read_to_string(&mut self, _: &mut String) -> io::Result<usize> {
|
||||
Err(io::Error::from(io::ErrorKind::OutOfMemory))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let mut nwritten = 0;
|
||||
for buf in bufs {
|
||||
nwritten += self.read(buf)?;
|
||||
}
|
||||
Ok(nwritten)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl SizeHint for Repeat {
|
||||
#[inline]
|
||||
fn lower_bound(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn upper_bound(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Repeat {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Repeat").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// A writer which will move data into the void.
|
||||
///
|
||||
/// This struct is generally created by calling [`sink()`]. Please
|
||||
/// see the documentation of [`sink()`] for more details.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Sink;
|
||||
|
||||
/// Creates an instance of a writer which will successfully consume all data.
|
||||
///
|
||||
/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`]
|
||||
/// and the contents of the buffer will not be inspected.
|
||||
///
|
||||
/// [`write`]: Write::write
|
||||
/// [`Ok(buf.len())`]: Ok
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::{self, Write};
|
||||
///
|
||||
/// let buffer = vec![1, 2, 3, 5, 8];
|
||||
/// let num_bytes = io::sink().write(&buffer).unwrap();
|
||||
/// assert_eq!(num_bytes, 5);
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")]
|
||||
pub const fn sink() -> Sink {
|
||||
Sink
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Write for Sink {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "write_mt", since = "1.48.0")]
|
||||
impl Write for &Sink {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let total_len = bufs.iter().map(|b| b.len()).sum();
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,299 +1,70 @@
|
||||
#![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)]
|
||||
pub mod error;
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![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")]
|
||||
extern crate alloc;
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod std_prelude {
|
||||
pub(crate) use core::prelude::rust_2024::*;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::String;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use super::alloc_crate::{
|
||||
boxed::Box,
|
||||
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.
|
||||
///
|
||||
/// Must be implemented for all types that also implement at least one of the following traits: `Read`, `Write`,
|
||||
/// `Seek`.
|
||||
pub trait IoBase {
|
||||
/// Type of errors returned by input/output operations.
|
||||
type Error: IoError;
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc_crate::collections;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use core::{cmp, error, fmt, hint, mem, ops, ptr, result, slice, str};
|
||||
|
||||
/// The `Read` trait allows for reading bytes from a source.
|
||||
///
|
||||
/// 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())
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::io::*;
|
||||
|
||||
1
crates/io/src/sys.rs
Normal file
1
crates/io/src/sys.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub(crate) mod io;
|
||||
15
crates/io/src/sys/io/error/generic.rs
Normal file
15
crates/io/src/sys/io/error/generic.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
pub fn errno() -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
pub fn is_interrupted(_code: i32) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
|
||||
crate::io::ErrorKind::Uncategorized
|
||||
}
|
||||
|
||||
pub fn error_string(_errno: i32) -> String {
|
||||
"operation successful".to_string()
|
||||
}
|
||||
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;
|
||||
52
crates/io/src/sys/io/io_slice/unsupported.rs
Normal file
52
crates/io/src/sys/io/io_slice/unsupported.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use core::mem;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct IoSlice<'a>(&'a [u8]);
|
||||
|
||||
impl<'a> IoSlice<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
|
||||
IoSlice(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
self.0 = &self.0[n..]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn as_slice(&self) -> &'a [u8] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IoSliceMut<'a>(&'a mut [u8]);
|
||||
|
||||
impl<'a> IoSliceMut<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
|
||||
IoSliceMut(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
let slice = mem::take(&mut self.0);
|
||||
let (_, remaining) = slice.split_at_mut(n);
|
||||
self.0 = remaining;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn into_slice(self) -> &'a mut [u8] {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
3
crates/io/src/sys/io/is_terminal/unsupported.rs
Normal file
3
crates/io/src/sys/io/is_terminal/unsupported.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub fn is_terminal<T>(_: &T) -> bool {
|
||||
false
|
||||
}
|
||||
16
crates/io/src/sys/io/kernel_copy/mod.rs
Normal file
16
crates/io/src/sys/io/kernel_copy/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
pub enum CopyState {
|
||||
#[cfg_attr(not(any(target_os = "linux", target_os = "android")), expect(dead_code))]
|
||||
Ended(u64),
|
||||
Fallback(u64),
|
||||
}
|
||||
|
||||
use crate::io::{Result, Read, Write};
|
||||
|
||||
pub fn kernel_copy<R: ?Sized, W: ?Sized>(_reader: &mut R, _writer: &mut W) -> Result<CopyState>
|
||||
where
|
||||
R: Read,
|
||||
W: Write,
|
||||
{
|
||||
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
|
||||
};
|
||||
@@ -5,7 +5,7 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
bffs = { path = "../bffs" }
|
||||
io = { path = "../io" }
|
||||
io = { package = "no-std-io", path = "../io" }
|
||||
|
||||
[features]
|
||||
kernel = []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use io::{IoBase, Read, Write};
|
||||
use io::{Read, Write};
|
||||
|
||||
use crate::syscall;
|
||||
|
||||
@@ -19,22 +19,18 @@ impl File {
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for File {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ pub enum SysCall {
|
||||
ExecVE = 59,
|
||||
Exit = 60,
|
||||
NanoSleep = 101,
|
||||
WriteIntTemp = 998,
|
||||
WriteTemp = 999,
|
||||
WaitPid = 247,
|
||||
Unimplemented = 1 << 31,
|
||||
}
|
||||
|
||||
@@ -32,8 +31,7 @@ impl From<u64> for SysCall {
|
||||
59 => SysCall::ExecVE,
|
||||
60 => SysCall::Exit,
|
||||
101 => SysCall::NanoSleep,
|
||||
998 => SysCall::WriteIntTemp,
|
||||
999 => SysCall::WriteTemp,
|
||||
247 => SysCall::WaitPid,
|
||||
_ => SysCall::Unimplemented,
|
||||
}
|
||||
}
|
||||
@@ -110,21 +108,6 @@ pub fn sleep(duration: Duration) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_string_temp(content: &str) {
|
||||
unsafe {
|
||||
syscall!(
|
||||
SysCall::WriteTemp,
|
||||
content.as_ptr() as u64,
|
||||
content.len() as u64
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn write_int_temp(content: u64) {
|
||||
unsafe {
|
||||
syscall!(SysCall::WriteIntTemp, content);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unknown_lints)]
|
||||
#[allow(fuzzy_provenance_casts)]
|
||||
pub fn alloc(layout: Layout) -> *mut u8 {
|
||||
@@ -177,11 +160,18 @@ pub fn seek(file_descriptor: u64, seek_type: u8, seek: u64) {
|
||||
syscall!(SysCall::Seek, file_descriptor, seek_type as u64, seek);
|
||||
}
|
||||
}
|
||||
pub fn spawn(path: &str) {
|
||||
pub fn spawn(path: &str, argc: isize, argv: *const *const u8) -> u64 {
|
||||
unsafe {
|
||||
let ptr = path.as_ptr();
|
||||
let size = path.len();
|
||||
syscall!(SysCall::Spawn, ptr as u64, size as u64);
|
||||
let (pid, ..) = syscall!(
|
||||
SysCall::Spawn,
|
||||
ptr as u64,
|
||||
size as u64,
|
||||
argc as u64,
|
||||
argv as u64
|
||||
);
|
||||
pid
|
||||
}
|
||||
}
|
||||
pub fn execve(path: &str) {
|
||||
@@ -191,3 +181,9 @@ pub fn execve(path: &str) {
|
||||
syscall!(SysCall::ExecVE, ptr as u64, size as u64);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: usize) {
|
||||
unsafe {
|
||||
syscall!(SysCall::WaitPid, pid as u64);
|
||||
}
|
||||
}
|
||||
|
||||
6
justfile
6
justfile
@@ -29,8 +29,10 @@ make-symbols:
|
||||
cd build-tools && cargo r --bin gen-symbols --release
|
||||
|
||||
build: mount_filesystem
|
||||
@for file in `ls user`; do \
|
||||
{{ just_executable() }} release="{{ release }}" cargo_flags="{{ cargo_flags }}" build_user_prog $file ; \
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
for file in `ls user`; do
|
||||
{{ just_executable() }} release="{{ release }}" cargo_flags="{{ cargo_flags }}" build_user_prog $file;
|
||||
done
|
||||
RUSTFLAGS="-Cforce-frame-pointers=yes -Clink-arg=-Tilm.ld --sysroot {{ justfile_directory() / "sysroot" }}" cargo b {{ cargo_flags }}
|
||||
{{ just_executable() }} make-symbols
|
||||
|
||||
@@ -14,12 +14,13 @@ REAL_CP_FILES := "\
|
||||
sys/random/mod.rs \
|
||||
sys/thread_local/mod.rs"
|
||||
STD_FILES := "\
|
||||
../tests ../benches \
|
||||
alloc.rs ascii.rs backtrace.rs bstr.rs env.rs error.rs fs.rs \
|
||||
keyword_docs.rs lib.rs macros.rs panic.rs panicking.rs pat.rs \
|
||||
path.rs process.rs random.rs rt.rs tests_helpers.rs time.rs \
|
||||
backtrace collections ffi fs hash io net num os/raw/mod.rs \
|
||||
os/raw/tests.rs os/mod.rs prelude process sync sys/alloc/mod.rs \
|
||||
sys/args/unsupported.rs sys/env/mod.rs sys/env/common.rs \
|
||||
sys/args/unsupported.rs sys/args/common.rs sys/env/mod.rs sys/env/common.rs \
|
||||
sys/env/unsupported.rs sys/fd/mod.rs sys/fs/mod.rs \
|
||||
sys/fs/common.rs sys/fs/unsupported.rs sys/helpers/mod.rs \
|
||||
sys/helpers/small_c_string.rs sys/helpers/tests.rs sys/helpers/wstr.rs \
|
||||
@@ -44,7 +45,7 @@ STD_FILES := "\
|
||||
sys/thread_local/no_threads.rs sys/thread_local/os.rs sys/time/mod.rs \
|
||||
sys/time/unsupported.rs sys/backtrace.rs sys/cmath.rs \
|
||||
sys/configure_builtins.rs sys/env_consts.rs sys/exit.rs sys/mod.rs thread"
|
||||
KEEP_FILES := "survos.rs"
|
||||
KEEP_FILES := "survos.rs ffi.rs"
|
||||
|
||||
setup-std:
|
||||
@echo "🔗 Linking root directories..."
|
||||
@@ -74,7 +75,8 @@ patch-std:
|
||||
done
|
||||
|
||||
build-sysroot:
|
||||
cargo clean
|
||||
rm -rf target/riscv64/debug/deps/
|
||||
rm -rf ../target/riscv64/
|
||||
RUSTFLAGS="-Zforce-unstable-if-unmarked -C relocation-model=pic -C link-arg=-pie" \
|
||||
cargo build --package std --target ../riscv64.json --features compiler-builtins-mem
|
||||
mkdir -p ../sysroot/lib/rustlib/riscv64/lib
|
||||
|
||||
7
library/std/.gitignore
vendored
7
library/std/.gitignore
vendored
@@ -2,6 +2,8 @@ Cargo.lock
|
||||
Cargo.toml
|
||||
build.rs
|
||||
target
|
||||
tests
|
||||
benches
|
||||
src/random.rs
|
||||
src/num
|
||||
src/prelude
|
||||
@@ -18,7 +20,6 @@ src/tests_helpers.rs
|
||||
src/error.rs
|
||||
src/net
|
||||
src/rt.rs
|
||||
src/sys/args
|
||||
src/sys/args/mod.rs
|
||||
src/sys/args/unsupported.rs
|
||||
src/sys/thread_local
|
||||
@@ -56,9 +57,7 @@ src/sys/sync/thread_parking/unsupported.rs
|
||||
src/sys/sync/once
|
||||
src/sys/sync/once/no_threads.rs
|
||||
src/sys/sync/once/mod.rs
|
||||
src/sys/fd
|
||||
src/sys/fd/mod.rs
|
||||
src/sys/process
|
||||
src/sys/process/mod.rs
|
||||
src/sys/process/env.rs
|
||||
src/sys/process/unsupported.rs
|
||||
@@ -100,7 +99,6 @@ src/sys/helpers/mod.rs
|
||||
src/sys/helpers/wstr.rs
|
||||
src/sys/helpers/tests.rs
|
||||
src/sys/helpers/small_c_string.rs
|
||||
src/sys/fs
|
||||
src/sys/fs/mod.rs
|
||||
src/sys/fs/common.rs
|
||||
src/sys/fs/unsupported.rs
|
||||
@@ -124,7 +122,6 @@ src/sys/os_str/bytes
|
||||
src/sys/os_str/bytes/tests.rs
|
||||
src/sys/os_str/bytes.rs
|
||||
src/macros.rs
|
||||
src/os
|
||||
src/os/mod.rs
|
||||
src/os/raw
|
||||
src/os/raw/mod.rs
|
||||
|
||||
5
library/std/patches/os/mod.sed
Normal file
5
library/std/patches/os/mod.sed
Normal file
@@ -0,0 +1,5 @@
|
||||
185a \#[cfg(target_os = "survos")]\
|
||||
pub mod survos;\
|
||||
#[cfg(target_os = "survos")]\
|
||||
#[unstable(feature = "survos_std", issue = "none")]\
|
||||
pub use survos::fd;
|
||||
@@ -1,4 +1,5 @@
|
||||
# 55a \ target_os = "survos" => { \
|
||||
# mod survos; \
|
||||
# pub use survos::*; \
|
||||
# }
|
||||
55a \ target_os = "survos" => { \
|
||||
mod survos; \
|
||||
pub use survos::*; \
|
||||
}
|
||||
12a \ target_os = "survos",
|
||||
|
||||
4
library/std/patches/sys/fd/mod.sed
Normal file
4
library/std/patches/sys/fd/mod.sed
Normal file
@@ -0,0 +1,4 @@
|
||||
21a \ target_os = "survos" => { \
|
||||
mod survos; \
|
||||
pub use survos::*; \
|
||||
}
|
||||
4
library/std/patches/sys/fs/mod.sed
Normal file
4
library/std/patches/sys/fs/mod.sed
Normal file
@@ -0,0 +1,4 @@
|
||||
47a \ target_os = "survos" => { \
|
||||
mod survos; \
|
||||
use survos as imp; \
|
||||
}
|
||||
4
library/std/patches/sys/process/mod.sed
Normal file
4
library/std/patches/sys/process/mod.sed
Normal file
@@ -0,0 +1,4 @@
|
||||
17a \ target_os = "survos" => { \
|
||||
mod survos; \
|
||||
use survos as imp; \
|
||||
}
|
||||
18
library/std/src/os/survos.rs
Normal file
18
library/std/src/os/survos.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
#![unstable(feature = "survos_std", issue = "none")]
|
||||
|
||||
#[unstable(feature = "survos_std", issue = "none")]
|
||||
pub mod fd {
|
||||
pub type RawFd = u64;
|
||||
|
||||
pub trait FromRawFd {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self;
|
||||
}
|
||||
|
||||
pub trait AsRawFd {
|
||||
// Required method
|
||||
fn as_raw_fd(&self) -> RawFd;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ffi;
|
||||
pub mod syscall;
|
||||
70
library/std/src/os/survos/ffi.rs
Normal file
70
library/std/src/os/survos/ffi.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::ffi::{OsStr, OsString};
|
||||
use crate::mem;
|
||||
use crate::sealed::Sealed;
|
||||
use crate::sys::os_str::Buf;
|
||||
use crate::sys::{AsInner, FromInner, IntoInner};
|
||||
|
||||
// Note: this file is currently reused in other `std::os::{platform}::ffi` modules to reduce duplication.
|
||||
// Keep this in mind when applying changes to this file that only apply to `unix`.
|
||||
|
||||
/// Platform-specific extensions to [`OsString`].
|
||||
///
|
||||
/// This trait is sealed: it cannot be implemented outside the standard library.
|
||||
/// This is so that future additional methods are not breaking changes.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait OsStringExt: Sealed {
|
||||
/// Creates an [`OsString`] from a byte vector.
|
||||
///
|
||||
/// See the module documentation for an example.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn from_vec(vec: Vec<u8>) -> Self;
|
||||
|
||||
/// Yields the underlying byte vector of this [`OsString`].
|
||||
///
|
||||
/// See the module documentation for an example.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn into_vec(self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl OsStringExt for OsString {
|
||||
#[inline]
|
||||
fn from_vec(vec: Vec<u8>) -> OsString {
|
||||
FromInner::from_inner(Buf { inner: vec })
|
||||
}
|
||||
#[inline]
|
||||
fn into_vec(self) -> Vec<u8> {
|
||||
self.into_inner().inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Platform-specific extensions to [`OsStr`].
|
||||
///
|
||||
/// This trait is sealed: it cannot be implemented outside the standard library.
|
||||
/// This is so that future additional methods are not breaking changes.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait OsStrExt: Sealed {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
/// Creates an [`OsStr`] from a byte slice.
|
||||
///
|
||||
/// See the module documentation for an example.
|
||||
fn from_bytes(slice: &[u8]) -> &Self;
|
||||
|
||||
/// Gets the underlying byte view of the [`OsStr`] slice.
|
||||
///
|
||||
/// See the module documentation for an example.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn as_bytes(&self) -> &[u8];
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl OsStrExt for OsStr {
|
||||
#[inline]
|
||||
fn from_bytes(slice: &[u8]) -> &OsStr {
|
||||
unsafe { mem::transmute(slice) }
|
||||
}
|
||||
#[inline]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
&self.as_inner().inner
|
||||
}
|
||||
}
|
||||
1
library/std/src/os/survos/syscall.rs
Symbolic link
1
library/std/src/os/survos/syscall.rs
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../crates/shared/src/syscall.rs
|
||||
1
library/std/src/sys/args/common.rs
Symbolic link
1
library/std/src/sys/args/common.rs
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/args/common.rs
|
||||
42
library/std/src/sys/args/survos.rs
Normal file
42
library/std/src/sys/args/survos.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
pub use super::common::Args;
|
||||
use crate::ffi::{CStr, OsStr, OsString};
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{Atomic, AtomicIsize, AtomicPtr, Ordering};
|
||||
|
||||
pub fn args() -> Args {
|
||||
let (argc, argv) = argc_argv();
|
||||
|
||||
let mut vec = Vec::with_capacity(argc as usize);
|
||||
|
||||
for i in 0..argc {
|
||||
let ptr = unsafe { argv.offset(i).read() };
|
||||
|
||||
if ptr.is_null() {
|
||||
break;
|
||||
}
|
||||
|
||||
let cstr = unsafe { CStr::from_ptr(ptr) };
|
||||
vec.push(unsafe { OsStr::from_encoded_bytes_unchecked(cstr.to_bytes()) }.to_owned());
|
||||
}
|
||||
|
||||
Args::new(vec)
|
||||
}
|
||||
|
||||
static ARGC: Atomic<isize> = AtomicIsize::new(0);
|
||||
static ARGV: Atomic<*mut *const u8> = AtomicPtr::new(ptr::null_mut());
|
||||
|
||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
||||
ARGC.store(argc, Ordering::Relaxed);
|
||||
ARGV.store(argv as *mut _, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn argc_argv() -> (isize, *const *const u8) {
|
||||
let argv = ARGV.load(Ordering::Relaxed);
|
||||
let argc = if argv.is_null() {
|
||||
0
|
||||
} else {
|
||||
ARGC.load(Ordering::Relaxed)
|
||||
};
|
||||
|
||||
(argc, argv.cast())
|
||||
}
|
||||
1
library/std/src/sys/fd/survos.rs
Normal file
1
library/std/src/sys/fd/survos.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
145
library/std/src/sys/fs/survos.rs
Normal file
145
library/std/src/sys/fs/survos.rs
Normal file
@@ -0,0 +1,145 @@
|
||||
#[path = "unsupported.rs"]
|
||||
mod unsupported;
|
||||
use core::io::BorrowedCursor;
|
||||
|
||||
pub use self::unsupported::*;
|
||||
|
||||
use crate::fs::TryLockError;
|
||||
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom, prelude::*};
|
||||
use crate::os::fd::{AsRawFd, FromRawFd, RawFd};
|
||||
use crate::os::survos::syscall;
|
||||
use crate::sys::FromInner;
|
||||
use crate::sys::fs::Path;
|
||||
use crate::sys::unsupported;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct File(RawFd);
|
||||
|
||||
impl File {
|
||||
pub fn open(path: &Path, _opts: &OpenOptions) -> io::Result<File> {
|
||||
unsafe {
|
||||
Ok(File::from_raw_fd(syscall::open(
|
||||
path.as_os_str().to_str().unwrap(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_attr(&self) -> io::Result<FileAttr> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn fsync(&self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn datasync(&self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn lock_shared(&self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> Result<(), TryLockError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn unlock(&self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn truncate(&self, _size: u64) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn is_read_vectored(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(syscall::write(self.as_raw_fd(), buf) as usize)
|
||||
}
|
||||
|
||||
pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Option<io::Result<u64>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<u64> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<File> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for File {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
syscall::close(self.as_raw_fd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for File {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
||||
Self(fd)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "survos_std", issue = "none")]
|
||||
impl FromRawFd for crate::fs::File {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
||||
unsafe { Self::from_inner(File::from_raw_fd(fd)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for File {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
#[path = "unsupported/mod.rs"]
|
||||
mod unsupported;
|
||||
pub use self::unsupported::*;
|
||||
use crate::os::survos::syscall;
|
||||
|
||||
#[path = "../../../../../crates/shared/src/syscall.rs"]
|
||||
mod syscall;
|
||||
pub use self::unsupported::*;
|
||||
|
||||
/// # Safety
|
||||
/// `argc` and `argv` are passed by the kernel
|
||||
@@ -17,6 +16,10 @@ pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize {
|
||||
unsafe { main(argc, argv) }
|
||||
}
|
||||
|
||||
pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
|
||||
crate::sys::args::init(argc, argv);
|
||||
}
|
||||
|
||||
pub fn abort_internal() -> ! {
|
||||
// todo real abort
|
||||
syscall::exit()
|
||||
|
||||
392
library/std/src/sys/process/survos.rs
Normal file
392
library/std/src/sys/process/survos.rs
Normal file
@@ -0,0 +1,392 @@
|
||||
use core::mem::ManuallyDrop;
|
||||
|
||||
use alloc_crate::ffi::CString;
|
||||
|
||||
use super::env::{CommandEnv, CommandEnvs};
|
||||
pub use crate::ffi::OsString as EnvKey;
|
||||
use crate::ffi::{CStr, OsStr, OsString};
|
||||
use crate::num::NonZero;
|
||||
use crate::os::survos::{ffi::OsStrExt, syscall};
|
||||
use crate::path::Path;
|
||||
use crate::process::StdioPipes;
|
||||
use crate::sys::fs::File;
|
||||
use crate::sys::unsupported;
|
||||
use crate::{fmt, io};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Command
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct Command {
|
||||
program: CString,
|
||||
args: Vec<*const u8>,
|
||||
env: CommandEnv,
|
||||
|
||||
cwd: Option<OsString>,
|
||||
stdin: Option<Stdio>,
|
||||
stdout: Option<Stdio>,
|
||||
stderr: Option<Stdio>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Stdio {
|
||||
Inherit,
|
||||
Null,
|
||||
MakePipe,
|
||||
ParentStdout,
|
||||
ParentStderr,
|
||||
#[allow(dead_code)] // This variant exists only for the Debug impl
|
||||
InheritFile(File),
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn new(program: &OsStr) -> Command {
|
||||
Command {
|
||||
program: CString::new(program.as_bytes()).unwrap(),
|
||||
args: vec![
|
||||
CString::new(program.as_bytes()).unwrap().into_raw(),
|
||||
crate::ptr::null(),
|
||||
],
|
||||
env: Default::default(),
|
||||
cwd: None,
|
||||
stdin: None,
|
||||
stdout: None,
|
||||
stderr: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arg(&mut self, arg: &OsStr) {
|
||||
let argc = self.args.len() - 1;
|
||||
// Replace the null pointer at the end of the array...
|
||||
self.args[argc] = CString::new(arg.as_bytes()).unwrap().into_raw();
|
||||
// ... and recreate it to restore the data structure invariant.
|
||||
self.args.push(crate::ptr::null());
|
||||
}
|
||||
|
||||
pub fn env_mut(&mut self) -> &mut CommandEnv {
|
||||
&mut self.env
|
||||
}
|
||||
|
||||
pub fn cwd(&mut self, dir: &OsStr) {
|
||||
self.cwd = Some(dir.to_owned());
|
||||
}
|
||||
|
||||
pub fn stdin(&mut self, stdin: Stdio) {
|
||||
self.stdin = Some(stdin);
|
||||
}
|
||||
|
||||
pub fn stdout(&mut self, stdout: Stdio) {
|
||||
self.stdout = Some(stdout);
|
||||
}
|
||||
|
||||
pub fn stderr(&mut self, stderr: Stdio) {
|
||||
self.stderr = Some(stderr);
|
||||
}
|
||||
|
||||
pub fn get_program(&self) -> &OsStr {
|
||||
OsStr::from_bytes(self.program.as_bytes())
|
||||
}
|
||||
|
||||
pub fn get_args(&self) -> CommandArgs<'_> {
|
||||
let mut iter = CStringIter {
|
||||
iter: self.args.iter(),
|
||||
};
|
||||
iter.next();
|
||||
CommandArgs { iter }
|
||||
}
|
||||
|
||||
pub fn get_envs(&self) -> CommandEnvs<'_> {
|
||||
self.env.iter()
|
||||
}
|
||||
|
||||
pub fn get_env_clear(&self) -> bool {
|
||||
self.env.does_clear()
|
||||
}
|
||||
|
||||
pub fn get_current_dir(&self) -> Option<&Path> {
|
||||
self.cwd.as_ref().map(|cs| Path::new(cs))
|
||||
}
|
||||
|
||||
pub fn spawn(
|
||||
&mut self,
|
||||
_default: Stdio,
|
||||
_needs_stdin: bool,
|
||||
) -> io::Result<(Process, StdioPipes)> {
|
||||
let (args, ..) = Vec::into_raw_parts(self.args.clone());
|
||||
let child_pid = syscall::spawn(
|
||||
self.program.to_str().unwrap(),
|
||||
(self.args.len() - 1) as isize,
|
||||
args,
|
||||
);
|
||||
Ok((
|
||||
Process(child_pid as usize),
|
||||
StdioPipes {
|
||||
stdin: None,
|
||||
stdout: None,
|
||||
stderr: None,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output(_cmd: &mut Command) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
impl From<ChildPipe> for Stdio {
|
||||
fn from(pipe: ChildPipe) -> Stdio {
|
||||
pipe.diverge()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Stdout> for Stdio {
|
||||
fn from(_: io::Stdout) -> Stdio {
|
||||
Stdio::ParentStdout
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Stderr> for Stdio {
|
||||
fn from(_: io::Stderr) -> Stdio {
|
||||
Stdio::ParentStderr
|
||||
}
|
||||
}
|
||||
|
||||
impl From<File> for Stdio {
|
||||
fn from(file: File) -> Stdio {
|
||||
Stdio::InheritFile(file)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Command {
|
||||
// show all attributes
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if f.alternate() {
|
||||
let mut debug_command = f.debug_struct("Command");
|
||||
debug_command
|
||||
.field("program", &self.program)
|
||||
.field("args", &self.args);
|
||||
if !self.env.is_unchanged() {
|
||||
debug_command.field("env", &self.env);
|
||||
}
|
||||
|
||||
if self.cwd.is_some() {
|
||||
debug_command.field("cwd", &self.cwd);
|
||||
}
|
||||
|
||||
if self.stdin.is_some() {
|
||||
debug_command.field("stdin", &self.stdin);
|
||||
}
|
||||
if self.stdout.is_some() {
|
||||
debug_command.field("stdout", &self.stdout);
|
||||
}
|
||||
if self.stderr.is_some() {
|
||||
debug_command.field("stderr", &self.stderr);
|
||||
}
|
||||
|
||||
debug_command.finish()
|
||||
} else {
|
||||
if let Some(ref cwd) = self.cwd {
|
||||
write!(f, "cd {cwd:?} && ")?;
|
||||
}
|
||||
if self.env.does_clear() {
|
||||
write!(f, "env -i ")?;
|
||||
// Altered env vars will be printed next, that should exactly work as expected.
|
||||
} else {
|
||||
// Removed env vars need the command to be wrapped in `env`.
|
||||
let mut any_removed = false;
|
||||
for (key, value_opt) in self.get_envs() {
|
||||
if value_opt.is_none() {
|
||||
if !any_removed {
|
||||
write!(f, "env ")?;
|
||||
any_removed = true;
|
||||
}
|
||||
write!(f, "-u {} ", key.to_string_lossy())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Altered env vars can just be added in front of the program.
|
||||
for (key, value_opt) in self.get_envs() {
|
||||
if let Some(value) = value_opt {
|
||||
write!(f, "{}={value:?} ", key.to_string_lossy())?;
|
||||
}
|
||||
}
|
||||
// if self.program != self.args[0] {
|
||||
// write!(f, "[{:?}] ", self.program)?;
|
||||
// }
|
||||
// write!(f, "{:?}", self.args[0])?;
|
||||
|
||||
// for arg in &self.args[1..] {
|
||||
// write!(f, " {:?}", arg)?;
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct ExitStatus();
|
||||
|
||||
impl ExitStatus {
|
||||
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn code(&self) -> Option<i32> {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExitStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<dummy exit status>")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExitStatusError(!);
|
||||
|
||||
impl Clone for ExitStatusError {
|
||||
fn clone(&self) -> ExitStatusError {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for ExitStatusError {}
|
||||
|
||||
impl PartialEq for ExitStatusError {
|
||||
fn eq(&self, _other: &ExitStatusError) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ExitStatusError {}
|
||||
|
||||
impl fmt::Debug for ExitStatusError {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ExitStatus> for ExitStatusError {
|
||||
fn into(self) -> ExitStatus {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ExitStatusError {
|
||||
pub fn code(self) -> Option<NonZero<i32>> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ExitCode(u8);
|
||||
|
||||
impl ExitCode {
|
||||
pub const SUCCESS: ExitCode = ExitCode(0);
|
||||
pub const FAILURE: ExitCode = ExitCode(1);
|
||||
|
||||
pub fn as_i32(&self) -> i32 {
|
||||
self.0 as i32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for ExitCode {
|
||||
fn from(code: u8) -> Self {
|
||||
Self(code)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Process(usize);
|
||||
|
||||
impl Process {
|
||||
pub fn id(&self) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn kill(&mut self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn wait(&mut self) -> io::Result<ExitStatus> {
|
||||
syscall::waitpid(self.0);
|
||||
Ok(ExitStatus())
|
||||
}
|
||||
|
||||
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandArgs<'a> {
|
||||
iter: CStringIter<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for CommandArgs<'a> {
|
||||
type Item = &'a OsStr;
|
||||
fn next(&mut self) -> Option<&'a OsStr> {
|
||||
self.iter.next().map(|os| OsStr::from_bytes(os.to_bytes()))
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for CommandArgs<'a> {
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.iter.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for CommandArgs<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self.iter.clone()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub type ChildPipe = crate::sys::pipe::Pipe;
|
||||
|
||||
pub fn read_output(
|
||||
out: ChildPipe,
|
||||
_stdout: &mut Vec<u8>,
|
||||
_err: ChildPipe,
|
||||
_stderr: &mut Vec<u8>,
|
||||
) -> io::Result<()> {
|
||||
match out.diverge() {}
|
||||
}
|
||||
|
||||
pub fn getpid() -> u32 {
|
||||
panic!("no pids on this platform")
|
||||
}
|
||||
|
||||
/// An iterator over all `CStr`s contained in a `CStringArray`.
|
||||
#[derive(Clone)]
|
||||
pub struct CStringIter<'a> {
|
||||
iter: crate::slice::Iter<'a, *const u8>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for CStringIter<'a> {
|
||||
type Item = &'a CStr;
|
||||
fn next(&mut self) -> Option<&'a CStr> {
|
||||
// SAFETY:
|
||||
// `CStringArray` owns all of its strings. Also, this is not the null
|
||||
// pointer since the last element is excluded when creating `iter`.
|
||||
self.iter.next().map(|&p| unsafe { CStr::from_ptr(p) })
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for CStringIter<'a> {
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.iter.is_empty()
|
||||
}
|
||||
}
|
||||
@@ -1 +1,5 @@
|
||||
#![allow(unused)]
|
||||
|
||||
pub mod circular_buffer;
|
||||
pub mod sized_string;
|
||||
pub mod sized_vec;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use core::mem::MaybeUninit;
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
#[derive(Debug, Copy)]
|
||||
pub struct CircularBuffer<T, const SIZE: usize> {
|
||||
@@ -49,13 +52,36 @@ impl<T, const SIZE: usize> CircularBuffer<T, SIZE> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: T) {
|
||||
pub fn push(&mut self, value: T) -> bool {
|
||||
self.buffer[self.tail] = MaybeUninit::new(value);
|
||||
self.tail = (self.tail + 1) % SIZE;
|
||||
if self.is_full() {
|
||||
self.head = (self.head + 1) % SIZE;
|
||||
true
|
||||
} else {
|
||||
self.len += 1;
|
||||
}
|
||||
self.tail = (self.tail + 1) % SIZE;
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const SIZE: usize> Index<usize> for CircularBuffer<T, SIZE> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
if self.len() > index {
|
||||
unsafe { self.buffer[(self.head + index) % SIZE].assume_init_ref() }
|
||||
} else {
|
||||
panic!("index is bigger than circular buffer")
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T, const SIZE: usize> IndexMut<usize> for CircularBuffer<T, SIZE> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
if self.len() > index {
|
||||
unsafe { self.buffer[(self.head + index) % SIZE].assume_init_mut() }
|
||||
} else {
|
||||
panic!("index is bigger than circular buffer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
81
src/data_structures/sized_string.rs
Normal file
81
src/data_structures/sized_string.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use core::ops::Index;
|
||||
|
||||
use crate::data_structures::sized_vec::SizedVec;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct SizedString<const SIZE: usize> {
|
||||
pub vec: SizedVec<u8, SIZE>,
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> SizedString<SIZE> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
vec: SizedVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.vec.len()
|
||||
}
|
||||
|
||||
// pub fn is_empty(&self) -> bool {
|
||||
// self.len() == 0
|
||||
// }
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.vec.is_full()
|
||||
}
|
||||
|
||||
// pub fn pop(&mut self) -> Option<T> {
|
||||
// if self.is_empty() {
|
||||
// None
|
||||
// } else {
|
||||
// let out = core::mem::replace(&mut self.buffer[self.len - 1], MaybeUninit::uninit());
|
||||
// self.len -= 1;
|
||||
// // # Safety
|
||||
// // the queue is not empty and tail points to a valid value
|
||||
// Some(unsafe { out.assume_init() })
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn push(&mut self, ch: char) -> bool {
|
||||
let mut buf = [0; 4];
|
||||
ch.encode_utf8(&mut buf);
|
||||
if self.len() + ch.len_utf8() > SIZE {
|
||||
return false;
|
||||
}
|
||||
buf.into_iter().take(ch.len_utf8()).for_each(|c| {
|
||||
self.vec.push(c);
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, idx: usize) -> char {
|
||||
let ch = match self[idx..].chars().next() {
|
||||
Some(ch) => ch,
|
||||
None => panic!("cannot remove a char from the end of a string"),
|
||||
};
|
||||
for _ in 0..ch.len_utf8() {
|
||||
self.remove(idx);
|
||||
}
|
||||
ch
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
// SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error
|
||||
// at construction.
|
||||
unsafe { str::from_utf8_unchecked(self.vec.as_slice()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, const SIZE: usize> Index<I> for SizedString<SIZE>
|
||||
where
|
||||
I: core::slice::SliceIndex<str>,
|
||||
{
|
||||
type Output = I::Output;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: I) -> &I::Output {
|
||||
index.index(self.as_str())
|
||||
}
|
||||
}
|
||||
102
src/data_structures/sized_vec.rs
Normal file
102
src/data_structures/sized_vec.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
#[derive(Debug, Copy)]
|
||||
pub struct SizedVec<T, const SIZE: usize> {
|
||||
buffer: [MaybeUninit<T>; SIZE],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<T: Copy, const SIZE: usize> Clone for SizedVec<T, SIZE> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const SIZE: usize> SizedVec<T, SIZE> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
buffer: [const { MaybeUninit::uninit() }; SIZE],
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
// pub fn is_empty(&self) -> bool {
|
||||
// self.len() == 0
|
||||
// }
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.len() == SIZE - 1
|
||||
}
|
||||
|
||||
// pub fn pop(&mut self) -> Option<T> {
|
||||
// if self.is_empty() {
|
||||
// None
|
||||
// } else {
|
||||
// let out = core::mem::replace(&mut self.buffer[self.len - 1], MaybeUninit::uninit());
|
||||
// self.len -= 1;
|
||||
// // # Safety
|
||||
// // the queue is not empty and tail points to a valid value
|
||||
// Some(unsafe { out.assume_init() })
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn push(&mut self, value: T) -> bool {
|
||||
if self.is_full() {
|
||||
false
|
||||
} else {
|
||||
self.buffer[self.len] = MaybeUninit::new(value);
|
||||
self.len += 1;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, index: usize) -> T {
|
||||
if self.len() > index {
|
||||
unsafe {
|
||||
let ret;
|
||||
{
|
||||
let ptr = self.buffer.as_mut_ptr().add(index);
|
||||
ret = core::ptr::read(ptr);
|
||||
core::ptr::copy(ptr.add(1), ptr, self.len() - index - 1);
|
||||
}
|
||||
self.len -= 1;
|
||||
ret.assume_init()
|
||||
}
|
||||
} else {
|
||||
panic!("index is bigger than length of the SizedVec")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
// SAFETY: values are init before the tail
|
||||
unsafe { core::slice::from_raw_parts(self.buffer.as_ptr() as *const T, self.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const SIZE: usize> Index<usize> for SizedVec<T, SIZE> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
if self.len() > index {
|
||||
unsafe { self.buffer[index].assume_init_ref() }
|
||||
} else {
|
||||
panic!("index is bigger than length of the SizedVec")
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T, const SIZE: usize> IndexMut<usize> for SizedVec<T, SIZE> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
if self.len() > index {
|
||||
unsafe { self.buffer[index].assume_init_mut() }
|
||||
} else {
|
||||
panic!("index is bigger than length of the SizedVec")
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/draw.rs
10
src/draw.rs
@@ -2,7 +2,7 @@ use kernel_macros::include_bitmap_image;
|
||||
|
||||
/// 24-bit RGB color used by the framebuffer.
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Color(u32);
|
||||
|
||||
#[allow(unused)]
|
||||
@@ -22,6 +22,9 @@ impl Color {
|
||||
pub const RED: Color = Color::from_rgb(255, 0, 0);
|
||||
pub const GREEN: Color = Color::from_rgb(0, 255, 0);
|
||||
pub const BLUE: Color = Color::from_rgb(0, 0, 255);
|
||||
pub const YELLOW: Color = Color::from_rgb(0, 255, 255);
|
||||
pub const MAGENTA: Color = Color::from_rgb(255, 255, 0);
|
||||
pub const CYAN: Color = Color::from_rgb(43, 255, 255);
|
||||
}
|
||||
|
||||
pub trait Draw {
|
||||
@@ -100,6 +103,11 @@ pub trait Draw {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn clear_white(&mut self) {
|
||||
self.clear_screen(Color::BLACK);
|
||||
}
|
||||
|
||||
/// Return whether a pixel inside the embedded font plate is set.
|
||||
unsafe fn font_plate_index(x: u16, y: u16) -> bool {
|
||||
let pixel_index = (y as usize) * FONTPLATE_WIDTH + (x as usize);
|
||||
|
||||
@@ -31,7 +31,7 @@ pub static mut KBD_DRIVER: VirtioPciDriver<KeyboardState> = unsafe {
|
||||
|state, event| {
|
||||
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
|
||||
kbd_buffer
|
||||
.write(core::mem::transmute::<
|
||||
.write_all(core::mem::transmute::<
|
||||
&VirtioInputEvent,
|
||||
&[u8; size_of::<VirtioInputEvent>()],
|
||||
>(event))
|
||||
|
||||
30
src/fs.rs
30
src/fs.rs
@@ -8,7 +8,7 @@ use core::fmt::Debug;
|
||||
use alloc::boxed::Box;
|
||||
use bffs::{Fat32FileSystem, ReadSeek, entry::DirEntry, file::File};
|
||||
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
use io::{Read, Seek, Write};
|
||||
|
||||
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
|
||||
|
||||
@@ -31,12 +31,8 @@ impl Disk {
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for Disk {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Seek for Disk {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
match pos {
|
||||
io::SeekFrom::Start(pos) => self.pos = pos,
|
||||
io::SeekFrom::End(_) => unimplemented!(),
|
||||
@@ -48,7 +44,7 @@ impl Seek for Disk {
|
||||
|
||||
impl Read for Disk {
|
||||
/// Read bytes from the in-memory disk image into `buf`.
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
if self.pos >= self.size {
|
||||
return Ok(0);
|
||||
}
|
||||
@@ -92,41 +88,37 @@ impl<'a, T: ReadSeek> Fat32VirtualNode<'a, T> {
|
||||
|
||||
impl<T: ReadSeek + Debug> VirtualNode for Fat32VirtualNode<'_, T> {}
|
||||
|
||||
impl<T> IoBase for Fat32VirtualNode<'_, T> {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl<T: ReadSeek + Debug> Seek for Fat32VirtualNode<'_, T> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
match &mut self.kind {
|
||||
Fat32VirtualNodeType::Dir => todo!(),
|
||||
Fat32VirtualNodeType::File(file) => file.seek(pos).map_err(|_| ()),
|
||||
Fat32VirtualNodeType::File(file) => file.seek(pos),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ReadSeek + Debug> Read for Fat32VirtualNode<'_, T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
match &mut self.kind {
|
||||
Fat32VirtualNodeType::Dir => unimplemented!(),
|
||||
Fat32VirtualNodeType::File(file) => file.read(buf).map_err(|_| ()),
|
||||
Fat32VirtualNodeType::File(file) => file.read(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ReadSeek + Debug> Write for Fat32VirtualNode<'_, T> {
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, ()> {
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, io::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ReadSeek + Debug> VirtualFileSystem for Fat32FileSystem<T> {
|
||||
fn open(&mut self, path: &bffs::path::Path) -> Result<Box<dyn VirtualNode + '_>, ()> {
|
||||
let entry = self.open_entry(path).unwrap();
|
||||
fn open(&mut self, path: &bffs::path::Path) -> Result<Box<dyn VirtualNode + '_>, io::Error> {
|
||||
let entry = self.open_entry(path)?;
|
||||
Ok(Box::new(unsafe { Fat32VirtualNode::new(entry) }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,25 @@
|
||||
//!
|
||||
//! This module contains the low-level trap handlers for machine and supervisor
|
||||
//! modes and the syscall dispatch implementation used by user processes.
|
||||
use alloc::str;
|
||||
use alloc::{rc::Rc, str};
|
||||
use io::SeekFrom;
|
||||
use log::info;
|
||||
use shared::syscall::SysCall;
|
||||
|
||||
use crate::{
|
||||
boot::sbi::{ExtensionID, TimerFunctionID}, clear_csr, drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER}, println, process::{ExecutionContext, exit_process, sleep}, read_csr, riscv::{disable_interrupt, dump_cpu}, scheduler::SCHEDULER, set_csr, syscall, time::{IRQ_M_EXTERNAL, IRQ_M_TIMER, setup_next_timer_interrupt}, virtio::input::HANDLING_INTERRUPT, virtual_fs::{FILE_SYSTEM, VirtualFileSystem}, write_csr
|
||||
boot::sbi::{ExtensionID, TimerFunctionID},
|
||||
clear_csr,
|
||||
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
|
||||
process::{ExecutionContext, exit_process, sleep},
|
||||
read_csr,
|
||||
riscv::{disable_interrupt, dump_cpu},
|
||||
scheduler::SCHEDULER,
|
||||
set_csr, syscall,
|
||||
time::{IRQ_M_EXTERNAL, IRQ_M_TIMER, setup_next_timer_interrupt},
|
||||
virtio::input::HANDLING_INTERRUPT,
|
||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem},
|
||||
write_csr,
|
||||
};
|
||||
use core::{alloc::Layout, arch::naked_asm, time::Duration};
|
||||
use core::{alloc::Layout, arch::naked_asm, cell::RefCell, time::Duration};
|
||||
|
||||
use crate::time::{setup_timer_interrupt, timer_interrupt};
|
||||
|
||||
@@ -127,14 +137,17 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
let a1: u64 = unsafe { (*interrupt_state).a[1] };
|
||||
let a2: u64 = unsafe { (*interrupt_state).a[2] };
|
||||
let a3: u64 = unsafe { (*interrupt_state).a[3] };
|
||||
let a4: u64 = unsafe { (*interrupt_state).a[4] };
|
||||
let syscall: SysCall = syscall_u64.into();
|
||||
match syscall {
|
||||
SysCall::Open => {
|
||||
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
|
||||
let virtual_node = unsafe { FILE_SYSTEM.open(path.as_ref()).unwrap() };
|
||||
let virtual_node = Rc::new(RefCell::new(unsafe {
|
||||
FILE_SYSTEM.open(path.as_ref()).unwrap()
|
||||
}));
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let current_process = scheduler.get_current_process();
|
||||
let current_process = scheduler.get_current_process_mut();
|
||||
|
||||
let fd = if let Some(fd) =
|
||||
current_process.fd_table.iter().position(Option::is_none)
|
||||
@@ -153,10 +166,10 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
let fd = a1;
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let current_process = scheduler.get_current_process();
|
||||
let mut vnode = current_process.fd_table[fd as usize].take().unwrap();
|
||||
let current_process = scheduler.get_current_process_mut();
|
||||
let vnode = current_process.fd_table[fd as usize].take().unwrap();
|
||||
|
||||
vnode.close();
|
||||
vnode.borrow_mut().close();
|
||||
}
|
||||
SysCall::Write => {
|
||||
let fd = a1;
|
||||
@@ -164,9 +177,9 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
unsafe { core::slice::from_raw_parts(a2 as *const u8, a3 as usize) };
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let current_process = scheduler.get_current_process();
|
||||
let current_process = scheduler.get_current_process_mut();
|
||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||
let res = vnode.write(buf).unwrap();
|
||||
let res = vnode.borrow_mut().write(buf).unwrap();
|
||||
unsafe { (*interrupt_state).a[0] = res as u64 };
|
||||
}
|
||||
SysCall::Read => {
|
||||
@@ -174,11 +187,10 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
let buf =
|
||||
unsafe { core::slice::from_raw_parts_mut(a2 as *mut u8, a3 as usize) };
|
||||
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let current_process = scheduler.get_current_process();
|
||||
let current_process = scheduler.get_current_process_mut();
|
||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||
let res = vnode.read(buf).unwrap();
|
||||
let res = vnode.borrow_mut().read(buf).unwrap();
|
||||
if res == 0 && !buf.is_empty() {
|
||||
loop_syscall(interrupt_state);
|
||||
scheduler.schedule(&mut interrupt_state);
|
||||
@@ -195,14 +207,22 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let current_process = scheduler.get_current_process();
|
||||
let current_process = scheduler.get_current_process_mut();
|
||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||
vnode.seek(seek).unwrap();
|
||||
vnode.borrow_mut().seek(seek).unwrap();
|
||||
}
|
||||
SysCall::Spawn => {
|
||||
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
|
||||
let argc = a3 as isize;
|
||||
let argv = a4 as *const *const u8;
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
scheduler.create_process_from_file(path, 0, core::ptr::null());
|
||||
let current_process = scheduler.get_current_process();
|
||||
let fd = current_process.fd_table.clone();
|
||||
let new_process = scheduler.create_process_from_file(path, argc, argv);
|
||||
new_process.fd_table = fd;
|
||||
|
||||
unsafe { (*interrupt_state).a[0] = new_process.pid as u64 };
|
||||
}
|
||||
SysCall::ExecVE => {
|
||||
// let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
|
||||
@@ -210,6 +230,16 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
// scheduler.create_process_from_file(path, &[]);
|
||||
unimplemented!("ExecVE is not implemented")
|
||||
}
|
||||
SysCall::WaitPid => {
|
||||
let pid = a1;
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let alive = scheduler.is_process_alive(pid);
|
||||
if alive {
|
||||
loop_syscall(interrupt_state);
|
||||
scheduler.schedule(&mut interrupt_state);
|
||||
}
|
||||
}
|
||||
SysCall::Alloc => {
|
||||
let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap();
|
||||
// Allocate memory and put the pointer in a0
|
||||
@@ -223,14 +253,6 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
}
|
||||
SysCall::Exit => exit_process(&mut interrupt_state),
|
||||
SysCall::NanoSleep => sleep(Duration::new(a1, a2 as u32), &mut interrupt_state),
|
||||
SysCall::WriteTemp => {
|
||||
info!("Print from user space : {}", unsafe {
|
||||
str::from_raw_parts(a1 as *const u8, a2 as usize)
|
||||
})
|
||||
}
|
||||
SysCall::WriteIntTemp => {
|
||||
info!("Print from user space int : {}, {:x}", a1, a1)
|
||||
}
|
||||
SysCall::Unimplemented => {
|
||||
unimplemented!("Syscall {syscall_u64} is not implemented")
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
//! Provides a lightweight logger implementation routing to UART and helper
|
||||
//! macros for printing from kernel code.
|
||||
|
||||
use crate::print;
|
||||
use crate::tty::{TTY_INITIALIZED, TTY0};
|
||||
|
||||
use alloc::format;
|
||||
|
||||
39
src/main.rs
39
src/main.rs
@@ -5,7 +5,7 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
// #![warn(clippy::pedantic)]
|
||||
#![allow(static_mut_refs)]
|
||||
#![allow(static_mut_refs, internal_features)]
|
||||
#![feature(
|
||||
riscv_ext_intrinsics,
|
||||
str_from_raw_parts,
|
||||
@@ -13,14 +13,13 @@
|
||||
derive_const,
|
||||
const_cmp,
|
||||
const_trait_impl,
|
||||
trait_alias
|
||||
trait_alias,
|
||||
prelude_import,
|
||||
slice_index_methods
|
||||
)]
|
||||
|
||||
use core::sync::atomic::AtomicBool;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use embedded_alloc::LlffHeap as Heap;
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
|
||||
@@ -28,12 +27,16 @@ use crate::{
|
||||
pci::scan_virtio_devices,
|
||||
riscv::enable_supervisor_interrupt,
|
||||
scheduler::{SCHEDULER, idle},
|
||||
user::{proc2, test},
|
||||
vga::Vga,
|
||||
virtio::input::init_plic_pci,
|
||||
virtual_fs::init_file_system,
|
||||
};
|
||||
|
||||
mod prelude;
|
||||
#[allow(unused_imports)]
|
||||
#[prelude_import]
|
||||
pub use prelude::*;
|
||||
|
||||
extern crate alloc;
|
||||
mod boot;
|
||||
mod critical_section;
|
||||
@@ -55,7 +58,6 @@ mod syscall;
|
||||
mod time;
|
||||
mod tty;
|
||||
mod uart;
|
||||
mod user;
|
||||
mod vga;
|
||||
mod virtio;
|
||||
mod virtual_console;
|
||||
@@ -83,23 +85,6 @@ pub extern "C" fn supervisor_mode_entry() {
|
||||
SCHEDULER.lock().init();
|
||||
}
|
||||
|
||||
info!("Hello World !");
|
||||
// unsafe { Vga.draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
|
||||
|
||||
// let binding = Box::leak(Box::new(["coucou".as_bytes()]));
|
||||
SCHEDULER
|
||||
.lock()
|
||||
.create_process(Box::new(test), "proc1", 0, core::ptr::null());
|
||||
SCHEDULER
|
||||
.lock()
|
||||
.create_process(Box::new(proc2), "proc2", 0, core::ptr::null());
|
||||
|
||||
SCHEDULER
|
||||
.lock()
|
||||
.create_process_from_file("/usr/bin/test_pic", 0, core::ptr::null());
|
||||
|
||||
enable_supervisor_interrupt();
|
||||
|
||||
unsafe {
|
||||
let (pci_keyboard, pci_mouse) = scan_virtio_devices();
|
||||
let (pci_keyboard, pci_mouse) = (pci_keyboard.unwrap(), pci_mouse.unwrap());
|
||||
@@ -121,5 +106,11 @@ pub extern "C" fn supervisor_mode_entry() {
|
||||
init_plic_pci(pci_keyboard.irq);
|
||||
init_plic_pci(pci_mouse.irq);
|
||||
}
|
||||
|
||||
SCHEDULER
|
||||
.lock()
|
||||
.create_process_from_file("/usr/bin/agetty", 0, core::ptr::null());
|
||||
enable_supervisor_interrupt();
|
||||
|
||||
idle(0, core::ptr::null());
|
||||
}
|
||||
|
||||
@@ -4,26 +4,19 @@
|
||||
//! framebuffer before halting the CPU.
|
||||
use core::arch::riscv64::wfi;
|
||||
|
||||
use alloc::{
|
||||
format,
|
||||
string::{String, ToString},
|
||||
};
|
||||
use alloc::string::ToString;
|
||||
use log::error;
|
||||
|
||||
use crate::{
|
||||
HEAP_INITIALIZED,
|
||||
draw::{Color, Draw, FONT_HEIGHT},
|
||||
uart::write_uart,
|
||||
vga::Vga,
|
||||
};
|
||||
use crate::{HEAP_INITIALIZED, uart::write_uart};
|
||||
|
||||
fn print_stack_trace() {
|
||||
fn print_stack_trace() -> String {
|
||||
let mut trace = String::new();
|
||||
let mut fp: usize;
|
||||
unsafe {
|
||||
core::arch::asm!("mv {}, s0", out(reg) fp);
|
||||
}
|
||||
|
||||
error!("STACK TRACE:");
|
||||
trace.push_str("STACK TRACE:\n");
|
||||
for i in 0..10 {
|
||||
if fp == 0 || !fp.is_multiple_of(8) {
|
||||
break;
|
||||
@@ -33,10 +26,11 @@ fn print_stack_trace() {
|
||||
// fp - 8 => Return Address (ra)
|
||||
// fp - 16 => Previous Frame Pointer
|
||||
let ra = *((fp - 8) as *const usize);
|
||||
error!(" [{}] 0x{:016x} {}", i, ra, resolve_symbol(ra));
|
||||
trace += &format!(" [{}] 0x{:016x} {}\n", i, ra, resolve_symbol(ra));
|
||||
fp = *((fp - 16) as *const usize);
|
||||
}
|
||||
}
|
||||
trace
|
||||
}
|
||||
#[panic_handler]
|
||||
/// Kernel panic handler that displays the panic message on the framebuffer and halts.
|
||||
@@ -50,19 +44,8 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
|
||||
panic_message = format!("{panic_message} at {}:{}", location.file(), location.line());
|
||||
}
|
||||
error!("{panic_message}");
|
||||
print_stack_trace();
|
||||
|
||||
Vga.clear_screen(Color::WHITE);
|
||||
unsafe { Vga.draw_string(0, 0, "PANIC !", Color::BLACK, Color::WHITE) };
|
||||
unsafe {
|
||||
Vga.draw_string(
|
||||
0,
|
||||
FONT_HEIGHT as u16,
|
||||
panic_message,
|
||||
Color::BLACK,
|
||||
Color::WHITE,
|
||||
)
|
||||
};
|
||||
let stack_trace = print_stack_trace();
|
||||
error!("{stack_trace}");
|
||||
}
|
||||
|
||||
loop {
|
||||
|
||||
@@ -6,7 +6,6 @@ use core::{
|
||||
use bytes_struct::VolatilePackedStruct;
|
||||
|
||||
use crate::{
|
||||
println,
|
||||
virtio::{
|
||||
VirtioCapability, VirtioCapabilityType, VirtioNotificationCapability, VirtioPciCommonCfg,
|
||||
},
|
||||
|
||||
6
src/prelude.rs
Normal file
6
src/prelude.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
pub use core::prelude::rust_2024::*;
|
||||
|
||||
pub use crate::{print, println};
|
||||
pub use alloc::{boxed::Box, format, string::String, vec::Vec};
|
||||
@@ -7,9 +7,9 @@
|
||||
//! keeps unsafe usage localized and documented where raw pointers or transmute
|
||||
//! are required.
|
||||
|
||||
use core::time::Duration;
|
||||
use core::{cell::RefCell, time::Duration};
|
||||
|
||||
use alloc::{boxed::Box, format, string::String, vec::Vec};
|
||||
use alloc::rc::Rc;
|
||||
use bffs::path::Path;
|
||||
use goblin::elf::reloc::R_RISCV_RELATIVE;
|
||||
use shared::syscall::exit;
|
||||
@@ -88,7 +88,8 @@ pub struct Process {
|
||||
/// Process stack.
|
||||
pub stack: Box<[u64; STACK_SIZE]>,
|
||||
/// File descriptor table.
|
||||
pub fd_table: Vec<Option<Box<dyn VirtualNode>>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fd_table: Vec<Option<Rc<RefCell<Box<dyn VirtualNode>>>>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Process {}
|
||||
@@ -157,12 +158,20 @@ impl Scheduler {
|
||||
path: T,
|
||||
argc: isize,
|
||||
argv: *const *const u8,
|
||||
) -> i64 {
|
||||
) -> &mut Process {
|
||||
let path = path.as_ref();
|
||||
let name = path.as_str();
|
||||
|
||||
// Open and read the binary file
|
||||
let mut bin = unsafe { FILE_SYSTEM.open(path).unwrap() };
|
||||
let mut bin = if let Ok(file) = unsafe { FILE_SYSTEM.open(path) } {
|
||||
file
|
||||
} else {
|
||||
unsafe {
|
||||
FILE_SYSTEM
|
||||
.open(format!("/usr/bin/{}", path).as_str().as_ref())
|
||||
.unwrap()
|
||||
}
|
||||
};
|
||||
println!("Creating process");
|
||||
let mut content: Vec<u8> = Vec::new();
|
||||
bin.read_to_end(&mut content).unwrap();
|
||||
@@ -295,7 +304,7 @@ impl Scheduler {
|
||||
name: T,
|
||||
argc: isize,
|
||||
argv: *const *const u8,
|
||||
) -> i64 {
|
||||
) -> &mut Process {
|
||||
// SAFETY: Initializing process in the global table.
|
||||
// Access is safe because we verified bounds and found a Dead slot.
|
||||
unsafe {
|
||||
@@ -314,17 +323,17 @@ impl Scheduler {
|
||||
// Box::new(TTY0.clone()),
|
||||
// );
|
||||
// FD 0
|
||||
process
|
||||
.fd_table
|
||||
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
|
||||
process.fd_table.push(Some(Rc::new(RefCell::new(
|
||||
FILE_SYSTEM.open("/dev/null".as_ref()).unwrap(),
|
||||
))));
|
||||
// FD 1
|
||||
process
|
||||
.fd_table
|
||||
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
|
||||
process.fd_table.push(Some(Rc::new(RefCell::new(
|
||||
FILE_SYSTEM.open("/dev/null".as_ref()).unwrap(),
|
||||
))));
|
||||
// FD 2
|
||||
process
|
||||
.fd_table
|
||||
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
|
||||
process.fd_table.push(Some(Rc::new(RefCell::new(
|
||||
FILE_SYSTEM.open("/dev/null".as_ref()).unwrap(),
|
||||
))));
|
||||
|
||||
// Configure execution context
|
||||
// a0 contains the pointer to the function to execute
|
||||
@@ -343,7 +352,7 @@ impl Scheduler {
|
||||
|
||||
self.next_pid += 1;
|
||||
|
||||
(self.next_pid - 1) as i64
|
||||
process
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -426,7 +435,7 @@ pub fn exit_process(interrupt_context: &mut *mut ExecutionContext) {
|
||||
pub fn sleep(duration: Duration, interrupt_context: &mut *mut ExecutionContext) {
|
||||
// SAFETY: ACTIVE_PID is maintained by the scheduler and is always valid.
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let process = scheduler.get_current_process();
|
||||
let process = scheduler.get_current_process_mut();
|
||||
process.wake_time = elapsed_time_since_startup() + duration;
|
||||
process.state = ProcessState::Asleep;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ use core::arch::naked_asm;
|
||||
|
||||
use alloc::fmt::format;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
|
||||
use crate::clear_csr;
|
||||
use crate::read_csr;
|
||||
|
||||
@@ -94,8 +94,22 @@ impl Scheduler {
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn get_current_process(&mut self) -> &mut Process {
|
||||
pub fn get_process_mut(&mut self, pid: u64) -> Option<&mut Process> {
|
||||
self.process_table.get_mut(&pid)
|
||||
}
|
||||
pub fn get_process(&self, pid: u64) -> Option<&Process> {
|
||||
self.process_table.get(&pid)
|
||||
}
|
||||
pub fn is_process_alive(&self, pid: u64) -> bool {
|
||||
self.get_process(pid)
|
||||
.is_some_and(|process| process.state != ProcessState::Dead)
|
||||
}
|
||||
pub fn get_current_process_mut(&mut self) -> &mut Process {
|
||||
let active_pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
self.process_table.get_mut(&active_pid).unwrap()
|
||||
}
|
||||
pub fn get_current_process(&self) -> &Process {
|
||||
let active_pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
self.process_table.get(&active_pid).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
18
src/tty.rs
18
src/tty.rs
@@ -4,7 +4,7 @@ use core::{
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
use io::{Read, Seek, Write};
|
||||
|
||||
use crate::{
|
||||
data_structures::circular_buffer::CircularBuffer,
|
||||
@@ -47,21 +47,17 @@ impl VirtualFileSystem for Tty {
|
||||
fn open(
|
||||
&mut self,
|
||||
path: &bffs::path::Path,
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, ()> {
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, io::Error> {
|
||||
if !path.is_empty() {
|
||||
Err(())
|
||||
Err(io::Error::from(io::ErrorKind::NotADirectory))
|
||||
} else {
|
||||
Ok(Box::new(self.new_node()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for TtyNode<'_> {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for TtyNode<'_> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
let mut buffer = self.tty.buffer.borrow_mut();
|
||||
let max_len = buffer.len();
|
||||
(0..buf.len().min(max_len)).for_each(|i| {
|
||||
@@ -72,13 +68,13 @@ impl Read for TtyNode<'_> {
|
||||
}
|
||||
|
||||
impl Seek for TtyNode<'_> {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for TtyNode<'_> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||
critical_section::with(|_| {
|
||||
self.tty
|
||||
.console
|
||||
@@ -88,7 +84,7 @@ impl Write for TtyNode<'_> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
22
src/user.rs
22
src/user.rs
@@ -1,22 +0,0 @@
|
||||
//! Example user processes used for testing and demonstrations.
|
||||
//!
|
||||
//! Provides a couple of simple user-space loops used to exercise syscalls
|
||||
//! and the scheduler.
|
||||
use core::time::Duration;
|
||||
|
||||
use shared::syscall::{sleep, write_string_temp};
|
||||
|
||||
pub fn test(_argc: isize, _argv: *const *const u8) {
|
||||
loop {
|
||||
// write_string_temp(str::from_utf8(_args[0]).unwrap());
|
||||
write_string_temp("test");
|
||||
sleep(Duration::new(2, 0));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn proc2(_argc: isize, _argv: *const *const u8) {
|
||||
loop {
|
||||
write_string_temp("proc2");
|
||||
sleep(Duration::new(3, 0));
|
||||
}
|
||||
}
|
||||
18
src/vga.rs
18
src/vga.rs
@@ -84,6 +84,24 @@ impl Draw for Vga {
|
||||
unsafe { *VGA_ADDRESS.add(pixel_index) = color }
|
||||
}
|
||||
|
||||
fn clear_screen(&mut self, color: Color) {
|
||||
for y in 0..self.get_height() {
|
||||
for x in 0..self.get_width() {
|
||||
unsafe { self.write_pixel_unsafe(x as u16, y as u16, color) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_white(&mut self) {
|
||||
unsafe {
|
||||
core::ptr::write_bytes(
|
||||
VGA_ADDRESS,
|
||||
255,
|
||||
self.get_height() * self.get_width() * size_of::<Color>(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
fn get_width(&self) -> usize {
|
||||
WIDTH
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ use alloc::boxed::Box;
|
||||
use io::SeekFrom;
|
||||
|
||||
use crate::{
|
||||
data_structures::{
|
||||
circular_buffer::CircularBuffer, sized_string::SizedString, sized_vec::SizedVec,
|
||||
},
|
||||
draw::{Color, Draw, FONT_HEIGHT, FONT_WIDTH},
|
||||
uart::write_uart,
|
||||
vga::Vga,
|
||||
@@ -25,15 +28,29 @@ impl Cursor {
|
||||
#[derive(Debug)]
|
||||
pub struct VirtualConsole {
|
||||
cursor: Cursor,
|
||||
content_cursor: Cursor,
|
||||
content: CircularBuffer<SizedVec<char, 256>, 256>,
|
||||
framebuffer: Box<dyn virtual_fs::VirtualNode>,
|
||||
|
||||
is_in_ansii_mode: bool,
|
||||
current_ansii: SizedString<20>,
|
||||
fg_color: Color,
|
||||
}
|
||||
|
||||
impl VirtualConsole {
|
||||
pub fn new() -> Self {
|
||||
VirtualConsole {
|
||||
let mut vc = VirtualConsole {
|
||||
cursor: Cursor::new(),
|
||||
content_cursor: Cursor::new(),
|
||||
content: CircularBuffer::new(),
|
||||
framebuffer: Box::new(VGAFileSystem.open_vga()),
|
||||
}
|
||||
|
||||
is_in_ansii_mode: false,
|
||||
current_ansii: SizedString::new(),
|
||||
fg_color: Color::WHITE,
|
||||
};
|
||||
vc.content.push(SizedVec::new());
|
||||
vc
|
||||
}
|
||||
|
||||
pub fn write_str(&mut self, s: &str) {
|
||||
@@ -42,18 +59,29 @@ impl VirtualConsole {
|
||||
}
|
||||
|
||||
pub fn write_char(&mut self, c: char) {
|
||||
if self.is_in_ansii_mode {
|
||||
return self.handle_ansii(c);
|
||||
}
|
||||
let mut last_cursor = self.cursor;
|
||||
match c {
|
||||
'\n' => {
|
||||
self.cursor.x = 0;
|
||||
self.cursor.y += 1;
|
||||
self.content_cursor.x = 0;
|
||||
if !self.content.is_full() {
|
||||
self.content_cursor.y += 1;
|
||||
self.content.push(SizedVec::new());
|
||||
}
|
||||
}
|
||||
'\r' => {
|
||||
self.cursor.x = 0;
|
||||
self.content_cursor.x = 0;
|
||||
}
|
||||
'\x08' if self.cursor.x > 0 => {
|
||||
// Backspace
|
||||
self.cursor.x -= 1;
|
||||
// self.content[self.content_cursor.y as usize].remove(self.content_cursor.x as usize);
|
||||
self.content_cursor.x -= 1;
|
||||
for y in 0..FONT_HEIGHT as u16 {
|
||||
for x in 0..FONT_WIDTH as u16 {
|
||||
unsafe {
|
||||
@@ -68,6 +96,15 @@ impl VirtualConsole {
|
||||
}
|
||||
'\t' => {
|
||||
self.cursor.x = (self.cursor.x / TAB_SIZE + 1) * TAB_SIZE;
|
||||
|
||||
// let last = self.content_cursor.x;
|
||||
// self.content_cursor.x = (self.content_cursor.x / TAB_SIZE + 1) * TAB_SIZE;
|
||||
// for _ in last..self.content_cursor.x {
|
||||
|
||||
// }
|
||||
}
|
||||
'\x1b' => {
|
||||
self.is_in_ansii_mode = true;
|
||||
}
|
||||
_ if c as u64 <= 42 * 32 && c >= 32 as char => {
|
||||
unsafe {
|
||||
@@ -75,11 +112,18 @@ impl VirtualConsole {
|
||||
(self.cursor.x * FONT_WIDTH as u64) as u16,
|
||||
(self.cursor.y * FONT_HEIGHT as u64) as u16,
|
||||
c,
|
||||
Color::WHITE,
|
||||
self.fg_color,
|
||||
Color::BLACK,
|
||||
)
|
||||
};
|
||||
self.cursor.x += 1;
|
||||
let row = &mut self.content[self.content_cursor.y as usize];
|
||||
if row.len() > self.content_cursor.x as usize {
|
||||
row[self.content_cursor.x as usize] = c;
|
||||
} else {
|
||||
row.push(c);
|
||||
}
|
||||
self.content_cursor.x += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -97,6 +141,35 @@ impl VirtualConsole {
|
||||
self.move_cursor_line(last_cursor);
|
||||
}
|
||||
}
|
||||
fn handle_ansii(&mut self, c: char) {
|
||||
self.current_ansii.push(c);
|
||||
if c.is_ascii_alphabetic() || self.current_ansii.is_full() {
|
||||
if let Ok(ansii) = ansii::parse_ansii(&mut self.current_ansii.as_str()) {
|
||||
match ansii {
|
||||
ansii::AnsiiEscape::Color16(
|
||||
ansii::ColorPlace::Foreground,
|
||||
ansii::Color16Type::Normal,
|
||||
c,
|
||||
) => {
|
||||
self.fg_color = match c {
|
||||
30 => Color::BLACK,
|
||||
31 => Color::RED,
|
||||
32 => Color::GREEN,
|
||||
33 => Color::YELLOW,
|
||||
34 => Color::BLUE,
|
||||
35 => Color::MAGENTA,
|
||||
36 => Color::CYAN,
|
||||
37 | 39 => Color::WHITE,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
self.is_in_ansii_mode = false;
|
||||
self.current_ansii = SizedString::new();
|
||||
}
|
||||
}
|
||||
|
||||
fn line_up(&mut self) {
|
||||
// TODO avoid calling Vga directly
|
||||
|
||||
@@ -6,9 +6,10 @@ use bffs::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
use io::{Read, Seek, Write};
|
||||
|
||||
pub mod keyboard;
|
||||
pub mod meminfo;
|
||||
pub mod null;
|
||||
pub mod stdin;
|
||||
pub mod virtual_stdin;
|
||||
@@ -17,15 +18,17 @@ use crate::{
|
||||
fs::Disk,
|
||||
tty::TTY0,
|
||||
vga::Vga,
|
||||
virtual_fs::{keyboard::KeyboardBuffer, null::Null, virtual_stdin::VirtualStdin},
|
||||
virtual_fs::{
|
||||
keyboard::KeyboardBuffer, meminfo::MemInfo, null::Null, virtual_stdin::VirtualStdin,
|
||||
},
|
||||
};
|
||||
|
||||
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {
|
||||
pub trait VirtualNode: Read + Write + Seek + Debug {
|
||||
fn close(&mut self) {}
|
||||
}
|
||||
|
||||
pub trait VirtualFileSystem: Debug {
|
||||
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, ()>;
|
||||
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, io::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -41,7 +44,7 @@ impl MainFileSystem {
|
||||
}
|
||||
|
||||
impl VirtualFileSystem for MainFileSystem {
|
||||
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, ()> {
|
||||
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, io::Error> {
|
||||
let mut max = &mut self.root;
|
||||
let mut max_path = Path::new("/");
|
||||
let mut path_remaining = path;
|
||||
@@ -71,6 +74,7 @@ pub unsafe fn init_file_system() {
|
||||
Box::new(KeyboardBuffer::new()),
|
||||
);
|
||||
FILE_SYSTEM.mount("/dev/stdin".into(), Box::new(VirtualStdin::new()));
|
||||
FILE_SYSTEM.mount("/proc/meminfo".into(), Box::new(MemInfo));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,12 +94,8 @@ pub struct VGAVirtualNode {
|
||||
|
||||
impl VirtualNode for VGAVirtualNode {}
|
||||
|
||||
impl IoBase for VGAVirtualNode {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Seek for VGAVirtualNode {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
self.position = match pos {
|
||||
io::SeekFrom::Start(v) => v,
|
||||
io::SeekFrom::End(v) => {
|
||||
@@ -109,7 +109,7 @@ impl Seek for VGAVirtualNode {
|
||||
}
|
||||
|
||||
impl Read for VGAVirtualNode {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
buf.iter_mut().for_each(|val| {
|
||||
*val = unsafe { Vga::read_u8_unsafe(self.position as usize) };
|
||||
});
|
||||
@@ -118,7 +118,7 @@ impl Read for VGAVirtualNode {
|
||||
}
|
||||
|
||||
impl Write for VGAVirtualNode {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, ()> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||
let start = self.position;
|
||||
buf.iter().for_each(|val| {
|
||||
unsafe { Vga::write_u8_unsafe(self.position as usize, *val) };
|
||||
@@ -127,15 +127,15 @@ impl Write for VGAVirtualNode {
|
||||
Ok((self.position - start) as usize)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualFileSystem for VGAFileSystem {
|
||||
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, ()> {
|
||||
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, io::Error> {
|
||||
if !path.is_empty() {
|
||||
Err(())
|
||||
Err(io::Error::from(io::ErrorKind::NotADirectory))
|
||||
} else {
|
||||
Ok(Box::new(self.open_vga()))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use core::cell::RefCell;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
use io::{Read, Seek, Write};
|
||||
|
||||
use crate::{
|
||||
data_structures::circular_buffer::CircularBuffer,
|
||||
@@ -38,21 +38,17 @@ impl VirtualFileSystem for KeyboardBuffer {
|
||||
fn open(
|
||||
&mut self,
|
||||
path: &bffs::path::Path,
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, ()> {
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, io::Error> {
|
||||
if !path.is_empty() {
|
||||
Err(())
|
||||
Err(io::Error::from(io::ErrorKind::NotADirectory))
|
||||
} else {
|
||||
Ok(Box::new(KeyboardBufferNode::new(self)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for KeyboardBufferNode<'_> {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for KeyboardBufferNode<'_> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
let mut buffer = self.buffer.buffer.borrow_mut();
|
||||
(0..buf.len()).for_each(|i| {
|
||||
buf[i] = buffer.pop().unwrap();
|
||||
@@ -62,13 +58,13 @@ impl Read for KeyboardBufferNode<'_> {
|
||||
}
|
||||
|
||||
impl Seek for KeyboardBufferNode<'_> {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for KeyboardBufferNode<'_> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||
let mut buffer = self.buffer.buffer.borrow_mut();
|
||||
for input in buf {
|
||||
buffer.push(*input);
|
||||
@@ -76,7 +72,7 @@ impl Write for KeyboardBufferNode<'_> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
48
src/virtual_fs/meminfo.rs
Normal file
48
src/virtual_fs/meminfo.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use alloc::boxed::Box;
|
||||
use io::{Read, Seek, Write};
|
||||
|
||||
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MemInfo;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MemInfoNode;
|
||||
|
||||
impl VirtualFileSystem for MemInfo {
|
||||
fn open(
|
||||
&mut self,
|
||||
path: &bffs::path::Path,
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, io::Error> {
|
||||
if !path.is_empty() {
|
||||
Err(io::Error::from(io::ErrorKind::NotADirectory))
|
||||
} else {
|
||||
Ok(Box::new(MemInfoNode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for MemInfoNode {
|
||||
fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
write!(buf, "uie")?;
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for MemInfoNode {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for MemInfoNode {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualNode for MemInfoNode {}
|
||||
@@ -1,5 +1,5 @@
|
||||
use alloc::boxed::Box;
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
use io::{Read, Seek, Write};
|
||||
|
||||
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
|
||||
|
||||
@@ -13,37 +13,33 @@ impl VirtualFileSystem for Null {
|
||||
fn open(
|
||||
&mut self,
|
||||
path: &bffs::path::Path,
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, ()> {
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, io::Error> {
|
||||
if !path.is_empty() {
|
||||
Err(())
|
||||
Err(io::Error::from(io::ErrorKind::NotADirectory))
|
||||
} else {
|
||||
Ok(Box::new(NullNode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for NullNode {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for NullNode {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for NullNode {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for NullNode {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use alloc::{boxed::Box, format};
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
use io::{Read, Seek, Write};
|
||||
|
||||
use crate::{
|
||||
scheduler::ACTIVE_PID,
|
||||
@@ -22,21 +22,18 @@ impl VirtualFileSystem for VirtualStdin {
|
||||
fn open(
|
||||
&mut self,
|
||||
path: &bffs::path::Path,
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, ()> {
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, io::Error> {
|
||||
if !path.is_empty() {
|
||||
Err(())
|
||||
Err(io::Error::from(io::ErrorKind::NotADirectory))
|
||||
} else {
|
||||
Ok(Box::new(VirtualStdinNode {}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for VirtualStdinNode {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for VirtualStdinNode {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
let pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
unsafe {
|
||||
FILE_SYSTEM
|
||||
@@ -48,7 +45,7 @@ impl Read for VirtualStdinNode {
|
||||
}
|
||||
|
||||
impl Seek for VirtualStdinNode {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
let pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
unsafe {
|
||||
FILE_SYSTEM
|
||||
@@ -60,7 +57,7 @@ impl Seek for VirtualStdinNode {
|
||||
}
|
||||
|
||||
impl Write for VirtualStdinNode {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||
let pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
unsafe {
|
||||
FILE_SYSTEM
|
||||
@@ -70,7 +67,7 @@ impl Write for VirtualStdinNode {
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
let pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
unsafe {
|
||||
FILE_SYSTEM
|
||||
|
||||
1
sysroot/lib/rustlib/src/rust/library
Symbolic link
1
sysroot/lib/rustlib/src/rust/library
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../library
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../library
|
||||
6
user/agetty/Cargo.toml
Normal file
6
user/agetty/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "agetty"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
29
user/agetty/src/main.rs
Normal file
29
user/agetty/src/main.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
#![allow(unused)]
|
||||
#![feature(survos_std)]
|
||||
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Stdin, Write, stdin, stdout};
|
||||
use std::os::fd::FromRawFd;
|
||||
|
||||
fn main() {
|
||||
// Close descriptor 0
|
||||
let f = unsafe { File::from_raw_fd(0) };
|
||||
drop(f);
|
||||
|
||||
// Close descriptor 1
|
||||
let f = unsafe { File::from_raw_fd(1) };
|
||||
drop(f);
|
||||
|
||||
// Open new input and output
|
||||
let mut input = File::open("/dev/tty0");
|
||||
let mut tty = File::open("/dev/tty0");
|
||||
|
||||
std::process::Command::new("/usr/bin/shell")
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
std::thread::yield_now();
|
||||
}
|
||||
}
|
||||
7
user/fastfetch/Cargo.toml
Normal file
7
user/fastfetch/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "fastfetch"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
owo-colors = "4.3.0"
|
||||
21
user/fastfetch/src/main.rs
Normal file
21
user/fastfetch/src/main.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
// #![feature(survos_std)]
|
||||
|
||||
use owo_colors::OwoColorize;
|
||||
|
||||
fn main() {
|
||||
println!(
|
||||
"{}",
|
||||
r" ___ ___ ___ ___ ___
|
||||
/ /\ /__/\ / /\ ___ / /\ / /\
|
||||
/ /:/_ \ \:\ / /::\ /__/\ / /::\ / /:/_
|
||||
/ /:/ /\ \ \:\ / /:/\:\ \ \:\ / /:/\:\ / /:/ /\
|
||||
/ /:/ /::\ ___ \ \:\ / /:/~/:/ \ \:\ / /:/ \:\ / /:/ /::\
|
||||
/__/:/ /:/\:/__/\ \__\:/__/:/ /:/______ \__\:/__/:/ \__\:/__/:/ /:/\:\
|
||||
\ \:\/:/~/:\ \:\ / /:\ \:\/:::::/__/\ | |:\ \:\ / /:\ \:\/:/~/:/
|
||||
\ \::/ /:/ \ \:\ /:/ \ \::/~~~~\ \:\| |:|\ \:\ /:/ \ \::/ /:/
|
||||
\__\/ /:/ \ \:\/:/ \ \:\ \ \:\__|:| \ \:\/:/ \__\/ /:/
|
||||
/__/:/ \ \::/ \ \:\ \__\::::/ \ \::/ /__/:/
|
||||
\__\/ \__\/ \__\/ ~~~~ \__\/ \__\/ "
|
||||
.cyan()
|
||||
);
|
||||
}
|
||||
@@ -4,5 +4,4 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# shared = { path = "../../crates/shared", features = ["user"] }
|
||||
# core = { path = "../../crates/std/crates/core" }
|
||||
winnow = "1.0.0"
|
||||
|
||||
@@ -1,11 +1,57 @@
|
||||
// use std::io::_print;
|
||||
// #![feature(survos_std)]
|
||||
|
||||
use std::io::{Read, Write, stdin, stdout};
|
||||
|
||||
fn main() {
|
||||
let a = std::env::args();
|
||||
for a in a {
|
||||
println!("Argument: {}", a);
|
||||
}
|
||||
println!(
|
||||
"Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! "
|
||||
);
|
||||
std::process::Command::new("fastfetch")
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
|
||||
let mut current_command = String::new();
|
||||
|
||||
fn new_line() {
|
||||
print!("> ");
|
||||
stdout().flush().unwrap();
|
||||
}
|
||||
|
||||
new_line();
|
||||
|
||||
loop {
|
||||
let mut test = [0; 4];
|
||||
let len = stdin().read(&mut test).unwrap();
|
||||
let input = str::from_utf8(&test[..len as usize]).unwrap();
|
||||
|
||||
for c in input.chars() {
|
||||
if c == '\n' {
|
||||
println!();
|
||||
let mut args = current_command.split(" ");
|
||||
std::process::Command::new(args.next().unwrap())
|
||||
.args(args)
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
current_command.clear();
|
||||
new_line();
|
||||
} else {
|
||||
if if c == '\x08' {
|
||||
current_command.pop().is_some()
|
||||
} else {
|
||||
current_command.push_str(input);
|
||||
true
|
||||
} {
|
||||
print!("{}", input);
|
||||
}
|
||||
}
|
||||
}
|
||||
stdout().flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// fn parse_command(input: &mut &str) {}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[package]
|
||||
name = "test_pic"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# std = { path = "../../crates/std" }
|
||||
shared = { path = "../../crates/shared", features = ["user"] }
|
||||
io = { path = "../../crates/io" }
|
||||
bffs = { path = "../../crates/bffs" }
|
||||
@@ -1,23 +0,0 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
use shared::{fs::File, syscall};
|
||||
use std::io::{Read, Stdin, Write, stdin, stdout};
|
||||
|
||||
fn main() {
|
||||
syscall::close(0);
|
||||
let mut tty = unsafe { File::from_raw_fd(syscall::open("/dev/tty0")) };
|
||||
syscall::close(1);
|
||||
let _ = syscall::open("/dev/tty0");
|
||||
println!("test from test_pic");
|
||||
syscall::spawn("/usr/bin/shell");
|
||||
// panic!("explicit panic");
|
||||
// std::process::abort();
|
||||
// unsafe {core::arch::asm!("unimp")};
|
||||
loop {
|
||||
let mut test = [0; 4];
|
||||
let len = stdin().read(&mut test).unwrap();
|
||||
print!("{}", str::from_utf8(&test[..len as usize]).unwrap());
|
||||
stdout().flush();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user