aboutsummaryrefslogtreecommitdiff
path: root/bencode/bencode.go
diff options
context:
space:
mode:
Diffstat (limited to 'bencode/bencode.go')
-rw-r--r--bencode/bencode.go126
1 files changed, 112 insertions, 14 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
}