use crate::file::{File, FileError};
use alloc::sync::Arc;
use spin::Mutex;
pub const PIPE_BUFFER_SIZE: usize = 4096;
pub(crate) struct PipeInner {
buf: [u8; PIPE_BUFFER_SIZE],
read_pos: usize,
write_pos: usize,
count: usize,
write_closed: bool,
read_closed: bool,
}
impl PipeInner {
fn new() -> Self {
Self {
buf: [0u8; PIPE_BUFFER_SIZE],
read_pos: 0,
write_pos: 0,
count: 0,
write_closed: false,
read_closed: false,
}
}
}
pub(crate) type PipeHandle = Arc<Mutex<PipeInner>>;
pub fn make_pipe() -> (PipeReadEnd, PipeWriteEnd) {
let inner = Arc::new(Mutex::new(PipeInner::new()));
(
PipeReadEnd {
inner: inner.clone(),
},
PipeWriteEnd { inner },
)
}
pub struct PipeReadEnd {
inner: PipeHandle,
}
impl File for PipeReadEnd {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, FileError> {
let mut p = self.inner.lock();
if p.count == 0 {
return if p.write_closed {
Ok(0)
} else {
Err(FileError::WouldBlock)
};
}
let n = core::cmp::min(buf.len(), p.count);
for slot in buf.iter_mut().take(n) {
*slot = p.buf[p.read_pos];
p.read_pos = (p.read_pos + 1) % PIPE_BUFFER_SIZE;
}
p.count -= n;
Ok(n)
}
fn write(&mut self, _buf: &[u8]) -> Result<usize, FileError> {
Err(FileError::InvalidOperation)
}
}
impl Drop for PipeReadEnd {
fn drop(&mut self) {
self.inner.lock().read_closed = true;
}
}
pub struct PipeWriteEnd {
inner: PipeHandle,
}
impl File for PipeWriteEnd {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, FileError> {
Err(FileError::InvalidOperation)
}
fn write(&mut self, buf: &[u8]) -> Result<usize, FileError> {
let mut p = self.inner.lock();
if p.read_closed {
return Err(FileError::IoError);
}
let free = PIPE_BUFFER_SIZE - p.count;
let n = core::cmp::min(buf.len(), free);
if n == 0 && !buf.is_empty() {
return Err(FileError::WouldBlock);
}
for &byte in buf.iter().take(n) {
let pos = p.write_pos;
p.buf[pos] = byte;
p.write_pos = (pos + 1) % PIPE_BUFFER_SIZE;
}
p.count += n;
Ok(n)
}
}
impl Drop for PipeWriteEnd {
fn drop(&mut self) {
self.inner.lock().write_closed = true;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pipe_round_trips_bytes() {
let (mut read_end, mut write_end) = make_pipe();
assert_eq!(write_end.write(b"abc").unwrap(), 3);
let mut buf = [0; 4];
assert_eq!(read_end.read(&mut buf).unwrap(), 3);
assert_eq!(&buf[..3], b"abc");
}
}