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}