mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2025-01-08 19:21:44 +00:00
127 lines
3.3 KiB
Ruby
127 lines
3.3 KiB
Ruby
|
|
module CompTree
|
|
#
|
|
# Driver is the main interface to the computation tree. It is
|
|
# responsible for defining nodes and running computations.
|
|
#
|
|
class Driver
|
|
#
|
|
# See CompTree.build
|
|
#
|
|
def initialize(opts = {}) #:nodoc:
|
|
@node_class = opts[:node_class] || Node
|
|
@nodes = Hash.new
|
|
end
|
|
|
|
#
|
|
# Name-to-node hash.
|
|
#
|
|
attr_reader :nodes
|
|
|
|
#
|
|
# _name_ -- unique node identifier (for example a symbol).
|
|
#
|
|
# _child_names_ -- unique node identifiers of children.
|
|
#
|
|
# Define a computation node.
|
|
#
|
|
# During a computation, the results of the child nodes are passed
|
|
# to the block. The block returns the result of this node's
|
|
# computation.
|
|
#
|
|
# In the following example, a computation node named +:area+ is
|
|
# defined which depends on the nodes named +:width+ and +:height+.
|
|
#
|
|
# driver.define(:area, :width, :height) { |w, h|
|
|
# w*h
|
|
# }
|
|
#
|
|
def define(name, *child_names, &block)
|
|
#
|
|
# retrieve or create node
|
|
#
|
|
node = @nodes[name] ||= @node_class.new(name)
|
|
raise RedefinitionError.new(node.name) if node.function
|
|
node.function = block
|
|
|
|
#
|
|
# retrieve or create children
|
|
#
|
|
children = child_names.map { |child_name|
|
|
@nodes[child_name] ||= @node_class.new(child_name)
|
|
}
|
|
|
|
#
|
|
# link
|
|
#
|
|
node.children = children
|
|
children.each { |child| child.parents << node }
|
|
|
|
node
|
|
end
|
|
|
|
#
|
|
# _name_ -- unique node identifier (for example a symbol).
|
|
#
|
|
# Mark this node and all its children as uncomputed.
|
|
#
|
|
def reset(name)
|
|
@nodes[name].reset
|
|
end
|
|
|
|
#
|
|
# _name_ -- unique node identifier (for example a symbol).
|
|
#
|
|
# Check for a cyclic graph below the given node. If found,
|
|
# returns the names of the nodes (in order) which form a loop.
|
|
# Otherwise returns nil.
|
|
#
|
|
def check_circular(name)
|
|
helper = Proc.new { |root, chain|
|
|
if chain.include? root
|
|
return chain + [root]
|
|
end
|
|
@nodes[root].children.each { |child|
|
|
helper.call(child.name, chain + [root])
|
|
}
|
|
}
|
|
helper.call(name, [])
|
|
nil
|
|
end
|
|
|
|
#
|
|
# _name_ -- unique node identifier (for example a symbol).
|
|
#
|
|
# _max_threads_ -- maximum number of threads, or +0+ to indicate
|
|
# no limit.
|
|
#
|
|
# Compute the tree below _name_ and return the result.
|
|
#
|
|
# If a node's computation raises an exception, the exception will
|
|
# be transferred to the caller of compute(). The tree will be
|
|
# left in a dirty state so that individual nodes may be examined.
|
|
# It is your responsibility to call reset() before attempting the
|
|
# computation again, otherwise the result will be undefined.
|
|
#
|
|
def compute(name, max_threads)
|
|
begin
|
|
max_threads = max_threads.to_int
|
|
rescue NoMethodError
|
|
raise TypeError, "can't convert #{max_threads.class} into Integer"
|
|
end
|
|
if max_threads < 0
|
|
raise RangeError, "max threads must be nonnegative"
|
|
end
|
|
|
|
root = @nodes[name] or raise NoNodeError.new(name)
|
|
if root.computed
|
|
root.result
|
|
elsif max_threads == 1
|
|
root.compute_now
|
|
else
|
|
Algorithm.compute_parallel(root, max_threads == 0 ? nil : max_threads)
|
|
end
|
|
end
|
|
end
|
|
end
|