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 }