Rewrite deduplicate_property_declarations to not require Mako.

This commit is contained in:
Simon Sapin 2017-02-23 17:44:42 +01:00
parent 6318c4b699
commit 066c5a01b2

View file

@ -178,47 +178,49 @@ pub mod animated_properties {
<%include file="/helpers/animated_properties.mako.rs" /> <%include file="/helpers/animated_properties.mako.rs" />
} }
// TODO(SimonSapin): Convert this to a syntax extension rather than a Mako template.
// Maybe submit for inclusion in libstd?
#[allow(missing_docs)] #[allow(missing_docs)]
pub mod property_bit_field { pub mod property_bit_field {
use logical_geometry::WritingMode; use logical_geometry::WritingMode;
use properties::animated_properties::TransitionProperty; use properties::animated_properties::TransitionProperty;
use properties::LonghandId;
/// A bitfield for all longhand properties, in order to quickly test whether /// A set of longhand properties
/// we've seen one of them.
pub struct PropertyBitField { pub struct PropertyBitField {
storage: [u32; (${len(data.longhands)} - 1 + 32) / 32] storage: [u32; (${len(data.longhands)} - 1 + 32) / 32]
} }
impl PropertyBitField { impl PropertyBitField {
/// Create a new `PropertyBitField`, with all the bits set to zero. /// Create an empty set
#[inline] #[inline]
pub fn new() -> PropertyBitField { pub fn new() -> PropertyBitField {
PropertyBitField { storage: [0; (${len(data.longhands)} - 1 + 32) / 32] } PropertyBitField { storage: [0; (${len(data.longhands)} - 1 + 32) / 32] }
} }
/// Return whether the given property is in the set
#[inline] #[inline]
fn get(&self, bit: usize) -> bool { pub fn contains(&self, id: LonghandId) -> bool {
let bit = id as usize;
(self.storage[bit / 32] & (1 << (bit % 32))) != 0 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
} }
/// Add the given property to the set
#[inline] #[inline]
fn set(&mut self, bit: usize) { pub fn insert(&mut self, id: LonghandId) {
self.storage[bit / 32] |= 1 << (bit % 32) let bit = id as usize;
self.storage[bit / 32] |= 1 << (bit % 32);
} }
% for i, property in enumerate(data.longhands):
% for property in data.longhands:
% if not property.derived_from: % if not property.derived_from:
#[allow(non_snake_case, missing_docs)] #[allow(non_snake_case, missing_docs)]
#[inline] #[inline]
pub fn get_${property.ident}(&self) -> bool { pub fn get_${property.ident}(&self) -> bool {
self.get(${i}) self.contains(LonghandId::${property.camel_case})
} }
#[allow(non_snake_case, missing_docs)] #[allow(non_snake_case, missing_docs)]
#[inline] #[inline]
pub fn set_${property.ident}(&mut self) { pub fn set_${property.ident}(&mut self) {
self.set(${i}) self.insert(LonghandId::${property.camel_case})
} }
% endif % endif
% if property.logical: % if property.logical:
@ -245,9 +247,9 @@ pub mod property_bit_field {
/// This function will panic if TransitionProperty::All is given. /// This function will panic if TransitionProperty::All is given.
pub fn set_transition_property_bit(&mut self, property: &TransitionProperty) { pub fn set_transition_property_bit(&mut self, property: &TransitionProperty) {
match *property { match *property {
% for i, prop in enumerate(data.longhands): % for prop in data.longhands:
% if prop.animatable: % if prop.animatable:
TransitionProperty::${prop.camel_case} => self.set(${i}), TransitionProperty::${prop.camel_case} => self.insert(LonghandId::${prop.camel_case}),
% endif % endif
% endfor % endfor
TransitionProperty::All => unreachable!("Tried to set TransitionProperty::All in a PropertyBitfield"), TransitionProperty::All => unreachable!("Tried to set TransitionProperty::All in a PropertyBitfield"),
@ -258,9 +260,9 @@ pub mod property_bit_field {
/// This function will panic if TransitionProperty::All is given. /// This function will panic if TransitionProperty::All is given.
pub fn has_transition_property_bit(&self, property: &TransitionProperty) -> bool { pub fn has_transition_property_bit(&self, property: &TransitionProperty) -> bool {
match *property { match *property {
% for i, prop in enumerate(data.longhands): % for prop in data.longhands:
% if prop.animatable: % if prop.animatable:
TransitionProperty::${prop.camel_case} => self.get(${i}), TransitionProperty::${prop.camel_case} => self.contains(LonghandId::${prop.camel_case}),
% endif % endif
% endfor % endfor
TransitionProperty::All => unreachable!("Tried to get TransitionProperty::All in a PropertyBitfield"), TransitionProperty::All => unreachable!("Tried to get TransitionProperty::All in a PropertyBitfield"),
@ -269,6 +271,44 @@ pub mod property_bit_field {
} }
} }
/// A specialized set of PropertyDeclarationId
pub struct PropertyDeclarationIdSet {
longhands: PropertyBitField,
// FIXME: Use a HashSet instead? This Vec is usually small, so linear scan might be ok.
custom: Vec<::custom_properties::Name>,
}
impl PropertyDeclarationIdSet {
/// Empty set
pub fn new() -> Self {
PropertyDeclarationIdSet {
longhands: PropertyBitField::new(),
custom: Vec::new(),
}
}
/// Returns whether the given ID is in the set
pub fn contains(&mut self, id: PropertyDeclarationId) -> bool {
match id {
PropertyDeclarationId::Longhand(id) => self.longhands.contains(id),
PropertyDeclarationId::Custom(name) => self.custom.contains(name),
}
}
/// Insert the given ID in the set
pub fn insert(&mut self, id: PropertyDeclarationId) {
match id {
PropertyDeclarationId::Longhand(id) => self.longhands.insert(id),
PropertyDeclarationId::Custom(name) => {
if !self.custom.contains(name) {
self.custom.push(name.clone())
}
}
}
}
}
% for property in data.longhands: % for property in data.longhands:
% if not property.derived_from: % if not property.derived_from:
/// Perform CSS variable substitution if needed, and execute `f` with /// Perform CSS variable substitution if needed, and execute `f` with
@ -373,59 +413,29 @@ pub mod property_bit_field {
/// The input and output are in source order /// The input and output are in source order
fn deduplicate_property_declarations(block: &mut PropertyDeclarationBlock) { fn deduplicate_property_declarations(block: &mut PropertyDeclarationBlock) {
let mut deduplicated = Vec::with_capacity(block.declarations.len()); let mut deduplicated = Vec::with_capacity(block.declarations.len());
let mut seen_normal = PropertyBitField::new(); let mut seen_normal = PropertyDeclarationIdSet::new();
let mut seen_important = PropertyBitField::new(); let mut seen_important = PropertyDeclarationIdSet::new();
let mut seen_custom_normal = Vec::new();
let mut seen_custom_important = Vec::new();
for (declaration, importance) in block.declarations.drain(..).rev() { for (declaration, importance) in block.declarations.drain(..).rev() {
match declaration { if importance.important() {
% for property in data.longhands: let id = declaration.id();
PropertyDeclaration::${property.camel_case}(..) => { if seen_important.contains(id) {
% if not property.derived_from: block.important_count -= 1;
if importance.important() { continue
if seen_important.get_${property.ident}() {
block.important_count -= 1;
continue
}
if seen_normal.get_${property.ident}() {
remove_one(&mut deduplicated, |d| {
matches!(d, &(PropertyDeclaration::${property.camel_case}(..), _))
});
}
seen_important.set_${property.ident}()
} else {
if seen_normal.get_${property.ident}() ||
seen_important.get_${property.ident}() {
continue
}
seen_normal.set_${property.ident}()
}
% else:
unreachable!();
% endif
},
% endfor
PropertyDeclaration::Custom(ref name, _) => {
if importance.important() {
if seen_custom_important.contains(name) {
block.important_count -= 1;
continue
}
if seen_custom_normal.contains(name) {
remove_one(&mut deduplicated, |d| {
matches!(d, &(PropertyDeclaration::Custom(ref n, _), _) if n == name)
});
}
seen_custom_important.push(name.clone())
} else {
if seen_custom_normal.contains(name) ||
seen_custom_important.contains(name) {
continue
}
seen_custom_normal.push(name.clone())
}
} }
if seen_normal.contains(id) {
let previous_len = deduplicated.len();
deduplicated.retain(|&(ref d, _)| PropertyDeclaration::id(d) != id);
debug_assert_eq!(deduplicated.len(), previous_len - 1);
}
seen_important.insert(id);
} else {
let id = declaration.id();
if seen_normal.contains(id) ||
seen_important.contains(id) {
continue
}
seen_normal.insert(id)
} }
deduplicated.push((declaration, importance)) deduplicated.push((declaration, importance))
} }
@ -433,13 +443,6 @@ fn deduplicate_property_declarations(block: &mut PropertyDeclarationBlock) {
block.declarations = deduplicated; block.declarations = deduplicated;
} }
#[inline]
fn remove_one<T, F: FnMut(&T) -> bool>(v: &mut Vec<T>, mut remove_this: F) {
let previous_len = v.len();
v.retain(|x| !remove_this(x));
debug_assert_eq!(v.len(), previous_len - 1);
}
/// An enum to represent a CSS Wide keyword. /// An enum to represent a CSS Wide keyword.
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CSSWideKeyword { pub enum CSSWideKeyword {
@ -653,7 +656,7 @@ impl<T: ToCss> ToCss for DeclaredValue<T> {
/// An identifier for a given property declaration, which can be either a /// An identifier for a given property declaration, which can be either a
/// longhand or a custom property. /// longhand or a custom property.
#[derive(PartialEq, Clone)] #[derive(PartialEq, Clone, Copy)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum PropertyDeclarationId<'a> { pub enum PropertyDeclarationId<'a> {
/// A longhand. /// A longhand.