Change io crate & add a small shell

This commit is contained in:
2026-03-25 20:45:11 +01:00
parent f966a1239e
commit ae0593c972
98 changed files with 11102 additions and 810 deletions

View File

@@ -7,5 +7,4 @@ json-target-spec = true
[target.riscv64]
rustflags = [
"-C", "link-arg=-Tilm.ld",
"--sysroot", "sysroot"
]

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
io = { path = "../io" }
io = { package = "no-std-io", path = "../io" }
bitflags = "2"
[features]

View File

@@ -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; _];

View File

@@ -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),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
View File

@@ -0,0 +1,3 @@
.cargo
target

View File

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

View File

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

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

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

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

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

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

View 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
View 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 youre
/// 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
View 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

File diff suppressed because it is too large Load Diff

View 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);

View 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
View 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

File diff suppressed because it is too large Load Diff

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

View File

@@ -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;
/// 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;
#[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;
/// 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>;
#[cfg(not(feature = "std"))]
use alloc_crate::collections;
#[cfg(not(feature = "std"))]
use core::{cmp, error, fmt, hint, mem, ops, ptr, result, slice, str};
/// 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
View File

@@ -0,0 +1 @@
pub(crate) mod io;

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

View File

@@ -0,0 +1,4 @@
mod generic;
pub use generic::*;
pub type RawOsError = i32;

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

View File

@@ -0,0 +1,3 @@
pub fn is_terminal<T>(_: &T) -> bool {
false
}

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

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

View File

@@ -5,7 +5,7 @@ edition = "2024"
[dependencies]
bffs = { path = "../bffs" }
io = { path = "../io" }
io = { package = "no-std-io", path = "../io" }
[features]
kernel = []

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@@ -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",

View File

@@ -0,0 +1,4 @@
21a \ target_os = "survos" => { \
mod survos; \
pub use survos::*; \
}

View File

@@ -0,0 +1,4 @@
47a \ target_os = "survos" => { \
mod survos; \
use survos as imp; \
}

View File

@@ -0,0 +1,4 @@
17a \ target_os = "survos" => { \
mod survos; \
use survos as imp; \
}

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

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

View File

@@ -0,0 +1 @@
../../../../../crates/shared/src/syscall.rs

View File

@@ -0,0 +1 @@
/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/args/common.rs

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

View File

@@ -0,0 +1 @@

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

View File

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

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

View File

@@ -1 +1,5 @@
#![allow(unused)]
pub mod circular_buffer;
pub mod sized_string;
pub mod sized_vec;

View File

@@ -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;
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")
}
self.tail = (self.tail + 1) % SIZE;
}
}

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,6 @@ use core::{
use bytes_struct::VolatilePackedStruct;
use crate::{
println,
virtio::{
VirtioCapability, VirtioCapabilityType, VirtioNotificationCapability, VirtioPciCommonCfg,
},

6
src/prelude.rs Normal file
View 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};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
../../../../../library

View File

@@ -1 +0,0 @@
../../../../../../library

6
user/agetty/Cargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[package]
name = "agetty"
version = "0.1.0"
edition = "2024"
[dependencies]

29
user/agetty/src/main.rs Normal file
View 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();
}
}

View File

@@ -0,0 +1,7 @@
[package]
name = "fastfetch"
version = "0.1.0"
edition = "2024"
[dependencies]
owo-colors = "4.3.0"

View File

@@ -0,0 +1,21 @@
// #![feature(survos_std)]
use owo_colors::OwoColorize;
fn main() {
println!(
"{}",
r" ___ ___ ___ ___ ___
/ /\ /__/\ / /\ ___ / /\ / /\
/ /:/_ \ \:\ / /::\ /__/\ / /::\ / /:/_
/ /:/ /\ \ \:\ / /:/\:\ \ \:\ / /:/\:\ / /:/ /\
/ /:/ /::\ ___ \ \:\ / /:/~/:/ \ \:\ / /:/ \:\ / /:/ /::\
/__/:/ /:/\:/__/\ \__\:/__/:/ /:/______ \__\:/__/:/ \__\:/__/:/ /:/\:\
\ \:\/:/~/:\ \:\ / /:\ \:\/:::::/__/\ | |:\ \:\ / /:\ \:\/:/~/:/
\ \::/ /:/ \ \:\ /:/ \ \::/~~~~\ \:\| |:|\ \:\ /:/ \ \::/ /:/
\__\/ /:/ \ \:\/:/ \ \:\ \ \:\__|:| \ \:\/:/ \__\/ /:/
/__/:/ \ \::/ \ \:\ \__\::::/ \ \::/ /__/:/
\__\/ \__\/ \__\/ ~~~~ \__\/ \__\/ "
.cyan()
);
}

View File

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

View File

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

View File

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

View File

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