raw-buffer 0.1.2

Generic low-level raw slice types
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
//! # Raw Slice Types
//!
//! This crate provides two generic raw slice type, [RawSlice] and [RawSliceMut], which allow
//! erasing the lifetime of a borrowed slice.
//!
//! ## Motivation
//!
//! In Rust, lifetimes are a powerful tool for ensuring memory safety at compile time.
//! However, there are cases where lifetimes become too restrictive, such as when working
//! with borrowed data across asynchronous or interrupt-driven contexts.
//!
//! This data structure is particularly useful in embedded systems, where data may be
//! passed to asynchronous peripherals such as serial TX drivers using interrupts or DMA.
//! The data may be static, but it could also reside on the stack. By using a shared [RawBufSlice],
//! you can pass borrowed data to a driver without needing to explicitly manage lifetimes.
//!
//! ## Safety Considerations
//!
//! - **No Lifetime Tracking:** Since [RawSlice] erases lifetimes, **the caller must ensure**
//!   that the referenced data remains valid while the [RawSlice] is in use.
//! - **Concurrency Risks:** Accessing the same underlying data from multiple contexts
//!   (e.g., an ISR and a task) requires proper synchronization.
//! - **Immutability:** [RawSlice] provides a **read-only view** of the data. If you need
//!   mutability, [RawSliceMut] can be used.
//! - If the raw slice wrapper is used with stack allocated slices: Higher-level APIs
//!   oftentimes rely on `Drop` implementations to allow cancelling
//!   transfers on non-blocking APIs. Using `core::mem::forget` on such implementations
//!   can leak the underlying memory. HAL and firmware authors *MUST* either use this
//!   abstraction combined with `'static` slices or ensure that the `Drop`
//!   implementations always runs properly.
//!
//! ## Usage Example
//!
//! ```rust
//! use raw_slice::RawBufSlice;
//!
//! static DATA: &[u8] = &[1, 2, 3, 4];
//!
//! // Safety: Your safety note here. We are using static data, your DMA might have other
//! // requirements to the buffer used, e.g. buffer in correct RAM, with certain alignment etc.
//! let raw_buf = unsafe { RawSlice::new(DATA) };
//!
//! // Later, in an ISR or different context
//! unsafe {
//!     if let Some(slice) = raw_buf.get() {
//!         // Process the data, e.g. send it via serial interface
//!         // self.rx.write(slice);
//!     }
//! }
//! ```
//!
//! ## API Design
//!
//! While this crate provides methods to interact with the stored data, most of these operations
//! remain `unsafe` due to the compiler's inability to enforce lifetimes after erasure. Users should
//! carefully ensure the referenced data remains valid for the required duration. In addition
//! to the concept of a slice being empty, a raw slice can also be NULL.
//!
//! ## Embedded DMA Support
//!
//! - The [RawBufSlice] structure implements the [embedded_dma::ReadBuffer] trait
//! - The [RawBufSliceMut] structure implements the [embedded_dma::WriteBuffer] trait
#![no_std]

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct RawSlice<T> {
    data: *const T,
    len: usize,
}

/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
unsafe impl<T: Send> Send for RawSlice<T> {}

impl<T> RawSlice<T> {
    /// Creates a new `RawSlice<T>` from a slice reference.
    ///
    /// # Safety
    ///
    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`. This is especially
    ///   important if the original slice is stack allocated.
    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
    pub const unsafe fn new(data: &[T]) -> Self {
        Self {
            data: data.as_ptr(),
            len: data.len(),
        }
    }

    /// Creates an empty `RawSlice<T>`, equivalent to a null pointer with zero length.
    pub const fn new_nulled() -> Self {
        Self {
            data: core::ptr::null(),
            len: 0,
        }
    }

    /// Updates the raw pointer and length to point to a new slice.
    ///
    /// # Safety
    ///
    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`. This is especially
    ///   important if the original slice is stack allocated.
    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
    pub const unsafe fn set(&mut self, data: &[T]) {
        self.data = data.as_ptr();
        self.len = data.len();
    }

    /// Set the internal data pointer to NULL and also clears the data length.
    pub const fn set_null(&mut self) {
        self.data = core::ptr::null();
        self.len = 0;
    }

    /// Check whether the internal data pointer is NULL.
    pub const fn is_null(&self) -> bool {
        self.data.is_null()
    }

    /// Converts the raw pointer into a slice.
    ///
    /// Returns [None] if the pointer is null.
    ///
    /// # Safety
    ///
    /// - The caller **must** ensure that the underlying memory is still valid.
    /// - Using this function after the original slice is dropped results in UB.
    pub const unsafe fn get(&self) -> Option<&[T]> {
        if self.data.is_null() {
            return None;
        }
        Some(unsafe { core::slice::from_raw_parts(self.data, self.len) })
    }

    /// Returns [None] if the pointer is null and whether [Self::len] is 0 otherwise.
    pub const fn is_empty(&self) -> Option<bool> {
        if self.is_null() {
            return None;
        }
        Some(self.len == 0)
    }

    /// Returns [None] if the pointer is null and the length of the raw slice otherwise.
    pub const fn len(&self) -> Option<usize> {
        if self.is_null() {
            return None;
        }
        Some(self.len)
    }
}

impl<T> Default for RawSlice<T> {
    fn default() -> Self {
        Self::new_nulled()
    }
}

pub type RawBufSlice = RawU8Slice;
pub type RawU8Slice = RawSlice<u8>;
pub type RawU16Slice = RawSlice<u16>;
pub type RawU32Slice = RawSlice<u32>;

macro_rules! impl_dma_read_buf {
    ($slice_type:ident, $ty:ident) => {
        /// This allows using [Self] in DMA based APIs which expect a [embedded_dma::ReadBuffer].
        ///
        /// However, the user still must ensure that any alignment rules for DMA buffers required by
        /// the hardware are met and than any MPU/MMU configuration necessary is also performed for this
        /// to work properly.
        ///
        /// # Safety
        ///
        /// - The raw slice type erases the lifetime of slice. The caller *MUST* ensure that the
        ///   lifetime of the slice is valid as long as the buffer is in-use by the DMA.
        /// - It is also imperitive that the DMA system you're using returns the pointer
        ///   *only after a DMA transfer is complete*. If you're unsure check the docs and if nothing
        ///   is mentioned in the docs please clarify it with a project maintainer.
        /// - If the raw slice wrapper is used on a stack allocated slice: Higher-level APIs
        ///   oftentimes rely on `Drop` implementations to allow cancelling
        ///   transfers on non-blocking APIs. Using `core::mem::forget` on such implementations
        ///   can leak the underlying memory. HAL and firmware authors *MUST* either use this
        ///   abstraction combined with `'static` slices or ensure that the `Drop`
        ///   implementations always runs properly.
        unsafe impl embedded_dma::ReadBuffer for $slice_type {
            type Word = $ty;

            unsafe fn read_buffer(&self) -> (*const Self::Word, usize) {
                (self.data, self.len)
            }
        }
    };
}

impl_dma_read_buf!(RawBufSlice, u8);
impl_dma_read_buf!(RawU16Slice, u16);
impl_dma_read_buf!(RawU32Slice, u32);

#[derive(Debug, Copy, Clone)]
pub struct RawSliceMut<T> {
    data: *mut T,
    len: usize,
}

/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
unsafe impl<T: Send> Send for RawSliceMut<T> {}

impl<T> RawSliceMut<T> {
    /// Creates a new `RawSlice<T>` from a slice reference.
    ///
    /// # Safety
    ///
    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`. This is especially
    ///   important if the original slice is stack allocated.
    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
    pub const unsafe fn new(data: &mut [T]) -> Self {
        Self {
            data: data.as_mut_ptr(),
            len: data.len(),
        }
    }

    /// Creates an empty `RawSlice<T>`, equivalent to a null pointer with zero length.
    pub const fn new_nulled() -> Self {
        Self {
            data: core::ptr::null_mut(),
            len: 0,
        }
    }

    /// Updates the raw pointer and length to point to a new slice.
    ///
    /// # Safety
    ///
    /// - The caller **must** ensure that the slice outlives this `RawSlice<T>`. This is especially
    ///   important if the original slice is stack allocated.
    /// - The original slice **must not** be mutated while this `RawSlice<T>` is used.
    pub const unsafe fn set(&mut self, data: &mut [T]) {
        self.data = data.as_mut_ptr();
        self.len = data.len();
    }

    /// Converts the raw pointer into a slice.
    ///
    /// Returns [None] if the pointer is null.
    ///
    /// # Safety
    ///
    /// - The caller **must** ensure that the underlying memory is still valid.
    /// - Using this function after the original slice is dropped results in UB.
    pub const unsafe fn get<'slice>(&self) -> Option<&'slice [T]> {
        if self.data.is_null() {
            return None;
        }
        Some(unsafe { core::slice::from_raw_parts(self.data, self.len) })
    }

    /// Converts the raw pointer into a mutable slice.
    ///
    /// Returns [None] if the pointer is null.
    ///
    /// # Safety
    ///
    /// - The caller **must** ensure that the underlying memory is still valid.
    /// - Using this function after the original slice is dropped results in UB.
    pub const unsafe fn get_mut<'slice>(&mut self) -> Option<&'slice mut [T]> {
        if self.data.is_null() {
            return None;
        }
        Some(unsafe { core::slice::from_raw_parts_mut(self.data, self.len) })
    }

    pub const fn set_null(&mut self) {
        self.data = core::ptr::null_mut();
        self.len = 0;
    }

    pub const fn is_null(&self) -> bool {
        self.data.is_null()
    }

    /// Returns [None] if the pointer is null and whether [Self::len] is 0 otherwise.
    pub const fn is_empty(&self) -> Option<bool> {
        if self.is_null() {
            return None;
        }
        Some(self.len == 0)
    }

    /// Returns [None] if the pointer is null and the length of the raw slice otherwise.
    pub const fn len(&self) -> Option<usize> {
        if self.is_null() {
            return None;
        }
        Some(self.len)
    }
}

impl<T> Default for RawSliceMut<T> {
    fn default() -> Self {
        Self::new_nulled()
    }
}

pub type RawBufSliceMut = RawU8SliceMut;
pub type RawU8SliceMut = RawSliceMut<u8>;
pub type RawU16SliceMut = RawSliceMut<u16>;
pub type RawU32SliceMut = RawSliceMut<u32>;

macro_rules! impl_dma_write_buf {
    ($slice_type:ident, $ty:ident) => {
        /// This allows using [Self] in DMA APIs which expect a [embedded_dma::WriteBuffer].
        ///
        /// However, the user still must ensure that any alignment rules for DMA buffers required by
        /// the hardware are met and than any MPU/MMU configuration necessary was also performed.
        ///
        /// # Safety
        ///
        /// - The raw slice type erases the lifetime of slice. The caller *MUST* ensure that the
        ///   lifetime of the slice is valid as long as the buffer is in-use by the DMA.
        /// - It is also imperitive that the DMA system you're using returns the pointer
        ///   *only after a DMA transfer is complete*. If you're unsure check the docs and if nothing
        ///   is mentioned in the docs please clarify it with a project maintainer.
        /// - If the raw slice wrapper is used on a stack allocated slice: Higher-level APIs
        ///   oftentimes rely on `Drop` implementations to allow cancelling
        ///   transfers on non-blocking APIs. Using `core::mem::forget` on such implementations
        ///   can leak the underlying memory, allowing hardware to write to invalid memory
        ///   locations. HAL and firmware authors *MUST* either use this abstraction combined with
        ///   `'static` slices or ensure that the `Drop` implementations always runs properly.
        unsafe impl embedded_dma::WriteBuffer for $slice_type {
            type Word = $ty;

