hala_io/
driver.rs

1use std::{
2    io,
3    net::{Shutdown, SocketAddr},
4    ptr::NonNull,
5    task::Waker,
6    time::Duration,
7};
8
9use bitmask_enum::bitmask;
10
11use crate::{Description, Handle, Interest};
12
13#[bitmask]
14pub enum FileMode {
15    Read,
16    Write,
17    Create,
18    Truncate,
19}
20
21/// File description open flags used by `fd_open` method.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
23pub enum OpenFlags<'a> {
24    None,
25    /// The path to the local file system to open.
26    OpenFile(&'a str, FileMode),
27    /// The binding addrs of the opening socket
28    Bind(&'a [SocketAddr]),
29    /// The address list of the remote peer to which the open socket will connect
30    Connect(&'a [SocketAddr]),
31    Duration(Duration),
32    UserDefined(&'a [u8]),
33}
34
35impl<'a> OpenFlags<'a> {
36    pub fn try_into_open_file(self) -> io::Result<(&'a str, FileMode)> {
37        match self {
38            Self::OpenFile(path, mode) => Ok((path, mode)),
39            _ => Err(io::Error::new(
40                io::ErrorKind::InvalidInput,
41                format!("Expect OpenFile, but got {:?}", self),
42            )),
43        }
44    }
45
46    pub fn try_into_bind(self) -> io::Result<&'a [SocketAddr]> {
47        match self {
48            Self::Bind(laddrs) => Ok(laddrs),
49            _ => Err(io::Error::new(
50                io::ErrorKind::InvalidInput,
51                format!("Expect Bind, but got {:?}", self),
52            )),
53        }
54    }
55
56    pub fn try_into_connect(self) -> io::Result<&'a [SocketAddr]> {
57        match self {
58            Self::Connect(raddrs) => Ok(raddrs),
59            _ => Err(io::Error::new(
60                io::ErrorKind::InvalidInput,
61                format!("Expect Bind, but got {:?}", self),
62            )),
63        }
64    }
65
66    pub fn try_into_duration(self) -> io::Result<Duration> {
67        match self {
68            Self::Duration(duration) => Ok(duration),
69            _ => Err(io::Error::new(
70                io::ErrorKind::InvalidInput,
71                format!("Expect Bind, but got {:?}", self),
72            )),
73        }
74    }
75
76    pub fn try_into_user_defined(self) -> io::Result<&'a [u8]> {
77        match self {
78            Self::UserDefined(buf) => Ok(buf),
79            _ => Err(io::Error::new(
80                io::ErrorKind::InvalidInput,
81                format!("Expect UserDefined, but got {:?}", self),
82            )),
83        }
84    }
85}
86
87/// File description control command.
88#[derive(Debug)]
89pub enum Cmd<'a> {
90    /// Write data to stream file description.
91    Read {
92        waker: Waker,
93        buf: &'a mut [u8],
94    },
95    /// Read data from stream file description.
96    Write {
97        waker: Waker,
98        buf: &'a [u8],
99    },
100
101    /// Command `Sendto` parameter for udp socket.
102    SendTo {
103        waker: Waker,
104        buf: &'a [u8],
105        raddr: SocketAddr,
106    },
107
108    /// Command to invoke UdpSocket `recv_from` method.
109    RecvFrom {
110        waker: Waker,
111        buf: &'a mut [u8],
112    },
113
114    /// Register io event interests with `Poll`
115    Register {
116        source: Handle,
117        interests: Interest,
118    },
119
120    /// Re-register io event interests with `Poll`
121    ReRegister {
122        source: Handle,
123        interests: Interest,
124    },
125
126    /// Deregister io event interests with `Poll`
127    // Deregister(Handle),
128
129    /// Try accept one incoming connection.
130    Accept(Waker),
131
132    /// Poll once io readiness events.
133    PollOnce(Option<Duration>),
134
135    /// Try to clone the handle.
136    TryClone,
137    Timeout(Waker),
138    LocalAddr,
139    RemoteAddr,
140
141    Shutdown(Shutdown),
142}
143
144/// The response of `fd_cntl` .
145#[derive(Debug, Clone)]
146pub enum CmdResp {
147    None,
148    /// Command `RecvFrom` response data.
149    RecvFrom(usize, SocketAddr),
150    /// Command `Accept` response data.
151    Incoming(Handle, SocketAddr),
152    /// Command `Write` / `SendTo` response data
153    DataLen(usize),
154    Timeout(bool),
155    /// Command `TryClone` response data.
156    Cloned(Handle),
157    SockAddr(SocketAddr),
158}
159
160impl CmdResp {
161    pub fn try_into_incoming(self) -> io::Result<(Handle, SocketAddr)> {
162        match self {
163            Self::Incoming(handle, raddr) => Ok((handle, raddr)),
164            _ => Err(io::Error::new(
165                io::ErrorKind::InvalidInput,
166                format!("Expect Incoming, but got {:?}", self),
167            )),
168        }
169    }
170
171    pub fn try_into_recv_from(self) -> io::Result<(usize, SocketAddr)> {
172        match self {
173            Self::RecvFrom(len, raddr) => Ok((len, raddr)),
174            _ => Err(io::Error::new(
175                io::ErrorKind::InvalidInput,
176                format!("Expect Incoming, but got {:?}", self),
177            )),
178        }
179    }
180
181    pub fn try_into_sockaddr(self) -> io::Result<SocketAddr> {
182        match self {
183            Self::SockAddr(addr) => Ok(addr),
184            _ => Err(io::Error::new(
185                io::ErrorKind::InvalidInput,
186                format!("Expect SockAddr, but got {:?}", self),
187            )),
188        }
189    }
190
191    pub fn try_into_datalen(self) -> io::Result<usize> {
192        match self {
193            Self::DataLen(len) => Ok(len),
194            _ => Err(io::Error::new(
195                io::ErrorKind::InvalidInput,
196                format!("Expect SockAddr, but got {:?}", self),
197            )),
198        }
199    }
200
201    pub fn try_into_timeout(self) -> io::Result<bool> {
202        match self {
203            Self::Timeout(status) => Ok(status),
204            _ => Err(io::Error::new(
205                io::ErrorKind::InvalidInput,
206                format!("Expect Timeout, but got {:?}", self),
207            )),
208        }
209    }
210}
211
212/// io driver must implement this trait.
213pub trait RawDriver {
214    /// Try open file description.
215    fn fd_open(&self, desc: Description, open_flags: OpenFlags) -> io::Result<Handle>;
216
217    /// performs one of file description operation.
218    fn fd_cntl(&self, handle: Handle, cmd: Cmd) -> io::Result<CmdResp>;
219
220    /// Close the opened file description.
221    ///
222    /// #Panic
223    ///
224    /// Closing the `Handle` twice must cause panic
225    fn fd_close(&self, handle: Handle) -> io::Result<()>;
226}
227
228#[repr(C)]
229#[derive(Clone)]
230struct DriverVTable {
231    fd_open: unsafe fn(NonNull<DriverVTable>, Description, OpenFlags) -> io::Result<Handle>,
232    fd_cntl: unsafe fn(NonNull<DriverVTable>, Handle, Cmd) -> io::Result<CmdResp>,
233    fd_close: unsafe fn(NonNull<DriverVTable>, Handle) -> io::Result<()>,
234    clone: unsafe fn(NonNull<DriverVTable>) -> Driver,
235    drop: unsafe fn(NonNull<DriverVTable>),
236}
237
238impl DriverVTable {
239    fn new<R: RawDriver + Clone>() -> Self {
240        fn fd_open<R: RawDriver + Clone>(
241            ptr: NonNull<DriverVTable>,
242            desc: Description,
243            flags: OpenFlags,
244        ) -> io::Result<Handle> {
245            let header = ptr.cast::<DriverHeader<R>>();
246
247            unsafe { header.as_ref().data.fd_open(desc, flags) }
248        }
249
250        fn fd_cntl<R: RawDriver + Clone>(
251            ptr: NonNull<DriverVTable>,
252            handle: Handle,
253            cmd: Cmd,
254        ) -> io::Result<CmdResp> {
255            let header = ptr.cast::<DriverHeader<R>>();
256
257            let result = unsafe { header.as_ref().data.fd_cntl(handle, cmd) };
258
259            // log::trace!("[HalaIO] fd_cntl({:?}), {:?}", handle, result);
260
261            result
262        }
263
264        fn fd_close<R: RawDriver + Clone>(
265            ptr: NonNull<DriverVTable>,
266            handle: Handle,
267        ) -> io::Result<()> {
268            let header = ptr.cast::<DriverHeader<R>>();
269
270            unsafe { header.as_ref().data.fd_close(handle) }
271        }
272
273        fn clone<R: RawDriver + Clone>(ptr: NonNull<DriverVTable>) -> Driver {
274            let driver = unsafe { ptr.cast::<DriverHeader<R>>().as_ref().clone() };
275
276            let ptr = unsafe {
277                NonNull::new_unchecked(Box::into_raw(Box::new(driver)) as *mut DriverVTable)
278            };
279
280            Driver { ptr }
281        }
282
283        fn drop<R: RawDriver + Clone>(ptr: NonNull<DriverVTable>) {
284            _ = unsafe { Box::from_raw(ptr.cast::<DriverHeader<R>>().as_ptr()) };
285        }
286
287        Self {
288            fd_open: fd_open::<R>,
289            fd_cntl: fd_cntl::<R>,
290            fd_close: fd_close::<R>,
291            clone: clone::<R>,
292            drop: drop::<R>,
293        }
294    }
295}
296
297pub trait IntoRawDriver {
298    type Driver: RawDriver + Clone;
299    fn into_raw_driver(self) -> Self::Driver;
300}
301
302#[repr(C)]
303#[derive(Clone)]
304struct DriverHeader<R: RawDriver + Clone> {
305    vtable: DriverVTable,
306    data: R,
307}
308
309/// The driver to driving Asynchronous IO
310#[derive(Debug)]
311pub struct Driver {
312    ptr: NonNull<DriverVTable>,
313}
314
315unsafe impl Send for Driver {}
316unsafe impl Sync for Driver {}
317
318impl<R: RawDriver + Clone> From<R> for Driver {
319    fn from(value: R) -> Self {
320        Self::new(value)
321    }
322}
323
324impl Driver {
325    /// Creates a new `Driver` from [`RawDriver`].
326    pub fn new<R: RawDriver + Clone>(raw: R) -> Self {
327        let boxed = Box::new(DriverHeader::<R> {
328            data: raw,
329            vtable: DriverVTable::new::<R>(),
330        });
331
332        let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(boxed) as *mut DriverVTable) };
333
334        Self { ptr }
335    }
336
337    /// Try open file description.
338    pub fn fd_open(&self, desc: Description, open_flags: OpenFlags) -> io::Result<Handle> {
339        unsafe { (self.ptr.as_ref().fd_open)(self.ptr, desc, open_flags) }
340    }
341
342    /// performs one of file description operation.
343    pub fn fd_cntl(&self, handle: Handle, cmd: Cmd) -> io::Result<CmdResp> {
344        unsafe { (self.ptr.as_ref().fd_cntl)(self.ptr, handle, cmd) }
345    }
346
347    /// Close the opened file description.
348    ///
349    /// #Panic
350    ///
351    /// Closing the `Handle` twice must cause panic
352    pub fn fd_close(&self, handle: Handle) -> io::Result<()> {
353        unsafe { (self.ptr.as_ref().fd_close)(self.ptr, handle) }
354    }
355}
356
357impl Clone for Driver {
358    fn clone(&self) -> Self {
359        unsafe { (self.ptr.as_ref().clone)(self.ptr) }
360    }
361}
362
363impl Drop for Driver {
364    fn drop(&mut self) {
365        unsafe { (self.ptr.as_ref().drop)(self.ptr) }
366    }
367}
368
369#[cfg(test)]
370mod tests {
371    use super::*;
372
373    #[derive(Clone)]
374    struct MockDriver {}
375
376    struct MockFile {}
377
378    impl RawDriver for MockDriver {
379        fn fd_open(
380            &self,
381            desc: crate::Description,
382            _open_flags: crate::OpenFlags,
383        ) -> std::io::Result<crate::Handle> {
384            Ok(Handle::from((desc, MockFile {})))
385        }
386
387        fn fd_cntl(
388            &self,
389            _handle: crate::Handle,
390            _cmd: crate::Cmd,
391        ) -> std::io::Result<crate::CmdResp> {
392            Ok(CmdResp::None)
393        }
394
395        fn fd_close(&self, handle: crate::Handle) -> std::io::Result<()> {
396            handle.drop_as::<MockFile>();
397
398            Ok(())
399        }
400    }
401
402    #[test]
403    fn test_driver_vtable() {
404        let driver = Driver::new(MockDriver {});
405
406        let driver = driver.clone();
407
408        let handle = driver.fd_open(Description::File, OpenFlags::None).unwrap();
409
410        driver.fd_cntl(handle, Cmd::PollOnce(None)).unwrap();
411
412        driver.fd_close(handle).unwrap();
413    }
414}