+// 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