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}