use std::io::Result; use std::mem::{self, MaybeUninit}; // A simple async-ification of sync Read plus downcasting methods. pub trait Read { // async fn read(&mut self, buf: &mut [u8]) -> Result; // ... // TODO should we have dynamic and/or static (Option<&mut impl ReadyRead>) versions? fn as_ready(&mut self) -> Option<&mut dyn ReadyRead> { None } // TODO OwnedRead is not object-safe. Could either return impl OwnedRead, or maybe dyn* helps? // Or change the sig of OwnedRead::read. fn as_owned(&mut self) -> Option<&mut dyn OwnedRead> { None } } // Used for completion model systems. pub trait OwnedRead: Read { // async fn read(&mut self, buf: B) -> (B, Result<()>); } // Abstracts over Vec and similar data structures pub trait OwnedBuf: Default + 'static { fn take(&mut self) -> Self { mem::take(self) } /// Returns a shared reference to the filled portion of the buffer. fn filled(&self) -> &[u8]; unsafe fn unfilled(&mut self) -> &mut [MaybeUninit]; fn init_mut(&mut self) -> &mut [u8]; fn uninit_mut(&mut self) -> &mut [MaybeUninit]; unsafe fn advance(&mut self, n: usize); unsafe fn set_init(&mut self, n: usize); fn ensure_init(&mut self); fn append(&mut self, buf: &[u8]); // TODO does this work? fn borrow<'a>(&'a mut self) -> BorrowedBuf<'a>; } // Used for epoll-like systems pub trait Ready { // async fn ready(&mut self, interest: Interest) -> Result; } pub trait ReadyRead: Ready + Read { fn non_blocking_read(&mut self, buf: &mut [u8]) -> Result>; // ... } /// Express which notifications the user is interested in receiving. #[derive(Copy, Clone)] pub struct Interest(u32); /// Describes which operations are ready for an IO resource. #[derive(Copy, Clone)] pub struct Readiness(u32); /// Whether an IO operation is ready for reading/writing or would block. #[derive(Copy, Clone, Debug)] pub enum NonBlocking { Ready(T), WouldBlock, } #[cfg(test)] mod tests { use super::*; // Some example readable resource, provided by a runtime. struct Socket; // A 'good citizen' implementation of Socket would support all appropriate traits (usually basic // Read and one of the specialised traits). impl Read for Socket {} impl OwnedRead for Socket {} impl ReadyRead for Socket {} fn simple_read() -> Result<()> { let mut s = Socket; let mut buf = [0; 1024]; s.read(&mut buf)?; // Use buf // See generic versions for owned/ready } fn generic_read(reader: &mut impl Read) -> Result<()> { let mut buf = [0; 1024]; reader.read(&mut buf)?; // Use buf } fn generic_owned_read(reader: &mut impl OwnedRead) -> Result<()> { let mut buf = vec![0; 1024]; let result = reader.read(buf); buf = result.0; result.1?; // Use buf } fn generic_ready_read(reader: &mut impl ReadyRead) -> Result<()> { loop { reader.ready(Interest::READABLE)/*.await*/?; let mut buf = [0; 1024]; if let NonBlocking::Ready(n) = reader.non_blocking_read(buf)? { // Use buf return Ok(()); } } } fn advanced_read(reader: &mut impl Read) -> Result<()> { if let Some(reader) = reader.as_owned() { let mut buf = vec![0; 1024]; let result = reader.read(buf); buf = result.0; result.1?; // Use buf } else if let Some(reader) = reader.as_ready() { loop { reader.ready(Interest::READABLE)/*.await*/?; let mut buf = [0; 1024]; if let NonBlocking::Ready(n) = reader.non_blocking_read(buf)? { // Use buf return Ok(()); } } } else { let mut buf = [0; 1024]; reader.read(&mut buf)?; // Use buf } } // An example of a 'middleware' library which should support all runtimes. struct Buffer { reader: T, buf: Vec, } // Good citizen libraries have to support all three traits. impl Read for Buffer { // read should use an advanced_read style, since even if the caller does // not use the trick, the advanced modes might be more efficient. } // These two impls have to downcast self.reader and either panic or use a // slow path if the downcast fails. impl OwnedRead for Buffer {} impl ReadyRead for Buffer {} }