Skip to main content

stabby_abi/alloc/
mod.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   Pierre Avital, <pierre.avital@me.com>
13//
14
15#![allow(deprecated)]
16use core::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull, sync::atomic::AtomicUsize};
17
18use self::vec::ptr_diff;
19
20/// Allocators provided by `stabby`
21pub mod allocators;
22
23/// A generic allocation error.
24#[crate::stabby]
25#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
26pub struct AllocationError();
27impl core::fmt::Display for AllocationError {
28    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
29        f.write_str("AllocationError")
30    }
31}
32#[cfg(feature = "std")]
33impl std::error::Error for AllocationError {}
34
35/// [`alloc::boxed`](https://doc.rust-lang.org/stable/alloc/boxed/), but ABI-stable.
36pub mod boxed;
37/// Allocated collections, including immutable ones.
38pub mod collections;
39/// A vector that stores a single element on the stack until allocation is necessary.
40pub mod single_or_vec;
41/// [`alloc::string`](https://doc.rust-lang.org/stable/alloc/string/), but ABI-stable
42pub mod string;
43/// [`alloc::sync`](https://doc.rust-lang.org/stable/alloc/sync/), but ABI-stable
44pub mod sync;
45/// [`alloc::vec`](https://doc.rust-lang.org/stable/alloc/vec/), but ABI-stable
46pub mod vec;
47
48/// The default allocator, depending on which of the following is available:
49/// - RustAlloc: Rust's `GlobalAlloc`, through a vtable that ensures FFI-safety.
50/// - LibcAlloc: libc::malloc, which is 0-sized.
51/// - None. I _am_ working on getting a 0-dependy allocator working, but you should probably go with `feature = "alloc-rs"` anyway.
52///
53/// You can also use the `stabby_default_alloc` cfg to override the default allocator regardless of feature flags.
54pub type DefaultAllocator = allocators::DefaultAllocator;
55
56#[crate::stabby]
57#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
58/// ABI-stable equivalent of std::mem::Layout
59pub struct Layout {
60    /// The expected size of the allocation.
61    pub size: usize,
62    /// The expected alignment of the allocation.
63    pub align: usize,
64}
65impl Layout {
66    /// Returns the [`Layout`] corresponding to `T`
67    pub const fn of<T: Sized>() -> Self {
68        Layout {
69            size: core::mem::size_of::<T>(),
70            align: core::mem::align_of::<T>(),
71        }
72    }
73    /// Returns the [`Layout`] corresponding to `[T; n]`.
74    ///
75    /// Note that while this ensures that even if `T`'s size is not a multiple of its alignment,
76    /// the layout will have sufficient memory to store `n` of `T` in an aligned fashion.
77    ///
78    /// # Panics
79    /// If the resulting size exceeds the capacity of `usize` (if `debug_assertions` are enabled)
80    #[allow(clippy::arithmetic_side_effects)]
81    pub const fn array<T: Sized>(n: usize) -> Self {
82        let Self { size, align } = Self::of::<T>();
83        Layout {
84            size: size * n,
85            align,
86        }
87    }
88    /// Concatenates a layout to `self`, ensuring that alignment padding is taken into account.
89    ///
90    /// # Panics
91    /// If the resulting size exceeds the capacity of `usize` (if `debug_assertions` are enabled)
92    #[allow(clippy::arithmetic_side_effects)]
93    pub const fn concat(mut self, other: Self) -> Self {
94        self.size += other.size;
95        self.realign(if self.align < other.align {
96            other.align
97        } else {
98            self.align
99        })
100    }
101    /// Returns the first pointer where `output >= ptr` such that `output % self.align == 0`.
102    #[inline]
103    pub fn next_matching<T>(self, ptr: *mut T) -> *mut T {
104        fn next_matching(align: usize, ptr: *mut u8) -> *mut u8 {
105            unsafe { ptr.add(ptr.align_offset(align)) }
106        }
107        next_matching(self.align, ptr.cast()).cast()
108    }
109    /// Changes the alignment of the layout, adding padding if necessary.
110    ///
111    /// # Panics
112    /// If the resulting size exceeds the capacity of `usize` (if `debug_assertions` are enabled)
113    #[allow(clippy::arithmetic_side_effects)]
114    pub const fn realign(mut self, new_align: usize) -> Self {
115        self.align = new_align;
116        self.size = self.size
117            + (new_align - (self.size % new_align)) * (((self.size % new_align) != 0) as usize);
118        self
119    }
120}
121
122/// An interface to an allocator.
123///
124/// Note that `stabby` often stores allocators inside allocations they made, so allocators that cannot allocate
125/// more than their size on stack will systematically fail to construct common stabby types.
126///
127/// Since the allocator may be moved, it must also be safe to do so, including after it has performed allocations.
128pub trait IAlloc: Unpin {
129    /// Allocates at least as much memory as requested by layout, ensuring the requested alignment is respected.
130    ///
131    /// If the requested size is 0, or allocation failed, then a null pointer is returned.
132    fn alloc(&mut self, layout: Layout) -> *mut ();
133    /// Frees the allocation
134    ///
135    /// # Safety
136    /// `ptr` MUST have been allocated through a succesful call to `Self::alloc` or `Self::realloc` with the same instance of `Self`
137    unsafe fn free(&mut self, ptr: *mut ());
138    /// Reallocates `ptr`, ensuring that it has enough memory for the newly requested layout.
139    ///
140    /// If the requested size is 0, or allocation failed, then a null pointer is returned, and `ptr` is not freed.
141    ///
142    /// # Safety
143    /// `ptr` MUST have been allocated through a succesful call to `Self::alloc` with the same instance of `Self`
144    unsafe fn realloc(&mut self, ptr: *mut (), prev_layout: Layout, new_size: usize) -> *mut () {
145        let ret = self.alloc(Layout {
146            size: new_size,
147            align: prev_layout.align,
148        });
149        if !ret.is_null() {
150            unsafe {
151                core::ptr::copy_nonoverlapping(ptr.cast::<u8>(), ret.cast(), prev_layout.size);
152                self.free(ptr);
153            }
154        }
155        ret
156    }
157}
158
159/// An ABI stable equivalent to [`IAlloc`].
160#[crate::stabby]
161#[deprecated = "Stabby doesn't actually use this trait due to conflicts."]
162pub trait IStableAlloc: Unpin {
163    /// Allocates at least as much memory as requested by layout, ensuring the requested alignment is respected.
164    ///
165    /// If the requested size is 0, or allocation failed, then a null pointer is returned.
166    extern "C" fn alloc(&mut self, layout: Layout) -> *mut ();
167    /// Frees the allocation
168    ///
169    /// # Safety
170    /// `ptr` MUST have been allocated through a succesful call to `Self::alloc` or `Self::realloc` with the same instance of `Self`
171    extern "C" fn free(&mut self, ptr: *mut ());
172    /// Reallocates `ptr`, ensuring that it has enough memory for the newly requested layout.
173    ///
174    /// If the requested size is 0, or allocation failed, then a null pointer is returned, and `ptr` is not freed.
175    ///
176    /// # Safety
177    /// `ptr` MUST have been allocated through a succesful call to `Self::alloc` with the same instance of `Self`
178    extern "C" fn realloc(
179        &mut self,
180        ptr: *mut (),
181        prev_layout: Layout,
182        new_size: usize,
183    ) -> *mut () {
184        let ret = self.alloc(Layout {
185            size: new_size,
186            align: prev_layout.align,
187        });
188        if !ret.is_null() {
189            unsafe {
190                core::ptr::copy_nonoverlapping(ptr.cast::<u8>(), ret.cast(), prev_layout.size);
191                self.free(ptr);
192            }
193        }
194        ret
195    }
196}
197#[allow(clippy::not_unsafe_ptr_arg_deref)]
198impl<T: IAlloc> IStableAlloc for T {
199    extern "C" fn alloc(&mut self, layout: Layout) -> *mut () {
200        IAlloc::alloc(self, layout)
201    }
202    extern "C" fn free(&mut self, ptr: *mut ()) {
203        unsafe { IAlloc::free(self, ptr) }
204    }
205    extern "C" fn realloc(
206        &mut self,
207        ptr: *mut (),
208        prev_layout: Layout,
209        new_size: usize,
210    ) -> *mut () {
211        unsafe { IAlloc::realloc(self, ptr, prev_layout, new_size) }
212    }
213}
214
215impl<T: IStableAllocDynMut<crate::vtable::H> + Unpin> IAlloc for T {
216    fn alloc(&mut self, layout: Layout) -> *mut () {
217        IStableAllocDynMut::alloc(self, layout)
218    }
219    unsafe fn free(&mut self, ptr: *mut ()) {
220        IStableAllocDynMut::free(self, ptr)
221    }
222    unsafe fn realloc(&mut self, ptr: *mut (), prev_layout: Layout, new_size: usize) -> *mut () {
223        IStableAllocDynMut::realloc(self, ptr, prev_layout, new_size)
224    }
225}
226impl IAlloc for core::convert::Infallible {
227    fn alloc(&mut self, _layout: Layout) -> *mut () {
228        unreachable!()
229    }
230    unsafe fn free(&mut self, _ptr: *mut ()) {
231        unreachable!()
232    }
233}
234
235/// The prefix common to all allocations in [`stabby::alloc`](crate::alloc).
236///
237/// This allows reuse of allocations when converting between container types.
238#[crate::stabby]
239pub struct AllocPrefix<Alloc> {
240    /// The strong count for reference counted types.
241    pub strong: core::sync::atomic::AtomicUsize,
242    /// The weak count for reference counted types.
243    pub weak: core::sync::atomic::AtomicUsize,
244    /// A slot to store a vector's capacity when it's turned into a boxed/arced slice.
245    pub capacity: core::sync::atomic::AtomicUsize,
246    /// The origin of the prefix
247    pub origin: NonNull<()>,
248    /// A slot for the allocator.
249    pub alloc: core::mem::MaybeUninit<Alloc>,
250}
251impl<Alloc> AllocPrefix<Alloc> {
252    /// The offset between the prefix and a field of type `T`.
253    pub const fn skip_to<T>() -> usize {
254        let mut size = core::mem::size_of::<Self>();
255        let mut align = core::mem::align_of::<T>();
256        if align == 0 {
257            align = 1
258        }
259        let sizemodalign = size.rem_euclid(align);
260        if sizemodalign != 0 {
261            size = size.wrapping_add(align).wrapping_sub(sizemodalign);
262        }
263        size
264    }
265}
266
267/// A non-null pointer guaranteed to be preceded by a valid
268/// [`AllocPrefix`] unless the pointer is dangling.
269///
270/// This means that unless `T` is a ZST, the pointer is guaranteed to be aligned to the maximum of `T`'s alignment and the alignment of the prefix, which itself is ptr-size aligned.
271#[crate::stabby]
272#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
273pub struct AllocPtr<T, Alloc> {
274    /// The pointer to the data.
275    pub ptr: NonNull<T>,
276    /// Remember the allocator's type.
277    pub marker: PhantomData<Alloc>,
278}
279impl<T, Alloc> Copy for AllocPtr<T, Alloc> {}
280impl<T, Alloc> Clone for AllocPtr<T, Alloc> {
281    fn clone(&self) -> Self {
282        *self
283    }
284}
285impl<T, Alloc> core::ops::Deref for AllocPtr<T, Alloc> {
286    type Target = NonNull<T>;
287    fn deref(&self) -> &Self::Target {
288        &self.ptr
289    }
290}
291impl<T, Alloc> core::ops::DerefMut for AllocPtr<T, Alloc> {
292    fn deref_mut(&mut self) -> &mut Self::Target {
293        &mut self.ptr
294    }
295}
296impl<T, Alloc> AllocPtr<MaybeUninit<T>, Alloc> {
297    /// Assumes the internals of the pointer have been initialized.
298    /// # Safety
299    /// The internals of the pointer must have been initialized.
300    pub const unsafe fn assume_init(self) -> AllocPtr<T, Alloc> {
301        unsafe { core::mem::transmute::<Self, AllocPtr<T, Alloc>>(self) }
302    }
303}
304impl<T, Alloc> AllocPtr<T, Alloc> {
305    /// Constructs a dangling pointer.
306    pub const fn dangling() -> Self {
307        Self {
308            ptr: NonNull::dangling(),
309            marker: PhantomData,
310        }
311    }
312    /// Casts an allocated pointer.
313    pub const fn cast<U>(self) -> AllocPtr<U, Alloc> {
314        AllocPtr {
315            ptr: self.ptr.cast(),
316            marker: PhantomData,
317        }
318    }
319    ///The pointer to the prefix for this allocation
320    const fn prefix_ptr(&self) -> NonNull<AllocPrefix<Alloc>> {
321        unsafe { NonNull::new_unchecked(self.ptr.as_ptr().cast::<AllocPrefix<Alloc>>().sub(1)) }
322    }
323    /// A reference to the prefix for this allocation.
324    /// # Safety
325    /// `self` must not be dangling, and have been properly allocated, using [`Self::alloc`] or [`Self::realloc`] for example.
326    #[rustversion::attr(since(1.73), const)]
327    pub unsafe fn prefix(&self) -> &AllocPrefix<Alloc> {
328        unsafe { self.prefix_ptr().as_ref() }
329    }
330    /// A mutable reference to the prefix for this allocation.
331    /// # Safety
332    /// `self` must not be dangling, and have been properly allocated, using [`Self::alloc`] or [`Self::realloc`] for example.
333    /// Since this type is [`Copy`], the `&mut self` is not a sufficient guarantee of uniqueness.
334    #[rustversion::attr(since(1.86), const)]
335    pub unsafe fn prefix_mut(&mut self) -> &mut AllocPrefix<Alloc> {
336        unsafe { self.prefix_ptr().as_mut() }
337    }
338    /// Returns mutable access to the prefix and the data.
339    /// # Safety
340    /// `self` must not be dangling, and have been properly allocated, using [`Self::alloc`] or [`Self::realloc`] for example.
341    #[rustversion::attr(since(1.86), const)]
342    pub unsafe fn split_mut(&mut self) -> (&mut AllocPrefix<Alloc>, &mut T) {
343        let prefix = self.prefix_ptr().as_mut();
344        let data = self.ptr.as_mut();
345        (prefix, data)
346    }
347    /// Initializes any given pointer:
348    /// - The returned pointer is guaranteed to be correctly aligned for `T`
349    /// - It is guaranteed to preceded without padding by an `AllocPrefix<Alloc>`
350    /// # Safety
351    /// `ptr` MUST be word-aligned, and MUST be valid for writes for at least the size of
352    /// `#[repr(C)] struct { prefix: AllocPrefix<Alloc>, data: [T; capacity] }`
353    pub unsafe fn init(ptr: NonNull<()>, capacity: usize) -> Self {
354        let shifted_for_prefix = ptr
355            .as_ptr()
356            .cast::<AllocPrefix<Alloc>>()
357            .add(1)
358            .cast::<u8>();
359        let inited = shifted_for_prefix
360            .cast::<u8>()
361            .add(shifted_for_prefix.align_offset(core::mem::align_of::<T>()))
362            .cast::<T>();
363        let this: Self = AllocPtr {
364            ptr: NonNull::new_unchecked(inited),
365            marker: core::marker::PhantomData,
366        };
367        this.prefix_ptr().as_ptr().write(AllocPrefix {
368            strong: AtomicUsize::new(1),
369            weak: AtomicUsize::new(1),
370            capacity: AtomicUsize::new(capacity),
371            origin: ptr,
372            alloc: core::mem::MaybeUninit::uninit(),
373        });
374        this
375    }
376}
377impl<T, Alloc: IAlloc> AllocPtr<T, Alloc> {
378    /// Allocates a pointer to a single element of `T`, prefixed by an [`AllocPrefix`]
379    pub fn alloc(alloc: &mut Alloc) -> Option<Self> {
380        Self::alloc_array(alloc, 1)
381    }
382    /// Allocates a pointer to an array of `capacity` `T`, prefixed by an [`AllocPrefix`]
383    pub fn alloc_array(alloc: &mut Alloc, capacity: usize) -> Option<Self> {
384        let mut layout = Layout::of::<AllocPrefix<Alloc>>().concat(Layout::array::<T>(capacity));
385        layout.align = core::mem::align_of::<AllocPrefix<Alloc>>();
386        let ptr = alloc.alloc(layout);
387        NonNull::new(ptr).map(|ptr| unsafe { Self::init(ptr, capacity) })
388    }
389    /// Reallocates a pointer to an array of `capacity` `T`, prefixed by an [`AllocPrefix`].
390    ///
391    /// In case of failure of the allocator, this will return `None` and `self` will not have been freed.
392    ///
393    /// # Safety
394    /// `self` must not be dangling
395    pub unsafe fn realloc(
396        self,
397        alloc: &mut Alloc,
398        prev_capacity: usize,
399        new_capacity: usize,
400    ) -> Option<Self> {
401        let mut layout =
402            Layout::of::<AllocPrefix<Alloc>>().concat(Layout::array::<T>(prev_capacity));
403        layout.align = core::mem::align_of::<AllocPrefix<Alloc>>();
404        let ptr = alloc.realloc(
405            self.prefix_ptr().cast().as_ptr(),
406            layout,
407            Layout::of::<AllocPrefix<Alloc>>()
408                .concat(Layout::array::<T>(new_capacity))
409                .size,
410        );
411        NonNull::new(ptr).map(|ptr| unsafe { Self::init(ptr, new_capacity) })
412    }
413    /// Reallocates a pointer to an array of `capacity` `T`, prefixed by an [`AllocPrefix`]
414    /// # Safety
415    /// `self` must not be dangling, and is freed after this returns.
416    pub unsafe fn free(self, alloc: &mut Alloc) {
417        alloc.free(self.prefix().origin.as_ptr())
418    }
419}
420
421/// A helper to work with allocated slices.
422#[crate::stabby]
423#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
424pub struct AllocSlice<T, Alloc> {
425    /// The start of the slice.
426    pub start: AllocPtr<T, Alloc>,
427    /// The end of the slice (exclusive).
428    pub end: NonNull<T>,
429}
430impl<T, Alloc> AllocSlice<T, Alloc> {
431    /// Returns the number of elements in the slice.
432    pub const fn len(&self) -> usize {
433        ptr_diff(self.end, self.start.ptr)
434    }
435    /// Returns `true` if the slice is empty.
436    pub const fn is_empty(&self) -> bool {
437        self.len() == 0
438    }
439    /// Returns this slice.
440    /// # Safety
441    /// `self` must be valid.
442    pub const unsafe fn as_slice(&self) -> &[T] {
443        core::slice::from_raw_parts(self.start.ptr.as_ptr(), ptr_diff(self.end, self.start.ptr))
444    }
445}
446impl<T, Alloc> Copy for AllocSlice<T, Alloc> {}
447impl<T, Alloc> Clone for AllocSlice<T, Alloc> {
448    fn clone(&self) -> Self {
449        *self
450    }
451}