Skip to content

Commit 88f0f63

Browse files
committed
Sloppy first implementation of decoder
1 parent 84f0000 commit 88f0f63

3 files changed

Lines changed: 367 additions & 0 deletions

File tree

decoder.go

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
package bencode
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"fmt"
7+
"io"
8+
"reflect"
9+
"strconv"
10+
)
11+
12+
type reader struct {
13+
*bytes.Reader
14+
}
15+
16+
func newReader(data []byte) *reader {
17+
return &reader{bytes.NewReader(data)}
18+
}
19+
20+
func (r *reader) ReadUntil(c byte) ([]byte, error) {
21+
res := []byte("")
22+
for {
23+
b, err := r.ReadByte()
24+
if err != nil {
25+
if errors.Is(err, io.EOF) {
26+
break
27+
}
28+
29+
return []byte{}, err
30+
}
31+
if b == c {
32+
break
33+
}
34+
res = append(res, b)
35+
}
36+
return res, nil
37+
}
38+
39+
func (r *reader) readNBytes(n uint64) ([]byte, error) {
40+
res := []byte("")
41+
var i uint64
42+
for i = 0; i < n; i++ {
43+
b, err := r.ReadByte()
44+
if err != nil {
45+
return []byte(""), err
46+
}
47+
res = append(res, b)
48+
}
49+
return res, nil
50+
}
51+
52+
func (r * reader) readStringLength() (uint64, error) {
53+
// Read the length of string
54+
size, err := r.ReadUntil(':')
55+
if err != nil {
56+
return 0, err
57+
}
58+
res, err := strconv.ParseUint(string(size), 10, 64)
59+
if err != nil {
60+
return 0, err
61+
}
62+
return res, nil
63+
}
64+
65+
func (r *reader) ReadString() (string, error) {
66+
size, err := r.readStringLength()
67+
if err != nil {
68+
return "", err
69+
}
70+
b, err := r.readNBytes(size)
71+
return string(b), err
72+
}
73+
74+
func (r *reader) readInteger() (string, error) {
75+
r.ReadByte()
76+
value, err := r.ReadUntil('e')
77+
if err != nil {
78+
return "", err
79+
}
80+
return string(value), nil
81+
}
82+
83+
func (r *reader) ReadList() ([]interface{}, error) {
84+
res := make([]interface{}, 0)
85+
r.ReadByte()
86+
var v interface{}
87+
for {
88+
e, err := r.ReadByte()
89+
if err != nil {
90+
return res, err
91+
}
92+
if e == 'e' {
93+
break
94+
}
95+
r.UnreadByte()
96+
vt, err := r.getValueType()
97+
if err != nil {
98+
return res, err
99+
}
100+
switch vt {
101+
case reflect.Uint64:
102+
v, err = r.readInteger()
103+
if err != nil {
104+
return res, err
105+
}
106+
case reflect.String:
107+
v, err = r.ReadString()
108+
if err != nil {
109+
return res, err
110+
}
111+
case reflect.Array:
112+
v, err = r.ReadList()
113+
if err != nil {
114+
return res, err
115+
}
116+
case reflect.Map:
117+
v, err = r.ReadDictionary()
118+
}
119+
res = append(res, v)
120+
}
121+
return res, nil
122+
}
123+
124+
func (r *reader) ReadDictionary() (map[string]interface{}, error) {
125+
res := make(map[string]interface{})
126+
r.ReadByte()
127+
var v interface{}
128+
for {
129+
s, _ := r.ReadString()
130+
vt, err := r.getValueType()
131+
if err != nil {
132+
return res, err
133+
}
134+
switch vt {
135+
case reflect.Uint64:
136+
v, err = r.readInteger()
137+
if err != nil {
138+
return res, err
139+
}
140+
case reflect.String:
141+
v, err = r.ReadString()
142+
if err != nil {
143+
return res, err
144+
}
145+
case reflect.Array:
146+
v, err = r.ReadList()
147+
if err != nil {
148+
return res, err
149+
}
150+
case reflect.Map:
151+
v, err = r.ReadDictionary()
152+
}
153+
res[s] = v
154+
if err != nil {
155+
return res, err
156+
}
157+
e, err := r.ReadByte()
158+
if err != nil {
159+
return res, err
160+
}
161+
if e == 'e' {
162+
break
163+
}
164+
r.UnreadByte()
165+
}
166+
return res, nil
167+
}
168+
169+
func (r *reader) getValueType() (reflect.Kind, error) {
170+
b, err := r.ReadByte()
171+
defer r.UnreadByte()
172+
if err != nil {
173+
return reflect.Invalid, err
174+
}
175+
switch b {
176+
case 'i':
177+
return reflect.Uint64, nil
178+
case 'l':
179+
return reflect.Array, nil
180+
case 'd':
181+
return reflect.Map, nil
182+
default:
183+
return reflect.String, nil
184+
}
185+
}
186+
187+
func (r *reader) readValue(vt reflect.Kind) (string, error) {
188+
var res string
189+
switch vt {
190+
case reflect.Uint64:
191+
res, err := r.readInteger()
192+
if err != nil {
193+
return res, err
194+
}
195+
return res, nil
196+
case reflect.String:
197+
res, err := r.ReadString()
198+
if err != nil {
199+
return res, err
200+
}
201+
return res, nil
202+
}
203+
return res, nil
204+
}
205+
206+
type decoder struct {
207+
reader *reader
208+
}
209+
210+
func (d *decoder) init(data []byte) {
211+
d.reader = newReader(data)
212+
}
213+
214+
func (d *decoder) setArray(rv reflect.Value, values []interface{}) error {
215+
indirect := reflect.Indirect(rv)
216+
for k, value := range values {
217+
switch decodedValue := value.(type) {
218+
case string:
219+
indirect.Set(reflect.Append(indirect, reflect.ValueOf(decodedValue)))
220+
case []interface{}:
221+
n := reflect.New(indirect.Type().Elem())
222+
indirect.Set(reflect.Append(indirect, reflect.Indirect(n)))
223+
d.setArray(indirect.Index(k).Addr(), decodedValue)
224+
}
225+
}
226+
227+
return nil
228+
}
229+
230+
func (d *decoder) setStruct(rv reflect.Value, values map[string]interface{}) error {
231+
indirect := reflect.Indirect(rv)
232+
if rv.Kind() != reflect.Pointer || rv.IsNil() {
233+
return fmt.Errorf("passed object is not a pointer or is nil: %v", reflect.TypeOf(rv))
234+
}
235+
if indirect.Kind() != reflect.Struct {
236+
return fmt.Errorf("passed object is not a struct: %v", reflect.TypeOf(rv))
237+
}
238+
fields := getStructFields(indirect.Type())
239+
for key, value := range values {
240+
if field, ok := fields[key]; ok {
241+
switch decodedValue := value.(type) {
242+
case string:
243+
switch field.Type.Kind() {
244+
case reflect.Uint64:
245+
insertValue, err := stringToUint(decodedValue)
246+
if err != nil {
247+
return err
248+
}
249+
indirect.FieldByName(field.Name).SetUint(insertValue)
250+
case reflect.String:
251+
indirect.FieldByName(field.Name).SetString(decodedValue)
252+
}
253+
case map[string]interface{}:
254+
d.setStruct(indirect.FieldByName(field.Name), decodedValue)
255+
case []interface{}:
256+
d.setArray(indirect.FieldByName(field.Name).Addr(), decodedValue)
257+
}
258+
}
259+
}
260+
return nil
261+
}
262+
263+
func (d *decoder) unmarshal(v any) error{
264+
values, err := d.reader.ReadDictionary()
265+
if err != nil {
266+
return err
267+
}
268+
rv := reflect.ValueOf(v)
269+
return d.setStruct(rv, values)
270+
}
271+
272+
func decode(input []byte, v any) {
273+
var d decoder
274+
d.init(input)
275+
d.unmarshal(v)
276+
}
277+
278+
func getStructFields(t reflect.Type) map[string]reflect.StructField {
279+
r := make(map[string]reflect.StructField)
280+
for i := 0; i < t.NumField(); i++ {
281+
f := t.Field(i)
282+
r[string(f.Tag.Get("bencode"))] = f
283+
}
284+
return r
285+
}
286+
287+
func stringToUint(s string) (uint64, error) {
288+
i, err := strconv.ParseUint(s, 10, 64)
289+
if err != nil {
290+
return 0, nil
291+
}
292+
return i, nil
293+
}
294+

