auto merge of #1566 : pcwalton/servo/fewer-allocations, r=SimonSapin

hash.

31% win on selector matching.

r? @larsbergstrom
This commit is contained in:
bors-servo 2014-01-27 16:59:18 -08:00
commit 75acf1175a

View file

@ -6,6 +6,7 @@ use extra::arc::Arc;
use std::ascii::StrAsciiExt;
use std::hashmap::HashMap;
use std::str;
use std::to_bytes;
use servo_util::namespace;
@ -24,6 +25,36 @@ pub enum StylesheetOrigin {
/// The definition of whitespace per CSS Selectors Level 3 § 4.
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.
///
/// e.g.,
@ -95,8 +126,10 @@ impl SelectorMap {
match element.get_attr(&namespace::Null, "class") {
Some(ref class_attr) => {
for class in class_attr.split(SELECTOR_WHITESPACE) {
SelectorMap::get_matching_rules_from_hash(
node, &self.class_hash, class, matching_rules_list);
SelectorMap::get_matching_rules_from_hash(node,
&self.class_hash,
class,
matching_rules_list);
}
}
None => {}
@ -104,10 +137,10 @@ impl SelectorMap {
// HTML elements in HTML documents must be matched case-insensitively.
// TODO(pradeep): Case-sensitivity depends on the document type.
SelectorMap::get_matching_rules_from_hash(node,
&self.element_hash,
element.get_local_name().to_ascii_lower(),
matching_rules_list);
SelectorMap::get_matching_rules_from_hash_ignoring_case(node,
&self.element_hash,
element.get_local_name(),
matching_rules_list);
SelectorMap::get_matching_rules(node,
self.universal_rules,
matching_rules_list);
@ -137,6 +170,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.
fn get_matching_rules<E:TElement,
N:TNode<E>>(