raw_slice/
lib.rs

1//! # Raw Slice Types
2//!
3//! This crate provides two generic raw slice type, [RawSlice] and [RawSliceMut], which allow
4//! erasing the lifetime of a borrowed slice.
5//!
6//! ## Motivation
7//!
8//! In Rust, lifetimes are a powerful tool for ensuring memory safety at compile time.
9//! However, there are cases where lifetimes become too restrictive, such as when working
10//! with borrowed data across asynchronous or interrupt-driven contexts.
11//!
12//! This data structure is particularly useful in embedded systems, where data may be
13//! passed to asynchronous peripherals such as serial TX drivers using interrupts or DMA.
14//! The data may be static, but it could also reside on the stack. By using a shared [RawBufSlice],
15//! you can pass borrowed data to a driver **without** needing to explicitly manage lifetimes.
16//!
17//! ## Safety Considerations
18//!
19//! - **No Lifetime Tracking:** Since [RawSlice] erases lifetimes, **the caller must ensure**
20//!   that the referenced data remains valid while the [RawSlice] is in use.
21//! - **Concurrency Risks:** Accessing the same underlying data from multiple contexts
22//!   (e.g., an ISR and a task) requires proper synchronization.
23//! - **Immutability:** [RawSlice] provides a **read-only view** of the data. If you need
24//!   mutability, [RawSliceMut] can be used.
25//!
26//! ## Usage Example
27//!
28//! ```rust
29//! use raw_slice::RawBufSlice;
30//!
31//! static DATA: &[u8] = &[1, 2, 3, 4];
32//!
33//! let raw_buf = unsafe { RawBufSlice::new(DATA) };
34//!
35//! // Later, in an ISR or different context
36//! unsafe {
37//!     if let Some(slice) = raw_buf.get() {
38//!         // Process the data, e.g. send it via serial interface
39//!         // self.rx.write(slice);
40//!     }
41//! }
42//! ```
43//!
44//! ## API Design
45//!
46//! While this crate provides methods to interact with the stored data, most of these operations
47//! remain `unsafe` due to the compiler's inability to enforce lifetimes after erasure. Users should
48//! carefully ensure the referenced data remains valid for the required duration. In addition
49//! to the concept of a slice being empty, a raw slice can also be NULL.
50//!
51//! ## Embedded DMA Support
52//!
53//! - The [RawBufSlice] structure implements the [embedded_dma::ReadBuffer] trait
54//! - The [RawBufSliceMut] structure implements the [embedded_dma::WriteBuffer] trait
55#![no_std]
56
57#[derive(Debug, Copy, Clone, PartialEq, Eq)]
58pub struct RawSlice<T> {
59    data: *const T,
60    len: usize,
61}
62
63/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
64unsafe impl<T: Send> Send for RawSlice<T> {}
65
66impl<T> RawSlice<T> {
67    /// Creates a new `RawSlice<T>` from a slice reference.
68    ///
69    /// # Safety
70    ///
71    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`.
72    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
73    #[allow(dead_code)]
74    pub const unsafe fn new(data: &[T]) -> Self {
75        Self {
76            data: data.as_ptr(),
77            len: data.len(),
78        }
79    }
80
81    /// Creates an empty `RawSlice<T>`, equivalent to a null pointer with zero length.
82    pub const fn new_nulled() -> Self {
83        Self {
84            data: core::ptr::null(),
85            len: 0,
86        }
87    }
88
89    /// Updates the raw pointer and length to point to a new slice.
90    ///
91    /// # Safety
92    ///
93    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`.
94    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
95    pub const unsafe fn set(&mut self, data: &[T]) {
96        self.data = data.as_ptr();
97        self.len = data.len();
98    }
99
100    /// Set the internal data pointer to NULL and also clears the data length.
101    pub const fn set_null(&mut self) {
102        self.data = core::ptr::null();
103        self.len = 0;
104    }
105
106    /// Check whether the internal data pointer is NULL.
107    pub const fn is_null(&self) -> bool {
108        self.data.is_null()
109    }
110
111    /// Converts the raw pointer into a slice.
112    ///
113    /// Returns [None] if the pointer is null.
114    ///
115    /// # Safety
116    ///
117    /// - The caller **must** ensure that the underlying memory is still valid.
118    /// - Using this function after the original slice is dropped results in UB.
119    pub const unsafe fn get(&self) -> Option<&[T]> {
120        if self.data.is_null() {
121            return None;
122        }
123        Some(unsafe { core::slice::from_raw_parts(self.data, self.len) })
124    }
125
126    /// Returns [None] if the pointer is null and whether [Self::len] is 0 otherwise.
127    pub const fn is_empty(&self) -> Option<bool> {
128        if self.is_null() {
129            return None;
130        }
131        Some(self.len == 0)
132    }
133
134    /// Returns [None] if the pointer is null and the length of the raw slice otherwise.
135    pub const fn len(&self) -> Option<usize> {
136        if self.is_null() {
137            return None;
138        }
139        Some(self.len)
140    }
141}
142
143impl<T> Default for RawSlice<T> {
144    fn default() -> Self {
145        Self::new_nulled()
146    }
147}
148
149pub type RawBufSlice = RawU8Slice;
150pub type RawU8Slice = RawSlice<u8>;
151pub type RawU16Slice = RawSlice<u16>;
152pub type RawU32Slice = RawSlice<u32>;
153
154macro_rules! impl_dma_read_buf {
155    ($slice_type:ident, $ty:ident) => {
156        /// This allows using [Self] in DMA APIs which expect a [embedded_dma::ReadBuffer].
157        ///
158        /// However, the user still must ensure that any alignment rules for DMA buffers required by
159        /// the hardware are met and than any MPU/MMU configuration necessary is also performed for this
160        /// to work properly.
161        unsafe impl embedded_dma::ReadBuffer for $slice_type {
162            type Word = $ty;
163
164            unsafe fn read_buffer(&self) -> (*const Self::Word, usize) {
165                (self.data, self.len)
166            }
167        }
168    };
169}
170
171impl_dma_read_buf!(RawBufSlice, u8);
172impl_dma_read_buf!(RawU16Slice, u16);
173impl_dma_read_buf!(RawU32Slice, u32);
174
175#[derive(Debug, Copy, Clone)]
176pub struct RawSliceMut<T> {
177    data: *mut T,
178    len: usize,
179}
180
181/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
182unsafe impl<T: Send> Send for RawSliceMut<T> {}
183
184impl<T> RawSliceMut<T> {
185    /// Creates a new `RawSlice<T>` from a slice reference.
186    ///
187    /// # Safety
188    ///
189    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`.
190    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
191    #[allow(dead_code)]
192    pub const unsafe fn new(data: &mut [T]) -> Self {
193        Self {
194            data: data.as_mut_ptr(),
195            len: data.len(),
196        }
197    }
198
199    /// Creates an empty `RawSlice<T>`, equivalent to a null pointer with zero length.
200    pub const fn new_nulled() -> Self {
201        Self {
202            data: core::ptr::null_mut(),
203            len: 0,
204        }
205    }
206
207    /// Updates the raw pointer and length to point to a new slice.
208    ///
209    /// # Safety
210    ///
211    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`.
212    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
213    pub const unsafe fn set(&mut self, data: &mut [T]) {
214        self.data = data.as_mut_ptr();
215        self.len = data.len();
216    }
217
218    /// Converts the raw pointer into a slice.
219    ///
220    /// Returns [None] if the pointer is null.
221    ///
222    /// # Safety
223    ///
224    /// - The caller **must** ensure that the underlying memory is still valid.
225    /// - Using this function after the original slice is dropped results in UB.
226    pub const unsafe fn get<'slice>(&self) -> Option<&'slice [T]> {
227        if self.data.is_null() {
228            return None;
229        }
230        Some(unsafe { core::slice::from_raw_parts(self.data, self.len) })
231    }
232
233    /// Converts the raw pointer into a mutable slice.
234    ///
235    /// Returns [None] if the pointer is null.
236    ///
237    /// # Safety
238    ///
239    /// - The caller **must** ensure that the underlying memory is still valid.
240    /// - Using this function after the original slice is dropped results in UB.
241    pub const unsafe fn get_mut<'slice>(&mut self) -> Option<&'slice mut [T]> {
242        if self.data.is_null() {
243            return None;
244        }
245        Some(unsafe { core::slice::from_raw_parts_mut(self.data, self.len) })
246    }
247
248    pub const fn set_null(&mut self) {
249        self.data = core::ptr::null_mut();
250        self.len = 0;
251    }
252
253    pub const fn is_null(&self) -> bool {
254        self.data.is_null()
255    }
256
257    /// Returns [None] if the pointer is null and whether [Self::len] is 0 otherwise.
258    pub const fn is_empty(&self) -> Option<bool> {
259        if self.is_null() {
260            return None;
261        }
262        Some(self.len == 0)
263    }
264
265    /// Returns [None] if the pointer is null and the length of the raw slice otherwise.
266    pub const fn len(&self) -> Option<usize> {
267        if self.is_null() {
268            return None;
269        }
270        Some(self.len)
271    }
272}
273
274impl<T> Default for RawSliceMut<T> {
275    fn default() -> Self {
276        Self::new_nulled()
277    }
278}
279
280pub type RawBufSliceMut = RawU8SliceMut;
281pub type RawU8SliceMut = RawSliceMut<u8>;
282pub type RawU16SliceMut = RawSliceMut<u16>;
283pub type RawU32SliceMut = RawSliceMut<u32>;
284
285macro_rules! impl_dma_write_buf {
286    ($slice_type:ident, $ty:ident) => {
287        /// This allows using [Self] in DMA APIs which expect a [embedded_dma::WriteBuffer].
288        ///
289        /// However, the user still must ensure that any alignment rules for DMA buffers required by
290        /// the hardware are met and than any MPU/MMU configuration necessary was also performed.
291        unsafe impl embedded_dma::WriteBuffer for $slice_type {
292            type Word = $ty;
293
294            unsafe fn write_buffer(&mut self) -> (*mut Self::Word, usize) {
295                (self.data, self.len)
296            }
297        }
298    };
299}
300
301impl_dma_write_buf!(RawBufSliceMut, u8);
302impl_dma_write_buf!(RawU16SliceMut, u16);
303impl_dma_write_buf!(RawU32SliceMut, u32);
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308
309    #[test]
310    pub fn test_basic() {
311        let slice = [1, 2, 3, 4];
312        let mut slice_raw = unsafe { RawBufSlice::new(&slice) };
313        assert_eq!(slice_raw.len().unwrap(), 4);
314        assert!(!slice_raw.is_null());
315        assert!(!slice_raw.is_empty().unwrap());
316        assert_eq!(slice_raw.len().unwrap(), 4);
317        let slice_read_back = unsafe { slice_raw.get().unwrap() };
318        assert_eq!(slice_read_back, slice);
319        slice_raw.set_null();
320        generic_empty_test(&slice_raw);
321    }
322
323    #[test]
324    pub fn test_empty() {
325        let empty = RawBufSlice::new_nulled();
326        generic_empty_test(&empty);
327    }
328
329    #[test]
330    pub fn test_empty_mut() {
331        let mut empty = RawBufSliceMut::new_nulled();
332        generic_empty_test_mut(&mut empty);
333    }
334
335    #[test]
336    pub fn test_clonable() {
337        let slice = [1, 2, 3, 4];
338        let slice_raw = unsafe { RawBufSlice::new(&slice) };
339        let slice_copied = slice_raw;
340        assert_eq!(slice_copied, slice_raw);
341    }
342
343    #[test]
344    pub fn test_basic_mut() {
345        let mut slice = [1, 2, 3, 4];
346        let mut slice_raw = unsafe { RawBufSliceMut::new(&mut slice) };
347        assert_eq!(slice_raw.len().unwrap(), 4);
348        assert!(!slice_raw.is_null());
349        assert!(!slice_raw.is_empty().unwrap());
350        assert_eq!(slice_raw.len().unwrap(), 4);
351        let slice_read_back = unsafe { slice_raw.get().unwrap() };
352        assert_eq!(slice_read_back, slice);
353        let mut_slice_read_back = unsafe { slice_raw.get_mut().unwrap() };
354        assert_eq!(slice_read_back, mut_slice_read_back);
355        mut_slice_read_back[0] = 5;
356        assert_eq!(slice[0], 5);
357        slice_raw.set_null();
358        generic_empty_test_mut(&mut slice_raw);
359    }
360
361    fn generic_empty_test(slice: &RawBufSlice) {
362        assert!(slice.is_null());
363        assert!(slice.is_empty().is_none());
364        assert!(slice.len().is_none());
365        assert!(unsafe { slice.get() }.is_none());
366    }
367
368    fn generic_empty_test_mut(slice: &mut RawBufSliceMut) {
369        assert!(slice.is_null());
370        assert!(slice.is_empty().is_none());
371        assert!(slice.len().is_none());
372        assert!(unsafe { slice.get() }.is_none());
373        assert!(unsafe { slice.get_mut() }.is_none());
374    }
375}