Toward fixing https://github.com/anacrolix/torrent/issues/50.
}
func (cl *Client) pieceHashed(t *Torrent, piece int, correct bool) {
+ if t.closed.IsSet() {
+ return
+ }
p := &t.pieces[piece]
if p.EverHashed {
// Don't score the first time a piece is hashed, it could be an
missinggo.CopyExact(&ret, p.Info.Pieces[p.i*20:(p.i+1)*20])
return
}
+
+func (p Piece) Index() int {
+ return p.i
+}
+
+func (p Piece) Key() PieceKey {
+ return PieceKey{p.Info.Hash(), p.i}
+}
+
+type PieceKey struct {
+ Hash Hash
+ Index int
+}
--- /dev/null
+package storage
+
+import "log"
+
+func pieceCompletionForDir(dir string) (ret pieceCompletion) {
+ ret, err := newDBPieceCompletion(dir)
+ if err != nil {
+ log.Printf("couldn't open piece completion db in %q: %s", dir, err)
+ ret = new(mapPieceCompletion)
+ }
+ return
+}
--- /dev/null
+package storage
+
+import (
+ "github.com/anacrolix/torrent/metainfo"
+)
+
+type mapPieceCompletion struct {
+ m map[metainfo.PieceKey]struct{}
+}
+
+func (mapPieceCompletion) Close() {}
+
+func (me *mapPieceCompletion) Get(p metainfo.Piece) bool {
+ _, ok := me.m[p.Key()]
+ return ok
+}
+
+func (me *mapPieceCompletion) Set(p metainfo.Piece, b bool) {
+ if b {
+ if me.m == nil {
+ me.m = make(map[metainfo.PieceKey]struct{})
+ }
+ me.m[p.Key()] = struct{}{}
+ } else {
+ delete(me.m, p.Key())
+ }
+}
--- /dev/null
+package storage
+
+import (
+ "database/sql"
+ "log"
+ "path/filepath"
+
+ "github.com/anacrolix/torrent/metainfo"
+)
+
+type dbPieceCompletion struct {
+ db *sql.DB
+}
+
+func newDBPieceCompletion(dir string) (ret *dbPieceCompletion, err error) {
+ p := filepath.Join(dir, ".torrent.db")
+ log.Println(p)
+ db, err := sql.Open("sqlite3", p)
+ if err != nil {
+ return
+ }
+ _, err = db.Exec(`create table if not exists completed(infohash, "index", unique(infohash, "index") on conflict ignore)`)
+ if err != nil {
+ db.Close()
+ return
+ }
+ ret = &dbPieceCompletion{db}
+ return
+}
+
+func (me *dbPieceCompletion) Get(p metainfo.Piece) (ret bool) {
+ row := me.db.QueryRow(`select exists(select * from completed where infohash=? and "index"=?)`, p.Info.Hash().HexString(), p.Index())
+ err := row.Scan(&ret)
+ if err != nil {
+ panic(err)
+ }
+ return
+}
+
+func (me *dbPieceCompletion) Set(p metainfo.Piece, b bool) {
+ var err error
+ if b {
+ _, err = me.db.Exec(`insert into completed (infohash, "index") values (?, ?)`, p.Info.Hash().HexString(), p.Index())
+ } else {
+ _, err = me.db.Exec(`delete from completed where infohash=? and "index"=?`, p.Info.Hash().HexString(), p.Index())
+ }
+ if err != nil {
+ panic(err)
+ }
+}
+
+func (me *dbPieceCompletion) Close() {
+ me.db.Close()
+}
"path/filepath"
"github.com/anacrolix/missinggo"
+ _ "github.com/mattn/go-sqlite3"
"github.com/anacrolix/torrent/metainfo"
)
+type pieceCompletion interface {
+ Get(metainfo.Piece) bool
+ Set(metainfo.Piece, bool)
+ Close()
+}
+
// File-based storage for torrents, that isn't yet bound to a particular
// torrent.
type fileStorage struct {
- baseDir string
- completed map[[20]byte]bool
+ baseDir string
+ completion pieceCompletion
}
func NewFile(baseDir string) Client {
return &fileStorage{
- baseDir: baseDir,
+ baseDir: baseDir,
+ completion: pieceCompletionForDir(baseDir),
}
}
}
func (fs *fileStorage) Close() error {
+ fs.completion.Close()
return nil
}
}
func (fs *fileStoragePiece) GetIsComplete() bool {
- return fs.completed[fs.p.Hash()]
+ return fs.completion.Get(fs.p)
}
func (fs *fileStoragePiece) MarkComplete() error {
- if fs.completed == nil {
- fs.completed = make(map[[20]byte]bool)
- }
- fs.completed[fs.p.Hash()] = true
+ fs.completion.Set(fs.p, true)
return nil
}