memapi2/c_alloc/
mod.rs

1use {
2    crate::{
3        Alloc,
4        Dealloc,
5        Grow,
6        Layout,
7        Realloc,
8        Shrink,
9        error::Error,
10        helpers::{null_q_dyn, null_q_dyn_zsl_check}
11    },
12    core::{cmp::Ordering, ffi::c_void, ptr::NonNull},
13    ffi::{aligned_zalloc, c_alloc, c_dealloc, grow_aligned, shrink_aligned}
14};
15
16#[cfg_attr(miri, track_caller)]
17fn pad_then_alloc(
18    layout: Layout,
19    alloc: unsafe fn(usize, usize) -> *mut c_void
20) -> Result<NonNull<u8>, Error> {
21    null_q_dyn_zsl_check(
22        tri!(do layout.to_aligned_alloc_compatible()),
23        // SAFETY: we rounded up the layout's values to satisfy the requirements.
24        |l| unsafe { alloc(l.align(), l.size()) }
25    )
26}
27
28#[cfg_attr(miri, track_caller)]
29unsafe fn pad_then_grow(
30    ptr: NonNull<u8>,
31    old_layout: Layout,
32    new_layout: Layout,
33    alloc: unsafe fn(usize, usize) -> *mut c_void
34) -> Result<NonNull<u8>, Error> {
35    let old_padded = tri!(do old_layout.to_aligned_alloc_compatible());
36    let new_padded = tri!(do new_layout.to_aligned_alloc_compatible());
37
38    if old_padded.size() > new_padded.size() {
39        return Err(Error::GrowSmallerNewLayout(old_layout.size(), new_layout.size()));
40    }
41
42    null_q_dyn_zsl_check(new_padded, |l| {
43        grow_aligned(ptr.as_ptr().cast(), old_padded.size(), l.align(), l.size(), alloc)
44    })
45}
46
47#[cfg_attr(miri, track_caller)]
48unsafe fn pad_then_realloc(
49    ptr: NonNull<u8>,
50    old_layout: Layout,
51    new_layout: Layout,
52    alloc: unsafe fn(usize, usize) -> *mut c_void
53) -> Result<NonNull<u8>, Error> {
54    let old_padded = tri!(do old_layout.to_aligned_alloc_compatible());
55    let new_padded = tri!(do new_layout.to_aligned_alloc_compatible());
56
57    null_q_dyn_zsl_check(new_padded, |l| {
58        let old_ptr = ptr.as_ptr().cast();
59        let old_size = old_padded.size();
60        let old_align = old_padded.align();
61
62        let size = l.size();
63        let align = l.align();
64
65        match old_size.cmp(&new_padded.size()) {
66            // SAFETY: caller guarantees that `old_ptr` and `old_size` are valid, we just
67            // checked that `size >= old_size`
68            Ordering::Less => unsafe { grow_aligned(old_ptr, old_size, align, size, alloc) },
69            Ordering::Equal => {
70                if align > old_align {
71                    // SAFETY: above
72                    unsafe { grow_aligned(old_ptr, old_size, align, size, alloc) }
73                } else {
74                    old_ptr
75                }
76            }
77            // SAFETY: caller guarantees that `old_ptr` and `size` are valid, we just checked
78            // that `size <= old_size`
79            Ordering::Greater => unsafe { shrink_aligned(old_ptr, align, size) }
80        }
81    })
82}
83
84/// An allocator which uses C's [`c_alloc`] set of allocation methods.
85///
86/// Note that layouts passed to this allocator's allocation methods will have their size and
87/// alignment rounded up to meet C's [`c_alloc`] requirements. See
88/// [`Layout::to_aligned_alloc_compatible`] for details.
89pub struct CAlloc;
90
91impl Alloc for CAlloc {
92    #[cfg_attr(miri, track_caller)]
93    #[inline]
94    fn alloc(&self, layout: Layout) -> Result<NonNull<u8>, Error> {
95        pad_then_alloc(layout, c_alloc)
96    }
97
98    #[cfg_attr(miri, track_caller)]
99    #[inline]
100    fn zalloc(&self, layout: Layout) -> Result<NonNull<u8>, Error> {
101        pad_then_alloc(layout, aligned_zalloc)
102    }
103}
104impl Dealloc for CAlloc {
105    #[cfg_attr(miri, track_caller)]
106    #[inline]
107    unsafe fn dealloc(&self, ptr: NonNull<u8>, _: Layout) {
108        c_dealloc(ptr.as_ptr().cast());
109    }
110}
111impl Grow for CAlloc {
112    #[cfg_attr(miri, track_caller)]
113    unsafe fn grow(
114        &self,
115        ptr: NonNull<u8>,
116        old_layout: Layout,
117        new_layout: Layout
118    ) -> Result<NonNull<u8>, Error> {
119        pad_then_grow(ptr, old_layout, new_layout, c_alloc)
120    }
121    #[cfg_attr(miri, track_caller)]
122    unsafe fn zgrow(
123        &self,
124        ptr: NonNull<u8>,
125        old_layout: Layout,
126        new_layout: Layout
127    ) -> Result<NonNull<u8>, Error> {
128        pad_then_grow(ptr, old_layout, new_layout, aligned_zalloc)
129    }
130}
131impl Shrink for CAlloc {
132    #[cfg_attr(miri, track_caller)]
133    unsafe fn shrink(
134        &self,
135        ptr: NonNull<u8>,
136        old_layout: Layout,
137        new_layout: Layout
138    ) -> Result<NonNull<u8>, Error> {
139        let old_padded = tri!(do old_layout.to_aligned_alloc_compatible());
140        let new_padded = tri!(do new_layout.to_aligned_alloc_compatible());
141
142        if old_padded.size() < new_padded.size() {
143            return Err(Error::ShrinkLargerNewLayout(old_layout.size(), new_layout.size()));
144        }
145
146        null_q_dyn(
147            shrink_aligned(ptr.as_ptr().cast(), new_padded.align(), new_padded.size()),
148            new_padded
149        )
150    }
151}
152impl Realloc for CAlloc {
153    #[cfg_attr(miri, track_caller)]
154    unsafe fn realloc(
155        &self,
156        ptr: NonNull<u8>,
157        old_layout: Layout,
158        new_layout: Layout
159    ) -> Result<NonNull<u8>, Error> {
160        pad_then_realloc(ptr, old_layout, new_layout, c_alloc)
161    }
162    #[cfg_attr(miri, track_caller)]
163    unsafe fn rezalloc(
164        &self,
165        ptr: NonNull<u8>,
166        old_layout: Layout,
167        new_layout: Layout
168    ) -> Result<NonNull<u8>, Error> {
169        pad_then_realloc(ptr, old_layout, new_layout, aligned_zalloc)
170    }
171}
172
173/// The C functions and low-level helpers wrapping them which this allocator is based on.
174pub mod ffi;