github.com/anacrolix/torrent@v1.61.0/metainfo/piece.go (about)

     1  package metainfo
     2  
     3  import (
     4  	"fmt"
     5  	"iter"
     6  
     7  	g "github.com/anacrolix/generics"
     8  	"github.com/anacrolix/missinggo/v2/panicif"
     9  )
    10  
    11  type Piece struct {
    12  	Info *Info // Can we embed the fields here instead, or is it something to do with saving memory?
    13  	i    PieceIndex
    14  }
    15  
    16  func (p Piece) String() string {
    17  	return fmt.Sprintf("metainfo.Piece(Info.Name=%q, i=%v)", p.Info.Name, p.i)
    18  }
    19  
    20  type PieceIndex = int
    21  
    22  func (p Piece) Length() int64 {
    23  	if p.Info.HasV2() {
    24  		var offset int64
    25  		pieceLength := p.Info.PieceLength
    26  		lastFileEnd := int64(0)
    27  		for fi := range p.Info.FileTree.upvertedFiles(pieceLength) {
    28  			// I don't think we need to track offset ourselves.
    29  			panicif.NotEq(offset, fi.TorrentOffset)
    30  			fileStartPiece := int(offset / pieceLength)
    31  			if fileStartPiece > p.i {
    32  				break
    33  			}
    34  			lastFileEnd = offset + fi.Length
    35  			offset = (lastFileEnd + pieceLength - 1) / pieceLength * pieceLength
    36  		}
    37  		ret := min(lastFileEnd-int64(p.i)*pieceLength, pieceLength)
    38  		if ret <= 0 {
    39  			panic(ret)
    40  		}
    41  		return ret
    42  	}
    43  	return p.V1Length()
    44  }
    45  
    46  func iterLast[T any](i iter.Seq[T]) (last g.Option[T]) {
    47  	for t := range i {
    48  		last.Set(t)
    49  	}
    50  	return
    51  }
    52  
    53  func (p Piece) V1Length() int64 {
    54  	i := p.i
    55  	lastPiece := p.Info.NumPieces() - 1
    56  	switch {
    57  	case 0 <= i && i < lastPiece:
    58  		return p.Info.PieceLength
    59  	case lastPiece >= 0 && i == lastPiece:
    60  		lastFile := iterLast(p.Info.UpvertedV1Files()).Unwrap()
    61  		length := lastFile.TorrentOffset + lastFile.Length - int64(i)*p.Info.PieceLength
    62  		if length <= 0 || length > p.Info.PieceLength {
    63  			panic(length)
    64  		}
    65  		return length
    66  	default:
    67  		panic(i)
    68  	}
    69  }
    70  
    71  func (p Piece) Offset() int64 {
    72  	return int64(p.i) * p.Info.PieceLength
    73  }
    74  
    75  func (p Piece) V1Hash() (ret g.Option[Hash]) {
    76  	if !p.Info.HasV1() {
    77  		return
    78  	}
    79  	copy(ret.Value[:], p.Info.Pieces[p.i*HashSize:(p.i+1)*HashSize])
    80  	ret.Ok = true
    81  	return
    82  }
    83  
    84  func (p Piece) Index() int {
    85  	return p.i
    86  }