Skip to main content

raw_buffer/
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//! - If the raw slice wrapper is used with stack allocated slices: Higher-level APIs
26//!   oftentimes rely on `Drop` implementations to allow cancelling
27//!   transfers on non-blocking APIs. Using `core::mem::forget` on such implementations
28//!   can leak the underlying memory. HAL and firmware authors *MUST* either use this
29//!   abstraction combined with `'static` slices or ensure that the `Drop`
30//!   implementations always runs properly.
31//!
32//! ## Usage Example
33//!
34//! ```rust
35//! use raw_slice::RawBufSlice;
36//!
37//! static DATA: &[u8] = &[1, 2, 3, 4];
38//!
39//! // Safety: Your safety note here. We are using static data, your DMA might have other
40//! // requirements to the buffer used, e.g. buffer in correct RAM, with certain alignment etc.
41//! let raw_buf = unsafe { RawSlice::new(DATA) };
42//!
43//! // Later, in an ISR or different context
44//! unsafe {
45//!     if let Some(slice) = raw_buf.get() {
46//!         // Process the data, e.g. send it via serial interface
47//!         // self.rx.write(slice);
48//!     }
49//! }
50//! ```
51//!
52//! ## API Design
53//!
54//! While this crate provides methods to interact with the stored data, most of these operations
55//! remain `unsafe` due to the compiler's inability to enforce lifetimes after erasure. Users should
56//! carefully ensure the referenced data remains valid for the required duration. In addition
57//! to the concept of a slice being empty, a raw slice can also be NULL.
58//!
59//! ## Embedded DMA Support
60//!
61//! - The [RawBufSlice] structure implements the [embedded_dma::ReadBuffer] trait
62//! - The [RawBufSliceMut] structure implements the [embedded_dma::WriteBuffer] trait
63#![no_std]
64
65#[derive(Debug, Copy, Clone, PartialEq, Eq)]
66pub struct RawSlice<T> {
67    data: *const T,
68    len: usize,
69}
70
71/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
72unsafe impl<T: Send> Send for RawSlice<T> {}
73
74impl<T> RawSlice<T> {
75    /// Creates a new `RawSlice<T>` from a slice reference.
76    ///
77    /// # Safety
78    ///
79    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`. This is especially
80    ///   important if the original slice is stack allocated.
81    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
82    pub const unsafe fn new(data: &[T]) -> Self {
83        Self {
84            data: data.as_ptr(),
85            len: data.len(),
86        }
87    }
88
89    /// Creates an empty `RawSlice<T>`, equivalent to a null pointer with zero length.
90    pub const fn new_nulled() -> Self {
91        Self {
92            data: core::ptr::null(),
93            len: 0,
94        }
95    }
96
97    /// Updates the raw pointer and length to point to a new slice.
98    ///
99    /// # Safety
100    ///
101    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`. This is especially
102    ///   important if the original slice is stack allocated.
103    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
104    pub const unsafe fn set(&mut self, data: &[T]) {
105        self.data = data.as_ptr();
106        self.len = data.len();
107    }
108
109    /// Set the internal data pointer to NULL and also clears the data length.
110    pub const fn set_null(&mut self) {
111        self.data = core::ptr::null();
112        self.len = 0;
113    }
114
115    /// Check whether the internal data pointer is NULL.
116    pub const fn is_null(&self) -> bool {
117        self.data.is_null()
118    }
119
120    /// Converts the raw pointer into a slice.
121    ///
122    /// Returns [None] if the pointer is null.
123    ///
124    /// # Safety
125    ///
126    /// - The caller **must** ensure that the underlying memory is still valid.
127    /// - Using this function after the original slice is dropped results in UB.
128    pub const unsafe fn get(&self) -> Option<&[T]> {
129        if self.data.is_null() {
130            return None;
131        }
132        Some(unsafe { core::slice::from_raw_parts(self.data, self.len) })
133    }
134
135    /// Returns [None] if the pointer is null and whether [Self::len] is 0 otherwise.
136    pub const fn is_empty(&self) -> Option<bool> {
137        if self.is_null() {
138            return None;
139        }
140        Some(self.len == 0)
141    }
142
143    /// Returns [None] if the pointer is null and the length of the raw slice otherwise.
144    pub const fn len(&self) -> Option<usize> {
145        if self.is_null() {
146            return None;
147        }
148        Some(self.len)
149    }
150}
151
152impl<T> Default for RawSlice<T> {
153    fn default() -> Self {
154        Self::new_nulled()
155    }
156}
157
158pub type RawBufSlice = RawU8Slice;
159pub type RawU8Slice = RawSlice<u8>;
160pub type RawU16Slice = RawSlice<u16>;
161pub type RawU32Slice = RawSlice<u32>;
162
163macro_rules! impl_dma_read_buf {
164    ($slice_type:ident, $ty:ident) => {
165        /// This allows using [Self] in DMA based APIs which expect a [embedded_dma::ReadBuffer].
166        ///
167        /// However, the user still must ensure that any alignment rules for DMA buffers required by
168        /// the hardware are met and than any MPU/MMU configuration necessary is also performed for this
169        /// to work properly.
170        ///
171        /// # Safety
172        ///
173        /// - The raw slice type erases the lifetime of slice. The caller *MUST* ensure that the
174        ///   lifetime of the slice is valid as long as the buffer is in-use by the DMA.
175        /// - It is also imperitive that the DMA system you're using returns the pointer
176        ///   *only after a DMA transfer is complete*. If you're unsure check the docs and if nothing
177        ///   is mentioned in the docs please clarify it with a project maintainer.
178        /// - If the raw slice wrapper is used on a stack allocated slice: Higher-level APIs
179        ///   oftentimes rely on `Drop` implementations to allow cancelling
180        ///   transfers on non-blocking APIs. Using `core::mem::forget` on such implementations
181        ///   can leak the underlying memory. HAL and firmware authors *MUST* either use this
182        ///   abstraction combined with `'static` slices or ensure that the `Drop`
183        ///   implementations always runs properly.
184        unsafe impl embedded_dma::ReadBuffer for $slice_type {
185            type Word = $ty;
186
187            unsafe fn read_buffer(&self) -> (*const Self::Word, usize) {
188                (self.data, self.len)
189            }
190        }
191    };
192}
193
194impl_dma_read_buf!(RawBufSlice, u8);
195impl_dma_read_buf!(RawU16Slice, u16);
196impl_dma_read_buf!(RawU32Slice, u32);
197
198#[derive(Debug, Copy, Clone)]
199pub struct RawSliceMut<T> {
200    data: *mut T,
201    len: usize,
202}
203
204/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
205unsafe impl<T: Send> Send for RawSliceMut<T> {}
206
207impl<T> RawSliceMut<T> {
208    /// Creates a new `RawSlice<T>` from a slice reference.
209    ///
210    /// # Safety
211    ///
212    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`. This is especially
213    ///   important if the original slice is stack allocated.
214    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
215    pub const unsafe fn new(data: &mut [T]) -> Self {
216        Self {
217            data: data.as_mut_ptr(),
218            len: data.len(),
219        }
220    }
221
222    /// Creates an empty `RawSlice<T>`, equivalent to a null pointer with zero length.
223    pub const fn new_nulled() -> Self {
224        Self {
225            data: core::ptr::null_mut(),
226            len: 0,
227        }
228    }
229
230    /// Updates the raw pointer and length to point to a new slice.
231    ///
232    /// # Safety
233    ///
234    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`. This is especially
235    ///   important if the original slice is stack allocated.
236    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
237    pub const unsafe fn set(&mut self, data: &mut [T]) {
238        self.data = data.as_mut_ptr();
239        self.len = data.len();
240    }
241
242    /// Converts the raw pointer into a slice.
243    ///
244    /// Returns [None] if the pointer is null.
245    ///
246    /// # Safety
247    ///
248    /// - The caller **must** ensure that the underlying memory is still valid.
249    /// - Using this function after the original slice is dropped results in UB.
250    pub const unsafe fn get<'slice>(&self) -> Option<&'slice [T]> {
251        if self.data.is_null() {
252            return None;
253        }
254        Some(unsafe { core::slice::from_raw_parts(self.data, self.len) })
255    }
256
257    /// Converts the raw pointer into a mutable slice.
258    ///
259    /// Returns [None] if the pointer is null.
260    ///
261    /// # Safety
262    ///
263    /// - The caller **must** ensure that the underlying memory is still valid.
264    /// - Using this function after the original slice is dropped results in UB.
265    pub const unsafe fn get_mut<'slice>(&mut self) -> Option<&'slice mut [T]> {
266        if self.data.is_null() {
267            return None;
268        }
269        Some(unsafe { core::slice::from_raw_parts_mut(self.data, self.len) })
270    }
271
272    pub const fn set_null(&mut self) {
273        self.data = core::ptr::null_mut();
274        self.len = 0;
275    }
276
277    pub const fn is_null(&self) -> bool {
278        self.data.is_null()
279    }
280
281    /// Returns [None] if the pointer is null and whether [Self::len] is 0 otherwise.
282    pub const fn is_empty(&self) -> Option<bool> {
283        if self.is_null() {
284            return None;
285        }
286        Some(self.len == 0)
287    }
288
289    /// Returns [None] if the pointer is null and the length of the raw slice otherwise.
290    pub const fn len(&self) -> Option<usize> {
291        if self.is_null() {
292            return None;
293        }
294        Some(self.len)
295    }
296}
297
298impl<T> Default for RawSliceMut<T> {
299    fn default() -> Self {
300        Self::new_nulled()
301    }
302}
303
304pub type RawBufSliceMut = RawU8SliceMut;
305pub type RawU8SliceMut = RawSliceMut<u8>;
306pub type RawU16SliceMut = RawSliceMut<u16>;
307pub type RawU32SliceMut = RawSliceMut<u32>;
308
309macro_rules! impl_dma_write_buf {
310    ($slice_type:ident, $ty:ident) => {
311        /// This allows using [Self] in DMA APIs which expect a [embedded_dma::WriteBuffer].
312        ///
313        /// However, the user still must ensure that any alignment rules for DMA buffers required by
314        /// the hardware are met and than any MPU/MMU configuration necessary was also performed.
315        ///
316        /// # Safety
317        ///
318        /// - The raw slice type erases the lifetime of slice. The caller *MUST* ensure that the
319        ///   lifetime of the slice is valid as long as the buffer is in-use by the DMA.
320        /// - It is also imperitive that the DMA system you're using returns the pointer
321        ///   *only after a DMA transfer is complete*. If you're unsure check the docs and if nothing
322        ///   is mentioned in the docs please clarify it with a project maintainer.
323        /// - If the raw slice wrapper is used on a stack allocated slice: Higher-level APIs
324        ///   oftentimes rely on `Drop` implementations to allow cancelling
325        ///   transfers on non-blocking APIs. Using `core::mem::forget` on such implementations
326        ///   can leak the underlying memory, allowing hardware to write to invalid memory
327        ///   locations. HAL and firmware authors *MUST* either use this abstraction combined with
328        ///   `'static` slices or ensure that the `Drop` implementations always runs properly.
329        unsafe impl embedded_dma::WriteBuffer for $slice_type {
330            type Word = $ty;
331
332            unsafe fn write_buffer(&mut self) -> (*mut Self::Word, usize) {
333                (self.data, self.len)
334            }
335        }
336    };
337}
338
339impl_dma_write_buf!(RawBufSliceMut, u8);
340impl_dma_write_buf!(RawU16SliceMut, u16);
341impl_dma_write_buf!(RawU32SliceMut, u32);
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346
347    #[test]
348    pub fn test_basic() {
349        let slice = [1, 2, 3, 4];
350        let mut slice_raw = unsafe { RawBufSlice::new(&slice) };
351        assert_eq!(slice_raw.len().unwrap(), 4);
352        assert!(!slice_raw.is_null());
353        assert!(!slice_raw.is_empty().unwrap());
354        assert_eq!(slice_raw.len().unwrap(), 4);
355        let slice_read_back = unsafe { slice_raw.get().unwrap() };
356        assert_eq!(slice_read_back, slice);
357        slice_raw.set_null();
358        generic_empty_test(&slice_raw);
359    }
360
361    #[test]
362    pub fn test_empty() {
363        let empty = RawBufSlice::new_nulled();
364        generic_empty_test(&empty);
365    }
366
367    #[test]
368    pub fn test_empty_mut() {
369        let mut empty = RawBufSliceMut::new_nulled();
370        generic_empty_test_mut(&mut empty);
371    }
372
373    #[test]
374    pub fn test_clonable() {
375        let slice = [1, 2, 3, 4];
376        let slice_raw = unsafe { RawBufSlice::new(&slice) };
377        let slice_copied = slice_raw;
378        assert_eq!(slice_copied, slice_raw);
379    }
380
381    #[test]
382    pub fn test_basic_mut() {
383        let mut slice = [1, 2, 3, 4];
384        let mut slice_raw = unsafe { RawBufSliceMut::new(&mut slice) };
385        assert_eq!(slice_raw.len().unwrap(), 4);
386        assert!(!slice_raw.is_null());
387        assert!(!slice_raw.is_empty().unwrap());
388        assert_eq!(slice_raw.len().unwrap(), 4);
389        let slice_read_back = unsafe { slice_raw.get().unwrap() };
390        assert_eq!(slice_read_back, slice);
391        let mut_slice_read_back = unsafe { slice_raw.get_mut().unwrap() };
392        assert_eq!(slice_read_back, mut_slice_read_back);
393        mut_slice_read_back[0] = 5;
394        assert_eq!(slice[0], 5);
395        slice_raw.set_null();
396        generic_empty_test_mut(&mut slice_raw);
397    }
398
399    fn generic_empty_test(slice: &RawBufSlice) {
400        assert!(slice.is_null());
401        assert!(slice.is_empty().is_none());
402        assert!(slice.len().is_none());
403        assert!(unsafe { slice.get() }.is_none());
404    }
405
406    fn generic_empty_test_mut(slice: &mut RawBufSliceMut) {
407        assert!(slice.is_null());
408        assert!(slice.is_empty().is_none());
409        assert!(slice.len().is_none());
410        assert!(unsafe { slice.get() }.is_none());
411        assert!(unsafe { slice.get_mut() }.is_none());
412    }
413}