Skip to content

Commit bbcbced

Browse files
committed
update decoder
1 parent 21e0290 commit bbcbced

File tree

6 files changed

+111
-65
lines changed

6 files changed

+111
-65
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ data, err := bencode.Unmarshal(value)
5656

5757
| Library | Time | Bytes Allocated | Objects Allocated |
5858
| :------------------ | :--------: | :-------------: | :---------------: |
59-
| IncSW/go-bencode | 1149 ns/op | 960 B/op | 18 allocs/op |
59+
| IncSW/go-bencode | 1001 ns/op | 960 B/op | 18 allocs/op |
6060
| cristalhq/bencode | 1160 ns/op | 960 B/op | 18 allocs/op |
6161
| nabilanam/bencode | 1379 ns/op | 1240 B/op | 39 allocs/op |
6262
| aleksatr/go-bencode | 2270 ns/op | 1816 B/op | 51 allocs/op |

bencode.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ func Marshal(data interface{}) ([]byte, error) {
1616
}
1717

1818
func Unmarshal(data []byte) (interface{}, error) {
19-
return decoder.Unmarshal(data)
19+
var d decoder.Decoder
20+
return d.Decode(data)
2021
}

decoder_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func TestUnmarshal(t *testing.T) {
5959
}
6060

6161
result, err = Unmarshal([]byte("i38qe"))
62-
if !assert.Error(err) || !assert.Nil(result) || !assert.Equal(`strconv.ParseInt: parsing "38q": invalid syntax`, err.Error()) {
62+
if !assert.Error(err) || !assert.Nil(result) || !assert.Equal("bencode: invalid integer byte: 113", err.Error()) {
6363
return
6464
}
6565

internal/decoder/bytes.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package decoder
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
)
7+
8+
func (d *Decoder) decodeBytes() ([]byte, error) {
9+
if d.data[d.cursor] < '0' || d.data[d.cursor] > '9' {
10+
return nil, errors.New("bencode: invalid string field")
11+
}
12+
index := bytes.IndexByte(d.data[d.cursor:], ':')
13+
if index == -1 {
14+
return nil, errors.New("bencode: invalid string field")
15+
}
16+
index += d.cursor
17+
stringLength, err := d.parseInt(d.data[d.cursor:index])
18+
if err != nil {
19+
return nil, err
20+
}
21+
index += 1
22+
endIndex := index + int(stringLength)
23+
if endIndex > d.length {
24+
return nil, errors.New("bencode: not a valid bencoded string")
25+
}
26+
value := d.data[index:endIndex]
27+
d.cursor = endIndex
28+
return value, nil
29+
}

internal/decoder/decoder.go

+23-62
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,67 @@
11
package decoder
22

33
import (
4-
"bytes"
54
"errors"
6-
"strconv"
5+
_ "unsafe"
76

87
"github.com/IncSW/go-bencode/internal"
98
)
109

