client.go | 176 ++++++++++++++++++++++++++--------------------------- client_test.go | 8 ++++---- cmd/magnet-metainfo/main.go | 2 +- cmd/torrent-pick/main.go | 2 +- cmd/torrent/main.go | 4 ++-- connection.go | 4 ++-- example_test.go | 2 +- file.go | 14 +++++++------- fs/torrentfs.go | 6 +++--- fs/torrentfs_test.go | 11 ++++------- piece.go | 2 +- reader.go | 30 +++++++++++++++--------------- t.go | 97 +++++++++++++++++++++++------------------------------ torrent.go | 180 +++++++++++++++++++++++++++--------------------------- torrent_test.go | 4 ++-- worst_conns.go | 2 +- diff --git a/client.go b/client.go index 9145a477598487a72f54d5209a3bcbf5c4ea3f6e..c03da7264411aeaaf06a533a7d64ee9c947b6918 100644 --- a/client.go +++ b/client.go @@ -114,7 +114,7 @@ extendedHandshakeClientVersion = "go.torrent dev 20150624" ) // Currently doesn't really queue, but should in the future. -func (cl *Client) queuePieceCheck(t *torrent, pieceIndex int) { +func (cl *Client) queuePieceCheck(t *Torrent, pieceIndex int) { piece := &t.pieces[pieceIndex] if piece.QueuedForHash { return @@ -126,7 +126,7 @@ } // Queue a piece check if one isn't already queued, and the piece has never // been checked before. -func (cl *Client) queueFirstHash(t *torrent, piece int) { +func (cl *Client) queueFirstHash(t *Torrent, piece int) { p := &t.pieces[piece] if p.EverHashed || p.Hashing || p.QueuedForHash || t.pieceComplete(piece) { return @@ -158,7 +158,7 @@ mu sync.RWMutex event sync.Cond closed missinggo.Event - torrents map[metainfo.InfoHash]*torrent + torrents map[metainfo.InfoHash]*Torrent } func (me *Client) IPBlockList() iplist.Ranger { @@ -204,7 +204,7 @@ func (me hashSorter) Swap(a, b int) { me.Hashes[a], me.Hashes[b] = me.Hashes[b], me.Hashes[a] } -func (cl *Client) sortedTorrents() (ret []*torrent) { +func (cl *Client) sortedTorrents() (ret []*Torrent) { var hs hashSorter for ih := range cl.torrents { hs.Hashes = append(hs.Hashes, ih) @@ -379,7 +379,7 @@ halfOpenLimit: socketsPerTorrent, config: *cfg, defaultStorage: cfg.DefaultStorage, dopplegangerAddrs: make(map[string]struct{}), - torrents: make(map[metainfo.InfoHash]*torrent), + torrents: make(map[metainfo.InfoHash]*Torrent), } missinggo.CopyExact(&cl.extensionBytes, defaultExtensionBytes) cl.event.L = &cl.mu @@ -576,18 +576,14 @@ } } // Returns a handle to the given torrent, if it's present in the client. -func (cl *Client) Torrent(ih metainfo.InfoHash) (T Torrent, ok bool) { +func (cl *Client) Torrent(ih metainfo.InfoHash) (t *Torrent, ok bool) { cl.mu.Lock() defer cl.mu.Unlock() - t, ok := cl.torrents[ih] - if !ok { - return - } - T = Torrent{cl, t} + t, ok = cl.torrents[ih] return } -func (me *Client) torrent(ih metainfo.InfoHash) *torrent { +func (me *Client) torrent(ih metainfo.InfoHash) *Torrent { return me.torrents[ih] } @@ -596,7 +592,7 @@ Conn net.Conn UTP bool } -func doDial(dial func(addr string, t *torrent) (net.Conn, error), ch chan dialResult, utp bool, addr string, t *torrent) { +func doDial(dial func(addr string, t *Torrent) (net.Conn, error), ch chan dialResult, utp bool, addr string, t *Torrent) { conn, err := dial(addr, t) if err != nil { if conn != nil { @@ -628,7 +624,7 @@ } // Start the process of connecting to the given peer for the given torrent if // appropriate. -func (me *Client) initiateConn(peer Peer, t *torrent) { +func (me *Client) initiateConn(peer Peer, t *Torrent) { if peer.Id == me.peerID { return } @@ -645,14 +641,14 @@ t.halfOpen[addr] = struct{}{} go me.outgoingConnection(t, addr, peer.Source) } -func (me *Client) dialTimeout(t *torrent) time.Duration { +func (me *Client) dialTimeout(t *Torrent) time.Duration { me.mu.Lock() pendingPeers := len(t.peers) me.mu.Unlock() return reducedDialTimeout(nominalDialTimeout, me.halfOpenLimit, pendingPeers) } -func (me *Client) dialTCP(addr string, t *torrent) (c net.Conn, err error) { +func (me *Client) dialTCP(addr string, t *Torrent) (c net.Conn, err error) { c, err = net.DialTimeout("tcp", addr, me.dialTimeout(t)) if err == nil { c.(*net.TCPConn).SetLinger(0) @@ -660,12 +656,12 @@ } return } -func (me *Client) dialUTP(addr string, t *torrent) (c net.Conn, err error) { +func (me *Client) dialUTP(addr string, t *Torrent) (c net.Conn, err error) { return me.utpSock.DialTimeout(addr, me.dialTimeout(t)) } // Returns a connection over UTP or TCP, whichever is first to connect. -func (me *Client) dialFirst(addr string, t *torrent) (conn net.Conn, utp bool) { +func (me *Client) dialFirst(addr string, t *Torrent) (conn net.Conn, utp bool) { // Initiate connections via TCP and UTP simultaneously. Use the first one // that succeeds. left := 0 @@ -703,7 +699,7 @@ utp = res.UTP return } -func (me *Client) noLongerHalfOpen(t *torrent, addr string) { +func (me *Client) noLongerHalfOpen(t *Torrent, addr string) { if _, ok := t.halfOpen[addr]; !ok { panic("invariant broken") } @@ -713,7 +709,7 @@ } // Performs initiator handshakes and returns a connection. Returns nil // *connection if no connection for valid reasons. -func (me *Client) handshakesConnection(nc net.Conn, t *torrent, encrypted, utp bool) (c *connection, err error) { +func (me *Client) handshakesConnection(nc net.Conn, t *Torrent, encrypted, utp bool) (c *connection, err error) { c = newConnection() c.conn = nc c.rw = nc @@ -732,7 +728,7 @@ } // Returns nil connection and nil error if no connection could be established // for valid reasons. -func (me *Client) establishOutgoingConn(t *torrent, addr string) (c *connection, err error) { +func (me *Client) establishOutgoingConn(t *Torrent, addr string) (c *connection, err error) { nc, utp := me.dialFirst(addr, t) if nc == nil { return @@ -769,7 +765,7 @@ } // Called to dial out and run a connection. The addr we're given is already // considered half-open. -func (me *Client) outgoingConnection(t *torrent, addr string, ps peerSource) { +func (me *Client) outgoingConnection(t *Torrent, addr string, ps peerSource) { c, err := me.establishOutgoingConn(t, addr) me.mu.Lock() defer me.mu.Unlock() @@ -981,22 +977,22 @@ } return } -func (me *Client) initiateHandshakes(c *connection, t *torrent) (ok bool, err error) { +func (me *Client) initiateHandshakes(c *connection, t *Torrent) (ok bool, err error) { if c.encrypted { - c.rw, err = mse.InitiateHandshake(c.rw, t.InfoHash[:], nil) + c.rw, err = mse.InitiateHandshake(c.rw, t.infoHash[:], nil) if err != nil { return } } - ih, ok, err := me.connBTHandshake(c, &t.InfoHash) - if ih != t.InfoHash { + ih, ok, err := me.connBTHandshake(c, &t.infoHash) + if ih != t.infoHash { ok = false } return } // Do encryption and bittorrent handshakes as receiver. -func (cl *Client) receiveHandshakes(c *connection) (t *torrent, err error) { +func (cl *Client) receiveHandshakes(c *connection) (t *Torrent, err error) { cl.mu.Lock() skeys := cl.receiveSkeys() cl.mu.Unlock() @@ -1036,7 +1032,7 @@ c.completedHandshake = time.Now() return } -func (cl *Client) runInitiatedHandshookConn(c *connection, t *torrent) (err error) { +func (cl *Client) runInitiatedHandshookConn(c *connection, t *Torrent) (err error) { if c.PeerID == cl.peerID { // Only if we initiated the connection is the remote address a // listen addr for a doppleganger. @@ -1069,7 +1065,7 @@ } return cl.runHandshookConn(c, t) } -func (cl *Client) runHandshookConn(c *connection, t *torrent) (err error) { +func (cl *Client) runHandshookConn(c *connection, t *Torrent) (err error) { c.conn.SetWriteDeadline(time.Time{}) c.rw = readWriter{ deadlineReader{c.conn, c.rw}, @@ -1090,7 +1086,7 @@ } return } -func (me *Client) sendInitialMessages(conn *connection, torrent *torrent) { +func (me *Client) sendInitialMessages(conn *connection, torrent *Torrent) { if conn.PeerExtensionBytes.SupportsExtended() && me.extensionBytes.SupportsExtended() { conn.Post(pp.Message{ Type: pp.Extended, @@ -1148,11 +1144,11 @@ }) } } -func (me *Client) peerUnchoked(torrent *torrent, conn *connection) { +func (me *Client) peerUnchoked(torrent *Torrent, conn *connection) { conn.updateRequests() } -func (cl *Client) connCancel(t *torrent, cn *connection, r request) (ok bool) { +func (cl *Client) connCancel(t *Torrent, cn *connection, r request) (ok bool) { ok = cn.Cancel(r) if ok { postedCancels.Add(1) @@ -1160,7 +1156,7 @@ } return } -func (cl *Client) connDeleteRequest(t *torrent, cn *connection, r request) bool { +func (cl *Client) connDeleteRequest(t *Torrent, cn *connection, r request) bool { if !cn.RequestPending(r) { return false } @@ -1168,7 +1164,7 @@ delete(cn.Requests, r) return true } -func (cl *Client) requestPendingMetadata(t *torrent, c *connection) { +func (cl *Client) requestPendingMetadata(t *Torrent, c *connection) { if t.haveInfo() { return } @@ -1188,12 +1184,12 @@ c.requestMetadataPiece(pending[i]) } } -func (cl *Client) completedMetadata(t *torrent) { +func (cl *Client) completedMetadata(t *Torrent) { h := sha1.New() h.Write(t.metadataBytes) var ih metainfo.InfoHash missinggo.CopyExact(&ih, h.Sum(nil)) - if ih != t.InfoHash { + if ih != t.infoHash { log.Print("bad metadata") t.invalidateMetadata() return @@ -1219,7 +1215,7 @@ } } // Process incoming ut_metadata message. -func (cl *Client) gotMetadataExtensionMsg(payload []byte, t *torrent, c *connection) (err error) { +func (cl *Client) gotMetadataExtensionMsg(payload []byte, t *Torrent, c *connection) (err error) { var d map[string]int err = bencode.Unmarshal(payload, &d) if err != nil { @@ -1268,7 +1264,7 @@ } return } -func (me *Client) upload(t *torrent, c *connection) { +func (me *Client) upload(t *Torrent, c *connection) { if me.config.NoUpload { return } @@ -1305,7 +1301,7 @@ } c.Choke() } -func (me *Client) sendChunk(t *torrent, c *connection, r request) error { +func (me *Client) sendChunk(t *Torrent, c *connection, r request) error { // Count the chunk being sent, even if it isn't. b := make([]byte, r.Length) p := t.info.Piece(int(r.Index)) @@ -1330,7 +1326,7 @@ } // Processes incoming bittorrent messages. The client lock is held upon entry // and exit. -func (me *Client) connectionLoop(t *torrent, c *connection) error { +func (me *Client) connectionLoop(t *Torrent, c *connection) error { decoder := pp.Decoder{ R: bufio.NewReader(c.rw), MaxLength: 256 * 1024, @@ -1532,7 +1528,7 @@ } } // Returns true if connection is removed from torrent.Conns. -func (me *Client) deleteConnection(t *torrent, c *connection) bool { +func (me *Client) deleteConnection(t *Torrent, c *connection) bool { for i0, _c := range t.conns { if _c != c { continue @@ -1547,7 +1543,7 @@ } return false } -func (me *Client) dropConnection(t *torrent, c *connection) { +func (me *Client) dropConnection(t *Torrent, c *connection) { me.event.Broadcast() c.Close() if me.deleteConnection(t, c) { @@ -1556,7 +1552,7 @@ } } // Returns true if the connection is added. -func (me *Client) addConnection(t *torrent, c *connection) bool { +func (me *Client) addConnection(t *Torrent, c *connection) bool { if me.closed.IsSet() { return false } @@ -1594,7 +1590,7 @@ c.t = t return true } -func (t *torrent) readerPieces() (ret bitmap.Bitmap) { +func (t *Torrent) readerPieces() (ret bitmap.Bitmap) { t.forReaderOffsetPieces(func(begin, end int) bool { ret.AddRange(begin, end) return true @@ -1602,7 +1598,7 @@ }) return } -func (t *torrent) needData() bool { +func (t *Torrent) needData() bool { if !t.haveInfo() { return true } @@ -1614,7 +1610,7 @@ return t.pieceComplete(piece) }) } -func (cl *Client) usefulConn(t *torrent, c *connection) bool { +func (cl *Client) usefulConn(t *Torrent, c *connection) bool { if c.closed.IsSet() { return false } @@ -1627,7 +1623,7 @@ } return t.connHasWantedPieces(c) } -func (me *Client) wantConns(t *torrent) bool { +func (me *Client) wantConns(t *Torrent) bool { if !me.seeding(t) && !t.needData() { return false } @@ -1637,7 +1633,7 @@ } return t.worstBadConn(me) != nil } -func (me *Client) openNewConns(t *torrent) { +func (me *Client) openNewConns(t *Torrent) { select { case <-t.ceasingNetworking: return @@ -1663,7 +1659,7 @@ } t.wantPeers.Broadcast() } -func (me *Client) addPeers(t *torrent, peers []Peer) { +func (me *Client) addPeers(t *Torrent, peers []Peer) { for _, p := range peers { if me.dopplegangerAddr(net.JoinHostPort( p.IP.String(), @@ -1686,8 +1682,8 @@ func (cl *Client) cachedMetaInfoFilename(ih metainfo.InfoHash) string { return filepath.Join(cl.configDir(), "torrents", ih.HexString()+".torrent") } -func (cl *Client) saveTorrentFile(t *torrent) error { - path := cl.cachedMetaInfoFilename(t.InfoHash) +func (cl *Client) saveTorrentFile(t *Torrent) error { + path := cl.cachedMetaInfoFilename(t.infoHash) os.MkdirAll(filepath.Dir(path), 0777) f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) if err != nil { @@ -1695,23 +1691,23 @@ return fmt.Errorf("error opening file: %s", err) } defer f.Close() e := bencode.NewEncoder(f) - err = e.Encode(t.MetaInfo()) + err = e.Encode(t.metainfo()) if err != nil { return fmt.Errorf("error marshalling metainfo: %s", err) } - mi, err := cl.torrentCacheMetaInfo(t.InfoHash) + mi, err := cl.torrentCacheMetaInfo(t.infoHash) if err != nil { // For example, a script kiddy makes us load too many files, and we're // able to save the torrent, but not load it again to check it. return nil } - if !bytes.Equal(mi.Info.Hash.Bytes(), t.InfoHash[:]) { - log.Fatalf("%x != %x", mi.Info.Hash, t.InfoHash[:]) + if !bytes.Equal(mi.Info.Hash.Bytes(), t.infoHash[:]) { + log.Fatalf("%x != %x", mi.Info.Hash, t.infoHash[:]) } return nil } -func (cl *Client) setMetaData(t *torrent, md *metainfo.Info, bytes []byte) (err error) { +func (cl *Client) setMetaData(t *Torrent, md *metainfo.Info, bytes []byte) (err error) { err = t.setMetadata(md, bytes) if err != nil { return @@ -1729,9 +1725,9 @@ // Prepare a Torrent without any attachment to a Client. That means we can // initialize fields all fields that don't require the Client without locking // it. -func newTorrent(ih metainfo.InfoHash) (t *torrent) { - t = &torrent{ - InfoHash: ih, +func newTorrent(ih metainfo.InfoHash) (t *Torrent) { + t = &Torrent{ + infoHash: ih, chunkSize: defaultChunkSize, peers: make(map[peersKey]Peer), @@ -1783,7 +1779,7 @@ } return tier } -func (t *torrent) addTrackers(announceList [][]string) { +func (t *Torrent) addTrackers(announceList [][]string) { newTrackers := copyTrackers(t.trackers) for tierIndex, tier := range announceList { if tierIndex < len(newTrackers) { @@ -1797,7 +1793,7 @@ t.trackers = newTrackers } // Don't call this before the info is available. -func (t *torrent) bytesCompleted() int64 { +func (t *Torrent) bytesCompleted() int64 { if !t.haveInfo() { return 0 } @@ -1814,7 +1810,7 @@ } // Returns handles to the files in the torrent. This requires the metainfo is // available first. -func (t Torrent) Files() (ret []File) { +func (t *Torrent) Files() (ret []File) { t.cl.mu.Lock() info := t.Info() t.cl.mu.Unlock() @@ -1835,20 +1831,20 @@ } return } -func (t Torrent) AddPeers(pp []Peer) error { +func (t *Torrent) AddPeers(pp []Peer) error { cl := t.cl cl.mu.Lock() defer cl.mu.Unlock() - cl.addPeers(t.torrent, pp) + cl.addPeers(t, pp) return nil } // Marks the entire torrent for download. Requires the info first, see // GotInfo. -func (t Torrent) DownloadAll() { +func (t *Torrent) DownloadAll() { t.cl.mu.Lock() defer t.cl.mu.Unlock() - t.torrent.pendPieceRange(0, t.torrent.numPieces()) + t.pendPieceRange(0, t.numPieces()) } // Returns nil metainfo if it isn't in the cache. Checks that the retrieved @@ -1926,8 +1922,7 @@ // Add or merge a torrent spec. If the torrent is already present, the // trackers will be merged with the existing ones. If the Info isn't yet // known, it will be set. The display name is replaced if the new spec // provides one. Returns new if the torrent wasn't already in the client. -func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (T Torrent, new bool, err error) { - T.cl = cl +func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (t *Torrent, new bool, err error) { cl.mu.Lock() defer cl.mu.Unlock() @@ -1979,16 +1974,15 @@ } t.addTrackers(spec.Trackers) cl.torrents[spec.InfoHash] = t - T.torrent = t // From this point onwards, we can consider the torrent a part of the // client. if new { if !cl.config.DisableTrackers { - go cl.announceTorrentTrackers(T.torrent) + go cl.announceTorrentTrackers(t) } if cl.dHT != nil { - go cl.announceTorrentDHT(T.torrent, true) + go cl.announceTorrentDHT(t, true) } } return @@ -2009,7 +2003,7 @@ return } // Returns true when peers are required, or false if the torrent is closing. -func (cl *Client) waitWantPeers(t *torrent) bool { +func (cl *Client) waitWantPeers(t *Torrent) bool { cl.mu.Lock() defer cl.mu.Unlock() for { @@ -2030,7 +2024,7 @@ } } // Returns whether the client should make effort to seed the torrent. -func (cl *Client) seeding(t *torrent) bool { +func (cl *Client) seeding(t *Torrent) bool { if cl.config.NoUpload { return false } @@ -2043,10 +2037,10 @@ } return true } -func (cl *Client) announceTorrentDHT(t *torrent, impliedPort bool) { +func (cl *Client) announceTorrentDHT(t *Torrent, impliedPort bool) { for cl.waitWantPeers(t) { // log.Printf("getting peers for %q from DHT", t) - ps, err := cl.dHT.Announce(string(t.InfoHash[:]), cl.incomingPeerPort(), impliedPort) + ps, err := cl.dHT.Announce(string(t.infoHash[:]), cl.incomingPeerPort(), impliedPort) if err != nil { log.Printf("error getting peers from dht: %s", err) return @@ -2113,7 +2107,7 @@ cl.mu.RUnlock() return } -func (cl *Client) announceTorrentSingleTracker(tr string, req *tracker.AnnounceRequest, t *torrent) error { +func (cl *Client) announceTorrentSingleTracker(tr string, req *tracker.AnnounceRequest, t *Torrent) error { blocked, err := cl.trackerBlockedUnlocked(tr) if err != nil { return fmt.Errorf("error determining if tracker blocked: %s", err) @@ -2142,7 +2136,7 @@ time.Sleep(time.Second * time.Duration(resp.Interval)) return nil } -func (cl *Client) announceTorrentTrackersFastStart(req *tracker.AnnounceRequest, trackers []trackerTier, t *torrent) (atLeastOne bool) { +func (cl *Client) announceTorrentTrackersFastStart(req *tracker.AnnounceRequest, trackers []trackerTier, t *Torrent) (atLeastOne bool) { oks := make(chan bool) outstanding := 0 for _, tier := range trackers { @@ -2165,13 +2159,13 @@ return } // Announce torrent to its trackers. -func (cl *Client) announceTorrentTrackers(t *torrent) { +func (cl *Client) announceTorrentTrackers(t *Torrent) { req := tracker.AnnounceRequest{ Event: tracker.Started, NumWant: -1, Port: uint16(cl.incomingPeerPort()), PeerId: cl.peerID, - InfoHash: t.InfoHash, + InfoHash: t.infoHash, } if !cl.waitWantPeers(t) { return @@ -2243,7 +2237,7 @@ return true } // Handle a received chunk from a peer. -func (me *Client) downloadedChunk(t *torrent, c *connection, msg *pp.Message) { +func (me *Client) downloadedChunk(t *Torrent, c *connection, msg *pp.Message) { chunksReceived.Add(1) req := newRequest(msg.Index, msg.Begin, pp.Integer(len(msg.Piece))) @@ -2315,7 +2309,7 @@ } // Return the connections that touched a piece, and clear the entry while // doing it. -func (me *Client) reapPieceTouches(t *torrent, piece int) (ret []*connection) { +func (me *Client) reapPieceTouches(t *Torrent, piece int) (ret []*connection) { for _, c := range t.conns { if _, ok := c.peerTouchedPieces[piece]; ok { ret = append(ret, c) @@ -2325,7 +2319,7 @@ } return } -func (me *Client) pieceHashed(t *torrent, piece int, correct bool) { +func (me *Client) pieceHashed(t *Torrent, piece int, correct bool) { p := &t.pieces[piece] if p.EverHashed { // Don't score the first time a piece is hashed, it could be an @@ -2354,7 +2348,7 @@ } me.pieceChanged(t, int(piece)) } -func (me *Client) onCompletedPiece(t *torrent, piece int) { +func (me *Client) onCompletedPiece(t *Torrent, piece int) { t.pendingPieces.Remove(piece) t.pendAllChunkSpecs(piece) for _, conn := range t.conns { @@ -2370,7 +2364,7 @@ me.upload(t, conn) } } -func (me *Client) onFailedPiece(t *torrent, piece int) { +func (me *Client) onFailedPiece(t *Torrent, piece int) { if t.pieceAllDirty(piece) { t.pendAllChunkSpecs(piece) } @@ -2385,7 +2379,7 @@ } } } -func (me *Client) pieceChanged(t *torrent, piece int) { +func (me *Client) pieceChanged(t *Torrent, piece int) { correct := t.pieceComplete(piece) defer me.event.Broadcast() if correct { @@ -2399,7 +2393,7 @@ } t.publishPieceChange(piece) } -func (cl *Client) verifyPiece(t *torrent, piece int) { +func (cl *Client) verifyPiece(t *Torrent, piece int) { cl.mu.Lock() defer cl.mu.Unlock() p := &t.pieces[piece] @@ -2427,16 +2421,16 @@ cl.pieceHashed(t, piece, sum == p.Hash) } // Returns handles to all the torrents loaded in the Client. -func (me *Client) Torrents() (ret []Torrent) { +func (me *Client) Torrents() (ret []*Torrent) { me.mu.Lock() for _, t := range me.torrents { - ret = append(ret, Torrent{me, t}) + ret = append(ret, t) } me.mu.Unlock() return } -func (me *Client) AddMagnet(uri string) (T Torrent, err error) { +func (me *Client) AddMagnet(uri string) (T *Torrent, err error) { spec, err := TorrentSpecFromMagnetURI(uri) if err != nil { return @@ -2445,7 +2439,7 @@ T, _, err = me.AddTorrentSpec(spec) return } -func (me *Client) AddTorrent(mi *metainfo.MetaInfo) (T Torrent, err error) { +func (me *Client) AddTorrent(mi *metainfo.MetaInfo) (T *Torrent, err error) { T, _, err = me.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) var ss []string missinggo.CastSlice(&ss, mi.Nodes) @@ -2453,7 +2447,7 @@ me.AddDHTNodes(ss) return } -func (me *Client) AddTorrentFromFile(filename string) (T Torrent, err error) { +func (me *Client) AddTorrentFromFile(filename string) (T *Torrent, err error) { mi, err := metainfo.LoadFromFile(filename) if err != nil { return diff --git a/client_test.go b/client_test.go index 350a100d86e487fa5b790c5ed86c85bcf08a24d1..108357d6653a92d7bbdc41a219231d4fba2ffb9f 100644 --- a/client_test.go +++ b/client_test.go @@ -455,8 +455,8 @@ _, new, _ = cl.AddTorrentSpec(&spec) if new { t.FailNow() } - assert.EqualValues(t, T.torrent.trackers[0][0], "http://a") - assert.EqualValues(t, T.torrent.trackers[1][0], "udp://b") + assert.EqualValues(t, T.trackers[0][0], "http://a") + assert.EqualValues(t, T.trackers[1][0], "udp://b") } type badStorage struct{} @@ -787,7 +787,7 @@ defer cl.Close() assert.EqualValues(t, cl.DHT().NumNodes(), 0) tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent") require.NoError(t, err) - assert.Len(t, tt.torrent.trackers, 5) + assert.Len(t, tt.trackers, 5) assert.EqualValues(t, 6, cl.DHT().NumNodes()) } @@ -897,7 +897,7 @@ require.NoError(t, err) assert.True(t, _new) defer tt.Drop() cn := &connection{ - t: tt.torrent, + t: tt, } assert.NoError(t, cn.peerSentHave(0)) assert.Error(t, cn.peerSentHave(1)) diff --git a/cmd/magnet-metainfo/main.go b/cmd/magnet-metainfo/main.go index 878fc7db9404c81445c57325b9a2ce16bde88982..8bad76fd6db40388910e4df3d32fea5d52d99cb7 100644 --- a/cmd/magnet-metainfo/main.go +++ b/cmd/magnet-metainfo/main.go @@ -27,7 +27,7 @@ wg.Add(1) go func() { defer wg.Done() <-t.GotInfo() - mi := t.MetaInfo() + mi := t.Metainfo() t.Drop() f, err := os.Create(mi.Info.Name + ".torrent") if err != nil { diff --git a/cmd/torrent-pick/main.go b/cmd/torrent-pick/main.go index 5389e3841e358eaf2e0ed43ae12421fc06bb49af..e8a25ad2278f977c96508bece61a3b2fbe7ebf1a 100644 --- a/cmd/torrent-pick/main.go +++ b/cmd/torrent-pick/main.go @@ -135,7 +135,7 @@ dstWriter := bufio.NewWriter(f) done := make(chan struct{}) for _, arg := range posArgs { - t := func() torrent.Torrent { + t := func() *torrent.Torrent { if strings.HasPrefix(arg, "magnet:") { t, err := client.AddMagnet(arg) if err != nil { diff --git a/cmd/torrent/main.go b/cmd/torrent/main.go index bf8dc0b06d23cab14c6a38342e5a55b410d89733..72406c960f9c66736899ab24646631fd2f1d5880 100644 --- a/cmd/torrent/main.go +++ b/cmd/torrent/main.go @@ -36,7 +36,7 @@ } return } -func torrentBar(t torrent.Torrent) { +func torrentBar(t *torrent.Torrent) { bar := uiprogress.AddBar(1) bar.AppendCompleted() bar.AppendFunc(func(*uiprogress.Bar) (ret string) { @@ -69,7 +69,7 @@ } func addTorrents(client *torrent.Client) { for _, arg := range opts.Torrent { - t := func() torrent.Torrent { + t := func() *torrent.Torrent { if strings.HasPrefix(arg, "magnet:") { t, err := client.AddMagnet(arg) if err != nil { diff --git a/connection.go b/connection.go index 51db22481db8da38816dd7b43ddb9fa8900dd356..aab97f563d2a1e9ede3bebaf7ec5e33b74c9a150 100644 --- a/connection.go +++ b/connection.go @@ -35,7 +35,7 @@ ) // Maintains the state of a connection with a peer. type connection struct { - t *torrent + t *Torrent conn net.Conn rw io.ReadWriter // The real slim shady encrypted bool @@ -188,7 +188,7 @@ cn.WriteStatus(&buf, nil) return buf.String() } -func (cn *connection) WriteStatus(w io.Writer, t *torrent) { +func (cn *connection) WriteStatus(w io.Writer, t *Torrent) { // \t isn't preserved in
blocks?
fmt.Fprintf(w, "%+q: %s-%s\n", cn.PeerID, cn.localAddr(), cn.remoteAddr())
fmt.Fprintf(w, " last msg: %s, connected: %s, last useful chunk: %s\n",
diff --git a/example_test.go b/example_test.go
index 8a4377af3b7836f6219482c8325ba211dfbddc7d..5c58e4d93a59d91cfa351904e99e31a9c4ee662c 100644
--- a/example_test.go
+++ b/example_test.go
@@ -20,7 +20,7 @@ }
func Example_fileReader() {
var (
- t torrent.Torrent
+ t *torrent.Torrent
f torrent.File
)
r := t.NewReader()
diff --git a/file.go b/file.go
index 7c8a79bb97e7db06296abc58ec087fab2540a18b..f57cd7af8a35b8491040fbd51e3e4fba88d611b1 100644
--- a/file.go
+++ b/file.go
@@ -8,14 +8,14 @@ )
// Provides access to regions of torrent data that correspond to its files.
type File struct {
- t Torrent
+ t *Torrent
path string
offset int64
length int64
fi metainfo.FileInfo
}
-func (f *File) Torrent() Torrent {
+func (f *File) Torrent() *Torrent {
return f.t
}
@@ -54,7 +54,7 @@ }
// Returns the state of pieces in this file.
func (f *File) State() (ret []FilePieceState) {
- pieceSize := int64(f.t.torrent.usualPieceSize())
+ pieceSize := int64(f.t.usualPieceSize())
off := f.offset % pieceSize
remaining := f.length
for i := int(f.offset / pieceSize); ; i++ {
@@ -66,7 +66,7 @@ if len1 > remaining {
len1 = remaining
}
f.t.cl.mu.RLock()
- ps := f.t.torrent.pieceState(i)
+ ps := f.t.pieceState(i)
f.t.cl.mu.RUnlock()
ret = append(ret, FilePieceState{len1, ps})
off = 0
@@ -77,13 +77,13 @@ }
// Requests that all pieces containing data in the file be downloaded.
func (f *File) Download() {
- f.t.DownloadPieces(f.t.torrent.byteRegionPieces(f.offset, f.length))
+ f.t.DownloadPieces(f.t.byteRegionPieces(f.offset, f.length))
}
// Requests that torrent pieces containing bytes in the given region of the
// file be downloaded.
func (f *File) PrioritizeRegion(off, len int64) {
- f.t.DownloadPieces(f.t.torrent.byteRegionPieces(f.offset+off, len))
+ f.t.DownloadPieces(f.t.byteRegionPieces(f.offset+off, len))
}
func byteRegionExclusivePieces(off, size, pieceSize int64) (begin, end int) {
@@ -93,7 +93,7 @@ return
}
func (f *File) exclusivePieces() (begin, end int) {
- return byteRegionExclusivePieces(f.offset, f.length, int64(f.t.torrent.usualPieceSize()))
+ return byteRegionExclusivePieces(f.offset, f.length, int64(f.t.usualPieceSize()))
}
func (f *File) Cancel() {
diff --git a/fs/torrentfs.go b/fs/torrentfs.go
index abca5c8fe1afd76120c94db94023e208a6c6a6ea..40650dd011b114dda114dddd747e40996f9fd67e 100644
--- a/fs/torrentfs.go
+++ b/fs/torrentfs.go
@@ -51,7 +51,7 @@ type node struct {
path string
metadata *metainfo.InfoEx
FS *TorrentFS
- t torrent.Torrent
+ t *torrent.Torrent
}
type fileNode struct {
@@ -70,7 +70,7 @@ func (n *node) fsPath() string {
return "/" + n.metadata.Name + "/" + n.path
}
-func blockingRead(ctx context.Context, fs *TorrentFS, t torrent.Torrent, off int64, p []byte) (n int, err error) {
+func blockingRead(ctx context.Context, fs *TorrentFS, t *torrent.Torrent, off int64, p []byte) (n int, err error) {
fs.mu.Lock()
fs.blockedReads++
fs.event.Broadcast()
@@ -106,7 +106,7 @@ fs.mu.Unlock()
return
}
-func readFull(ctx context.Context, fs *TorrentFS, t torrent.Torrent, off int64, p []byte) (n int, err error) {
+func readFull(ctx context.Context, fs *TorrentFS, t *torrent.Torrent, off int64, p []byte) (n int, err error) {
for len(p) != 0 {
var nn int
nn, err = blockingRead(ctx, fs, t, off, p)
diff --git a/fs/torrentfs_test.go b/fs/torrentfs_test.go
index 62191c6f6c6395afefa9294df3720992ac3b11f5..cca219925184a38e60c53bde93ab2e2f95c6df8b 100644
--- a/fs/torrentfs_test.go
+++ b/fs/torrentfs_test.go
@@ -84,9 +84,7 @@ // Unmount without first killing the FUSE connection while there are FUSE
// operations blocked inside the filesystem code.
func TestUnmountWedged(t *testing.T) {
layout, err := newGreetingLayout()
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
defer func() {
err := layout.Destroy()
if err != nil {
@@ -103,11 +101,10 @@ DisableUTP: true,
NoDefaultBlocklist: true,
})
- if err != nil {
- t.Fatal(err)
- }
+ require.NoError(t, err)
defer client.Close()
- client.AddTorrent(layout.Metainfo)
+ _, err = client.AddTorrent(layout.Metainfo)
+ require.NoError(t, err)
fs := New(client)
fuseConn, err := fuse.Mount(layout.MountDir)
if err != nil {
diff --git a/piece.go b/piece.go
index f5112f807518fadfd9223d8e0ab7299e46b16047..015ffb2fe510d30f6ddcaf83da7b110f6a8522f8 100644
--- a/piece.go
+++ b/piece.go
@@ -31,7 +31,7 @@
type piece struct {
// The completed piece SHA1 hash, from the metainfo "pieces" field.
Hash pieceSum
- t *torrent
+ t *Torrent
index int
// Chunks we've written to since the last check. The chunk offset and
// length can be determined by the request chunkSize in use.
diff --git a/reader.go b/reader.go
index a8897ba3b94879813ea2ba12ace4a9be7e0bcbbc..46e41dff06b40e4d53a54d8b34eb8ff613f3fb6a 100644
--- a/reader.go
+++ b/reader.go
@@ -47,27 +47,27 @@ func (r *Reader) readable(off int64) (ret bool) {
if r.torrentClosed() {
return true
}
- req, ok := r.t.torrent.offsetRequest(off)
+ req, ok := r.t.offsetRequest(off)
if !ok {
panic(off)
}
if r.responsive {
- return r.t.torrent.haveChunk(req)
+ return r.t.haveChunk(req)
}
- return r.t.torrent.pieceComplete(int(req.Index))
+ return r.t.pieceComplete(int(req.Index))
}
// How many bytes are available to read. Max is the most we could require.
func (r *Reader) available(off, max int64) (ret int64) {
for max > 0 {
- req, ok := r.t.torrent.offsetRequest(off)
+ req, ok := r.t.offsetRequest(off)
if !ok {
break
}
- if !r.t.torrent.haveChunk(req) {
+ if !r.t.haveChunk(req) {
break
}
- len1 := int64(req.Length) - (off - r.t.torrent.requestOffset(req))
+ len1 := int64(req.Length) - (off - r.t.requestOffset(req))
max -= len1
ret += len1
off += len1
@@ -80,7 +80,7 @@ return
}
func (r *Reader) tickleClient() {
- r.t.torrent.readersChanged()
+ r.t.readersChanged()
}
func (r *Reader) waitReadable(off int64) {
@@ -108,7 +108,7 @@ r.mu.Lock()
r.pos += int64(n1)
r.mu.Unlock()
}
- if r.pos >= r.t.torrent.length {
+ if r.pos >= r.t.length {
err = io.EOF
} else if err == io.EOF {
err = io.ErrUnexpectedEOF
@@ -118,7 +118,7 @@ }
// Safe to call with or without client lock.
func (r *Reader) torrentClosed() bool {
- return r.t.torrent.isClosed()
+ return r.t.isClosed()
}
// Wait until some data should be available to read. Tickles the client if it
@@ -134,7 +134,7 @@ }
// Performs at most one successful read to torrent storage.
func (r *Reader) readOnceAt(b []byte, pos int64) (n int, err error) {
- if pos >= r.t.torrent.length {
+ if pos >= r.t.length {
err = io.EOF
return
}
@@ -151,15 +151,15 @@ pi := int(pos / r.t.Info().PieceLength)
ip := r.t.Info().Piece(pi)
po := pos % ip.Length()
missinggo.LimitLen(&b1, ip.Length()-po)
- n, err = r.t.torrent.readAt(b1, pos)
+ n, err = r.t.readAt(b1, pos)
if n != 0 {
err = nil
return
}
// log.Printf("%s: error reading from torrent storage pos=%d: %s", r.t, pos, err)
r.t.cl.mu.Lock()
- r.t.torrent.updateAllPieceCompletions()
- r.t.torrent.updatePiecePriorities()
+ r.t.updateAllPieceCompletions()
+ r.t.updatePiecePriorities()
r.t.cl.mu.Unlock()
}
}
@@ -173,7 +173,7 @@
func (r *Reader) posChanged() {
r.t.cl.mu.Lock()
defer r.t.cl.mu.Unlock()
- r.t.torrent.readersChanged()
+ r.t.readersChanged()
}
func (r *Reader) Seek(off int64, whence int) (ret int64, err error) {
@@ -187,7 +187,7 @@ r.pos = off
case os.SEEK_CUR:
r.pos += off
case os.SEEK_END:
- r.pos = r.t.torrent.info.TotalLength() + off
+ r.pos = r.t.info.TotalLength() + off
default:
err = errors.New("bad whence")
}
diff --git a/t.go b/t.go
index 7cd28ab65ce738e6083e8a2403b74cd1549be071..fffdb858f249f70327459435e26e30c1b20c2150 100644
--- a/t.go
+++ b/t.go
@@ -6,38 +6,29 @@
"github.com/anacrolix/torrent/metainfo"
)
-// This file contains Torrent, until I decide where the private, lower-case
-// "torrent" type belongs. That type is currently mostly in torrent.go.
-
-// The public handle to a live torrent within a Client.
-type Torrent struct {
- cl *Client
- torrent *torrent
-}
-
// The torrent's infohash. This is fixed and cannot change. It uniquely
// identifies a torrent.
-func (t Torrent) InfoHash() metainfo.InfoHash {
- return t.torrent.InfoHash
+func (t *Torrent) InfoHash() metainfo.InfoHash {
+ return t.infoHash
}
// Closed when the info (.Info()) for the torrent has become available. Using
// features of Torrent that require the info before it is available will have
// undefined behaviour.
-func (t Torrent) GotInfo() <-chan struct{} {
- return t.torrent.gotMetainfo
+func (t *Torrent) GotInfo() <-chan struct{} {
+ return t.gotMetainfo
}
// Returns the metainfo info dictionary, or nil if it's not yet available.
-func (t Torrent) Info() *metainfo.InfoEx {
- return t.torrent.info
+func (t *Torrent) Info() *metainfo.InfoEx {
+ return t.info
}
// Returns a Reader bound to the torrent's data. All read calls block until
// the data requested is actually available.
-func (t Torrent) NewReader() (ret *Reader) {
+func (t *Torrent) NewReader() (ret *Reader) {
ret = &Reader{
- t: &t,
+ t: t,
readahead: 5 * 1024 * 1024,
}
t.addReader(ret)
@@ -47,72 +38,72 @@
// Returns the state of pieces of the torrent. They are grouped into runs of
// same state. The sum of the state run lengths is the number of pieces
// in the torrent.
-func (t Torrent) PieceStateRuns() []PieceStateRun {
+func (t *Torrent) PieceStateRuns() []PieceStateRun {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- return t.torrent.pieceStateRuns()
+ return t.pieceStateRuns()
}
-func (t Torrent) PieceState(piece int) PieceState {
+func (t *Torrent) PieceState(piece int) PieceState {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- return t.torrent.pieceState(piece)
+ return t.pieceState(piece)
}
// The number of pieces in the torrent. This requires that the info has been
// obtained first.
-func (t Torrent) NumPieces() int {
- return t.torrent.numPieces()
+func (t *Torrent) NumPieces() int {
+ return t.numPieces()
}
// Drop the torrent from the client, and close it.
-func (t Torrent) Drop() {
+func (t *Torrent) Drop() {
t.cl.mu.Lock()
- t.cl.dropTorrent(t.torrent.InfoHash)
+ t.cl.dropTorrent(t.infoHash)
t.cl.mu.Unlock()
}
// Number of bytes of the entire torrent we have completed.
-func (t Torrent) BytesCompleted() int64 {
+func (t *Torrent) BytesCompleted() int64 {
t.cl.mu.RLock()
defer t.cl.mu.RUnlock()
- return t.torrent.bytesCompleted()
+ return t.bytesCompleted()
}
// The subscription emits as (int) the index of pieces as their state changes.
// A state change is when the PieceState for a piece alters in value.
-func (t Torrent) SubscribePieceStateChanges() *pubsub.Subscription {
- return t.torrent.pieceStateChanges.Subscribe()
+func (t *Torrent) SubscribePieceStateChanges() *pubsub.Subscription {
+ return t.pieceStateChanges.Subscribe()
}
// Returns true if the torrent is currently being seeded. This occurs when the
// client is willing to upload without wanting anything in return.
-func (t Torrent) Seeding() bool {
+func (t *Torrent) Seeding() bool {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- return t.cl.seeding(t.torrent)
+ return t.cl.seeding(t)
}
// Clobbers the torrent display name. The display name is used as the torrent
// name if the metainfo is not available.
-func (t Torrent) SetDisplayName(dn string) {
+func (t *Torrent) SetDisplayName(dn string) {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- t.torrent.setDisplayName(dn)
+ t.setDisplayName(dn)
}
// The current working name for the torrent. Either the name in the info dict,
// or a display name given such as by the dn value in a magnet link, or "".
-func (t Torrent) Name() string {
+func (t *Torrent) Name() string {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- return t.torrent.Name()
+ return t.name()
}
-func (t Torrent) Length() int64 {
+func (t *Torrent) Length() int64 {
select {
case <-t.GotInfo():
- return t.torrent.length
+ return t.length
default:
return -1
}
@@ -120,41 +111,37 @@ }
// Returns a run-time generated metainfo for the torrent that includes the
// info bytes and announce-list as currently known to the client.
-func (t Torrent) MetaInfo() *metainfo.MetaInfo {
+func (t *Torrent) Metainfo() *metainfo.MetaInfo {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- return t.torrent.MetaInfo()
+ return t.metainfo()
}
-func (t Torrent) addReader(r *Reader) {
+func (t *Torrent) addReader(r *Reader) {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- if t.torrent.readers == nil {
- t.torrent.readers = make(map[*Reader]struct{})
+ if t.readers == nil {
+ t.readers = make(map[*Reader]struct{})
}
- t.torrent.readers[r] = struct{}{}
- t.torrent.readersChanged()
+ t.readers[r] = struct{}{}
+ t.readersChanged()
}
-func (t Torrent) deleteReader(r *Reader) {
+func (t *Torrent) deleteReader(r *Reader) {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- delete(t.torrent.readers, r)
- t.torrent.readersChanged()
+ delete(t.readers, r)
+ t.readersChanged()
}
-func (t Torrent) DownloadPieces(begin, end int) {
+func (t *Torrent) DownloadPieces(begin, end int) {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- t.torrent.pendPieceRange(begin, end)
+ t.pendPieceRange(begin, end)
}
-func (t Torrent) CancelPieces(begin, end int) {
+func (t *Torrent) CancelPieces(begin, end int) {
t.cl.mu.Lock()
defer t.cl.mu.Unlock()
- t.torrent.unpendPieceRange(begin, end)
-}
-
-func (t Torrent) String() string {
- return t.torrent.String()
+ t.unpendPieceRange(begin, end)
}
diff --git a/torrent.go b/torrent.go
index 3fd2e63c6c54b1f0c71e325744fa765a79a3280e..790be4d5c870af336fd473ec73d4b19fabaf4721 100644
--- a/torrent.go
+++ b/torrent.go
@@ -26,7 +26,7 @@ pp "github.com/anacrolix/torrent/peer_protocol"
"github.com/anacrolix/torrent/storage"
)
-func (t *torrent) chunkIndexSpec(chunkIndex, piece int) chunkSpec {
+func (t *Torrent) chunkIndexSpec(chunkIndex, piece int) chunkSpec {
return chunkIndexSpec(chunkIndex, t.pieceLength(piece), t.chunkSize)
}
@@ -36,7 +36,7 @@ Port int
}
// Maintains state of torrent within a Client.
-type torrent struct {
+type Torrent struct {
cl *Client
closing chan struct{}
@@ -45,7 +45,7 @@ // Closed when no more network activity is desired. This includes
// announcing, and communicating with peers.
ceasingNetworking chan struct{}
- InfoHash metainfo.InfoHash
+ infoHash metainfo.InfoHash
pieces []piece
// Values are the piece indices that changed.
pieceStateChanges *pubsub.PubSub
@@ -101,19 +101,19 @@ pieceInclinationsNew = expvar.NewInt("pieceInclinationsNew")
pieceInclinationsPut = expvar.NewInt("pieceInclinationsPut")
)
-func (t *torrent) setDisplayName(dn string) {
+func (t *Torrent) setDisplayName(dn string) {
t.displayName = dn
}
-func (t *torrent) pieceComplete(piece int) bool {
+func (t *Torrent) pieceComplete(piece int) bool {
return t.completedPieces.Get(piece)
}
-func (t *torrent) pieceCompleteUncached(piece int) bool {
+func (t *Torrent) pieceCompleteUncached(piece int) bool {
return t.pieces[piece].Storage().GetIsComplete()
}
-func (t *torrent) numConnsUnchoked() (num int) {
+func (t *Torrent) numConnsUnchoked() (num int) {
for _, c := range t.conns {
if !c.PeerChoked {
num++
@@ -123,7 +123,7 @@ return
}
// There's a connection to that address already.
-func (t *torrent) addrActive(addr string) bool {
+func (t *Torrent) addrActive(addr string) bool {
if _, ok := t.halfOpen[addr]; ok {
return true
}
@@ -135,7 +135,7 @@ }
return false
}
-func (t *torrent) worstConns(cl *Client) (wcs *worstConns) {
+func (t *Torrent) worstConns(cl *Client) (wcs *worstConns) {
wcs = &worstConns{
c: make([]*connection, 0, len(t.conns)),
t: t,
@@ -149,7 +149,7 @@ }
return
}
-func (t *torrent) ceaseNetworking() {
+func (t *Torrent) ceaseNetworking() {
select {
case <-t.ceasingNetworking:
return
@@ -161,7 +161,7 @@ c.Close()
}
}
-func (t *torrent) addPeer(p Peer, cl *Client) {
+func (t *Torrent) addPeer(p Peer, cl *Client) {
cl.openNewConns(t)
if len(t.peers) >= torrentPeersHighWater {
return
@@ -176,13 +176,13 @@ cl.openNewConns(t)
}
-func (t *torrent) invalidateMetadata() {
+func (t *Torrent) invalidateMetadata() {
t.metadataBytes = nil
t.metadataCompletedChunks = nil
t.info = nil
}
-func (t *torrent) saveMetadataPiece(index int, data []byte) {
+func (t *Torrent) saveMetadataPiece(index int, data []byte) {
if t.haveInfo() {
return
}
@@ -194,11 +194,11 @@ copy(t.metadataBytes[(1<<14)*index:], data)
t.metadataCompletedChunks[index] = true
}
-func (t *torrent) metadataPieceCount() int {
+func (t *Torrent) metadataPieceCount() int {
return (len(t.metadataBytes) + (1 << 14) - 1) / (1 << 14)
}
-func (t *torrent) haveMetadataPiece(piece int) bool {
+func (t *Torrent) haveMetadataPiece(piece int) bool {
if t.haveInfo() {
return (1<<14)*piece < len(t.metadataBytes)
} else {
@@ -206,11 +206,11 @@ return piece < len(t.metadataCompletedChunks) && t.metadataCompletedChunks[piece]
}
}
-func (t *torrent) metadataSizeKnown() bool {
+func (t *Torrent) metadataSizeKnown() bool {
return t.metadataBytes != nil
}
-func (t *torrent) metadataSize() int {
+func (t *Torrent) metadataSize() int {
return len(t.metadataBytes)
}
@@ -222,7 +222,7 @@ return
}
// Called when metadata for a torrent becomes available.
-func (t *torrent) setMetadata(md *metainfo.Info, infoBytes []byte) (err error) {
+func (t *Torrent) setMetadata(md *metainfo.Info, infoBytes []byte) (err error) {
err = validateInfo(md)
if err != nil {
err = fmt.Errorf("bad info: %s", err)
@@ -231,7 +231,7 @@ }
t.info = &metainfo.InfoEx{
Info: *md,
Bytes: infoBytes,
- Hash: &t.InfoHash,
+ Hash: &t.infoHash,
}
t.storage, err = t.storageOpener.OpenTorrent(t.info)
if err != nil {
@@ -270,11 +270,11 @@ }()
return
}
-func (t *torrent) verifyPiece(piece int) {
+func (t *Torrent) verifyPiece(piece int) {
t.cl.verifyPiece(t, piece)
}
-func (t *torrent) haveAllMetadataPieces() bool {
+func (t *Torrent) haveAllMetadataPieces() bool {
if t.haveInfo() {
return true
}
@@ -289,7 +289,7 @@ }
return true
}
-func (t *torrent) setMetadataSize(bytes int64, cl *Client) {
+func (t *Torrent) setMetadataSize(bytes int64, cl *Client) {
if t.haveInfo() {
// We already know the correct metadata size.
return
@@ -311,14 +311,14 @@ }
// The current working name for the torrent. Either the name in the info dict,
// or a display name given such as by the dn value in a magnet link, or "".
-func (t *torrent) Name() string {
+func (t *Torrent) name() string {
if t.haveInfo() {
return t.info.Name
}
return t.displayName
}
-func (t *torrent) pieceState(index int) (ret PieceState) {
+func (t *Torrent) pieceState(index int) (ret PieceState) {
p := &t.pieces[index]
ret.Priority = t.piecePriority(index)
if t.pieceComplete(index) {
@@ -333,11 +333,11 @@ }
return
}
-func (t *torrent) metadataPieceSize(piece int) int {
+func (t *Torrent) metadataPieceSize(piece int) int {
return metadataPieceSize(len(t.metadataBytes), piece)
}
-func (t *torrent) newMetadataExtensionMessage(c *connection, msgType int, piece int, data []byte) pp.Message {
+func (t *Torrent) newMetadataExtensionMessage(c *connection, msgType int, piece int, data []byte) pp.Message {
d := map[string]int{
"msg_type": msgType,
"piece": piece,
@@ -356,7 +356,7 @@ ExtendedPayload: append(p, data...),
}
}
-func (t *torrent) pieceStateRuns() (ret []PieceStateRun) {
+func (t *Torrent) pieceStateRuns() (ret []PieceStateRun) {
rle := missinggo.NewRunLengthEncoder(func(el interface{}, count uint64) {
ret = append(ret, PieceStateRun{
PieceState: el.(PieceState),
@@ -399,8 +399,8 @@ }
return
}
-func (t *torrent) writeStatus(w io.Writer, cl *Client) {
- fmt.Fprintf(w, "Infohash: %x\n", t.InfoHash)
+func (t *Torrent) writeStatus(w io.Writer, cl *Client) {
+ fmt.Fprintf(w, "Infohash: %x\n", t.infoHash)
fmt.Fprintf(w, "Metadata length: %d\n", t.metadataSize())
if !t.haveInfo() {
fmt.Fprintf(w, "Metadata have: ")
@@ -458,27 +458,27 @@ c.WriteStatus(w, t)
}
}
-func (t *torrent) String() string {
+func (t *Torrent) String() string {
s := t.Name()
if s == "" {
- s = fmt.Sprintf("%x", t.InfoHash)
+ s = fmt.Sprintf("%x", t.infoHash)
}
return s
}
-func (t *torrent) haveInfo() bool {
+func (t *Torrent) haveInfo() bool {
return t.info != nil
}
// TODO: Include URIs that weren't converted to tracker clients.
-func (t *torrent) announceList() (al [][]string) {
+func (t *Torrent) announceList() (al [][]string) {
missinggo.CastSlice(&al, t.trackers)
return
}
// Returns a run-time generated MetaInfo that includes the info bytes and
// announce-list as currently known to the client.
-func (t *torrent) MetaInfo() *metainfo.MetaInfo {
+func (t *Torrent) metainfo() *metainfo.MetaInfo {
if t.metadataBytes == nil {
panic("info bytes not set")
}
@@ -491,7 +491,7 @@ AnnounceList: t.announceList(),
}
}
-func (t *torrent) bytesLeft() (left int64) {
+func (t *Torrent) bytesLeft() (left int64) {
for i := 0; i < t.numPieces(); i++ {
left += int64(t.pieces[i].bytesLeft())
}
@@ -499,7 +499,7 @@ return
}
// Bytes left to give in tracker announces.
-func (t *torrent) bytesLeftAnnounce() uint64 {
+func (t *Torrent) bytesLeftAnnounce() uint64 {
if t.haveInfo() {
return uint64(t.bytesLeft())
} else {
@@ -507,7 +507,7 @@ return math.MaxUint64
}
}
-func (t *torrent) piecePartiallyDownloaded(piece int) bool {
+func (t *Torrent) piecePartiallyDownloaded(piece int) bool {
if t.pieceComplete(piece) {
return false
}
@@ -521,24 +521,24 @@ func numChunksForPiece(chunkSize int, pieceSize int) int {
return (pieceSize + chunkSize - 1) / chunkSize
}
-func (t *torrent) usualPieceSize() int {
+func (t *Torrent) usualPieceSize() int {
return int(t.info.PieceLength)
}
-func (t *torrent) lastPieceSize() int {
+func (t *Torrent) lastPieceSize() int {
return int(t.pieceLength(t.numPieces() - 1))
}
-func (t *torrent) numPieces() int {
+func (t *Torrent) numPieces() int {
return t.info.NumPieces()
}
-func (t *torrent) numPiecesCompleted() (num int) {
+func (t *Torrent) numPiecesCompleted() (num int) {
return t.completedPieces.Len()
}
// Safe to call with or without client lock.
-func (t *torrent) isClosed() bool {
+func (t *Torrent) isClosed() bool {
select {
case <-t.closing:
return true
@@ -547,7 +547,7 @@ return false
}
}
-func (t *torrent) close() (err error) {
+func (t *Torrent) close() (err error) {
if t.isClosed() {
return
}
@@ -563,17 +563,17 @@ t.pieceStateChanges.Close()
return
}
-func (t *torrent) requestOffset(r request) int64 {
+func (t *Torrent) requestOffset(r request) int64 {
return torrentRequestOffset(t.length, int64(t.usualPieceSize()), r)
}
// Return the request that would include the given offset into the torrent
// data. Returns !ok if there is no such request.
-func (t *torrent) offsetRequest(off int64) (req request, ok bool) {
+func (t *Torrent) offsetRequest(off int64) (req request, ok bool) {
return torrentOffsetRequest(t.length, t.info.PieceLength, int64(t.chunkSize), off)
}
-func (t *torrent) writeChunk(piece int, begin int64, data []byte) (err error) {
+func (t *Torrent) writeChunk(piece int, begin int64, data []byte) (err error) {
tr := perf.NewTimer()
n, err := t.pieces[piece].Storage().WriteAt(data, begin)
@@ -586,7 +586,7 @@ }
return
}
-func (t *torrent) bitfield() (bf []bool) {
+func (t *Torrent) bitfield() (bf []bool) {
bf = make([]bool, t.numPieces())
t.completedPieces.IterTyped(func(piece int) (again bool) {
bf[piece] = true
@@ -595,7 +595,7 @@ })
return
}
-func (t *torrent) validOutgoingRequest(r request) bool {
+func (t *Torrent) validOutgoingRequest(r request) bool {
if r.Index >= pp.Integer(t.info.NumPieces()) {
return false
}
@@ -612,7 +612,7 @@ }
return r.Length == t.chunkSize || r.Begin+r.Length == pieceLength
}
-func (t *torrent) pieceChunks(piece int) (css []chunkSpec) {
+func (t *Torrent) pieceChunks(piece int) (css []chunkSpec) {
css = make([]chunkSpec, 0, (t.pieceLength(piece)+t.chunkSize-1)/t.chunkSize)
var cs chunkSpec
for left := t.pieceLength(piece); left != 0; left -= cs.Length {
@@ -626,11 +626,11 @@ }
return
}
-func (t *torrent) pieceNumChunks(piece int) int {
+func (t *Torrent) pieceNumChunks(piece int) int {
return int((t.pieceLength(piece) + t.chunkSize - 1) / t.chunkSize)
}
-func (t *torrent) pendAllChunkSpecs(pieceIndex int) {
+func (t *Torrent) pendAllChunkSpecs(pieceIndex int) {
t.pieces[pieceIndex].DirtyChunks.Clear()
}
@@ -643,7 +643,7 @@ // Peer is known to support encryption.
SupportsEncryption bool
}
-func (t *torrent) pieceLength(piece int) (len_ pp.Integer) {
+func (t *Torrent) pieceLength(piece int) (len_ pp.Integer) {
if piece < 0 || piece >= t.info.NumPieces() {
return
}
@@ -656,7 +656,7 @@ }
return
}
-func (t *torrent) hashPiece(piece int) (ret pieceSum) {
+func (t *Torrent) hashPiece(piece int) (ret pieceSum) {
hash := pieceHash.New()
p := &t.pieces[piece]
p.waitNoPendingWrites()
@@ -673,14 +673,14 @@ }
return
}
-func (t *torrent) haveAllPieces() bool {
+func (t *Torrent) haveAllPieces() bool {
if !t.haveInfo() {
return false
}
return t.completedPieces.Len() == t.numPieces()
}
-func (me *torrent) haveAnyPieces() bool {
+func (me *Torrent) haveAnyPieces() bool {
for i := range me.pieces {
if me.pieceComplete(i) {
return true
@@ -689,11 +689,11 @@ }
return false
}
-func (t *torrent) havePiece(index int) bool {
+func (t *Torrent) havePiece(index int) bool {
return t.haveInfo() && t.pieceComplete(index)
}
-func (t *torrent) haveChunk(r request) (ret bool) {
+func (t *Torrent) haveChunk(r request) (ret bool) {
// defer func() {
// log.Println("have chunk", r, ret)
// }()
@@ -712,7 +712,7 @@ return int(cs.Begin / chunkSize)
}
// TODO: This should probably be called wantPiece.
-func (t *torrent) wantChunk(r request) bool {
+func (t *Torrent) wantChunk(r request) bool {
if !t.wantPiece(int(r.Index)) {
return false
}
@@ -725,7 +725,7 @@ return false
}
// TODO: This should be called wantPieceIndex.
-func (t *torrent) wantPiece(index int) bool {
+func (t *Torrent) wantPiece(index int) bool {
if !t.haveInfo() {
return false
}
@@ -747,7 +747,7 @@ return index < begin || index >= end
})
}
-func (t *torrent) forNeededPieces(f func(piece int) (more bool)) (all bool) {
+func (t *Torrent) forNeededPieces(f func(piece int) (more bool)) (all bool) {
return t.forReaderOffsetPieces(func(begin, end int) (more bool) {
for i := begin; begin < end; i++ {
if !f(i) {
@@ -758,18 +758,18 @@ return true
})
}
-func (t *torrent) connHasWantedPieces(c *connection) bool {
+func (t *Torrent) connHasWantedPieces(c *connection) bool {
return !c.pieceRequestOrder.IsEmpty()
}
-func (t *torrent) extentPieces(off, _len int64) (pieces []int) {
+func (t *Torrent) extentPieces(off, _len int64) (pieces []int) {
for i := off / int64(t.usualPieceSize()); i*int64(t.usualPieceSize()) < off+_len; i++ {
pieces = append(pieces, int(i))
}
return
}
-func (t *torrent) worstBadConn(cl *Client) *connection {
+func (t *Torrent) worstBadConn(cl *Client) *connection {
wcs := t.worstConns(cl)
heap.Init(wcs)
for wcs.Len() != 0 {
@@ -792,7 +792,7 @@ Index int
PieceState
}
-func (t *torrent) publishPieceChange(piece int) {
+func (t *Torrent) publishPieceChange(piece int) {
cur := t.pieceState(piece)
p := &t.pieces[piece]
if cur != p.PublicPieceState {
@@ -804,18 +804,18 @@ })
}
}
-func (t *torrent) pieceNumPendingChunks(piece int) int {
+func (t *Torrent) pieceNumPendingChunks(piece int) int {
if t.pieceComplete(piece) {
return 0
}
return t.pieceNumChunks(piece) - t.pieces[piece].numDirtyChunks()
}
-func (t *torrent) pieceAllDirty(piece int) bool {
+func (t *Torrent) pieceAllDirty(piece int) bool {
return t.pieces[piece].DirtyChunks.Len() == t.pieceNumChunks(piece)
}
-func (t *torrent) forUrgentPieces(f func(piece int) (again bool)) (all bool) {
+func (t *Torrent) forUrgentPieces(f func(piece int) (again bool)) (all bool) {
return t.forReaderOffsetPieces(func(begin, end int) (again bool) {
if begin < end {
if !f(begin) {
@@ -826,17 +826,17 @@ return true
})
}
-func (t *torrent) readersChanged() {
+func (t *Torrent) readersChanged() {
t.updatePiecePriorities()
}
-func (t *torrent) maybeNewConns() {
+func (t *Torrent) maybeNewConns() {
// Tickle the accept routine.
t.cl.event.Broadcast()
t.openNewConns()
}
-func (t *torrent) piecePriorityChanged(piece int) {
+func (t *Torrent) piecePriorityChanged(piece int) {
for _, c := range t.conns {
c.updatePiecePriority(piece)
}
@@ -844,7 +844,7 @@ t.maybeNewConns()
t.publishPieceChange(piece)
}
-func (t *torrent) updatePiecePriority(piece int) bool {
+func (t *Torrent) updatePiecePriority(piece int) bool {
p := &t.pieces[piece]
newPrio := t.piecePriorityUncached(piece)
if newPrio == p.priority {
@@ -856,7 +856,7 @@ }
// Update all piece priorities in one hit. This function should have the same
// output as updatePiecePriority, but across all pieces.
-func (t *torrent) updatePiecePriorities() {
+func (t *Torrent) updatePiecePriorities() {
newPrios := make([]piecePriority, t.numPieces())
t.pendingPieces.IterTyped(func(piece int) (more bool) {
newPrios[piece] = PiecePriorityNormal
@@ -883,7 +883,7 @@ }
}
}
-func (t *torrent) byteRegionPieces(off, size int64) (begin, end int) {
+func (t *Torrent) byteRegionPieces(off, size int64) (begin, end int) {
if off >= t.length {
return
}
@@ -903,7 +903,7 @@ return
}
// Returns true if all iterations complete without breaking.
-func (t *torrent) forReaderOffsetPieces(f func(begin, end int) (more bool)) (all bool) {
+func (t *Torrent) forReaderOffsetPieces(f func(begin, end int) (more bool)) (all bool) {
// There's an oppurtunity here to build a map of beginning pieces, and a
// bitmap of the rest. I wonder if it's worth the allocation overhead.
for r := range t.readers {
@@ -924,14 +924,14 @@ }
return true
}
-func (t *torrent) piecePriority(piece int) piecePriority {
+func (t *Torrent) piecePriority(piece int) piecePriority {
if !t.haveInfo() {
return PiecePriorityNone
}
return t.pieces[piece].priority
}
-func (t *torrent) piecePriorityUncached(piece int) (ret piecePriority) {
+func (t *Torrent) piecePriorityUncached(piece int) (ret piecePriority) {
ret = PiecePriorityNone
if t.pieceComplete(piece) {
return
@@ -952,7 +952,7 @@ })
return
}
-func (t *torrent) pendPiece(piece int) {
+func (t *Torrent) pendPiece(piece int) {
if t.pendingPieces.Contains(piece) {
return
}
@@ -966,28 +966,28 @@ }
t.piecePriorityChanged(piece)
}
-func (t *torrent) getCompletedPieces() (ret bitmap.Bitmap) {
+func (t *Torrent) getCompletedPieces() (ret bitmap.Bitmap) {
return t.completedPieces.Copy()
}
-func (t *torrent) unpendPieces(unpend *bitmap.Bitmap) {
+func (t *Torrent) unpendPieces(unpend *bitmap.Bitmap) {
t.pendingPieces.Sub(unpend)
t.updatePiecePriorities()
}
-func (t *torrent) pendPieceRange(begin, end int) {
+func (t *Torrent) pendPieceRange(begin, end int) {
for i := begin; i < end; i++ {
t.pendPiece(i)
}
}
-func (t *torrent) unpendPieceRange(begin, end int) {
+func (t *Torrent) unpendPieceRange(begin, end int) {
var bm bitmap.Bitmap
bm.AddRange(begin, end)
t.unpendPieces(&bm)
}
-func (t *torrent) connRequestPiecePendingChunks(c *connection, piece int) (more bool) {
+func (t *Torrent) connRequestPiecePendingChunks(c *connection, piece int) (more bool) {
if !c.PeerHasPiece(piece) {
return true
}
@@ -998,20 +998,20 @@ return c.Request(req)
})
}
-func (t *torrent) pendRequest(req request) {
+func (t *Torrent) pendRequest(req request) {
ci := chunkIndex(req.chunkSpec, t.chunkSize)
t.pieces[req.Index].pendChunkIndex(ci)
}
-func (t *torrent) pieceChanged(piece int) {
+func (t *Torrent) pieceChanged(piece int) {
t.cl.pieceChanged(t, piece)
}
-func (t *torrent) openNewConns() {
+func (t *Torrent) openNewConns() {
t.cl.openNewConns(t)
}
-func (t *torrent) getConnPieceInclination() []int {
+func (t *Torrent) getConnPieceInclination() []int {
_ret := t.connPieceInclinationPool.Get()
if _ret == nil {
pieceInclinationsNew.Add(1)
@@ -1021,12 +1021,12 @@ pieceInclinationsReused.Add(1)
return _ret.([]int)
}
-func (t *torrent) putPieceInclination(pi []int) {
+func (t *Torrent) putPieceInclination(pi []int) {
t.connPieceInclinationPool.Put(pi)
pieceInclinationsPut.Add(1)
}
-func (t *torrent) updatePieceCompletion(piece int) {
+func (t *Torrent) updatePieceCompletion(piece int) {
pcu := t.pieceCompleteUncached(piece)
changed := t.completedPieces.Get(piece) != pcu
t.completedPieces.Set(piece, pcu)
@@ -1036,13 +1036,13 @@ }
}
// Non-blocking read. Client lock is not required.
-func (t *torrent) readAt(b []byte, off int64) (n int, err error) {
+func (t *Torrent) readAt(b []byte, off int64) (n int, err error) {
p := &t.pieces[off/t.info.PieceLength]
p.waitNoPendingWrites()
return p.Storage().ReadAt(b, off-p.Info().Offset())
}
-func (t *torrent) updateAllPieceCompletions() {
+func (t *Torrent) updateAllPieceCompletions() {
for i := range iter.N(t.numPieces()) {
t.updatePieceCompletion(i)
}
diff --git a/torrent_test.go b/torrent_test.go
index bca6f775f10bba480af15bea6d5c2d1b068d910b..6ffc860549fbc357658770582dd7e224e9be7831 100644
--- a/torrent_test.go
+++ b/torrent_test.go
@@ -52,8 +52,8 @@ }
}
func TestTorrentString(t *testing.T) {
- tor := &torrent{}
- s := tor.InfoHash.HexString()
+ tor := &Torrent{}
+ s := tor.InfoHash().HexString()
if s != "0000000000000000000000000000000000000000" {
t.FailNow()
}
diff --git a/worst_conns.go b/worst_conns.go
index 0f7aa3aa7842b2c5190b259067b8b08fba7f7716..7b9c5a916a0322d72f92f841e15778ab8d18bffc 100644
--- a/worst_conns.go
+++ b/worst_conns.go
@@ -7,7 +7,7 @@
// Implements heap functions such that [0] is the worst connection.
type worstConns struct {
c []*connection
- t *torrent
+ t *Torrent
cl *Client
}