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

     1  package torrent
     2  
     3  import (
     4  	"errors"
     5  	"net"
     6  
     7  	"github.com/RoaringBitmap/roaring"
     8  	"github.com/anacrolix/missinggo/v2"
     9  	"github.com/anacrolix/missinggo/v2/panicif"
    10  	"golang.org/x/time/rate"
    11  
    12  	"github.com/anacrolix/torrent/metainfo"
    13  	pp "github.com/anacrolix/torrent/peer_protocol"
    14  	"github.com/anacrolix/torrent/types"
    15  	"github.com/anacrolix/torrent/types/infohash"
    16  )
    17  
    18  type (
    19  	// TODO: Make this private. Use types.Request in the (one?) place it's exposed here.
    20  	Request       = types.Request
    21  	ChunkSpec     = types.ChunkSpec
    22  	PiecePriority = types.PiecePriority
    23  )
    24  
    25  const (
    26  	PiecePriorityNormal    = types.PiecePriorityNormal
    27  	PiecePriorityNone      = types.PiecePriorityNone
    28  	PiecePriorityNow       = types.PiecePriorityNow
    29  	PiecePriorityReadahead = types.PiecePriorityReadahead
    30  	PiecePriorityNext      = types.PiecePriorityNext
    31  	PiecePriorityHigh      = types.PiecePriorityHigh
    32  )
    33  
    34  func newRequest(index, begin, length pp.Integer) Request {
    35  	return Request{index, ChunkSpec{begin, length}}
    36  }
    37  
    38  func newRequestFromMessage(msg *pp.Message) Request {
    39  	switch msg.Type {
    40  	case pp.Request, pp.Cancel, pp.Reject:
    41  		return newRequest(msg.Index, msg.Begin, msg.Length)
    42  	case pp.Piece:
    43  		return newRequest(msg.Index, msg.Begin, pp.Integer(len(msg.Piece)))
    44  	default:
    45  		panic(msg.Type)
    46  	}
    47  }
    48  
    49  // The size in bytes of a metadata extension piece.
    50  func metadataPieceSize(totalSize, piece int) int {
    51  	ret := totalSize - piece*(1<<14)
    52  	if ret > 1<<14 {
    53  		ret = 1 << 14
    54  	}
    55  	return ret
    56  }
    57  
    58  // Return the request that would include the given offset into the torrent data.
    59  func torrentOffsetRequest(
    60  	torrentLength, pieceSize, chunkSize, offset int64,
    61  ) (
    62  	r Request, ok bool,
    63  ) {
    64  	if offset < 0 || offset >= torrentLength {
    65  		return
    66  	}
    67  	r.Index = pp.Integer(offset / pieceSize)
    68  	r.Begin = pp.Integer(offset % pieceSize / chunkSize * chunkSize)
    69  	r.Length = pp.Integer(chunkSize)
    70  	pieceLeft := pp.Integer(pieceSize - int64(r.Begin))
    71  	if r.Length > pieceLeft {
    72  		r.Length = pieceLeft
    73  	}
    74  	torrentLeft := torrentLength - int64(r.Index)*pieceSize - int64(r.Begin)
    75  	if int64(r.Length) > torrentLeft {
    76  		r.Length = pp.Integer(torrentLeft)
    77  	}
    78  	ok = true
    79  	return
    80  }
    81  
    82  func torrentRequestOffset(torrentLength, pieceSize int64, r Request) (off int64) {
    83  	off = int64(r.Index)*pieceSize + int64(r.Begin)
    84  	if off < 0 || off >= torrentLength {
    85  		panic("invalid Request")
    86  	}
    87  	return
    88  }
    89  
    90  func validateInfo(info *metainfo.Info) error {
    91  	if len(info.Pieces)%20 != 0 {
    92  		return errors.New("pieces has invalid length")
    93  	}
    94  	if info.PieceLength == 0 {
    95  		if info.TotalLength() != 0 {
    96  			return errors.New("zero piece length")
    97  		}
    98  	} else if !info.HasV2() {
    99  		// TotalLength returns different values for V1 and V2 depending on whether v1 pad files are
   100  		// counted. Split the interface into several methods?
   101  		if int((info.TotalLength()+info.PieceLength-1)/info.PieceLength) != info.NumPieces() {
   102  			return errors.New("piece count and file lengths are at odds")
   103  		}
   104  	}
   105  	return nil
   106  }
   107  
   108  func chunkIndexSpec(index, pieceLength, chunkSize pp.Integer) ChunkSpec {
   109  	begin := index * chunkSize
   110  	panicif.GreaterThanOrEqual(begin, pieceLength)
   111  	return ChunkSpec{
   112  		Begin:  begin,
   113  		Length: min(chunkSize, pieceLength-begin),
   114  	}
   115  }
   116  
   117  func comparePeerTrust(l, r *Peer) int {
   118  	return l.trust().Cmp(r.trust())
   119  }
   120  
   121  func connIsIpv6(nc interface {
   122  	LocalAddr() net.Addr
   123  },
   124  ) bool {
   125  	ra := nc.LocalAddr()
   126  	rip := addrIpOrNil(ra)
   127  	return rip.To4() == nil && rip.To16() != nil
   128  }
   129  
   130  var unlimited = rate.NewLimiter(rate.Inf, 0)
   131  
   132  type (
   133  	pieceIndex = int
   134  	// Deprecated: Use infohash.T directly to avoid unnecessary imports.
   135  	InfoHash = infohash.T
   136  	IpPort   = missinggo.IpPort
   137  )
   138  
   139  func boolSliceToBitmap(slice []bool) (rb roaring.Bitmap) {
   140  	for i, b := range slice {
   141  		if b {
   142  			rb.AddInt(i)
   143  		}
   144  	}
   145  	return
   146  }