Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Unified Diff: chrome/browser/devtools/serialize_dictionary_forest.cc

Issue 2825533002: Introduce SerializeDictionaryForest for devtools_target_ui (Closed)
Patch Set: ChildrenRange, base::Reversed and Value::GetList Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
+}
« no previous file with comments | « chrome/browser/devtools/serialize_dictionary_forest.h ('k') | chrome/browser/devtools/serialize_dictionary_forest_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698