            unsafe fn write_buffer(&mut self) -> (*mut Self::Word, usize) {
                (self.data, self.len)
            }
        }
    };
}

impl_dma_write_buf!(RawBufSliceMut, u8);
impl_dma_write_buf!(RawU16SliceMut, u16);
impl_dma_write_buf!(RawU32SliceMut, u32);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    pub fn test_basic() {
        let slice = [1, 2, 3, 4];
        let mut slice_raw = unsafe { RawBufSlice::new(&slice) };
        assert_eq!(slice_raw.len().unwrap(), 4);
        assert!(!slice_raw.is_null());
        assert!(!slice_raw.is_empty().unwrap());
        assert_eq!(slice_raw.len().unwrap(), 4);
        let slice_read_back = unsafe { slice_raw.get().unwrap() };
        assert_eq!(slice_read_back, slice);
        slice_raw.set_null();
        generic_empty_test(&slice_raw);
    }

    #[test]
    pub fn test_empty() {
        let empty = RawBufSlice::new_nulled();
        generic_empty_test(&empty);
    }

    #[test]
    pub fn test_empty_mut() {
        let mut empty = RawBufSliceMut::new_nulled();
        generic_empty_test_mut(&mut empty);
    }

    #[test]
    pub fn test_clonable() {
        let slice = [1, 2, 3, 4];
        let slice_raw = unsafe { RawBufSlice::new(&slice) };
        let slice_copied = slice_raw;
        assert_eq!(slice_copied, slice_raw);
    }

    #[test]
    pub fn test_basic_mut() {
        let mut slice = [1, 2, 3, 4];
        let mut slice_raw = unsafe { RawBufSliceMut::new(&mut slice) };
        assert_eq!(slice_raw.len().unwrap(), 4);
        assert!(!slice_raw.is_null());
        assert!(!slice_raw.is_empty().unwrap());
        assert_eq!(slice_raw.len().unwrap(), 4);
        let slice_read_back = unsafe { slice_raw.get().unwrap() };
        assert_eq!(slice_read_back, slice);
        let mut_slice_read_back = unsafe { slice_raw.get_mut().unwrap() };
        assert_eq!(slice_read_back, mut_slice_read_back);
        mut_slice_read_back[0] = 5;
        assert_eq!(slice[0], 5);
        slice_raw.set_null();
        generic_empty_test_mut(&mut slice_raw);
    }

    fn generic_empty_test(slice: &RawBufSlice) {
        assert!(slice.is_null());
        assert!(slice.is_empty().is_none());
        assert!(slice.len().is_none());
        assert!(unsafe { slice.get() }.is_none());
    }

    fn generic_empty_test_mut(slice: &mut RawBufSliceMut) {
        assert!(slice.is_null());
        assert!(slice.is_empty().is_none());
        assert!(slice.len().is_none());
        assert!(unsafe { slice.get() }.is_none());
        assert!(unsafe { slice.get_mut() }.is_none());
    }
}