package bencode
import (
- "bufio"
"bytes"
"fmt"
"io"
"reflect"
+
+ "github.com/anacrolix/missinggo/expect"
)
//----------------------------------------------------------------------------
// Unmarshaler spotted a value that was not appropriate for a given Go value.
type UnmarshalTypeError struct {
- Value string
- Type reflect.Type
+ BencodeTypeName string
+ UnmarshalTargetType reflect.Type
}
+// This could probably be a value type, but we may already have users assuming
+// that it's passed by pointer.
func (e *UnmarshalTypeError) Error() string {
- return "bencode: value (" + e.Value + ") is not appropriate for type: " +
- e.Type.String()
+ return fmt.Sprintf(
+ "can't unmarshal a bencode %v into a %v",
+ e.BencodeTypeName,
+ e.UnmarshalTargetType,
+ )
}
// Unmarshaler tried to write to an unexported (therefore unwritable) field.
UnmarshalBencode([]byte) error
}
-//----------------------------------------------------------------------------
-// Stateless interface
-//----------------------------------------------------------------------------
-
-// Marshal the value 'v' to the bencode form, return the result as []byte and an
-// error if any.
+// Marshal the value 'v' to the bencode form, return the result as []byte and
+// an error if any.
func Marshal(v interface{}) ([]byte, error) {
var buf bytes.Buffer
- e := encoder{Writer: bufio.NewWriter(&buf)}
- err := e.encode(v)
+ e := Encoder{w: &buf}
+ err := e.Encode(v)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
-// Unmarshal the bencode value in the 'data' to a value pointed by the 'v'
-// pointer, return a non-nil error if any.
-func Unmarshal(data []byte, v interface{}) error {
- e := decoder{Reader: bufio.NewReader(bytes.NewBuffer(data))}
- return e.decode(v)
+func MustMarshal(v interface{}) []byte {
+ b, err := Marshal(v)
+ expect.Nil(err)
+ return b
}
-//----------------------------------------------------------------------------
-// Stateful interface
-//----------------------------------------------------------------------------
-
-type Decoder struct {
- d decoder
+// Unmarshal the bencode value in the 'data' to a value pointed by the 'v' pointer, return a non-nil
+// error if any. If there are trailing bytes, this results in ErrUnusedTrailingBytes, but the value
+// will be valid. It's probably more consistent to use Decoder.Decode if you want to rely on this
+// behaviour (inspired by Rust's serde here).
+func Unmarshal(data []byte, v interface{}) (err error) {
+ buf := bytes.NewReader(data)
+ e := Decoder{r: buf}
+ err = e.Decode(v)
+ if err == nil && buf.Len() != 0 {
+ err = ErrUnusedTrailingBytes{buf.Len()}
+ }
+ return
}
-func NewDecoder(r io.Reader) *Decoder {
- return &Decoder{decoder{Reader: bufio.NewReader(r)}}
+type ErrUnusedTrailingBytes struct {
+ NumUnusedBytes int
}
-func (d *Decoder) Decode(v interface{}) error {
- return d.d.decode(v)
+func (me ErrUnusedTrailingBytes) Error() string {
+ return fmt.Sprintf("%d unused trailing bytes", me.NumUnusedBytes)
}
-type Encoder struct {
- e encoder
+func NewDecoder(r io.Reader) *Decoder {
+ return &Decoder{r: &scanner{r: r}}
}
func NewEncoder(w io.Writer) *Encoder {
- return &Encoder{encoder{Writer: bufio.NewWriter(w)}}
-}
-
-func (e *Encoder) Encode(v interface{}) error {
- err := e.e.encode(v)
- if err != nil {
- return err
- }
- return nil
+ return &Encoder{w: w}
}