Skip to content

Commit 3ccd320

Browse files
committed
feat: saving functionality now provided by file
1 parent 002e081 commit 3ccd320

File tree

6 files changed

+148
-40
lines changed

6 files changed

+148
-40
lines changed

cmd/mtor/main.go

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66
"time"
77

8+
"github.com/raklaptudirm/mtor/internal/build"
89
"github.com/raklaptudirm/mtor/pkg/file"
910
"github.com/raklaptudirm/mtor/pkg/torrent"
1011
)
@@ -43,51 +44,22 @@ func main() {
4344

4445
fmt.Printf("torrent %x - %d pieces\n", t.InfoHash, len(t.PieceHashes))
4546

46-
ps := Pieces{
47-
size: t.PieceLength,
48-
buffer: make([]byte, t.Length),
49-
}
50-
err = t.Download(&ps, config)
47+
ps := build.PieceManager
48+
err = ps.Init()
5149
if err != nil {
5250
fmt.Println(err)
53-
return
5451
}
52+
defer ps.Close()
5553

56-
w, err := os.Create(string(f.Info.Name))
54+
err = t.DownloadPieces(ps, config)
5755
if err != nil {
5856
fmt.Println(err)
5957
return
6058
}
61-
defer w.Close()
62-
63-
for i := range t.PieceHashes {
64-
block, err := ps.Get(i)
65-
if err != nil {
66-
fmt.Println(err)
67-
return
68-
}
6959

70-
_, err = w.Write(block)
71-
if err != nil {
72-
fmt.Println(err)
73-
return
74-
}
60+
err = f.Save(ps, ".") // save in cwd
61+
if err != nil {
62+
fmt.Println(err)
63+
return
7564
}
7665
}
77-
78-
// structure to put stuff together
79-
type Pieces struct {
80-
size int
81-
length int
82-
buffer []byte
83-
}
84-
85-
func (p *Pieces) Put(index int, block []byte) error {
86-
p.length++
87-
copy(p.buffer[index*p.size:], block)
88-
return nil
89-
}
90-
91-
func (p *Pieces) Get(index int) ([]byte, error) {
92-
return p.buffer[index*p.size : (index+1)*p.size], nil
93-
}

internal/build/env.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package build
2+
3+
import "github.com/raklaptudirm/mtor/internal/manager"
4+
5+
var PieceManager = manager.New()

internal/manager/piece.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright © 2021 Rak Laptudirm <raklaptudirm@gmail.com>
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package manager
15+
16+
import (
17+
"errors"
18+
"fmt"
19+
"os"
20+
"path"
21+
)
22+
23+
// piece represents the piece manager.
24+
type piece struct {
25+
src string // storage directory
26+
}
27+
28+
// ErrManagerClosed is returned when the manager is not initialized,
29+
// or closed.
30+
var ErrManagerClosed = errors.New("the manager is closed")
31+
32+
// Init initializes the manager.
33+
func (p *piece) Init() error {
34+
home, err := os.UserHomeDir()
35+
if err != nil {
36+
return err
37+
}
38+
39+
// create storage directory
40+
dir, err := os.MkdirTemp(home, "mtor pieces ")
41+
if err != nil {
42+
return err
43+
}
44+
45+
p.src = dir
46+
return nil
47+
}
48+
49+
// Put stores a piece in the manager.
50+
func (p *piece) Put(i int, buf []byte) error {
51+
if p.isClosed() {
52+
return ErrManagerClosed
53+
}
54+
55+
file := path.Join(p.src, fmt.Sprintf("%x", i))
56+
return os.WriteFile(file, buf, 0600)
57+
}
58+
59+
// Get fetches a piece from the manager.
60+
func (p *piece) Get(i int) ([]byte, error) {
61+
if p.isClosed() {
62+
return nil, ErrManagerClosed
63+
}
64+
65+
file := path.Join(p.src, fmt.Sprintf("%x", i))
66+
return os.ReadFile(file)
67+
}
68+
69+
// Close closes the manager.
70+
func (p *piece) Close() error {
71+
if p.isClosed() {
72+
return ErrManagerClosed
73+
}
74+
75+
// free space
76+
return os.RemoveAll(p.src)
77+
}
78+
79+
// isClosed checks if the manager is closed.
80+
func (p *piece) isClosed() bool {
81+
return p.src == ""
82+
}
83+
84+
// New returns a new and un-initialzed instance of the manager.
85+
func New() *piece {
86+
return &piece{}
87+
}

pkg/file/file.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"fmt"
2020
"io"
2121
"math/rand"
22+
"os"
23+
"path"
2224
"time"
2325

2426
"github.com/jackpal/bencode-go"
@@ -59,6 +61,44 @@ type singleFile struct {
5961
Path []string `bencode:"path"` // path of the file
6062
}
6163

64+
// Save saves the torrent as a file or directory, fetching pieces from the
65+
// provided piece manager.
66+
func (f *file) Save(pieces torrent.PieceManager, dst string) error {
67+
if f.isSingleFile() {
68+
return f.saveSingleFile(pieces, dst)
69+
}
70+
71+
// TODO: add support for saving multifile torrents
72+
return nil
73+
}
74+
75+
// saveSingleFile saves a single-file torrent as a file, fetching the pieces
76+
// from the provided piece manager.
77+
func (f *file) saveSingleFile(pieces torrent.PieceManager, dst string) error {
78+
length := len(f.Info.Pieces) / 20 // each hash is 20 bytes
79+
80+
file, err := os.Create(path.Join(dst, f.Info.Name))
81+
if err != nil {
82+
return err
83+
}
84+
defer file.Close()
85+
86+
// get each piece
87+
for i := 0; i < length; i++ {
88+
piece, err := pieces.Get(i)
89+
if err != nil {
90+
return err
91+
}
92+
93+
// write each piece to the file
94+
_, err = file.Write(piece)
95+
if err != nil {
96+
return err
97+
}
98+
}
99+
return nil
100+
}
101+
62102
// Torrent converts a file into a torrent.Torrent.
63103
func (f *file) Torrent() (*torrent.Torrent, error) {
64104
hash, err := f.Info.hash()

pkg/torrent/download.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,9 @@ func (t *Torrent) pieceLen(index int) int {
286286
return t.PieceLength
287287
}
288288

289-
// Download downloads the t torrent and stores the downloaded pieces into
290-
// the provided PieceManager.
291-
func (t *Torrent) Download(p PieceManager, c *DownloadConfig) error {
289+
// DownloadPieces downloads the pieces of the provided torrent and stores
290+
// them into the provided PieceManager.
291+
func (t *Torrent) DownloadPieces(p PieceManager, c *DownloadConfig) error {
292292
start := time.Now()
293293

294294
err := t.newDownload(p, c).start()

pkg/torrent/piece.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,12 @@ func (p *pieceProgress) readMessage() error {
8787
// PieceManager represents an interface which can handle the storage of the
8888
// torrent's pieces.
8989
type PieceManager interface {
90+
// Init initializes the manager to start storing pieces.
91+
Init() error
9092
// Put stores the buffer data with the provided piece index.
9193
Put(int, []byte) error
9294
// Get gets the data of the provided piece index.
9395
Get(int) ([]byte, error)
96+
// Close destroy's the manager's data. Call this when done.
97+
Close() error
9498
}

0 commit comments

Comments
 (0)