diff options
Diffstat (limited to 'servo/components/fallible/lib.rs')
-rw-r--r-- | servo/components/fallible/lib.rs | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/servo/components/fallible/lib.rs b/servo/components/fallible/lib.rs new file mode 100644 index 0000000000..5b4836bf26 --- /dev/null +++ b/servo/components/fallible/lib.rs @@ -0,0 +1,119 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +extern crate hashglobe; +extern crate smallvec; + +#[cfg(feature = "known_system_malloc")] +use hashglobe::alloc; +use hashglobe::FailedAllocationError; +use smallvec::Array; +use smallvec::SmallVec; +use std::vec::Vec; + +pub trait FallibleVec<T> { + /// Append |val| to the end of |vec|. Returns Ok(()) on success, + /// Err(reason) if it fails, with |reason| describing the failure. + fn try_push(&mut self, value: T) -> Result<(), FailedAllocationError>; +} + +///////////////////////////////////////////////////////////////// +// Vec + +impl<T> FallibleVec<T> for Vec<T> { + #[inline(always)] + fn try_push(&mut self, val: T) -> Result<(), FailedAllocationError> { + #[cfg(feature = "known_system_malloc")] + { + if self.capacity() == self.len() { + try_double_vec(self)?; + debug_assert!(self.capacity() > self.len()); + } + } + self.push(val); + Ok(()) + } +} + +// Double the capacity of |vec|, or fail to do so due to lack of memory. +// Returns Ok(()) on success, Err(..) on failure. +#[cfg(feature = "known_system_malloc")] +#[inline(never)] +#[cold] +fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), FailedAllocationError> { + use std::mem; + + let old_ptr = vec.as_mut_ptr(); + let old_len = vec.len(); + + let old_cap: usize = vec.capacity(); + let new_cap: usize = if old_cap == 0 { + 4 + } else { + old_cap + .checked_mul(2) + .ok_or(FailedAllocationError::new("capacity overflow for Vec"))? + }; + + let new_size_bytes = new_cap + .checked_mul(mem::size_of::<T>()) + .ok_or(FailedAllocationError::new("capacity overflow for Vec"))?; + + let new_ptr = unsafe { + if old_cap == 0 { + alloc::alloc(new_size_bytes, 0) + } else { + alloc::realloc(old_ptr as *mut u8, new_size_bytes) + } + }; + + if new_ptr.is_null() { + return Err(FailedAllocationError::new( + "out of memory when allocating Vec", + )); + } + + let new_vec = unsafe { Vec::from_raw_parts(new_ptr as *mut T, old_len, new_cap) }; + + mem::forget(mem::replace(vec, new_vec)); + Ok(()) +} + +///////////////////////////////////////////////////////////////// +// SmallVec + +impl<T: Array> FallibleVec<T::Item> for SmallVec<T> { + #[inline(always)] + fn try_push(&mut self, val: T::Item) -> Result<(), FailedAllocationError> { + if self.capacity() == self.len() { + try_grow_small_vec(self)?; + debug_assert!(self.capacity() > self.len()); + } + self.push(val); + Ok(()) + } +} + +// Grow the capacity of |svec|, or fail to do so due to lack of memory. +#[cfg(feature = "known_system_malloc")] +#[inline(never)] +#[cold] +fn try_grow_small_vec<T>(svec: &mut SmallVec<T>) -> Result<(), FailedAllocationError> +where + T: Array, +{ + let error = match svec.try_reserve(1) { + Ok(..) => return Ok(()), + Err(e) => e, + }; + + Err(match error { + smallvec::CollectionAllocErr::AllocErr { .. } => FailedAllocationError::new( + "out of memory when allocating SmallVec", + ), + smallvec::CollectionAllocErr::CapacityOverflow => FailedAllocationError::new( + "capacity overflow for SmallVec", + ), + }) +} |