mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
Auto merge of #18397 - julian-seward1:master, r=Manishearth
Add fallible append APIs for Vec and SmallVec …r=manishearth. <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [ ] `./mach build -d` does not report any errors - [ ] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18397) <!-- Reviewable:end -->
This commit is contained in:
commit
c68bc0c145
4 changed files with 165 additions and 0 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -972,6 +972,13 @@ name = "extra-default"
|
|||
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"
|
||||
|
@ -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)",
|
||||
|
|
13
components/fallible/Cargo.toml
Normal file
13
components/fallible/Cargo.toml
Normal file
|
@ -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"
|
143
components/fallible/lib.rs
Normal file
143
components/fallible/lib.rs
Normal file
|
@ -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(())
|
||||
}
|
|
@ -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}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue