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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/devtools/serialize_dictionary_forest.h"
6
7 #include <map>
8 #include <utility>
9
10 #include "base/containers/adapters.h"
11 #include "base/macros.h"
12 #include "base/memory/ptr_util.h"
13
14 namespace {
15
16 // Helper class; takes a forest represented by nodes with pointers from
17 // children to parents and computes a representation allowing to iterate over
18 // children of a given parent efficiently.
19 class ChildrenRepresentation {
20 public:
21 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.
22
23 // |forest| must outlive this class.
24 explicit ChildrenRepresentation(std::vector<DictionaryForestNode>* forest);
25
26 ~ChildrenRepresentation();
27
28 // Returns a range with all nodes from |forest_| which have |node| as parent.
29 std::pair<ChildIter, ChildIter> Children(DictionaryForestNode* node);
30
31 private:
32 // Just a named pair of offset and length.
33 struct OffsetLength {
34 size_t offset = 0;
35 size_t length = 0;
36 };
37
38 // Another named pair:
39 // |children| stores pointers to nodes in consecutive blocks, such that
40 // within each block, all nodes have the same parent. |locations| maps
41 // parents to offset and length of the corresponding block of children in
42 // |children|.
43 struct ChildrenLocations {
44 std::vector<DictionaryForestNode*> children;
45 std::map<DictionaryForestNode*, OffsetLength> locations;
46 };
47
48 // Used by the constructor to compute the edges from parents to children in
49 // |forest|.
50 static ChildrenLocations InitFromForest(
51 std::vector<DictionaryForestNode>* forest);
52
53 ChildrenLocations children_locations_;
54
55 DISALLOW_COPY_AND_ASSIGN(ChildrenRepresentation);
56 };
57
58 ChildrenRepresentation::ChildrenRepresentation(
59 std::vector<DictionaryForestNode>* forest)
60 : children_locations_(InitFromForest(forest)) {}
61
62 ChildrenRepresentation::~ChildrenRepresentation() {}
63
64 std::pair<ChildrenRepresentation::ChildIter, ChildrenRepresentation::ChildIter>
65 ChildrenRepresentation::Children(DictionaryForestNode* node) {
66 OffsetLength ol = children_locations_.locations.at(node);
67 ChildIter first_child = children_locations_.children.begin() + ol.offset;
68 return std::make_pair(first_child, first_child + ol.length);
69 }
70
71 ChildrenRepresentation::ChildrenLocations
72 ChildrenRepresentation::InitFromForest(
73 std::vector<DictionaryForestNode>* forest) {
74 ChildrenLocations result;
75 // There are always less children than all nodes in the |forest|, so resizing
76 // the vector below is a little wasteful. But it keeps the code simple.
77 result.children.resize(forest->size());
78
79 // In the first pass, only count the number of children.
80 for (DictionaryForestNode& node : *forest) {
81 DictionaryForestNode* parent = node.parent;
82 if (!parent)
83 continue;
84 ++result.locations[parent].length;
85 }
86
87 // In the second pass, allocate the offsets and initialize |free_slots|,
88 // which tells for every node where to put its next child in
89 // |result.children|.
90 size_t next_free = 0;
91 std::map<DictionaryForestNode*, size_t> free_slots;
92 for (DictionaryForestNode& node : *forest) {
93 result.locations[&node].offset = next_free;
94 free_slots[&node] = next_free;
95 next_free += result.locations[&node].length;
96 }
97
98 // In the last pass, populate |result.children|.
99 for (DictionaryForestNode& node : *forest) {
100 DictionaryForestNode* parent = node.parent;
101 if (!parent)
102 continue;
103 const size_t offset = free_slots[parent];
104 ++free_slots[parent];
105 result.children[offset] = &node;
106 }
107
108 return result;
109 }
110
111 // Appends |child_description| to a ListValue under key |child_key| in
112 // |parent->representation|, creating said ListValue if needed.
113 void InjectChildDescription(base::DictionaryValue child_description,
114 DictionaryForestNode* parent,
115 base::StringPiece child_key) {
116 base::ListValue* children_weak = nullptr;
117 base::DictionaryValue* parent_rep = &parent->representation;
118 if (!parent_rep->GetList(child_key, &children_weak)) {
119 auto children = base::MakeUnique<base::ListValue>();
120 children_weak = children.get();
121 parent_rep->Set(child_key, std::move(children));
122 }
123 children_weak->base::Value::GetList().push_back(std::move(child_description));
124 }
125
126 } // namespace
127
128 base::ListValue SerializeDictionaryForest(
129 std::vector<DictionaryForestNode> forest,
130 base::StringPiece child_key) {
131 // To serialize |forest|, DictionaryValue descriptions of child nodes need to
132 // be injected into DictionaryValue descriptions of their parents. That means
133 // the forest needs to be processed in topological order, understanding the
134 // direction of edges to go from child to parent.
135 //
136 // However, computing a topological order using Kahn's algorithm is easier
137 // for forests where the edge direction goes from parents to children: the
138 // condition for inserting a node into the resulting sorted list, "if node
139 // has no other incoming edges", is trivial, because every node has at most
140 // one parent and hence at most one incoming edge.
141 //
142 // Luckily, the reverse of a topological order of a graph is a topological
143 // order of the same graph with edges reversed. So here first the easy
144 // topological order for (parent -> child) edges is computed, and then
145 // processed in reverse to serialize the forest.
146 ChildrenRepresentation children(&forest);
147
148 std::vector<DictionaryForestNode*> sorted;
149 sorted.reserve(forest.size());
150
151 // Initialize the algorithm with nodes which come first in topological order.
152 std::vector<DictionaryForestNode*> no_incoming_edges;
153 no_incoming_edges.reserve(forest.size());
154 for (DictionaryForestNode& node : forest) {
155 if (!node.parent)
156 no_incoming_edges.push_back(&node);
157 }
158
159 // Iteratively add more nodes, respecting the topological order.
160 while (!no_incoming_edges.empty()) {
161 DictionaryForestNode* node = no_incoming_edges.back();
162 no_incoming_edges.pop_back();
163 sorted.push_back(node);
164 // Once |node| is removed, all of its children have 0 incoming edges.
165 auto child_range = children.Children(node);
166 no_incoming_edges.insert(no_incoming_edges.end(), child_range.first,
167 child_range.second);
168 }
169
170 // Now let's build the representation: injecting DictionaryValues of children
171 // into those of their parents, and adding the roots to the returned
172 // ListValue.
173 base::ListValue roots;
174 for (DictionaryForestNode* node : base::Reversed(sorted)) {
175 DictionaryForestNode* parent = node->parent;
176 if (parent) {
177 InjectChildDescription(std::move(node->representation), parent,
178 child_key);
179 } else {
180 roots.base::Value::GetList().push_back(std::move(node->representation));
181 }
182 }
183
184 return roots;
185 }
OLDNEW
« 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