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}