decoder_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package bencode
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"testing"
7+
)
8+
9+
func TestDecodeInt(t *testing.T) {
10+
type test struct {
11+
Test uint64 `bencode:"test"`
12+
}
13+
have := &test{}
14+
want := &test{1234567890}
15+
input := []byte("d4:testi1234567890ee")
16+
decode(input, have)
17+
if !reflect.DeepEqual(have, want) {
18+
t.Errorf("Struct not properly hidrated: wanted %v but have %v", want, have)
19+
}
20+
}
21+
22+
func TestDecodeString(t *testing.T) {
23+
type test struct {
24+
Test string `bencode:"test"`
25+
}
26+
have := &test{}
27+
want := &test{"test"}
28+
input := []byte("d4:test4:teste")
29+
decode(input, have)
30+
if !reflect.DeepEqual(have, want) {
31+
t.Errorf("Struct not properly hidrated: wanted %v but have %v", want, have)
32+
}
33+
}
34+
35+
func TestDecodeStringInt(t *testing.T) {
36+
type test struct {
37+
TestString string `bencode:"teststring"`
38+
TestInt uint64 `bencode:"testint"`
39+
}
40+
have := &test{}
41+
want := &test{"test", 1234567890}
42+
input := []byte("d7:testinti1234567890e10:teststring4:teste")
43+
decode(input, have)
44+
if !reflect.DeepEqual(have, want) {
45+
t.Errorf("Struct not properly hidrated: wanted %v but have %v", want, have)
46+
}
47+
}
48+
49+
func TestDecodeMockTorrentFile(t *testing.T) {
50+
type info struct {
51+
Length uint64 `bencode:"length"`
52+
Name string `bencode:"name"`
53+
PieceLength uint64 `bencode:"piece length"`
54+
}
55+
type test struct {
56+
Announce string `bencode:"announce"`
57+
Comment string `bencode:"comment"`
58+
AnnounceList [][]string `bencode:"announce-list"`
59+
Info *info `bencode:"info"`
60+
}
61+
have := &test{Info: &info{}}
62+
want := &test{"https://torrent.ubuntu.com/announce", "Ubuntu CD releases.ubuntu.com", [][]string{{"https://torrent.ubuntu.com/announce"}, {"https://ipv6.torrent.ubuntu.com/announce"}}, &info{4932407296, "ubuntu-23.04-desktop-amd64.iso", 262144}}
63+
input := []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")
64+
decode(input, have)
65+
if !reflect.DeepEqual(have, want) {
66+
fmt.Println(have.Info)
67+
fmt.Println(want.Info)
68+
t.Errorf("Struct not properly hidrated: wanted %+v but have %+v", want, have)
69+
}
70+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/Extintor/bencode
2+
3+
go 1.19

0 commit comments

Comments
 (0)