package bencode import ( "bytes" "errors" "fmt" "io" "reflect" "sort" "time" ) // mapping / limitations // dict -> struct // list -> aray of same type func Marshal(v interface{}) ([]byte, error) { var out bytes.Buffer val := reflect.ValueOf(v) err := marshalField(&out, val) return out.Bytes(), err } func marshalField(out io.Writer, v reflect.Value) error { if !v.IsValid() { return errors.New("ivalid value") } switch v.Kind() { case reflect.String: marshalString(out, v.String()) case reflect.Int, reflect.Int64: marshalInt(out, v.Int()) case reflect.Slice: marshalList(out, v) case reflect.Struct: marshalDict(out, v) case reflect.Bool: if v.Bool() { marshalInt(out, 1) } else { marshalInt(out, 0) } } return nil } func isZero(v reflect.Value) bool { switch v.Kind() { case reflect.Int, reflect.Int64: return v.Int() == 0 case reflect.String, reflect.Slice: return v.Len() == 0 case reflect.Interface: if t, ok := v.Interface().(time.Time); ok { return t.IsZero() } case reflect.Ptr: return v.IsNil() } return false } type byName []reflect.StructField func (n byName) Len() int { return len(n) } func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name } func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] } func marshalDict(out io.Writer, v reflect.Value) { switch val := v.Interface().(type) { case time.Time: marshalInt(out, val.Unix()) default: t := v.Type() fields := make([]reflect.StructField, t.NumField()) for i := 0; i < t.NumField(); i++ { fields[i] = t.Field(i) } sort.Sort(byName(fields)) io.WriteString(out, "d") for _, n := range fields { tag := n.Tag.Get("bencode") if tag == "-" { continue } name, param := parseTag(tag) if name == "" { name = n.Name } vf := v.FieldByIndex(n.Index) if param == "optional" && isZero(vf) { continue } marshalString(out, name) marshalField(out, vf) } io.WriteString(out, "e") } } func marshalList(out io.Writer, v reflect.Value) { switch v.Type().Elem().Kind() { case reflect.Uint8: marshalString(out, string(v.Bytes())) default: io.WriteString(out, "l") for i := 0; i < v.Len(); i++ { marshalField(out, v.Index(i)) } io.WriteString(out, "e") } } func marshalString(out io.Writer, s string) { fmt.Fprintf(out, "%d:%s", len(s), s) } func marshalInt(out io.Writer, i int64) { fmt.Fprintf(out, "i%de", i) }