Bug 1121792 - Allow to use ThinVec/nsTArray in the style crate. r=jfkthame,layout-reviewers

This allows to clean-up the previous patches by using a single ThinVec
(which stores length / capacity along with the allocation).

Differential Revision: https://phabricator.services.mozilla.com/D175029
This commit is contained in:
Emilio Cobos Álvarez 2023-04-12 21:58:29 +00:00
parent ebb4d854c5
commit 3a961b3d9a
10 changed files with 104 additions and 48 deletions

3
Cargo.lock generated
View File

@ -3072,6 +3072,7 @@ dependencies = [
"servo_arc",
"smallbitvec",
"smallvec",
"thin-vec",
"void",
]
@ -4998,6 +4999,7 @@ dependencies = [
"static_prefs",
"style_derive",
"style_traits",
"thin-vec",
"time 0.1.45",
"to_shmem",
"to_shmem_derive",
@ -5273,6 +5275,7 @@ dependencies = [
"servo_arc",
"smallbitvec",
"smallvec",
"thin-vec",
]
[[package]]

View File

@ -44,6 +44,7 @@ servo_arc = { path = "../servo_arc" }
smallbitvec = "2.3.0"
smallvec = "1.0"
string_cache = { version = "0.8", optional = true }
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
time = { version = "0.1.17", optional = true }
url = { version = "2.0", optional = true }
uuid = { version = "0.8", features = ["v4"], optional = true }

View File

@ -427,6 +427,28 @@ where
}
}
impl<T> MallocShallowSizeOf for thin_vec::ThinVec<T> {
fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
if self.capacity() == 0 {
// If it's the singleton we might not be a heap pointer.
return 0;
}
assert_eq!(std::mem::size_of::<Self>(), std::mem::size_of::<*const ()>());
unsafe { ops.malloc_size_of(*(self as *const Self as *const *const ())) }
}
}
impl<T: MallocSizeOf> MallocSizeOf for thin_vec::ThinVec<T> {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
let mut n = self.shallow_size_of(ops);
for elem in self.iter() {
n += elem.size_of(ops);
}
n
}
}
macro_rules! malloc_size_of_hash_set {
($ty:ty) => {
impl<T, S> MallocShallowSizeOf for $ty

View File

@ -73,6 +73,7 @@ servo_url = {path = "../url", optional = true}
to_shmem = {path = "../to_shmem"}
to_shmem_derive = {path = "../to_shmem_derive"}
time = "0.1"
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
uluru = "3.0"
unicode-bidi = "0.3"
unicode-segmentation = "1.0"

View File

@ -11,7 +11,7 @@ pub enum PseudoElement {
% for pseudo in PSEUDOS:
/// ${pseudo.value}
% if pseudo.is_tree_pseudo_element():
${pseudo.capitalized_pseudo()}(Box<Box<[Atom]>>),
${pseudo.capitalized_pseudo()}(thin_vec::ThinVec<Atom>),
% elif pseudo.pseudo_ident == "highlight":
${pseudo.capitalized_pseudo()}(AtomIdent),
% else:
@ -210,7 +210,7 @@ impl PseudoElement {
},
_ => {
if starts_with_ignore_ascii_case(name, "-moz-tree-") {
return PseudoElement::tree_pseudo_element(name, Box::new([]))
return PseudoElement::tree_pseudo_element(name, Default::default())
}
const WEBKIT_PREFIX: &str = "-webkit-";
if allow_unkown_webkit && starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) {
@ -228,12 +228,12 @@ impl PseudoElement {
///
/// Returns `None` if the pseudo-element is not recognized.
#[inline]
pub fn tree_pseudo_element(name: &str, args: Box<[Atom]>) -> Option<Self> {
pub fn tree_pseudo_element(name: &str, args: thin_vec::ThinVec<Atom>) -> Option<Self> {
debug_assert!(starts_with_ignore_ascii_case(name, "-moz-tree-"));
let tree_part = &name[10..];
% for pseudo in TREE_PSEUDOS:
if tree_part.eq_ignore_ascii_case("${pseudo.value[11:]}") {
return Some(${pseudo_element_variant(pseudo, "args.into()")});
return Some(${pseudo_element_variant(pseudo, "args")});
}
% endfor
None

View File

@ -19,7 +19,8 @@ use dom::{DocumentState, ElementState};
use selectors::parser::SelectorParseErrorKind;
use selectors::SelectorList;
use std::fmt;
use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError, StyleParseErrorKind, ToCss as ToCss_};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
use thin_vec::ThinVec;
pub use crate::gecko::pseudo_element::{
PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT,
@ -38,13 +39,9 @@ bitflags! {
}
/// The type used to store the language argument to the `:lang` pseudo-class.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
pub enum Lang {
/// A single language code.
Single(AtomIdent),
/// A list of language codes.
List(Box<Vec<AtomIdent>>),
}
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[css(comma)]
pub struct Lang(#[css(iterable)] pub ThinVec<AtomIdent>);
macro_rules! pseudo_class_name {
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
@ -66,10 +63,6 @@ macro_rules! pseudo_class_name {
}
apply_non_ts_list!(pseudo_class_name);
impl OneOrMoreSeparated for AtomIdent {
type S = Comma;
}
impl ToCss for NonTSPseudoClass {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where
@ -79,12 +72,9 @@ impl ToCss for NonTSPseudoClass {
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
match *self {
$(NonTSPseudoClass::$name => concat!(":", $css),)*
NonTSPseudoClass::Lang(ref s) => {
NonTSPseudoClass::Lang(ref lang) => {
dest.write_str(":lang(")?;
match &s {
Lang::Single(lang) => cssparser::ToCss::to_css(lang, dest)?,
Lang::List(list) => list.to_css(&mut CssWriter::new(dest))?,
}
lang.to_css(&mut CssWriter::new(dest))?;
return dest.write_char(')');
},
NonTSPseudoClass::MozLocaleDir(ref dir) => {
@ -394,11 +384,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
if result.is_empty() {
return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
if result.len() == 1 {
NonTSPseudoClass::Lang(Lang::Single(result[0].clone()))
} else {
NonTSPseudoClass::Lang(Lang::List(Box::new(result)))
}
NonTSPseudoClass::Lang(Lang(result.into()))
},
"-moz-locale-dir" => {
NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?)
@ -448,7 +434,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
if starts_with_ignore_ascii_case(&name, "-moz-tree-") {
// Tree pseudo-elements can have zero or more arguments, separated
// by either comma or space.
let mut args = Vec::new();
let mut args = ThinVec::new();
loop {
let location = parser.current_source_location();
match parser.next() {
@ -462,7 +448,6 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
_ => unreachable!("Parser::next() shouldn't return any other error"),
}
}
let args = args.into_boxed_slice();
if let Some(pseudo) = PseudoElement::tree_pseudo_element(&name, args) {
if self.is_pseudo_element_enabled(&pseudo) {
return Ok(pseudo);

View File

@ -1566,26 +1566,14 @@ impl<'le> TElement for GeckoElement<'le> {
Some(Some(ref atom)) => atom.as_ptr(),
_ => ptr::null_mut(),
};
match value {
Lang::Single(lang) => unsafe {
Gecko_MatchLang(
self.0,
override_lang_ptr,
override_lang.is_some(),
lang.as_slice().as_ptr(),
)
},
Lang::List(list) => {
list.iter().any(|lang| unsafe {
Gecko_MatchLang(
self.0,
override_lang_ptr,
override_lang.is_some(),
lang.as_slice().as_ptr(),
)
})
},
}
value.0.iter().any(|lang| unsafe {
Gecko_MatchLang(
self.0,
override_lang_ptr,
override_lang.is_some(),
lang.as_slice().as_ptr(),
)
})
}
fn is_html_document_body_element(&self) -> bool {

View File

@ -19,3 +19,4 @@ servo_arc = { path = "../servo_arc" }
smallbitvec = "2.1.1"
smallvec = "1.0"
string_cache = { version = "0.8", optional = true }
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }

View File

@ -18,6 +18,7 @@ extern crate smallbitvec;
extern crate smallvec;
#[cfg(feature = "string_cache")]
extern crate string_cache;
extern crate thin_vec;
use servo_arc::{Arc, ThinArc};
use smallbitvec::{InternalStorage, SmallBitVec};
@ -37,6 +38,7 @@ use std::os::raw::c_void;
use std::ptr::{self, NonNull};
use std::slice;
use std::str;
use thin_vec::ThinVec;
/// Result type for ToShmem::to_shmem.
///
@ -483,6 +485,58 @@ impl<H: ToShmem, T: ToShmem> ToShmem for ThinArc<H, T> {
}
}
impl<T: ToShmem> ToShmem for ThinVec<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let len = self.len();
if len == 0 {
return Ok(ManuallyDrop::new(Self::new()))
}
assert_eq!(mem::size_of::<Self>(), mem::size_of::<*const ()>());
// nsTArrayHeader size.
// FIXME: Would be nice not to hard-code this, but in practice thin-vec crate also relies
// on this.
let header_size = 2 * mem::size_of::<u32>();
let header_align = mem::size_of::<u32>();
let item_size = mem::size_of::<T>();
let item_align = mem::align_of::<T>();
// We don't need to support underalignment for now, this could be supported if needed.
assert!(item_align >= header_align);
// This is explicitly unsupported by ThinVec, see:
// https://searchfox.org/mozilla-central/rev/ad732108b073742d7324f998c085f459674a6846/third_party/rust/thin-vec/src/lib.rs#375-386
assert!(item_align <= header_size);
let header_padding = 0;
let layout = Layout::from_size_align(header_size + header_padding + padded_size(item_size, item_align) * len, item_align).unwrap();
let shmem_header_ptr = builder.alloc::<u8>(layout);
let shmem_data_ptr = unsafe { shmem_header_ptr.add(header_size + header_padding) };
let data_ptr = self.as_ptr() as *const T as *const u8;
let header_ptr = unsafe { data_ptr.sub(header_size + header_padding) };
unsafe {
// Copy the header. Note this might copy a wrong capacity, but it doesn't matter,
// because shared memory ptrs are immutable anyways, and we can't relocate.
ptr::copy(header_ptr, shmem_header_ptr, header_size);
// ToShmem + copy the contents into the shared buffer.
to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?;
// Return the new ThinVec, which is just a pointer to the shared memory buffer.
let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);
// Sanity-check that the ptr and length match.
debug_assert_eq!(shmem_thinvec.as_ptr(), shmem_data_ptr as *const T);
debug_assert_eq!(shmem_thinvec.len(), len);
Ok(ManuallyDrop::new(shmem_thinvec))
}
}
}
impl ToShmem for SmallBitVec {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let storage = match self.clone().into_storage() {

View File

@ -321,6 +321,7 @@ renaming_overrides_prefixing = true
"BeforeFlag" = "StyleEasingBeforeFlag"
"FontPaletteValueSet" = "gfx::FontPaletteValueSet"
"PaletteValues" = "gfx::FontPaletteValueSet::PaletteValues"
"ThinVec" = "nsTArray"
[export.body]
"CSSPixelLength" = """