mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
style: Allow to use ThinVec/nsTArray in the style crate
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:
parent
a2df8f7ea5
commit
0709e13446
9 changed files with 110 additions and 48 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -3396,6 +3396,7 @@ dependencies = [
|
||||||
"smallbitvec",
|
"smallbitvec",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"string_cache",
|
"string_cache",
|
||||||
|
"thin-vec",
|
||||||
"time 0.1.45",
|
"time 0.1.45",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
|
@ -5711,6 +5712,7 @@ dependencies = [
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"style_derive",
|
"style_derive",
|
||||||
"style_traits",
|
"style_traits",
|
||||||
|
"thin-vec",
|
||||||
"time 0.1.45",
|
"time 0.1.45",
|
||||||
"to_shmem",
|
"to_shmem",
|
||||||
"to_shmem_derive",
|
"to_shmem_derive",
|
||||||
|
@ -5924,6 +5926,12 @@ version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thin-vec"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aac81b6fd6beb5884b0cf3321b8117e6e5d47ecb6fc89f414cfdcca8b2fe2dd8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.50"
|
version = "1.0.50"
|
||||||
|
@ -6060,6 +6068,7 @@ dependencies = [
|
||||||
"smallbitvec",
|
"smallbitvec",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"string_cache",
|
"string_cache",
|
||||||
|
"thin-vec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -45,6 +45,7 @@ servo_arc = { path = "../servo_arc" }
|
||||||
smallbitvec = { workspace = true }
|
smallbitvec = { workspace = true }
|
||||||
smallvec = { workspace = true }
|
smallvec = { workspace = true }
|
||||||
string_cache = { workspace = true, optional = true }
|
string_cache = { workspace = true, optional = true }
|
||||||
|
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
|
||||||
time = { workspace = true, optional = true }
|
time = { workspace = true, optional = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
url = { workspace = true, optional = true }
|
url = { workspace = true, optional = true }
|
||||||
|
|
|
@ -396,6 +396,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 {
|
macro_rules! malloc_size_of_hash_set {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
impl<T, S> MallocShallowSizeOf for $ty
|
impl<T, S> MallocShallowSizeOf for $ty
|
||||||
|
|
|
@ -77,6 +77,7 @@ string_cache = { version = "0.8", optional = true }
|
||||||
style_derive = { path = "../style_derive" }
|
style_derive = { path = "../style_derive" }
|
||||||
style_traits = { workspace = true }
|
style_traits = { workspace = true }
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
|
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
|
||||||
to_shmem = { path = "../to_shmem" }
|
to_shmem = { path = "../to_shmem" }
|
||||||
to_shmem_derive = { path = "../to_shmem_derive" }
|
to_shmem_derive = { path = "../to_shmem_derive" }
|
||||||
uluru = "3.0"
|
uluru = "3.0"
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub enum PseudoElement {
|
||||||
% for pseudo in PSEUDOS:
|
% for pseudo in PSEUDOS:
|
||||||
/// ${pseudo.value}
|
/// ${pseudo.value}
|
||||||
% if pseudo.is_tree_pseudo_element():
|
% if pseudo.is_tree_pseudo_element():
|
||||||
${pseudo.capitalized_pseudo()}(Box<Box<[Atom]>>),
|
${pseudo.capitalized_pseudo()}(thin_vec::ThinVec<Atom>),
|
||||||
% elif pseudo.pseudo_ident == "highlight":
|
% elif pseudo.pseudo_ident == "highlight":
|
||||||
${pseudo.capitalized_pseudo()}(AtomIdent),
|
${pseudo.capitalized_pseudo()}(AtomIdent),
|
||||||
% else:
|
% else:
|
||||||
|
@ -210,7 +210,7 @@ impl PseudoElement {
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
if starts_with_ignore_ascii_case(name, "-moz-tree-") {
|
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-";
|
const WEBKIT_PREFIX: &str = "-webkit-";
|
||||||
if allow_unkown_webkit && starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) {
|
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.
|
/// Returns `None` if the pseudo-element is not recognized.
|
||||||
#[inline]
|
#[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-"));
|
debug_assert!(starts_with_ignore_ascii_case(name, "-moz-tree-"));
|
||||||
let tree_part = &name[10..];
|
let tree_part = &name[10..];
|
||||||
% for pseudo in TREE_PSEUDOS:
|
% for pseudo in TREE_PSEUDOS:
|
||||||
if tree_part.eq_ignore_ascii_case("${pseudo.value[11:]}") {
|
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
|
% endfor
|
||||||
None
|
None
|
||||||
|
|
|
@ -19,7 +19,8 @@ use dom::{DocumentState, ElementState};
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
use selectors::SelectorList;
|
use selectors::SelectorList;
|
||||||
use std::fmt;
|
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::{
|
pub use crate::gecko::pseudo_element::{
|
||||||
PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT,
|
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.
|
/// The type used to store the language argument to the `:lang` pseudo-class.
|
||||||
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
|
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
|
||||||
pub enum Lang {
|
#[css(comma)]
|
||||||
/// A single language code.
|
pub struct Lang(#[css(iterable)] pub ThinVec<AtomIdent>);
|
||||||
Single(AtomIdent),
|
|
||||||
/// A list of language codes.
|
|
||||||
List(Box<Vec<AtomIdent>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! pseudo_class_name {
|
macro_rules! pseudo_class_name {
|
||||||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||||
|
@ -66,10 +63,6 @@ macro_rules! pseudo_class_name {
|
||||||
}
|
}
|
||||||
apply_non_ts_list!(pseudo_class_name);
|
apply_non_ts_list!(pseudo_class_name);
|
||||||
|
|
||||||
impl OneOrMoreSeparated for AtomIdent {
|
|
||||||
type S = Comma;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for NonTSPseudoClass {
|
impl ToCss for NonTSPseudoClass {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
where
|
where
|
||||||
|
@ -79,12 +72,9 @@ impl ToCss for NonTSPseudoClass {
|
||||||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||||
match *self {
|
match *self {
|
||||||
$(NonTSPseudoClass::$name => concat!(":", $css),)*
|
$(NonTSPseudoClass::$name => concat!(":", $css),)*
|
||||||
NonTSPseudoClass::Lang(ref s) => {
|
NonTSPseudoClass::Lang(ref lang) => {
|
||||||
dest.write_str(":lang(")?;
|
dest.write_str(":lang(")?;
|
||||||
match &s {
|
lang.to_css(&mut CssWriter::new(dest))?;
|
||||||
Lang::Single(lang) => cssparser::ToCss::to_css(lang, dest)?,
|
|
||||||
Lang::List(list) => list.to_css(&mut CssWriter::new(dest))?,
|
|
||||||
}
|
|
||||||
return dest.write_char(')');
|
return dest.write_char(')');
|
||||||
},
|
},
|
||||||
NonTSPseudoClass::MozLocaleDir(ref dir) => {
|
NonTSPseudoClass::MozLocaleDir(ref dir) => {
|
||||||
|
@ -394,11 +384,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
||||||
if result.is_empty() {
|
if result.is_empty() {
|
||||||
return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
}
|
}
|
||||||
if result.len() == 1 {
|
NonTSPseudoClass::Lang(Lang(result.into()))
|
||||||
NonTSPseudoClass::Lang(Lang::Single(result[0].clone()))
|
|
||||||
} else {
|
|
||||||
NonTSPseudoClass::Lang(Lang::List(Box::new(result)))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"-moz-locale-dir" => {
|
"-moz-locale-dir" => {
|
||||||
NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?)
|
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-") {
|
if starts_with_ignore_ascii_case(&name, "-moz-tree-") {
|
||||||
// Tree pseudo-elements can have zero or more arguments, separated
|
// Tree pseudo-elements can have zero or more arguments, separated
|
||||||
// by either comma or space.
|
// by either comma or space.
|
||||||
let mut args = Vec::new();
|
let mut args = ThinVec::new();
|
||||||
loop {
|
loop {
|
||||||
let location = parser.current_source_location();
|
let location = parser.current_source_location();
|
||||||
match parser.next() {
|
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"),
|
_ => 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 let Some(pseudo) = PseudoElement::tree_pseudo_element(&name, args) {
|
||||||
if self.is_pseudo_element_enabled(&pseudo) {
|
if self.is_pseudo_element_enabled(&pseudo) {
|
||||||
return Ok(pseudo);
|
return Ok(pseudo);
|
||||||
|
|
|
@ -1566,17 +1566,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
Some(Some(ref atom)) => atom.as_ptr(),
|
Some(Some(ref atom)) => atom.as_ptr(),
|
||||||
_ => ptr::null_mut(),
|
_ => ptr::null_mut(),
|
||||||
};
|
};
|
||||||
match value {
|
value.0.iter().any(|lang| unsafe {
|
||||||
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(
|
Gecko_MatchLang(
|
||||||
self.0,
|
self.0,
|
||||||
override_lang_ptr,
|
override_lang_ptr,
|
||||||
|
@ -1584,8 +1574,6 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
lang.as_slice().as_ptr(),
|
lang.as_slice().as_ptr(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_html_document_body_element(&self) -> bool {
|
fn is_html_document_body_element(&self) -> bool {
|
||||||
|
|
|
@ -20,3 +20,4 @@ servo_arc = { path = "../servo_arc" }
|
||||||
smallbitvec = { workspace = true }
|
smallbitvec = { workspace = true }
|
||||||
smallvec = { workspace = true }
|
smallvec = { workspace = true }
|
||||||
string_cache = { workspace = true, optional = true }
|
string_cache = { workspace = true, optional = true }
|
||||||
|
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#![crate_name = "to_shmem"]
|
#![crate_name = "to_shmem"]
|
||||||
#![crate_type = "rlib"]
|
#![crate_type = "rlib"]
|
||||||
|
|
||||||
|
extern crate thin_vec;
|
||||||
|
|
||||||
use std::alloc::Layout;
|
use std::alloc::Layout;
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
@ -31,6 +33,7 @@ use std::{isize, slice, str};
|
||||||
use servo_arc::{Arc, ThinArc};
|
use servo_arc::{Arc, ThinArc};
|
||||||
use smallbitvec::{InternalStorage, SmallBitVec};
|
use smallbitvec::{InternalStorage, SmallBitVec};
|
||||||
use smallvec::{Array, SmallVec};
|
use smallvec::{Array, SmallVec};
|
||||||
|
use thin_vec::ThinVec;
|
||||||
|
|
||||||
/// Result type for ToShmem::to_shmem.
|
/// Result type for ToShmem::to_shmem.
|
||||||
///
|
///
|
||||||
|
@ -498,6 +501,58 @@ impl<H: 'static + ToShmem, T: 'static + 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 {
|
impl ToShmem for SmallBitVec {
|
||||||
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
|
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
|
||||||
let storage = match self.clone().into_storage() {
|
let storage = match self.clone().into_storage() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue