mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
style: Avoid some allocations in selector serialization.
The allocations in display_to_css_identifier show up in the profiles of bug 1675628. Differential Revision: https://phabricator.services.mozilla.com/D97856
This commit is contained in:
parent
e9c1d490a9
commit
484cf5771b
2 changed files with 51 additions and 53 deletions
|
@ -10,15 +10,15 @@ use crate::builder::{SelectorBuilder, SelectorFlags, SpecificityAndFlags};
|
||||||
use crate::context::QuirksMode;
|
use crate::context::QuirksMode;
|
||||||
use crate::sink::Push;
|
use crate::sink::Push;
|
||||||
pub use crate::visitor::SelectorVisitor;
|
pub use crate::visitor::SelectorVisitor;
|
||||||
use cssparser::{parse_nth, serialize_identifier};
|
use cssparser::parse_nth;
|
||||||
use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind};
|
use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind};
|
||||||
use cssparser::{CowRcStr, Delimiter, SourceLocation};
|
use cssparser::{CowRcStr, Delimiter, SourceLocation};
|
||||||
use cssparser::{CssStringWriter, Parser as CssParser, ToCss, Token};
|
use cssparser::{Parser as CssParser, ToCss, Token};
|
||||||
use precomputed_hash::PrecomputedHash;
|
use precomputed_hash::PrecomputedHash;
|
||||||
use servo_arc::ThinArc;
|
use servo_arc::ThinArc;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::borrow::{Borrow, Cow};
|
use std::borrow::{Borrow, Cow};
|
||||||
use std::fmt::{self, Debug, Display, Write};
|
use std::fmt::{self, Debug};
|
||||||
use std::iter::Rev;
|
use std::iter::Rev;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
|
@ -197,8 +197,6 @@ macro_rules! with_all_bounds {
|
||||||
type ExtraMatchingData: Sized + Default + 'static;
|
type ExtraMatchingData: Sized + Default + 'static;
|
||||||
type AttrValue: $($InSelector)*;
|
type AttrValue: $($InSelector)*;
|
||||||
type Identifier: $($InSelector)*;
|
type Identifier: $($InSelector)*;
|
||||||
type ClassName: $($InSelector)*;
|
|
||||||
type PartName: $($InSelector)*;
|
|
||||||
type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>;
|
type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>;
|
||||||
type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl>;
|
type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl>;
|
||||||
type NamespacePrefix: $($InSelector)* + Default;
|
type NamespacePrefix: $($InSelector)* + Default;
|
||||||
|
@ -218,7 +216,7 @@ macro_rules! with_all_bounds {
|
||||||
macro_rules! with_bounds {
|
macro_rules! with_bounds {
|
||||||
( [ $( $CommonBounds: tt )* ] [ $( $FromStr: tt )* ]) => {
|
( [ $( $CommonBounds: tt )* ] [ $( $FromStr: tt )* ]) => {
|
||||||
with_all_bounds! {
|
with_all_bounds! {
|
||||||
[$($CommonBounds)* + $($FromStr)* + Display]
|
[$($CommonBounds)* + $($FromStr)* + ToCss]
|
||||||
[$($CommonBounds)*]
|
[$($CommonBounds)*]
|
||||||
[$($FromStr)*]
|
[$($FromStr)*]
|
||||||
}
|
}
|
||||||
|
@ -462,7 +460,6 @@ fn collect_ancestor_hashes<Impl: SelectorImpl>(
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
Impl::Identifier: PrecomputedHash,
|
Impl::Identifier: PrecomputedHash,
|
||||||
Impl::ClassName: PrecomputedHash,
|
|
||||||
Impl::LocalName: PrecomputedHash,
|
Impl::LocalName: PrecomputedHash,
|
||||||
Impl::NamespaceUrl: PrecomputedHash,
|
Impl::NamespaceUrl: PrecomputedHash,
|
||||||
{
|
{
|
||||||
|
@ -514,7 +511,6 @@ impl AncestorHashes {
|
||||||
pub fn new<Impl: SelectorImpl>(selector: &Selector<Impl>, quirks_mode: QuirksMode) -> Self
|
pub fn new<Impl: SelectorImpl>(selector: &Selector<Impl>, quirks_mode: QuirksMode) -> Self
|
||||||
where
|
where
|
||||||
Impl::Identifier: PrecomputedHash,
|
Impl::Identifier: PrecomputedHash,
|
||||||
Impl::ClassName: PrecomputedHash,
|
|
||||||
Impl::LocalName: PrecomputedHash,
|
Impl::LocalName: PrecomputedHash,
|
||||||
Impl::NamespaceUrl: PrecomputedHash,
|
Impl::NamespaceUrl: PrecomputedHash,
|
||||||
{
|
{
|
||||||
|
@ -593,7 +589,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parts(&self) -> Option<&[Impl::PartName]> {
|
pub fn parts(&self) -> Option<&[Impl::Identifier]> {
|
||||||
if !self.is_part() {
|
if !self.is_part() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -1012,7 +1008,7 @@ pub enum Component<Impl: SelectorImpl> {
|
||||||
LocalName(LocalName<Impl>),
|
LocalName(LocalName<Impl>),
|
||||||
|
|
||||||
ID(#[shmem(field_bound)] Impl::Identifier),
|
ID(#[shmem(field_bound)] Impl::Identifier),
|
||||||
Class(#[shmem(field_bound)] Impl::ClassName),
|
Class(#[shmem(field_bound)] Impl::Identifier),
|
||||||
|
|
||||||
AttributeInNoNamespaceExists {
|
AttributeInNoNamespaceExists {
|
||||||
#[shmem(field_bound)]
|
#[shmem(field_bound)]
|
||||||
|
@ -1061,7 +1057,7 @@ pub enum Component<Impl: SelectorImpl> {
|
||||||
Slotted(Selector<Impl>),
|
Slotted(Selector<Impl>),
|
||||||
/// The `::part` pseudo-element.
|
/// The `::part` pseudo-element.
|
||||||
/// https://drafts.csswg.org/css-shadow-parts/#part
|
/// https://drafts.csswg.org/css-shadow-parts/#part
|
||||||
Part(#[shmem(field_bound)] Box<[Impl::PartName]>),
|
Part(#[shmem(field_bound)] Box<[Impl::Identifier]>),
|
||||||
/// The `:host` pseudo-class:
|
/// The `:host` pseudo-class:
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-scoping/#host-selector
|
/// https://drafts.csswg.org/css-scoping/#host-selector
|
||||||
|
@ -1470,18 +1466,18 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
}
|
}
|
||||||
display_to_css_identifier(name, dest)?;
|
name.to_css(dest)?;
|
||||||
}
|
}
|
||||||
dest.write_char(')')
|
dest.write_char(')')
|
||||||
},
|
},
|
||||||
PseudoElement(ref p) => p.to_css(dest),
|
PseudoElement(ref p) => p.to_css(dest),
|
||||||
ID(ref s) => {
|
ID(ref s) => {
|
||||||
dest.write_char('#')?;
|
dest.write_char('#')?;
|
||||||
display_to_css_identifier(s, dest)
|
s.to_css(dest)
|
||||||
},
|
},
|
||||||
Class(ref s) => {
|
Class(ref s) => {
|
||||||
dest.write_char('.')?;
|
dest.write_char('.')?;
|
||||||
display_to_css_identifier(s, dest)
|
s.to_css(dest)
|
||||||
},
|
},
|
||||||
LocalName(ref s) => s.to_css(dest),
|
LocalName(ref s) => s.to_css(dest),
|
||||||
ExplicitUniversalType => dest.write_char('*'),
|
ExplicitUniversalType => dest.write_char('*'),
|
||||||
|
@ -1490,13 +1486,13 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
||||||
ExplicitNoNamespace => dest.write_char('|'),
|
ExplicitNoNamespace => dest.write_char('|'),
|
||||||
ExplicitAnyNamespace => dest.write_str("*|"),
|
ExplicitAnyNamespace => dest.write_str("*|"),
|
||||||
Namespace(ref prefix, _) => {
|
Namespace(ref prefix, _) => {
|
||||||
display_to_css_identifier(prefix, dest)?;
|
prefix.to_css(dest)?;
|
||||||
dest.write_char('|')
|
dest.write_char('|')
|
||||||
},
|
},
|
||||||
|
|
||||||
AttributeInNoNamespaceExists { ref local_name, .. } => {
|
AttributeInNoNamespaceExists { ref local_name, .. } => {
|
||||||
dest.write_char('[')?;
|
dest.write_char('[')?;
|
||||||
display_to_css_identifier(local_name, dest)?;
|
local_name.to_css(dest)?;
|
||||||
dest.write_char(']')
|
dest.write_char(']')
|
||||||
},
|
},
|
||||||
AttributeInNoNamespace {
|
AttributeInNoNamespace {
|
||||||
|
@ -1507,10 +1503,10 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
dest.write_char('[')?;
|
dest.write_char('[')?;
|
||||||
display_to_css_identifier(local_name, dest)?;
|
local_name.to_css(dest)?;
|
||||||
operator.to_css(dest)?;
|
operator.to_css(dest)?;
|
||||||
dest.write_char('"')?;
|
dest.write_char('"')?;
|
||||||
write!(CssStringWriter::new(dest), "{}", value)?;
|
value.to_css(dest)?;
|
||||||
dest.write_char('"')?;
|
dest.write_char('"')?;
|
||||||
match case_sensitivity {
|
match case_sensitivity {
|
||||||
ParsedCaseSensitivity::CaseSensitive |
|
ParsedCaseSensitivity::CaseSensitive |
|
||||||
|
@ -1582,13 +1578,13 @@ impl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {
|
||||||
dest.write_char('[')?;
|
dest.write_char('[')?;
|
||||||
match self.namespace {
|
match self.namespace {
|
||||||
Some(NamespaceConstraint::Specific((ref prefix, _))) => {
|
Some(NamespaceConstraint::Specific((ref prefix, _))) => {
|
||||||
display_to_css_identifier(prefix, dest)?;
|
prefix.to_css(dest)?;
|
||||||
dest.write_char('|')?
|
dest.write_char('|')?
|
||||||
},
|
},
|
||||||
Some(NamespaceConstraint::Any) => dest.write_str("*|")?,
|
Some(NamespaceConstraint::Any) => dest.write_str("*|")?,
|
||||||
None => {},
|
None => {},
|
||||||
}
|
}
|
||||||
display_to_css_identifier(&self.local_name, dest)?;
|
self.local_name.to_css(dest)?;
|
||||||
match self.operation {
|
match self.operation {
|
||||||
ParsedAttrSelectorOperation::Exists => {},
|
ParsedAttrSelectorOperation::Exists => {},
|
||||||
ParsedAttrSelectorOperation::WithValue {
|
ParsedAttrSelectorOperation::WithValue {
|
||||||
|
@ -1598,7 +1594,7 @@ impl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {
|
||||||
} => {
|
} => {
|
||||||
operator.to_css(dest)?;
|
operator.to_css(dest)?;
|
||||||
dest.write_char('"')?;
|
dest.write_char('"')?;
|
||||||
write!(CssStringWriter::new(dest), "{}", expected_value)?;
|
expected_value.to_css(dest)?;
|
||||||
dest.write_char('"')?;
|
dest.write_char('"')?;
|
||||||
match case_sensitivity {
|
match case_sensitivity {
|
||||||
ParsedCaseSensitivity::CaseSensitive |
|
ParsedCaseSensitivity::CaseSensitive |
|
||||||
|
@ -1617,29 +1613,10 @@ impl<Impl: SelectorImpl> ToCss for LocalName<Impl> {
|
||||||
where
|
where
|
||||||
W: fmt::Write,
|
W: fmt::Write,
|
||||||
{
|
{
|
||||||
display_to_css_identifier(&self.name, dest)
|
self.name.to_css(dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize the output of Display as a CSS identifier
|
|
||||||
fn display_to_css_identifier<T: Display, W: fmt::Write>(x: &T, dest: &mut W) -> fmt::Result {
|
|
||||||
// FIXME(SimonSapin): it is possible to avoid this heap allocation
|
|
||||||
// by creating a stream adapter like cssparser::CssStringWriter
|
|
||||||
// that holds and writes to `&mut W` and itself implements `fmt::Write`.
|
|
||||||
//
|
|
||||||
// I haven’t done this yet because it would require somewhat complex and fragile state machine
|
|
||||||
// to support in `fmt::Write::write_char` cases that,
|
|
||||||
// in `serialize_identifier` (which has the full value as a `&str` slice),
|
|
||||||
// can be expressed as
|
|
||||||
// `string.starts_with("--")`, `string == "-"`, `string.starts_with("-")`, etc.
|
|
||||||
//
|
|
||||||
// And I don’t even know if this would be a performance win: jemalloc is good at what it does
|
|
||||||
// and the state machine might be slower than `serialize_identifier` as currently written.
|
|
||||||
let string = x.to_string();
|
|
||||||
|
|
||||||
serialize_identifier(&string, dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build up a Selector.
|
/// Build up a Selector.
|
||||||
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
|
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
|
||||||
///
|
///
|
||||||
|
@ -1810,7 +1787,7 @@ enum SimpleSelectorParseResult<Impl: SelectorImpl> {
|
||||||
SimpleSelector(Component<Impl>),
|
SimpleSelector(Component<Impl>),
|
||||||
PseudoElement(Impl::PseudoElement),
|
PseudoElement(Impl::PseudoElement),
|
||||||
SlottedPseudo(Selector<Impl>),
|
SlottedPseudo(Selector<Impl>),
|
||||||
PartPseudo(Box<[Impl::PartName]>),
|
PartPseudo(Box<[Impl::Identifier]>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -2605,10 +2582,8 @@ pub mod tests {
|
||||||
|
|
||||||
impl SelectorImpl for DummySelectorImpl {
|
impl SelectorImpl for DummySelectorImpl {
|
||||||
type ExtraMatchingData = ();
|
type ExtraMatchingData = ();
|
||||||
type AttrValue = DummyAtom;
|
type AttrValue = DummyAttrValue;
|
||||||
type Identifier = DummyAtom;
|
type Identifier = DummyAtom;
|
||||||
type ClassName = DummyAtom;
|
|
||||||
type PartName = DummyAtom;
|
|
||||||
type LocalName = DummyAtom;
|
type LocalName = DummyAtom;
|
||||||
type NamespaceUrl = DummyAtom;
|
type NamespaceUrl = DummyAtom;
|
||||||
type NamespacePrefix = DummyAtom;
|
type NamespacePrefix = DummyAtom;
|
||||||
|
@ -2618,12 +2593,35 @@ pub mod tests {
|
||||||
type PseudoElement = PseudoElement;
|
type PseudoElement = PseudoElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||||
|
pub struct DummyAttrValue(String);
|
||||||
|
|
||||||
|
impl ToCss for DummyAttrValue {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: fmt::Write,
|
||||||
|
{
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
write!(cssparser::CssStringWriter::new(dest), "{}", &self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for DummyAttrValue {
|
||||||
|
fn from(string: &'a str) -> Self {
|
||||||
|
Self(string.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||||
pub struct DummyAtom(String);
|
pub struct DummyAtom(String);
|
||||||
|
|
||||||
impl fmt::Display for DummyAtom {
|
impl ToCss for DummyAtom {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
<String as fmt::Display>::fmt(&self.0, fmt)
|
where
|
||||||
|
W: fmt::Write,
|
||||||
|
{
|
||||||
|
serialize_identifier(&self.0, dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3108,7 +3106,7 @@ pub mod tests {
|
||||||
vec![Component::AttributeInNoNamespace {
|
vec![Component::AttributeInNoNamespace {
|
||||||
local_name: DummyAtom::from("attr"),
|
local_name: DummyAtom::from("attr"),
|
||||||
operator: AttrSelectorOperator::DashMatch,
|
operator: AttrSelectorOperator::DashMatch,
|
||||||
value: DummyAtom::from("foo"),
|
value: DummyAttrValue::from("foo"),
|
||||||
never_matches: false,
|
never_matches: false,
|
||||||
case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
|
case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -113,7 +113,7 @@ pub trait Element: Sized + Clone + Debug {
|
||||||
|
|
||||||
fn has_class(
|
fn has_class(
|
||||||
&self,
|
&self,
|
||||||
name: &<Self::Impl as SelectorImpl>::ClassName,
|
name: &<Self::Impl as SelectorImpl>::Identifier,
|
||||||
case_sensitivity: CaseSensitivity,
|
case_sensitivity: CaseSensitivity,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
|
|
||||||
|
@ -121,10 +121,10 @@ pub trait Element: Sized + Clone + Debug {
|
||||||
/// direction, that is, in an outer-tree -> inner-tree direction.
|
/// direction, that is, in an outer-tree -> inner-tree direction.
|
||||||
fn imported_part(
|
fn imported_part(
|
||||||
&self,
|
&self,
|
||||||
name: &<Self::Impl as SelectorImpl>::PartName,
|
name: &<Self::Impl as SelectorImpl>::Identifier,
|
||||||
) -> Option<<Self::Impl as SelectorImpl>::PartName>;
|
) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
|
||||||
|
|
||||||
fn is_part(&self, name: &<Self::Impl as SelectorImpl>::PartName) -> bool;
|
fn is_part(&self, name: &<Self::Impl as SelectorImpl>::Identifier) -> bool;
|
||||||
|
|
||||||
/// Returns whether this element matches `:empty`.
|
/// Returns whether this element matches `:empty`.
|
||||||
///
|
///
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue