Skip to main content

esp_alloc/
allocators.rs

1#[cfg(feature = "nightly")]
2use core::alloc::{AllocError as CoreAllocError, Allocator as CoreAllocator};
3use core::{
4    alloc::{GlobalAlloc, Layout},
5    ptr::NonNull,
6};
7
8use allocator_api2::alloc::{AllocError, Allocator};
9use enumset::EnumSet;
10
11use crate::MemoryCapability;
12
13fn allocate_caps(
14    capabilities: EnumSet<MemoryCapability>,
15    layout: Layout,
16) -> Result<NonNull<[u8]>, AllocError> {
17    let raw_ptr = unsafe { crate::HEAP.alloc_caps(capabilities, layout) };
18    let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
19    Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
20}
21
22use crate::EspHeap;
23
24unsafe impl Allocator for EspHeap {
25    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
26        let raw_ptr = unsafe { self.alloc(layout) };
27        let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
28        Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
29    }
30
31    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
32        unsafe {
33            self.dealloc(ptr.as_ptr(), layout);
34        }
35    }
36}
37
38#[cfg(feature = "nightly")]
39unsafe impl CoreAllocator for EspHeap {
40    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, CoreAllocError> {
41        let raw_ptr = unsafe { self.alloc(layout) };
42        let ptr = NonNull::new(raw_ptr).ok_or(CoreAllocError)?;
43        Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
44    }
45
46    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
47        unsafe {
48            self.dealloc(ptr.as_ptr(), layout);
49        }
50    }
51}
52
53/// An allocator that uses all configured, available memory.
54pub struct AnyMemory;
55
56unsafe impl Allocator for AnyMemory {
57    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
58        allocate_caps(EnumSet::empty(), layout)
59    }
60
61    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
62        unsafe {
63            crate::HEAP.dealloc(ptr.as_ptr(), layout);
64        }
65    }
66}
67
68#[cfg(feature = "nightly")]
69unsafe impl CoreAllocator for AnyMemory {
70    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, CoreAllocError> {
71        allocate_caps(EnumSet::empty(), layout).map_err(|_| CoreAllocError)
72    }
73
74    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
75        unsafe {
76            crate::HEAP.dealloc(ptr.as_ptr(), layout);
77        }
78    }
79}
80
81/// An allocator that uses internal memory only.
82pub struct InternalMemory;
83
84unsafe impl Allocator for InternalMemory {
85    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
86        allocate_caps(EnumSet::from(MemoryCapability::Internal), layout)
87    }
88
89    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
90        unsafe {
91            crate::HEAP.dealloc(ptr.as_ptr(), layout);
92        }
93    }
94}
95
96#[cfg(feature = "nightly")]
97unsafe impl CoreAllocator for InternalMemory {
98    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, CoreAllocError> {
99        allocate_caps(EnumSet::from(MemoryCapability::Internal), layout).map_err(|_| CoreAllocError)
100    }
101
102    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
103        unsafe {
104            crate::HEAP.dealloc(ptr.as_ptr(), layout);
105        }
106    }
107}
108
109/// An allocator that uses external (PSRAM) memory only.
110pub struct ExternalMemory;
111
112unsafe impl Allocator for ExternalMemory {
113    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
114        allocate_caps(EnumSet::from(MemoryCapability::External), layout)
115    }
116
117    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
118        unsafe {
119            crate::HEAP.dealloc(ptr.as_ptr(), layout);
120        }
121    }
122}
123
124#[cfg(feature = "nightly")]
125unsafe impl CoreAllocator for ExternalMemory {
126    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, CoreAllocError> {
127        allocate_caps(EnumSet::from(MemoryCapability::External), layout).map_err(|_| CoreAllocError)
128    }
129
130    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
131        unsafe {
132            crate::HEAP.dealloc(ptr.as_ptr(), layout);
133        }
134    }
135}
136
137/// A DMA-compatible allocator that uses internal memory only.
138///
139/// This allocator adjusts the allocation alignment and size to be safe for use with DMA.
140pub struct DmaCompatibleInternalMemory;
141
142fn align(layout: Layout, min_alignment: usize) -> Layout {
143    let alignment = layout.align().max(min_alignment);
144    Layout::from_size_align(layout.size().next_multiple_of(alignment), alignment).unwrap()
145}
146
147impl DmaCompatibleInternalMemory {
148    fn align(layout: Layout) -> Layout {
149        align(
150            layout,
151            cfg_select! {
152                soc_internal_memory_cached => 64,
153                _ => 4,
154            },
155        )
156    }
157}
158
159unsafe impl Allocator for DmaCompatibleInternalMemory {
160    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
161        allocate_caps(
162            EnumSet::from(MemoryCapability::Internal),
163            Self::align(layout),
164        )
165    }
166
167    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
168        unsafe {
169            crate::HEAP.dealloc(ptr.as_ptr(), Self::align(layout));
170        }
171    }
172}
173
174#[cfg(feature = "nightly")]
175unsafe impl CoreAllocator for DmaCompatibleInternalMemory {
176    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, CoreAllocError> {
177        allocate_caps(
178            EnumSet::from(MemoryCapability::Internal),
179            Self::align(layout),
180        )
181        .map_err(|_| CoreAllocError)
182    }
183
184    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
185        unsafe {
186            crate::HEAP.dealloc(ptr.as_ptr(), Self::align(layout));
187        }
188    }
189}
190
191/// A DMA-compatible allocator that uses external (PSRAM) memory only.
192///
193/// This allocator adjusts the allocation alignment and size to be safe for use with DMA.
194pub struct DmaCompatibleExternalMemory;
195
196impl DmaCompatibleExternalMemory {
197    fn align(layout: Layout) -> Layout {
198        // Pessimistic alignment based on maximum possible cache line size
199        // TODO: relax by stealing the `data-cache-size` config from esp-hal
200        align(
201            layout,
202            cfg_select! {
203                esp32p4 => 128,
204                any(esp32s3, esp32s31) => 64,
205                _ => 32,
206            },
207        )
208    }
209}
210
211unsafe impl Allocator for DmaCompatibleExternalMemory {
212    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
213        allocate_caps(
214            EnumSet::from(MemoryCapability::External),
215            Self::align(layout),
216        )
217    }
218
219    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
220        unsafe {
221            crate::HEAP.dealloc(ptr.as_ptr(), Self::align(layout));
222        }
223    }
224}
225
226#[cfg(feature = "nightly")]
227unsafe impl CoreAllocator for DmaCompatibleExternalMemory {
228    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, CoreAllocError> {
229        allocate_caps(
230            EnumSet::from(MemoryCapability::External),
231            Self::align(layout),
232        )
233        .map_err(|_| CoreAllocError)
234    }
235
236    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
237        unsafe {
238            crate::HEAP.dealloc(ptr.as_ptr(), Self::align(layout));
239        }
240    }
241}