style: Stop allocating when lowercasing element names for lookup in the

hash.

31% win on selector matching.
This commit is contained in:
Patrick Walton 2014-01-26 16:37:21 -08:00
parent 78e0545918
commit c6fed026ac

View file

@ -6,6 +6,7 @@ use extra::arc::Arc;
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
use std::hashmap::HashMap; use std::hashmap::HashMap;
use std::str; use std::str;
use std::to_bytes;
use servo_util::namespace; use servo_util::namespace;
@ -24,6 +25,36 @@ pub enum StylesheetOrigin {
/// The definition of whitespace per CSS Selectors Level 3 § 4. /// The definition of whitespace per CSS Selectors Level 3 § 4.
static SELECTOR_WHITESPACE: &'static [char] = &'static [' ', '\t', '\n', '\r', '\x0C']; static SELECTOR_WHITESPACE: &'static [char] = &'static [' ', '\t', '\n', '\r', '\x0C'];
/// A newtype struct used to perform lowercase ASCII comparisons without allocating a whole new
/// string.
struct LowercaseAsciiString<'a>(&'a str);
impl<'a> Equiv<~str> for LowercaseAsciiString<'a> {
fn equiv(&self, other: &~str) -> bool {
let LowercaseAsciiString(this) = *self;
this.eq_ignore_ascii_case(*other)
}
}
impl<'a> IterBytes for LowercaseAsciiString<'a> {
#[inline]
fn iter_bytes(&self, _: bool, f: to_bytes::Cb) -> bool {
for b in self.bytes() {
// FIXME(pcwalton): This is a nasty hack for performance. We temporarily violate the
// `Ascii` type's invariants by using `to_ascii_nocheck`, but it's OK as we simply
// convert to a byte afterward.
unsafe {
if !f([ b.to_ascii_nocheck().to_lower().to_byte() ]) {
return false
}
}
}
// Terminate the string with a non-UTF-8 character, to match what the built-in string
// `ToBytes` implementation does. (See `libstd/to_bytes.rs`.)
f([ 0xff ])
}
}
/// Map node attributes to Rules whose last simple selector starts with them. /// Map node attributes to Rules whose last simple selector starts with them.
/// ///
/// e.g., /// e.g.,
@ -88,8 +119,10 @@ impl SelectorMap {
match element.get_attr(&namespace::Null, "class") { match element.get_attr(&namespace::Null, "class") {
Some(ref class_attr) => { Some(ref class_attr) => {
for class in class_attr.split(SELECTOR_WHITESPACE) { for class in class_attr.split(SELECTOR_WHITESPACE) {
SelectorMap::get_matching_rules_from_hash( SelectorMap::get_matching_rules_from_hash(node,
node, &self.class_hash, class, matching_rules_list); &self.class_hash,
class,
matching_rules_list);
} }
} }
None => {} None => {}
@ -97,10 +130,10 @@ impl SelectorMap {
// HTML elements in HTML documents must be matched case-insensitively. // HTML elements in HTML documents must be matched case-insensitively.
// TODO(pradeep): Case-sensitivity depends on the document type. // TODO(pradeep): Case-sensitivity depends on the document type.
SelectorMap::get_matching_rules_from_hash(node, SelectorMap::get_matching_rules_from_hash_ignoring_case(node,
&self.element_hash, &self.element_hash,
element.get_local_name().to_ascii_lower(), element.get_local_name(),
matching_rules_list); matching_rules_list);
SelectorMap::get_matching_rules(node, SelectorMap::get_matching_rules(node,
self.universal_rules, self.universal_rules,
matching_rules_list); matching_rules_list);
@ -130,6 +163,20 @@ impl SelectorMap {
} }
} }
fn get_matching_rules_from_hash_ignoring_case<E:TElement,
N:TNode<E>>(
node: &N,
hash: &HashMap<~str,~[Rule]>,
key: &str,
matching_rules: &mut ~[Rule]) {
match hash.find_equiv(&LowercaseAsciiString(key)) {
Some(rules) => {
SelectorMap::get_matching_rules(node, *rules, matching_rules)
}
None => {}
}
}
/// Adds rules in `rules` that match `node` to the `matching_rules` list. /// Adds rules in `rules` that match `node` to the `matching_rules` list.
fn get_matching_rules<E:TElement, fn get_matching_rules<E:TElement,
N:TNode<E>>( N:TNode<E>>(