Source/WebCore/ChangeLog

 12015-04-22 Alex Christensen <achristensen@webkit.org>
 2
 3 Use less memory when compiling content extensions.
 4 https://bugs.webkit.org/show_bug.cgi?id=144051
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 No change in functionality, correctness already covered by existing tests.
 9
 10 * contentextensions/CombinedURLFilters.cpp:
 11 (WebCore::ContentExtensions::recursiveMemoryUsed):
 12 (WebCore::ContentExtensions::CombinedURLFilters::memoryUsed):
 13 * contentextensions/CombinedURLFilters.h:
 14 * contentextensions/ContentExtensionCompiler.cpp:
 15 (WebCore::ContentExtensions::compileRuleList):
 16 * contentextensions/ContentExtensionsDebugging.h:
 17 * contentextensions/DFA.cpp:
 18 (WebCore::ContentExtensions::DFA::DFA):
 19 (WebCore::ContentExtensions::DFA::memoryUsed):
 20 (WebCore::ContentExtensions::DFANode::actions):
 21 (WebCore::ContentExtensions::DFANode::transitions):
 22 (WebCore::ContentExtensions::DFANode::fallbackTransitionDestination):
 23 (WebCore::ContentExtensions::DFANode::addFallbackTransition):
 24 (WebCore::ContentExtensions::DFANode::containsTransition):
 25 (WebCore::ContentExtensions::DFA::minimize):
 26 (WebCore::ContentExtensions::DFA::operator=): Deleted.
 27 * contentextensions/DFA.h:
 28 * contentextensions/DFABytecodeCompiler.cpp:
 29 (WebCore::ContentExtensions::DFABytecodeCompiler::emitAppendAction):
 30 (WebCore::ContentExtensions::DFABytecodeCompiler::emitTestFlagsAndAppendAction):
 31 (WebCore::ContentExtensions::DFABytecodeCompiler::emitJump):
 32 (WebCore::ContentExtensions::DFABytecodeCompiler::emitCheckValue):
 33 (WebCore::ContentExtensions::DFABytecodeCompiler::emitCheckValueRange):
 34 (WebCore::ContentExtensions::DFABytecodeCompiler::emitTerminate):
 35 (WebCore::ContentExtensions::DFABytecodeCompiler::compileNode):
 36 (WebCore::ContentExtensions::DFABytecodeCompiler::compileNodeTransitions):
 37 (WebCore::ContentExtensions::DFABytecodeCompiler::compile):
 38 * contentextensions/DFAMinimizer.cpp:
 39 (WebCore::ContentExtensions::DFAMinimizer::minimize):
 40 * contentextensions/DFAMinimizer.h:
 41 * contentextensions/DFANode.h:
 42 (WebCore::ContentExtensions::DFANode::DFANode):
 43 (WebCore::ContentExtensions::DFANode::isKilled):
 44 (WebCore::ContentExtensions::DFANode::hasFallbackTransition):
 45 (WebCore::ContentExtensions::DFANode::hasActions):
 46 (WebCore::ContentExtensions::DFANode::transitionsLength):
 47 (WebCore::ContentExtensions::DFANode::setActions):
 48 (WebCore::ContentExtensions::DFANode::setTransitions):
 49 (WebCore::ContentExtensions::DFANode::transitionsStart):
 50 * contentextensions/NFA.cpp:
 51 (WebCore::ContentExtensions::NFA::memoryUsed):
 52 * contentextensions/NFA.h:
 53 * contentextensions/NFAToDFA.cpp:
 54 (WebCore::ContentExtensions::NodeIdSetToUniqueNodeIdSetSource::NodeIdSetToUniqueNodeIdSetSource):
 55 (WebCore::ContentExtensions::NodeIdSetToUniqueNodeIdSetTranslator::translate):
 56 (WebCore::ContentExtensions::getOrCreateDFANode):
 57 (WebCore::ContentExtensions::NFAToDFA::convert):
 58
1592015-04-22 Eric Carlson <eric.carlson@apple.com>
260
361 Unreviewed post-review clean up after r183096.
183114

Source/WebCore/contentextensions/CombinedURLFilters.cpp

@@struct PrefixTreeVertex {
4343 bool inVariableLengthPrefix { false };
4444};
4545
 46static size_t recursiveMemoryUsed(const std::unique_ptr<PrefixTreeVertex>& node)
 47{
 48 size_t size = sizeof(PrefixTreeVertex)
 49 + node->edges.capacity() * sizeof(std::pair<Term, std::unique_ptr<PrefixTreeVertex>>)
 50 + node->finalActions.capacity() * sizeof(uint64_t);
 51 for (const auto& child : node->edges.values())
 52 size += recursiveMemoryUsed(child);
 53 return size;
 54}
 55
 56size_t CombinedURLFilters::memoryUsed() const
 57{
 58 return recursiveMemoryUsed(m_prefixTreeRoot);
 59}
 60
4661CombinedURLFilters::CombinedURLFilters()
4762 : m_prefixTreeRoot(std::make_unique<PrefixTreeVertex>())
4863{
183021

Source/WebCore/contentextensions/CombinedURLFilters.h

@@public:
4747 Vector<NFA> createNFAs() const;
4848 void clear();
4949
 50 size_t memoryUsed() const;
 51
5052private:
5153 std::unique_ptr<PrefixTreeVertex> m_prefixTreeRoot;
5254};
183021

Source/WebCore/contentextensions/ContentExtensionCompiler.cpp

