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// ============================================================================