// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/url_matcher/substring_set_matcher.h" #include #include #include "base/logging.h" #include "base/stl_util.h" namespace url_matcher { namespace { // Compare StringPattern instances based on their string patterns. bool ComparePatterns(const StringPattern* a, const StringPattern* b) { return a->pattern() < b->pattern(); } // Given the set of patterns, compute how many nodes will the corresponding // Aho-Corasick tree have. Note that |patterns| need to be sorted. uint32 TreeSize(const std::vector& patterns) { uint32 result = 1u; // 1 for the root node. if (patterns.empty()) return result; std::vector::const_iterator last = patterns.begin(); std::vector::const_iterator current = last + 1; // For the first pattern, each letter is a label of an edge to a new node. result += (*last)->pattern().size(); // For the subsequent patterns, only count the edges which were not counted // yet. For this it suffices to test against the previous pattern, because the // patterns are sorted. for (; current != patterns.end(); ++last, ++current) { const std::string& last_pattern = (*last)->pattern(); const std::string& current_pattern = (*current)->pattern(); const uint32 prefix_bound = std::min(last_pattern.size(), current_pattern.size()); uint32 common_prefix = 0; while (common_prefix < prefix_bound && last_pattern[common_prefix] == current_pattern[common_prefix]) ++common_prefix; result += current_pattern.size() - common_prefix; } return result; } } // namespace // // SubstringSetMatcher // SubstringSetMatcher::SubstringSetMatcher() { RebuildAhoCorasickTree(SubstringPatternVector()); } SubstringSetMatcher::~SubstringSetMatcher() {} void SubstringSetMatcher::RegisterPatterns( const std::vector& patterns) { RegisterAndUnregisterPatterns(patterns, std::vector()); } void SubstringSetMatcher::UnregisterPatterns( const std::vector& patterns) { RegisterAndUnregisterPatterns(std::vector(), patterns); } void SubstringSetMatcher::RegisterAndUnregisterPatterns( const std::vector& to_register, const std::vector& to_unregister) { // Register patterns. for (std::vector::const_iterator i = to_register.begin(); i != to_register.end(); ++i) { DCHECK(patterns_.find((*i)->id()) == patterns_.end()); patterns_[(*i)->id()] = *i; } // Unregister patterns for (std::vector::const_iterator i = to_unregister.begin(); i != to_unregister.end(); ++i) { patterns_.erase((*i)->id()); } // Now we compute the total number of tree nodes needed. SubstringPatternVector sorted_patterns; sorted_patterns.resize(patterns_.size()); size_t next = 0; for (SubstringPatternMap::const_iterator i = patterns_.begin(); i != patterns_.end(); ++i, ++next) { sorted_patterns[next] = i->second; } std::sort(sorted_patterns.begin(), sorted_patterns.end(), ComparePatterns); tree_.reserve(TreeSize(sorted_patterns)); RebuildAhoCorasickTree(sorted_patterns); } bool SubstringSetMatcher::Match(const std::string& text, std::set* matches) const { const size_t old_number_of_matches = matches->size(); // Handle patterns matching the empty string. matches->insert(tree_[0].matches().begin(), tree_[0].matches().end()); uint32 current_node = 0; for (std::string::const_iterator i = text.begin(); i != text.end(); ++i) { uint32 edge_from_current = tree_[current_node].GetEdge(*i); while (edge_from_current == AhoCorasickNode::kNoSuchEdge && current_node != 0) { current_node = tree_[current_node].failure(); edge_from_current = tree_[current_node].GetEdge(*i); } if (edge_from_current != AhoCorasickNode::kNoSuchEdge) { current_node = edge_from_current; matches->insert(tree_[current_node].matches().begin(), tree_[current_node].matches().end()); } else { DCHECK_EQ(0u, current_node); } } return old_number_of_matches != matches->size(); } bool SubstringSetMatcher::IsEmpty() const { // An empty tree consists of only the root node. return patterns_.empty() && tree_.size() == 1u; } void SubstringSetMatcher::RebuildAhoCorasickTree( const SubstringPatternVector& sorted_patterns) { tree_.clear(); // Initialize root note of tree. AhoCorasickNode root; root.set_failure(0); tree_.push_back(root); // Insert all patterns. for (SubstringPatternVector::const_iterator i = sorted_patterns.begin(); i != sorted_patterns.end(); ++i) { InsertPatternIntoAhoCorasickTree(*i); } CreateFailureEdges(); } void SubstringSetMatcher::InsertPatternIntoAhoCorasickTree( const StringPattern* pattern) { const std::string& text = pattern->pattern(); const std::string::const_iterator text_end = text.end(); // Iterators on the tree and the text. uint32 current_node = 0; std::string::const_iterator i = text.begin(); // Follow existing paths for as long as possible. while (i != text_end) { uint32 edge_from_current = tree_[current_node].GetEdge(*i); if (edge_from_current == AhoCorasickNode::kNoSuchEdge) break; current_node = edge_from_current; ++i; } // Create new nodes if necessary. while (i != text_end) { tree_.push_back(AhoCorasickNode()); tree_[current_node].SetEdge(*i, tree_.size() - 1); current_node = tree_.size() - 1; ++i; } // Register match. tree_[current_node].AddMatch(pattern->id()); } void SubstringSetMatcher::CreateFailureEdges() { typedef AhoCorasickNode::Edges Edges; std::queue queue; AhoCorasickNode& root = tree_[0]; root.set_failure(0); const Edges& root_edges = root.edges(); for (Edges::const_iterator e = root_edges.begin(); e != root_edges.end(); ++e) { const uint32& leads_to = e->second; tree_[leads_to].set_failure(0); queue.push(leads_to); } while (!queue.empty()) { AhoCorasickNode& current_node = tree_[queue.front()]; queue.pop(); for (Edges::const_iterator e = current_node.edges().begin(); e != current_node.edges().end(); ++e) { const char& edge_label = e->first; const uint32& leads_to = e->second; queue.push(leads_to); uint32 failure = current_node.failure(); uint32 edge_from_failure = tree_[failure].GetEdge(edge_label); while (edge_from_failure == AhoCorasickNode::kNoSuchEdge && failure != 0) { failure = tree_[failure].failure(); edge_from_failure = tree_[failure].GetEdge(edge_label); } const uint32 follow_in_case_of_failure = edge_from_failure != AhoCorasickNode::kNoSuchEdge ? edge_from_failure : 0; tree_[leads_to].set_failure(follow_in_case_of_failure); tree_[leads_to].AddMatches(tree_[follow_in_case_of_failure].matches()); } } } const uint32 SubstringSetMatcher::AhoCorasickNode::kNoSuchEdge = 0xFFFFFFFF; SubstringSetMatcher::AhoCorasickNode::AhoCorasickNode() : failure_(kNoSuchEdge) {} SubstringSetMatcher::AhoCorasickNode::~AhoCorasickNode() {} SubstringSetMatcher::AhoCorasickNode::AhoCorasickNode( const SubstringSetMatcher::AhoCorasickNode& other) : edges_(other.edges_), failure_(other.failure_), matches_(other.matches_) {} SubstringSetMatcher::AhoCorasickNode& SubstringSetMatcher::AhoCorasickNode::operator=( const SubstringSetMatcher::AhoCorasickNode& other) { edges_ = other.edges_; failure_ = other.failure_; matches_ = other.matches_; return *this; } uint32 SubstringSetMatcher::AhoCorasickNode::GetEdge(char c) const { Edges::const_iterator i = edges_.find(c); return i == edges_.end() ? kNoSuchEdge : i->second; } void SubstringSetMatcher::AhoCorasickNode::SetEdge(char c, uint32 node) { edges_[c] = node; } void SubstringSetMatcher::AhoCorasickNode::AddMatch(StringPattern::ID id) { matches_.insert(id); } void SubstringSetMatcher::AhoCorasickNode::AddMatches( const SubstringSetMatcher::AhoCorasickNode::Matches& matches) { matches_.insert(matches.begin(), matches.end()); } } // namespace url_matcher