@@ -19,30 +19,53 @@ type AllocatorFn = unsafe fn(Layout) -> *mut u8;
19
19
/// Allocate `size_bytes` many bytes using the `allocator` function
20
20
/// and return a pointer.
21
21
///
22
- /// # Safety
22
+ /// # Panics
23
23
///
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).
25
27
///
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`].
27
32
unsafe 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 ) ;
31
36
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.
36
57
let ptr_prefix = allocator ( layout) ;
37
58
if ptr_prefix. is_null ( ) {
59
+ // Abort execution if allocation failed.
38
60
alloc:: handle_alloc_error ( layout) ;
39
61
}
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.
41
65
( 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.
46
69
ptr_prefix. add ( MIN_ALIGN )
47
70
}
48
71
@@ -53,6 +76,7 @@ unsafe fn allocate(size_bytes: usize, allocator: AllocatorFn) -> *mut u8 {
53
76
/// Allocated bytes must be freed using [`rust_free`].
54
77
#[ no_mangle]
55
78
pub unsafe extern "C" fn rust_malloc ( size_bytes : usize ) -> * mut u8 {
79
+ // SAFETY: Allocator is `alloc::alloc`.
56
80
allocate ( size_bytes, alloc:: alloc)
57
81
}
58
82
@@ -64,23 +88,44 @@ pub unsafe extern "C" fn rust_malloc(size_bytes: usize) -> *mut u8 {
64
88
#[ no_mangle]
65
89
pub unsafe extern "C" fn rust_calloc ( num : usize , size : usize ) -> * mut u8 {
66
90
let size_bytes = num * size;
91
+ // SAFETY: Allocator is `alloc_alloc_zeroed`.
67
92
allocate ( size_bytes, alloc:: alloc_zeroed)
68
93
}
69
94
70
95
/// Free allocated bytes at `ptr_bytes`.
71
96
///
72
97
/// # Safety
73
98
///
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.
75
101
#[ no_mangle]
76
102
pub 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.
79
123
let ptr_prefix = ptr_bytes. sub ( MIN_ALIGN ) ;
124
+ // SAFETY: prefix is valid for reads and well-aligned.
80
125
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.
83
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.
84
129
alloc:: dealloc ( ptr_prefix, layout)
85
130
}
86
131
0 commit comments