OLD | NEW |
---|---|
(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 } | |
OLD | NEW |