Skip to main content

static_alloc/unsync/
chain.rs

1//! This module defines a simple bump allocator.
2//! The allocator is not thread safe.
3use core::{
4    alloc::{Layout, LayoutError},
5    cell::Cell,
6    mem::MaybeUninit,
7    ptr::{self, NonNull},
8};
9
10use alloc::{alloc::alloc_zeroed, boxed::Box};
11
12use crate::bump::Failure;
13use crate::leaked::LeakBox;
14use crate::unsync::bump::BumpSlice;
15
16/// An error representing an error while construction
17/// a [`Chain`].
18#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
19pub struct TryNewError {
20    inner: RawAllocError,
21}
22
23impl TryNewError {
24    /// Returns the allocation size of a `Chain`
25    /// that couldn't be allocated.
26    pub const fn allocation_size(&self) -> usize {
27        self.inner.allocation_size()
28    }
29}
30
31type LinkPtr = Option<NonNull<Link>>;
32
33struct Link {
34    /// A pointer to the next node within the list.
35    /// This is wrapped in a Cell, so we can modify
36    /// this field with just an &self reference.
37    next: Cell<LinkPtr>,
38    /// The bump allocator of this link.
39    bump: BumpSlice,
40}
41
42/// A `Chain` is a simple bump allocator, that draws
43/// it's memory from another allocator. Chain allocators
44/// can be chained together using [`Chain::chain`].
45pub struct Chain {
46    /// The root. Critically, we must not deallocate before all borrows on self have ended, in
47    /// other words until its destructor.
48    root: Cell<LinkPtr>,
49}
50
51impl Chain {
52    /// Creates a new `Chain` that has a capacity of `size`
53    /// bytes.
54    pub fn new(size: usize) -> Result<Self, TryNewError> {
55        let link = Link::alloc(size).map_err(|e| TryNewError { inner: e })?;
56        Ok(Chain {
57            root: Cell::new(Some(link)),
58        })
59    }
60
61    /// Attempts to allocate `elem` within the allocator.
62    pub fn bump_box<'bump, T: 'bump>(
63        &'bump self,
64    ) -> Result<LeakBox<'bump, MaybeUninit<T>>, Failure> {
65        let root = self.root().ok_or(Failure::Exhausted)?;
66        root.as_bump().bump_box()
67    }
68
69    /// Chains `self` together with `new`.
70    ///
71    /// Following allocations will first be allocated from `new`.
72    ///
73    /// Note that this will drop all but the first link from `new`.
74    pub fn chain(&self, new: Chain) {
75        // We can't drop our own, but we can drop the tail of `new`.
76        let self_bump = self.root.take();
77
78        match new.root() {
79            None => self.root.set(self_bump),
80            Some(root) => {
81                unsafe { root.set_next(self_bump) };
82                self.root.set(new.root.take())
83            }
84        }
85    }
86
87    /// Returns the capacity of this `Chain`.
88    /// This is how many *bytes* in total can
89    /// be allocated within this `Chain`.
90    pub fn capacity(&self) -> usize {
91        match self.root() {
92            None => 0,
93            Some(root) => root.as_bump().capacity(),
94        }
95    }
96
97    /// Returns the remaining capacity of this `Chain`.
98    /// This is how many more *bytes* can be allocated
99    /// within this `Chain`.
100    pub fn remaining_capacity(&self) -> usize {
101        match self.root() {
102            None => 0,
103            Some(root) => self.capacity() - root.as_bump().level().0,
104        }
105    }
106
107    fn root(&self) -> Option<&Link> {
108        unsafe {
109            let bump_ptr = self.root.get()?.as_ptr();
110            Some(&*bump_ptr)
111        }
112    }
113}
114
115/// A type representing a failure while allocating
116/// a `BumpSlice`.
117#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
118pub(crate) struct RawAllocError {
119    allocation_size: usize,
120    kind: RawAllocFailure,
121}
122
123#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
124enum RawAllocFailure {
125    Exhausted,
126    Layout,
127}
128
129impl Link {
130    /// Override the next pointer.
131    ///
132    /// ## Safety
133    /// It must point to a valid link. Furthermore, the old link is dropped!
134    pub(crate) unsafe fn set_next(&self, next: LinkPtr) {
135        if let Some(next) = self.next.replace(next) {
136            // Safety: any value we stored into `next` is from `Box::leak`
137            let _ = unsafe { Box::from_raw(next.as_ptr()) };
138        }
139    }
140
141    /// Take over the control over the tail.
142    pub(crate) fn take_next(&self) -> Option<Box<Link>> {
143        let ptr = self.next.take()?;
144        // Safety: any value we stored into `next` is from `Box::leak`
145        Some(unsafe { Box::from_raw(ptr.as_ptr()) })
146    }
147
148    pub(crate) fn as_bump(&self) -> &BumpSlice {
149        &self.bump
150    }
151
152    pub(crate) fn layout_from_size(size: usize) -> Result<Layout, LayoutError> {
153        Layout::new::<Cell<LinkPtr>>()
154            .extend(BumpSlice::layout_from_size(size)?)
155            .map(|layout| layout.0)
156    }
157
158    unsafe fn alloc_raw(layout: Layout) -> Result<NonNull<u8>, RawAllocError> {
159        let ptr = alloc_zeroed(layout);
160        NonNull::new(ptr)
161            .ok_or_else(|| RawAllocError::new(layout.size(), RawAllocFailure::Exhausted))
162    }
163
164    /// Allocates a BumpSlice and returns it.
165    pub(crate) fn alloc(capacity: usize) -> Result<NonNull<Self>, RawAllocError> {
166        let layout = Self::layout_from_size(capacity)
167            .map_err(|_| RawAllocError::new(capacity, RawAllocFailure::Layout))?;
168
169        unsafe {
170            let raw = Link::alloc_raw(layout)?;
171            let raw_mut: *mut [Cell<MaybeUninit<u8>>] =
172                ptr::slice_from_raw_parts_mut(raw.cast().as_ptr(), capacity);
173            Ok(NonNull::new_unchecked(raw_mut as *mut Link))
174        }
175    }
176}
177
178impl RawAllocError {
179    const fn new(allocation_size: usize, kind: RawAllocFailure) -> Self {
180        Self {
181            allocation_size,
182            kind,
183        }
184    }
185
186    pub(crate) const fn allocation_size(&self) -> usize {
187        self.allocation_size
188    }
189}
190
191/// Chain drops iteratively, so that we do not stack overflow.
192impl Drop for Chain {
193    fn drop(&mut self) {
194        let mut current = self.root.take();
195        while let Some(non_null) = current {
196            // Drop as a box.
197            let link = unsafe { Box::from_raw(non_null.as_ptr()) };
198            current = link.next.take();
199        }
200    }
201}
202
203impl Drop for Link {
204    fn drop(&mut self) {
205        let mut current = self.take_next();
206        while let Some(link) = current {
207            current = link.take_next();
208        }
209    }
210}