aboutsummaryrefslogtreecommitdiff
path: root/bencode
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2016-06-12 05:36:39 +0200
committerDimitri Sokolyuk <demon@dim13.org>2016-06-12 05:36:39 +0200
commitcf9e66821412aaf7461410928659c1b0296bc5c2 (patch)
tree15ed393753907b9821bae6271b06fdfb41009983 /bencode
parent695ac4be1f813f3605ed4f15444670dbd1689288 (diff)
Unmarshal
Diffstat (limited to 'bencode')
-rw-r--r--bencode/bencode.go126
-rw-r--r--bencode/bencode_test.go33
2 files changed, 139 insertions, 20 deletions
diff --git a/bencode/bencode.go b/bencode/bencode.go
index 9a5cccd..f1c0245 100644
--- a/bencode/bencode.go
+++ b/bencode/bencode.go
@@ -117,34 +117,132 @@ func parseTag(tag string) (string, string) {
return tag, ""
}
+type decodeState struct {
+ data []byte
+ off int
+}
+
func Unmarshal(data []byte, v interface{}) error {
val := reflect.ValueOf(v)
if val.Kind() != reflect.Ptr {
return errors.New("non-pointer passed to Unmarshal")
}
+ d := decodeState{data, 0}
+ return d.unmarshalField(val.Elem())
+}
+
+func (d *decodeState) unmarshalField(v reflect.Value) error {
+ switch d.data[d.off] {
+ case 'd':
+ d.unmarshalDict(v)
+ case 'i':
+ d.unmarshalInt(v)
+ case 'l':
+ d.unmarshalList(v)
+ case 'e':
+ panic("end")
+ default:
+ d.unmarshalString(v)
+ }
return nil
}
-func unmarshalString(data []byte) string {
- if i := bytes.IndexByte(data, ':'); i != -1 {
- len, err := strconv.Atoi(string(data[:i]))
- if err != nil {
- return ""
+func (d *decodeState) unmarshalDict(v reflect.Value) {
+ if d.data[d.off] == 'd' {
+ d.off++
+ } else {
+ panic("not dict")
+ }
+
+ for d.data[d.off] != 'e' {
+ key, n := parseString(d.data[d.off:])
+ d.off += n
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ tag := f.Tag.Get("bencode")
+ name, _ := parseTag(tag)
+ if name == key || f.Name == key {
+ d.unmarshalField(v.Field(i))
+ break
+ }
+ }
+ }
+ d.off++
+}
+
+func (d *decodeState) unmarshalList(v reflect.Value) {
+ if d.data[d.off] == 'l' {
+ d.off++
+ } else {
+ panic("not list")
+ }
+
+ for i := 0; d.data[d.off] != 'e'; i++ {
+ if i >= v.Cap() {
+ newcap := v.Cap() + v.Cap()/2
+ if newcap < 4 {
+ newcap = 4
+ }
+ newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
+ reflect.Copy(newv, v)
+ v.Set(newv)
+ }
+ if i >= v.Len() {
+ v.SetLen(i + 1)
}
- return string(data[i+1 : i+1+len])
+ d.unmarshalField(v.Index(i))
+ }
+ d.off++
+}
+
+func (d *decodeState) unmarshalString(v reflect.Value) {
+ s, n := parseString(d.data[d.off:])
+ d.off += n
+ switch v.Kind() {
+ case reflect.Slice:
+ v.SetBytes([]byte(s))
+ default:
+ v.SetString(s)
+ }
+}
+
+func parseString(data []byte) (string, int) {
+ i := bytes.IndexByte(data, ':')
+ if i < 0 {
+ panic("not string")
+ }
+ n, err := strconv.Atoi(string(data[:i]))
+ if err != nil {
+ return "", 0
+ }
+ end := i + 1 + n
+ return string(data[i+1 : end]), end
+}
+
+func (d *decodeState) unmarshalInt(v reflect.Value) {
+ i, n := parseInt(d.data[d.off:])
+ d.off += n
+ switch v.Interface().(type) {
+ case time.Time:
+ t := time.Unix(i, 0)
+ v.Set(reflect.ValueOf(t))
+ default:
+ v.SetInt(i)
}
- return ""
}
-func unmarshalInt(data []byte) int {
- pos := 0
- if data[pos] == 'i' {
- pos++
+func parseInt(data []byte) (int64, int) {
+ off := 0
+ if data[off] == 'i' {
+ off++
+ } else {
+ panic("not int")
}
end := bytes.IndexByte(data, 'e')
- i, err := strconv.Atoi(string(data[pos:end]))
+ i, err := strconv.Atoi(string(data[off:end]))
if err != nil {
- return 0
+ return 0, end + 1
}
- return i
+ return int64(i), end + 1
}
diff --git a/bencode/bencode_test.go b/bencode/bencode_test.go
index a7c534c..9ee40d7 100644
--- a/bencode/bencode_test.go
+++ b/bencode/bencode_test.go
@@ -1,6 +1,7 @@
package bencode
import (
+ "io/ioutil"
"testing"
"time"
@@ -27,18 +28,38 @@ func TestMarshal(t *testing.T) {
t.Logf("%q\n", string(out))
}
-func TestUnmarshalString(t *testing.T) {
+func TestParseString(t *testing.T) {
in := "4:testZZZ"
- s := unmarshalString([]byte(in))
- if s != "test" {
+ s, l := parseString([]byte(in))
+ if s != "test" || l != 6 {
t.Error("expected test, got", s)
}
}
-func TestUnmarshalInt(t *testing.T) {
+func TestParseInt(t *testing.T) {
in := "i12345e999"
- i := unmarshalInt([]byte(in))
- if i != 12345 {
+ i, l := parseInt([]byte(in))
+ if i != 12345 || l != 7 {
t.Error("expected 12345, got", i)
}
}
+
+func TestUnmarshal(t *testing.T) {
+ files := []string{
+ "../examples/OpenBSD_5.9_amd64_install59.iso-2016-03-29-0449.torrent",
+ "../examples/OpenBSD_songs_ogg-2016-03-25-0127.torrent",
+ "../examples/debian-8.5.0-amd64-netinst.iso.torrent",
+ }
+ for _, file := range files {
+ var tor torrent.Torrent
+ body, err := ioutil.ReadFile(file)
+ if err != nil {
+ t.Error(err)
+ }
+ err = Unmarshal(body, &tor)
+ if err != nil {
+ t.Error(err)
+ }
+ t.Logf("%+v\n", tor)
+ }
+}