use std::io;
pub const DEFAULT_CAPACITY: usize = 16 * 1024;
#[derive(Debug)]
pub struct PtyBuffer {
data: Box<[u8]>,
read_pos: usize,
write_pos: usize,
}
impl PtyBuffer {
#[must_use]
pub fn new(capacity: usize) -> Self {
Self {
data: vec![0u8; capacity].into_boxed_slice(),
read_pos: 0,
write_pos: 0,
}
}
#[must_use]
pub fn with_default_capacity() -> Self {
Self::new(DEFAULT_CAPACITY)
}
#[must_use]
pub const fn capacity(&self) -> usize {
self.data.len()
}
#[must_use]
pub const fn len(&self) -> usize {
if self.write_pos >= self.read_pos {
self.write_pos - self.read_pos
} else {
self.capacity() - self.read_pos + self.write_pos
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.read_pos == self.write_pos
}
#[must_use]
pub const fn available(&self) -> usize {
self.capacity() - self.len() - 1
}
#[must_use]
pub const fn is_full(&self) -> bool {
self.available() == 0
}
#[must_use]
pub fn readable(&self) -> &[u8] {
if self.write_pos >= self.read_pos {
&self.data[self.read_pos..self.write_pos]
} else {
&self.data[self.read_pos..]
}
}
#[must_use]
pub fn writable(&mut self) -> &mut [u8] {
let cap = self.capacity();
if self.write_pos >= self.read_pos {
let end = if self.read_pos == 0 { cap - 1 } else { cap };
&mut self.data[self.write_pos..end]
} else {
&mut self.data[self.write_pos..self.read_pos - 1]
}
}
pub fn consume(&mut self, count: usize) {
assert!(count <= self.len(), "cannot consume more than available");
self.read_pos = (self.read_pos + count) % self.capacity();
}
pub fn produce(&mut self, count: usize) {
assert!(
count <= self.available(),
"cannot produce more than available"
);
self.write_pos = (self.write_pos + count) % self.capacity();
}
pub const fn clear(&mut self) {
self.read_pos = 0;
self.write_pos = 0;
}
pub fn read(&mut self, buf: &mut [u8]) -> usize {
let mut total = 0;
while total < buf.len() && !self.is_empty() {
let readable = self.readable();
let to_copy = readable.len().min(buf.len() - total);
buf[total..total + to_copy].copy_from_slice(&readable[..to_copy]);
self.consume(to_copy);
total += to_copy;
}
total
}
pub fn write(&mut self, data: &[u8]) -> usize {
let mut total = 0;
while total < data.len() && !self.is_full() {
let writable = self.writable();
let to_copy = writable.len().min(data.len() - total);
writable[..to_copy].copy_from_slice(&data[total..total + to_copy]);
self.produce(to_copy);
total += to_copy;
}
total
}
}
impl Default for PtyBuffer {
fn default() -> Self {
Self::with_default_capacity()
}
}
impl io::Read for PtyBuffer {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
Ok(self.read(buf))
}
}
impl io::Write for PtyBuffer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(self.write(buf))
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_buffer_is_empty() {
let buf = PtyBuffer::new(1024);
assert!(buf.is_empty());
assert_eq!(buf.len(), 0);
assert_eq!(buf.capacity(), 1024);
}
#[test]
fn write_and_read() {
let mut buf = PtyBuffer::new(1024);
let data = b"hello world";
let written = buf.write(data);
assert_eq!(written, data.len());
assert_eq!(buf.len(), data.len());
let mut output = [0u8; 32];
let read = buf.read(&mut output);
assert_eq!(read, data.len());
assert_eq!(&output[..read], data);
assert!(buf.is_empty());
}
#[test]
fn wrap_around() {
let mut buf = PtyBuffer::new(16);
let data1 = b"12345678901";
buf.write(data1);
let mut tmp = [0u8; 8];
buf.read(&mut tmp);
let data2 = b"abcdefgh";
let written = buf.write(data2);
assert!(written > 0);
let mut output = [0u8; 32];
let total = buf.read(&mut output);
assert!(!output[..total].is_empty());
}
#[test]
fn clear_resets_buffer() {
let mut buf = PtyBuffer::new(1024);
buf.write(b"test data");
assert!(!buf.is_empty());
buf.clear();
assert!(buf.is_empty());
assert_eq!(buf.len(), 0);
}
}