From: Matt Joiner Date: Sun, 3 Apr 2016 08:40:43 +0000 (+1000) Subject: Merge Torrent and torrent types X-Git-Tag: v1.0.0~795 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=d3a1c79c7985f2bf973f5d6cc788a5dd067f1426;p=btrtrc.git Merge Torrent and torrent types --- diff --git a/client.go b/client.go index 9145a477..c03da726 100644 --- a/client.go +++ b/client.go @@ -114,7 +114,7 @@ const ( ) // 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 @@ func (cl *Client) queuePieceCheck(t *torrent, pieceIndex int) { // 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 @@ type Client struct { 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 @@ func NewClient(cfg *Config) (cl *Client, err error) { 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 @@ func (cl *Client) incomingConnection(nc net.Conn, utp bool) { } // 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 @@ type dialResult struct { 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 @@ func (me *Client) dopplegangerAddr(addr string) bool { // 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 @@ func (me *Client) initiateConn(peer Peer, t *torrent) { 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 @@ func (me *Client) dialTCP(addr string, t *torrent) (c net.Conn, err error) { 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 @@ func (me *Client) dialFirst(addr string, t *torrent) (conn net.Conn, utp bool) { 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 @@ func (me *Client) noLongerHalfOpen(t *torrent, addr string) { // 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 @@ func (me *Client) handshakesConnection(nc net.Conn, t *torrent, encrypted, utp b // 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 @@ func (me *Client) establishOutgoingConn(t *torrent, addr string) (c *connection, // 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 @@ func (cl *Client) receiveSkeys() (ret [][]byte) { 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 @@ func (cl *Client) connBTHandshake(c *connection, ih *metainfo.InfoHash) (ret met 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 @@ func (cl *Client) runReceivedConn(c *connection) (err error) { 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 @@ func (cl *Client) runHandshookConn(c *connection, t *torrent) (err error) { 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) sendInitialMessages(conn *connection, torrent *torrent) { } } -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 @@ func (cl *Client) connCancel(t *torrent, cn *connection, r request) (ok bool) { 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 @@ func (cl *Client) connDeleteRequest(t *torrent, cn *connection, r request) bool 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 @@ func (cl *Client) requestPendingMetadata(t *torrent, c *connection) { } } -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 @@ func (cl *Client) completedMetadata(t *torrent) { } // 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 @@ func (cl *Client) gotMetadataExtensionMsg(payload []byte, t *torrent, c *connect 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 @@ another: 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 @@ func (me *Client) sendChunk(t *torrent, c *connection, r request) error { // 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 @@ func (me *Client) connectionLoop(t *torrent, c *connection) error { } // 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 @@ func (me *Client) deleteConnection(t *torrent, c *connection) bool { 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 @@ func (me *Client) dropConnection(t *torrent, c *connection) { } // 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 @@ func (me *Client) addConnection(t *torrent, c *connection) bool { 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 @@ func (t *torrent) readerPieces() (ret bitmap.Bitmap) { return } -func (t *torrent) needData() bool { +func (t *Torrent) needData() bool { if !t.haveInfo() { return true } @@ -1614,7 +1610,7 @@ func (t *torrent) needData() bool { }) } -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 @@ func (cl *Client) usefulConn(t *torrent, c *connection) bool { 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 @@ func (me *Client) wantConns(t *torrent) bool { 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 @@ func (me *Client) openNewConns(t *torrent) { 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 @@ func (cl *Client) saveTorrentFile(t *torrent) error { } 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 @@ func (cl *Client) setMetaData(t *torrent, md *metainfo.Info, bytes []byte) (err // 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 @@ nextURL: 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 @@ func (t *torrent) addTrackers(announceList [][]string) { } // 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 @@ type Handle interface { // 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 @@ func (t Torrent) Files() (ret []File) { 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 @@ func TorrentSpecFromMetaInfo(mi *metainfo.MetaInfo) (spec *TorrentSpec) { // 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 @@ func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (T Torrent, new bool, err er 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 @@ func (me *Client) dropTorrent(infoHash metainfo.InfoHash) (err error) { } // 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 @@ func (cl *Client) waitWantPeers(t *torrent) bool { } // 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 @@ func (cl *Client) seeding(t *torrent) bool { 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 @@ func (cl *Client) trackerBlockedUnlocked(trRawURL string) (blocked bool, err err 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 @@ func (cl *Client) announceTorrentSingleTracker(tr string, req *tracker.AnnounceR 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 @@ func (cl *Client) announceTorrentTrackersFastStart(req *tracker.AnnounceRequest, } // 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 @@ func (me *Client) WaitAll() bool { } // 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 @@ func (me *Client) downloadedChunk(t *torrent, c *connection, msg *pp.Message) { // 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 @@ func (me *Client) reapPieceTouches(t *torrent, piece int) (ret []*connection) { 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 @@ func (me *Client) pieceHashed(t *torrent, piece int, correct bool) { 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 @@ func (me *Client) onCompletedPiece(t *torrent, piece int) { } } -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) onFailedPiece(t *torrent, piece int) { } } -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 @@ func (me *Client) pieceChanged(t *torrent, piece int) { 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 @@ func (cl *Client) verifyPiece(t *torrent, piece int) { } // 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 @@ func (me *Client) AddMagnet(uri string) (T Torrent, err error) { 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 @@ func (me *Client) AddTorrent(mi *metainfo.MetaInfo) (T Torrent, err error) { 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 350a100d..108357d6 100644 --- a/client_test.go +++ b/client_test.go @@ -455,8 +455,8 @@ func TestMergingTrackersByAddingSpecs(t *testing.T) { 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 @@ func TestAddMetainfoWithNodes(t *testing.T) { 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 @@ func TestPeerInvalidHave(t *testing.T) { 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 878fc7db..8bad76fd 100644 --- a/cmd/magnet-metainfo/main.go +++ b/cmd/magnet-metainfo/main.go @@ -27,7 +27,7 @@ func main() { 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 5389e384..e8a25ad2 100644 --- a/cmd/torrent-pick/main.go +++ b/cmd/torrent-pick/main.go @@ -135,7 +135,7 @@ func main() { 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 bf8dc0b0..72406c96 100644 --- a/cmd/torrent/main.go +++ b/cmd/torrent/main.go @@ -36,7 +36,7 @@ func resolvedPeerAddrs(ss []string) (ret []torrent.Peer, err error) { 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 torrentBar(t torrent.Torrent) { 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 51db2248..aab97f56 100644 --- a/connection.go +++ b/connection.go @@ -35,7 +35,7 @@ const ( // 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 @@ func (cn *connection) String() string { 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 8a4377af..5c58e4d9 100644
--- a/example_test.go
+++ b/example_test.go
@@ -20,7 +20,7 @@ func Example() {
 
 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 7c8a79bb..f57cd7af 100644
--- a/file.go
+++ b/file.go
@@ -8,14 +8,14 @@ import (
 
 // 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 @@ type FilePieceState struct {
 
 // 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 @@ func (f *File) State() (ret []FilePieceState) {
 			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 @@ func (f *File) State() (ret []FilePieceState) {
 
 // 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 @@ func byteRegionExclusivePieces(off, size, pieceSize int64) (begin, end int) {
 }
 
 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 abca5c8f..40650dd0 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 @@ func blockingRead(ctx context.Context, fs *TorrentFS, t torrent.Torrent, off int
 	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 62191c6f..cca21992 100644
--- a/fs/torrentfs_test.go
+++ b/fs/torrentfs_test.go
@@ -84,9 +84,7 @@ func newGreetingLayout() (tl testLayout, err error) {
 // 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 @@ func TestUnmountWedged(t *testing.T) {
 
 		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 f5112f80..015ffb2f 100644
--- a/piece.go
+++ b/piece.go
@@ -31,7 +31,7 @@ const (
 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 a8897ba3..46e41dff 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 @@ func (r *Reader) available(off, max int64) (ret int64) {
 }
 
 func (r *Reader) tickleClient() {
-	r.t.torrent.readersChanged()
+	r.t.readersChanged()
 }
 
 func (r *Reader) waitReadable(off int64) {
@@ -108,7 +108,7 @@ func (r *Reader) Read(b []byte) (n int, err error) {
 		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 @@ func (r *Reader) Read(b []byte) (n int, err error) {
 
 // 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 @@ func (r *Reader) waitAvailable(pos, wanted int64) (avail int64) {
 
 // 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 @@ func (r *Reader) readOnceAt(b []byte, pos int64) (n int, err error) {
 		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) Close() error {
 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 @@ func (r *Reader) Seek(off int64, whence int) (ret int64, err error) {
 	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 7cd28ab6..fffdb858 100644
--- a/t.go
+++ b/t.go
@@ -6,38 +6,29 @@ import (
 	"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 @@ func (t Torrent) NewReader() (ret *Reader) {
 // 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 @@ func (t Torrent) Length() int64 {
 
 // 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 3fd2e63c..790be4d5 100644
--- a/torrent.go
+++ b/torrent.go
@@ -26,7 +26,7 @@ import (
 	"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 @@ type peersKey struct {
 }
 
 // Maintains state of torrent within a Client.
-type torrent struct {
+type Torrent struct {
 	cl *Client
 
 	closing chan struct{}
@@ -45,7 +45,7 @@ type torrent struct {
 	// 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 @@ var (
 	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 @@ func (t *torrent) numConnsUnchoked() (num int) {
 }
 
 // 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 @@ func (t *torrent) addrActive(addr string) bool {
 	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 @@ func (t *torrent) worstConns(cl *Client) (wcs *worstConns) {
 	return
 }
 
-func (t *torrent) ceaseNetworking() {
+func (t *Torrent) ceaseNetworking() {
 	select {
 	case <-t.ceasingNetworking:
 		return
@@ -161,7 +161,7 @@ func (t *torrent) ceaseNetworking() {
 	}
 }
 
-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 @@ func (t *torrent) addPeer(p Peer, cl *Client) {
 
 }
 
-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 @@ func (t *torrent) saveMetadataPiece(index int, data []byte) {
 	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 @@ func (t *torrent) haveMetadataPiece(piece int) bool {
 	}
 }
 
-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 @@ func infoPieceHashes(info *metainfo.Info) (ret []string) {
 }
 
 // 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 @@ func (t *torrent) setMetadata(md *metainfo.Info, infoBytes []byte) (err error) {
 	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 @@ func (t *torrent) setMetadata(md *metainfo.Info, infoBytes []byte) (err error) {
 	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 @@ func (t *torrent) haveAllMetadataPieces() bool {
 	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 @@ func (t *torrent) setMetadataSize(bytes int64, cl *Client) {
 
 // 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 @@ func (t *torrent) pieceState(index int) (ret PieceState) {
 	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 @@ func (t *torrent) newMetadataExtensionMessage(c *connection, msgType int, piece
 	}
 }
 
-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 @@ func pieceStateRunStatusChars(psr PieceStateRun) (ret string) {
 	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 @@ func (t *torrent) writeStatus(w io.Writer, cl *Client) {
 	}
 }
 
-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 @@ func (t *torrent) MetaInfo() *metainfo.MetaInfo {
 	}
 }
 
-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 @@ func (t *torrent) bytesLeft() (left int64) {
 }
 
 // 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 @@ func (t *torrent) bytesLeftAnnounce() uint64 {
 	}
 }
 
-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 @@ func (t *torrent) isClosed() bool {
 	}
 }
 
-func (t *torrent) close() (err error) {
+func (t *Torrent) close() (err error) {
 	if t.isClosed() {
 		return
 	}
@@ -563,17 +563,17 @@ func (t *torrent) close() (err error) {
 	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 @@ func (t *torrent) writeChunk(piece int, begin int64, data []byte) (err error) {
 	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 @@ func (t *torrent) bitfield() (bf []bool) {
 	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 @@ func (t *torrent) validOutgoingRequest(r request) bool {
 	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 @@ func (t *torrent) pieceChunks(piece int) (css []chunkSpec) {
 	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 @@ type Peer struct {
 	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 @@ func (t *torrent) pieceLength(piece int) (len_ pp.Integer) {
 	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 @@ func (t *torrent) hashPiece(piece int) (ret pieceSum) {
 	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 @@ func (me *torrent) haveAnyPieces() bool {
 	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 @@ func chunkIndex(cs chunkSpec, chunkSize pp.Integer) int {
 }
 
 // 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 @@ func (t *torrent) wantChunk(r request) bool {
 }
 
 // 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 @@ func (t *torrent) wantPiece(index int) bool {
 	})
 }
 
-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 @@ func (t *torrent) forNeededPieces(f func(piece int) (more bool)) (all bool) {
 	})
 }
 
-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 @@ type PieceStateChange struct {
 	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) publishPieceChange(piece int) {
 	}
 }
 
-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 @@ func (t *torrent) forUrgentPieces(f func(piece int) (again bool)) (all bool) {
 	})
 }
 
-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 @@ func (t *torrent) piecePriorityChanged(piece int) {
 	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 @@ func (t *torrent) updatePiecePriority(piece int) bool {
 
 // 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) updatePiecePriorities() {
 	}
 }
 
-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 @@ func (t *torrent) byteRegionPieces(off, size int64) (begin, end int) {
 }
 
 // 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 @@ func (t *torrent) forReaderOffsetPieces(f func(begin, end int) (more bool)) (all
 	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 @@ func (t *torrent) piecePriorityUncached(piece int) (ret piecePriority) {
 	return
 }
 
-func (t *torrent) pendPiece(piece int) {
+func (t *Torrent) pendPiece(piece int) {
 	if t.pendingPieces.Contains(piece) {
 		return
 	}
@@ -966,28 +966,28 @@ func (t *torrent) pendPiece(piece int) {
 	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 @@ func (t *torrent) connRequestPiecePendingChunks(c *connection, piece int) (more
 	})
 }
 
-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 @@ func (t *torrent) getConnPieceInclination() []int {
 	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 @@ func (t *torrent) updatePieceCompletion(piece int) {
 }
 
 // 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 bca6f775..6ffc8605 100644
--- a/torrent_test.go
+++ b/torrent_test.go
@@ -52,8 +52,8 @@ func TestAppendToCopySlice(t *testing.T) {
 }
 
 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 0f7aa3aa..7b9c5a91 100644
--- a/worst_conns.go
+++ b/worst_conns.go
@@ -7,7 +7,7 @@ import (
 // Implements heap functions such that [0] is the worst connection.
 type worstConns struct {
 	c  []*connection
-	t  *torrent
+	t  *Torrent
 	cl *Client
 }