Chromium Code Reviews| 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 |