From cf9e66821412aaf7461410928659c1b0296bc5c2 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Sun, 12 Jun 2016 05:36:39 +0200 Subject: Unmarshal --- bencode/bencode.go | 126 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 112 insertions(+), 14 deletions(-) (limited to 'bencode/bencode.go') 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 } -- cgit v1.2.3