Skip to content

Commit

Permalink
Hoist specificity computation into a new private builder module.
Browse files Browse the repository at this point in the history
This patch doesn't modify any of the code because making a few things pub. I
did this first to make the next patch easier to audit.

MozReview-Commit-ID: 7PYxoS5bVGN
  • Loading branch information
bholley committed Jun 20, 2017
1 parent 1d242ad commit 1fc1d64
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 136 deletions.
141 changes: 141 additions & 0 deletions components/selectors/builder.rs
@@ -0,0 +1,141 @@
/* 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/. */

use parser::{Component, SelectorImpl, SelectorIter};
use std::cmp;
use std::ops::Add;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct SpecificityAndFlags(pub u32);

pub const HAS_PSEUDO_BIT: u32 = 1 << 30;

impl SpecificityAndFlags {
pub fn specificity(&self) -> u32 {
self.0 & !HAS_PSEUDO_BIT
}

pub fn has_pseudo_element(&self) -> bool {
(self.0 & HAS_PSEUDO_BIT) != 0
}
}

const MAX_10BIT: u32 = (1u32 << 10) - 1;

#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
struct Specificity {
id_selectors: u32,
class_like_selectors: u32,
element_selectors: u32,
}

impl Add for Specificity {
type Output = Specificity;

fn add(self, rhs: Specificity) -> Specificity {
Specificity {
id_selectors: self.id_selectors + rhs.id_selectors,
class_like_selectors:
self.class_like_selectors + rhs.class_like_selectors,
element_selectors:
self.element_selectors + rhs.element_selectors,
}
}
}

impl Default for Specificity {
fn default() -> Specificity {
Specificity {
id_selectors: 0,
class_like_selectors: 0,
element_selectors: 0,
}
}
}

impl From<u32> for Specificity {
fn from(value: u32) -> Specificity {
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
Specificity {
id_selectors: value >> 20,
class_like_selectors: (value >> 10) & MAX_10BIT,
element_selectors: value & MAX_10BIT,
}
}
}

impl From<Specificity> for u32 {
fn from(specificity: Specificity) -> u32 {
cmp::min(specificity.id_selectors, MAX_10BIT) << 20
| cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10
| cmp::min(specificity.element_selectors, MAX_10BIT)
}
}

pub fn specificity<Impl>(iter: SelectorIter<Impl>) -> u32
where Impl: SelectorImpl
{
complex_selector_specificity(iter).into()
}

fn complex_selector_specificity<Impl>(mut iter: SelectorIter<Impl>)
-> Specificity
where Impl: SelectorImpl
{
fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>,
specificity: &mut Specificity)
where Impl: SelectorImpl
{
match *simple_selector {
Component::Combinator(..) => unreachable!(),
Component::PseudoElement(..) |
Component::LocalName(..) => {
specificity.element_selectors += 1
}
Component::ID(..) => {
specificity.id_selectors += 1
}
Component::Class(..) |
Component::AttributeInNoNamespace { .. } |
Component::AttributeInNoNamespaceExists { .. } |
Component::AttributeOther(..) |

Component::FirstChild | Component::LastChild |
Component::OnlyChild | Component::Root |
Component::Empty |
Component::NthChild(..) |
Component::NthLastChild(..) |
Component::NthOfType(..) |
Component::NthLastOfType(..) |
Component::FirstOfType | Component::LastOfType |
Component::OnlyOfType |
Component::NonTSPseudoClass(..) => {
specificity.class_like_selectors += 1
}
Component::ExplicitUniversalType |
Component::ExplicitAnyNamespace |
Component::ExplicitNoNamespace |
Component::DefaultNamespace(..) |
Component::Namespace(..) => {
// Does not affect specificity
}
Component::Negation(ref negated) => {
for ss in negated.iter() {
simple_selector_specificity(&ss, specificity);
}
}
}
}

let mut specificity = Default::default();
loop {
for simple_selector in &mut iter {
simple_selector_specificity(&simple_selector, &mut specificity);
}
if iter.next_sequence().is_none() {
break;
}
}
specificity
}
1 change: 1 addition & 0 deletions components/selectors/lib.rs
Expand Up @@ -15,6 +15,7 @@ extern crate smallvec;

pub mod attr;
pub mod bloom;
mod builder;
pub mod context;
pub mod matching;
pub mod parser;
Expand Down
138 changes: 2 additions & 136 deletions components/selectors/parser.rs
Expand Up @@ -5,6 +5,8 @@
use attr::{AttrSelectorWithNamespace, ParsedAttrSelectorOperation, AttrSelectorOperator};
use attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE, NamespaceConstraint};
use bloom::BLOOM_HASH_MASK;
use builder::{HAS_PSEUDO_BIT, SpecificityAndFlags};
use builder::specificity;
use cssparser::{ParseError, BasicParseError, CompactCowStr};
use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter};
use precomputed_hash::PrecomputedHash;
Expand All @@ -13,10 +15,8 @@ use sink::Push;
use smallvec::SmallVec;
use std::ascii::AsciiExt;
use std::borrow::{Borrow, Cow};
use std::cmp;
use std::fmt::{self, Display, Debug, Write};
use std::iter::Rev;
use std::ops::Add;
use std::slice;
use visitor::SelectorVisitor;

