diff --git a/src/liballoc/faux.rs b/src/liballoc/faux.rs new file mode 100644 index 0000000000000..1355df1962c57 --- /dev/null +++ b/src/liballoc/faux.rs @@ -0,0 +1,240 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use heap::{mod, usable_size, EMPTY}; +use core::mem::{size_of, min_align_of}; +use core::ptr::null_mut; +use core::num::Int; +use core::result::Result; +use core::result::Result::{Ok, Err}; + +/// Allocates and returns a ptr to memory to store a single element of type T. Handles zero-sized +/// types automatically by returning the non-null EMPTY ptr. +/// +/// # Aborts +/// +/// Aborts on OOM +#[inline] +pub unsafe fn alloc() -> *mut T { + let size = size_of::(); + if size == 0 { + EMPTY as *mut T + } else { + let ptr = heap::allocate(size, min_align_of::()) as *mut T; + if ptr.is_null() { ::oom(); } + ptr + } +} + +/// Allocates and returns a ptr to memory to store a `len` elements of type T. Handles zero-sized +/// types automatically by returning the EMPTY ptr. +/// +/// # Undefined Behaviour +/// +/// * `len` must not be 0. +/// +/// # Panics +/// +/// Panics if size_of * len overflows. +/// +/// # Aborts +/// +/// Aborts on OOM +#[inline] +pub unsafe fn alloc_array(len: uint) -> *mut T { + debug_assert!(len != 0, "0 len passed to alloc_array"); + let size = size_of::(); + if size == 0 { + EMPTY as *mut T + } else { + let desired_size = size.checked_mul(len).expect("capacity overflow"); + let ptr = heap::allocate(desired_size, min_align_of::()) as *mut T; + if ptr.is_null() { ::oom(); } + ptr + } +} + +/// Resizes the allocation referenced by `ptr` to fit `len` elements of type T. Handles zero-sized +/// types automatically by returning the given ptr. `old_len` must be then `len` provided to the +/// call to `alloc_array` or `realloc_array` that created `ptr`. +/// +/// # Undefined Behaviour +/// +/// * `len` must not be 0. +/// +/// # Panics +/// +/// Panics if size_of * len overflows. +/// +/// # Aborts +/// +/// Aborts on OOM +#[inline] +pub unsafe fn realloc_array(ptr: *mut T, old_len: uint, len: uint) -> *mut T { + debug_assert!(len != 0, "0 len passed to realloc_array"); + let size = size_of::(); + if size == 0 { + ptr + } else { + let desired_size = size.checked_mul(len).expect("capacity overflow"); + let align = min_align_of::(); + // No need to check old_size * len, must have been checked when the ptr was made, or + // else UB anyway. + let ptr = heap::reallocate(ptr as *mut u8, size * old_len, desired_size, align) as *mut T; + if ptr.is_null() { ::oom(); } + ptr + } + +} + +/// Tries to resize the allocation referenced by `ptr` in-place to fit `len` elements of type `T`. +/// If successful, yields `Ok`. If unsuccessful, yields `Err`, and the allocation is unchanged. +/// Handles zero-sized types by always returning `Ok`. +/// +/// # Undefined Behaviour +/// +/// * `old_len` must be the `len` provided to the last successful allocator call that created or +/// changed `ptr`. +/// * `len` must not be 0. +/// +/// # Panics +/// +/// Panics if size_of * len overflows. +#[inline] +pub unsafe fn realloc_array_inplace(ptr: *mut T, old_len: uint, len: uint) -> Result<(), ()> { + debug_assert!(len != 0, "0 len passed to realloc_array_inplace"); + // FIXME: just remove this in favour of only shrink/grow? + let size = size_of::(); + let align = min_align_of::(); + if size == 0 { + Ok(()) + } else { + let desired_size = size.checked_mul(len).expect("capacity overflow"); + // No need to check old_size * len, must have been checked when the ptr was made, or + // else UB anyway. + let result_size = heap::reallocate_inplace(ptr as *mut u8, size * old_len, + desired_size, align); + if result_size == usable_size(desired_size, align) { + Ok(()) + } else { + Err(()) + } + } +} + +/// Tries to grow the allocation referenced by `ptr` in-place to fit `len` elements of type `T`. +/// If successful, yields `Ok`. If unsuccessful, yields `Err`, and the allocation is unchanged. +/// Handles zero-sized types by always returning `Ok`. +/// +/// # Undefined Behaviour +/// +/// * `old_len` must be the `len` provided to the last successful allocator call that created or +/// changed `ptr`. +/// * `len` must not be 0. +/// * `len` must not be smaller than `old_len`. +/// +/// # Panics +/// +/// Panics if size_of * len overflows. +#[inline] +pub unsafe fn try_grow_inplace(ptr: *mut T, old_len: uint, len: uint) -> Result<(), ()> { + debug_assert!(len >= old_len, "new len smaller than old_len in try_grow_inplace"); + let size = size_of::(); + let align = min_align_of::(); + if size == 0 { + Ok(()) + } else { + let desired_size = size.checked_mul(len).expect("capacity overflow"); + // No need to check size * old_len, must have been checked when the ptr was made, or + // else UB anyway. + let result_size = heap::reallocate_inplace(ptr as *mut u8, size * old_len, + desired_size, align); + if result_size >= desired_size { + Ok(()) + } else { + Err(()) + } + } +} + +/// Tries to shrink the allocation referenced by `ptr` in-place to fit `len` elements of type `T`. +/// If successful, yields `Ok`. If unsuccessful, yields `Err`, and the allocation is unchanged. +/// Handles zero-sized types by always returning `Ok`. +/// +/// # Undefined Behaviour +/// +/// * `old_len` must be the `len` provided to the last successful allocator call that created or +/// changed `ptr`. +/// * `len` must not be 0. +/// * `len` must not be larger than `old_len`. +/// +/// # Panics +/// +/// Panics if size_of * len overflows. +#[inline] +pub unsafe fn try_shrink_inplace(ptr: *mut T, old_len: uint, len: uint) -> Result<(), ()> { + debug_assert!(len != 0, "0 len passed to try_shrink_inplace"); + debug_assert!(len <= old_len, "new len bigger than old_len in try_grow_inplace"); + let size = size_of::(); + let align = min_align_of::(); + if size == 0 { + Ok(()) + } else { + // No need to check either mul, size * len <= size * old_len, and size * old_len must have + // been checked when the ptr was made, or else UB anyway. + let desired_size = size * len; + let result_size = heap::reallocate_inplace(ptr as *mut u8, size * old_len, + desired_size, align); + if result_size == usable_size(desired_size, align) { + Ok(()) + } else { + Err(()) + } + } +} + + +/// Deallocates the memory referenced by `ptr`, assuming it was allocated with `alloc`. +/// Handles zero-sized types automatically by doing nothing. +/// +/// # Undefined Behaviour +/// +/// * The `ptr` must have been allocated by this API's `alloc` method. +/// * The `ptr` must not have been previously deallocated. +#[inline] +pub unsafe fn dealloc(ptr: *mut T) { + let size = size_of::(); + if size == 0 { + // Do nothing + } else { + heap::deallocate(ptr as *mut u8, size, min_align_of::()); + } +} + +/// Deallocates the memory referenced by `ptr`, assuming it was allocated with `alloc_array` or +/// `realloc_array`. Handles zero-sized types automatically by doing nothing. +/// +/// # Undefined Behaviour +/// +/// * The `ptr` must have been allocated by this API's `alloc_array` or `realloc_array` methods. +/// * The `ptr` must not have been previously deallocated. +/// * `len` must be the `len` provided to the last successful allocator call that created or +/// changed `ptr`. +#[inline] +pub unsafe fn dealloc_array(ptr: *mut T, len: uint) { + let size = size_of::(); + if size == 0 { + // Do nothing + } else { + // No need to check size * len, must have been checked when the ptr was made, or + // else UB anyway. + heap::deallocate(ptr as *mut u8, size * len, min_align_of::()); + } +} \ No newline at end of file diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 173ca008d0356..9994fc036d14e 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -85,6 +85,7 @@ pub use boxed as owned; // Heaps provided for low-level allocation strategies pub mod heap; +pub mod faux; // Primitive types using the heaps above diff --git a/src/libcollections/ring_buf.rs b/src/libcollections/ring_buf.rs index 643b500ec3e2b..8542a20ce1476 100644 --- a/src/libcollections/ring_buf.rs +++ b/src/libcollections/ring_buf.rs @@ -27,7 +27,7 @@ use core::num::{Int, UnsignedInt}; use std::hash::{Writer, Hash}; use std::cmp; -use alloc::heap; +use alloc::faux; static INITIAL_CAPACITY: uint = 8u; // 2^3 static MINIMUM_CAPACITY: uint = 2u; @@ -62,11 +62,7 @@ impl Drop for RingBuf { fn drop(&mut self) { self.clear(); unsafe { - if mem::size_of::() != 0 { - heap::deallocate(self.ptr as *mut u8, - self.cap * mem::size_of::(), - mem::min_align_of::()) - } + faux::dealloc_array(self.ptr, self.cap); } } } @@ -116,18 +112,7 @@ impl RingBuf { pub fn with_capacity(n: uint) -> RingBuf { // +1 since the ringbuffer always leaves one space empty let cap = cmp::max(n + 1, MINIMUM_CAPACITY).next_power_of_two(); - let size = cap.checked_mul(mem::size_of::()) - .expect("capacity overflow"); - - let ptr = if mem::size_of::() != 0 { - unsafe { - let ptr = heap::allocate(size, mem::min_align_of::()) as *mut T;; - if ptr.is_null() { ::alloc::oom() } - ptr - } - } else { - heap::EMPTY as *mut T - }; + let ptr = unsafe { faux::alloc_array(cap) }; RingBuf { tail: 0, @@ -283,17 +268,8 @@ impl RingBuf { let count = (new_len + 1).next_power_of_two(); assert!(count >= new_len + 1); - if mem::size_of::() != 0 { - let old = self.cap * mem::size_of::(); - let new = count.checked_mul(mem::size_of::()) - .expect("capacity overflow"); - unsafe { - self.ptr = heap::reallocate(self.ptr as *mut u8, - old, - new, - mem::min_align_of::()) as *mut T; - if self.ptr.is_null() { ::alloc::oom() } - } + unsafe { + self.ptr = faux::realloc_array(self.ptr, self.cap, count); } // Move the shortest contiguous section of the ring buffer