simplicity_sys/
alloc.rs

1use std::alloc::{self, Layout};
2use std::mem;
3
4/// A type whose alignment is greater equal that of any C type.
5/// Allocations with this alignment are valid for all C types.
6/// Remake of `max_align_t` from `stddef.h`.
7/// [16 bytes is the greatest alignment of any architecture Rust currently supports](https://github.com/rust-lang/rust/blob/61223975d46f794466efa832bc7562b9707ecc46/library/std/src/sys/pal/common/alloc.rs).
8#[repr(align(16))]
9#[derive(Default, Copy, Clone)]
10#[allow(dead_code)]
11pub struct AlignedType([u8; 16]);
12
13/// Minimal alignment which is valid for all C types.
14pub const MIN_ALIGN: usize = mem::align_of::<AlignedType>();
15
16/// Signature of an allocator function.
17type AllocatorFn = unsafe fn(Layout) -> *mut u8;
18
19/// Allocate `size_bytes` many bytes using the `allocator` function
20/// and return a pointer.
21///
22/// # Panics
23///
24/// This function panics if `size_bytes` + [`MIN_ALIGN`],
25/// rounded up to next multiple of [`MIN_ALIGN`],
26/// is greater than [`isize::MAX`] (allocated too many bytes).
27///
28/// # Safety
29///
30/// - `allocator` must be [`alloc::alloc`] or [`alloc::alloc_zeroed`].
31/// - Allocated bytes must be freed using [`rust_0_6_free`].
32unsafe fn allocate(size_bytes: usize, allocator: AllocatorFn) -> *mut u8 {
33    assert!(mem::align_of::<usize>() <= MIN_ALIGN);
34    assert!(mem::align_of::<&usize>() <= MIN_ALIGN);
35    assert!(mem::size_of::<usize>() <= MIN_ALIGN);
36
37    // We allocate a sequence of N bytes (size_bytes) that will hold the actual data,
38    // prefixed by a sequence of MIN_ALIGN bytes that hold the number `MIN_ALIGN + N`
39    // (number of allocated bytes).
40    // The prefix needs to be offset by MIN_ALIGN to ensure correct alignment of the next bytes.
41    // Finally, we return a pointer after this prefix for the caller to use.
42    //
43    //                 MIN_ALIGN       MIN_ALIGN + 2
44    //                 |               |
45    // +---------------+-------+-------+-----+-------+
46    // | MIN_ALIGN + N | byte0 | byte1 | ... | byteN |
47    // +---------------+-------+-------+-----+-------+
48    // |               ^       |                     |
49    // 0               |       MIN_ALIGN + 1         MIN_ALIGN + N
50    //                 WE RETURN THIS POINTER
51    //
52    let size_prefixed_bytes = MIN_ALIGN.saturating_add(size_bytes);
53    // PANIC: Allocated too many bytes (documented above).
54    let layout =
55        Layout::from_size_align(size_prefixed_bytes, MIN_ALIGN).expect("allocated too many bytes");
56    // SAFETY: `layout` is nonzero.
57    let ptr_prefix = allocator(layout);
58    if ptr_prefix.is_null() {
59        // Abort execution if allocation failed.
60        alloc::handle_alloc_error(layout);
61    }
62    // Write number of allocated bytes into prefix.
63    //
64    // SAFETY: prefix is valid for writes and well-aligned.
65    (ptr_prefix as *mut usize).write(size_prefixed_bytes);
66    // Return pointer behind prefix.
67    //
68    // SAFETY: `ptr_prefix` and `ptr_prefix + MIN_ALIGN` are part of same allocated object.
69    ptr_prefix.add(MIN_ALIGN)
70}
71
72/// Allocate `size_bytes` many bytes and return a pointer.
73///
74/// # Safety
75///
76/// Allocated bytes must be freed using [`rust_0_6_free`].
77#[no_mangle]
78pub unsafe extern "C" fn rust_0_6_malloc(size_bytes: usize) -> *mut u8 {
79    // SAFETY: Allocator is `alloc::alloc`.
80    allocate(size_bytes, alloc::alloc)
81}
82
83/// Allocate `size_bytes` many zeroed bytes and return a pointer.
84///
85/// # Safety
86///
87/// Allocated bytes must be freed using [`rust_0_6_free`].
88#[no_mangle]
89pub unsafe extern "C" fn rust_0_6_calloc(num: usize, size: usize) -> *mut u8 {
90    let size_bytes = num * size;
91    // SAFETY: Allocator is `alloc_alloc_zeroed`.
92    allocate(size_bytes, alloc::alloc_zeroed)
93}
94
95/// Free allocated bytes at `ptr_bytes`.
96///
97/// # Safety
98///
99/// - `ptr_bytes` must have been allocated using [`rust_0_6_malloc`] or [`rust_0_6_calloc`].
100/// - If `ptr_bytes` is a `NULL` pointer, then this function is a NO-OP.
101#[no_mangle]
102pub unsafe extern "C" fn rust_0_6_free(ptr_bytes: *mut u8) {
103    if ptr_bytes.is_null() {
104        return;
105    }
106
107    // We got a pointer to an allocation from `rust_0_6_malloc` or `rust_0_6_calloc`,
108    // so the memory looks as follows.
109    // There is a prefix of `MIN_ALIGN` bytes in front of the pointer we got.
110    // This prefix holds the total number of allocated bytes.
111    // We free this number of bytes to free the entire sequence.
112    //
113    //                 MIN_ALIGN       MIN_ALIGN + 2
114    //                 |               |
115    // +---------------+-------+-------+-----+-------+
116    // | MIN_ALIGN + N | byte0 | byte1 | ... | byteN |
117    // +---------------+-------+-------+-----+-------+
118    // |               ^       |                     |
119    // 0               |       MIN_ALIGN + 1         MIN_ALIGN + N
120    //                 WE GOT THIS POINTER
121    //
122    // SAFETY: `ptr_bytes` and `ptr_bytes - MIN_ALIGN` are part of same allocated object.
123    let ptr_prefix = ptr_bytes.sub(MIN_ALIGN);
124    // SAFETY: prefix is valid for reads and well-aligned.
125    let size_prefixed_bytes = (ptr_prefix as *mut usize).read();
126    // INFALLIBLE: This layout was already allocated, so there is no overflow.
127    let layout = Layout::from_size_align(size_prefixed_bytes, MIN_ALIGN).unwrap();
128    // SAFETY: `ptr_prefix` was allocated via same allocator with same layout.
129    alloc::dealloc(ptr_prefix, layout)
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn aligned_type_size() {
138        unsafe {
139            assert!(MIN_ALIGN >= mem::size_of::<usize>());
140            assert!(MIN_ALIGN >= mem::size_of::<u128>());
141            assert!(MIN_ALIGN >= crate::ffi::c_sizeof_long_double);
142        }
143    }
144
145    #[test]
146    fn aligned_type_align() {
147        unsafe {
148            assert!(MIN_ALIGN.is_power_of_two());
149            assert!(MIN_ALIGN >= mem::align_of::<usize>());
150            assert!(MIN_ALIGN >= mem::align_of::<&usize>());
151            assert!(MIN_ALIGN >= mem::align_of::<u128>());
152            assert!(MIN_ALIGN >= crate::ffi::c_alignof_long_double);
153        }
154    }
155}