aboutsummaryrefslogtreecommitdiff
path: root/bencode/bencode.go
blob: d2d78159b2e3f9080f251cc97594c34e04ebb1c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package bencode

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"reflect"
	"strings"
	"time"
)

// mapping / limitations
// dict -> struct
// list -> aray of same type

type itemType int

const (
	itemError itemType = iota
	itemDict
	itemList
	itemNumber
	itemString
)

var ErrValue = errors.New("invalid value")

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 ErrValue
	}
	switch v.Kind() {
	case reflect.String:
		marshalString(out, v.String())
	case reflect.Int:
		marshalInt(out, v.Int())
	case reflect.Slice:
		marshalList(out, v)
	case reflect.Struct:
		marshalDict(out, v)
	}
	return nil
}

func marshalDict(out io.Writer, v reflect.Value) {
	switch val := v.Interface().(type) {
	case time.Time:
		marshalInt(out, val.Unix())
	default:
		t := v.Type()
		io.WriteString(out, "d")
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			tag := f.Tag.Get("bencode")
			if tag == "-" {
				continue
			}
			name, _ := parseTag(tag)
			if name == "" {
				name = f.Name
			}
			if !isEmpty(v.Field(i)) {
				marshalString(out, name)
				marshalField(out, v.Field(i))
			}
		}
		io.WriteString(out, "e")
	}
}

func isEmpty(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Int:
		return v.Int() == 0
	case reflect.String, reflect.Slice:
		return v.Len() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

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)
}

func Unmarshal(data []byte, v interface{}) error {
	return nil
}

func parseTag(tag string) (string, string) {
	if i := strings.Index(tag, ","); i != -1 {
		return tag[:i], tag[i+1:]
	}
	return tag, ""
}