one_alloc/
lib.rs

1//! A custom allocator that allows a singular allocation of a specific size
2//! known ahead of time.
3//!
4//! # Getting Started
5//! This example allocates once by creating an `Arc` of the unit tuple `()`.
6//! Requires libc for printing, but can be replaced with a serial port
7//! implementation.
8//!
9//! ```rust
10#![doc = include_str!("../examples/main.rs")]
11//! ```
12//! 
13//! Run with `cargo +nightly run --example main` from within the repo.
14
15#![no_std]
16
17use core::{
18    alloc::{GlobalAlloc, Layout},
19    cell::UnsafeCell,
20    ptr::null_mut,
21    sync::atomic::{AtomicBool, Ordering},
22};
23
24const MAX_SUPPORTED_ALIGN: usize = 16;
25/// A fixed-size single allocation allocator.
26#[repr(C, align(16))]
27pub struct Allocator<const SIZE: usize>(UnsafeCell<[u8; SIZE]>);
28
29impl<const SIZE: usize> Allocator<SIZE> {
30    /// Create a new one-time allocator
31    #[inline]
32    pub const fn new() -> Self {
33        Self(UnsafeCell::new([0; SIZE]))
34    }
35}
36
37static FULL: AtomicBool = AtomicBool::new(false);
38
39unsafe impl<const SIZE: usize> Sync for Allocator<SIZE> {}
40
41unsafe impl<const SIZE: usize> GlobalAlloc for Allocator<SIZE> {
42    // Provide pointer so long as expected size, alignment, and called once.
43    #[inline]
44    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
45        if layout.size() != SIZE
46            || layout.align() > MAX_SUPPORTED_ALIGN
47            || FULL.fetch_or(true, Ordering::SeqCst)
48        {
49            return null_mut();
50        }
51
52        UnsafeCell::raw_get(&self.0).cast()
53    }
54
55    // Don't do anything on deallocation.
56    #[inline]
57    unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
58}