| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 | 
							- 'use strict'
 
- var transport = require('../spdy-transport')
 
- var utils = transport.utils
 
- var assert = require('assert')
 
- var debug = require('debug')('spdy:priority')
 
- function PriorityNode (tree, options) {
 
-   this.tree = tree
 
-   this.id = options.id
 
-   this.parent = options.parent
 
-   this.weight = options.weight
 
-   // To be calculated in `addChild`
 
-   this.priorityFrom = 0
 
-   this.priorityTo = 1
 
-   this.priority = 1
 
-   this.children = {
 
-     list: [],
 
-     weight: 0
 
-   }
 
-   if (this.parent !== null) {
 
-     this.parent.addChild(this)
 
-   }
 
- }
 
- function compareChildren (a, b) {
 
-   return a.weight === b.weight ? a.id - b.id : a.weight - b.weight
 
- }
 
- PriorityNode.prototype.toJSON = function toJSON () {
 
-   return {
 
-     parent: this.parent,
 
-     weight: this.weight,
 
-     exclusive: this.exclusive
 
-   }
 
- }
 
- PriorityNode.prototype.getPriority = function getPriority () {
 
-   return this.priority
 
- }
 
- PriorityNode.prototype.getPriorityRange = function getPriorityRange () {
 
-   return { from: this.priorityFrom, to: this.priorityTo }
 
- }
 
- PriorityNode.prototype.addChild = function addChild (child) {
 
-   child.parent = this
 
-   utils.binaryInsert(this.children.list, child, compareChildren)
 
-   this.children.weight += child.weight
 
-   this._updatePriority(this.priorityFrom, this.priorityTo)
 
- }
 
- PriorityNode.prototype.remove = function remove () {
 
-   assert(this.parent, 'Can\'t remove root node')
 
-   this.parent.removeChild(this)
 
-   this.tree._removeNode(this)
 
-   // Move all children to the parent
 
-   for (var i = 0; i < this.children.list.length; i++) {
 
-     this.parent.addChild(this.children.list[i])
 
-   }
 
- }
 
- PriorityNode.prototype.removeChild = function removeChild (child) {
 
-   this.children.weight -= child.weight
 
-   var index = utils.binarySearch(this.children.list, child, compareChildren)
 
-   if (index !== -1 && this.children.list.length >= index) {
 
-     this.children.list.splice(index, 1)
 
-   }
 
- }
 
- PriorityNode.prototype.removeChildren = function removeChildren () {
 
-   var children = this.children.list
 
-   this.children.list = []
 
-   this.children.weight = 0
 
-   return children
 
- }
 
- PriorityNode.prototype._updatePriority = function _updatePriority (from, to) {
 
-   this.priority = to - from
 
-   this.priorityFrom = from
 
-   this.priorityTo = to
 
-   var weight = 0
 
-   for (var i = 0; i < this.children.list.length; i++) {
 
-     var node = this.children.list[i]
 
-     var nextWeight = weight + node.weight
 
-     node._updatePriority(
 
-       from + this.priority * (weight / this.children.weight),
 
-       from + this.priority * (nextWeight / this.children.weight)
 
-     )
 
-     weight = nextWeight
 
-   }
 
- }
 
- function PriorityTree (options) {
 
-   this.map = {}
 
-   this.list = []
 
-   this.defaultWeight = options.defaultWeight || 16
 
-   this.count = 0
 
-   this.maxCount = options.maxCount
 
-   // Root
 
-   this.root = this.add({
 
-     id: 0,
 
-     parent: null,
 
-     weight: 1
 
-   })
 
- }
 
- module.exports = PriorityTree
 
- PriorityTree.create = function create (options) {
 
-   return new PriorityTree(options)
 
- }
 
- PriorityTree.prototype.add = function add (options) {
 
-   if (options.id === options.parent) {
 
-     return this.addDefault(options.id)
 
-   }
 
-   var parent = options.parent === null ? null : this.map[options.parent]
 
-   if (parent === undefined) {
 
-     return this.addDefault(options.id)
 
-   }
 
-   debug('add node=%d parent=%d weight=%d exclusive=%d',
 
-     options.id,
 
-     options.parent === null ? -1 : options.parent,
 
-     options.weight || this.defaultWeight,
 
-     options.exclusive ? 1 : 0)
 
-   var children
 
-   if (options.exclusive) {
 
-     children = parent.removeChildren()
 
-   }
 
-   var node = new PriorityNode(this, {
 
-     id: options.id,
 
-     parent: parent,
 
-     weight: options.weight || this.defaultWeight
 
-   })
 
-   this.map[options.id] = node
 
-   if (options.exclusive) {
 
-     for (var i = 0; i < children.length; i++) {
 
-       node.addChild(children[i])
 
-     }
 
-   }
 
-   this.count++
 
-   if (this.count > this.maxCount) {
 
-     debug('hit maximum remove id=%d', this.list[0].id)
 
-     this.list.shift().remove()
 
-   }
 
-   // Root node is not subject to removal
 
-   if (node.parent !== null) {
 
-     this.list.push(node)
 
-   }
 
-   return node
 
- }
 
- // Only for testing, should use `node`'s methods
 
- PriorityTree.prototype.get = function get (id) {
 
-   return this.map[id]
 
- }
 
- PriorityTree.prototype.addDefault = function addDefault (id) {
 
-   debug('creating default node')
 
-   return this.add({ id: id, parent: 0, weight: this.defaultWeight })
 
- }
 
- PriorityTree.prototype._removeNode = function _removeNode (node) {
 
-   delete this.map[node.id]
 
-   var index = utils.binarySearch(this.list, node, compareChildren)
 
-   this.list.splice(index, 1)
 
-   this.count--
 
- }
 
 
  |