func (t *Torrent) setMetadata(md *metainfo.Info, infoBytes []byte) (err error) {
	err = validateInfo(md)
	if err != nil {
		err = fmt.Errorf("bad info: %s", err)
		return
	}
	t.info = &metainfo.InfoEx{
		Info:  *md,
		Bytes: infoBytes,
		Hash:  &t.infoHash,
	}
	t.storage, err = t.storageOpener.OpenTorrent(t.info)
	if err != nil {
		return
	}
	t.length = 0
	for _, f := range t.info.UpvertedFiles() {
		t.length += f.Length
	}
	t.metadataBytes = infoBytes
	t.metadataCompletedChunks = nil
	hashes := infoPieceHashes(md)
	t.pieces = make([]piece, len(hashes))
	for i, hash := range hashes {
		piece := &t.pieces[i]
		piece.t = t
		piece.index = i
		piece.noPendingWrites.L = &piece.pendingWritesMutex
		missinggo.CopyExact(piece.Hash[:], hash)
	}
	for _, conn := range t.conns {
		if err := conn.setNumPieces(t.numPieces()); err != nil {
			log.Printf("closing connection: %s", err)
			conn.Close()
		}
	}
	for i := range t.pieces {
		t.updatePieceCompletion(i)
		t.pieces[i].QueuedForHash = true
	}
	go func() {
		for i := range t.pieces {
			t.verifyPiece(i)
		}
	}()
	return
}
Called when metadata for a torrent becomes available.
package torrent
import (
	"container/heap"
	"expvar"
	"fmt"
	"io"
	"log"
	"math"
	"math/rand"
	"net"
	"sort"
	"sync"
	"time"
	"github.com/anacrolix/missinggo"
	"github.com/anacrolix/missinggo/bitmap"
	"github.com/anacrolix/missinggo/itertools"
	"github.com/anacrolix/missinggo/perf"
	"github.com/anacrolix/missinggo/pubsub"
	"github.com/bradfitz/iter"
	"github.com/anacrolix/torrent/bencode"
	"github.com/anacrolix/torrent/metainfo"
	pp "github.com/anacrolix/torrent/peer_protocol"
	"github.com/anacrolix/torrent/storage"
)
func (t *Torrent) chunkIndexSpec(chunkIndex, piece int) chunkSpec {
	return chunkIndexSpec(chunkIndex, t.pieceLength(piece), t.chunkSize)
}
type peersKey struct {
	IPBytes string
	Port    int
}
type Torrent struct {
	cl *Client
	closing chan struct{}
	ceasingNetworking chan struct{}
	infoHash metainfo.Hash
	pieces   []piece
	pieceStateChanges *pubsub.PubSub
	chunkSize         pp.Integer
	length int64
	storageOpener storage.I
	storage storage.Torrent
	info *metainfo.InfoEx
	conns []*connection
	halfOpen map[string]struct{}
	peers     map[peersKey]Peer
	wantPeers sync.Cond
	trackers []trackerTier
	displayName string
	metadataBytes []byte
	metadataCompletedChunks []bool
	gotMetainfo chan struct{}
	readers map[*Reader]struct{}
	pendingPieces   bitmap.Bitmap
	completedPieces bitmap.Bitmap
	connPieceInclinationPool sync.Pool
}
var (
	pieceInclinationsReused = expvar.NewInt("pieceInclinationsReused")
	pieceInclinationsNew    = expvar.NewInt("pieceInclinationsNew")
	pieceInclinationsPut    = expvar.NewInt("pieceInclinationsPut")
)
func (t *Torrent) setDisplayName(dn string) {
	t.displayName = dn
}
func (t *Torrent) pieceComplete(piece int) bool {
	return t.completedPieces.Get(piece)
}
func (t *Torrent) pieceCompleteUncached(piece int) bool {
	return t.pieces[piece].Storage().GetIsComplete()
}
func (t *Torrent) numConnsUnchoked() (num int) {
	for _, c := range t.conns {
		if !c.PeerChoked {
			num++
		}
	}
	return
}
func (t *Torrent) addrActive(addr string) bool {
	if _, ok := t.halfOpen[addr]; ok {
		return true
	}
	for _, c := range t.conns {
		if c.remoteAddr().String() == addr {
			return true
		}
	}
	return false
}
func (t *Torrent) worstConns(cl *Client) (wcs *worstConns) {
	wcs = &worstConns{
		c:  make([]*connection, 0, len(t.conns)),
		t:  t,
		cl: cl,
	}
	for _, c := range t.conns {
		if !c.closed.IsSet() {
			wcs.c = append(wcs.c, c)
		}
	}
	return
}
func (t *Torrent) ceaseNetworking() {
	select {
	case <-t.ceasingNetworking:
		return
	default:
	}
	close(t.ceasingNetworking)
	for _, c := range t.conns {
		c.Close()
	}
}
func (t *Torrent) addPeer(p Peer, cl *Client) {
	cl.openNewConns(t)
	if len(t.peers) >= torrentPeersHighWater {
		return
	}
	key := peersKey{string(p.IP), p.Port}
	if _, ok := t.peers[key]; ok {
		return
	}
	t.peers[key] = p
	peersAddedBySource.Add(string(p.Source), 1)
	cl.openNewConns(t)
}
func (t *Torrent) invalidateMetadata() {
	t.metadataBytes = nil
	t.metadataCompletedChunks = nil
	t.info = nil
}
func (t *Torrent) saveMetadataPiece(index int, data []byte) {
	if t.haveInfo() {
		return
	}
	if index >= len(t.metadataCompletedChunks) {
		log.Printf("%s: ignoring metadata piece %d", t, index)
		return
	}
	copy(t.metadataBytes[(1<<14)*index:], data)
	t.metadataCompletedChunks[index] = true
}
func (t *Torrent) metadataPieceCount() int {
	return (len(t.metadataBytes) + (1 << 14) - 1) / (1 << 14)
}
func (t *Torrent) haveMetadataPiece(piece int) bool {
	if t.haveInfo() {
		return (1<<14)*piece < len(t.metadataBytes)
	} else {
		return piece < len(t.metadataCompletedChunks) && t.metadataCompletedChunks[piece]
	}
}
func (t *Torrent) metadataSizeKnown() bool {
	return t.metadataBytes != nil
}
func (t *Torrent) metadataSize() int {
	return len(t.metadataBytes)
}
func infoPieceHashes(info *metainfo.Info) (ret []string) {
	for i := 0; i < len(info.Pieces); i += 20 {
		ret = append(ret, string(info.Pieces[i:i+20]))
	}
	return
}