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

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

Powered by Google App Engine
This is Rietveld 408576698