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    pub const fn array<T: Sized>(n: usize) -> Self {
78        let Self { size, align } = Self::of::<T>();
79        Layout {
80            size: size * n,
81            align,
82        }
83    }
84    /// Concatenates a layout to `self`, ensuring that alignment padding is taken into account.
85    pub const fn concat(mut self, other: Self) -> Self {
86        self.size += other.size;
87        self.realign(if self.align < other.align {
88            other.align
89        } else {
90            self.align
91        })
92    }
93    /// Returns the first pointer where `output >= ptr` such that `output % self.align == 0`.
94    #[inline]
95    pub fn next_matching<T>(self, ptr: *mut T) -> *mut T {
96        fn next_matching(align: usize, ptr: *mut u8) -> *mut u8 {
97            unsafe { ptr.add(ptr.align_offset(align)) }
98        }
99        next_matching(self.align, ptr.cast()).cast()
100    }
101    /// Changes the alignment of the layout, adding padding if necessary.
102    pub const fn realign(mut self, new_align: usize) -> Self {
103        self.align = new_align;
104        self.size = self.size
105            + (new_align - (self.size % new_align)) * (((self.size % new_align) != 0) as usize);
106        self
107    }
108}
109
110/// An interface to an allocator.
111///
112/// Note that `stabby` often stores allocators inside allocations they made, so allocators that cannot allocate
113/// more than their size on stack will systematically fail to construct common stabby types.
114///
115/// Since the allocator may be moved, it must also be safe to do so, including after it has performed allocations.
116pub trait IAlloc: Unpin {
117    /// Allocates at least as much memory as requested by layout, ensuring the requested alignment is respected.
118    ///
119    /// If the requested size is 0, or allocation failed, then a null pointer is returned.
120    fn alloc(&mut self, layout: Layout) -> *mut ();
121    /// Frees the allocation
122    ///
123    /// # Safety
124    /// `ptr` MUST have been allocated through a succesful call to `Self::alloc` or `Self::realloc` with the same instance of `Self`
125    unsafe fn free(&mut self, ptr: *mut ());
126    /// Reallocates `ptr`, ensuring that it has enough memory for the newly requested layout.
127    ///
128    /// If the requested size is 0, or allocation failed, then a null pointer is returned, and `ptr` is not freed.
129    ///
130    /// # Safety
131    /// `ptr` MUST have been allocated through a succesful call to `Self::alloc` with the same instance of `Self`
132    unsafe fn realloc(&mut self, ptr: *mut (), prev_layout: Layout, new_size: usize) -> *mut () {
133        let ret = self.alloc(Layout {
134            size: new_size,
135            align: prev_layout.align,
136        });
137        if !ret.is_null() {
138            unsafe {
139                core::ptr::copy_nonoverlapping(ptr.cast::<u8>(), ret.cast(), prev_layout.size);
140                self.free(ptr);
141            }
142        }
143        ret
144    }
145}
146
147/// An ABI stable equivalent to [`IAlloc`].
148#[crate::stabby]
149#[deprecated = "Stabby doesn't actually use this trait due to conflicts."]
150pub trait IStableAlloc: Unpin {
151    /// Allocates at least as much memory as requested by layout, ensuring the requested alignment is respected.
152    ///
153    /// If the requested size is 0, or allocation failed, then a null pointer is returned.
154    extern "C" fn alloc(&mut self, layout: Layout) -> *mut ();
155    /// Frees the allocation
156    ///
157    /// # Safety
158    /// `ptr` MUST have been allocated through a succesful call to `Self::alloc` or `Self::realloc` with the same instance of `Self`
159    extern "C" fn free(&mut self, ptr: *mut ());
160    /// Reallocates `ptr`, ensuring that it has enough memory for the newly requested layout.
161    ///
162    /// If the requested size is 0, or allocation failed, then a null pointer is returned, and `ptr` is not freed.
163    ///
164    /// # Safety
165    /// `ptr` MUST have been allocated through a succesful call to `Self::alloc` with the same instance of `Self`
166    extern "C" fn realloc(
167        &mut self,
168        ptr: *mut (),
169        prev_layout: Layout,
170        new_size: usize,
171    ) -> *mut () {
172        let ret = self.alloc(Layout {
173            size: new_size,
174            align: prev_layout.align,
175        });
176        if !ret.is_null() {
177            unsafe {
178                core::ptr::copy_nonoverlapping(ptr.cast::<u8>(), ret.cast(), prev_layout.size);
179                self.free(ptr);
180            }
181        }
182        ret
183    }
184}
185#[allow(clippy::not_unsafe_ptr_arg_deref)]
186impl<T: IAlloc> IStableAlloc for T {
187    extern "C" fn alloc(&mut self, layout: Layout) -> *mut () {
188        IAlloc::alloc(self, layout)
189    }
190    extern "C" fn free(&mut self, ptr: *mut ()) {
191        unsafe { IAlloc::free(self, ptr) }
192    }
193    extern "C" fn realloc(
194        &mut self,
195        ptr: *mut (),
196        prev_layout: Layout,
197        new_size: usize,
198    ) -> *mut () {
199        unsafe { IAlloc::realloc(self, ptr, prev_layout, new_size) }
200    }
201}
202
203impl<T: IStableAllocDynMut<crate::vtable::H> + Unpin> IAlloc for T {
204    fn alloc(&mut self, layout: Layout) -> *mut () {
205        IStableAllocDynMut::alloc(self, layout)
206    }
207    unsafe fn free(&mut self, ptr: *mut ()) {
208        IStableAllocDynMut::free(self, ptr)
209    }
210    unsafe fn realloc(&mut self, ptr: *mut (), prev_layout: Layout, new_size: usize) -> *mut () {
211        IStableAllocDynMut::realloc(self, ptr, prev_layout, new_size)
212    }
213}
214impl IAlloc for core::convert::Infallible {
215    fn alloc(&mut self, _layout: Layout) -> *mut () {
216        unreachable!()
217    }
218    unsafe fn free(&mut self, _ptr: *mut ()) {
219        unreachable!()
220    }
221}
222
223/// The prefix common to all allocations in [`stabby::alloc`](crate::alloc).
224///
225/// This allows reuse of allocations when converting between container types.
226#[crate::stabby]
227pub struct AllocPrefix<Alloc> {
228    /// The strong count for reference counted types.
229    pub strong: core::sync::atomic::AtomicUsize,
230    /// The weak count for reference counted types.
231    pub weak: core::sync::atomic::AtomicUsize,
232    /// A slot to store a vector's capacity when it's turned into a boxed/arced slice.
233    pub capacity: core::sync::atomic::AtomicUsize,
234    /// The origin of the prefix
235    pub origin: NonNull<()>,
236    /// A slot for the allocator.
237    pub alloc: core::mem::MaybeUninit<Alloc>,
238}
239impl<Alloc> AllocPrefix<Alloc> {
240    /// The offset between the prefix and a field of type `T`.
241    pub const fn skip_to<T>() -> usize {
242        let mut size = core::mem::size_of::<Self>();
243        let align = core::mem::align_of::<T>();
244        let sizemodalign = size % align;
245        if sizemodalign != 0 {
246            size += align;
247            size -= sizemodalign;
248        }
249        size
250    }
251}
252
253/// A non-null pointer guaranteed to be preceded by a valid
254/// [`AllocPrefix`] unless the pointer is dangling.
255///
256/// 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.
257#[crate::stabby]
258#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
259pub struct AllocPtr<T, Alloc> {
260    /// The pointer to the data.
261    pub ptr: NonNull<T>,
262    /// Remember the allocator's type.
263    pub marker: PhantomData<Alloc>,
264}
265impl<T, Alloc> Copy for AllocPtr<T, Alloc> {}
266impl<T, Alloc> Clone for AllocPtr<T, Alloc> {
267    fn clone(&self) -> Self {
268        *self
269    }
270}
271impl<T, Alloc> core::ops::Deref for AllocPtr<T, Alloc> {
272    type Target = NonNull<T>;
273    fn deref(&self) -> &Self::Target {
274        &self.ptr
275    }
276}
277impl<T, Alloc> core::ops::DerefMut for AllocPtr<T, Alloc> {
278    fn deref_mut(&mut self) -> &mut Self::Target {
279        &mut self.ptr
280    }
281}
282impl<T, Alloc> AllocPtr<MaybeUninit<T>, Alloc> {
283    /// Assumes the internals of the pointer have been initialized.
284    /// # Safety
285    /// The internals of the pointer must have been initialized.
286    pub const unsafe fn assume_init(self) -> AllocPtr<T, Alloc> {
287        unsafe { core::mem::transmute::<Self, AllocPtr<T, Alloc>>(self) }
288    }
289}
290impl<T, Alloc> AllocPtr<T, Alloc> {
291    /// Constructs a dangling pointer.
292    pub const fn dangling() -> Self {
293        Self {
294            ptr: NonNull::dangling(),
295            marker: PhantomData,
296        }
297    }
298    /// Casts an allocated pointer.
299    pub const fn cast<U>(self) -> AllocPtr<U, Alloc> {
300        AllocPtr {
301            ptr: self.ptr.cast(),
302            marker: PhantomData,
303        }
304    }
305    ///The pointer to the prefix for this allocation
306    const fn prefix_ptr(&self) -> NonNull<AllocPrefix<Alloc>> {
307        unsafe { NonNull::new_unchecked(self.ptr.as_ptr().cast::<AllocPrefix<Alloc>>().sub(1)) }
308    }
309    /// A reference to the prefix for this allocation.
310    /// # Safety
311    /// `self` must not be dangling, and have been properly allocated, using [`Self::alloc`] or [`Self::realloc`] for example.
312    #[rustversion::since(1.73)]
313    pub const unsafe fn prefix(&self) -> &AllocPrefix<Alloc> {
314        unsafe { self.prefix_ptr().as_ref() }
315    }
316    /// A reference to the prefix for this allocation.
317    /// # Safety
318    /// `self` must not be dangling, and have been properly allocated, using [`Self::alloc`] or [`Self::realloc`] for example.
319    #[rustversion::before(1.73)]
320    pub unsafe fn prefix(&self) -> &AllocPrefix<Alloc> {
321        unsafe { self.prefix_ptr().as_ref() }
322    }
323    /// A mutable 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    /// Since this type is [`Copy`], the `&mut self` is not a sufficient guarantee of uniqueness.
327    pub unsafe fn prefix_mut(&mut self) -> &mut AllocPrefix<Alloc> {
328        unsafe { self.prefix_ptr().as_mut() }
329    }
330    /// Initializes any given pointer:
331    /// - The returned pointer is guaranteed to be correctly aligned for `T`
332    /// - It is guaranteed to preceded without padding by an `AllocPrefix<Alloc>`
333    /// # Safety
334    /// `ptr` MUST be word-aligned, and MUST be valid for writes for at least the size of
335    /// `#[repr(C)] struct { prefix: AllocPrefix<Alloc>, data: [T; capacity] }`
336    pub unsafe fn init(ptr: NonNull<()>, capacity: usize) -> Self {
337        let shifted_for_prefix = ptr
338            .as_ptr()
339            .cast::<AllocPrefix<Alloc>>()
340            .add(1)
341            .cast::<u8>();
342        let inited = shifted_for_prefix
343            .cast::<u8>()
344            .add(shifted_for_prefix.align_offset(core::mem::align_of::<T>()))
345            .cast::<T>();
346        let this: Self = AllocPtr {
347            ptr: NonNull::new_unchecked(inited),
348            marker: core::marker::PhantomData,
349        };
350        this.prefix_ptr().as_ptr().write(AllocPrefix {
351            strong: AtomicUsize::new(1),
352            weak: AtomicUsize::new(1),
353            capacity: AtomicUsize::new(capacity),
354            origin: ptr,
355            alloc: core::mem::MaybeUninit::uninit(),
356        });
357        this
358    }
359}
360impl<T, Alloc: IAlloc> AllocPtr<T, Alloc> {
361    /// Allocates a pointer to a single element of `T`, prefixed by an [`AllocPrefix`]
362    pub fn alloc(alloc: &mut Alloc) -> Option<Self> {
363        Self::alloc_array(alloc, 1)
364    }
365    /// Allocates a pointer to an array of `capacity` `T`, prefixed by an [`AllocPrefix`]
366    pub fn alloc_array(alloc: &mut Alloc, capacity: usize) -> Option<Self> {
367        let mut layout = Layout::of::<AllocPrefix<Alloc>>().concat(Layout::array::<T>(capacity));
368        layout.align = core::mem::align_of::<AllocPrefix<Alloc>>();
369        let ptr = alloc.alloc(layout);
370        NonNull::new(ptr).map(|ptr| unsafe { Self::init(ptr, capacity) })
371    }
372    /// Reallocates a pointer to an array of `capacity` `T`, prefixed by an [`AllocPrefix`].
373    ///
374    /// In case of failure of the allocator, this will return `None` and `self` will not have been freed.
375    ///
376    /// # Safety
377    /// `self` must not be dangling
378    pub unsafe fn realloc(
379        self,
380        alloc: &mut Alloc,
381        prev_capacity: usize,
382        new_capacity: usize,
383    ) -> Option<Self> {
384        let mut layout =
385            Layout::of::<AllocPrefix<Alloc>>().concat(Layout::array::<T>(prev_capacity));
386        layout.align = core::mem::align_of::<AllocPrefix<Alloc>>();
387        let ptr = alloc.realloc(
388            (self.prefix() as *const AllocPrefix<Alloc>)
389                .cast_mut()
390                .cast(),
391            layout,
392            Layout::of::<AllocPrefix<Alloc>>()
393                .concat(Layout::array::<T>(new_capacity))
394                .size,
395        );
396        NonNull::new(ptr).map(|ptr| unsafe { Self::init(ptr, new_capacity) })
397    }
398    /// Reallocates a pointer to an array of `capacity` `T`, prefixed by an [`AllocPrefix`]
399    /// # Safety
400    /// `self` must not be dangling, and is freed after this returns.
401    pub unsafe fn free(self, alloc: &mut Alloc) {
402        alloc.free(self.prefix().origin.as_ptr())
403    }
404}
405
406/// A helper to work with allocated slices.
407#[crate::stabby]
408#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
409pub struct AllocSlice<T, Alloc> {
410    /// The start of the slice.
411    pub start: AllocPtr<T, Alloc>,
412    /// The end of the slice (exclusive).
413    pub end: NonNull<T>,
414}
415impl<T, Alloc> AllocSlice<T, Alloc> {
416    /// Returns the number of elements in the slice.
417    pub const fn len(&self) -> usize {
418        ptr_diff(self.end, self.start.ptr)
419    }
420    /// Returns `true` if the slice is empty.
421    pub const fn is_empty(&self) -> bool {
422        self.len() == 0
423    }
424    /// Returns this slice.
425    /// # Safety
426    /// `self` must be valid.
427    pub const unsafe fn as_slice(&self) -> &[T] {
428        core::slice::from_raw_parts(self.start.ptr.as_ptr(), ptr_diff(self.end, self.start.ptr))
429    }
430}
431impl<T, Alloc> Copy for AllocSlice<T, Alloc> {}
432impl<T, Alloc> Clone for AllocSlice<T, Alloc> {
433    fn clone(&self) -> Self {
434        *self
435    }
436}