go-bt/bencode/parse.go

201 lines
3.9 KiB
Go
Raw Normal View History

2010-01-06 15:38:33 +00:00
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// bencode parser.
// See the bittorrent protocol
package bencode
import (
"bufio"
2011-12-17 16:35:20 +00:00
"errors"
"fmt"
2010-01-06 15:38:33 +00:00
"io"
"strconv"
)
// Parser
//
// Implements parsing but not the actions. Those are
// carried out by the implementation of the builder interface.
// A builder represents the object being created.
2010-01-06 15:38:33 +00:00
// Calling a method like Int64(i) sets that object to i.
// Calling a method like Elem(i) or Key(s) creates a
// new builder for a subpiece of the object (logically,
2011-12-17 16:35:20 +00:00
// a slice element or a map key).
2010-01-06 15:38:33 +00:00
//
// There are two Builders, in other files.
// The decoder builds a generic bencode structures
// in which maps are maps.
// The structBuilder copies data into a possibly
// nested data structure, using the "map keys"
// as struct field names.
// A builder is an interface implemented by clients and passed
2010-01-06 15:38:33 +00:00
// to the bencode parser. It gives clients full control over the
// eventual representation returned by the parser.
type builder interface {
2010-01-06 15:38:33 +00:00
// Set value
Int64(i int64)
Uint64(i uint64)
String(s string)
Array()
Map()
// Create sub-Builders
Elem(i int) builder
Key(s string) builder
2010-01-06 15:38:33 +00:00
// Flush changes to parent builder if necessary.
2010-01-06 15:38:33 +00:00
Flush()
}
func collectInt(r *bufio.Reader, delim byte) (buf []byte, err error) {
2010-01-06 15:38:33 +00:00
for {
var c byte
c, err = r.ReadByte()
2010-01-06 15:38:33 +00:00
if err != nil {
return
}
if c == delim {
return
}
if !(c == '-' || (c >= '0' && c <= '9')) {
2011-12-17 16:35:20 +00:00
err = errors.New("expected digit")
2010-01-06 15:38:33 +00:00
return
}
buf = append(buf, c)
2010-01-06 15:38:33 +00:00
}
return
}
func decodeInt64(r *bufio.Reader, delim byte) (data int64, err error) {
2010-01-06 15:38:33 +00:00
buf, err := collectInt(r, delim)
if err != nil {
return
}
2011-12-17 16:35:20 +00:00
data, err = strconv.ParseInt(string(buf), 10, 64)
2010-01-06 15:38:33 +00:00
return
}
func decodeString(r *bufio.Reader) (data string, err error) {
length, err := decodeInt64(r, ':')
2010-01-06 15:38:33 +00:00
if err != nil {
return
}
if length < 0 {
2011-12-17 16:35:20 +00:00
err = errors.New("Bad string length")
2010-01-06 15:38:33 +00:00
return
}
2010-07-25 19:32:08 +00:00
var buf = make([]byte, length)
2010-01-06 15:38:33 +00:00
_, err = io.ReadFull(r, buf)
if err != nil {
return
}
data = string(buf)
return
}
func parseFromReader(r *bufio.Reader, build builder) (err error) {
2010-01-06 15:38:33 +00:00
c, err := r.ReadByte()
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
switch {
case c >= '0' && c <= '9':
2010-01-06 15:38:33 +00:00
// String
err = r.UnreadByte()
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
var str string
str, err = decodeString(r)
2010-01-06 15:38:33 +00:00
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
build.String(str)
case c == 'd':
// dictionary
build.Map()
for {
c, err = r.ReadByte()
2010-01-06 15:38:33 +00:00
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
if c == 'e' {
break
}
err = r.UnreadByte()
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
var key string
key, err = decodeString(r)
2010-01-06 15:38:33 +00:00
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
// TODO: in pendantic mode, check for keys in ascending order.
err = parse(r, build.Key(key))
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
}
case c == 'i':
var buf []byte
buf, err = collectInt(r, 'e')
2010-01-06 15:38:33 +00:00
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
var str string
var i int64
var i2 uint64
str = string(buf)
2010-01-06 15:38:33 +00:00
// If the number is exactly an integer, use that.
2011-12-17 16:35:20 +00:00
if i, err = strconv.ParseInt(str, 10, 64); err == nil {
2010-01-06 15:38:33 +00:00
build.Int64(i)
2011-12-17 16:35:20 +00:00
} else if i2, err = strconv.ParseUint(str, 10, 64); err == nil {
build.Uint64(i2)
2010-01-06 15:38:33 +00:00
} else {
2011-12-17 16:35:20 +00:00
err = errors.New("Bad integer")
2010-01-06 15:38:33 +00:00
}
case c == 'l':
// array
build.Array()
n := 0
for {
c, err = r.ReadByte()
2010-01-06 15:38:33 +00:00
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
if c == 'e' {
break
}
err = r.UnreadByte()
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
err = parseFromReader(r, build.Elem(n))
2010-01-06 15:38:33 +00:00
if err != nil {
goto exit
2010-01-06 15:38:33 +00:00
}
n++
}
default:
2011-12-17 16:35:20 +00:00
err = errors.New(fmt.Sprintf("Unexpected character: '%v'", c))
2010-01-06 15:38:33 +00:00
}
exit:
2010-01-06 15:38:33 +00:00
build.Flush()
return
}
// Parse parses the bencode stream and makes calls to
// the builder to construct a parsed representation.
func parse(r io.Reader, builder builder) (err error) {
return parseFromReader(bufio.NewReader(r), builder)
2010-01-06 15:38:33 +00:00
}