use alloc::collections::VecDeque;
use alloc::vec::Vec;
use core::fmt;
pub trait CharDevice: Send + Sync + fmt::Debug {
fn read(&self, dst: &mut [u8]) -> usize;
fn write(&self, src: &[u8]) -> usize;
fn writable(&self) -> bool {
true
}
fn flush(&self) {}
fn read_byte(&self) -> Option<u8> {
let mut byte = [0u8; 1];
(self.read(&mut byte) == 1).then_some(byte[0])
}
fn write_byte(&self, byte: u8) -> bool {
self.write(&[byte]) == 1
}
}
pub const PORT_CAPACITY: usize = 64 * 1024;
pub struct CharPort {
state: crate::core::sync::Mutex<PortState>,
}
#[derive(Debug, Default)]
struct PortState {
to_guest: VecDeque<u8>,
to_host: VecDeque<u8>,
}
impl fmt::Debug for CharPort {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.state.try_lock() {
Some(state) => f
.debug_struct("CharPort")
.field("to_guest", &state.to_guest.len())
.field("to_host", &state.to_host.len())
.finish(),
None => f
.debug_struct("CharPort")
.field("state", &"<in use>")
.finish(),
}
}
}
impl Default for CharPort {
fn default() -> Self {
CharPort::new()
}
}
impl CharPort {
#[must_use]
pub fn new() -> CharPort {
CharPort {
state: crate::core::sync::Mutex::new(PortState::default()),
}
}
pub fn feed(&self, bytes: &[u8]) -> usize {
let mut state = self.state.lock();
let room = PORT_CAPACITY.saturating_sub(state.to_guest.len());
let take = room.min(bytes.len());
state.to_guest.extend(&bytes[..take]);
take
}
#[must_use]
pub fn drain(&self) -> Vec<u8> {
let mut out = Vec::new();
self.drain_into(&mut out);
out
}
pub fn drain_into(&self, dst: &mut Vec<u8>) {
let mut state = self.state.lock();
dst.reserve(state.to_host.len());
dst.extend(state.to_host.drain(..));
}
#[must_use]
pub fn pending_output(&self) -> usize {
self.state.lock().to_host.len()
}
#[must_use]
pub fn pending_input(&self) -> usize {
self.state.lock().to_guest.len()
}
pub fn clear(&self) {
let mut state = self.state.lock();
state.to_guest.clear();
state.to_host.clear();
}
}
impl CharDevice for CharPort {
fn read(&self, dst: &mut [u8]) -> usize {
let mut state = self.state.lock();
let mut taken = 0;
while taken < dst.len() {
match state.to_guest.pop_front() {
Some(byte) => {
dst[taken] = byte;
taken += 1;
}
None => break,
}
}
taken
}
fn write(&self, src: &[u8]) -> usize {
let mut state = self.state.lock();
let room = PORT_CAPACITY.saturating_sub(state.to_host.len());
let take = room.min(src.len());
state.to_host.extend(&src[..take]);
take
}
fn writable(&self) -> bool {
self.state.lock().to_host.len() < PORT_CAPACITY
}
}
pub mod ports {
use super::CharPort;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::core::error::Result;
use crate::core::hosts::{HostKind, HostObjects};
use crate::core::props::Props;
pub const KIND: HostKind = HostKind::new("chardev");
pub fn open(hosts: &HostObjects, name: &str) -> Result<Arc<CharPort>> {
hosts.open(KIND, name, CharPort::new)
}
pub fn attach(props: &Props, name: &str) -> Result<Arc<CharPort>> {
props.host(KIND, name, CharPort::new)
}
pub fn get(hosts: &HostObjects, name: &str) -> Result<Option<Arc<CharPort>>> {
hosts.get(KIND, name)
}
pub fn close(hosts: &HostObjects, name: &str) -> bool {
hosts.close(KIND, name)
}
#[must_use]
pub fn names(hosts: &HostObjects) -> Vec<String> {
hosts.names(KIND)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::sync::Arc;
#[test]
fn bytes_cross_in_both_directions_without_meeting() {
let port = CharPort::new();
assert_eq!(port.feed(b"hi"), 2);
assert_eq!(port.write(b"there"), 5);
let mut buf = [0u8; 8];
assert_eq!(port.read(&mut buf), 2);
assert_eq!(&buf[..2], b"hi");
assert_eq!(
port.read(&mut buf),
0,
"nothing left, and it does not block"
);
assert_eq!(port.drain(), b"there".to_vec());
assert!(port.drain().is_empty());
}
#[test]
fn a_byte_at_a_time_is_the_same_stream() {
let port = CharPort::new();
port.feed(b"AB");
assert_eq!(port.read_byte(), Some(b'A'));
assert_eq!(port.read_byte(), Some(b'B'));
assert_eq!(port.read_byte(), None);
assert!(port.write_byte(b'C'));
assert_eq!(port.drain(), b"C".to_vec());
}
#[test]
fn a_full_port_pushes_back_rather_than_growing() {
let port = CharPort::new();
let flood = alloc::vec![b'x'; PORT_CAPACITY + 10];
assert_eq!(port.write(&flood), PORT_CAPACITY, "the write is short");
assert!(!port.writable(), "and it says so");
assert_eq!(port.write(b"y"), 0);
assert_eq!(port.pending_output(), PORT_CAPACITY);
assert_eq!(port.drain().len(), PORT_CAPACITY);
assert!(port.writable());
assert_eq!(port.feed(&flood), PORT_CAPACITY, "input pushes back too");
}
#[test]
fn clearing_a_port_empties_both_queues() {
let port = CharPort::new();
port.feed(b"in");
port.write(b"out");
port.clear();
assert_eq!(port.pending_input(), 0);
assert_eq!(port.pending_output(), 0);
}
#[test]
fn a_name_reaches_the_same_port_from_both_ends() {
let hosts = crate::core::hosts::HostObjects::new();
let device_end: Arc<dyn CharDevice> = ports::open(&hosts, "console").unwrap();
let host_end = ports::open(&hosts, "console").unwrap();
host_end.feed(b"Q");
assert_eq!(device_end.read_byte(), Some(b'Q'));
device_end.write_byte(b'R');
assert_eq!(host_end.drain(), b"R".to_vec());
assert_eq!(ports::names(&hosts), ["console"]);
assert!(ports::close(&hosts, "console"));
assert!(ports::get(&hosts, "console").unwrap().is_none());
host_end.feed(b"S");
assert_eq!(ports::open(&hosts, "console").unwrap().pending_input(), 0);
}
#[test]
fn two_builds_with_one_port_name_are_two_ports() {
let left = crate::core::hosts::HostObjects::new();
let right = crate::core::hosts::HostObjects::new();
let a = ports::open(&left, "console").unwrap();
let b = ports::open(&right, "console").unwrap();
assert!(!Arc::ptr_eq(&a, &b));
a.feed(b"only mine");
assert_eq!(b.pending_input(), 0);
}
}