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