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}