Expand Down Expand Up @@ -266,8 +266,6 @@ impl AncestorHashes {
}
}

const HAS_PSEUDO_BIT: u32 = 1 << 30;

pub trait SelectorMethods {
type Impl: SelectorImpl;

Expand Down Expand Up @@ -370,19 +368,6 @@ pub fn namespace_empty_string<Impl: SelectorImpl>() -> Impl::NamespaceUrl {
Impl::NamespaceUrl::default()
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct SpecificityAndFlags(u32);

impl SpecificityAndFlags {
fn specificity(&self) -> u32 {
self.0 & !HAS_PSEUDO_BIT
}

fn has_pseudo_element(&self) -> bool {
(self.0 & HAS_PSEUDO_BIT) != 0
}
}

/// A Selector stores a sequence of simple selectors and combinators. The
/// iterator classes allow callers to iterate at either the raw sequence level or
/// at the level of sequences of simple selectors separated by combinators. Most
Expand Down Expand Up @@ -920,125 +905,6 @@ fn display_to_css_identifier<T: Display, W: fmt::Write>(x: &T, dest: &mut W) ->
serialize_identifier(&string, dest)
}

const MAX_10BIT: u32 = (1u32 << 10) - 1;

#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
struct Specificity {
id_selectors: u32,
class_like_selectors: u32,
element_selectors: u32,
}

impl Add for Specificity {
type Output = Specificity;

fn add(self, rhs: Specificity) -> Specificity {
Specificity {
id_selectors: self.id_selectors + rhs.id_selectors,
class_like_selectors:
self.class_like_selectors + rhs.class_like_selectors,
element_selectors:
self.element_selectors + rhs.element_selectors,
}
}
}

impl Default for Specificity {
fn default() -> Specificity {
Specificity {
id_selectors: 0,
class_like_selectors: 0,
element_selectors: 0,
}
}
}

impl From<u32> for Specificity {
fn from(value: u32) -> Specificity {
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
Specificity {
id_selectors: value >> 20,
class_like_selectors: (value >> 10) & MAX_10BIT,
element_selectors: value & MAX_10BIT,
}
}
}

impl From<Specificity> for u32 {
fn from(specificity: Specificity) -> u32 {
cmp::min(specificity.id_selectors, MAX_10BIT) << 20
| cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10
| cmp::min(specificity.element_selectors, MAX_10BIT)
}
}

fn specificity<Impl>(iter: SelectorIter<Impl>) -> u32
where Impl: SelectorImpl
{
complex_selector_specificity(iter).into()
}

fn complex_selector_specificity<Impl>(mut iter: SelectorIter<Impl>)
-> Specificity
where Impl: SelectorImpl
{
fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>,
specificity: &mut Specificity)
where Impl: SelectorImpl
{
match *simple_selector {
Component::Combinator(..) => unreachable!(),
Component::PseudoElement(..) |
Component::LocalName(..) => {
specificity.element_selectors += 1
}
Component::ID(..) => {
specificity.id_selectors += 1
}
Component::Class(..) |
Component::AttributeInNoNamespace { .. } |
Component::AttributeInNoNamespaceExists { .. } |
Component::AttributeOther(..) |

Component::FirstChild | Component::LastChild |
Component::OnlyChild | Component::Root |
Component::Empty |
Component::NthChild(..) |
Component::NthLastChild(..) |
Component::NthOfType(..) |
Component::NthLastOfType(..) |
Component::FirstOfType | Component::LastOfType |
Component::OnlyOfType |
Component::NonTSPseudoClass(..) => {
specificity.class_like_selectors += 1
}
Component::ExplicitUniversalType |
Component::ExplicitAnyNamespace |
Component::ExplicitNoNamespace |
Component::DefaultNamespace(..) |
Component::Namespace(..) => {
// Does not affect specificity
}
Component::Negation(ref negated) => {
for ss in negated.iter() {
simple_selector_specificity(&ss, specificity);
}
}
}
}

let mut specificity = Default::default();
loop {
for simple_selector in &mut iter {
simple_selector_specificity(&simple_selector, &mut specificity);
}
if iter.next_sequence().is_none() {
break;
}
}
specificity
}

/// We make this large because the result of parsing a selector is fed into a new
/// Arc-ed allocation, so any spilled vec would be a wasted allocation. Also,
/// Components are large enough that we don't have much cache locality benefit
Expand Down

0 comments on commit 1fc1d64

Please sign in to comment.