]> Sergey Matveev's repositories - btrtrc.git/blobdiff - file.go
Drop support for go 1.20
[btrtrc.git] / file.go
diff --git a/file.go b/file.go
index 4775b4ff0e8d55e3781971c53af5297a2958b93b..bea4b13655d6024915434eecc66eeb0cab6524ad 100644 (file)
--- a/file.go
+++ b/file.go
@@ -1,22 +1,21 @@
 package torrent
 
 import (
-       "strings"
-
-       "github.com/anacrolix/missinggo/bitmap"
+       "github.com/RoaringBitmap/roaring"
+       "github.com/anacrolix/missinggo/v2/bitmap"
 
        "github.com/anacrolix/torrent/metainfo"
-       pp "github.com/anacrolix/torrent/peer_protocol"
 )
 
 // 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
-       prio   piecePriority
+       t           *Torrent
+       path        string
+       offset      int64
+       length      int64
+       fi          metainfo.FileInfo
+       displayPath string
+       prio        piecePriority
 }
 
 func (f *File) Torrent() *Torrent {
@@ -45,53 +44,82 @@ func (f *File) Length() int64 {
 
 // 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() int64 {
+func (f *File) BytesCompleted() (n int64) {
        f.t.cl.rLock()
-       defer f.t.cl.rUnlock()
-       return f.bytesCompleted()
+       n = f.bytesCompletedLocked()
+       f.t.cl.rUnlock()
+       return
 }
 
-func (f *File) bytesCompleted() int64 {
+func (f *File) bytesCompletedLocked() int64 {
        return f.length - f.bytesLeft()
 }
 
-func (f *File) bytesLeft() (left int64) {
-       firstPieceIndex := f.firstPieceIndex()
-       endPieceIndex := f.endPieceIndex()
-       bitmap.Flip(f.t.completedPieces, firstPieceIndex, endPieceIndex+1).IterTyped(func(piece int) bool {
-               p := &f.t.pieces[piece]
-               left += int64(p.length() - p.numDirtyBytes())
-               return true
-       })
-       startPiece := f.t.piece(firstPieceIndex)
-       endChunk := int(f.offset%f.t.info.PieceLength) * int(startPiece.numChunks()) / int(startPiece.length())
-       bitmap.Flip(startPiece.dirtyChunks, 0, endChunk).IterTyped(func(chunk int) bool {
-               left -= int64(startPiece.chunkSize())
-               return true
-       })
-       endPiece := f.t.piece(endPieceIndex)
-       startChunk := int((f.offset+f.length)%f.t.info.PieceLength) * int(endPiece.numChunks()) / int(endPiece.length())
-       lastChunkIndex := int(endPiece.lastChunkIndex())
-       bitmap.Flip(endPiece.dirtyChunks, startChunk, int(endPiece.numChunks())).IterTyped(func(chunk int) bool {
-               if chunk == lastChunkIndex {
-                       left -= int64(endPiece.chunkIndexSpec(pp.Integer(chunk)).Length)
+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 -= int64(endPiece.chunkSize())
+                       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.
+// single-file torrent. Dir separators are '/'.
 func (f *File) DisplayPath() string {
-       fip := f.FileInfo().Path
-       if len(fip) == 0 {
-               return f.t.info.Name
-       }
-       return strings.Join(fip, "/")
-
+       return f.displayPath
 }
 
 // The download status of a piece that comprises part of a File.
@@ -140,45 +168,39 @@ func (f *File) Cancel() {
 }
 
 func (f *File) NewReader() Reader {
-       tr := reader{
-               mu:        f.t.cl.locker(),
-               t:         f.t,
-               readahead: 5 * 1024 * 1024,
-               offset:    f.Offset(),
-               length:    f.Length(),
-       }
-       f.t.addReader(&tr)
-       return &tr
+       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()
-       defer f.t.cl.unlock()
-       if prio == f.prio {
-               return
+       if prio != f.prio {
+               f.prio = prio
+               f.t.updatePiecePriorities(f.BeginPieceIndex(), f.EndPieceIndex(), "File.SetPriority")
        }
-       f.prio = prio
-       f.t.updatePiecePriorities(f.firstPieceIndex(), f.endPieceIndex())
+       f.t.cl.unlock()
 }
 
 // Returns the priority per File.SetPriority.
-func (f *File) Priority() piecePriority {
-       f.t.cl.lock()
-       defer f.t.cl.unlock()
-       return f.prio
+func (f *File) Priority() (prio piecePriority) {
+       f.t.cl.rLock()
+       prio = f.prio
+       f.t.cl.rUnlock()
+       return
 }
 
-func (f *File) firstPieceIndex() pieceIndex {
+// 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()))
 }
 
-func (f *File) endPieceIndex() pieceIndex {
+// 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-1)/int64(f.t.usualPieceSize())) + 1
+       return pieceIndex((f.offset + f.length + int64(f.t.usualPieceSize()) - 1) / int64(f.t.usualPieceSize()))
 }