neotron_ffi/
slice.rs

1//! Types representing immutable 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 slice, but compatible with FFI. Assume the lifetime is only valid
20/// until the callee returns to the caller.
21#[repr(C)]
22#[derive(Clone)]
23pub struct FfiByteSlice<'a> {
24    /// A pointer to the data
25    pub data: *const u8,
26    /// The number of bytes we are pointing at
27    pub data_len: usize,
28    /// A phantom object to hold the lifetime
29    _phantom: core::marker::PhantomData<&'a [u8]>,
30}
31
32impl<'a> FfiByteSlice<'a> {
33    /// Create a new byte slice we can send over the FFI.
34    pub fn new(s: &'a [u8]) -> FfiByteSlice<'a> {
35        FfiByteSlice {
36            data: s.as_ptr(),
37            data_len: s.len(),
38            _phantom: core::marker::PhantomData,
39        }
40    }
41
42    /// Make an empty slice.
43    pub fn empty() -> FfiByteSlice<'static> {
44        static EMPTY: &[u8] = &[];
45        FfiByteSlice::new(EMPTY)
46    }
47
48    /// Turn this byte slice into a Rust byte slice.
49    pub fn as_slice(&self) -> &[u8] {
50        unsafe { core::slice::from_raw_parts(self.data, self.data_len) }
51    }
52}
53
54impl core::fmt::Debug for FfiByteSlice<'_> {
55    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
56        let slice = self.as_slice();
57        write!(f, "[ ")?;
58        if let Some((last, rest)) = slice.split_last() {
59            for i in rest.iter() {
60                write!(f, "0x{:02x}, ", i)?;
61            }
62            write!(f, "0x{:02x} ", last)?;
63        }
64        write!(f, "]")
65    }
66}
67
68impl<'a> From<&'a [u8]> for FfiByteSlice<'a> {
69    /// Convert from a Rust byte slice into an FFI compatible byte slice
70    fn from(input: &'a [u8]) -> FfiByteSlice<'a> {
71        FfiByteSlice::new(input)
72    }
73}
74
75impl<'a> core::cmp::PartialEq for FfiByteSlice<'a> {
76    /// Check if two ApiByteSlices are equal.
77    ///
78    /// We just make some actual slices and compare then.
79    fn eq(&self, rhs: &Self) -> bool {
80        if self.data_len != rhs.data_len {
81            return false;
82        }
83        let this_slice = self.as_slice();
84        let that_slice = rhs.as_slice();
85        this_slice == that_slice
86    }
87}
88
89impl<'a> core::cmp::Eq for FfiByteSlice<'a> {}
90
91impl<'a> core::cmp::Ord for FfiByteSlice<'a> {
92    /// Compare two ApiByteSlices.
93    ///
94    /// We just make some actual slices and compare then.
95    fn cmp(&self, rhs: &Self) -> core::cmp::Ordering {
96        let this_slice = self.as_slice();
97        let that_slice = rhs.as_slice();
98        this_slice.cmp(that_slice)
99    }
100}
101
102impl<'a> core::cmp::PartialOrd for FfiByteSlice<'a> {
103    /// Compare two ApiByteSlices.
104    ///
105    /// We are `Ord` so we can defer to that.
106    fn partial_cmp(&self, rhs: &Self) -> core::option::Option<core::cmp::Ordering> {
107        Some(self.cmp(rhs))
108    }
109}
110
111// ============================================================================
112// Tests
113// ============================================================================
114
115#[cfg(test)]
116mod test {
117    use super::*;
118
119    #[test]
120    fn make_slices() {
121        let data1 = &[1, 2, 3, 4];
122        let data2 = &[1, 2, 3, 4];
123        let data3 = vec![1, 2, 3, 5];
124        let slice1 = FfiByteSlice::new(data1);
125        let slice2 = FfiByteSlice::new(data2);
126        let slice3 = FfiByteSlice::new(&data3);
127        assert_eq!(slice1, slice2);
128        assert_ne!(slice1, slice3);
129        // This should be a compile failure because we drop the data source and
130        // use the ApiByteSlice again.
131        // drop(data3);
132        assert!(slice1 < slice3);
133    }
134}
135
136// ============================================================================
137// End of File
138// ============================================================================