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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
package bencode
import (
"errors"
"fmt"
"strconv"
"unicode/utf8"
)
// Decode decodes a bencoded string to string, int, list or map.
func Decode(data []byte) (r interface{}, err error) {
r, _, err = decodeItem(data, 0)
return r, err
}
// DecodeString decodes a string from a given index
// It returns the string and the position of the last character
// or the offset at the point of error
func DecodeString(data []byte, start int) (r string, end int, err error) {
if start >= len(data) || data[start] < '0' || data[start] > '9' {
err = errors.New("bencode: invalid string length")
return r, 0, err
}
prefix, i, err := readUntil(data, start, ':')
if err != nil {
return r, i, err
}
length, err := strconv.ParseInt(string(prefix), 10, 0)
if err != nil {
return r, 0, err
}
end = i + int(length)
if end > len(data) || end < i {
err = errors.New("bencode: string length out of range")
return r, end, err
}
return string(data[i:end]), end - 1, nil
}
// DecodeInt decodes an integer value
// It returns the integer and the position of the last character
func DecodeInt(data []byte, start int) (r int64, end int, err error) {
if start >= len(data) || data[start] != 'i' {
err = errors.New("bencode: invalid integer")
return r, end, err
}
prefix, end, err := readUntil(data, start, 'e')
if err != nil {
return r, end, err
}
r, err = strconv.ParseInt(string(prefix[1:]), 10, 64)
return r, end - 1, err
}
// DecodeList decodes a list value
// It returns the array and the position of the last character
func DecodeList(data []byte, start int) (r []interface{}, end int, err error) {
if start >= len(data) {
return r, end, errors.New("bencode: list range error")
}
if data[start] != 'l' {
return r, end, errors.New("bencode: invalid list")
}
end = start + 1
// Empty list
if data[end] == 'e' {
return r, end, nil
}
var item interface{}
for end < len(data) {
item, end, err = decodeItem(data, end)
if err != nil {
return r, end, err
}
r = append(r, item)
end++
char, _ := utf8.DecodeRune(data[end:])
if char == 'e' {
return r, end, nil
}
}
return r, end, errors.New("bencode: invalid list termination")
}
// DecodeDict decodes a dict as a map
// It returns the map and the position of the last character
func DecodeDict(data []byte, start int) (r map[string]interface{}, end int, err error) {
if start >= len(data) {
return r, end, errors.New("bencode: dict range error")
}
if data[start] != 'd' {
return r, end, errors.New("bencode: invalid dict")
}
end = start + 1
// Empty dict
if data[end] == 'e' {
return r, end, nil
}
var item interface{}
var key string
r = make(map[string]interface{})
for end < len(data) {
key, end, err = DecodeString(data, end)
if err != nil {
return r, end, errors.New("bencode: invalid dict key")
}
if end >= len(data) {
return r, end, errors.New("bencode: dict range error")
}
end++
item, end, err = decodeItem(data, end)
if err != nil {
return r, end, err
}
r[key] = item
end++
char, _ := utf8.DecodeRune(data[end:])
if char == 'e' {
return r, end, nil
}
}
return r, end, errors.New("bencode: invalid dict termination")
}
// decodeItem decodes the next type at the given index
// It returns the index of the last character decoded
func decodeItem(data []byte, start int) (r interface{}, end int, err error) {
switch data[start] {
case 'l':
return DecodeList(data, start)
case 'd':
return DecodeDict(data, start)
case 'i':
return DecodeInt(data, start)
default:
return DecodeString(data, start)
}
}
// Read until the given character
// Returns the slice before the character and the index of the next character
// or the offset at the point of error
func readUntil(data []byte, start int, c byte) ([]byte, int, error) {
i := start
for ; i < len(data); i++ {
if data[i] == c {
return data[start:i], i + 1, nil
}
}
return data, i, fmt.Errorf("bencode: '%b' not found", c)
}
|