]> git.lizzy.rs Git - rust.git/blob - src/liballoc/heap.rs
d46c6a83ff32c64069c50f985838e1885199909b
[rust.git] / src / liballoc / heap.rs
1 // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![unstable(feature = "heap_api",
12             reason = "the precise API and guarantees it provides may be tweaked \
13                       slightly, especially to possibly take into account the \
14                       types being stored to make room for a future \
15                       tracing garbage collector",
16             issue = "27700")]
17
18 use allocator::{Alloc, AllocErr, CannotReallocInPlace, Layout};
19 use core::{isize, usize, cmp, ptr};
20 use core::intrinsics::{min_align_of_val, size_of_val};
21
22 #[allow(improper_ctypes)]
23 extern "C" {
24     #[allocator]
25     fn __rust_allocate(size: usize, align: usize) -> *mut u8;
26     fn __rust_allocate_zeroed(size: usize, align: usize) -> *mut u8;
27     fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize);
28     fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8;
29     fn __rust_reallocate_inplace(ptr: *mut u8,
30                                  old_size: usize,
31                                  size: usize,
32                                  align: usize)
33                                  -> usize;
34     fn __rust_usable_size(size: usize, align: usize) -> usize;
35 }
36
37 #[inline(always)]
38 fn check_size_and_alignment(size: usize, align: usize) {
39     debug_assert!(size != 0);
40     debug_assert!(size <= isize::MAX as usize,
41                   "Tried to allocate too much: {} bytes",
42                   size);
43     debug_assert!(usize::is_power_of_two(align),
44                   "Invalid alignment of allocation: {}",
45                   align);
46 }
47
48 #[derive(Copy, Clone, Default, Debug)]
49 pub struct HeapAlloc;
50
51 unsafe impl Alloc for HeapAlloc {
52     unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
53         let addr = allocate(layout.size(), layout.align());
54         if addr.is_null() {
55             Err(AllocErr::Exhausted { request: layout })
56         } else {
57             Ok(addr)
58         }
59     }
60
61     unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
62         let addr = allocate_zeroed(layout.size(), layout.align());
63         if addr.is_null() {
64             Err(AllocErr::Exhausted { request: layout })
65         } else {
66             Ok(addr)
67         }
68     }
69
70     unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
71         deallocate(ptr, layout.size(), layout.align());
72     }
73
74     fn usable_size(&self, layout: &Layout) -> (usize, usize) {
75         (layout.size(), usable_size(layout.size(), layout.align()))
76     }
77
78     unsafe fn realloc(&mut self,
79                       ptr: *mut u8,
80                       layout: Layout,
81                       new_layout: Layout)
82                       -> Result<*mut u8, AllocErr>
83     {
84         let old_size = layout.size();
85         let new_size = new_layout.size();
86         if layout.align() == new_layout.align() {
87             let new_ptr = reallocate(ptr, old_size, new_size, layout.align());
88             if new_ptr.is_null() {
89                 // We assume `reallocate` already tried alloc + copy +
90                 // dealloc fallback; thus pointless to repeat effort
91                 Err(AllocErr::Exhausted { request: new_layout })
92             } else {
93                 Ok(new_ptr)
94             }
95         } else {
96             // if alignments don't match, fall back on alloc + copy + dealloc
97             let result = self.alloc(new_layout);
98             if let Ok(new_ptr) = result {
99                 ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size));
100                 self.dealloc(ptr, layout);
101             }
102             result
103         }
104     }
105
106     unsafe fn grow_in_place(&mut self,
107                             ptr: *mut u8,
108                             layout: Layout,
109                             new_layout: Layout)
110                             -> Result<(), CannotReallocInPlace>
111     {
112         // grow_in_place spec requires this, and the spec for reallocate_inplace
113         // makes it hard to detect failure if it does not hold.
114         debug_assert!(new_layout.size() >= layout.size());
115
116         if layout.align() != new_layout.align() { // reallocate_inplace requires this.
117             return Err(CannotReallocInPlace);
118         }
119         let usable = reallocate_inplace(ptr, layout.size(), new_layout.size(), layout.align());
120         if usable >= new_layout.size() { Ok(()) } else { Err(CannotReallocInPlace) }
121     }
122 }
123
124 // FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias`
125
126 /// Return a pointer to `size` bytes of memory aligned to `align`.
127 ///
128 /// On failure, return a null pointer.
129 ///
130 /// Behavior is undefined if the requested size is 0 or the alignment is not a
131 /// power of 2. The alignment must be no larger than the largest supported page
132 /// size on the platform.
133 #[inline]
134 pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 {
135     check_size_and_alignment(size, align);
136     __rust_allocate(size, align)
137 }
138
139 /// Return a pointer to `size` bytes of memory aligned to `align` and
140 /// initialized to zeroes.
141 ///
142 /// On failure, return a null pointer.
143 ///
144 /// Behavior is undefined if the requested size is 0 or the alignment is not a
145 /// power of 2. The alignment must be no larger than the largest supported page
146 /// size on the platform.
147 #[inline]
148 pub unsafe fn allocate_zeroed(size: usize, align: usize) -> *mut u8 {
149     check_size_and_alignment(size, align);
150     __rust_allocate_zeroed(size, align)
151 }
152
153 /// Resize the allocation referenced by `ptr` to `size` bytes.
154 ///
155 /// On failure, return a null pointer and leave the original allocation intact.
156 ///
157 /// If the allocation was relocated, the memory at the passed-in pointer is
158 /// undefined after the call.
159 ///
160 /// Behavior is undefined if the requested size is 0 or the alignment is not a
161 /// power of 2. The alignment must be no larger than the largest supported page
162 /// size on the platform.
163 ///
164 /// The `old_size` and `align` parameters are the parameters that were used to
165 /// create the allocation referenced by `ptr`. The `old_size` parameter may be
166 /// any value in range_inclusive(requested_size, usable_size).
167 #[inline]
168 pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 {
169     check_size_and_alignment(size, align);
170     __rust_reallocate(ptr, old_size, size, align)
171 }
172
173 /// Resize the allocation referenced by `ptr` to `size` bytes.
174 ///
175 /// If the operation succeeds, it returns `usable_size(size, align)` and if it
176 /// fails (or is a no-op) it returns `usable_size(old_size, align)`.
177 ///
178 /// Behavior is undefined if the requested size is 0 or the alignment is not a
179 /// power of 2. The alignment must be no larger than the largest supported page
180 /// size on the platform.
181 ///
182 /// The `old_size` and `align` parameters are the parameters that were used to
183 /// create the allocation referenced by `ptr`. The `old_size` parameter may be
184 /// any value in range_inclusive(requested_size, usable_size).
185 #[inline]
186 pub unsafe fn reallocate_inplace(ptr: *mut u8,
187                                  old_size: usize,
188                                  size: usize,
189                                  align: usize)
190                                  -> usize {
191     check_size_and_alignment(size, align);
192     __rust_reallocate_inplace(ptr, old_size, size, align)
193 }
194
195 /// Deallocates the memory referenced by `ptr`.
196 ///
197 /// The `ptr` parameter must not be null.
198 ///
199 /// The `old_size` and `align` parameters are the parameters that were used to
200 /// create the allocation referenced by `ptr`. The `old_size` parameter may be
201 /// any value in range_inclusive(requested_size, usable_size).
202 #[inline]
203 pub unsafe fn deallocate(ptr: *mut u8, old_size: usize, align: usize) {
204     __rust_deallocate(ptr, old_size, align)
205 }
206
207 /// Returns the usable size of an allocation created with the specified the
208 /// `size` and `align`.
209 #[inline]
210 pub fn usable_size(size: usize, align: usize) -> usize {
211     unsafe { __rust_usable_size(size, align) }
212 }
213
214 /// An arbitrary non-null address to represent zero-size allocations.
215 ///
216 /// This preserves the non-null invariant for types like `Box<T>`. The address
217 /// may overlap with non-zero-size memory allocations.
218 #[rustc_deprecated(since = "1.19", reason = "Use Unique/Shared::empty() instead")]
219 #[unstable(feature = "heap_api", issue = "27700")]
220 pub const EMPTY: *mut () = 1 as *mut ();
221
222 /// The allocator for unique pointers.
223 // This function must not unwind. If it does, MIR trans will fail.
224 #[cfg(not(test))]
225 #[lang = "exchange_malloc"]
226 #[inline]
227 unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
228     if size == 0 {
229         align as *mut u8
230     } else {
231         let ptr = allocate(size, align);
232         if ptr.is_null() {
233             ::oom()
234         }
235         ptr
236     }
237 }
238
239 #[cfg_attr(not(test), lang = "box_free")]
240 #[inline]
241 pub(crate) unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
242     let size = size_of_val(&*ptr);
243     let align = min_align_of_val(&*ptr);
244     // We do not allocate for Box<T> when T is ZST, so deallocation is also not necessary.
245     if size != 0 {
246         deallocate(ptr as *mut u8, size, align);
247     }
248 }
249
250 #[cfg(test)]
251 mod tests {
252     extern crate test;
253     use self::test::Bencher;
254     use boxed::Box;
255     use heap;
256
257     #[test]
258     fn allocate_zeroed() {
259         unsafe {
260             let size = 1024;
261             let ptr = heap::allocate_zeroed(size, 1);
262             if ptr.is_null() {
263                 ::oom()
264             }
265
266             let end = ptr.offset(size as isize);
267             let mut i = ptr;
268             while i < end {
269                 assert_eq!(*i, 0);
270                 i = i.offset(1);
271             }
272             heap::deallocate(ptr, size, 1);
273         }
274     }
275
276     #[test]
277     fn basic_reallocate_inplace_noop() {
278         unsafe {
279             let size = 4000;
280             let ptr = heap::allocate(size, 8);
281             if ptr.is_null() {
282                 ::oom()
283             }
284             let ret = heap::reallocate_inplace(ptr, size, size, 8);
285             heap::deallocate(ptr, size, 8);
286             assert_eq!(ret, heap::usable_size(size, 8));
287         }
288     }
289
290     #[bench]
291     fn alloc_owned_small(b: &mut Bencher) {
292         b.iter(|| {
293             let _: Box<_> = box 10;
294         })
295     }
296 }