byte_slab/lib.rs
1//! # Byte Slab
2//!
3//! Byte Slab is a crate that provides a pool or slab of bytes, which can be granted in
4//! fixed-size chunks. It is similar to heapless::Pool, however it also allows conversion
5//! of the allocations (`SlabBox`es) into shared, reference counted objects (`SlabArc`s).
6//!
7//! Currently, it maintains its free list as an MPMC queue, though that is an implementation
8//! detail that may change. This implementation is convenient, but not particularly memory-dense.
9//!
10//! The slab is statically allocated, and the size of each Box, as well as the total number of
11//! Boxes available is selected through compile time `const` values.
12//!
13//! Byte Slab is intended to provide boxes suitable for using as DMA buffers on bare metal
14//! embedded systems without a general purpose allocator. All allocations are failable.
15//!
16//! ## Main components
17//!
18//! The byte slab crate is made up of the following primary elements:
19//!
20//! * `BSlab` - a Byte Slab. This struct represents the storage of all boxes and their
21//! related metadata.
22//! * `SlabBox` - An owned allocation from the BSlab, which may be read or written to
23//! (exclusively) by the owner. A `SlabBox` may be converted into a `SlabArc`. The
24//! underlying memory is freed for reuse automatically when the Box has been dropped.
25//! * `SlabArc` - A reference counted allocation from the BSlab, obtained by consuming a
26//! `SlabBox`. As the underlying allocation may be shared, a `SlabArc` does not allow
27//! for the contents to be modified. `SlabArc`s may be cloned (which increases the
28//! reference count), allowing for multiple (immutable) access to the same data. The
29//! underlying memory is freed for reuse automatically when the reference count reaches
30//! zero.
31//! * `SlabSliceArc` - a reference counted view of a `SlabArc`. This is used to provide a
32//! view onto a portion of a `SlabArc`, without sharing the entire allocation. It shares
33//! the same reference count as the underlying `SlabArc`, meaning the underlying `SlabArc`
34//! will not be freed if there are only `SlabSliceArc`s remaining. The underlying memory
35//! is freed for reuse automatically when the reference count reaches zero.
36//! * `ManagedArcSlab` - a convenience type that may contain EITHER a borrowed `&[u8]` slice,
37//! or a `SlabSliceArc`.
38//!
39//! ## Example
40//!
41//! ```rust
42//! use byte_slab::BSlab;
43//!
44//! // Declare a byte slab with four elements, each 128 bytes
45//! static SLAB: BSlab<4, 128> = BSlab::new();
46//!
47//! fn main() {
48//! // Initialize the byte slab
49//! SLAB.init().unwrap();
50//!
51//! // Get the first box
52//! let mut box_1 = SLAB.alloc_box().unwrap();
53//!
54//! assert_eq!(box_1.len(), 128);
55//! box_1.iter_mut().for_each(|i| *i = 42);
56//!
57//! // We can also get three more boxes
58//! let mut box_2 = SLAB.alloc_box().unwrap();
59//! let mut box_3 = SLAB.alloc_box().unwrap();
60//! let mut box_4 = SLAB.alloc_box().unwrap();
61//!
62//! // Uh oh, no more boxes!
63//! assert!(SLAB.alloc_box().is_none());
64//!
65//! // Until we free one!
66//! drop(box_2);
67//!
68//! // Then we can grab one again
69//! let mut box_4 = SLAB.alloc_box().unwrap();
70//! }
71//! ```
72//!
73//! ## Safety
74//!
75//! This probably does not handle unwind safety correctly!
76//! Please verify before using in non-abort-panic environments!
77
78#![cfg_attr(not(test), no_std)]
79
80pub mod byte_slab;
81pub mod slab_arc;
82pub mod slab_box;
83pub mod slab_slice_arc;
84pub mod managed_arc_slab;
85
86pub use crate::{
87 byte_slab::BSlab,
88 slab_arc::{SlabArc, RerooterKey},
89 slab_box::SlabBox,
90 slab_slice_arc::{SlabSliceArc, SlabStrArc},
91 managed_arc_slab::{ManagedArcSlab, ManagedArcStr, Reroot},
92};
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn it_works() {
100 static SLAB: BSlab<32, 512> = BSlab::new();
101 SLAB.init().unwrap();
102 let mut allocs = vec![];
103
104 for i in 0..32 {
105 let mut alloc_box = SLAB.alloc_box().unwrap();
106 alloc_box[0] = i;
107 alloc_box[1] = 0x42;
108 allocs.push(alloc_box);
109 }
110
111 assert!(SLAB.alloc_box().is_none());
112
113 for (i, ab) in allocs.iter().enumerate() {
114 assert_eq!(i as u8, ab[0]);
115 assert_eq!(0x42, ab[1]);
116 }
117
118 // Drop allocs, freeing them for reuse
119 drop(allocs);
120
121 let mut allocs = vec![];
122
123 for _ in 0..32 {
124 allocs.push(SLAB.alloc_box().unwrap());
125 }
126
127 assert!(SLAB.alloc_box().is_none());
128 }
129
130 #[test]
131 fn slicing_and_arcs() {
132 static SLAB: BSlab<32, 128> = BSlab::new();
133 SLAB.init().unwrap();
134
135 let mut slab_box = SLAB.alloc_box().unwrap();
136 slab_box.iter_mut().enumerate().for_each(|(i, by)| {
137 *by = i as u8;
138 });
139
140 let slab_arc = slab_box.into_arc();
141 slab_arc.iter().enumerate().for_each(|(i, by)| {
142 assert_eq!(*by, i as u8);
143 });
144
145 let sl_1 = slab_arc.sub_slice_arc(0, 64).unwrap();
146 let sl_2 = slab_arc.sub_slice_arc(64, 64).unwrap();
147
148 sl_1.iter().enumerate().for_each(|(i, by)| {
149 assert_eq!(*by, i as u8);
150 });
151 sl_2.iter().enumerate().for_each(|(i, by)| {
152 assert_eq!(*by, i as u8 + 64);
153 });
154
155 let sl_2_1 = sl_2.sub_slice_arc(0, 32).unwrap();
156 let sl_2_2 = sl_2.sub_slice_arc(32, 32).unwrap();
157
158 sl_2_1.iter().enumerate().for_each(|(i, by)| {
159 assert_eq!(*by, i as u8 + 64);
160 });
161 sl_2_2.iter().enumerate().for_each(|(i, by)| {
162 assert_eq!(*by, i as u8 + 64 + 32);
163 });
164
165 // We should now be able to allocate EXACTLY 31 pages, not 32.
166 let mut allocs = vec![];
167
168 for i in 0..31 {
169 let mut alloc_box = SLAB.alloc_box().unwrap();
170 alloc_box[0] = i;
171 alloc_box[1] = 0x42;
172 allocs.push(alloc_box);
173 }
174
175 assert!(SLAB.alloc_box().is_none());
176
177 // Now, if we drop the root arc, we still shouldn't be able to alloc.
178 drop(slab_arc);
179 assert!(SLAB.alloc_box().is_none());
180
181 // Now, the top level slices, still no alloc
182 drop(sl_1);
183 drop(sl_2);
184 assert!(SLAB.alloc_box().is_none());
185
186 // Second to last, still no alloc
187 drop(sl_2_1);
188 assert!(SLAB.alloc_box().is_none());
189
190 // Final slice. Should be free now.
191 drop(sl_2_2);
192 assert!(SLAB.alloc_box().is_some());
193 }
194}