Index: chrome/browser/devtools/serialize_dictionary_forest.cc |
diff --git a/chrome/browser/devtools/serialize_dictionary_forest.cc b/chrome/browser/devtools/serialize_dictionary_forest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cb8dba8dced12f0b5768c191983c305252da70e5 |
--- /dev/null |
+++ b/chrome/browser/devtools/serialize_dictionary_forest.cc |
@@ -0,0 +1,185 @@ |
+// Copyright 2017 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 "chrome/browser/devtools/serialize_dictionary_forest.h" |
+ |
+#include <map> |
+#include <utility> |
+ |
+#include "base/containers/adapters.h" |
+#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
+ |
+namespace { |
+ |
+// Helper class; takes a forest represented by nodes with pointers from |
+// children to parents and computes a representation allowing to iterate over |
+// children of a given parent efficiently. |
+class ChildrenRepresentation { |
+ public: |
+ using ChildIter = std::vector<DictionaryForestNode*>::iterator; |
jdoerrie
2017/04/19 11:45:51
nit: This could be a const_iterator.
Also some po
vabr (Chromium)
2017/04/19 16:25:49
[EDIT: This is now obsolete, the code has changed.
|
+ |
+ // |forest| must outlive this class. |
+ explicit ChildrenRepresentation(std::vector<DictionaryForestNode>* forest); |
+ |
+ ~ChildrenRepresentation(); |
+ |
+ // Returns a range with all nodes from |forest_| which have |node| as parent. |
+ std::pair<ChildIter, ChildIter> Children(DictionaryForestNode* node); |
+ |
+ private: |
+ // Just a named pair of offset and length. |
+ struct OffsetLength { |
+ size_t offset = 0; |
+ size_t length = 0; |
+ }; |
+ |
+ // Another named pair: |
+ // |children| stores pointers to nodes in consecutive blocks, such that |
+ // within each block, all nodes have the same parent. |locations| maps |
+ // parents to offset and length of the corresponding block of children in |
+ // |children|. |
+ struct ChildrenLocations { |
+ std::vector<DictionaryForestNode*> children; |
+ std::map<DictionaryForestNode*, OffsetLength> locations; |
+ }; |
+ |
+ // Used by the constructor to compute the edges from parents to children in |
+ // |forest|. |
+ static ChildrenLocations InitFromForest( |
+ std::vector<DictionaryForestNode>* forest); |
+ |
+ ChildrenLocations children_locations_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ChildrenRepresentation); |
+}; |
+ |
+ChildrenRepresentation::ChildrenRepresentation( |
+ std::vector<DictionaryForestNode>* forest) |
+ : children_locations_(InitFromForest(forest)) {} |
+ |
+ChildrenRepresentation::~ChildrenRepresentation() {} |
+ |
+std::pair<ChildrenRepresentation::ChildIter, ChildrenRepresentation::ChildIter> |
+ChildrenRepresentation::Children(DictionaryForestNode* node) { |
+ OffsetLength ol = children_locations_.locations.at(node); |
+ ChildIter first_child = children_locations_.children.begin() + ol.offset; |
+ return std::make_pair(first_child, first_child + ol.length); |
+} |
+ |
+ChildrenRepresentation::ChildrenLocations |
+ChildrenRepresentation::InitFromForest( |
+ std::vector<DictionaryForestNode>* forest) { |
+ ChildrenLocations result; |
+ // There are always less children than all nodes in the |forest|, so resizing |
+ // the vector below is a little wasteful. But it keeps the code simple. |
+ result.children.resize(forest->size()); |
+ |
+ // In the first pass, only count the number of children. |
+ for (DictionaryForestNode& node : *forest) { |
+ DictionaryForestNode* parent = node.parent; |
+ if (!parent) |
+ continue; |
+ ++result.locations[parent].length; |
+ } |
+ |
+ // In the second pass, allocate the offsets and initialize |free_slots|, |
+ // which tells for every node where to put its next child in |
+ // |result.children|. |
+ size_t next_free = 0; |
+ std::map<DictionaryForestNode*, size_t> free_slots; |
+ for (DictionaryForestNode& node : *forest) { |
+ result.locations[&node].offset = next_free; |
+ free_slots[&node] = next_free; |
+ next_free += result.locations[&node].length; |
+ } |
+ |
+ // In the last pass, populate |result.children|. |
+ for (DictionaryForestNode& node : *forest) { |
+ DictionaryForestNode* parent = node.parent; |
+ if (!parent) |
+ continue; |
+ const size_t offset = free_slots[parent]; |
+ ++free_slots[parent]; |
+ result.children[offset] = &node; |
+ } |
+ |
+ return result; |
+} |
+ |
+// Appends |child_description| to a ListValue under key |child_key| in |
+// |parent->representation|, creating said ListValue if needed. |
+void InjectChildDescription(base::DictionaryValue child_description, |
+ DictionaryForestNode* parent, |
+ base::StringPiece child_key) { |
+ base::ListValue* children_weak = nullptr; |
+ base::DictionaryValue* parent_rep = &parent->representation; |
+ if (!parent_rep->GetList(child_key, &children_weak)) { |
+ auto children = base::MakeUnique<base::ListValue>(); |
+ children_weak = children.get(); |
+ parent_rep->Set(child_key, std::move(children)); |
+ } |
+ children_weak->base::Value::GetList().push_back(std::move(child_description)); |
+} |
+ |
+} // namespace |
+ |
+base::ListValue SerializeDictionaryForest( |
+ std::vector<DictionaryForestNode> forest, |
+ base::StringPiece child_key) { |
+ // To serialize |forest|, DictionaryValue descriptions of child nodes need to |
+ // be injected into DictionaryValue descriptions of their parents. That means |
+ // the forest needs to be processed in topological order, understanding the |
+ // direction of edges to go from child to parent. |
+ // |
+ // However, computing a topological order using Kahn's algorithm is easier |
+ // for forests where the edge direction goes from parents to children: the |
+ // condition for inserting a node into the resulting sorted list, "if node |
+ // has no other incoming edges", is trivial, because every node has at most |
+ // one parent and hence at most one incoming edge. |
+ // |
+ // Luckily, the reverse of a topological order of a graph is a topological |
+ // order of the same graph with edges reversed. So here first the easy |
+ // topological order for (parent -> child) edges is computed, and then |
+ // processed in reverse to serialize the forest. |
+ ChildrenRepresentation children(&forest); |
+ |
+ std::vector<DictionaryForestNode*> sorted; |
+ sorted.reserve(forest.size()); |
+ |
+ // Initialize the algorithm with nodes which come first in topological order. |
+ std::vector<DictionaryForestNode*> no_incoming_edges; |
+ no_incoming_edges.reserve(forest.size()); |
+ for (DictionaryForestNode& node : forest) { |
+ if (!node.parent) |
+ no_incoming_edges.push_back(&node); |
+ } |
+ |
+ // Iteratively add more nodes, respecting the topological order. |
+ while (!no_incoming_edges.empty()) { |
+ DictionaryForestNode* node = no_incoming_edges.back(); |
+ no_incoming_edges.pop_back(); |
+ sorted.push_back(node); |
+ // Once |node| is removed, all of its children have 0 incoming edges. |
+ auto child_range = children.Children(node); |
+ no_incoming_edges.insert(no_incoming_edges.end(), child_range.first, |
+ child_range.second); |
+ } |
+ |
+ // Now let's build the representation: injecting DictionaryValues of children |
+ // into those of their parents, and adding the roots to the returned |
+ // ListValue. |
+ base::ListValue roots; |
+ for (DictionaryForestNode* node : base::Reversed(sorted)) { |
+ DictionaryForestNode* parent = node->parent; |
+ if (parent) { |
+ InjectChildDescription(std::move(node->representation), parent, |
+ child_key); |
+ } else { |
+ roots.base::Value::GetList().push_back(std::move(node->representation)); |
+ } |
+ } |
+ |
+ return roots; |
+} |