class Protocol::HTTP2::Dependency

def self.create(connection, id, priority = nil)

def self.create(connection, id, priority = nil)
	weight = DEFAULT_WEIGHT
	exclusive = false
	
	if priority
		if parent = connection.dependencies[priority.stream_dependency]
			exclusive = priority.exclusive
		end
		
		weight = priority.weight
	end
	
	if parent.nil?
		parent = connection.dependency
	end
	
	dependency = self.new(connection, id, weight)
	
	connection.dependencies[id] = dependency
	
	if exclusive
		parent.exclusive_child(dependency)
	else
		parent.add_child(dependency)
	end
	
	return dependency
end

def <=> other

def <=> other
	@weight <=> other.weight
end

def == other

def == other
	@id == other.id
end

def add_child(dependency)

def add_child(dependency)
	@children ||= {}
	@children[dependency.id] = dependency
	
	dependency.parent = self
	
	if @ordered_children
		# Binary search for insertion point:
		index = @ordered_children.bsearch_index do |child|
			child.weight >= dependency.weight
		end
		
		if index
			@ordered_children.insert(index, dependency)
		else
			@ordered_children.push(dependency)
		end
		
		@total_weight += dependency.weight
	end
end

def clear_cache!

def clear_cache!
	@ordered_children = nil
end

def consume_window(size)

Parameters:
  • amount (Integer) -- the amount of data to write. Defaults to the current window capacity.
def consume_window(size)
	# If there is an associated stream, give it priority:
	if stream = self.stream
		return if stream.window_updated(size)
	end
	
	# Otherwise, allow the dependent children to use up the available window:
	self.ordered_children&.delete_if do |child|
		if child.parent
			# Compute the proportional allocation:
			allocated = (child.weight * size) / @total_weight
			
			child.consume_window(allocated) if allocated > 0
			
			false
		else
			true
		end
	end
end

def delete!

def delete!
	@connection.dependencies.delete(@id)
	
	@parent.remove_child(self)
	
	@children&.each do |id, child|
		parent.add_child(child)
	end
	
	@connection = nil
	@parent = nil
	@children = nil
end

def exclusive_child(parent)

Parameters:
  • parent (Dependency) -- the dependency which will be inserted, taking control of all current children.
def exclusive_child(parent)
	parent.children = @children
	
	@children&.each_value do |child|
		child.parent = parent
	end
	
	parent.clear_cache!
	
	@children = {parent.id => parent}
	self.clear_cache!
	
	parent.parent = self
end

def initialize(connection, id, weight = DEFAULT_WEIGHT)

def initialize(connection, id, weight = DEFAULT_WEIGHT)
	@connection = connection
	@id = id
	
	@parent = nil
	@children = nil
	
	@weight = weight
	
	# Cache of any associated stream:
	@stream = nil
	
	# Cache of children for window allocation:
	@total_weight = 0
	@ordered_children = nil
end

def inspect

def inspect
	"\#<#{self.class} id=#{@id} parent id=#{@parent&.id} weight=#{@weight} #{@children&.size || 0} children>"
end

def ordered_children

def ordered_children
	unless @ordered_children
		if @children and !@children.empty?
			@ordered_children = @children.values.sort
			@total_weight = @ordered_children.sum(&:weight)
		end
	end
	
	return @ordered_children
end

def print_hierarchy(output = $stderr, indent: 0)

def print_hierarchy(output = $stderr, indent: 0)
	output.puts "#{"\t" * indent}#{self.inspect}"
	@children&.each_value do |child|
		child.print_hierarchy(output, indent: indent+1)
	end
end

def priority(exclusive = false)

The current local priority of the stream.
def priority(exclusive = false)
	Priority.new(exclusive, @parent.id, @weight)
end

def priority= priority

Change the priority of the stream both locally and remotely.
def priority= priority
	send_priority(priority)
	process_priority(priority)
end

def process_priority(priority)

def process_priority(priority)
	dependent_id = priority.stream_dependency
	
	if dependent_id == @id
		raise ProtocolError, "Stream priority for stream id #{@id} cannot depend on itself!"
	end
	
	@weight = priority.weight
	
	# We essentially ignore `dependent_id` if the dependency does not exist:
	if parent = @connection.dependencies[dependent_id]
		if priority.exclusive
			@parent.remove_child(self)
			
			parent.exclusive_child(self)
		elsif !@parent.equal?(parent)
			@parent.remove_child(self)
			
			parent.add_child(self)
		end
	end
end

def receive_priority(frame)

def receive_priority(frame)
	self.process_priority(frame.unpack)
end

def remove_child(dependency)

def remove_child(dependency)
	@children&.delete(dependency.id)
	
	if @ordered_children
		# Don't do a linear search here, it can be slow. Instead, the child's parent will be set to `nil`, and we check this in {#consume_window} using `delete_if`.
		# @ordered_children.delete(dependency)
		@total_weight -= dependency.weight
	end
end

def send_priority(priority)

def send_priority(priority)
	@connection.send_priority(@id, priority)
end

def stream

def stream
	@stream ||= @connection.streams[@id]
end

def total_weight

def total_weight
	self.ordered_children
	
	return @total_weight
end