Skip to content

Commit e3c46bb

Browse files
committed
Encoder
1 parent 05983d0 commit e3c46bb

2 files changed

Lines changed: 149 additions & 0 deletions

File tree

encoder.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package bencode
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"reflect"
7+
)
8+
9+
type encoder struct {
10+
output bytes.Buffer
11+
}
12+
13+
func (e *encoder) init() *encoder {
14+
return &encoder{bytes.Buffer{}}
15+
}
16+
17+
func (e *encoder) encodeString(value string) {
18+
e.output.WriteString(fmt.Sprintf("%d", len(value)))
19+
e.output.WriteByte(':')
20+
e.output.WriteString(value)
21+
}
22+
23+
func (e *encoder) encodeUint(value uint64) {
24+
e.output.WriteByte('i')
25+
e.output.WriteString(fmt.Sprintf("%d", value))
26+
e.output.WriteByte('e')
27+
}
28+
func (e *encoder) encodeList(rv reflect.Value) {
29+
e.output.WriteByte('l')
30+
indirect := reflect.Indirect(rv)
31+
for i := 0; i < indirect.Len(); i++ {
32+
switch indirect.Index(i).Kind() {
33+
case reflect.Uint64:
34+
case reflect.String:
35+
e.encodeString(indirect.Index(i).Interface().(string))
36+
case reflect.Struct:
37+
case reflect.Slice:
38+
e.encodeList(indirect.Index(i).Addr())
39+
}
40+
}
41+
e.output.WriteByte('e')
42+
}
43+
44+
func (e *encoder) encodeDict(rv reflect.Value) error {
45+
e.output.WriteByte('d')
46+
indirect := reflect.Indirect(rv)
47+
for i := 0; i < indirect.NumField(); i++ {
48+
switch indirect.Type().Field(i).Type.Kind() {
49+
case reflect.Uint64:
50+
e.encodeString(indirect.Type().Field(i).Tag.Get("bencode"))
51+
e.encodeUint(indirect.Field(i).Interface().(uint64))
52+
case reflect.String:
53+
e.encodeString(indirect.Type().Field(i).Tag.Get("bencode"))
54+
e.encodeString(indirect.Field(i).Interface().(string))
55+
case reflect.Struct:
56+
e.encodeString(indirect.Type().Field(i).Tag.Get("bencode"))
57+
e.encodeDict(indirect.Field(i).Addr())
58+
case reflect.Slice:
59+
e.encodeString(indirect.Type().Field(i).Tag.Get("bencode"))
60+
e.encodeList(indirect.Field(i).Addr())
61+
}
62+
}
63+
e.output.WriteByte('e')
64+
return nil
65+
}
66+
67+
func (e *encoder) marshal(v any) ([]byte, error) {
68+
e.encodeDict(reflect.ValueOf(v))
69+
return e.output.Bytes(), nil
70+
}
71+
72+
func encode(v any) ([]byte, error) {
73+
var e encoder
74+
e.init()
75+
return e.marshal(v)
76+
}

encoder_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package bencode
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestEcodeInt(t *testing.T) {
9+
type test struct {
10+
Test uint64 `bencode:"test"`
11+
}
12+
have, err := encode(&test{1234567890})
13+
want := []byte("d4:testi1234567890ee")
14+
if err != nil {
15+
t.Error(err)
16+
}
17+
if !reflect.DeepEqual(have, want) {
18+
t.Errorf("Struct not properly encoded: wanted %s but have %s", want, have)
19+
}
20+
}
21+
22+
func TestEncodeString(t *testing.T) {
23+
type test struct {
24+
Test string `bencode:"test"`
25+
}
26+
have, err := encode(&test{"test"})
27+
want := []byte("d4:test4:teste")
28+
if err != nil {
29+
t.Error(err)
30+
}
31+
if !reflect.DeepEqual(have, want) {
32+
t.Errorf("Struct not properly encoded: wanted %s but have %s", want, have)
33+
}
34+
}
35+
36+
func TestEncodeStringInt(t *testing.T) {
37+
type test struct {
38+
TestString string `bencode:"teststring"`
39+
TestInt uint64 `bencode:"testint"`
40+
}
41+
have, err := encode(&test{"test", 1234567890})
42+
want := []byte("d10:teststring4:test7:testinti1234567890ee")
43+
if err != nil {
44+
t.Error(err)
45+
}
46+
if !reflect.DeepEqual(have, want) {
47+
t.Errorf("Struct not properly encoded: wanted %s but have %s", want, have)
48+
}
49+
}
50+
51+
func TestEncodeMockTorrentFile(t *testing.T) {
52+
type info struct {
53+
Length uint64 `bencode:"length"`
54+
Name string `bencode:"name"`
55+
PieceLength uint64 `bencode:"piece length"`
56+
}
57+
type test struct {
58+
Announce string `bencode:"announce"`
59+
AnnounceList [][]string `bencode:"announce-list"`
60+
Comment string `bencode:"comment"`
61+
CreatedBy string `bencode:"created by"`
62+
CreationDate uint64 `bencode:"creation date"`
63+
Info info `bencode:"info"`
64+
}
65+
have, err := encode(&test{"https://torrent.ubuntu.com/announce", [][]string{{"https://torrent.ubuntu.com/announce"}, {"https://ipv6.torrent.ubuntu.com/announce"}}, "Ubuntu CD releases.ubuntu.com", "mktorrent 1.1", 1681992794, info{4932407296, "ubuntu-23.04-desktop-amd64.iso", 262144}})
66+
want := []byte("d8:announce35:https://torrent.ubuntu.com/announce13:announce-listll35:https://torrent.ubuntu.com/announceel40:https://ipv6.torrent.ubuntu.com/announceee7:comment29:Ubuntu CD releases.ubuntu.com10:created by13:mktorrent 1.113:creation datei1681992794e4:infod6:lengthi4932407296e4:name30:ubuntu-23.04-desktop-amd64.iso12:piece lengthi262144eee")
67+
if err != nil {
68+
t.Error(err)
69+
}
70+
if !reflect.DeepEqual(have, want) {
71+
t.Errorf("Struct not properly encoded: wanted %s but have %s", want, have)
72+
}
73+
}

0 commit comments

Comments
 (0)