hala_io/
file.rs

1use std::{
2    io,
3    marker::PhantomData,
4    sync::atomic::{AtomicUsize, Ordering},
5};
6
7/// File id for file description handle.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct Token(pub usize);
10
11pub trait TokenGenerator {
12    /// generate next file description handle.
13    fn next() -> Token {
14        static NEXT: AtomicUsize = AtomicUsize::new(0);
15
16        Token(NEXT.fetch_add(1, Ordering::SeqCst))
17    }
18}
19
20impl TokenGenerator for Token {}
21
22/// File description variants are used by the `fd_open` function to open file [`Handle`].
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub enum Description {
25    /// File description for generating filesystem `File`
26    File,
27    /// File description for generating `TcpListener`
28    TcpListener,
29    /// File description for generating `TcpStream`
30    TcpStream,
31    /// File description for generating `UdpSocket`
32    UdpSocket,
33    /// File description for timeout event
34    Timeout,
35    /// poller for io readiness events.
36    Poller,
37    /// Extended file description type defined by the implementation.
38    External(usize),
39}
40
41/// File description handle. created by implementator.
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct Handle {
44    /// `Token` associated with this handle object
45    pub token: Token,
46    /// File description variant
47    pub desc: Description,
48    /// Additional user-defined fd metadata
49    pub user_defined_desc: Option<*const ()>,
50    /// user-defined context data for this file handle.
51    pub data: *const (),
52}
53
54unsafe impl Send for Handle {}
55unsafe impl Sync for Handle {}
56
57impl Handle {
58    /// Create new file handle.
59    pub fn new(
60        desc: Description,
61        user_defined_desc: Option<*const ()>,
62        token: Option<Token>,
63        data: *const (),
64    ) -> Self {
65        Self {
66            token: token.unwrap_or(Token::next()),
67            desc,
68            user_defined_desc,
69            data,
70        }
71    }
72
73    /// Drop handle context data with special type.
74    pub fn drop_as<T>(self) {
75        let boxed: Box<T> = unsafe { Box::from_raw(self.data as *mut T) };
76
77        drop(boxed);
78    }
79
80    pub fn expect(&self, desc: Description) -> io::Result<()> {
81        if self.desc != desc {
82            return Err(io::Error::new(
83                io::ErrorKind::InvalidInput,
84                format!(
85                    "Expect file handle type {:?}, but got {:?}",
86                    desc, self.desc
87                ),
88            ));
89        }
90
91        Ok(())
92    }
93}
94
95impl<T> From<(Description, T)> for Handle {
96    fn from(value: (Description, T)) -> Self {
97        let data = Box::into_raw(Box::new(value.1)) as *const ();
98
99        Self::new(value.0, None, None, data)
100    }
101}
102
103impl<UDDESC, T> From<(Description, UDDESC, T)> for Handle {
104    fn from(value: (Description, UDDESC, T)) -> Self {
105        let user_defined_desc = Box::into_raw(Box::new(value.1)) as *const ();
106        let data: *const () = Box::into_raw(Box::new(value.2)) as *const ();
107
108        Self::new(value.0, Some(user_defined_desc), None, data)
109    }
110}
111
112impl<UDDESC, T> From<(Description, UDDESC, Token, T)> for Handle {
113    fn from(value: (Description, UDDESC, Token, T)) -> Self {
114        let user_defined_desc = Box::into_raw(Box::new(value.1)) as *const ();
115        let data: *const () = Box::into_raw(Box::new(value.3)) as *const ();
116
117        Self::new(value.0, Some(user_defined_desc), Some(value.2), data)
118    }
119}
120
121/// Strong type version file handle.
122pub struct TypedHandle<T> {
123    inner: Handle,
124    _marked: PhantomData<T>,
125}
126
127impl<T> From<Handle> for TypedHandle<T> {
128    fn from(value: Handle) -> Self {
129        Self::new(value)
130    }
131}
132
133impl<T> TypedHandle<T> {
134    pub fn new(inner: Handle) -> Self {
135        Self {
136            inner,
137            _marked: Default::default(),
138        }
139    }
140
141    /// Acquires a immutable reference to the handle value.
142    ///
143    /// #Panic
144    ///
145    /// The function is not "UnwindSafe" and therefore requires the closure "F" to never panic
146    #[inline]
147    pub fn with<F, R>(&self, f: F) -> R
148    where
149        F: FnOnce(&T) -> R,
150    {
151        let boxed = unsafe { Box::from_raw(self.inner.data as *mut T) };
152
153        let r = f(boxed.as_ref());
154
155        Box::into_raw(boxed);
156
157        r
158    }
159
160    /// Acquires a mutable reference to the handle value.
161    ///
162    /// #Panic
163    ///
164    /// The function is not "UnwindSafe" and therefore requires the closure "F" to never panic
165    #[inline]
166    pub fn with_mut<F, R>(&self, f: F) -> R
167    where
168        F: FnOnce(&mut T) -> R,
169    {
170        let mut boxed = unsafe { Box::from_raw(self.inner.data as *mut T) };
171
172        let r = f(boxed.as_mut());
173
174        Box::into_raw(boxed);
175
176        r
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use crate::*;
183
184    #[test]
185    fn test_typed_handle() {
186        let handle = Handle::from((Description::File, 100i32));
187
188        let typed_handle: TypedHandle<i32> = handle.into();
189
190        typed_handle.with(|v| assert_eq!(*v, 100));
191
192        typed_handle.with_mut(|v| *v = 10);
193
194        typed_handle.with(|v| assert_eq!(*v, 10));
195    }
196}