neotron_ffi/
buffer.rs

1//! Types representing mutable borrowed byte slices.
2
3// ============================================================================
4// Imports
5// ============================================================================
6
7// None
8
9// ============================================================================
10// Constants
11// ============================================================================
12
13// None
14
15// ============================================================================
16// Types
17// ============================================================================
18
19/// A Rust u8 mutable slice, but compatible with FFI. Assume the lifetime is
20/// only valid until the callee returns to the caller.
21#[repr(C)]
22#[derive(Clone)]
23pub struct FfiBuffer<'a> {
24    /// A pointer to where the data can be put
25    pub data: *mut u8,
26    /// The maximum number of bytes we can store in this buffer
27    pub data_len: usize,
28    /// A phantom object to hold the lifetime
29    _phantom: core::marker::PhantomData<&'a [u8]>,
30}
31
32impl<'a> FfiBuffer<'a> {
33    /// Create a new buffer we can send over the FFI.
34    ///
35    /// This buffer is a mutable borrow of some storage space allocated
36    /// elsewhere. If you are given this type in an API, assume it is only
37    /// valid for as long as the function call you were given in it.
38    pub fn new(s: &'a mut [u8]) -> FfiBuffer<'a> {
39        FfiBuffer {
40            data: s.as_mut_ptr(),
41            data_len: s.len(),
42            _phantom: core::marker::PhantomData,
43        }
44    }
45
46    /// Make an empty slice.
47    pub fn empty() -> FfiBuffer<'static> {
48        FfiBuffer {
49            data: core::ptr::null_mut(),
50            data_len: 0,
51            _phantom: core::marker::PhantomData,
52        }
53    }
54
55    /// Turn this buffer into a Rust byte slice.
56    pub fn as_slice(&self) -> &[u8] {
57        if self.data.is_null() {
58            &[]
59        } else {
60            unsafe { core::slice::from_raw_parts(self.data, self.data_len) }
61        }
62    }
63
64    /// Turn this buffer into a Rust mutable byte slice.
65    ///
66    /// You will get `None` if the buffer is empty (i.e. has zero length).
67    pub fn as_mut_slice(&mut self) -> core::option::Option<&mut [u8]> {
68        if self.data.is_null() {
69            None
70        } else {
71            Some(unsafe { core::slice::from_raw_parts_mut(self.data, self.data_len) })
72        }
73    }
74}
75
76impl core::fmt::Debug for FfiBuffer<'_> {
77    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
78        let slice = self.as_slice();
79        write!(f, "[ ")?;
80        if let Some((last, rest)) = slice.split_last() {
81            for i in rest.iter() {
82                write!(f, "0x{:02x}, ", i)?;
83            }
84            write!(f, "0x{:02x} ", last)?;
85        }
86        write!(f, "]")
87    }
88}
89
90impl<'a> From<&'a mut [u8]> for FfiBuffer<'a> {
91    /// Convert from a Rust byte slice into an FFI compatible byte slice
92    fn from(input: &'a mut [u8]) -> FfiBuffer<'a> {
93        FfiBuffer::new(input)
94    }
95}
96
97impl<'a> core::cmp::PartialEq for FfiBuffer<'a> {
98    /// Check if two ApiBuffers are equal.
99    ///
100    /// We just make some actual slices and compare then.
101    fn eq(&self, rhs: &Self) -> bool {
102        if self.data_len != rhs.data_len {
103            return false;
104        }
105        let this_slice = self.as_slice();
106        let that_slice = rhs.as_slice();
107        this_slice == that_slice
108    }
109}
110
111impl<'a> core::cmp::Eq for FfiBuffer<'a> {}
112
113impl<'a> core::cmp::Ord for FfiBuffer<'a> {
114    /// Compare two ApiBuffers.
115    ///
116    /// We just make some actual slices and compare then.
117    fn cmp(&self, rhs: &Self) -> core::cmp::Ordering {
118        let this_slice = self.as_slice();
119        let that_slice = rhs.as_slice();
120        this_slice.cmp(that_slice)
121    }
122}
123
124impl<'a> core::cmp::PartialOrd for FfiBuffer<'a> {
125    /// Compare two ApiBuffers.
126    ///
127    /// We are `Ord` so we can defer to that.
128    fn partial_cmp(&self, rhs: &Self) -> core::option::Option<core::cmp::Ordering> {
129        Some(self.cmp(rhs))
130    }
131}
132
133// ============================================================================
134// Tests
135// ============================================================================
136
137#[cfg(test)]
138mod test {
139    use super::*;
140
141    #[test]
142    fn make_buffers() {
143        let data1 = &mut [1, 2, 3, 4];
144        let data2 = &mut [1, 2, 3, 4];
145        let mut data3 = vec![1, 2, 3, 5];
146        let mut buffer1 = FfiBuffer::new(data1);
147        let buffer2 = FfiBuffer::new(data2);
148        let buffer3 = FfiBuffer::new(&mut data3);
149        assert_eq!(buffer1, buffer2);
150        assert_ne!(buffer1, buffer3);
151        // This should be a compile failure because we drop the data source and
152        // use the ApiBuffer again.
153        // drop(data3);
154        assert!(buffer1 < buffer3);
155
156        let output1 = buffer1.as_mut_slice().unwrap();
157        assert_eq!(output1, &[1, 2, 3, 4]);
158    }
159}
160
161// ============================================================================
162// End of File
163// ============================================================================