@@std::error_code compileRuleList(ContentE
134134 Vector<SerializedActionByte> actions;
135135 Vector<unsigned> actionLocations = serializeActions(parsedRuleList, actions);
136136 client.writeActions(WTF::move(actions));
 137 LOG_LARGE_STRUCTURES(actions, actions.size() * sizeof(SerializedActionByte));
137138 actions.clear();
138139
139140 HashSet<uint64_t, DefaultHash<uint64_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> universalActionLocations;

@@std::error_code compileRuleList(ContentE
164165 if (contentExtensionRule.action().type() == ActionType::IgnorePreviousRules)
165166 ignorePreviousRulesSeen = true;
166167 }
 168 LOG_LARGE_STRUCTURES(parsedRuleList, parsedRuleList.size() * sizeof(ContentExtensionRule)); // Doesn't include strings.
 169 LOG_LARGE_STRUCTURES(actionLocations, actionLocations.size() * sizeof(unsigned));
167170 parsedRuleList.clear();
168171 actionLocations.clear();
169172

@@std::error_code compileRuleList(ContentE
177180#endif
178181
179182 Vector<NFA> nfas = combinedURLFilters.createNFAs();
 183 LOG_LARGE_STRUCTURES(combinedURLFilters, combinedURLFilters.memoryUsed());
180184 combinedURLFilters.clear();
181185 if (!nfas.size() && universalActionLocations.size())
182186 nfas.append(NFA());

@@std::error_code compileRuleList(ContentE
204208 double dfaBuildTimeStart = monotonicallyIncreasingTime();
205209#endif
206210 DFA dfa = NFAToDFA::convert(nfas[i]);
 211 LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
207212#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
208213 double dfaBuildTimeEnd = monotonicallyIncreasingTime();
209214 dataLogF(" Time spent building the DFA %zu: %f\n", i, (dfaBuildTimeEnd - dfaBuildTimeStart));

@@std::error_code compileRuleList(ContentE
223228 dfa.debugPrintDot();
224229#endif
225230
226  ASSERT_WITH_MESSAGE(!dfa.nodeAt(dfa.root()).actions.size(), "All actions on the DFA root should come from regular expressions that match everything.");
227231 if (!i) {
228232 // Put all the universal actions on the first DFA.
 233 unsigned actionsStart = dfa.actions.size();
229234 for (uint64_t actionLocation : universalActionLocations)
230  dfa.nodeAt(dfa.root()).actions.append(actionLocation);
 235 dfa.actions.append(actionLocation);
 236 unsigned actionsEnd = dfa.actions.size();
 237 unsigned actionsLength = actionsEnd - actionsStart;
 238 RELEASE_ASSERT_WITH_MESSAGE(actionsLength < std::numeric_limits<uint16_t>::max(), "Too many uncombined actions that match everything");
 239 dfa.nodes[dfa.root].setActions(actionsStart, static_cast<uint16_t>(actionsLength));
231240 }
232241 DFABytecodeCompiler compiler(dfa, bytecode);
233242 compiler.compile();
234243 }
 244 LOG_LARGE_STRUCTURES(universalActionLocations, universalActionLocations.size() * sizeof(unsigned));
235245 universalActionLocations.clear();
236246
237247#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING

@@std::error_code compileRuleList(ContentE
240250 dataLogF(" Bytecode size %zu\n", bytecode.size());
241251 dataLogF(" DFA count %zu\n", nfas.size());
242252#endif
 253 size_t nfaMemoryUsed = 0;
 254 for (const NFA& nfa : nfas)
 255 nfaMemoryUsed += sizeof(NFA) + nfa.memoryUsed();
 256 LOG_LARGE_STRUCTURES(nfas, nfaMemoryUsed);
243257 nfas.clear();
244258
 259 LOG_LARGE_STRUCTURES(bytecode, bytecode.size() * sizeof(uint8_t));
245260 client.writeBytecode(WTF::move(bytecode));
 261 bytecode.clear();
246262
247263 return { };
248264}
183021

Source/WebCore/contentextensions/ContentExtensionsDebugging.h

3535#define CONTENT_EXTENSIONS_MEMORY_REPORTING 0
3636#define CONTENT_EXTENSIONS_PAGE_SIZE 16384
3737
 38#define LOG_LARGE_STRUCTURES(name, size) /* if (size > 1000000) { WTFLogAlways("NAME: %s SIZE %d", #name, (int)(size)); }; */
 39
3840#endif // ENABLE(CONTENT_EXTENSIONS)
3941
4042#endif // ContentExtensionsDebugging_h
183021

Source/WebCore/contentextensions/DFA.cpp

@@namespace WebCore {
3636namespace ContentExtensions {
3737
3838DFA::DFA()
39  : m_root(0)
 39 : root(0)
4040{
4141}
4242
43 DFA::DFA(Vector<DFANode>&& nodes, unsigned rootIndex)
44  : m_nodes(WTF::move(nodes))
45  , m_root(rootIndex)
 43size_t DFA::memoryUsed() const
4644{
47  ASSERT(rootIndex < m_nodes.size());
 45 return sizeof(DFA)
 46 + actions.size() * sizeof(uint64_t)
 47 + transitions.size() * sizeof(std::pair<uint8_t, uint32_t>)
 48 + nodes.size() * sizeof(DFANode);
4849}
4950
50 DFA::DFA(const DFA& dfa)
51  : m_nodes(dfa.m_nodes)
52  , m_root(dfa.m_root)
 51// FIXME: Make DFANode.cpp.
 52Vector<uint64_t> DFANode::actions(const DFA& dfa) const
5353{
 54 // FIXME: Use iterators instead of copying the Vector elements.
 55 Vector<uint64_t> vector;
 56 vector.reserveInitialCapacity(m_actionsLength);
 57 for (uint32_t i = m_actionsStart; i < m_actionsStart + m_actionsLength; ++i)
 58 vector.append(dfa.actions[i]);
 59 return vector;
 60}
 61
 62Vector<std::pair<uint8_t, uint32_t>> DFANode::transitions(const DFA& dfa) const
 63{
 64 // FIXME: Use iterators instead of copying the Vector elements.
 65 Vector<std::pair<uint8_t, uint32_t>> vector;
 66 vector.reserveInitialCapacity(transitionsLength());
 67 for (uint32_t i = m_transitionsStart; i < m_transitionsStart + m_transitionsLength; ++i)
 68 vector.append(dfa.transitions[i]);
 69 return vector;
5470}
5571
56 DFA& DFA::operator=(const DFA& dfa)
 72uint32_t DFANode::fallbackTransitionDestination(const DFA& dfa) const
5773{
58  m_nodes = dfa.m_nodes;
59  m_root = dfa.m_root;
60  return *this;
 74 RELEASE_ASSERT(hasFallbackTransition());
 75
 76 // If there is a fallback transition, it is just after the other transitions and has an invalid ASCII character to mark it as a fallback transition.
 77 ASSERT(dfa.transitions[m_transitionsStart + m_transitionsLength].first == std::numeric_limits<uint8_t>::max());
 78 return dfa.transitions[m_transitionsStart + m_transitionsLength].second;
 79}
 80
 81void DFANode::changeFallbackTransition(DFA& dfa, uint32_t newDestination)
 82{
 83 RELEASE_ASSERT(hasFallbackTransition());
 84 ASSERT_WITH_MESSAGE(dfa.transitions[m_transitionsStart + m_transitionsLength].first == std::numeric_limits<uint8_t>::max(), "When changing a fallback transition, the fallback transition should already be marked as such");
 85 dfa.transitions[m_transitionsStart + m_transitionsLength] = std::pair<uint8_t, uint32_t>(std::numeric_limits<uint8_t>::max(), newDestination);
 86}
 87
 88void DFANode::addFallbackTransition(DFA& dfa, uint32_t destination)
 89{
 90 RELEASE_ASSERT_WITH_MESSAGE(dfa.transitions.size() == m_transitionsStart + m_transitionsLength, "Adding a fallback transition should only happen if the node is at the end");
 91 dfa.transitions.append(std::pair<uint8_t, uint32_t>(std::numeric_limits<uint8_t>::max(), destination));
 92 ASSERT(!(m_flags & HasFallbackTransition));
 93 m_flags |= HasFallbackTransition;
 94}
 95
 96bool DFANode::containsTransition(uint8_t transition, DFA& dfa)
 97{
 98 // Called from DFAMinimizer, this loops though a maximum of 128 transitions, so it's not too slow.
 99 ASSERT(m_transitionsLength <= 128);
 100 for (unsigned i = m_transitionsStart; i < m_transitionsStart + m_transitionsLength; ++i) {
 101 if (dfa.transitions[i].first == transition)
 102 return true;
 103 }
 104 return false;
61105}
62106
63107void DFA::minimize()
64108{
65  m_root = DFAMinimizer::minimize(m_nodes, m_root);
 109 DFAMinimizer::minimize(*this);
66110}
67111
68112#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
183021

Source/WebCore/contentextensions/DFA.h

@@namespace ContentExtensions {
4040class WEBCORE_EXPORT DFA {
4141public:
4242 DFA();
43  DFA(Vector<DFANode>&& nodes, unsigned rootIndex);
44  DFA(const DFA& dfa);
45 
46  DFA& operator=(const DFA&);
47 
48  unsigned root() const { return m_root; }
49  unsigned size() const { return m_nodes.size(); }
50  const DFANode& nodeAt(unsigned i) const { return m_nodes[i]; }
51  DFANode& nodeAt(unsigned i) { return m_nodes[i]; }
5243
5344 void minimize();
 45 size_t memoryUsed() const;
5446
5547#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
5648 void debugPrintDot() const;
5749#endif
5850
59 private:
60  Vector<DFANode> m_nodes;
61  unsigned m_root;
 51 Vector<uint64_t> actions;
 52 Vector<std::pair<uint8_t, uint32_t>> transitions;
 53 Vector<DFANode> nodes;
 54 unsigned root;
6255};
6356
6457}
183021

Source/WebCore/contentextensions/DFABytecodeCompiler.cpp

@@void DFABytecodeCompiler::emitTerminate(
9595
9696void DFABytecodeCompiler::compileNode(unsigned index, bool root)
9797{
98  const DFANode& node = m_dfa.nodeAt(index);
99  if (node.isKilled) {
 98 const DFANode& node = m_dfa.nodes[index];
 99 if (node.isKilled()) {
100100 m_nodeStartOffsets[index] = std::numeric_limits<unsigned>::max();
101101 return;
102102 }

@@void DFABytecodeCompiler::compileNode(un
105105 if (!root)
106106 m_nodeStartOffsets[index] = m_bytecode.size();
107107
108  for (uint64_t action : node.actions) {
 108 for (uint64_t action : node.actions(m_dfa)) {
109109 // High bits are used to store flags. See compileRuleList.
110110 if (action & 0xFFFF00000000)
111111 emitTestFlagsAndAppendAction(static_cast<uint16_t>(action >> 32), static_cast<unsigned>(action));

@@void DFABytecodeCompiler::compileNode(un
123123
124124void DFABytecodeCompiler::compileNodeTransitions(const DFANode& node)
125125{
126  unsigned destinations[128];
127  const unsigned noDestination = std::numeric_limits<unsigned>::max();
128  for (uint16_t i = 0; i < 128; i++) {
129  auto it = node.transitions.find(i);
130  if (it == node.transitions.end())
131  destinations[i] = noDestination;
132  else
133  destinations[i] = it->value;
 126 uint32_t destinations[128];
 127 memset(destinations, 0xff, sizeof(destinations));
 128 const uint32_t noDestination = std::numeric_limits<uint32_t>::max();
 129
 130 for (const std::pair<uint8_t, uint32_t>& pair : node.transitions(m_dfa)) {
 131 RELEASE_ASSERT(pair.first < 128);
 132 ASSERT_WITH_MESSAGE(destinations[pair.first] == noDestination, "Transitions should be unique");
 133 destinations[pair.first] = pair.second;
134134 }
135135
136136 Vector<Range> ranges;

@@void DFABytecodeCompiler::compileNodeTra
138138 bool hasRangeMin = false;
139139 for (uint8_t i = 0; i < 128; i++) {
140140 if (hasRangeMin) {
141  ASSERT_WITH_MESSAGE(!(node.hasFallbackTransition && node.fallbackTransition == destinations[rangeMin]), "Individual transitions to the fallback transitions should have been eliminated by the optimizer.");
 141 ASSERT_WITH_MESSAGE(!(node.hasFallbackTransition() && node.fallbackTransitionDestination(m_dfa) == destinations[rangeMin]), "Individual transitions to the fallback transitions should have been eliminated by the optimizer.");
142142 if (destinations[i] != destinations[rangeMin]) {
143143
144144 // This is the end of a range. Check if it can be case insensitive.

@@void DFABytecodeCompiler::compileNodeTra
186186
187187 for (const auto& range : ranges)
188188 compileCheckForRange(range);
189  if (node.hasFallbackTransition)
190  emitJump(node.fallbackTransition);
 189 if (node.hasFallbackTransition())
 190 emitJump(node.fallbackTransitionDestination(m_dfa));
191191 else
192192 emitTerminate();
193193}

@@void DFABytecodeCompiler::compileCheckFo
205205
206206void DFABytecodeCompiler::compile()
207207{
 208 WTFLogAlways("DFABytecodeCompiler::compile");
208209 // DFA header.
209210 unsigned startLocation = m_bytecode.size();
210211 append<unsigned>(m_bytecode, 0);
211  m_nodeStartOffsets.resize(m_dfa.size());
 212 m_nodeStartOffsets.resize(m_dfa.nodes.size());
212213
213214 // Make sure the root is always at the beginning of the bytecode.
214  compileNode(m_dfa.root(), true);
215  for (unsigned i = 0; i < m_dfa.size(); i++) {
216  if (i != m_dfa.root())
 215 compileNode(m_dfa.root, true);
 216 for (unsigned i = 0; i < m_dfa.nodes.size(); i++) {
 217 if (i != m_dfa.root)
217218 compileNode(i, false);
218219 }
219220
183021

Source/WebCore/contentextensions/DFAMinimizer.cpp

@@namespace {
3939// simplifyTransitions() tries to collapse individual transitions into fallback transitions.
4040// After simplifyTransitions(), you can also make the assumption that a fallback transition target will never be
4141// also the target of an individual transition.
42 static void simplifyTransitions(Vector<DFANode>& dfaGraph)
 42static void simplifyTransitions(DFA& dfa)
4343{
44  for (DFANode& dfaNode : dfaGraph) {
45  if (!dfaNode.hasFallbackTransition
46  && ((dfaNode.transitions.size() == 126 && !dfaNode.transitions.contains(0))
47  || (dfaNode.transitions.size() == 127 && dfaNode.transitions.contains(0)))) {
 44 for (DFANode& dfaNode : dfa.nodes) {
 45 bool addingFallbackTransition = false;
 46 uint32_t newFallbackDestination = std::numeric_limits<uint32_t>::max();
 47 if (!dfaNode.hasFallbackTransition()
 48 && ((dfaNode.transitionsLength() == 126 && !dfaNode.containsTransition(0, dfa))
 49 || (dfaNode.transitionsLength() == 127 && dfaNode.containsTransition(0, dfa)))) {
4850 unsigned bestTarget = std::numeric_limits<unsigned>::max();
4951 unsigned bestTargetScore = 0;
5052 HashMap<unsigned, unsigned, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> targetHistogram;
51  for (const auto& transition : dfaNode.transitions) {
52  if (!transition.key)
 53 for (const auto& transition : dfaNode.transitions(dfa)) {
 54 if (!transition.first)
5355 continue;
5456
55  unsigned transitionTarget = transition.value;
 57 unsigned transitionTarget = transition.second;
5658 auto addResult = targetHistogram.add(transitionTarget, 1);
5759 if (!addResult.isNewEntry)
5860 addResult.iterator->value++;

@@static void simplifyTransitions(Vector<D
6466 }
6567 ASSERT_WITH_MESSAGE(bestTargetScore, "There should be at least one valid target since having transitions is a precondition to enter this path.");
6668
67  dfaNode.hasFallbackTransition = true;
68  dfaNode.fallbackTransition = bestTarget;
69  }
70 
71  if (dfaNode.hasFallbackTransition) {
72  Vector<uint16_t, 128> keys;
73  DFANodeTransitions& transitions = dfaNode.transitions;
74  copyKeysToVector(transitions, keys);
75 
76  for (uint16_t key : keys) {
77  auto transitionIterator = transitions.find(key);
78  if (transitionIterator->value == dfaNode.fallbackTransition)
79  transitions.remove(transitionIterator);
80  }
 69 addingFallbackTransition = true;
 70 newFallbackDestination = bestTarget;
8171 }
 72
 73 // FIXME: Use the same location to write new transitions followed by unused memory.
 74 // We can do this because we are always decreasing the amount of memory used.
 75 // We will probably need something like setHasFallbackTransitionWithoutChangingDFA to do that.
8276 }
8377}
8478

@@private:
221215
222216class FullGraphPartition {
223217public:
224  FullGraphPartition(const Vector<DFANode>& dfaGraph)
 218 FullGraphPartition(const DFA& dfa)
225219 {
226  m_nodePartition.initialize(dfaGraph.size());
 220 m_nodePartition.initialize(dfa.nodes.size());
227221
228  m_flattenedTransitionsStartOffsetPerNode.resize(dfaGraph.size());
 222 m_flattenedTransitionsStartOffsetPerNode.resize(dfa.nodes.size());
229223 for (unsigned& counter : m_flattenedTransitionsStartOffsetPerNode)
230224 counter = 0;
231225
232  m_flattenedFallbackTransitionsStartOffsetPerNode.resize(dfaGraph.size());
 226 m_flattenedFallbackTransitionsStartOffsetPerNode.resize(dfa.nodes.size());
233227 for (unsigned& counter : m_flattenedFallbackTransitionsStartOffsetPerNode)
234228 counter = 0;
235229
236230 // Count the number of incoming transitions per node.
237  for (const DFANode& dfaNode : dfaGraph) {
238  for (const auto& transition : dfaNode.transitions)
239  ++m_flattenedTransitionsStartOffsetPerNode[transition.value];
240  if (dfaNode.hasFallbackTransition)
241  ++m_flattenedFallbackTransitionsStartOffsetPerNode[dfaNode.fallbackTransition];
 231 for (const DFANode& dfaNode : dfa.nodes) {
 232 for (const auto& transition : dfaNode.transitions(dfa))
 233 ++m_flattenedTransitionsStartOffsetPerNode[transition.second];
 234 if (dfaNode.hasFallbackTransition())
 235 ++m_flattenedFallbackTransitionsStartOffsetPerNode[dfaNode.fallbackTransitionDestination(dfa)];
242236 }
243237
244238 // Accumulate the offsets.

@@public:
259253 unsigned flattenedFallbackTransitionsSize = fallbackTransitionAccumulator;
260254
261255 // Next, let's fill the transition table and set up the size of each group at the same time.
262  m_flattenedTransitionsSizePerNode.resize(dfaGraph.size());
 256 m_flattenedTransitionsSizePerNode.resize(dfa.nodes.size());
263257 for (unsigned& counter : m_flattenedTransitionsSizePerNode)
264258 counter = 0;
265259
266  m_flattenedFallbackTransitionsSizePerNode.resize(dfaGraph.size());
 260 m_flattenedFallbackTransitionsSizePerNode.resize(dfa.nodes.size());
267261 for (unsigned& counter : m_flattenedFallbackTransitionsSizePerNode)
268262 counter = 0;
269263

@@public:
271265
272266 m_flattenedFallbackTransitions.resize(flattenedFallbackTransitionsSize);
273267
274  for (unsigned i = 0; i < dfaGraph.size(); ++i) {
275  const DFANode& dfaNode = dfaGraph[i];
276  for (const auto& transition : dfaNode.transitions) {
277  unsigned targetNodeIndex = transition.value;
 268 for (unsigned i = 0; i < dfa.nodes.size(); ++i) {
 269 const DFANode& dfaNode = dfa.nodes[i];
 270 for (const auto& transition : dfaNode.transitions(dfa)) {
 271 unsigned targetNodeIndex = transition.second;
278272
279273 unsigned start = m_flattenedTransitionsStartOffsetPerNode[targetNodeIndex];
280274 unsigned offset = m_flattenedTransitionsSizePerNode[targetNodeIndex];
281275
282  m_flattenedTransitions[start + offset] = Transition({ i, targetNodeIndex, transition.key });
 276 m_flattenedTransitions[start + offset] = Transition({ i, targetNodeIndex, transition.first });
283277
284278 ++m_flattenedTransitionsSizePerNode[targetNodeIndex];
285279 }
286  if (dfaNode.hasFallbackTransition) {
287  unsigned targetNodeIndex = dfaNode.fallbackTransition;
 280 if (dfaNode.hasFallbackTransition()) {
 281 unsigned targetNodeIndex = dfaNode.fallbackTransitionDestination(dfa);
288282
289283 unsigned start = m_flattenedFallbackTransitionsStartOffsetPerNode[targetNodeIndex];
290284 unsigned offset = m_flattenedFallbackTransitionsSizePerNode[targetNodeIndex];

@@struct ActionKeyHashTraits : public WTF:
443437
444438} // anonymous namespace.
445439
446 unsigned DFAMinimizer::minimize(Vector<DFANode>& dfaGraph, unsigned root)
 440void DFAMinimizer::minimize(DFA& dfa)
447441{
448  simplifyTransitions(dfaGraph);
 442 simplifyTransitions(dfa);
449443
450  FullGraphPartition fullGraphPartition(dfaGraph);
 444 FullGraphPartition fullGraphPartition(dfa);
451445
452446 // Unlike traditional minimization final states can be differentiated by their action.
453447 // Instead of creating a single set for the final state, we partition by actions from
454448 // the start.
455449 HashMap<ActionKey, Vector<unsigned>, ActionKeyHash, ActionKeyHashTraits> finalStates;
456  for (unsigned i = 0; i < dfaGraph.size(); ++i) {
457  Vector<uint64_t>& actions = dfaGraph[i].actions;
 450 for (unsigned i = 0; i < dfa.nodes.size(); ++i) {
 451 Vector<uint64_t> actions = dfa.nodes[i].actions(dfa);
458452 if (actions.size()) {
459453 std::sort(actions.begin(), actions.end());
460454 auto addResult = finalStates.add(ActionKey(actions), Vector<unsigned>());

@@unsigned DFAMinimizer::minimize(Vector<D
481475 fullGraphPartition.splitByFallbackTransitions();
482476
483477 Vector<unsigned> relocationVector;
484  relocationVector.reserveInitialCapacity(dfaGraph.size());
485  for (unsigned i = 0; i < dfaGraph.size(); ++i)
 478 relocationVector.reserveInitialCapacity(dfa.nodes.size());
 479 for (unsigned i = 0; i < dfa.nodes.size(); ++i)
486480 relocationVector.append(i);
487481
488482 // Kill the useless nodes and keep track of the new node transitions should point to.
489  for (unsigned i = 0; i < dfaGraph.size(); ++i) {
 483 for (unsigned i = 0; i < dfa.nodes.size(); ++i) {
490484 unsigned replacement = fullGraphPartition.nodeReplacement(i);
491485 if (i != replacement) {
492486 relocationVector[i] = replacement;
493 
494  DFANode& node = dfaGraph[i];
495  node.actions.clear();
496  node.transitions.clear();
497  node.hasFallbackTransition = false;
498  node.isKilled = true;
 487 dfa.nodes[i].kill();
499488 }
500489 }
501490
502  for (DFANode& node : dfaGraph) {
503  for (auto& transition : node.transitions)
504  transition.value = relocationVector[transition.value];
505  if (node.hasFallbackTransition)
506  node.fallbackTransition = relocationVector[node.fallbackTransition];
 491 for (DFANode& node : dfa.nodes) {
 492 for (auto& transition : node.transitions(dfa))
 493 transition.second = relocationVector[transition.second];
 494 if (node.hasFallbackTransition())
 495 node.changeFallbackTransition(dfa, relocationVector[node.fallbackTransitionDestination(dfa)]);
507496 }
508497
509498 // After minimizing, there is no guarantee individual transition are still poiting to different states.
510499 // The state pointed by one individual transition and the fallback states may have been merged.
511  simplifyTransitions(dfaGraph);
 500 simplifyTransitions(dfa);
512501
513  return relocationVector[root];
 502 dfa.root = relocationVector[dfa.root];
514503}
515504
516505} // namespace ContentExtensions
183021

Source/WebCore/contentextensions/DFAMinimizer.h

2828
2929#if ENABLE(CONTENT_EXTENSIONS)
3030
31 #include "DFANode.h"
 31#include "DFA.h"
3232#include <wtf/Vector.h>
3333
3434namespace WebCore {

@@namespace ContentExtensions {
3636
3737class DFAMinimizer {
3838public:
39  WEBCORE_EXPORT static unsigned minimize(Vector<DFANode>& dfaGraph, unsigned rootNode);
 39 WEBCORE_EXPORT static void minimize(DFA&);
4040};
4141
4242} // namespace ContentExtensions
183021

Source/WebCore/contentextensions/DFANode.h

@@namespace WebCore {
3636
3737namespace ContentExtensions {
3838
 39class DFA;
 40
3941// A DFANode abstract the transition table out of a DFA state. If a state is accepting, the DFANode also have
4042// the actions for that state.
41 
42 typedef HashMap<uint16_t, unsigned, DefaultHash<uint16_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint16_t>> DFANodeTransitions;
43 
4443class DFANode {
4544public:
46  DFANodeTransitions transitions;
47  bool hasFallbackTransition { false };
48  bool isKilled { false };
49  unsigned fallbackTransition;
50  Vector<uint64_t> actions;
51 
 45
 46 // FIXME: Stop minimizing killed nodes and add ASSERT(!isKilled()) in many functions here.
 47 void kill()
 48 {
 49 ASSERT(m_flags != IsKilled);
 50 m_flags = IsKilled; // Killed nodes don't have any other flags.
 51 m_actionsStart = m_actionsLength = m_transitionsStart = m_transitionsLength = 0;
 52 };
 53 bool isKilled() const { return m_flags & IsKilled; }
 54 bool hasFallbackTransition() const { return m_flags & HasFallbackTransition; }
 55 bool hasActions() const { return !!m_actionsLength; }
 56 Vector<uint64_t> actions(const DFA&) const;
 57 uint32_t fallbackTransitionDestination(const DFA&) const;
 58 Vector<std::pair<uint8_t, uint32_t>> transitions(const DFA&) const;
 59 uint16_t transitionsLength() const { return m_transitionsLength; }
 60 void addFallbackTransition(DFA&, uint32_t destination);
 61 void setActions(uint32_t start, uint16_t length) { ASSERT(!m_actionsStart); ASSERT(!m_actionsLength); m_actionsStart = start; m_actionsLength = length; }
 62 void setTransitions(uint32_t start, uint16_t length) { ASSERT(!m_transitionsStart); ASSERT(!m_transitionsLength); m_transitionsStart = start; m_transitionsLength = length; }
 63 bool containsTransition(uint8_t, DFA&);
 64 uint32_t transitionsStart() { return m_transitionsStart; }
 65 void changeFallbackTransition(DFA&, uint32_t newDestination);
 66 void setHasFallbackTransitionWithoutChangingDFA(bool shouldHaveFallbackTransition)
 67 {
 68 if (shouldHaveFallbackTransition)
 69 m_flags |= HasFallbackTransition;
 70 else
 71 m_flags &= ~HasFallbackTransition;
 72 }
 73
5274#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
5375 Vector<unsigned> correspondingNFANodes;
5476#endif
 77private:
 78 uint32_t m_actionsStart { 0 };
 79 uint32_t m_transitionsStart { 0 };
 80 uint16_t m_actionsLength { 0 };
 81 uint8_t m_transitionsLength { 0 };
 82
 83 uint8_t m_flags { 0 };
 84 const uint8_t HasFallbackTransition = 0x01;
 85 const uint8_t IsKilled = 0x02;
5586};
5687
 88// FIXME: Pack this down to 12.
 89// It's probably already 12 on ARMv7.
 90COMPILE_ASSERT(sizeof(DFANode) <= 16, Keep the DFANodes small);
 91
5792}
5893
5994} // namespace WebCore
183021

Source/WebCore/contentextensions/NFA.cpp

@@unsigned NFA::createNode()
4747 return nextId;
4848}
4949
 50size_t NFA::memoryUsed() const
 51{
 52 size_t size = 0;
 53 for (const NFANode& node : m_nodes) {
 54 size += sizeof(node)
 55 + node.transitions.capacity() * sizeof(std::pair<uint16_t, NFANodeIndexSet>)
 56 + node.transitionsOnAnyCharacter.capacity() * sizeof(unsigned)
 57 + node.finalRuleIds.size() * sizeof(uint64_t);
 58 }
 59 return size;
 60}
 61
5062void NFA::addTransition(unsigned from, unsigned to, char character)
5163{
5264 ASSERT(from < m_nodes.size());
183021

Source/WebCore/contentextensions/NFA.h

@@public:
6363#else
6464 void addRuleId(unsigned, uint64_t) { }
6565#endif
 66 size_t memoryUsed() const;
6667
6768private:
6869 friend class NFAToDFA;
183021

Source/WebCore/contentextensions/NFAToDFA.cpp

@@struct UniqueNodeIdSetHashHashTraits : p
214214typedef HashSet<std::unique_ptr<UniqueNodeIdSet>, UniqueNodeIdSetHash, UniqueNodeIdSetHashHashTraits> UniqueNodeIdSetTable;
215215
216216struct NodeIdSetToUniqueNodeIdSetSource {
217  NodeIdSetToUniqueNodeIdSetSource(Vector<DFANode>& dfaGraph, const Vector<NFANode>& nfaGraph, const NodeIdSet& nodeIdSet)
218  : dfaGraph(dfaGraph)
 217 NodeIdSetToUniqueNodeIdSetSource(DFA& dfa, const Vector<NFANode>& nfaGraph, const NodeIdSet& nodeIdSet)
 218 : dfa(dfa)
219219 , nfaGraph(nfaGraph)
220220 , nodeIdSet(nodeIdSet)
221221 {

@@struct NodeIdSetToUniqueNodeIdSetSource
225225 hash += nodeId;
226226 this->hash = DefaultHash<unsigned>::Hash::hash(hash);
227227 }
228  Vector<DFANode>& dfaGraph;
 228 DFA& dfa;
229229 const Vector<NFANode>& nfaGraph;
230230 const NodeIdSet& nodeIdSet;
231231 unsigned hash;

@@struct NodeIdSetToUniqueNodeIdSetTransla
254254 newDFANode.correspondingNFANodes.append(nfaNodeId);
255255#endif
256256 }
257  copyToVector(actions, newDFANode.actions);
 257
 258 unsigned actionsStart = source.dfa.actions.size();
 259 for (uint64_t action : actions)
 260 source.dfa.actions.append(action);
 261 unsigned actionsEnd = source.dfa.actions.size();
 262 unsigned actionsLength = actionsEnd - actionsStart;
 263 RELEASE_ASSERT_WITH_MESSAGE(actionsLength <= std::numeric_limits<uint16_t>::max(), "Too many actions for the current DFANode size.");
 264 newDFANode.setActions(actionsStart, static_cast<uint16_t>(actionsLength));
258265
259  unsigned dfaNodeId = source.dfaGraph.size();
260  source.dfaGraph.append(newDFANode);
 266 unsigned dfaNodeId = source.dfa.nodes.size();
 267 source.dfa.nodes.append(newDFANode);
261268 new (NotNull, &location) UniqueNodeIdSet(source.nodeIdSet, hash, dfaNodeId);
262269
263270 ASSERT(location.impl());

@@static inline void populateTransitions(S
328335 }
329336}
330337
331 static ALWAYS_INLINE unsigned getOrCreateDFANode(const NodeIdSet& nfaNodeSet, const Vector<NFANode>& nfaGraph, Vector<DFANode>& dfaGraph, UniqueNodeIdSetTable& uniqueNodeIdSetTable, Vector<UniqueNodeIdSetImpl*>& unprocessedNodes)
 338static ALWAYS_INLINE unsigned getOrCreateDFANode(const NodeIdSet& nfaNodeSet, const Vector<NFANode>& nfaGraph, DFA& dfa, UniqueNodeIdSetTable& uniqueNodeIdSetTable, Vector<UniqueNodeIdSetImpl*>& unprocessedNodes)
332339{
333  NodeIdSetToUniqueNodeIdSetSource nodeIdSetToUniqueNodeIdSetSource(dfaGraph, nfaGraph, nfaNodeSet);
 340 NodeIdSetToUniqueNodeIdSetSource nodeIdSetToUniqueNodeIdSetSource(dfa, nfaGraph, nfaNodeSet);
334341 auto uniqueNodeIdAddResult = uniqueNodeIdSetTable.add<NodeIdSetToUniqueNodeIdSetTranslator>(nodeIdSetToUniqueNodeIdSetSource);
335342 if (uniqueNodeIdAddResult.isNewEntry)
336343 unprocessedNodes.append(uniqueNodeIdAddResult.iterator->impl());

@@static ALWAYS_INLINE unsigned getOrCreat
340347
341348DFA NFAToDFA::convert(NFA& nfa)
342349{
 350 DFA dfa;
343351 Vector<NFANode>& nfaGraph = nfa.m_nodes;
344352
345353 Vector<Vector<unsigned>> nfaNodeClosures;

@@DFA NFAToDFA::convert(NFA& nfa)
350358
351359 UniqueNodeIdSetTable uniqueNodeIdSetTable;
352360
353  Vector<DFANode> dfaGraph;
354  NodeIdSetToUniqueNodeIdSetSource initialNodeIdSetToUniqueNodeIdSetSource(dfaGraph, nfaGraph, initialSet);
 361 NodeIdSetToUniqueNodeIdSetSource initialNodeIdSetToUniqueNodeIdSetSource(dfa, nfaGraph, initialSet);
355362 auto addResult = uniqueNodeIdSetTable.add<NodeIdSetToUniqueNodeIdSetTranslator>(initialNodeIdSetToUniqueNodeIdSetSource);
356363
357364 Vector<UniqueNodeIdSetImpl*> unprocessedNodes;

@@DFA NFAToDFA::convert(NFA& nfa)
366373 NodeIdSet setFallbackTransition;
367374 populateTransitions(transitionsFromClosedSet, setFallbackTransition, *uniqueNodeIdSetImpl, nfaGraph, nfaNodeClosures);
368375
 376 unsigned transitionsStart = dfa.transitions.size();
369377 for (unsigned key = 0; key < transitionsFromClosedSet.size(); ++key) {
370378 NodeIdSet& targetNodeSet = transitionsFromClosedSet[key];
371379
372380 if (targetNodeSet.isEmpty())
373381 continue;
374382
375  unsigned targetNodeId = getOrCreateDFANode(targetNodeSet, nfaGraph, dfaGraph, uniqueNodeIdSetTable, unprocessedNodes);
376  const auto addResult = dfaGraph[dfaNodeId].transitions.add(key, targetNodeId);
377  ASSERT_UNUSED(addResult, addResult.isNewEntry);
 383 unsigned targetNodeId = getOrCreateDFANode(targetNodeSet, nfaGraph, dfa, uniqueNodeIdSetTable, unprocessedNodes);
 384 RELEASE_ASSERT_WITH_MESSAGE(key <= std::numeric_limits<uint8_t>::max(), "ASCII transition should always fit in 8 bits");
 385 dfa.transitions.append(std::make_pair(static_cast<uint8_t>(key), targetNodeId));
378386
379387 targetNodeSet.clear();
380388 }
 389 unsigned transitionsEnd = dfa.transitions.size();
 390 unsigned transitionsLength = transitionsEnd - transitionsStart;
 391 RELEASE_ASSERT_WITH_MESSAGE(transitionsLength <= std::numeric_limits<uint8_t>::max(), "ASCII transition count should always fit in 8 bits");
 392 dfa.nodes[dfaNodeId].setTransitions(transitionsStart, static_cast<uint8_t>(transitionsLength));
 393
381394 if (!setFallbackTransition.isEmpty()) {
382  unsigned targetNodeId = getOrCreateDFANode(setFallbackTransition, nfaGraph, dfaGraph, uniqueNodeIdSetTable, unprocessedNodes);
383  DFANode& dfaSourceNode = dfaGraph[dfaNodeId];
384  ASSERT(!dfaSourceNode.hasFallbackTransition);
385  dfaSourceNode.hasFallbackTransition = true;
386  dfaSourceNode.fallbackTransition = targetNodeId;
 395 unsigned targetNodeId = getOrCreateDFANode(setFallbackTransition, nfaGraph, dfa, uniqueNodeIdSetTable, unprocessedNodes);
 396 DFANode& dfaSourceNode = dfa.nodes[dfaNodeId];
 397 dfaSourceNode.addFallbackTransition(dfa, targetNodeId);
387398 }
388399 } while (!unprocessedNodes.isEmpty());
389400
390  return DFA(WTF::move(dfaGraph), 0);
 401 return dfa;
391402}
392403
393404} // namespace ContentExtensions
183021

Tools/ChangeLog

 12015-04-22 Alex Christensen <achristensen@webkit.org>
 2
 3 Use less memory when compiling content extensions.
 4 https://bugs.webkit.org/show_bug.cgi?id=144051
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp:
 9 (TestWebKitAPI::countLiveNodes):
 10
1112015-04-22 Carlos Garcia Campos <cgarcia@igalia.com>
212
313 Unreviewed. Do not run GTK+ user media unit tests when ENABLE_MEDIA_STREAM is disabled.
183114

Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp

@@public:
4646unsigned countLiveNodes(const ContentExtensions::DFA& dfa)
4747{
4848 unsigned counter = 0;
49  for (unsigned i = 0; i < dfa.size(); ++i) {
50  if (!dfa.nodeAt(i).isKilled)
 49 for (const auto& node : dfa.nodes) {
 50 if (!node.isKilled())
5151 ++counter;
5252 }
5353 return counter;
183021