class QuartzTorrent::PieceIO
Can read and write pieces and blocks of a torrent.
Attributes
Get the overall length of the torrent data
Public Class Methods
Create a new PieceIO
that will map to files inside ‘baseDirectory’. Parameter ‘torrinfo’ should be a Metainfo::Info
object (the info part of the metainfo).
# File lib/quartz_torrent/filemanager.rb, line 148 def initialize(baseDirectory, torrinfo, ioManager = IOManager.new) @baseDirectory = baseDirectory @torrinfo = torrinfo @pieceMapper = PieceMapper.new(baseDirectory, torrinfo) @ioManager = ioManager @logger = LogManager.getLogger("pieceio") @torrentDataLength = torrinfo.dataLength end
Public Instance Methods
# File lib/quartz_torrent/filemanager.rb, line 214 def flush @ioManager.flush end
Read a block from a completed piece. Returns nil if the block doesn’t exist yet. Throws exceptions on error (for example, opening a file failed)
# File lib/quartz_torrent/filemanager.rb, line 203 def readBlock(pieceIndex, offset, length) readRegions @pieceMapper.findBlock(pieceIndex, offset, length) end
Read a piece. Returns nil if the piece is not yet present. NOTE: this method expects that if the ioManager is a reactor iomanager, that the io was set with errorHandler=false so that we get the EOF errors.
# File lib/quartz_torrent/filemanager.rb, line 210 def readPiece(pieceIndex) readRegions @pieceMapper.findPiece(pieceIndex) end
Write a block to an in-progress piece. The block is written to piece ‘peiceIndex’, at offset ‘offset’. The block data is in block. Throws exceptions on failure.
# File lib/quartz_torrent/filemanager.rb, line 163 def writeBlock(pieceIndex, offset, block) regions = @pieceMapper.findBlock(pieceIndex, offset, block.length) indexInBlock = 0 regions.each do |region| # Get the IO for the file with path 'path'. If we are being used in a reactor, this is the IO facade. If we # are not then this is a real IO. io = @ioManager.get(region.path) if ! io # No IO for this file. raise "This process doesn't have write permission for the file #{region.path}" if File.exists?(region.path) && ! File.writable?(region.path) # Ensure parent directories exist. dir = File.dirname region.path FileUtils.mkdir_p dir if ! File.directory?(dir) begin io = @ioManager.open(region.path) rescue @logger.error "Opening file #{region.path} failed: #{$!}" raise "Opening file #{region.path} failed" end end io.seek region.offset, IO::SEEK_SET begin io.write(block[indexInBlock, region.length]) indexInBlock += region.length rescue # Error when writing... @logger.error "Writing block to file #{region.path} failed: #{$!}" piece = nil break end break if indexInBlock >= block.length end end
Private Instance Methods
Pass an ordered list of FileRegions to load.
# File lib/quartz_torrent/filemanager.rb, line 220 def readRegions(regions) piece = "" regions.each do |region| # Get the IO for the file with path 'path'. If we are being used in a reactor, this is the IO facade. If we # are not then this is a real IO. io = @ioManager.get(region.path) if ! io # No IO for this file. if ! File.exists?(region.path) # This file hasn't been created yet by having blocks written to it. piece = nil break end raise "This process doesn't have read permission for the file #{region.path}" if ! File.readable?(region.path) begin io = @ioManager.open(region.path) rescue @logger.error "Opening file #{region.path} failed: #{$!}" raise "Opening file #{region.path} failed" end end io.seek region.offset, IO::SEEK_SET begin piece << io.read(region.length) rescue # Error when reading. Likely EOF, meaning this peice isn't all there yet. piece = nil break end end piece end