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

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

Issue 2825533002: Introduce SerializeDictionaryForest for devtools_target_ui (Closed)
Patch Set: Self review 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..d5319541f9c8f57ce3e1aef5a8670dca5837be71
--- /dev/null
+++ b/chrome/browser/devtools/serialize_dictionary_forest.cc
@@ -0,0 +1,203 @@
+// 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/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:
+ // A range expression usable in for loops.
+ class ChildrenRange {
+ public:
+ ChildrenRange(DictionaryForestNode** begin, DictionaryForestNode** end)
+ : begin_(begin), end_(end) {}
+
+ DictionaryForestNode** begin() const { return begin_; }
+ DictionaryForestNode** end() const { return end_; }
+
+ private:
+ DictionaryForestNode** const begin_;
+ DictionaryForestNode** const end_;
+ };
+
+ // |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.
+ ChildrenRange For(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() {}
+
+ChildrenRepresentation::ChildrenRange ChildrenRepresentation::For(
+ DictionaryForestNode* node) {
+ OffsetLength ol = children_locations_.locations.at(node);
+ DictionaryForestNode** first_child = &children_locations_.children[ol.offset];
+ return ChildrenRange(first_child, first_child + ol.length);
jdoerrie 2017/04/18 09:57:44 Nit: return {first_child, first_child + ol.length
vabr (Chromium) 2017/04/19 11:03:17 Acknowledged, but changed due to getting rid of Ch
+}
+
+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));
+ }
+ // TODO(crbug.com/646113) Get rid of the unique_ptr once ListValue::Append
+ // takes the value directly.
+ children_weak->Append(
+ base::MakeUnique<base::Value>(std::move(child_description)));
jdoerrie 2017/04/19 08:35:00 FYI, r465517 just landed, so you could get rid of
vabr (Chromium) 2017/04/19 11:03:17 Cool, thanks!
+}
+
+} // 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);
+ for (DictionaryForestNode* child : children.For(node)) {
+ // Once |node| is removed, all of its children have 0 incoming edges.
+ no_incoming_edges.push_back(child);
jdoerrie 2017/04/18 09:57:44 Another small suggestion: Given that the children
vabr (Chromium) 2017/04/19 11:03:17 I like that, ChildrenRange was too much boilerplat
+ }
+ }
+
+ // 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 (auto node_it = sorted.rbegin(); node_it != sorted.rend(); ++node_it) {
+ DictionaryForestNode* node = *node_it;
+ DictionaryForestNode* parent = node->parent;
+ if (parent) {
+ InjectChildDescription(std::move(node->representation), parent,
+ child_key);
+ } else {
+ // TODO(crbug.com/646113) Get rid of the unique_ptr once ListValue::Append
+ // takes the value directly.
+ roots.Append(
+ base::MakeUnique<base::Value>(std::move(node->representation)));
jdoerrie 2017/04/19 08:35:00 Same here, this could be: roots.base::Value::GetL
vabr (Chromium) 2017/04/19 11:03:17 Done.
+ }
+ }
+
+ return roots;
+}

Powered by Google App Engine
This is Rietveld 408576698