diff options
-rw-r--r-- | Cargo.lock | 8 | ||||
-rw-r--r-- | components/fallible/Cargo.toml | 13 | ||||
-rw-r--r-- | components/fallible/lib.rs | 143 | ||||
-rw-r--r-- | components/style/Cargo.toml | 1 |
4 files changed, 165 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock index fb1e0982b0b..443483e7275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -973,6 +973,13 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "fallible" +version = "0.0.1" +dependencies = [ + "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "flate2" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3084,6 +3091,7 @@ dependencies = [ "cssparser 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fallible 0.0.1", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "hashglobe 0.1.0", "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/fallible/Cargo.toml b/components/fallible/Cargo.toml new file mode 100644 index 00000000000..b9425d39acb --- /dev/null +++ b/components/fallible/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "fallible" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +publish = false + +[lib] +name = "fallible" +path = "lib.rs" + +[dependencies] +smallvec = "0.4" diff --git a/components/fallible/lib.rs b/components/fallible/lib.rs new file mode 100644 index 00000000000..388a4de12bd --- /dev/null +++ b/components/fallible/lib.rs @@ -0,0 +1,143 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +extern crate smallvec; + +use smallvec::Array; +use smallvec::SmallVec; +use std::mem; +use std::ptr::copy_nonoverlapping; +use std::vec::Vec; + +extern "C" { + fn realloc(ptr: *mut u8, bytes: usize) -> *mut u8; + fn malloc(bytes: usize) -> *mut u8; +} + +pub trait FallibleVec<T> { + /// Append |val| to the end of |vec|. Returns Ok(()) on success, + /// Err(()) if it fails, which can only be due to lack of memory. + fn try_push(&mut self, value: T) -> Result<(), ()>; +} + + +///////////////////////////////////////////////////////////////// +// Vec + +impl<T> FallibleVec<T> for Vec<T> { + #[inline] + fn try_push(&mut self, val: T) -> Result<(), ()> { + 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. +#[inline(never)] +#[cold] +fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), ()> { + 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(()) ? }; + + let new_size_bytes = + new_cap.checked_mul(mem::size_of::<T>()).ok_or(()) ? ; + + let new_ptr = unsafe { + if old_cap == 0 { + malloc(new_size_bytes) + } else { + realloc(old_ptr as *mut u8, new_size_bytes) + } + }; + + if new_ptr.is_null() { + return Err(()); + } + + 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] + fn try_push(&mut self, val: T::Item) -> Result<(), ()> { + if self.capacity() == self.len() { + try_double_small_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. +#[inline(never)] +#[cold] +fn try_double_small_vec<T>(svec: &mut SmallVec<T>) -> Result<(), ()> +where + T: Array, +{ + let old_ptr = svec.as_mut_ptr(); + let old_len = svec.len(); + + let old_cap: usize = svec.capacity(); + let new_cap: usize = + if old_cap == 0 { 4 } else { old_cap.checked_mul(2).ok_or(()) ? }; + + // This surely shouldn't fail, if |old_cap| was previously accepted as a + // valid value. But err on the side of caution. + let old_size_bytes = + old_cap.checked_mul(mem::size_of::<T>()).ok_or(()) ? ; + + let new_size_bytes = + new_cap.checked_mul(mem::size_of::<T>()).ok_or(()) ? ; + + let new_ptr; + if svec.spilled() { + // There's an old block to free, and, presumably, old contents to + // copy. realloc takes care of both aspects. + unsafe { + new_ptr = realloc(old_ptr as *mut u8, new_size_bytes); + } + } else { + // There's no old block to free. There may be old contents to copy. + unsafe { + new_ptr = malloc(new_size_bytes); + if !new_ptr.is_null() && old_size_bytes > 0 { + copy_nonoverlapping(old_ptr as *const u8, + new_ptr as *mut u8, old_size_bytes); + } + } + } + + if new_ptr.is_null() { + return Err(()); + } + + let new_vec = unsafe { + Vec::from_raw_parts(new_ptr as *mut T::Item, old_len, new_cap) + }; + + let new_svec = SmallVec::from_vec(new_vec); + mem::forget(mem::replace(svec, new_svec)); + Ok(()) +} diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index b97002f3c4d..6839dea0d9f 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -40,6 +40,7 @@ cfg-if = "0.1.0" cssparser = "0.20" encoding = {version = "0.2", optional = true} euclid = "0.15" +fallible = { path = "../fallible" } fnv = "1.0" hashglobe = { path = "../hashglobe" } heapsize = {version = "0.4", optional = true} |