aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/mybittorrent/decoder.go140
-rw-r--r--cmd/mybittorrent/file.go74
-rw-r--r--cmd/mybittorrent/main.go22
3 files changed, 14 insertions, 222 deletions
diff --git a/cmd/mybittorrent/decoder.go b/cmd/mybittorrent/decoder.go
deleted file mode 100644
index 641b042..0000000
--- a/cmd/mybittorrent/decoder.go
+++ /dev/null
@@ -1,140 +0,0 @@
-package main
-
-import (
- "fmt"
- "strconv"
- "strings"
- "unicode"
-)
-
-type Decoder struct {
- raw string
- decoderFuncs []DecoderFunc
-}
-
-type DecoderFunc func(string) (any, int, error)
-
-func NewDecoder(raw string) *Decoder {
- d := &Decoder{raw: raw}
- return d.WithDecoderFunc(d.decodeSlice).WithDecoderFunc(d.decodeMap).WithDecoderFunc(d.decodeString).WithDecoderFunc(d.decodeInteger)
-}
-
-func (d *Decoder) WithDecoderFunc(f DecoderFunc) *Decoder {
- d.decoderFuncs = append(d.decoderFuncs, f)
- return d
-}
-
-func (d *Decoder) Decode() (any, error) {
- for _, f := range d.decoderFuncs {
- if r, l, err := f(d.raw); l > 0 || err != nil {
- return r, nil
- }
- }
-
- return nil, fmt.Errorf("failed to decode value: %s", d.raw)
-}
-
-func (d *Decoder) decodeString(chunk string) (any, int, error) {
- if !unicode.IsDigit(rune(chunk[0])) {
- return "", 0, nil
- }
-
- var firstColonIndex int
-
- for i := 0; i < len(chunk); i++ {
- if chunk[i] == ':' {
- firstColonIndex = i
- break
- }
- }
-
- lengthStr := chunk[:firstColonIndex]
-
- length, err := strconv.Atoi(lengthStr)
- if err != nil {
- return "", 0, err
- }
-
- val := chunk[firstColonIndex+1 : firstColonIndex+1+length]
-
- return val, length + 1 + len(lengthStr), nil
-}
-
-func (d *Decoder) decodeInteger(chunk string) (any, int, error) {
- if rune(chunk[0]) != 'i' {
- return 0, 0, nil
- }
-
- num, err := strconv.Atoi(chunk[1:strings.IndexByte(chunk, 'e')])
- if err != nil {
- return 0, 0, err
- }
-
- return num, len(strconv.Itoa(num)) + 2, nil
-}
-
-func (d *Decoder) decodeSlice(chunk string) (any, int, error) {
- if rune(chunk[0]) != 'l' {
- return nil, 0, nil
- }
-
- if rune(chunk[1]) == 'e' {
- return []any{}, 2, nil
- }
-
- result := make([]any, 0, 32)
- offset := 1
-
- for rune(chunk[offset]) != 'e' {
- for _, f := range d.decoderFuncs {
- if v, l, err := f(chunk[offset:]); l > 0 {
- if err != nil {
- return nil, 0, err
- }
- result = append(result, v)
- offset += l
- break
- }
- }
- }
-
- return result, offset + 1, nil
-}
-
-func (d *Decoder) decodeMap(chunk string) (any, int, error) {
- if rune(chunk[0]) != 'd' {
- return nil, 0, nil
- }
-
- if rune(chunk[1]) == 'e' {
- return map[string]any{}, 2, nil
- }
-
- result := make(map[string]any, 32)
- offset := 1
-
- for rune(chunk[offset]) != 'e' {
- key, keyLen, err := d.decodeString(chunk[offset:])
- if err != nil {
- return nil, 0, err
- }
-
- if keyLen == 0 {
- return nil, 0, fmt.Errorf("invalid bencoded map key length")
- }
-
- for _, f := range d.decoderFuncs {
- if v, l, err := f(chunk[offset+keyLen:]); l > 0 {
- if err != nil {
- return nil, 0, err
- }
-
- result[key.(string)] = v
- offset += keyLen + l
- break
- }
- }
- }
-
- return result, offset + 1, nil
-}
diff --git a/cmd/mybittorrent/file.go b/cmd/mybittorrent/file.go
index 2302976..498ea3f 100644
--- a/cmd/mybittorrent/file.go
+++ b/cmd/mybittorrent/file.go
@@ -1,12 +1,6 @@
package main
-import (
- "bytes"
- "fmt"
- "io"
-)
-
-type File struct {
+type Meta struct {
Announce string `bencode:"announce"`
Info FileInfo `bencode:"info"`
}
@@ -17,69 +11,3 @@ type FileInfo struct {
PieceLength int `bencode:"piece length"`
Pieces string `bencode:"pieces"`
}
-
-func NewFile() *File {
- return &File{}
-}
-
-func (f *File) ReadFrom(r io.ReadCloser) error {
- buf := new(bytes.Buffer)
-
- _, err := buf.ReadFrom(r)
- if err != nil {
- return err
- }
-
- defer func(r io.ReadCloser) {
- err := r.Close()
- if err != nil {
- fmt.Printf("failed to close: %v+\n", err)
- }
- }(r)
-
- if err != nil {
- return err
- }
-
- decoded, err := NewDecoder(buf.String()).Decode()
- if err != nil {
- return err
- }
-
- content, ok := decoded.(map[string]any)
- if !ok {
- return fmt.Errorf("invalid contents")
- }
-
- f.Announce, ok = content["announce"].(string)
- if !ok {
- return fmt.Errorf("invalid announce field")
- }
-
- info, ok := content["info"].(map[string]any)
- if !ok {
- return fmt.Errorf("invalid info field")
- }
-
- f.Info.Length, ok = info["length"].(int)
- if !ok {
- return fmt.Errorf("invalid info.length field")
- }
-
- f.Info.Name, ok = info["name"].(string)
- if !ok {
- return fmt.Errorf("invalid info.name field")
- }
-
- f.Info.PieceLength, ok = info["piece length"].(int)
- if !ok {
- return fmt.Errorf("invalid info.piece length field")
- }
-
- f.Info.Pieces, ok = info["pieces"].(string)
- if !ok {
- return fmt.Errorf("invalid info.pieces field")
- }
-
- return nil
-}
diff --git a/cmd/mybittorrent/main.go b/cmd/mybittorrent/main.go
index 7905e01..d8014e7 100644
--- a/cmd/mybittorrent/main.go
+++ b/cmd/mybittorrent/main.go
@@ -1,10 +1,13 @@
package main
import (
+ "crypto/sha1"
"encoding/json"
"fmt"
"os"
- // bencode "github.com/jackpal/bencode-go" // Available if you need it!
+ "strings"
+
+ bencode "github.com/jackpal/bencode-go" // Available if you need it!
)
func main() {
@@ -13,8 +16,7 @@ func main() {
switch command {
case "decode":
bencodedValue := os.Args[2]
-
- decoded, err := NewDecoder(bencodedValue).Decode()
+ decoded, err := bencode.Decode(strings.NewReader(bencodedValue))
if err != nil {
fmt.Println(err)
return
@@ -26,18 +28,20 @@ func main() {
case "info":
f, err := os.Open(os.Args[2])
if err != nil {
- fmt.Println("Failed to open file: " + os.Args[2])
+ fmt.Println("Failed to open file: ", os.Args[2])
}
- tf := NewFile()
+ var meta Meta
+ if err = bencode.Unmarshal(f, &meta); err != nil {
+ panic(err)
+ }
- err = tf.ReadFrom(f)
- if err != nil {
- fmt.Println("Failed to parse file: " + os.Args[2])
+ sha := sha1.New()
+ if err = bencode.Marshal(sha, meta.Info); err != nil {
panic(err)
}
- fmt.Printf("Tracker URL: %s\nLength: %d", tf.Announce, tf.Info.Length)
+ fmt.Printf("Tracker URL: %s\nLength: %d\nInfo Hash: %x", meta.Announce, meta.Info.Length, sha.Sum(nil))
default:
fmt.Println("Unknown command: " + command)