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--
- }
|