Re-land: C++ readability review
[chromium-blink-merge.git] / net / spdy / spdy_priority_forest.h
blob8624bbfe8eeac441b372bb7b2815035c5810a33a
1 // Copyright (c) 2013 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.
5 #ifndef NET_SPDY_SPDY_PRIORITY_FOREST_H_
6 #define NET_SPDY_SPDY_PRIORITY_FOREST_H_
8 #include <map>
9 #include <set>
10 #include <vector>
12 #include "base/basictypes.h"
13 #include "base/containers/hash_tables.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/rand_util.h"
18 namespace net {
20 // This data structure implements the SPDY prioriziation data structures
21 // defined in this document: http://go/spdy4-prioritization
23 // Nodes can be added and removed, and dependencies between them defined. Each
24 // node can have at most one parent and at most one child (forming a list), but
25 // there can be multiple lists, with each list root having its own priority.
26 // Individual nodes can also be marked as ready to read/write, and then the
27 // whole structure can be queried to pick the next node to read/write out of
28 // those ready.
30 // The NodeId and Priority types must be POD that support comparison (most
31 // likely, they will be numbers).
32 template <typename NodeId, typename Priority>
33 class SpdyPriorityForest {
34 public:
35 SpdyPriorityForest();
36 ~SpdyPriorityForest();
38 // Return the number of nodes currently in the forest.
39 int num_nodes() const;
41 // Return true if the forest contains a node with the given ID.
42 bool NodeExists(NodeId node_id) const;
44 // Add a new root node to the forest, with the given priority. Returns true
45 // on success, or false if the node_id already exists within the forest.
46 bool AddRootNode(NodeId node_id, Priority priority);
48 // Add a new node to the forest, with the given parent. Returns true on
49 // success. Returns false and has no effect if the new node already exists,
50 // or if the parent doesn't exist, or if the parent already has a child.
51 bool AddNonRootNode(NodeId node_id, NodeId parent_id, bool unordered);
53 // Remove an existing node from the forest. Returns true on success, or
54 // false if the node doesn't exist.
55 bool RemoveNode(NodeId node_id);
57 // Get the priority of the given node. If the node doesn't exist, or is not
58 // a root node (and thus has no priority), returns Priority().
59 Priority GetPriority(NodeId node_id) const;
61 // Get the parent of the given node. If the node doesn't exist, or is a root
62 // node (and thus has no parent), returns NodeId().
63 NodeId GetParent(NodeId node_id) const;
65 // Determine if the given node is unordered with respect to its parent. If
66 // the node doesn't exist, or is a root node (and thus has no parent),
67 // returns false.
68 bool IsNodeUnordered(NodeId node_id) const;
70 // Get the child of the given node. If the node doesn't exist, or has no
71 // child, returns NodeId().
72 NodeId GetChild(NodeId node_id) const;
74 // Set the priority of the given node. If the node was not already a root
75 // node, this makes it a root node. Returns true on success, or false if the
76 // node doesn't exist.
77 bool SetPriority(NodeId node_id, Priority priority);
79 // Set the parent of the given node. If the node was a root node, this makes
80 // it no longer a root. Returns true on success. Returns false and has no
81 // effect if (1) the node and/or the parent doesn't exist, (2) the new parent
82 // already has a different child than the node, or (3) if the new parent is a
83 // descendant of the node (so this would have created a cycle).
84 bool SetParent(NodeId node_id, NodeId parent_id, bool unordered);
86 // Check if a node is marked as ready to read. Returns false if the node
87 // doesn't exist.
88 bool IsMarkedReadyToRead(NodeId node_id) const;
89 // Mark the node as ready or not ready to read. Returns true on success, or
90 // false if the node doesn't exist.
91 bool MarkReadyToRead(NodeId node_id);
92 bool MarkNoLongerReadyToRead(NodeId node_id);
93 // Return the ID of the next node that we should read, or return NodeId() if
94 // no node in the forest is ready to read.
95 NodeId NextNodeToRead();
97 // Check if a node is marked as ready to write. Returns false if the node
98 // doesn't exist.
99 bool IsMarkedReadyToWrite(NodeId node_id) const;
100 // Mark the node as ready or not ready to write. Returns true on success, or
101 // false if the node doesn't exist.
102 bool MarkReadyToWrite(NodeId node_id);
103 bool MarkNoLongerReadyToWrite(NodeId node_id);
104 // Return the ID of the next node that we should write, or return NodeId() if
105 // no node in the forest is ready to write.
106 NodeId NextNodeToWrite();
108 // Return true if all internal invariants hold (useful for unit tests).
109 // Unless there are bugs, this should always return true.
110 bool ValidateInvariantsForTests() const;
112 private:
113 enum NodeType { ROOT_NODE, NONROOT_ORDERED, NONROOT_UNORDERED };
114 struct Node {
115 Node() : type(ROOT_NODE), flags(0), child() {
116 depends_on.priority = Priority();
118 NodeType type;
119 unsigned int flags; // bitfield of flags
120 union {
121 Priority priority; // used for root nodes
122 NodeId parent_id; // used for non-root nodes
123 } depends_on;
124 NodeId child; // node ID of child (or NodeId() for no child)
127 typedef base::hash_map<NodeId, Node> NodeMap;
129 // Constants for the Node.flags bitset:
130 // kReadToRead: set for nodes that are ready for reading
131 static const unsigned int kReadyToRead = (1 << 0);
132 // kReadToWrite: set for nodes that are ready for writing
133 static const unsigned int kReadyToWrite = (1 << 1);
135 // Common code for IsMarkedReadyToRead and IsMarkedReadyToWrite.
136 bool IsMarked(NodeId node_id, unsigned int flag) const;
137 // Common code for MarkReadyToRead and MarkReadyToWrite.
138 bool Mark(NodeId node_id, unsigned int flag);
139 // Common code for MarkNoLongerReadyToRead and MarkNoLongerReadyToWrite.
140 bool Unmark(NodeId node_id, unsigned int flag);
141 // Common code for NextNodeToRead and NextNodeToWrite;
142 NodeId FirstMarkedNode(unsigned int flag);
143 // Get the given node, or return NULL if it doesn't exist.
144 const Node* FindNode(NodeId node_id) const;
146 NodeMap all_nodes_; // maps from node IDs to Node objects
148 DISALLOW_COPY_AND_ASSIGN(SpdyPriorityForest);
151 template <typename NodeId, typename Priority>
152 SpdyPriorityForest<NodeId, Priority>::SpdyPriorityForest() {}
154 template <typename NodeId, typename Priority>
155 SpdyPriorityForest<NodeId, Priority>::~SpdyPriorityForest() {}
157 template <typename NodeId, typename Priority>
158 int SpdyPriorityForest<NodeId, Priority>::num_nodes() const {
159 return all_nodes_.size();
162 template <typename NodeId, typename Priority>
163 bool SpdyPriorityForest<NodeId, Priority>::NodeExists(NodeId node_id) const {
164 return all_nodes_.count(node_id) != 0;
167 template <typename NodeId, typename Priority>
168 bool SpdyPriorityForest<NodeId, Priority>::AddRootNode(
169 NodeId node_id, Priority priority) {
170 if (NodeExists(node_id)) {
171 return false;
173 Node* new_node = &all_nodes_[node_id];
174 new_node->type = ROOT_NODE;
175 new_node->depends_on.priority = priority;
176 return true;
179 template <typename NodeId, typename Priority>
180 bool SpdyPriorityForest<NodeId, Priority>::AddNonRootNode(
181 NodeId node_id, NodeId parent_id, bool unordered) {
182 if (NodeExists(node_id) || !NodeExists(parent_id)) {
183 return false;
186 Node* parent = &all_nodes_[parent_id];
187 if (parent->child != NodeId()) {
188 return false;
191 Node* new_node = &all_nodes_[node_id];
192 new_node->type = (unordered ? NONROOT_UNORDERED : NONROOT_ORDERED);
193 new_node->depends_on.parent_id = parent_id;
194 parent->child = node_id;
195 return true;
198 template <typename NodeId, typename Priority>
199 bool SpdyPriorityForest<NodeId, Priority>::RemoveNode(NodeId node_id) {
200 if (!NodeExists(node_id)) {
201 return false;
203 const Node& node = all_nodes_[node_id];
205 // If the node to be removed is not a root node, we need to change its
206 // parent's child ID.
207 if (node.type != ROOT_NODE) {
208 DCHECK(NodeExists(node.depends_on.parent_id));
209 Node* parent = &all_nodes_[node.depends_on.parent_id];
210 DCHECK_EQ(node_id, parent->child);
211 parent->child = node.child;
214 // If the node has a child, we need to change the child's priority or parent.
215 if (node.child != NodeId()) {
216 DCHECK(NodeExists(node.child));
217 Node* child = &all_nodes_[node.child];
218 DCHECK_NE(ROOT_NODE, child->type);
219 DCHECK_EQ(node_id, child->depends_on.parent_id);
220 // Make the child's new depends_on be the node's depends_on (whether that
221 // be a priority or a parent node ID).
222 child->depends_on = node.depends_on;
223 // If the removed node was a root, its child is now a root. Otherwise, the
224 // child will be be unordered if and only if it was already unordered and
225 // the removed not is also not ordered.
226 if (node.type == ROOT_NODE) {
227 child->type = ROOT_NODE;
228 } else if (node.type == NONROOT_ORDERED) {
229 child->type = NONROOT_ORDERED;
233 // Delete the node.
234 all_nodes_.erase(node_id);
235 return true;
238 template <typename NodeId, typename Priority>
239 Priority SpdyPriorityForest<NodeId, Priority>::GetPriority(
240 NodeId node_id) const {
241 const Node* node = FindNode(node_id);
242 if (node != NULL && node->type == ROOT_NODE) {
243 return node->depends_on.priority;
244 } else {
245 return Priority();
249 template <typename NodeId, typename Priority>
250 NodeId SpdyPriorityForest<NodeId, Priority>::GetParent(NodeId node_id) const {
251 const Node* node = FindNode(node_id);
252 if (node != NULL && node->type != ROOT_NODE) {
253 return node->depends_on.parent_id;
254 } else {
255 return NodeId();
259 template <typename NodeId, typename Priority>
260 bool SpdyPriorityForest<NodeId, Priority>::IsNodeUnordered(
261 NodeId node_id) const {
262 const Node* node = FindNode(node_id);
263 return node != NULL && node->type == NONROOT_UNORDERED;
266 template <typename NodeId, typename Priority>
267 NodeId SpdyPriorityForest<NodeId, Priority>::GetChild(NodeId node_id) const {
268 const Node* node = FindNode(node_id);
269 if (node != NULL) {
270 return node->child;
271 } else {
272 return NodeId();
276 template <typename NodeId, typename Priority>
277 bool SpdyPriorityForest<NodeId, Priority>::SetPriority(
278 NodeId node_id, Priority priority) {
279 if (!NodeExists(node_id)) {
280 return false;
283 Node* node = &all_nodes_[node_id];
284 // If this is not already a root node, we need to make it be a root node.
285 if (node->type != ROOT_NODE) {
286 DCHECK(NodeExists(node->depends_on.parent_id));
287 Node* parent = &all_nodes_[node->depends_on.parent_id];
288 parent->child = NodeId();
289 node->type = ROOT_NODE;
292 node->depends_on.priority = priority;
293 return true;
296 template <typename NodeId, typename Priority>
297 bool SpdyPriorityForest<NodeId, Priority>::SetParent(
298 NodeId node_id, NodeId parent_id, bool unordered) {
299 if (!NodeExists(node_id) || !NodeExists(parent_id)) {
300 return false;
303 Node* node = &all_nodes_[node_id];
304 Node* new_parent = &all_nodes_[parent_id];
305 // If the new parent is already the node's parent, all we have to do is
306 // update the node type and we're done.
307 if (new_parent->child == node_id) {
308 node->type = (unordered ? NONROOT_UNORDERED : NONROOT_ORDERED);
309 return true;
311 // Otherwise, if the new parent already has a child, we fail.
312 if (new_parent->child != NodeId()) {
313 return false;
316 // Next, make sure we won't create a cycle.
317 if (node_id == parent_id) return false;
318 Node* last = node;
319 NodeId last_id = node_id;
320 while (last->child != NodeId()) {
321 if (last->child == parent_id) return false;
322 last_id = last->child;
323 DCHECK(NodeExists(last_id));
324 last = &all_nodes_[last_id];
327 // If the node is not a root, we need clear its old parent's child field
328 // (unless the old parent is the same as the new parent).
329 if (node->type != ROOT_NODE) {
330 const NodeId old_parent_id = node->depends_on.parent_id;
331 DCHECK(NodeExists(old_parent_id));
332 DCHECK(old_parent_id != parent_id);
333 Node* old_parent = &all_nodes_[old_parent_id];
334 DCHECK_EQ(node_id, old_parent->child);
335 old_parent->child = NodeId();
338 // Make the change.
339 node->type = (unordered ? NONROOT_UNORDERED : NONROOT_ORDERED);
340 node->depends_on.parent_id = parent_id;
341 new_parent->child = node_id;
342 return true;
345 template <typename NodeId, typename Priority>
346 bool SpdyPriorityForest<NodeId, Priority>::IsMarkedReadyToRead(
347 NodeId node_id) const {
348 return IsMarked(node_id, kReadyToRead);
351 template <typename NodeId, typename Priority>
352 bool SpdyPriorityForest<NodeId, Priority>::MarkReadyToRead(NodeId node_id) {
353 return Mark(node_id, kReadyToRead);
356 template <typename NodeId, typename Priority>
357 bool SpdyPriorityForest<NodeId, Priority>::MarkNoLongerReadyToRead(
358 NodeId node_id) {
359 return Unmark(node_id, kReadyToRead);
362 template <typename NodeId, typename Priority>
363 NodeId SpdyPriorityForest<NodeId, Priority>::NextNodeToRead() {
364 return FirstMarkedNode(kReadyToRead);
367 template <typename NodeId, typename Priority>
368 bool SpdyPriorityForest<NodeId, Priority>::IsMarkedReadyToWrite(
369 NodeId node_id) const {
370 return IsMarked(node_id, kReadyToWrite);
373 template <typename NodeId, typename Priority>
374 bool SpdyPriorityForest<NodeId, Priority>::MarkReadyToWrite(NodeId node_id) {
375 return Mark(node_id, kReadyToWrite);
378 template <typename NodeId, typename Priority>
379 bool SpdyPriorityForest<NodeId, Priority>::MarkNoLongerReadyToWrite(
380 NodeId node_id) {
381 return Unmark(node_id, kReadyToWrite);
384 template <typename NodeId, typename Priority>
385 NodeId SpdyPriorityForest<NodeId, Priority>::NextNodeToWrite() {
386 return FirstMarkedNode(kReadyToWrite);
389 template <typename NodeId, typename Priority>
390 bool SpdyPriorityForest<NodeId, Priority>::IsMarked(
391 NodeId node_id, unsigned int flag) const {
392 const Node* node = FindNode(node_id);
393 return node != NULL && (node->flags & flag) != 0;
396 template <typename NodeId, typename Priority>
397 bool SpdyPriorityForest<NodeId, Priority>::Mark(
398 NodeId node_id, unsigned int flag) {
399 if (!NodeExists(node_id)) {
400 return false;
402 all_nodes_[node_id].flags |= flag;
403 return true;
406 template <typename NodeId, typename Priority>
407 bool SpdyPriorityForest<NodeId, Priority>::Unmark(
408 NodeId node_id, unsigned int flag) {
409 if (!NodeExists(node_id)) {
410 return false;
412 all_nodes_[node_id].flags &= ~flag;
413 return true;
416 template <typename NodeId, typename Priority>
417 NodeId SpdyPriorityForest<NodeId, Priority>::FirstMarkedNode(
418 unsigned int flag) {
419 // TODO(mdsteele): This is an *incredibly* stupid brute force solution.
421 // Get all root nodes that have at least one marked child.
422 uint64 total_weight = 0;
423 std::map<uint64, NodeId> roots; // maps cumulative weight to root node ID
424 for (typename NodeMap::const_iterator iter = all_nodes_.begin();
425 iter != all_nodes_.end(); ++iter) {
426 const NodeId root_id = iter->first;
427 const Node& root = iter->second;
428 if (root.type == ROOT_NODE) {
429 // See if there is at least one marked node in this root's chain.
430 for (const Node* node = &root; ; node = &all_nodes_[node->child]) {
431 if ((node->flags & flag) != 0) {
432 total_weight += static_cast<uint64>(root.depends_on.priority);
433 roots[total_weight] = root_id;
434 break;
436 if (node->child == NodeId()) {
437 break;
439 DCHECK(NodeExists(node->child));
444 // If there are no ready nodes, then return NodeId().
445 if (total_weight == 0) {
446 DCHECK(roots.empty());
447 return NodeId();
448 } else {
449 DCHECK(!roots.empty());
452 // Randomly select a tree to use.
453 typename std::map<uint64, NodeId>::const_iterator root_iter =
454 roots.upper_bound(base::RandGenerator(total_weight));
455 DCHECK(root_iter != roots.end());
456 const NodeId root_id = root_iter->second;
458 // Find the first node in the chain that is ready.
459 NodeId node_id = root_id;
460 while (true) {
461 DCHECK(NodeExists(node_id));
462 Node* node = &all_nodes_[node_id];
463 if ((node->flags & flag) != 0) {
464 // There might be more nodes that are ready and that are linked to this
465 // one in an unordered chain. Find all of them, then pick one randomly.
466 std::vector<NodeId> group;
467 group.push_back(node_id);
468 for (Node* next = node; next->child != NodeId();) {
469 DCHECK(NodeExists(next->child));
470 Node *child = &all_nodes_[next->child];
471 DCHECK_NE(ROOT_NODE, child->type);
472 if (child->type != NONROOT_UNORDERED) {
473 break;
475 if ((child->flags & flag) != 0) {
476 group.push_back(next->child);
478 next = child;
480 return group[base::RandGenerator(group.size())];
482 node_id = node->child;
486 template <typename NodeId, typename Priority>
487 const typename SpdyPriorityForest<NodeId, Priority>::Node*
488 SpdyPriorityForest<NodeId, Priority>::FindNode(NodeId node_id) const {
489 typename NodeMap::const_iterator iter = all_nodes_.find(node_id);
490 if (iter == all_nodes_.end()) {
491 return NULL;
493 return &iter->second;
496 template <typename NodeId, typename Priority>
497 bool SpdyPriorityForest<NodeId, Priority>::ValidateInvariantsForTests() const {
498 for (typename NodeMap::const_iterator iter = all_nodes_.begin();
499 iter != all_nodes_.end(); ++iter) {
500 const NodeId node_id = iter->first;
501 const Node& node = iter->second;
502 if (node.type != ROOT_NODE &&
503 (!NodeExists(node.depends_on.parent_id) ||
504 GetChild(node.depends_on.parent_id) != node_id)) {
505 return false;
507 if (node.child != NodeId()) {
508 if (!NodeExists(node.child) || node_id != GetParent(node.child)) {
509 return false;
513 NodeId child_id = node.child;
514 int count = 0;
515 while (child_id != NodeId()) {
516 if (count > num_nodes() || node_id == child_id) {
517 return false;
519 child_id = GetChild(child_id);
520 ++count;
523 return true;
526 } // namespace net
528 #endif // NET_SPDY_SPDY_PRIORITY_FOREST_H_