mini_io_queue/storage/
mod.rs

1//! Types to define how data is stored in a queue.
2//!
3//! To access data in an [`asyncio`], [`blocking`] or [`nonblocking`] queue, the backing buffer must
4//! implement the [`Storage`] trait. This trait acts as an _unsafe_ building block to access
5//! multiple mutable regions in a buffer, which each queue builds on to provide safe abstractions.
6//!
7//! Two standard storage types are available out of the box:
8//!  - [`HeapBuffer`] stores a contiguous array of items on the heap. This means the buffer can have
9//!    a size chosen at runtime and is cheaper to move, with the possible downside of requiring an
10//!    allocator.
11//!  - [`StackBuffer`] stores a contiguous array of items on the stack. As a result, it has a fixed
12//!    size at compile-time and can be expensive to move but is extremely cheap to create.
13//!
14//! Note: all queues currently move their buffers to the heap when constructed.
15//!
16//! [`asyncio`]: crate::asyncio
17//! [`blocking`]: crate::blocking
18//! [`nonblocking`]: crate::nonblocking
19//!
20//! # Examples
21//!
22//! Creating a queue from a heap-allocated buffer:
23//!
24//! ```
25//! use mini_io_queue::blocking;
26//! use mini_io_queue::storage::HeapBuffer;
27//!
28//! let buffer: HeapBuffer<u8> = HeapBuffer::new(100);
29//! let (reader, writer) = blocking::queue_from(buffer);
30//! ```
31//!
32//! Creating a queue from a stack-allocated buffer:
33//!
34//! ```
35//! use mini_io_queue::blocking;
36//! use mini_io_queue::storage::StackBuffer;
37//!
38//! let buffer: StackBuffer<u8, 100> = StackBuffer::default();
39//! let (reader, writer) = blocking::queue_from(buffer);
40//! ```
41
42use core::cell::UnsafeCell;
43use core::ops::Range;
44use core::slice::{from_raw_parts, from_raw_parts_mut};
45
46#[cfg(feature = "heap-buffer")]
47mod heap_buffer;
48
49#[cfg(feature = "stack-buffer")]
50mod stack_buffer;
51
52#[cfg(feature = "heap-buffer")]
53pub use self::heap_buffer::*;
54
55#[cfg(feature = "stack-buffer")]
56pub use self::stack_buffer::*;
57
58/// Storage for a contiguous array of items allowing mutable access to multiple ranges as long as
59/// they don't overlap.
60///
61/// This is implemented for any type that implements `AsRef<[UnsafeCell<T>]>`. No matter the backing
62/// store, elements must be wrapped in some kind of [cell], as this is the only safe way to get a
63/// mutable reference to something through a non-mutable reference.
64///
65/// This is a low-level trait, and must be implemented carefully.
66///
67/// [cell]: core::cell
68pub trait Storage<T> {
69    /// Gets the capacity of the array, or in other words the upper bound for ranges passed into
70    /// [`slice`] and [`slice_mut`] before they will panic.
71    ///
72    /// [`slice`]: Storage::slice
73    /// [`slice_mut`]: Storage::slice_mut
74    fn capacity(&self) -> usize;
75
76    /// Gets a slice of elements in the range provided. The length of the slice will always match
77    /// the length of the range.
78    ///
79    /// # Panics
80    /// This function may panic if the range extends beyond the length of the array returned by
81    /// [`capacity`].
82    ///
83    /// [`capacity`]: Storage::capacity
84    fn slice(&self, range: Range<usize>) -> &[T];
85
86    /// Gets a mutable slice of elements in the range provided. The length of the slice will always
87    /// match the length of the range. This function is unchecked and unsafe because it does not
88    /// ensure there are no other references overlapping with the range, which is against Rust's
89    /// borrowing rules and is **very unsafe!**
90    ///
91    /// This function does ensure the range is valid, however.
92    ///
93    /// # Safety
94    /// No other slices that overlap with the range must exist for the duration of this slice's
95    /// lifetime.
96    ///
97    /// # Panics
98    /// This function may panic if the range extends beyond the length of the array returned by
99    /// [`capacity`].
100    ///
101    /// [`capacity`]: Storage::capacity
102    #[allow(clippy::mut_from_ref)]
103    unsafe fn slice_mut_unchecked(&self, range: Range<usize>) -> &mut [T];
104
105    /// Gets a mutable slice of elements in the range provided. The length of the slice will always
106    /// match the length of the range.
107    ///
108    /// # Panics
109    /// This function may panic if the range extends beyond the length of the array returned by
110    /// [`capacity`].
111    ///
112    /// [`capacity`]: Storage::capacity
113    #[inline]
114    fn slice_mut(&mut self, range: Range<usize>) -> &mut [T] {
115        // Safety: the borrow checker prevents overlapping slices existing.
116        unsafe { self.slice_mut_unchecked(range) }
117    }
118}
119
120impl<T, A: AsRef<[UnsafeCell<T>]>> Storage<T> for A {
121    fn capacity(&self) -> usize {
122        self.as_ref().len()
123    }
124
125    fn slice(&self, range: Range<usize>) -> &[T] {
126        let slice = &self.as_ref()[range];
127
128        let first_elem = match slice.first() {
129            Some(val) => val,
130            None => return Default::default(),
131        };
132
133        // Access the slice of UnsafeCells as a slice of inner values.
134        let slice_ptr = first_elem.get();
135
136        // Safety:
137        //  - len is guaranteed to be in range since the slice is valid.
138        //  - UnsafeCell is repr(transparent) so an UnsafeCell<T> slice has the same layout and
139        //    alignment as a T.
140        unsafe { from_raw_parts(slice_ptr, slice.len()) }
141    }
142
143    unsafe fn slice_mut_unchecked(&self, range: Range<usize>) -> &mut [T] {
144        let slice = &self.as_ref()[range];
145
146        let first_elem = match slice.first() {
147            Some(val) => val,
148            None => return Default::default(),
149        };
150
151        // Access the slice of UnsafeCells as a mutable slice of inner values.
152        let slice_ptr = first_elem.get();
153
154        // Safety:
155        //  - len is guaranteed to be in range since the slice is valid.
156        //  - UnsafeCell is repr(transparent) so an UnsafeCell<T> slice has the same layout and
157        //    alignment as a T.
158        //  - caller ensures no overlapping slices exist.
159        from_raw_parts_mut(slice_ptr, slice.len())
160    }
161}