client.go | 6 +++--- client_test.go | 10 +++++++++- mmap_span/mmap_span.go | 9 +++++++-- storage/file.go | 12 ++++++++++++ storage/file_test.go | 6 ++++-- storage/interface.go | 26 ++++++++------------------ storage/mmap.go | 41 +++++++++++++++++++---------------------- torrent.go | 7 ++++++- diff --git a/client.go b/client.go index 7f0d54845a38e671d135f092d789bcb714a01c5d..d470ceac6acd3e08c5d909e0a6918c5c8c804107 100644 --- a/client.go +++ b/client.go @@ -1949,9 +1949,9 @@ t.wantPeers.L = &cl.mu if spec.ChunkSize != 0 { t.chunkSize = pp.Integer(spec.ChunkSize) } - t.storage = spec.Storage - if t.storage == nil { - t.storage = cl.defaultStorage + t.storageOpener = spec.Storage + if t.storageOpener == nil { + t.storageOpener = cl.defaultStorage } } if spec.DisplayName != "" { diff --git a/client_test.go b/client_test.go index 6a33f3b8c4be6af125b5b333b3521d63a69c75f7..faded767ef37fddce923be879c8a7e16db9c2fb5 100644 --- a/client_test.go +++ b/client_test.go @@ -92,7 +92,7 @@ missinggo.CopyExact(ih[:], mi.Info.Hash) return }()) tor.chunkSize = 2 - tor.storage = storage.NewFile(dir) + tor.storageOpener = storage.NewFile(dir) // Needed to lock for asynchronous piece verification. tor.cl = new(Client) err := tor.setMetadata(&mi.Info.Info, mi.Info.Bytes) @@ -463,6 +463,14 @@ assert.EqualValues(t, T.torrent.Trackers[1][0], "udp://b") } type badStorage struct{} + +func (me badStorage) OpenTorrent(*metainfo.InfoEx) (storage.Torrent, error) { + return me, nil +} + +func (me badStorage) Close() error { + return nil +} func (me badStorage) Piece(p metainfo.Piece) storage.Piece { return badStoragePiece{p} diff --git a/mmap_span/mmap_span.go b/mmap_span/mmap_span.go index 43e5458f0db07b7c762a592f8786a9044d78e8ab..58d86732f432d8a5324a4f08e37c26162f8ea836 100644 --- a/mmap_span/mmap_span.go +++ b/mmap_span/mmap_span.go @@ -2,6 +2,7 @@ package mmap_span import ( "io" + "log" "github.com/edsrzf/mmap-go" ) @@ -22,10 +23,14 @@ func (me *MMapSpan) Append(mmap mmap.MMap) { me.span = append(me.span, segment{&mmap}) } -func (me MMapSpan) Close() { +func (me MMapSpan) Close() error { for _, mMap := range me.span { - mMap.(segment).Unmap() + err := mMap.(segment).Unmap() + if err != nil { + log.Print(err) + } } + return nil } func (me MMapSpan) Size() (ret int64) { diff --git a/storage/file.go b/storage/file.go index 7f9d61b363ad0184dcb253319f7da66dc75357e2..10df26376a8820e119655048823d0fc9e95006ea 100644 --- a/storage/file.go +++ b/storage/file.go @@ -21,6 +21,14 @@ baseDir: baseDir, } } +func (me *fileStorage) OpenTorrent(info *metainfo.InfoEx) (Torrent, error) { + return fileTorrentStorage{me}, nil +} + +type fileTorrentStorage struct { + *fileStorage +} + func (me *fileStorage) Piece(p metainfo.Piece) Piece { _io := &fileStorageTorrent{ p.Info, @@ -32,6 +40,10 @@ p, missinggo.NewSectionWriter(_io, p.Offset(), p.Length()), io.NewSectionReader(_io, p.Offset(), p.Length()), } +} + +func (me *fileStorage) Close() error { + return nil } type fileStoragePiece struct { diff --git a/storage/file_test.go b/storage/file_test.go index 0297c389eaae839a328749332f5e4e9edbc1425f..6e1d055e7d65c13a6e9fa32078b8a2f1c6d839b4 100644 --- a/storage/file_test.go +++ b/storage/file_test.go @@ -19,7 +19,7 @@ func TestShortFile(t *testing.T) { td, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(td) - data := NewFile(td) + s := NewFile(td) info := &metainfo.InfoEx{ Info: metainfo.Info{ Name: "a", @@ -27,12 +27,14 @@ Length: 2, PieceLength: missinggo.MiB, }, } + ts, err := s.OpenTorrent(info) + assert.NoError(t, err) f, err := os.Create(filepath.Join(td, "a")) err = f.Truncate(1) f.Close() var buf bytes.Buffer p := info.Piece(0) - n, err := io.Copy(&buf, io.NewSectionReader(data.Piece(p), 0, p.Length())) + n, err := io.Copy(&buf, io.NewSectionReader(ts.Piece(p), 0, p.Length())) assert.EqualValues(t, 1, n) assert.Equal(t, io.ErrUnexpectedEOF, err) } diff --git a/storage/interface.go b/storage/interface.go index 0662ebd261fea64e1eb02d9068cd9d076c195708..71b34abd0f076ed5da6237baa0b6ddc95da57d13 100644 --- a/storage/interface.go +++ b/storage/interface.go @@ -6,11 +6,18 @@ "github.com/anacrolix/torrent/metainfo" ) -// Represents data storage for a Torrent. +// Represents data storage for an unspecified torrent. type I interface { + OpenTorrent(info *metainfo.InfoEx) (Torrent, error) +} + +// Data storage bound to a torrent. +type Torrent interface { Piece(metainfo.Piece) Piece + Close() error } +// Interacts with torrent piece data. type Piece interface { // Should return io.EOF only at end of torrent. Short reads due to missing // data should return io.ErrUnexpectedEOF. @@ -23,20 +30,3 @@ MarkComplete() error // Returns true if the piece is complete. GetIsComplete() bool } - -// type PieceStorage interface { -// ReadAt(metainfo.Piece, []byte, int64) (int, error) -// WriteAt(metainfo.Piece, []byte, int64) (int, error) -// MarkComplete(metainfo.Piece) error -// GetIsComplete(metainfo.Piece) bool -// } - -// type wrappedPieceStorage struct { -// ps PieceStorage -// } - -// func WrapPieceStorage(ps PieceStorage) I { -// return wrappedPieceStorage{ps} -// } - -// func (me wrappedPieceStorage) Piece(metainfo.Piece) diff --git a/storage/mmap.go b/storage/mmap.go index e24c9c69ea1a063e3999259c105c06b2fbf329fd..07faa191239c1155d658eb6c60a1bddc109890a7 100644 --- a/storage/mmap.go +++ b/storage/mmap.go @@ -14,9 +14,7 @@ "github.com/anacrolix/torrent/mmap_span" ) type mmapStorage struct { - baseDir string - spans map[metainfo.InfoHash]mmap_span.MMapSpan - completed map[metainfo.InfoHash]bool + baseDir string } func NewMMap(baseDir string) I { @@ -25,36 +23,35 @@ baseDir: baseDir, } } -func (me *mmapStorage) lazySpan(info *metainfo.InfoEx) error { - if me.spans == nil { - me.spans = make(map[metainfo.InfoHash]mmap_span.MMapSpan) - } - if _, ok := me.spans[*info.Hash]; ok { - return nil - } +func (me *mmapStorage) OpenTorrent(info *metainfo.InfoEx) (t Torrent, err error) { span, err := MMapTorrent(&info.Info, me.baseDir) - if err != nil { - return err + t = &mmapTorrentStorage{ + span: span, } - me.spans[*info.Hash] = span - return nil + return +} + +type mmapTorrentStorage struct { + span mmap_span.MMapSpan + completed map[metainfo.InfoHash]bool } -func (me *mmapStorage) Piece(p metainfo.Piece) Piece { - err := me.lazySpan(p.Info) - if err != nil { - panic(err) - } +func (me *mmapTorrentStorage) Piece(p metainfo.Piece) Piece { return mmapStoragePiece{ storage: me, p: p, - ReaderAt: io.NewSectionReader(me.spans[*p.Info.Hash], p.Offset(), p.Length()), - WriterAt: missinggo.NewSectionWriter(me.spans[*p.Info.Hash], p.Offset(), p.Length()), + ReaderAt: io.NewSectionReader(me.span, p.Offset(), p.Length()), + WriterAt: missinggo.NewSectionWriter(me.span, p.Offset(), p.Length()), } } +func (me *mmapTorrentStorage) Close() error { + me.span.Close() + return nil +} + type mmapStoragePiece struct { - storage *mmapStorage + storage *mmapTorrentStorage p metainfo.Piece io.ReaderAt io.WriterAt diff --git a/torrent.go b/torrent.go index 228135c3a03b2ae577a0c31feb70e882e3305a46..a7aa57e0d65cc88e22244cd4e0da5448b4be29d0 100644 --- a/torrent.go +++ b/torrent.go @@ -54,7 +54,8 @@ // Total length of the torrent in bytes. Stored because it's not O(1) to // get this from the info dict. length int64 - storage storage.I + storageOpener storage.I + storage storage.Torrent // The info dict. Nil if we don't have it (yet). Info *metainfo.InfoEx @@ -229,6 +230,10 @@ 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() {