aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--components/fallible/Cargo.toml13
-rw-r--r--components/fallible/lib.rs143
-rw-r--r--components/style/Cargo.toml1
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}