X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=file.go;h=bea4b13655d6024915434eecc66eeb0cab6524ad;hb=HEAD;hp=f57cd7af8a35b8491040fbd51e3e4fba88d611b1;hpb=d3a1c79c7985f2bf973f5d6cc788a5dd067f1426;p=btrtrc.git diff --git a/file.go b/file.go index f57cd7af..bea4b136 100644 --- a/file.go +++ b/file.go @@ -1,52 +1,128 @@ package torrent import ( - "strings" + "github.com/RoaringBitmap/roaring" + "github.com/anacrolix/missinggo/v2/bitmap" "github.com/anacrolix/torrent/metainfo" ) // Provides access to regions of torrent data that correspond to its files. type File struct { - t *Torrent - path string - offset int64 - length int64 - fi metainfo.FileInfo + t *Torrent + path string + offset int64 + length int64 + fi metainfo.FileInfo + displayPath string + prio piecePriority } func (f *File) Torrent() *Torrent { return f.t } -// Data for this file begins this far into the torrent. +// Data for this file begins this many bytes into the Torrent. func (f *File) Offset() int64 { return f.offset } +// The FileInfo from the metainfo.Info to which this file corresponds. func (f File) FileInfo() metainfo.FileInfo { return f.fi } +// The file's path components joined by '/'. func (f File) Path() string { return f.path } +// The file's length in bytes. func (f *File) Length() int64 { return f.length } -// The relative file path for a multi-file torrent, and the torrent name for a -// single-file torrent. -func (f *File) DisplayPath() string { - fip := f.FileInfo().Path - if len(fip) == 0 { - return f.t.Info().Name +// Number of bytes of the entire file we have completed. This is the sum of +// completed pieces, and dirtied chunks of incomplete pieces. +func (f *File) BytesCompleted() (n int64) { + f.t.cl.rLock() + n = f.bytesCompletedLocked() + f.t.cl.rUnlock() + return +} + +func (f *File) bytesCompletedLocked() int64 { + return f.length - f.bytesLeft() +} + +func fileBytesLeft( + torrentUsualPieceSize int64, + fileFirstPieceIndex int, + fileEndPieceIndex int, + fileTorrentOffset int64, + fileLength int64, + torrentCompletedPieces *roaring.Bitmap, + pieceSizeCompletedFn func(pieceIndex int) int64, +) (left int64) { + if fileLength == 0 { + return } - return strings.Join(fip, "/") + noCompletedMiddlePieces := roaring.New() + noCompletedMiddlePieces.AddRange(bitmap.BitRange(fileFirstPieceIndex), bitmap.BitRange(fileEndPieceIndex)) + noCompletedMiddlePieces.AndNot(torrentCompletedPieces) + noCompletedMiddlePieces.Iterate(func(pieceIndex uint32) bool { + i := int(pieceIndex) + pieceSizeCompleted := pieceSizeCompletedFn(i) + if i == fileFirstPieceIndex { + beginOffset := fileTorrentOffset % torrentUsualPieceSize + beginSize := torrentUsualPieceSize - beginOffset + beginDownLoaded := pieceSizeCompleted - beginOffset + if beginDownLoaded < 0 { + beginDownLoaded = 0 + } + left += beginSize - beginDownLoaded + } else if i == fileEndPieceIndex-1 { + endSize := (fileTorrentOffset + fileLength) % torrentUsualPieceSize + if endSize == 0 { + endSize = torrentUsualPieceSize + } + endDownloaded := pieceSizeCompleted + if endDownloaded > endSize { + endDownloaded = endSize + } + left += endSize - endDownloaded + } else { + left += torrentUsualPieceSize - pieceSizeCompleted + } + return true + }) + + if left > fileLength { + left = fileLength + } + // + //numPiecesSpanned := f.EndPieceIndex() - f.BeginPieceIndex() + //completedMiddlePieces := f.t._completedPieces.Clone() + //completedMiddlePieces.RemoveRange(0, bitmap.BitRange(f.BeginPieceIndex()+1)) + //completedMiddlePieces.RemoveRange(bitmap.BitRange(f.EndPieceIndex()-1), bitmap.ToEnd) + //left += int64(numPiecesSpanned-2-pieceIndex(completedMiddlePieces.GetCardinality())) * torrentUsualPieceSize + return +} + +func (f *File) bytesLeft() (left int64) { + return fileBytesLeft(int64(f.t.usualPieceSize()), f.BeginPieceIndex(), f.EndPieceIndex(), f.offset, f.length, &f.t._completedPieces, func(pieceIndex int) int64 { + return int64(f.t.piece(pieceIndex).numDirtyBytes()) + }) +} + +// The relative file path for a multi-file torrent, and the torrent name for a +// single-file torrent. Dir separators are '/'. +func (f *File) DisplayPath() string { + return f.displayPath } +// The download status of a piece that comprises part of a File. type FilePieceState struct { Bytes int64 // Bytes within the piece that are part of this File. PieceState @@ -54,10 +130,12 @@ type FilePieceState struct { // Returns the state of pieces in this file. func (f *File) State() (ret []FilePieceState) { + f.t.cl.rLock() + defer f.t.cl.rUnlock() pieceSize := int64(f.t.usualPieceSize()) off := f.offset % pieceSize remaining := f.length - for i := int(f.offset / pieceSize); ; i++ { + for i := pieceIndex(f.offset / pieceSize); ; i++ { if remaining == 0 { break } @@ -65,9 +143,7 @@ func (f *File) State() (ret []FilePieceState) { if len1 > remaining { len1 = remaining } - f.t.cl.mu.RLock() ps := f.t.pieceState(i) - f.t.cl.mu.RUnlock() ret = append(ret, FilePieceState{len1, ps}) off = 0 remaining -= len1 @@ -77,13 +153,7 @@ func (f *File) State() (ret []FilePieceState) { // Requests that all pieces containing data in the file be downloaded. func (f *File) Download() { - f.t.DownloadPieces(f.t.byteRegionPieces(f.offset, f.length)) -} - -// Requests that torrent pieces containing bytes in the given region of the -// file be downloaded. -func (f *File) PrioritizeRegion(off, len int64) { - f.t.DownloadPieces(f.t.byteRegionPieces(f.offset+off, len)) + f.SetPriority(PiecePriorityNormal) } func byteRegionExclusivePieces(off, size, pieceSize int64) (begin, end int) { @@ -92,10 +162,45 @@ func byteRegionExclusivePieces(off, size, pieceSize int64) (begin, end int) { return } -func (f *File) exclusivePieces() (begin, end int) { - return byteRegionExclusivePieces(f.offset, f.length, int64(f.t.usualPieceSize())) +// Deprecated: Use File.SetPriority. +func (f *File) Cancel() { + f.SetPriority(PiecePriorityNone) } -func (f *File) Cancel() { - f.t.CancelPieces(f.exclusivePieces()) +func (f *File) NewReader() Reader { + return f.t.newReader(f.Offset(), f.Length()) +} + +// Sets the minimum priority for pieces in the File. +func (f *File) SetPriority(prio piecePriority) { + f.t.cl.lock() + if prio != f.prio { + f.prio = prio + f.t.updatePiecePriorities(f.BeginPieceIndex(), f.EndPieceIndex(), "File.SetPriority") + } + f.t.cl.unlock() +} + +// Returns the priority per File.SetPriority. +func (f *File) Priority() (prio piecePriority) { + f.t.cl.rLock() + prio = f.prio + f.t.cl.rUnlock() + return +} + +// Returns the index of the first piece containing data for the file. +func (f *File) BeginPieceIndex() int { + if f.t.usualPieceSize() == 0 { + return 0 + } + return pieceIndex(f.offset / int64(f.t.usualPieceSize())) +} + +// Returns the index of the piece after the last one containing data for the file. +func (f *File) EndPieceIndex() int { + if f.t.usualPieceSize() == 0 { + return 0 + } + return pieceIndex((f.offset + f.length + int64(f.t.usualPieceSize()) - 1) / int64(f.t.usualPieceSize())) }