X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=file.go;h=bea4b13655d6024915434eecc66eeb0cab6524ad;hb=HEAD;hp=688fc2faa92ae6af221d706a94fe4d27461f5eb2;hpb=fd24910257f4fa49323f0bb66b69dc3f4389a53a;p=btrtrc.git diff --git a/file.go b/file.go index 688fc2fa..bea4b136 100644 --- a/file.go +++ b/file.go @@ -1,33 +1,128 @@ package torrent -import "github.com/anacrolix/torrent/metainfo" +import ( + "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 } +// 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 + } + + 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 @@ -35,12 +130,12 @@ type FilePieceState struct { // Returns the state of pieces in this file. func (f *File) State() (ret []FilePieceState) { - f.t.cl.mu.Lock() - defer f.t.cl.mu.Unlock() + 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 } @@ -48,20 +143,64 @@ func (f *File) State() (ret []FilePieceState) { if len1 > remaining { len1 = remaining } - ret = append(ret, FilePieceState{len1, f.t.pieceState(i)}) + ps := f.t.pieceState(i) + ret = append(ret, FilePieceState{len1, ps}) off = 0 remaining -= len1 } return } -func (f *File) PrioritizeRegion(off, len int64) { - if off < 0 || off >= f.length { - return +// Requests that all pieces containing data in the file be downloaded. +func (f *File) Download() { + f.SetPriority(PiecePriorityNormal) +} + +func byteRegionExclusivePieces(off, size, pieceSize int64) (begin, end int) { + begin = int((off + pieceSize - 1) / pieceSize) + end = int((off + size) / pieceSize) + return +} + +// Deprecated: Use File.SetPriority. +func (f *File) Cancel() { + f.SetPriority(PiecePriorityNone) +} + +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") } - if off+len > f.length { - len = f.length - off + 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 } - off += f.offset - f.t.SetRegionPriority(off, len) + return pieceIndex((f.offset + f.length + int64(f.t.usualPieceSize()) - 1) / int64(f.t.usualPieceSize())) }