style: Document the bloom filter.

This commit is contained in:
Emilio Cobos Álvarez 2016-12-31 14:18:21 +01:00
parent eb90004f5a
commit 30d010342a
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C

View file

@ -5,10 +5,43 @@
//! The style bloom filter is used as an optimization when matching deep //! The style bloom filter is used as an optimization when matching deep
//! descendant selectors. //! descendant selectors.
#![deny(missing_docs)]
use dom::{SendElement, TElement}; use dom::{SendElement, TElement};
use matching::MatchMethods; use matching::MatchMethods;
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
/// A struct that allows us to fast-reject deep descendant selectors avoiding
/// selector-matching.
///
/// This is implemented using a counting bloom filter, and it's a standard
/// optimization. See Gecko's `AncestorFilter`, and Blink's and WebKit's
/// `SelectorFilter`.
///
/// The constraints for Servo's style system are a bit different compared to
/// traditional style systems given Servo does a parallel breadth-first
/// traversal instead of a sequential depth-first traversal.
///
/// This implies that we need to track a bit more state than other browsers to
/// ensure we're doing the correct thing during the traversal, and being able to
/// apply this optimization effectively.
///
/// Concretely, we have a bloom filter instance per worker thread, and we track
/// the current DOM depth in order to find a common ancestor when it doesn't
/// match the previous element we've styled.
///
/// This is usually a pretty fast operation (we use to be one level deeper than
/// the previous one), but in the case of work-stealing, we may needed to push
/// and pop multiple elements.
///
/// See the `insert_parents_recovering`, where most of the magic happens.
///
/// Regarding thread-safety, this struct is safe because:
///
/// * We clear this after a restyle.
/// * The DOM shape and attributes (and every other thing we access here) are
/// immutable during a restyle.
///
pub struct StyleBloom<E: TElement> { pub struct StyleBloom<E: TElement> {
/// The bloom filter per se. /// The bloom filter per se.
filter: Box<BloomFilter>, filter: Box<BloomFilter>,
@ -18,6 +51,7 @@ pub struct StyleBloom<E: TElement> {
} }
impl<E: TElement> StyleBloom<E> { impl<E: TElement> StyleBloom<E> {
/// Create an empty `StyleBloom`.
pub fn new() -> Self { pub fn new() -> Self {
StyleBloom { StyleBloom {
filter: Box::new(BloomFilter::new()), filter: Box::new(BloomFilter::new()),
@ -25,19 +59,14 @@ impl<E: TElement> StyleBloom<E> {
} }
} }
/// Return the bloom filter used properly by the `selectors` crate.
pub fn filter(&self) -> &BloomFilter { pub fn filter(&self) -> &BloomFilter {
&*self.filter &*self.filter
} }
pub fn maybe_pop(&mut self, element: E) {
if self.elements.last().map(|el| **el) == Some(element) {
self.pop().unwrap();
}
}
/// Push an element to the bloom filter, knowing that it's a child of the /// Push an element to the bloom filter, knowing that it's a child of the
/// last element parent. /// last element parent.
pub fn push(&mut self, element: E) { fn push(&mut self, element: E) {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
if self.elements.is_empty() { if self.elements.is_empty() {
assert!(element.parent_element().is_none()); assert!(element.parent_element().is_none());
@ -78,6 +107,8 @@ impl<E: TElement> StyleBloom<E> {
/// In debug builds, asserts that all the parents of `element` are in the /// In debug builds, asserts that all the parents of `element` are in the
/// bloom filter. /// bloom filter.
///
/// Goes away in release builds.
pub fn assert_complete(&self, mut element: E) { pub fn assert_complete(&self, mut element: E) {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
let mut checked = 0; let mut checked = 0;
@ -96,7 +127,8 @@ impl<E: TElement> StyleBloom<E> {
/// Gets the element depth in the dom, to make it efficient, or if not /// Gets the element depth in the dom, to make it efficient, or if not
/// provided always rebuilds the filter from scratch. /// provided always rebuilds the filter from scratch.
/// ///
/// Returns the new bloom filter depth. /// Returns the new bloom filter depth, that the traversal code is
/// responsible to keep around if it wants to get an effective filter.
pub fn insert_parents_recovering(&mut self, pub fn insert_parents_recovering(&mut self,
element: E, element: E,
element_depth: Option<usize>) element_depth: Option<usize>)