Skip to main content

memapi2/
lib.rs

1//! A small, `no_std`/`no_alloc`-friendly memory allocation interface for managing raw buffers.
2//!
3//! This crate provides explicit layouts, a split allocator trait stack, and structured errors.
4//! It is `no_std` by default but relies on the `alloc` crate unless `no_alloc` is enabled. Enable
5//! `std` for system allocator integrations and `os_err_reporting` for richer diagnostics.
6//!
7//! # Core traits
8//! - [`Alloc`](traits::alloc::Alloc), [`Dealloc`](traits::alloc::Dealloc),
9//!   [`Grow`](traits::alloc::Grow), [`Shrink`](traits::alloc::Shrink),
10//!   [`Realloc`](traits::alloc::Realloc)
11//! - Convenience aliases: [`BasicAlloc`](traits::alloc::BasicAlloc) and
12//!   [`FullAlloc`](traits::alloc::FullAlloc)
13//! - Mutable variants in [`alloc_mut`](traits::alloc_mut):
14//!   [`AllocMut`](traits::alloc_mut::AllocMut), [`DeallocMut`](traits::alloc_mut::DeallocMut),
15//!   [`GrowMut`](traits::alloc_mut::GrowMut), [`ShrinkMut`](traits::alloc_mut::ShrinkMut),
16//!   [`ReallocMut`](traits::alloc_mut::ReallocMut),
17//!   [`BasicAllocMut`](traits::alloc_mut::BasicAllocMut),
18//!   [`FullAllocMut`](traits::alloc_mut::FullAllocMut)
19//! - Optional scoped allocations (`alloc_temp_trait` feature):
20//!   [`AllocTemp`](traits::alloc_temp::AllocTemp)
21//!
22//! # Types and errors
23//! - [`Layout`](layout::Layout): crate layout type (with conversion to/from
24//!   [`StdLayout`](layout::StdLayout) unless `no_alloc` is enabled and `std` isn't)
25//! - [`DefaultAlloc`]: default allocator wrapper that delegates to the global allocator
26//! - Errors: [`Error`](error::Error), [`Cause`](error::Cause), [`LayoutErr`](error::LayoutErr),
27//!   [`ArithErr`](error::ArithErr), [`ArithOp`](error::ArithOp)
28//!
29//! # Data and type utilities
30//! - [`traits::data::type_props`][]: [`SizedProps`](traits::data::type_props::SizedProps),
31//!   [`PtrProps`](traits::data::type_props::PtrProps),
32//!   [`VarSized`](traits::data::type_props::VarSized),
33//!   [`VarSizedStruct`](traits::data::type_props::VarSizedStruct)
34//! - [`traits::data::marker`][]: [`UnsizedCopy`](traits::data::marker::UnsizedCopy),
35//!   [`Thin`](traits::data::marker::Thin), [`SizeMeta`](traits::data::marker::SizeMeta)
36//! - [`helpers`]: alignment, checked arithmetic, and pointer helpers
37//!
38//! # Allocator implementations
39//! - [`DefaultAlloc`] (available unless `no_alloc` is enabled and `std` isn't)
40//! - [`System`](::std::alloc::System) when the `std` feature is enabled
41//! - [`CAlloc`](allocs::c_alloc::CAlloc) behind the `c_alloc` feature
42//! - [`StackAlloc`](allocs::stack_alloc::StackAlloc) behind the `stack_alloc` feature
43//!
44//! # Feature flags
45//! - `no_alloc` disables usage of the rust `alloc` crate; `std` crate is used instead if the `std`
46//!   feature is enabled.
47//! - `std`: enables `std` integration (including [`System`](::std::alloc::System))
48//! - `os_err_reporting`: best-effort OS error reporting via `errno` (requires `std`)
49//! - `alloc_temp_trait`: scoped/temporary allocation trait
50//! - `c_alloc`: C `aligned_alloc`-style allocator ([`allocs::c_alloc`])
51//! - `stack_alloc`: `alloca`-based allocator ([`allocs::stack_alloc`])
52//! - `c_str`: enables `CStr`-specific data traits in `no_std` (MSRV: 1.64)
53//! - `metadata`: enables [`core::ptr::Pointee`] metadata support on nightly
54//! - `sized_hierarchy`: enables [`core::marker::MetaSized`] support on nightly
55//! - `full`, `full_nightly`: convenience bundles
56
57#![allow(unknown_lints)]
58#![deny(
59    clippy::all,
60    clippy::pedantic,
61    clippy::nursery,
62    clippy::multiple_unsafe_ops_per_block,
63    clippy::undocumented_unsafe_blocks,
64    missing_docs
65)]
66#![warn(clippy::missing_errors_doc)]
67#![allow(
68    clippy::inline_always,
69    clippy::borrow_as_ptr,
70    clippy::module_name_repetitions,
71    clippy::use_self,
72    clippy::question_mark,
73    unused_unsafe
74)]
75#![warn(unknown_lints)]
76#![no_implicit_prelude]
77#![cfg_attr(feature = "dev", warn(rustdoc::broken_intra_doc_links))]
78#![cfg_attr(not(feature = "std"), no_std)]
79// nightly is set by the build.rs
80#![cfg_attr(nightly, feature(allocator_api))]
81#![cfg_attr(feature = "metadata", feature(ptr_metadata))]
82#![cfg_attr(feature = "sized_hierarchy", feature(sized_hierarchy))]
83
84// TODO: add any missing cfg_attr(miri, track_caller) attributes, remove unnecessary ones
85// TODO: consider behavior of all allocation methods in all possible cases for all allocators and
86//  make sure they match and make sense
87
88extern crate core;
89extern crate rustversion;
90
91#[cfg(not(feature = "no_alloc"))] extern crate alloc as stdalloc;
92#[cfg(all(feature = "std", feature = "no_alloc"))] extern crate std as stdalloc;
93
94/// A relatively minimal prelude containing the most common, important things from this crate.
95// unfortunately we need this cfg_attr, or it thinks rustfmt is a module and can't find it
96#[allow(clippy::deprecated_cfg_attr)]
97#[cfg_attr(rustfmt, rustfmt::skip)]
98pub mod prelude {
99    pub use crate::{
100        // default allocator and layout are necessary
101        DefaultAlloc,
102        error::Error,
103        layout::Layout,
104        // traits are useful as well
105        traits::{
106            AllocError,
107            alloc::{Alloc, BasicAlloc, Dealloc, FullAlloc, Grow, Realloc, Shrink},
108            alloc_mut::{
109                AllocMut,
110                BasicAllocMut,
111                DeallocMut,
112                FullAllocMut,
113                GrowMut,
114                ReallocMut,
115                ShrinkMut
116            },
117            data::type_props::{PtrProps, SizedProps},
118            data::marker::{SizeMeta, Thin, UnsizedCopy}
119        }
120    };
121    
122    // alloc_temp trait too if the feature is on
123    #[cfg(feature = "alloc_temp_trait")] pub use crate::traits::alloc_temp::AllocTemp;
124
125    // and extra allocators that are enabled
126    #[cfg(feature = "c_alloc")] pub use crate::allocs::c_alloc::CAlloc;
127    #[cfg(feature = "stack_alloc")] pub use crate::allocs::stack_alloc::StackAlloc;
128}
129
130/// This macro is theoretically faster than `<fallible>?`.
131macro_rules! tri {
132    (::$err:ident $($fallible:expr)+) => {
133        match $($fallible)+ {
134            ::core::result::Result::Ok(x) => x,
135            ::core::result::Result::Err(e) => return ::core::result::Result::Err(Error::$err(e)),
136        }
137    };
138    (opt $($fallible:expr)+) => {
139        match $($fallible)+ {
140            Some(x) => x,
141            None => return None,
142        }
143    };
144    (do $($fallible:expr)+) => {
145        match $($fallible)+ {
146            ::core::result::Result::Ok(s) => s,
147            ::core::result::Result::Err(e) => return ::core::result::Result::Err(e),
148        }
149    };
150    (cmap($err:expr) from $e:ty, $($fallible:expr)+) => {
151        match $($fallible)+ {
152            ::core::result::Result::Ok(s) => s,
153            ::core::result::Result::Err(_) => return ::core::result::Result::Err(<$e>::from($err)),
154        }
155    };
156}
157
158macro_rules! zalloc {
159    ($self:ident, $alloc:ident, $layout:ident) => {{
160        let res = $self.$alloc($layout);
161        if let ::core::result::Result::Ok(p) = res {
162            // SAFETY: alloc returns at least layout.size() allocated bytes
163            unsafe {
164                ptr::write_bytes(p.as_ptr(), 0, $layout.size());
165            }
166        }
167        res
168    }};
169}
170
171macro_rules! default_dealloc {
172    ($self:ident.$de:ident, $ptr:ident, $l:ident) => {
173        if $l.is_nonzero_sized() && $ptr != $l.dangling() {
174            if let ::core::result::Result::Err(e) = $self.$de($ptr, $l) {
175                default_dealloc_panic($ptr, $l, e)
176            }
177        }
178    };
179}
180
181macro_rules! default_shrink {
182    ($self:ident::$unchecked:ident, $ptr:ident, $old:ident, $new:ident) => {
183        match $new.size().cmp(&$old.size()) {
184            Ordering::Greater => ::core::result::Result::Err(<Self as AllocError>::Error::from(
185                Error::ShrinkLargerNewLayout($old.size(), $new.size())
186            )),
187            Ordering::Equal => {
188                if $new.align() > $old.align() {
189                    $unchecked($self, $ptr, $old, $new)
190                } else {
191                    ::core::result::Result::Ok($ptr)
192                }
193            }
194            Ordering::Less => $unchecked($self, $ptr, $old, $new)
195        }
196    };
197}
198
199/// All traits provided by this crate.
200pub mod traits;
201
202/// Helpers that tend to be useful in other libraries as well.
203pub mod helpers;
204
205/// Errors that can occur during allocation.
206pub mod error;
207
208/// A custom layout type to get around some strange Rust stdlib limitations.
209pub mod layout;
210
211#[cfg(any(feature = "c_alloc", feature = "stack_alloc"))]
212/// Additional allocators which this crate supports.
213pub mod allocs;
214
215#[cfg(any(feature = "c_alloc", feature = "stack_alloc"))]
216/// FFI backing any extra enabled allocators.
217pub mod ffi;
218
219/// Default allocator, delegating to the global allocator.
220///
221/// # Note
222///
223/// This must _not_ be set as the global allocator (via `#[global_allocator]`). Doing so will lead
224/// to infinite recursion, as the allocation functions this calls (in
225/// [`alloc::alloc`](::stdalloc::alloc)) delegate to the global allocator.
226#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
227pub struct DefaultAlloc;
228
229#[cfg(any(not(feature = "no_alloc"), feature = "std"))]
230macro_rules! default_alloc_impl {
231    ($ty:ty) => {
232        impl crate::traits::AllocError for $ty {
233            type Error = crate::error::Error;
234        }
235
236        impl crate::traits::alloc::Alloc for $ty {
237            #[cfg_attr(miri, track_caller)]
238            #[inline(always)]
239            fn alloc(
240                &self,
241                layout: crate::layout::Layout
242            ) -> ::core::result::Result<::core::ptr::NonNull<u8>, crate::error::Error> {
243                crate::helpers::null_q_dyn_zsl_check(
244                    layout,
245                    // SAFETY: we check the layout is non-zero-sized before use.
246                    |layout| unsafe { ::stdalloc::alloc::alloc(layout.to_stdlib()) }
247                )
248            }
249
250            #[cfg_attr(miri, track_caller)]
251            #[inline(always)]
252            fn zalloc(
253                &self,
254                layout: crate::layout::Layout
255            ) -> ::core::result::Result<::core::ptr::NonNull<u8>, crate::error::Error> {
256                crate::helpers::null_q_dyn_zsl_check(
257                    layout,
258                    // SAFETY: we check the layout is non-zero-sized before use.
259                    |layout| unsafe { ::stdalloc::alloc::alloc_zeroed(layout.to_stdlib()) }
260                )
261            }
262        }
263        impl crate::traits::alloc::Dealloc for $ty {
264            #[cfg_attr(miri, track_caller)]
265            #[inline(always)]
266            unsafe fn dealloc(&self, ptr: ::core::ptr::NonNull<u8>, layout: crate::layout::Layout) {
267                if layout.is_nonzero_sized() && ptr != layout.dangling() {
268                    ::stdalloc::alloc::dealloc(ptr.as_ptr(), layout.to_stdlib());
269                }
270            }
271
272            #[cfg_attr(miri, track_caller)]
273            #[inline(always)]
274            unsafe fn try_dealloc(
275                &self,
276                ptr: ::core::ptr::NonNull<u8>,
277                layout: crate::layout::Layout
278            ) -> ::core::result::Result<(), crate::error::Error> {
279                if layout.is_zero_sized() {
280                    ::core::result::Result::Err(crate::error::Error::ZeroSizedLayout)
281                } else if ptr == layout.dangling() {
282                    ::core::result::Result::Err(crate::error::Error::DanglingDeallocation)
283                } else {
284                    ::stdalloc::alloc::dealloc(ptr.as_ptr(), layout.to_stdlib());
285                    ::core::result::Result::Ok(())
286                }
287            }
288        }
289        impl crate::traits::alloc::Grow for $ty {}
290        impl crate::traits::alloc::Shrink for $ty {}
291        impl crate::traits::alloc::Realloc for $ty {}
292    };
293}
294
295#[cfg(any(not(feature = "no_alloc"), feature = "std"))]
296// SAFETY: DefaultAlloc doesn't unwind, and all layout operations are correct
297unsafe impl ::stdalloc::alloc::GlobalAlloc for DefaultAlloc {
298    #[cfg_attr(miri, track_caller)]
299    #[inline]
300    unsafe fn alloc(&self, layout: crate::layout::StdLayout) -> *mut u8 {
301        ::stdalloc::alloc::alloc(layout)
302    }
303
304    #[cfg_attr(miri, track_caller)]
305    #[inline]
306    unsafe fn dealloc(&self, ptr: *mut u8, layout: crate::layout::StdLayout) {
307        ::stdalloc::alloc::dealloc(ptr, layout);
308    }
309
310    #[cfg_attr(miri, track_caller)]
311    #[inline]
312    unsafe fn alloc_zeroed(&self, layout: crate::layout::StdLayout) -> *mut u8 {
313        ::stdalloc::alloc::alloc_zeroed(layout)
314    }
315
316    #[cfg_attr(miri, track_caller)]
317    #[inline]
318    unsafe fn realloc(
319        &self,
320        ptr: *mut u8,
321        layout: crate::layout::StdLayout,
322        new_size: usize
323    ) -> *mut u8 {
324        ::stdalloc::alloc::realloc(ptr, layout, new_size)
325    }
326}
327
328#[cfg(any(not(feature = "no_alloc"), feature = "std"))]
329default_alloc_impl!(DefaultAlloc);
330
331#[cfg(all(nightly, not(feature = "no_alloc")))]
332/// The primary module for when `nightly` is enabled.
333pub(crate) mod nightly {
334    use {
335        ::core::ptr::NonNull,
336        ::stdalloc::alloc::{AllocError, Allocator, Global}
337    };
338
339    // SAFETY: DefaultAlloc's allocated memory isn't deallocated until a deallocation method is
340    //  called. as a ZST allocator, copying/cloning it doesn't change behavior or invalidate
341    //  allocations.
342    unsafe impl Allocator for crate::DefaultAlloc {
343        #[cfg_attr(miri, track_caller)]
344        #[inline]
345        fn allocate(
346            &self,
347            layout: crate::layout::StdLayout
348        ) -> ::core::result::Result<NonNull<[u8]>, AllocError> {
349            Allocator::allocate(&Global, layout)
350        }
351
352        #[cfg_attr(miri, track_caller)]
353        #[inline]
354        fn allocate_zeroed(
355            &self,
356            layout: crate::layout::StdLayout
357        ) -> ::core::result::Result<NonNull<[u8]>, AllocError> {
358            Allocator::allocate_zeroed(&Global, layout)
359        }
360
361        #[cfg_attr(miri, track_caller)]
362        #[inline]
363        unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: crate::layout::StdLayout) {
364            Allocator::deallocate(&Global, ptr.cast(), layout);
365        }
366
367        #[cfg_attr(miri, track_caller)]
368        #[inline]
369        unsafe fn grow(
370            &self,
371            ptr: NonNull<u8>,
372            old_layout: crate::layout::StdLayout,
373            new: crate::layout::StdLayout
374        ) -> ::core::result::Result<NonNull<[u8]>, AllocError> {
375            Allocator::grow(&Global, ptr.cast(), old_layout, new)
376        }
377
378        #[cfg_attr(miri, track_caller)]
379        #[inline]
380        unsafe fn grow_zeroed(
381            &self,
382            ptr: NonNull<u8>,
383            old_layout: crate::layout::StdLayout,
384            new: crate::layout::StdLayout
385        ) -> ::core::result::Result<NonNull<[u8]>, AllocError> {
386            Allocator::grow_zeroed(&Global, ptr.cast(), old_layout, new)
387        }
388
389        #[cfg_attr(miri, track_caller)]
390        #[inline]
391        unsafe fn shrink(
392            &self,
393            ptr: NonNull<u8>,
394            old_layout: crate::layout::StdLayout,
395            new: crate::layout::StdLayout
396        ) -> ::core::result::Result<NonNull<[u8]>, AllocError> {
397            Allocator::shrink(&Global, ptr.cast(), old_layout, new)
398        }
399    }
400
401    #[cfg(any(not(feature = "no_alloc"), feature = "std"))]
402    default_alloc_impl!(Global);
403
404    // TODO: either Allocator for A: Alloc or vice versa, not sure which. i think i removed that at
405    //  some point but i can't remember why.
406}