@@ -19,30 +19,53 @@ type AllocatorFn = unsafe fn(Layout) -> *mut u8;
1919/// Allocate `size_bytes` many bytes using the `allocator` function
2020/// and return a pointer.
2121///
22- /// # Safety
22+ /// # Panics
2323///
24- /// `allocator` must be [`alloc::alloc`] or [`alloc::alloc_zeroed`].
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).
2527///
26- /// Allocated bytes must be freed using [`rust_free`].
28+ /// # Safety
29+ ///
30+ /// - `allocator` must be [`alloc::alloc`] or [`alloc::alloc_zeroed`].
31+ /// - Allocated bytes must be freed using [`rust_free`].
2732unsafe fn allocate ( size_bytes : usize , allocator : AllocatorFn ) -> * mut u8 {
28- assert ! ( MIN_ALIGN >= mem:: align_of:: <usize >( ) ) ;
29- assert ! ( MIN_ALIGN >= mem:: align_of:: <& usize >( ) ) ;
30- assert ! ( MIN_ALIGN >= mem:: size_of:: <usize >( ) ) ;
33+ assert ! ( mem:: align_of:: <usize >( ) <= MIN_ALIGN ) ;
34+ assert ! ( mem:: align_of:: <& usize >( ) <= MIN_ALIGN ) ;
35+ assert ! ( mem:: size_of:: <usize >( ) <= MIN_ALIGN ) ;
3136
32- // Allocate MIN_ALIGN + size_bytes many bytes
33- // Panic if too many bytes are tried to be allocated
34- let size_prefixed_bytes = MIN_ALIGN + size_bytes;
35- let layout = Layout :: from_size_align ( size_prefixed_bytes, MIN_ALIGN ) . unwrap ( ) ;
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.
3657 let ptr_prefix = allocator ( layout) ;
3758 if ptr_prefix. is_null ( ) {
59+ // Abort execution if allocation failed.
3860 alloc:: handle_alloc_error ( layout) ;
3961 }
40- // Write the number of allocated bytes to memory
62+ // Write number of allocated bytes into prefix.
63+ //
64+ // SAFETY: prefix is valid for writes and well-aligned.
4165 ( ptr_prefix as * mut usize ) . write ( size_prefixed_bytes) ;
42- // Return a pointer to the size_bytes many allocated bytes behind the counter
43- // We need to offset the pointer by MIN_ALIGN to keep alignment
44- // This means there is a gap of MIN_ALIGN - sizeof(size_t) many unused bytes
45- // We asserted that MIN_ALIGN >= sizeof(size_t), so this gap is nonnegative
66+ // Return pointer behind prefix.
67+ //
68+ // SAFETY: `ptr_prefix` and `ptr_prefix + MIN_ALIGN` are part of same allocated object.
4669 ptr_prefix. add ( MIN_ALIGN )
4770}
4871
@@ -53,6 +76,7 @@ unsafe fn allocate(size_bytes: usize, allocator: AllocatorFn) -> *mut u8 {
5376/// Allocated bytes must be freed using [`rust_free`].
5477#[ no_mangle]
5578pub unsafe extern "C" fn rust_malloc ( size_bytes : usize ) -> * mut u8 {
79+ // SAFETY: Allocator is `alloc::alloc`.
5680 allocate ( size_bytes, alloc:: alloc)
5781}
5882
@@ -64,23 +88,44 @@ pub unsafe extern "C" fn rust_malloc(size_bytes: usize) -> *mut u8 {
6488#[ no_mangle]
6589pub unsafe extern "C" fn rust_calloc ( num : usize , size : usize ) -> * mut u8 {
6690 let size_bytes = num * size;
91+ // SAFETY: Allocator is `alloc_alloc_zeroed`.
6792 allocate ( size_bytes, alloc:: alloc_zeroed)
6893}
6994
7095/// Free allocated bytes at `ptr_bytes`.
7196///
7297/// # Safety
7398///
74- /// Bytes must have been allocated using [`rust_malloc`] or [`rust_calloc`].
99+ /// - `ptr_bytes` must have been allocated using [`rust_malloc`] or [`rust_calloc`].
100+ /// - If `ptr_bytes` is a `NULL` pointer, then this function is a NO-OP.
75101#[ no_mangle]
76102pub unsafe extern "C" fn rust_free ( ptr_bytes : * mut u8 ) {
77- // Move MIN_ALIGN many bytes back in memory
78- // and read the number of allocated bytes
103+ if ptr_bytes. is_null ( ) {
104+ return ;
105+ }
106+
107+ // We got a pointer to an allocation from `rust_malloc` or `rust_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.
79123 let ptr_prefix = ptr_bytes. sub ( MIN_ALIGN ) ;
124+ // SAFETY: prefix is valid for reads and well-aligned.
80125 let size_prefixed_bytes = ( ptr_prefix as * mut usize ) . read ( ) ;
81- // Free the allocated bytes including the counter
82- // Panic if the number of bytes overflows
126+ // INFALLIBLE: This layout was already allocated, so there is no overflow.
83127 let layout = Layout :: from_size_align ( size_prefixed_bytes, MIN_ALIGN ) . unwrap ( ) ;
128+ // SAFETY: `ptr_prefix` was allocated via same allocator with same layout.
84129 alloc:: dealloc ( ptr_prefix, layout)
85130}
86131
0 commit comments