Skip to main content

turnloop/
buffer.rs

1//! Stable host memory and explicitly released pool leases.
2use std::{cell::RefCell, ptr::NonNull, rc::Rc};
3
4/// A stable, initialized, immutable byte region. Does not own the memory.
5#[derive(Debug)]
6pub struct IoBuf {
7    ptr: NonNull<u8>,
8    len: usize,
9}
10impl IoBuf {
11    /// # Safety
12    /// `ptr` must be non-null and readable for `len` bytes. Keep the region alive,
13    /// unmoved and unmodified until the terminal completion is delivered, or until
14    /// the driver is dropped (which must quiesce all native I/O before returning).
15    pub unsafe fn from_raw_parts(ptr: *const u8, len: usize) -> Self {
16        assert!(
17            len <= isize::MAX as usize,
18            "buffer length fits a Rust slice"
19        );
20        Self {
21            ptr: NonNull::new(ptr.cast_mut()).expect("non-null buffer pointer"),
22            len,
23        }
24    }
25    /// Return the number of bytes in this region.
26    pub fn len(&self) -> usize {
27        self.len
28    }
29    /// Whether this region contains zero bytes.
30    pub fn is_empty(&self) -> bool {
31        self.len == 0
32    }
33    /// Return the stable address of the readable byte region.
34    pub fn as_ptr(&self) -> *const u8 {
35        self.ptr.as_ptr()
36    }
37}
38/// A stable, exclusively writable region. Does not own the memory.
39#[derive(Debug)]
40pub struct IoBufMut {
41    ptr: NonNull<u8>,
42    len: usize,
43}
44impl IoBufMut {
45    /// # Safety
46    /// `ptr` must be non-null and writable for `len` bytes. The region must remain
47    /// alive and unmoved, and no other access is permitted, until delivery of the
48    /// terminal completion or return from driver destruction.
49    pub unsafe fn from_raw_parts(ptr: *mut u8, len: usize) -> Self {
50        assert!(
51            len <= isize::MAX as usize,
52            "buffer length fits a Rust slice"
53        );
54        Self {
55            ptr: NonNull::new(ptr).expect("non-null buffer pointer"),
56            len,
57        }
58    }
59    /// Return the number of bytes in this region.
60    pub fn len(&self) -> usize {
61        self.len
62    }
63    /// Whether this region contains zero bytes.
64    pub fn is_empty(&self) -> bool {
65        self.len == 0
66    }
67    /// Return the stable address of the exclusively writable byte region.
68    pub fn as_mut_ptr(&self) -> *mut u8 {
69        self.ptr.as_ptr()
70    }
71}
72#[derive(Debug)]
73/// Choose host-provided memory or an explicitly released pool lease for a read.
74pub enum ReadBuf {
75    /// Host-provided stable memory, retained under its constructor lifetime contract.
76    Provided(IoBufMut),
77    /// Use a reusable pool buffer; exhaustion delays progress until a lease is released.
78    Pooled,
79}
80#[derive(Debug)]
81/// Stable write data, either borrowed under an unsafe lifetime contract or owned.
82pub enum WriteBuf {
83    /// Host-provided stable memory, retained under its constructor lifetime contract.
84    Provided(IoBuf),
85    /// Owned initialized bytes, retained until the operation terminates.
86    Owned(Vec<u8>),
87}
88impl WriteBuf {
89    /// Borrow the initialized bytes covered by this buffer.
90    pub fn as_slice(&self) -> &[u8] {
91        match self {
92            Self::Provided(b) => {
93                // SAFETY: construction promises a readable region for the entire op lifetime.
94                unsafe { std::slice::from_raw_parts(b.as_ptr(), b.len()) }
95            }
96            Self::Owned(b) => b,
97        }
98    }
99}
100
101/// Fixed upper bound keeps writev descriptors inline in the operation table.
102pub const MAX_IOV: usize = 8;
103#[derive(Debug)]
104/// Up to MAX_IOV owned or provided segments stored inline.
105pub struct WriteVectored {
106    /// Inline segments in transmission order; unused entries are None.
107    pub bufs: [Option<WriteBuf>; MAX_IOV],
108}
109impl WriteVectored {
110    /// Build inline vectored storage; returns InvalidInput if more than MAX_IOV segments are supplied.
111    pub fn new(bufs: impl IntoIterator<Item = WriteBuf>) -> crate::Result<Self> {
112        let mut out = Self {
113            bufs: std::array::from_fn(|_| None),
114        };
115        for (i, b) in bufs.into_iter().enumerate() {
116            if i == MAX_IOV {
117                return Err(crate::Error::new(crate::ErrorKind::InvalidInput));
118            }
119            out.bufs[i] = Some(b);
120        }
121        Ok(out)
122    }
123}
124
125#[derive(Clone, Debug)]
126/// Fixed reusable read storage; leases return buffers when explicitly released or dropped.
127pub struct BufferPool {
128    inner: Rc<RefCell<Vec<Vec<u8>>>>,
129}
130impl BufferPool {
131    /// Allocate the fixed number and size of reusable read buffers.
132    pub fn new(count: usize, size: usize) -> Self {
133        Self {
134            inner: Rc::new(RefCell::new((0..count).map(|_| vec![0; size]).collect())),
135        }
136    }
137    #[cfg(any(
138        turnloop_backend = "kqueue",
139        turnloop_backend = "epoll",
140        turnloop_backend = "iocp",
141        turnloop_backend = "wasi_p2",
142        all(turnloop_backend = "wasi_p3", feature = "wasi-p3-experimental")
143    ))]
144    pub(crate) fn available(&self) -> bool {
145        !self.inner.borrow().is_empty()
146    }
147    /// Exhaustion applies backpressure: the backend keeps the read pending.
148    pub fn acquire(&self) -> Option<BufLease> {
149        self.inner.borrow_mut().pop().map(|data| BufLease {
150            data: Some(data),
151            len: 0,
152            pool: self.inner.clone(),
153        })
154    }
155}
156/// Owns the buffer until explicitly dropped/released. May safely survive turns.
157/// Keeping all leases applies backpressure; it never allocates a new pool buffer.
158#[derive(Debug)]
159pub struct BufLease {
160    data: Option<Vec<u8>>,
161    len: usize,
162    pool: Rc<RefCell<Vec<Vec<u8>>>>,
163}
164impl BufLease {
165    /// Borrow the initialized bytes covered by this buffer.
166    pub fn as_slice(&self) -> &[u8] {
167        &self.data.as_ref().expect("live lease")[..self.len]
168    }
169    /// Borrow the full exclusive pool-buffer storage for a backend read.
170    pub fn writable(&mut self) -> &mut [u8] {
171        self.data.as_mut().expect("live lease")
172    }
173    /// Set the initialized completion length; panics if it exceeds buffer capacity.
174    pub fn set_len(&mut self, len: usize) {
175        assert!(len <= self.data.as_ref().expect("live lease").len());
176        self.len = len;
177    }
178    /// Return this lease to its originating pool.
179    pub fn release(self) {}
180}
181impl Drop for BufLease {
182    fn drop(&mut self) {
183        if let Some(data) = self.data.take() {
184            self.pool.borrow_mut().push(data);
185        }
186    }
187}