]> git.lizzy.rs Git - rust.git/blob - src/liballoc_system/lib.rs
Rollup merge of #43000 - estebank:on-unimplemented-path, r=arielb1
[rust.git] / src / liballoc_system / lib.rs
1 // Copyright 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 #![crate_name = "alloc_system"]
12 #![crate_type = "rlib"]
13 #![no_std]
14 #![deny(warnings)]
15 #![unstable(feature = "alloc_system",
16             reason = "this library is unlikely to be stabilized in its current \
17                       form or name",
18             issue = "27783")]
19 #![cfg_attr(stage0, allocator)]
20 #![cfg_attr(stage0, feature(allocator))]
21 #![cfg_attr(stage0, feature(core_intrinsics))]
22 #![cfg_attr(not(stage0), feature(global_allocator))]
23 #![cfg_attr(not(stage0), feature(allocator_api))]
24 #![cfg_attr(not(stage0), feature(alloc))]
25 #![cfg_attr(not(stage0), feature(core_intrinsics))]
26 #![feature(staged_api)]
27 #![cfg_attr(any(unix, target_os = "redox"), feature(libc))]
28
29 // The minimum alignment guaranteed by the architecture. This value is used to
30 // add fast paths for low alignment values. In practice, the alignment is a
31 // constant at the call site and the branch will be optimized out.
32 #[cfg(all(any(target_arch = "x86",
33               target_arch = "arm",
34               target_arch = "mips",
35               target_arch = "powerpc",
36               target_arch = "powerpc64",
37               target_arch = "asmjs",
38               target_arch = "wasm32")))]
39 const MIN_ALIGN: usize = 8;
40 #[cfg(all(any(target_arch = "x86_64",
41               target_arch = "aarch64",
42               target_arch = "mips64",
43               target_arch = "s390x",
44               target_arch = "sparc64")))]
45 const MIN_ALIGN: usize = 16;
46
47 #[cfg(stage0)]
48 pub use old::*;
49 #[cfg(stage0)]
50 mod old;
51
52 #[cfg(not(stage0))]
53 pub use new::System;
54 #[cfg(not(stage0))]
55 mod new {
56     pub extern crate alloc;
57
58     use self::alloc::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace};
59
60     #[unstable(feature = "allocator_api", issue = "32838")]
61     pub struct System;
62
63     #[unstable(feature = "allocator_api", issue = "32838")]
64     unsafe impl Alloc for System {
65         #[inline]
66         unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
67             (&*self).alloc(layout)
68         }
69
70         #[inline]
71         unsafe fn alloc_zeroed(&mut self, layout: Layout)
72             -> Result<*mut u8, AllocErr>
73         {
74             (&*self).alloc_zeroed(layout)
75         }
76
77         #[inline]
78         unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
79             (&*self).dealloc(ptr, layout)
80         }
81
82         #[inline]
83         unsafe fn realloc(&mut self,
84                           ptr: *mut u8,
85                           old_layout: Layout,
86                           new_layout: Layout) -> Result<*mut u8, AllocErr> {
87             (&*self).realloc(ptr, old_layout, new_layout)
88         }
89
90         fn oom(&mut self, err: AllocErr) -> ! {
91             (&*self).oom(err)
92         }
93
94         #[inline]
95         fn usable_size(&self, layout: &Layout) -> (usize, usize) {
96             (&self).usable_size(layout)
97         }
98
99         #[inline]
100         unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
101             (&*self).alloc_excess(layout)
102         }
103
104         #[inline]
105         unsafe fn realloc_excess(&mut self,
106                                  ptr: *mut u8,
107                                  layout: Layout,
108                                  new_layout: Layout) -> Result<Excess, AllocErr> {
109             (&*self).realloc_excess(ptr, layout, new_layout)
110         }
111
112         #[inline]
113         unsafe fn grow_in_place(&mut self,
114                                 ptr: *mut u8,
115                                 layout: Layout,
116                                 new_layout: Layout) -> Result<(), CannotReallocInPlace> {
117             (&*self).grow_in_place(ptr, layout, new_layout)
118         }
119
120         #[inline]
121         unsafe fn shrink_in_place(&mut self,
122                                   ptr: *mut u8,
123                                   layout: Layout,
124                                   new_layout: Layout) -> Result<(), CannotReallocInPlace> {
125             (&*self).shrink_in_place(ptr, layout, new_layout)
126         }
127     }
128 }
129
130 #[cfg(all(not(stage0), any(unix, target_os = "redox")))]
131 mod platform {
132     extern crate libc;
133
134     use core::cmp;
135     use core::ptr;
136
137     use MIN_ALIGN;
138     use new::System;
139     use new::alloc::heap::{Alloc, AllocErr, Layout};
140
141     #[unstable(feature = "allocator_api", issue = "32838")]
142     unsafe impl<'a> Alloc for &'a System {
143         #[inline]
144         unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
145             let ptr = if layout.align() <= MIN_ALIGN {
146                 libc::malloc(layout.size()) as *mut u8
147             } else {
148                 aligned_malloc(&layout)
149             };
150             if !ptr.is_null() {
151                 Ok(ptr)
152             } else {
153                 Err(AllocErr::Exhausted { request: layout })
154             }
155         }
156
157         #[inline]
158         unsafe fn alloc_zeroed(&mut self, layout: Layout)
159             -> Result<*mut u8, AllocErr>
160         {
161             if layout.align() <= MIN_ALIGN {
162                 let ptr = libc::calloc(layout.size(), 1) as *mut u8;
163                 if !ptr.is_null() {
164                     Ok(ptr)
165                 } else {
166                     Err(AllocErr::Exhausted { request: layout })
167                 }
168             } else {
169                 let ret = self.alloc(layout.clone());
170                 if let Ok(ptr) = ret {
171                     ptr::write_bytes(ptr, 0, layout.size());
172                 }
173                 ret
174             }
175         }
176
177         #[inline]
178         unsafe fn dealloc(&mut self, ptr: *mut u8, _layout: Layout) {
179             libc::free(ptr as *mut libc::c_void)
180         }
181
182         #[inline]
183         unsafe fn realloc(&mut self,
184                           ptr: *mut u8,
185                           old_layout: Layout,
186                           new_layout: Layout) -> Result<*mut u8, AllocErr> {
187             if old_layout.align() != new_layout.align() {
188                 return Err(AllocErr::Unsupported {
189                     details: "cannot change alignment on `realloc`",
190                 })
191             }
192
193             if new_layout.align() <= MIN_ALIGN {
194                 let ptr = libc::realloc(ptr as *mut libc::c_void, new_layout.size());
195                 if !ptr.is_null() {
196                     Ok(ptr as *mut u8)
197                 } else {
198                     Err(AllocErr::Exhausted { request: new_layout })
199                 }
200             } else {
201                 let res = self.alloc(new_layout.clone());
202                 if let Ok(new_ptr) = res {
203                     let size = cmp::min(old_layout.size(), new_layout.size());
204                     ptr::copy_nonoverlapping(ptr, new_ptr, size);
205                     self.dealloc(ptr, old_layout);
206                 }
207                 res
208             }
209         }
210
211         fn oom(&mut self, err: AllocErr) -> ! {
212             use core::fmt::{self, Write};
213
214             // Print a message to stderr before aborting to assist with
215             // debugging. It is critical that this code does not allocate any
216             // memory since we are in an OOM situation. Any errors are ignored
217             // while printing since there's nothing we can do about them and we
218             // are about to exit anyways.
219             drop(writeln!(Stderr, "fatal runtime error: {}", err));
220             unsafe {
221                 ::core::intrinsics::abort();
222             }
223
224             struct Stderr;
225
226             impl Write for Stderr {
227                 fn write_str(&mut self, s: &str) -> fmt::Result {
228                     unsafe {
229                         libc::write(libc::STDERR_FILENO,
230                                     s.as_ptr() as *const libc::c_void,
231                                     s.len());
232                     }
233                     Ok(())
234                 }
235             }
236         }
237     }
238
239     #[cfg(any(target_os = "android", target_os = "redox"))]
240     #[inline]
241     unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
242         // On android we currently target API level 9 which unfortunately
243         // doesn't have the `posix_memalign` API used below. Instead we use
244         // `memalign`, but this unfortunately has the property on some systems
245         // where the memory returned cannot be deallocated by `free`!
246         //
247         // Upon closer inspection, however, this appears to work just fine with
248         // Android, so for this platform we should be fine to call `memalign`
249         // (which is present in API level 9). Some helpful references could
250         // possibly be chromium using memalign [1], attempts at documenting that
251         // memalign + free is ok [2] [3], or the current source of chromium
252         // which still uses memalign on android [4].
253         //
254         // [1]: https://codereview.chromium.org/10796020/
255         // [2]: https://code.google.com/p/android/issues/detail?id=35391
256         // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
257         // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
258         //                                       /memory/aligned_memory.cc
259         libc::memalign(layout.align(), layout.size()) as *mut u8
260     }
261
262     #[cfg(not(any(target_os = "android", target_os = "redox")))]
263     #[inline]
264     unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
265         let mut out = ptr::null_mut();
266         let ret = libc::posix_memalign(&mut out, layout.align(), layout.size());
267         if ret != 0 {
268             ptr::null_mut()
269         } else {
270             out as *mut u8
271         }
272     }
273 }
274
275 #[cfg(all(windows, not(stage0)))]
276 #[allow(bad_style)]
277 mod platform {
278     use core::cmp;
279     use core::ptr;
280
281     use MIN_ALIGN;
282     use new::System;
283     use new::alloc::heap::{Alloc, AllocErr, Layout, CannotReallocInPlace};
284
285     type LPVOID = *mut u8;
286     type HANDLE = LPVOID;
287     type SIZE_T = usize;
288     type DWORD = u32;
289     type BOOL = i32;
290     type LPDWORD = *mut DWORD;
291     type LPOVERLAPPED = *mut u8;
292
293     const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
294
295     extern "system" {
296         fn GetProcessHeap() -> HANDLE;
297         fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
298         fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID;
299         fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
300         fn GetLastError() -> DWORD;
301         fn WriteFile(hFile: HANDLE,
302                      lpBuffer: LPVOID,
303                      nNumberOfBytesToWrite: DWORD,
304                      lpNumberOfBytesWritten: LPDWORD,
305                      lpOverlapped: LPOVERLAPPED)
306                      -> BOOL;
307         fn GetStdHandle(which: DWORD) -> HANDLE;
308     }
309
310     #[repr(C)]
311     struct Header(*mut u8);
312
313     const HEAP_ZERO_MEMORY: DWORD = 0x00000008;
314     const HEAP_REALLOC_IN_PLACE_ONLY: DWORD = 0x00000010;
315
316     unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header {
317         &mut *(ptr as *mut Header).offset(-1)
318     }
319
320     unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
321         let aligned = ptr.offset((align - (ptr as usize & (align - 1))) as isize);
322         *get_header(aligned) = Header(ptr);
323         aligned
324     }
325
326     #[inline]
327     unsafe fn allocate_with_flags(layout: Layout, flags: DWORD)
328         -> Result<*mut u8, AllocErr>
329     {
330         let ptr = if layout.align() <= MIN_ALIGN {
331             HeapAlloc(GetProcessHeap(), flags, layout.size())
332         } else {
333             let size = layout.size() + layout.align();
334             let ptr = HeapAlloc(GetProcessHeap(), flags, size);
335             if ptr.is_null() {
336                 ptr
337             } else {
338                 align_ptr(ptr, layout.align())
339             }
340         };
341         if ptr.is_null() {
342             Err(AllocErr::Exhausted { request: layout })
343         } else {
344             Ok(ptr as *mut u8)
345         }
346     }
347
348     #[unstable(feature = "allocator_api", issue = "32838")]
349     unsafe impl<'a> Alloc for &'a System {
350         #[inline]
351         unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
352             allocate_with_flags(layout, 0)
353         }
354
355         #[inline]
356         unsafe fn alloc_zeroed(&mut self, layout: Layout)
357             -> Result<*mut u8, AllocErr>
358         {
359             allocate_with_flags(layout, HEAP_ZERO_MEMORY)
360         }
361
362         #[inline]
363         unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
364             if layout.align() <= MIN_ALIGN {
365                 let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
366                 debug_assert!(err != 0, "Failed to free heap memory: {}",
367                               GetLastError());
368             } else {
369                 let header = get_header(ptr);
370                 let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
371                 debug_assert!(err != 0, "Failed to free heap memory: {}",
372                               GetLastError());
373             }
374         }
375
376         #[inline]
377         unsafe fn realloc(&mut self,
378                           ptr: *mut u8,
379                           old_layout: Layout,
380                           new_layout: Layout) -> Result<*mut u8, AllocErr> {
381             if old_layout.align() != new_layout.align() {
382                 return Err(AllocErr::Unsupported {
383                     details: "cannot change alignment on `realloc`",
384                 })
385             }
386
387             if new_layout.align() <= MIN_ALIGN {
388                 let ptr = HeapReAlloc(GetProcessHeap(),
389                                       0,
390                                       ptr as LPVOID,
391                                       new_layout.size());
392                 if !ptr.is_null() {
393                     Ok(ptr as *mut u8)
394                 } else {
395                     Err(AllocErr::Exhausted { request: new_layout })
396                 }
397             } else {
398                 let res = self.alloc(new_layout.clone());
399                 if let Ok(new_ptr) = res {
400                     let size = cmp::min(old_layout.size(), new_layout.size());
401                     ptr::copy_nonoverlapping(ptr, new_ptr, size);
402                     self.dealloc(ptr, old_layout);
403                 }
404                 res
405             }
406         }
407
408         #[inline]
409         unsafe fn grow_in_place(&mut self,
410                                 ptr: *mut u8,
411                                 layout: Layout,
412                                 new_layout: Layout) -> Result<(), CannotReallocInPlace> {
413             self.shrink_in_place(ptr, layout, new_layout)
414         }
415
416         #[inline]
417         unsafe fn shrink_in_place(&mut self,
418                                   ptr: *mut u8,
419                                   old_layout: Layout,
420                                   new_layout: Layout) -> Result<(), CannotReallocInPlace> {
421             if old_layout.align() != new_layout.align() {
422                 return Err(CannotReallocInPlace)
423             }
424
425             let new = if new_layout.align() <= MIN_ALIGN {
426                 HeapReAlloc(GetProcessHeap(),
427                             HEAP_REALLOC_IN_PLACE_ONLY,
428                             ptr as LPVOID,
429                             new_layout.size())
430             } else {
431                 let header = get_header(ptr);
432                 HeapReAlloc(GetProcessHeap(),
433                             HEAP_REALLOC_IN_PLACE_ONLY,
434                             header.0 as LPVOID,
435                             new_layout.size() + new_layout.align())
436             };
437             if new.is_null() {
438                 Err(CannotReallocInPlace)
439             } else {
440                 Ok(())
441             }
442         }
443
444         fn oom(&mut self, err: AllocErr) -> ! {
445             use core::fmt::{self, Write};
446
447             // Same as with unix we ignore all errors here
448             drop(writeln!(Stderr, "fatal runtime error: {}", err));
449             unsafe {
450                 ::core::intrinsics::abort();
451             }
452
453             struct Stderr;
454
455             impl Write for Stderr {
456                 fn write_str(&mut self, s: &str) -> fmt::Result {
457                     unsafe {
458                         // WriteFile silently fails if it is passed an invalid
459                         // handle, so there is no need to check the result of
460                         // GetStdHandle.
461                         WriteFile(GetStdHandle(STD_ERROR_HANDLE),
462                                   s.as_ptr() as LPVOID,
463                                   s.len() as DWORD,
464                                   ptr::null_mut(),
465                                   ptr::null_mut());
466                     }
467                     Ok(())
468                 }
469             }
470         }
471     }
472 }