aboutsummaryrefslogtreecommitdiff
path: root/bencode/encode.go
blob: 7664169e372cc06e802359d89689b89a1edd15f8 (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
package bencode

import (
	"errors"
	"fmt"
	"sort"
)

// Encode encodes a string, int, dict or list value to a bencoded string.
func Encode(data interface{}) ([]byte, error) {
	return encodeItem(data)
}

// EncodeString encodes a string value.
func EncodeString(data string) ([]byte, error) {
	length := fmt.Sprintf("%d:", len(data))
	out := make([]byte, 0, len(length)+len(data)+1)
	out = append(out, []byte(length)...)
	return append(out, []byte(data)...), nil
}

// EncodeInt encodes a int value.
func EncodeInt(data int64) ([]byte, error) {
	ib := fmt.Sprintf("i%de", data)
	return []byte(ib), nil
}

// EncodeDict encodes a dict value.
func EncodeDict(data map[string]interface{}) ([]byte, error) {
	out := make([]byte, 0, 2)
	out = append(out, 'd')

	// Sort keys
	list := make(sort.StringSlice, len(data))
	i := 0
	for key := range data {
		list[i] = key
		i++
	}
	list.Sort()

	for _, key := range list {
		keyb, err := EncodeString(key)
		if err != nil {
			return nil, err
		}
		value, err := encodeItem(data[key])
		if err != nil {
			return nil, err
		}
		out = append(out, keyb...)
		out = append(out, value...)
	}
	return append(out, 'e'), nil
}

// EncodeList encodes a list value.
func EncodeList(data []interface{}) ([]byte, error) {
	out := make([]byte, 0, 2)
	out = append(out, 'l')

	for _, item := range data {
		b, err := encodeItem(item)
		if err != nil {
			return nil, err
		}
		out = append(out, b...)
	}
	return append(out, 'e'), nil
}

// EncodeItem encodes an item of dict or list.
func encodeItem(data interface{}) ([]byte, error) {
	switch v := data.(type) {
	case []byte:
		return EncodeString(string(v))
	case string:
		return EncodeString(v)
	case int:
		return EncodeInt(int64(v))
	case int16:
		return EncodeInt(int64(v))
	case int32:
		return EncodeInt(int64(v))
	case int64:
		return EncodeInt(int64(v))
	case []interface{}:
		return EncodeList(v)
	case map[string]interface{}:
		return EncodeDict(v)
	default:
		return nil, errors.New("bencode: invalid type to encode")
	}
}