)
// 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
// 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
event sync.Cond
closed missinggo.Event
- torrents map[metainfo.InfoHash]*torrent
+ torrents map[metainfo.InfoHash]*Torrent
}
func (me *Client) IPBlockList() iplist.Ranger {
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)
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
}
// 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]
}
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 {
// 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
}
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)
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
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")
}
// 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
// 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
// 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()
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()
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.
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},
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,
}
}
-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)
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
}
return true
}
-func (cl *Client) requestPendingMetadata(t *torrent, c *connection) {
+func (cl *Client) requestPendingMetadata(t *Torrent, c *connection) {
if t.haveInfo() {
return
}
}
}
-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
}
// 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 {
return
}
-func (me *Client) upload(t *torrent, c *connection) {
+func (me *Client) upload(t *Torrent, c *connection) {
if me.config.NoUpload {
return
}
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))
// 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,
}
// 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
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) {
}
// 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
}
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
return
}
-func (t *torrent) needData() bool {
+func (t *Torrent) needData() bool {
if !t.haveInfo() {
return true
}
})
}
-func (cl *Client) usefulConn(t *torrent, c *connection) bool {
+func (cl *Client) usefulConn(t *Torrent, c *connection) bool {
if c.closed.IsSet() {
return false
}
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
}
return t.worstBadConn(me) != nil
}
-func (me *Client) openNewConns(t *torrent) {
+func (me *Client) openNewConns(t *Torrent) {
select {
case <-t.ceasingNetworking:
return
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(),
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 {
}
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
// 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),
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) {
}
// Don't call this before the info is available.
-func (t *torrent) bytesCompleted() int64 {
+func (t *Torrent) bytesCompleted() int64 {
if !t.haveInfo() {
return 0
}
// 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()
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
// 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()
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
}
// 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 {
}
// 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
}
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
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)
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 {
}
// 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
}
// 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)))
// 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)
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
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 {
}
}
-func (me *Client) onFailedPiece(t *torrent, piece int) {
+func (me *Client) onFailedPiece(t *Torrent, piece int) {
if t.pieceAllDirty(piece) {
t.pendAllChunkSpecs(piece)
}
}
}
-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 {
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]
}
// 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
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)
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
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{}
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())
}
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))
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 {
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 {
return
}
-func torrentBar(t torrent.Torrent) {
+func torrentBar(t *torrent.Torrent) {
bar := uiprogress.AddBar(1)
bar.AppendCompleted()
bar.AppendFunc(func(*uiprogress.Bar) (ret string) {
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 {
// 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
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 <pre> 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",
func Example_fileReader() {
var (
- t torrent.Torrent
+ t *torrent.Torrent
f torrent.File
)
r := t.NewReader()
// 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
}
// 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++ {
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
// 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) {
}
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() {
path string
metadata *metainfo.InfoEx
FS *TorrentFS
- t torrent.Torrent
+ t *torrent.Torrent
}
type fileNode struct {
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()
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)
// 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 {
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 {
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.
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
}
func (r *Reader) tickleClient() {
- r.t.torrent.readersChanged()
+ r.t.readersChanged()
}
func (r *Reader) waitReadable(off int64) {
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
// 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
// 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
}
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()
}
}
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) {
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")
}
"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)
// 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
}
// 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)
}
"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)
}
}
// Maintains state of torrent within a Client.
-type torrent struct {
+type Torrent struct {
cl *Client
closing chan 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
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++
}
// 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
}
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,
return
}
-func (t *torrent) ceaseNetworking() {
+func (t *Torrent) ceaseNetworking() {
select {
case <-t.ceasingNetworking:
return
}
}
-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
}
-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
}
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 {
}
}
-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)
}
}
// 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)
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 {
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
}
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
// 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) {
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,
}
}
-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),
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: ")
}
}
-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")
}
}
}
-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())
}
}
// 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 {
}
}
-func (t *torrent) piecePartiallyDownloaded(piece int) bool {
+func (t *Torrent) piecePartiallyDownloaded(piece int) bool {
if t.pieceComplete(piece) {
return false
}
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
}
}
-func (t *torrent) close() (err error) {
+func (t *Torrent) close() (err error) {
if t.isClosed() {
return
}
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)
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
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
}
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 {
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()
}
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
}
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()
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
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)
// }()
}
// 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
}
}
// 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
}
})
}
-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) {
})
}
-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 {
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 {
}
}
-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) {
})
}
-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)
}
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 {
// 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
}
}
-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
}
}
// 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 {
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
return
}
-func (t *torrent) pendPiece(piece int) {
+func (t *Torrent) pendPiece(piece int) {
if t.pendingPieces.Contains(piece) {
return
}
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
}
})
}
-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)
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)
}
// 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)
}
}
func TestTorrentString(t *testing.T) {
- tor := &torrent{}
- s := tor.InfoHash.HexString()
+ tor := &Torrent{}
+ s := tor.InfoHash().HexString()
if s != "0000000000000000000000000000000000000000" {
t.FailNow()
}
// Implements heap functions such that [0] is the worst connection.
type worstConns struct {
c []*connection
- t *torrent
+ t *Torrent
cl *Client
}