11-
func Unmarshal(data []byte) (interface{}, error) {
12-
return (&unmarshaler{
13-
data: data,
14-
length: len(data),
15-
}).unmarshal()
16-
}
17-
18-
type unmarshaler struct {
10+
type Decoder struct {
1911
data []byte
2012
length int
2113
cursor int
2214
}
2315

24-
func (u *unmarshaler) unmarshal() (interface{}, error) {
25-
switch u.data[u.cursor] {
26-
case 'i':
27-
u.cursor += 1
28-
index := bytes.IndexByte(u.data[u.cursor:], 'e')
29-
if index == -1 {
30-
return nil, errors.New("bencode: invalid integer field")
31-
}
32-
index += u.cursor
33-
integer, err := strconv.ParseInt(internal.B2S(u.data[u.cursor:index]), 10, 64)
34-
if err != nil {
35-
return nil, err
36-
}
37-
u.cursor = index + 1
38-
return integer, nil
16+
func (d *Decoder) Decode(data []byte) (interface{}, error) {
17+
d.data = data
18+
d.length = len(data)
19+
return d.decode()
20+
}
3921

22+
func (d *Decoder) decode() (interface{}, error) {
23+
switch d.data[d.cursor] {
24+
case 'i':
25+
return d.decodeInt()
4026
case 'l':
41-
u.cursor += 1
27+
d.cursor += 1
4228
list := []interface{}{}
4329
for {
44-
if u.cursor == u.length {
30+
if d.cursor == d.length {
4531
return nil, errors.New("bencode: invalid list field")
4632
}
47-
if u.data[u.cursor] == 'e' {
48-
u.cursor += 1
33+
if d.data[d.cursor] == 'e' {
34+
d.cursor += 1
4935
return list, nil
5036
}
51-
value, err := u.unmarshal()
37+
value, err := d.decode()
5238
if err != nil {
5339
return nil, err
5440
}
5541
list = append(list, value)
5642
}
57-
5843
case 'd':
59-
u.cursor += 1
44+
d.cursor += 1
6045
dictionary := map[string]interface{}{}
6146
for {
62-
if u.cursor == u.length {
47+
if d.cursor == d.length {
6348
return nil, errors.New("bencode: invalid dictionary field")
6449
}
65-
if u.data[u.cursor] == 'e' {
66-
u.cursor += 1
50+
if d.data[d.cursor] == 'e' {
51+
d.cursor += 1
6752
return dictionary, nil
6853
}
69-
key, err := u.unmarshalString()
54+
key, err := d.decodeBytes()
7055
if err != nil {
7156
return nil, errors.New("bencode: non-string dictionary key")
7257
}
73-
value, err := u.unmarshal()
58+
value, err := d.decode()
7459
if err != nil {
7560
return nil, err
7661
}
7762
dictionary[internal.B2S(key)] = value
7863
}
79-
8064
default:
81-
return u.unmarshalString()
82-
}
83-
}
84-
85-
func (u *unmarshaler) unmarshalString() ([]byte, error) {
86-
if u.data[u.cursor] < '0' || u.data[u.cursor] > '9' {
87-
return nil, errors.New("bencode: invalid string field")
88-
}
89-
index := bytes.IndexByte(u.data[u.cursor:], ':')
90-
if index == -1 {
91-
return nil, errors.New("bencode: invalid string field")
92-
}
93-
index += u.cursor
94-
stringLength, err := strconv.ParseInt(internal.B2S(u.data[u.cursor:index]), 10, 64)
95-
if err != nil {
96-
return nil, err
97-
}
98-
index += 1
99-
endIndex := index + int(stringLength)
100-
if endIndex > u.length {
101-
return nil, errors.New("bencode: not a valid bencoded string")
65+
return d.decodeBytes()
10266
}
103-
value := u.data[index:endIndex]
104-
u.cursor = endIndex
105-
return value, nil
10667
}

internal/decoder/int.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package decoder
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"strconv"
7+
)
8+
9+
var (
10+
pow10i64 = [...]int64{
11+
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
12+
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18,
13+
}
14+
pow10i64Len = len(pow10i64)
15+
)
16+
17+
func (d *Decoder) parseInt(data []byte) (int64, error) {
18+
isNegative := false
19+
if data[0] == '-' {
20+
data = data[1:]
21+
isNegative = true
22+
}
23+
maxDigit := len(data)
24+
if maxDigit > pow10i64Len {
25+
return 0, errors.New("bencode: invalid length of number")
26+
}
27+
sum := int64(0)
28+
for i, b := range data {
29+
if b < '0' || b > '9' {
30+
return 0, errors.New("bencode: invalid integer byte: " + strconv.FormatUint(uint64(b), 10))
31+
}
32+
c := int64(b) - 48
33+
digitValue := pow10i64[maxDigit-i-1]
34+
sum += c * digitValue
35+
}
36+
if isNegative {
37+
return -1 * sum, nil
38+
}
39+
return sum, nil
40+
}
41+
42+
func (d *Decoder) decodeInt() (interface{}, error) {
43+
d.cursor += 1
44+
index := bytes.IndexByte(d.data[d.cursor:], 'e')
45+
if index == -1 {
46+
return nil, errors.New("bencode: invalid integer field")
47+
}
48+
index += d.cursor
49+
integer, err := d.parseInt(d.data[d.cursor:index])
50+
if err != nil {
51+
return nil, err
52+
}
53+
d.cursor = index + 1
54+
return integer, nil
55+
}

0 commit comments

Comments
 (0)